mirror of
https://github.com/mauiaaron/apple2.git
synced 2024-06-26 00:29:27 +00:00
Reset Darwin target(s)
* Unfortunately cocos2d doesn't support what I'd like to do with a Mac target at this point, maybe sometime...
This commit is contained in:
parent
3a3219234f
commit
a69d8685eb
|
@ -1,22 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.deadc0de.${PRODUCT_NAME:rfc1034identifier}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>BNDL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
|
@ -1,9 +0,0 @@
|
|||
//
|
||||
// Prefix header
|
||||
//
|
||||
// The contents of this file are implicitly included at the beginning of every source file.
|
||||
//
|
||||
|
||||
#ifdef __OBJC__
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#endif
|
|
@ -1,46 +0,0 @@
|
|||
//
|
||||
// Apple2Mac_Tests.m
|
||||
// Apple2Mac Tests
|
||||
//
|
||||
// Created by Aaron Culliney on 6/21/14.
|
||||
// Copyright (c) 2014 deadc0de.org. All rights reserved.
|
||||
//
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
@interface Apple2Mac_CPUTests : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation Apple2Mac_CPUTests
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
[super setUp];
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
- (void)tearDown
|
||||
{
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
extern int test_cpu(int, char **);
|
||||
|
||||
- (void)testCPU
|
||||
{
|
||||
char *argv[] = {
|
||||
"-f",
|
||||
NULL
|
||||
};
|
||||
int argc = 0;
|
||||
for (char **p = &argv[0]; *p != NULL; p++) {
|
||||
++argc;
|
||||
}
|
||||
int val = test_cpu(argc, argv);
|
||||
|
||||
XCTAssertEqual(val, 0);
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,2 +0,0 @@
|
|||
/* Localized versions of Info.plist keys */
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:Apple2Mac.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
|
@ -1,18 +0,0 @@
|
|||
//
|
||||
// AppDelegate.h
|
||||
// Apple2Mac
|
||||
//
|
||||
// Created by Aaron Culliney on 6/16/14.
|
||||
// Copyright deadc0de.org 2014. All rights reserved.
|
||||
//
|
||||
|
||||
#import "cocos2d.h"
|
||||
|
||||
@interface Apple2MacAppDelegate : NSObject <NSApplicationDelegate>
|
||||
|
||||
@property (nonatomic, weak) IBOutlet NSWindow *window;
|
||||
@property (nonatomic, weak) IBOutlet CCGLView *glView;
|
||||
|
||||
- (IBAction)toggleFullScreen:(id)sender;
|
||||
|
||||
@end
|
|
@ -1,56 +0,0 @@
|
|||
//
|
||||
// AppDelegate.m
|
||||
// Apple2Mac
|
||||
//
|
||||
// Created by Aaron Culliney on 6/16/14.
|
||||
// Copyright deadc0de.org 2014. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppDelegate.h"
|
||||
#import "HelloWorldLayer.h"
|
||||
|
||||
@implementation Apple2MacAppDelegate
|
||||
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
|
||||
{
|
||||
CCDirectorMac *director = (CCDirectorMac*) [CCDirector sharedDirector];
|
||||
|
||||
// enable FPS and SPF
|
||||
[director setDisplayStats:YES];
|
||||
|
||||
// connect the OpenGL view with the director
|
||||
[director setView:_glView];
|
||||
|
||||
// EXPERIMENTAL stuff.
|
||||
// 'Effects' don't work correctly when autoscale is turned on.
|
||||
// Use kCCDirectorResize_NoScale if you don't want auto-scaling.
|
||||
[director setResizeMode:kCCDirectorResize_AutoScale];
|
||||
|
||||
// Enable "moving" mouse event. Default no.
|
||||
[_window setAcceptsMouseMovedEvents:NO];
|
||||
|
||||
// Center main window
|
||||
[_window center];
|
||||
|
||||
[director runWithScene:[HelloWorldLayer scene]];
|
||||
}
|
||||
|
||||
- (BOOL) applicationShouldTerminateAfterLastWindowClosed: (NSApplication *) theApplication
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[CCDirector sharedDirector] end];
|
||||
}
|
||||
|
||||
#pragma mark AppDelegate - IBActions
|
||||
|
||||
- (IBAction)toggleFullScreen: (id)sender
|
||||
{
|
||||
CCDirectorMac *director = (CCDirectorMac*) [CCDirector sharedDirector];
|
||||
[director setFullScreen: ! [director isFullScreen] ];
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,16 +0,0 @@
|
|||
//
|
||||
// DarwinVideo.h
|
||||
// Apple2Mac
|
||||
//
|
||||
// Created by Aaron Culliney on 6/21/14.
|
||||
// Copyright 2014 deadc0de.org. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "cocos2d.h"
|
||||
|
||||
@interface DarwinVideo : CCNode {
|
||||
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,28 +0,0 @@
|
|||
//
|
||||
// DarwinVideo.m
|
||||
// Apple2Mac
|
||||
//
|
||||
// Created by Aaron Culliney on 6/21/14.
|
||||
// Copyright 2014 deadc0de.org All rights reserved.
|
||||
//
|
||||
|
||||
#import "DarwinVideo.h"
|
||||
|
||||
void video_driver_init(void)
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
void video_driver_shutdown(void)
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
void video_sync(int block)
|
||||
{
|
||||
// TODO ...
|
||||
}
|
||||
|
||||
@implementation DarwinVideo
|
||||
|
||||
@end
|
|
@ -1,19 +0,0 @@
|
|||
//
|
||||
// HelloWorldLayer.h
|
||||
// Apple2Mac
|
||||
//
|
||||
// Created by Aaron Culliney on 6/16/14.
|
||||
// Copyright deadc0de.org 2014. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
// When you import this file, you import all the cocos2d classes
|
||||
#import "cocos2d.h"
|
||||
|
||||
// HelloWorldLayer
|
||||
@interface HelloWorldLayer : CCNode
|
||||
|
||||
// returns a CCScene that contains the HelloWorldLayer as the only child
|
||||
+(CCScene *) scene;
|
||||
|
||||
@end
|
|
@ -1,64 +0,0 @@
|
|||
//
|
||||
// HelloWorldLayer.m
|
||||
// Apple2Mac
|
||||
//
|
||||
// Created by Aaron Culliney on 6/16/14.
|
||||
// Copyright deadc0de.org 2014. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
#include "common.h"
|
||||
|
||||
// Import the interfaces
|
||||
#import "HelloWorldLayer.h"
|
||||
|
||||
// HelloWorldLayer implementation
|
||||
@implementation HelloWorldLayer
|
||||
|
||||
+ (CCScene *)scene
|
||||
{
|
||||
// 'scene' is an autorelease object.
|
||||
CCScene *scene = [CCScene node];
|
||||
|
||||
// 'layer' is an autorelease object.
|
||||
HelloWorldLayer *layer = [HelloWorldLayer node];
|
||||
|
||||
// add layer as a child to scene
|
||||
[scene addChild: layer];
|
||||
|
||||
// return the scene
|
||||
return scene;
|
||||
}
|
||||
|
||||
// on "init" you need to initialize your instance
|
||||
- (id)init
|
||||
{
|
||||
// always call "super" init
|
||||
// Apple recommends to re-assign "self" with the "super" return value
|
||||
if( (self=[super init]) ) {
|
||||
|
||||
// create and initialize a Label
|
||||
CCLabelTTF *label = [CCLabelTTF labelWithString:@"Hello World" fontName:@"Marker Felt" fontSize:64];
|
||||
|
||||
// ask director for the window size
|
||||
CGSize size = [[CCDirector sharedDirector] viewSize];
|
||||
|
||||
// position the label on the center of the screen
|
||||
label.position = ccp( size.width /2 , size.height/2 );
|
||||
|
||||
// add the label as a child to this Layer
|
||||
[self addChild: label];
|
||||
|
||||
#warning TODO FIXME ...
|
||||
#if 0
|
||||
// initialize emulator
|
||||
c_initialize_firsttime();
|
||||
|
||||
// spin off CPU thread
|
||||
pthread_create(&cpu_thread_id, NULL, (void *) &cpu_thread, (void *)NULL);
|
||||
#endif
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,121 +0,0 @@
|
|||
/*
|
||||
* cocos2d for iPhone: http://www.cocos2d-iphone.org
|
||||
*
|
||||
* Copyright (c) 2013 Apportable Inc.
|
||||
*
|
||||
* 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 <Foundation/Foundation.h>
|
||||
#import "cocos2d.h"
|
||||
#import "CCBSequenceProperty.h"
|
||||
|
||||
@class CCBSequence;
|
||||
|
||||
#pragma mark Animation Manager Delegate
|
||||
|
||||
/**
|
||||
* The animation manager delegate receives callbacks when animation sequences finishes playing.
|
||||
*/
|
||||
@protocol CCBAnimationManagerDelegate <NSObject>
|
||||
|
||||
/**
|
||||
* Called when an animation sequence has finished playing.
|
||||
* @param name The name of the sequence that just finished playing.
|
||||
*/
|
||||
- (void) completedAnimationSequenceNamed:(NSString*)name;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark Animation Manager
|
||||
|
||||
/**
|
||||
* The animation manager plays back animations, usually created by a tool such as SpriteBuilder. Any animation can have an arbitrary number of sequences (timelines) which each have keyframes for different properties.
|
||||
*/
|
||||
@interface CCAnimationManager : NSObject <CCSchedulerTarget>
|
||||
{
|
||||
NSMutableDictionary* _nodeSequences;
|
||||
NSMutableDictionary* _baseValues;
|
||||
|
||||
NSInteger _animationManagerId;
|
||||
CCBSequence* _runningSequence;
|
||||
CCBSequence* _lastSequence;
|
||||
|
||||
void (^block)(id sender);
|
||||
|
||||
CCScheduler* _scheduler;
|
||||
NSMutableArray* _currentActions;
|
||||
|
||||
BOOL _loop;
|
||||
|
||||
}
|
||||
|
||||
/// If set to true the animation manager will run on a fixed time step, this is required to run animations toghether with physics.
|
||||
@property (nonatomic,assign) bool fixedTimestep;
|
||||
|
||||
|
||||
/// The animation manager delegate receives updates about the animations currently being played.
|
||||
@property (nonatomic,weak) NSObject<CCBAnimationManagerDelegate>* delegate;
|
||||
|
||||
/// The name of the currently running sequence (timeline).
|
||||
@property (unsafe_unretained, nonatomic,readonly) NSString* runningSequenceName;
|
||||
|
||||
/// The name of the last completed sequence (timeline).
|
||||
@property (nonatomic,readonly) NSString* lastCompletedSequenceName;
|
||||
|
||||
/// Playback speed, default is 1 and corresponds to the normal playback speed. Use this property for fast forward or slow motion playback.
|
||||
@property (nonatomic,assign) float playbackSpeed;
|
||||
|
||||
/// Set to true to pause the animation currently being run.
|
||||
@property (nonatomic,assign) bool paused;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Plays an animation sequence (timeline) by its name.
|
||||
* @param name The name of the sequence to play.
|
||||
*/
|
||||
- (void) runAnimationsForSequenceNamed:(NSString*)name;
|
||||
|
||||
/**
|
||||
* Plays an animation sequence (timeline) by its name, tweens smoothly to the new sequence.
|
||||
* @param name The name of the sequence to play.
|
||||
* @param tweenDuration Time to tween to the new sequence.
|
||||
*/
|
||||
- (void) runAnimationsForSequenceNamed:(NSString*)name tweenDuration:(float)tweenDuration;
|
||||
|
||||
/**
|
||||
* Sets a block to be called when an animation sequence has finished playing.
|
||||
* @param b The block to call.
|
||||
*/
|
||||
-(void) setCompletedAnimationCallbackBlock:(void(^)(id sender))b;
|
||||
|
||||
#pragma mark Time Controls
|
||||
|
||||
//Renamed to jumpToSequenceNamed:time
|
||||
//- (void)timeSeekForSequenceNamed:(NSString*)name time:(float)time __attribute__((deprecated));
|
||||
|
||||
/**
|
||||
* Jumps to a specific time in a specific sequence (timeline).
|
||||
* @param name The name of the sequence to jump to.
|
||||
* @param time The time in the sequence.
|
||||
*/
|
||||
- (void)jumpToSequenceNamed:(NSString*)name time:(float)time;
|
||||
|
||||
@end
|
|
@ -1,943 +0,0 @@
|
|||
/*
|
||||
* cocos2d for iPhone: http://www.cocos2d-iphone.org
|
||||
*
|
||||
* Copyright (c) 2013 Apportable Inc.
|
||||
*
|
||||
* 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 "CCAnimationManager.h"
|
||||
#import "CCAnimationManager_Private.h"
|
||||
#import "CCBKeyframe.h"
|
||||
#import "CCBSequence.h"
|
||||
#import "CCBSequenceProperty.h"
|
||||
#import "CCBReader.h"
|
||||
#import "CCBKeyframe.h"
|
||||
#import "OALSimpleAudio.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
#import "CCDirector_Private.h"
|
||||
#import "CCBReader_Private.h"
|
||||
#import "CCActionManager.h"
|
||||
|
||||
// Unique Manager ID
|
||||
static NSInteger ccbAnimationManagerID = 0;
|
||||
|
||||
@implementation CCAnimationManager
|
||||
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
if (!self) return NULL;
|
||||
|
||||
_animationManagerId = ccbAnimationManagerID;
|
||||
ccbAnimationManagerID++;
|
||||
|
||||
_sequences = [[NSMutableArray alloc] init];
|
||||
_nodeSequences = [[NSMutableDictionary alloc] init];
|
||||
_baseValues = [[NSMutableDictionary alloc] init];
|
||||
|
||||
// Scheduler
|
||||
_scheduler = [[CCDirector sharedDirector] scheduler];
|
||||
[_scheduler scheduleTarget:self];
|
||||
[_scheduler setPaused:NO target:self];
|
||||
|
||||
// Current Sequence Actions
|
||||
_currentActions = [[NSMutableArray alloc] init];
|
||||
_playbackSpeed = 1.0f;
|
||||
_paused = NO;
|
||||
|
||||
_lastSequence = nil;
|
||||
_fixedTimestep = NO;
|
||||
_loop = NO;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSInteger)priority {
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (CGSize)containerSize:(CCNode*)node {
|
||||
if (node) {
|
||||
return node.contentSize;
|
||||
} else {
|
||||
return _rootContainerSize;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)addNode:(CCNode*)node andSequences:(NSDictionary*)seq {
|
||||
NSValue* nodePtr = [NSValue valueWithPointer:(__bridge const void *)(node)];
|
||||
[_nodeSequences setObject:seq forKey:nodePtr];
|
||||
}
|
||||
|
||||
- (id)seqForNode:(CCNode*)node {
|
||||
NSValue* nodePtr = [NSValue valueWithPointer:(__bridge const void *)(node)];
|
||||
return [_nodeSequences objectForKey:nodePtr];
|
||||
}
|
||||
|
||||
- (void)moveAnimationsFromNode:(CCNode*)fromNode toNode:(CCNode*)toNode {
|
||||
NSValue* fromNodePtr = [NSValue valueWithPointer:(__bridge const void *)(fromNode)];
|
||||
NSValue* toNodePtr = [NSValue valueWithPointer:(__bridge const void *)(toNode)];
|
||||
|
||||
// Move base values
|
||||
id baseValue = [_baseValues objectForKey:fromNodePtr];
|
||||
if (baseValue) {
|
||||
[_baseValues setObject:baseValue forKey:toNodePtr];
|
||||
[_baseValues removeObjectForKey:fromNodePtr];
|
||||
}
|
||||
|
||||
// Move keyframes
|
||||
NSDictionary* seqs = [_nodeSequences objectForKey:fromNodePtr];
|
||||
if (seqs) {
|
||||
[_nodeSequences setObject:seqs forKey:toNodePtr];
|
||||
[_nodeSequences removeObjectForKey:fromNodePtr];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setBaseValue:(id)value forNode:(CCNode*)node propertyName:(NSString*)propName {
|
||||
NSValue* nodePtr = [NSValue valueWithPointer:(__bridge const void *)(node)];
|
||||
|
||||
NSMutableDictionary* props = [_baseValues objectForKey:nodePtr];
|
||||
if (!props) {
|
||||
props = [NSMutableDictionary dictionary];
|
||||
[_baseValues setObject:props forKey:nodePtr];
|
||||
}
|
||||
|
||||
[props setObject:value forKey:propName];
|
||||
}
|
||||
|
||||
- (id)baseValueForNode:(CCNode*) node propertyName:(NSString*) propName {
|
||||
NSValue* nodePtr = [NSValue valueWithPointer:(__bridge const void *)(node)];
|
||||
|
||||
NSMutableDictionary* props = [_baseValues objectForKey:nodePtr];
|
||||
return [props objectForKey:propName];
|
||||
}
|
||||
|
||||
- (int)sequenceIdForSequenceNamed:(NSString*)name {
|
||||
for (CCBSequence* seq in _sequences) {
|
||||
if ([seq.name isEqualToString:name]) {
|
||||
return seq.sequenceId;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
- (CCBSequence*)sequenceFromSequenceId:(int)seqId {
|
||||
for (CCBSequence* seq in _sequences)
|
||||
{
|
||||
if (seq.sequenceId == seqId) return seq;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
- (CCActionInterval*)actionFromKeyframe0:(CCBKeyframe*)kf0 andKeyframe1:(CCBKeyframe*)kf1 propertyName:(NSString*)name node:(CCNode*)node {
|
||||
float duration = kf1.time - kf0.time;
|
||||
|
||||
if ([name isEqualToString:@"rotation"]) {
|
||||
return [CCActionRotateTo actionWithDuration:duration angle:[kf1.value floatValue] simple:YES];
|
||||
} else if ([name isEqualToString:@"position"]) {
|
||||
id value = kf1.value;
|
||||
|
||||
// Get relative position
|
||||
float x = [[value objectAtIndex:0] floatValue];
|
||||
float y = [[value objectAtIndex:1] floatValue];
|
||||
|
||||
return [CCActionMoveTo actionWithDuration:duration position:ccp(x,y)];
|
||||
} else if ([name isEqualToString:@"scale"]) {
|
||||
id value = kf1.value;
|
||||
|
||||
// Get relative scale
|
||||
float x = [[value objectAtIndex:0] floatValue];
|
||||
float y = [[value objectAtIndex:1] floatValue];
|
||||
|
||||
return [CCActionScaleTo actionWithDuration:duration scaleX:x scaleY:y];
|
||||
} else if ([name isEqualToString:@"skew"]) {
|
||||
id value = kf1.value;
|
||||
|
||||
float x = [[value objectAtIndex:0] floatValue];
|
||||
float y = [[value objectAtIndex:1] floatValue];
|
||||
|
||||
return [CCActionSkewTo actionWithDuration:duration skewX:x skewY:y];
|
||||
} else if ([name isEqualToString:@"rotationalSkewX"]) {
|
||||
return [CCActionRotateTo actionWithDuration:duration angleX:[kf1.value floatValue]];
|
||||
} else if ([name isEqualToString:@"rotationalSkewY"]) {
|
||||
return [CCActionRotateTo actionWithDuration:duration angleY:[kf1.value floatValue]];
|
||||
} else if ([name isEqualToString:@"opacity"]) {
|
||||
return [CCActionFadeTo actionWithDuration:duration opacity:[kf1.value intValue]];
|
||||
} else if ([name isEqualToString:@"color"]) {
|
||||
CCColor* color = kf1.value;
|
||||
return [CCActionTintTo actionWithDuration:duration color:color];
|
||||
} else if ([name isEqualToString:@"visible"]) {
|
||||
if ([kf1.value boolValue]) {
|
||||
return [CCActionSequence actionOne:[CCActionDelay actionWithDuration:duration] two:[CCActionShow action]];
|
||||
} else {
|
||||
return [CCActionSequence actionOne:[CCActionDelay actionWithDuration:duration] two:[CCActionHide action]];
|
||||
}
|
||||
} else if ([name isEqualToString:@"spriteFrame"]) {
|
||||
return [CCActionSequence actionOne:[CCActionDelay actionWithDuration:duration] two:[CCActionSpriteFrame actionWithSpriteFrame:kf1.value]];
|
||||
} else {
|
||||
CCLOG(@"CCBReader: Failed to create animation for property: %@", name);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
- (void)setAnimatedProperty:(NSString*)name forNode:(CCNode*)node toValue:(id)value tweenDuration:(float) tweenDuration {
|
||||
if (tweenDuration > 0) {
|
||||
// Create a fake keyframe to generate the action from
|
||||
CCBKeyframe* kf1 = [[CCBKeyframe alloc] init];
|
||||
kf1.value = value;
|
||||
kf1.time = tweenDuration;
|
||||
kf1.easingType = kCCBKeyframeEasingLinear;
|
||||
|
||||
CCActionInterval* tweenAction = [self actionFromKeyframe0:NULL andKeyframe1:kf1 propertyName:name node:node];
|
||||
tweenAction.tag = (int)_animationManagerId;
|
||||
[tweenAction startWithTarget:node];
|
||||
[_currentActions addObject:tweenAction];
|
||||
} else {
|
||||
|
||||
if ([name isEqualToString:@"position"]) {
|
||||
|
||||
// Get relative position
|
||||
float x = [[value objectAtIndex:0] floatValue];
|
||||
float y = [[value objectAtIndex:1] floatValue];
|
||||
#ifdef __CC_PLATFORM_IOS
|
||||
[node setValue:[NSValue valueWithCGPoint:ccp(x,y)] forKey:name];
|
||||
#elif defined (__CC_PLATFORM_MAC)
|
||||
[node setValue:[NSValue valueWithPoint:ccp(x,y)] forKey:name];
|
||||
#endif
|
||||
} else if ([name isEqualToString:@"scale"]) {
|
||||
// Get relative scale
|
||||
float x = [[value objectAtIndex:0] floatValue];
|
||||
float y = [[value objectAtIndex:1] floatValue];
|
||||
|
||||
[node setValue:[NSNumber numberWithFloat:x] forKey:[name stringByAppendingString:@"X"]];
|
||||
[node setValue:[NSNumber numberWithFloat:y] forKey:[name stringByAppendingString:@"Y"]];
|
||||
} else if ([name isEqualToString:@"skew"]) {
|
||||
node.skewX = [[value objectAtIndex:0] floatValue];
|
||||
node.skewY = [[value objectAtIndex:1] floatValue];
|
||||
} else if ([name isEqualToString:@"spriteFrame"]) {
|
||||
[(CCSprite*)node setSpriteFrame:value];
|
||||
} else {
|
||||
[node setValue:value forKey:name];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setKeyFrameForNode:(CCNode*)node sequenceProperty:(CCBSequenceProperty*)seqProp tweenDuration:(float)tweenDuration keyFrame:(int)kf {
|
||||
NSArray* keyframes = [seqProp keyframes];
|
||||
|
||||
if ([keyframes count] == 0) {
|
||||
// No Animation, Set Base Value
|
||||
id baseValue = [self baseValueForNode:node propertyName:seqProp.name];
|
||||
NSAssert1(baseValue, @"No baseValue found for property (%@)", seqProp.name);
|
||||
[self setAnimatedProperty:seqProp.name forNode:node toValue:baseValue tweenDuration:tweenDuration];
|
||||
} else {
|
||||
// Use Specified KeyFrame
|
||||
CCBKeyframe* keyframe = [keyframes objectAtIndex:kf];
|
||||
[self setAnimatedProperty:seqProp.name forNode:node toValue:keyframe.value tweenDuration:tweenDuration];
|
||||
}
|
||||
}
|
||||
|
||||
- (CCActionInterval*)easeAction:(CCActionInterval*) action easingType:(int)easingType easingOpt:(float) easingOpt
|
||||
{
|
||||
if ([action isKindOfClass:[CCActionSequence class]]) return action;
|
||||
|
||||
if (easingType == kCCBKeyframeEasingLinear)
|
||||
{
|
||||
return action;
|
||||
}
|
||||
else if (easingType == kCCBKeyframeEasingInstant)
|
||||
{
|
||||
return [CCActionEaseInstant actionWithAction:action];
|
||||
}
|
||||
else if (easingType == kCCBKeyframeEasingCubicIn)
|
||||
{
|
||||
return [CCActionEaseIn actionWithAction:action rate:easingOpt];
|
||||
}
|
||||
else if (easingType == kCCBKeyframeEasingCubicOut)
|
||||
{
|
||||
return [CCActionEaseOut actionWithAction:action rate:easingOpt];
|
||||
}
|
||||
else if (easingType == kCCBKeyframeEasingCubicInOut)
|
||||
{
|
||||
return [CCActionEaseInOut actionWithAction:action rate:easingOpt];
|
||||
}
|
||||
else if (easingType == kCCBKeyframeEasingBackIn)
|
||||
{
|
||||
return [CCActionEaseBackIn actionWithAction:action];
|
||||
}
|
||||
else if (easingType == kCCBKeyframeEasingBackOut)
|
||||
{
|
||||
return [CCActionEaseBackOut actionWithAction:action];
|
||||
}
|
||||
else if (easingType == kCCBKeyframeEasingBackInOut)
|
||||
{
|
||||
return [CCActionEaseBackInOut actionWithAction:action];
|
||||
}
|
||||
else if (easingType == kCCBKeyframeEasingBounceIn)
|
||||
{
|
||||
return [CCActionEaseBounceIn actionWithAction:action];
|
||||
}
|
||||
else if (easingType == kCCBKeyframeEasingBounceOut)
|
||||
{
|
||||
return [CCActionEaseBounceOut actionWithAction:action];
|
||||
}
|
||||
else if (easingType == kCCBKeyframeEasingBounceInOut)
|
||||
{
|
||||
return [CCActionEaseBounceInOut actionWithAction:action];
|
||||
}
|
||||
else if (easingType == kCCBKeyframeEasingElasticIn)
|
||||
{
|
||||
return [CCActionEaseElasticIn actionWithAction:action period:easingOpt];
|
||||
}
|
||||
else if (easingType == kCCBKeyframeEasingElasticOut)
|
||||
{
|
||||
return [CCActionEaseElasticOut actionWithAction:action period:easingOpt];
|
||||
}
|
||||
else if (easingType == kCCBKeyframeEasingElasticInOut)
|
||||
{
|
||||
return [CCActionEaseElasticInOut actionWithAction:action period:easingOpt];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"CCBReader: Unkown easing type %d", easingType);
|
||||
return action;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)runActionsForNode:(CCNode*)node sequenceProperty:(CCBSequenceProperty*)seqProp tweenDuration:(float)tweenDuration startKeyFrame:(int)startFrame {
|
||||
|
||||
// Grab Key Frames / Count
|
||||
NSArray* keyframes = [seqProp keyframes];
|
||||
int numKeyframes = (int)keyframes.count;
|
||||
|
||||
// Nothing to do - No Keyframes
|
||||
if(numKeyframes<1) return;
|
||||
|
||||
// Action Sequence Builder
|
||||
NSMutableArray* actions = [NSMutableArray array];
|
||||
int endFrame = startFrame+1;
|
||||
|
||||
if(endFrame==numKeyframes || endFrame<0)
|
||||
return;
|
||||
|
||||
// First Frame
|
||||
CCBKeyframe* kf0 = [keyframes objectAtIndex:startFrame];
|
||||
|
||||
// Initial Tween Required
|
||||
if(startFrame==0) {
|
||||
float timeFirst = kf0.time + tweenDuration;
|
||||
|
||||
// Handle Tween
|
||||
if (timeFirst > 0) {
|
||||
[actions addObject:[CCActionDelay actionWithDuration:timeFirst]];
|
||||
}
|
||||
}
|
||||
|
||||
// Build Actions
|
||||
CCActionSequence* actionSeq = [self createActionForNode:node sequenceProperty:seqProp beginKeyFrame:startFrame endKeyFrame:endFrame];
|
||||
if(actionSeq) {
|
||||
[actions addObject:actionSeq];
|
||||
}
|
||||
|
||||
// Next Sequence
|
||||
CCActionCallBlock* nextKeyFrameBlock = [CCActionCallBlock actionWithBlock:^{
|
||||
[self runActionsForNode:node sequenceProperty:seqProp tweenDuration:0 startKeyFrame:endFrame];
|
||||
}];
|
||||
|
||||
[actions addObject:nextKeyFrameBlock];
|
||||
|
||||
CCActionSequence* seq = [CCActionSequence actionWithArray:actions];
|
||||
seq.tag = _animationManagerId;
|
||||
[seq startWithTarget:node];
|
||||
if(kf0.time>0 || _loop) { // Ensure Sync
|
||||
[seq step:0];
|
||||
[seq step:_runningSequence.time-kf0.time];
|
||||
}
|
||||
[_currentActions addObject:seq];
|
||||
}
|
||||
|
||||
- (id)actionForCallbackChannel:(CCBSequenceProperty*) channel {
|
||||
float lastKeyframeTime = 0;
|
||||
|
||||
NSMutableArray* actions = [NSMutableArray array];
|
||||
|
||||
for (CCBKeyframe* keyframe in channel.keyframes) {
|
||||
|
||||
float timeSinceLastKeyframe = keyframe.time - lastKeyframeTime;
|
||||
lastKeyframeTime = keyframe.time;
|
||||
if (timeSinceLastKeyframe > 0) {
|
||||
[actions addObject:[CCActionDelay actionWithDuration:timeSinceLastKeyframe]];
|
||||
}
|
||||
|
||||
NSString* selectorName = [keyframe.value objectAtIndex:0];
|
||||
int selectorTarget = [[keyframe.value objectAtIndex:1] intValue];
|
||||
|
||||
// Callback through obj-c
|
||||
id target = NULL;
|
||||
if (selectorTarget == kCCBTargetTypeDocumentRoot) target = self.rootNode;
|
||||
else if (selectorTarget == kCCBTargetTypeOwner) target = _owner;
|
||||
|
||||
SEL selector = NSSelectorFromString(selectorName);
|
||||
|
||||
if (target && selector) {
|
||||
[actions addObject:[CCActionCallFunc actionWithTarget:target selector:selector]];
|
||||
}
|
||||
}
|
||||
|
||||
if (!actions.count) return NULL;
|
||||
|
||||
return [CCActionSequence actionWithArray:actions];
|
||||
}
|
||||
|
||||
- (id)actionForSoundChannel:(CCBSequenceProperty*) channel {
|
||||
|
||||
float lastKeyframeTime = 0;
|
||||
|
||||
NSMutableArray* actions = [NSMutableArray array];
|
||||
|
||||
for (CCBKeyframe* keyframe in channel.keyframes) {
|
||||
|
||||
float timeSinceLastKeyframe = keyframe.time - lastKeyframeTime;
|
||||
lastKeyframeTime = keyframe.time;
|
||||
if (timeSinceLastKeyframe > 0) {
|
||||
[actions addObject:[CCActionDelay actionWithDuration:timeSinceLastKeyframe]];
|
||||
}
|
||||
|
||||
NSString* soundFile = [keyframe.value objectAtIndex:0];
|
||||
float pitch = [[keyframe.value objectAtIndex:1] floatValue];
|
||||
float pan = [[keyframe.value objectAtIndex:2] floatValue];
|
||||
float gain = [[keyframe.value objectAtIndex:3] floatValue];
|
||||
|
||||
[actions addObject:[CCActionSoundEffect actionWithSoundFile:soundFile pitch:pitch pan:pan gain:gain]];
|
||||
}
|
||||
|
||||
if (!actions.count) return NULL;
|
||||
|
||||
return [CCActionSequence actionWithArray:actions];
|
||||
}
|
||||
|
||||
- (void)runAnimationsForSequenceId:(int)seqId tweenDuration:(float) tweenDuration {
|
||||
|
||||
NSAssert(seqId != -1, @"Sequence id %d couldn't be found",seqId);
|
||||
|
||||
_paused = YES;
|
||||
[self clearAllActions];
|
||||
|
||||
// Contains all Sequence Propertys / Keyframe
|
||||
for (NSValue* nodePtr in _nodeSequences) {
|
||||
|
||||
CCNode* node = [nodePtr pointerValue];
|
||||
|
||||
NSDictionary* seqs = [_nodeSequences objectForKey:nodePtr];
|
||||
NSDictionary* seqNodeProps = [seqs objectForKey:[NSNumber numberWithInt:seqId]];
|
||||
|
||||
NSMutableSet* seqNodePropNames = [NSMutableSet set];
|
||||
|
||||
if(_lastSequence.sequenceId!=seqId) {
|
||||
_loop = NO;
|
||||
|
||||
// Reset the nodes that may have been changed by other timelines
|
||||
NSDictionary* nodeBaseValues = [_baseValues objectForKey:nodePtr];
|
||||
for (NSString* propName in nodeBaseValues) {
|
||||
|
||||
if (![seqNodePropNames containsObject:propName]) {
|
||||
|
||||
id value = [nodeBaseValues objectForKey:propName];
|
||||
|
||||
if (value) {
|
||||
[self setAnimatedProperty:propName forNode:node toValue:value tweenDuration:tweenDuration];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reset nodes that have sequence node properties, build first keyframe action sequence.
|
||||
for (NSString* propName in seqNodeProps) {
|
||||
CCBSequenceProperty* seqProp = [seqNodeProps objectForKey:propName];
|
||||
[seqNodePropNames addObject:propName];
|
||||
|
||||
// Reset Node State to First KeyFrame
|
||||
[self setKeyFrameForNode:node sequenceProperty:seqProp tweenDuration:tweenDuration keyFrame:0];
|
||||
|
||||
// Build First Key Frame Sequence
|
||||
[self runActionsForNode:node sequenceProperty:seqProp tweenDuration:tweenDuration startKeyFrame:0];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[self addSequenceCallBacks:seqId tweenDuration:tweenDuration startTime:0];
|
||||
|
||||
// Set the running scene
|
||||
_runningSequence = [self sequenceFromSequenceId:seqId];
|
||||
_runningSequence.time = 0.0f;
|
||||
|
||||
_paused = NO;
|
||||
}
|
||||
|
||||
- (void)runAnimationsForSequenceNamed:(NSString*)name tweenDuration:(float)tweenDuration {
|
||||
int seqId = [self sequenceIdForSequenceNamed:name];
|
||||
[self runAnimationsForSequenceId:seqId tweenDuration:tweenDuration];
|
||||
}
|
||||
|
||||
- (void)runAnimationsForSequenceNamed:(NSString*)name {
|
||||
[self runAnimationsForSequenceNamed:name tweenDuration:0];
|
||||
}
|
||||
|
||||
- (void)addSequenceCallBacks:(int)seqId tweenDuration:(float)tweenDuration startTime:(float)time {
|
||||
|
||||
// End of Sequence Callback
|
||||
CCBSequence* seq = [self sequenceFromSequenceId:seqId];
|
||||
|
||||
CCActionSequence* completeAction = [CCActionSequence
|
||||
actionOne:[CCActionDelay actionWithDuration:seq.duration+tweenDuration-time]
|
||||
two:[CCActionCallFunc actionWithTarget:self selector:@selector(sequenceCompleted)]];
|
||||
completeAction.tag = (int)_animationManagerId;
|
||||
[completeAction startWithTarget:self.rootNode];
|
||||
[_currentActions addObject:completeAction];
|
||||
|
||||
// Playback callbacks and sounds
|
||||
if (seq.callbackChannel) {
|
||||
// Build sound actions for channel
|
||||
CCAction* action = [self actionForCallbackChannel:seq.callbackChannel];
|
||||
if (action) {
|
||||
action.tag = (int)_animationManagerId;
|
||||
[action startWithTarget:self.rootNode];
|
||||
[_currentActions addObject:action];
|
||||
}
|
||||
}
|
||||
|
||||
if (seq.soundChannel) {
|
||||
// Build sound actions for channel
|
||||
CCAction* action = [self actionForSoundChannel:seq.soundChannel];
|
||||
if (action) {
|
||||
action.tag = (int)_animationManagerId;
|
||||
[action startWithTarget:self.rootNode];
|
||||
[_currentActions addObject:action];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
- (void)sequenceCompleted {
|
||||
|
||||
// Save last completed sequence
|
||||
if (_lastCompletedSequenceName != _runningSequence.name) {
|
||||
_lastCompletedSequenceName = [_runningSequence.name copy];
|
||||
_lastSequence = _runningSequence;
|
||||
}
|
||||
|
||||
// Play next sequence
|
||||
int nextSeqId = _runningSequence.chainedSequenceId;
|
||||
|
||||
// Repeat Same Sequence
|
||||
if(nextSeqId!=-1&& nextSeqId==_runningSequence.sequenceId) {
|
||||
_loop = YES;
|
||||
}
|
||||
|
||||
_runningSequence = NULL;
|
||||
|
||||
// Callbacks
|
||||
[_delegate completedAnimationSequenceNamed:_lastCompletedSequenceName];
|
||||
if (block) block(self);
|
||||
|
||||
// Run next sequence if callbacks did not start a new sequence
|
||||
if (_runningSequence == NULL && nextSeqId != -1) {
|
||||
[self runAnimationsForSequenceId:nextSeqId tweenDuration:0];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString*)runningSequenceName {
|
||||
return _runningSequence.name;
|
||||
}
|
||||
|
||||
- (void)setCompletedAnimationCallbackBlock:(void(^)(id sender))b {
|
||||
block = [b copy];
|
||||
}
|
||||
|
||||
- (void)cleanup {
|
||||
[_scheduler setPaused:YES target:self];
|
||||
[_scheduler unscheduleTarget:self];
|
||||
[self clearAllActions];
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
_rootNode = NULL;
|
||||
}
|
||||
|
||||
- (void)debug {
|
||||
CCLOG(@"baseValues: %@", _baseValues);
|
||||
CCLOG(@"nodeSequences: %@", _nodeSequences);
|
||||
}
|
||||
|
||||
- (void)setPlaybackSpeed:(float)playbackSpeed {
|
||||
|
||||
// Backward Motion (Backwards)
|
||||
if(_playbackSpeed>0 && playbackSpeed<0 && _runningSequence) {
|
||||
[self timeSeekStaticForSequenceId:_runningSequence.sequenceId time:_runningSequence.time];
|
||||
}
|
||||
|
||||
// Forward Motion
|
||||
if(_playbackSpeed<0 && playbackSpeed>0 && _runningSequence) {
|
||||
[self timeSeekForSequenceId:_runningSequence.sequenceId time:_runningSequence.time];
|
||||
}
|
||||
|
||||
_playbackSpeed = playbackSpeed;
|
||||
}
|
||||
|
||||
|
||||
- (void)timeSeekStaticForSequenceId:(int)seqId time:(float)time {
|
||||
NSAssert(seqId != -1, @"Sequence id %d couldn't be found",seqId);
|
||||
|
||||
// Reverse Loop Hack
|
||||
if(_playbackSpeed<0 && time<0 && _runningSequence.chainedSequenceId==_runningSequence.sequenceId) {
|
||||
time = _runningSequence.duration;
|
||||
}
|
||||
|
||||
[self clearAllActions];
|
||||
|
||||
// Contains all Sequence Propertys / Keyframe
|
||||
for (NSValue* nodePtr in _nodeSequences) {
|
||||
CCNode* node = [nodePtr pointerValue];
|
||||
|
||||
NSDictionary* seqs = [_nodeSequences objectForKey:nodePtr];
|
||||
NSDictionary* seqNodeProps = [seqs objectForKey:[NSNumber numberWithInt:seqId]];
|
||||
|
||||
// Reset Nodes, Create Actions
|
||||
NSMutableSet* seqNodePropNames = [NSMutableSet set];
|
||||
|
||||
// Reset the nodes that may have been changed by other timelines
|
||||
NSDictionary* nodeBaseValues = [_baseValues objectForKey:nodePtr];
|
||||
for (NSString* propName in nodeBaseValues) {
|
||||
if (![seqNodePropNames containsObject:propName]) {
|
||||
id value = [nodeBaseValues objectForKey:propName];
|
||||
if (value) {
|
||||
[self setAnimatedProperty:propName forNode:node toValue:value tweenDuration:0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (NSString* propName in seqNodeProps) {
|
||||
CCBSequenceProperty* seqProp = [seqNodeProps objectForKey:propName];
|
||||
NSMutableArray* keyFrames = [self findFrames:time sequenceProperty:seqProp];
|
||||
|
||||
// No KeyFrames Found
|
||||
if([keyFrames count]==0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Last Sequence KeyFrame Ended Before Seek Time / Set State
|
||||
if([keyFrames count]==1) {
|
||||
[self setKeyFrameForNode:node sequenceProperty:seqProp tweenDuration:0 keyFrame:[[keyFrames objectAtIndex:0] intValue]];
|
||||
continue;
|
||||
}
|
||||
|
||||
// Set Initial State First Key Frame
|
||||
[self setKeyFrameForNode:node sequenceProperty:seqProp tweenDuration:0 keyFrame:[[keyFrames objectAtIndex:0] intValue]];
|
||||
|
||||
CCBKeyframe* currentKeyFrame = [seqProp.keyframes objectAtIndex:[[keyFrames objectAtIndex:0] unsignedIntegerValue]];
|
||||
|
||||
float timeFoward = time - currentKeyFrame.time;
|
||||
|
||||
// Create Action Sequence
|
||||
CCActionSequence* action = [self createActionForNode:node
|
||||
sequenceProperty:seqProp
|
||||
beginKeyFrame:[[keyFrames objectAtIndex:0] intValue]
|
||||
endKeyFrame:[[keyFrames objectAtIndex:1] intValue]];
|
||||
|
||||
// Fast forward to time point
|
||||
[action startWithTarget:node];
|
||||
[action step:0]; // First Tick
|
||||
[action step:timeFoward];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_runningSequence = [self sequenceFromSequenceId:seqId];
|
||||
_runningSequence.time = time;
|
||||
}
|
||||
|
||||
|
||||
- (void)timeSeekForSequenceNamed:(NSString*)name time:(float)time {
|
||||
[self jumpToSequenceNamed:name time:time];
|
||||
}
|
||||
|
||||
- (void)jumpToSequenceNamed:(NSString *)name time:(float)time {
|
||||
int seqId = [self sequenceIdForSequenceNamed:name];
|
||||
[self timeSeekForSequenceId:seqId time:time];
|
||||
}
|
||||
|
||||
- (void) timeSeekForSequenceId:(int)seqId time:(float)time {
|
||||
NSAssert(seqId != -1, @"Sequence id %d couldn't be found",seqId);
|
||||
|
||||
[self clearAllActions];
|
||||
// Contains all Sequence Propertys / Keyframe
|
||||
for (NSValue* nodePtr in _nodeSequences) {
|
||||
CCNode* node = [nodePtr pointerValue];
|
||||
|
||||
NSDictionary* seqs = [_nodeSequences objectForKey:nodePtr];
|
||||
NSDictionary* seqNodeProps = [seqs objectForKey:[NSNumber numberWithInt:seqId]];
|
||||
|
||||
// Reset Nodes, Create Actions
|
||||
NSMutableSet* seqNodePropNames = [NSMutableSet set];
|
||||
|
||||
// Reset the nodes that may have been changed by other timelines
|
||||
NSDictionary* nodeBaseValues = [_baseValues objectForKey:nodePtr];
|
||||
for (NSString* propName in nodeBaseValues) {
|
||||
if (![seqNodePropNames containsObject:propName]) {
|
||||
id value = [nodeBaseValues objectForKey:propName];
|
||||
if (value) {
|
||||
[self setAnimatedProperty:propName forNode:node toValue:value tweenDuration:0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (NSString* propName in seqNodeProps) {
|
||||
CCBSequenceProperty* seqProp = [seqNodeProps objectForKey:propName];
|
||||
NSMutableArray* keyFrames = [self findFrames:time sequenceProperty:seqProp];
|
||||
|
||||
// No KeyFrames Found
|
||||
if([keyFrames count]==0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Last Sequence KeyFrame Ended Before Seek Time / Set State
|
||||
if([keyFrames count]==1) {
|
||||
[self setKeyFrameForNode:node sequenceProperty:seqProp tweenDuration:0 keyFrame:[[keyFrames objectAtIndex:0] intValue]];
|
||||
continue;
|
||||
}
|
||||
|
||||
// Set Initial State First Key Frame
|
||||
[self setKeyFrameForNode:node sequenceProperty:seqProp tweenDuration:0 keyFrame:[[keyFrames objectAtIndex:0] intValue]];
|
||||
|
||||
CCBKeyframe* currentKeyFrame = [seqProp.keyframes objectAtIndex:[[keyFrames objectAtIndex:0] unsignedIntegerValue]];
|
||||
|
||||
float timeFoward = time - currentKeyFrame.time;
|
||||
|
||||
// Create Action Sequence
|
||||
CCActionSequence* action = [self createActionForNode:node
|
||||
sequenceProperty:seqProp
|
||||
beginKeyFrame:[[keyFrames objectAtIndex:0] intValue]
|
||||
endKeyFrame:[[keyFrames objectAtIndex:1] intValue]];
|
||||
|
||||
|
||||
// Next Sequence
|
||||
CCActionCallBlock* nextKeyFrameBlock = [CCActionCallBlock actionWithBlock:^{
|
||||
[self runActionsForNode:node sequenceProperty:seqProp tweenDuration:0 startKeyFrame:[[keyFrames objectAtIndex:1] intValue]];
|
||||
}];
|
||||
|
||||
|
||||
CCActionSequence* animSequence = [CCActionSequence actions:action, nextKeyFrameBlock,nil];
|
||||
|
||||
// Fast forward to time point
|
||||
[animSequence setTag:_animationManagerId];
|
||||
[animSequence startWithTarget:node];
|
||||
[animSequence step:0]; // First Tick
|
||||
[animSequence step:timeFoward];
|
||||
[_currentActions addObject:animSequence];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[self addSequenceCallBacks:seqId tweenDuration:0 startTime:time];
|
||||
|
||||
_runningSequence = [self sequenceFromSequenceId:seqId];
|
||||
_runningSequence.time = time;
|
||||
}
|
||||
|
||||
- (NSMutableArray*)findFrames:(float)time sequenceProperty:(CCBSequenceProperty*) seqProp{
|
||||
NSMutableArray* result = [[NSMutableArray alloc] init];
|
||||
|
||||
CCBKeyframe* startKeyFrame = [seqProp.keyframes objectAtIndex:0];
|
||||
CCBKeyframe* endKeyFrame = [seqProp.keyframes objectAtIndex:0];
|
||||
|
||||
NSUInteger frameCount = [seqProp.keyframes count];
|
||||
|
||||
// Find KeyFrames
|
||||
int i;
|
||||
for (i = 0; i < frameCount; i++) {
|
||||
CCBKeyframe* currentKey = [seqProp.keyframes objectAtIndex:i];
|
||||
|
||||
if (currentKey.time>time) {
|
||||
endKeyFrame = currentKey;
|
||||
// Add KeyFrames
|
||||
[result addObject:[NSNumber numberWithUnsignedInteger:[seqProp.keyframes indexOfObject:startKeyFrame]]];
|
||||
[result addObject:[NSNumber numberWithUnsignedInteger:[seqProp.keyframes indexOfObject:endKeyFrame]]];
|
||||
break;
|
||||
}
|
||||
|
||||
startKeyFrame = [seqProp.keyframes objectAtIndex:i];
|
||||
}
|
||||
|
||||
// No Frames
|
||||
if([result count]==0) {
|
||||
[result addObject:[NSNumber numberWithInteger:(i-1)]];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (CCActionSequence*)createActionForNode:(CCNode*)node sequenceProperty:(CCBSequenceProperty*)seqProp beginKeyFrame:(int)beginKeyFrame endKeyFrame:(int)endKeyFrame
|
||||
{
|
||||
NSArray* keyframes = [seqProp keyframes];
|
||||
|
||||
CCBKeyframe* startKF = [keyframes objectAtIndex:beginKeyFrame];
|
||||
CCBKeyframe* endKF = [keyframes objectAtIndex:endKeyFrame];
|
||||
|
||||
CCActionSequence* seq = nil;
|
||||
|
||||
// Check Keyframe Cache
|
||||
if(endKF.frameActions) {
|
||||
seq = [endKF.frameActions copy];
|
||||
} else {
|
||||
|
||||
// Build Animation Actions
|
||||
NSMutableArray* actions = [[NSMutableArray alloc] init];
|
||||
|
||||
CCActionInterval* action = [self actionFromKeyframe0:startKF andKeyframe1:endKF propertyName:seqProp.name node:node];
|
||||
|
||||
if (action) {
|
||||
// Apply Easing
|
||||
action = [self easeAction:action easingType:startKF.easingType easingOpt:startKF.easingOpt];
|
||||
[actions addObject:action];
|
||||
|
||||
// Cache
|
||||
seq = [CCActionSequence actionWithArray:actions];
|
||||
seq.tag = _animationManagerId;
|
||||
endKF.frameActions = [seq copy];
|
||||
}
|
||||
}
|
||||
|
||||
return seq;
|
||||
}
|
||||
|
||||
-(void)fixedUpdate:(CCTime)delta {
|
||||
|
||||
if(self.fixedTimestep) {
|
||||
[self updateInternal:delta];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)update:(CCTime)delta {
|
||||
|
||||
if(!self.fixedTimestep) {
|
||||
[self updateInternal:delta];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateInternal:(CCTime)delta {
|
||||
|
||||
if(_paused) return;
|
||||
|
||||
float step = delta*_playbackSpeed;
|
||||
|
||||
if(_playbackSpeed<0) {
|
||||
[self timeSeekStaticForSequenceId:_runningSequence.sequenceId time:_runningSequence.time+step];
|
||||
return;
|
||||
}
|
||||
|
||||
if(_currentActions.count==0) return;
|
||||
|
||||
CCAction *action;
|
||||
NSArray *actionsCopy = [_currentActions copy];
|
||||
|
||||
for(action in actionsCopy) {
|
||||
[action step:step];
|
||||
|
||||
if([action isDone]) {
|
||||
[_currentActions removeObject:action];
|
||||
}
|
||||
}
|
||||
|
||||
_runningSequence.time+=step;
|
||||
}
|
||||
|
||||
- (void)clearAllActions {
|
||||
|
||||
if(!_currentActions.count) return;
|
||||
|
||||
for(CCAction *action in _currentActions) {
|
||||
[action stop];
|
||||
}
|
||||
|
||||
[_currentActions removeAllObjects];
|
||||
}
|
||||
|
||||
#pragma mark Simple Sequence Builder
|
||||
|
||||
- (void)addKeyFramesForSequenceNamed:(NSString*)name propertyType:(CCBSequencePropertyType)propertyType frameArray:(NSArray*)frameArray node:(CCNode *)node loop:(BOOL)loop {
|
||||
|
||||
int seqId = (int)[self.sequences count];
|
||||
|
||||
// Create New Sequence
|
||||
CCBSequence* sequence = [[CCBSequence alloc] init];
|
||||
[sequence setName:name];
|
||||
[sequence setSequenceId:seqId];
|
||||
[self.sequences addObject:sequence];
|
||||
|
||||
// Repeat Sequence (Loop)
|
||||
if(loop) {
|
||||
[sequence setChainedSequenceId:seqId];
|
||||
}
|
||||
|
||||
NSString *propertyName = [CCBSequenceProperty getPropertyNameFromTypeId:propertyType];
|
||||
NSAssert(propertyName != nil, @"Property type %d couldn't be found",(int)propertyType);
|
||||
|
||||
// Create Sequence Property
|
||||
CCBSequenceProperty* sequenceProperty = [[CCBSequenceProperty alloc] init];
|
||||
[sequenceProperty setName:propertyName];
|
||||
[sequenceProperty setType:propertyType];
|
||||
|
||||
// Keyframe total time
|
||||
float duration = 0.0f;
|
||||
|
||||
for(NSDictionary* frameDict in frameArray) {
|
||||
|
||||
// Create KeyFrame
|
||||
CCBKeyframe* newFrame = [[CCBKeyframe alloc] init];
|
||||
[newFrame setTime:[[frameDict valueForKey:@"time"] floatValue]];
|
||||
[newFrame setValue:[CCSpriteFrame frameWithImageNamed:[frameDict objectForKey:@"value"]]];
|
||||
|
||||
[sequenceProperty.keyframes addObject:newFrame];
|
||||
duration=newFrame.time;
|
||||
}
|
||||
|
||||
// Set Sequence Duration
|
||||
sequence.duration = duration;
|
||||
|
||||
NSMutableDictionary* seqs = [NSMutableDictionary dictionary];
|
||||
NSMutableDictionary* seqNodeProps = [NSMutableDictionary dictionary];
|
||||
|
||||
[seqNodeProps setObject:sequenceProperty forKey:sequenceProperty.name];
|
||||
[seqs setObject:seqNodeProps forKey:[NSNumber numberWithInt:seqId]];
|
||||
|
||||
NSMutableDictionary* seqNode = [self seqForNode:node];
|
||||
if(seqNode) {
|
||||
[seqNode setObject:seqNodeProps forKey:[NSNumber numberWithInt:seqId]];
|
||||
} else {
|
||||
[self addNode:node andSequences:seqs];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,59 +0,0 @@
|
|||
/*
|
||||
* cocos2d for iPhone: http://www.cocos2d-iphone.org
|
||||
*
|
||||
* Copyright (c) 2013 Apportable Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@interface CCAnimationManager ()
|
||||
|
||||
// Sequence Array
|
||||
@property (nonatomic,readonly) NSMutableArray* sequences;
|
||||
|
||||
|
||||
// Auto play sequence id.
|
||||
@property (nonatomic,assign) int autoPlaySequenceId;
|
||||
|
||||
// Base node.
|
||||
@property (nonatomic,unsafe_unretained) CCNode* rootNode;
|
||||
|
||||
// (CCB) Optional owner
|
||||
@property (nonatomic,unsafe_unretained) id owner;
|
||||
|
||||
// (CCB) Resolution and default container size.
|
||||
@property (nonatomic,assign) CGSize rootContainerSize;
|
||||
|
||||
// (CCB) Node Management
|
||||
- (CGSize) containerSize:(CCNode*)node;
|
||||
- (void) addNode:(CCNode*)node andSequences:(NSDictionary*)seq;
|
||||
- (void) moveAnimationsFromNode:(CCNode*)fromNode toNode:(CCNode*)toNode;
|
||||
|
||||
// Reset node state.
|
||||
- (void) setBaseValue:(id)value forNode:(CCNode*)node propertyName:(NSString*)propName;
|
||||
|
||||
- (void) runAnimationsForSequenceId:(int)seqId tweenDuration:(float) tweenDuration;
|
||||
|
||||
- (void)timeSeekForSequenceId:(int)seqId time:(float)time;
|
||||
|
||||
#pragma mark Simple Sequence Builder
|
||||
- (void)addKeyFramesForSequenceNamed:(NSString*)name propertyType:(CCBSequencePropertyType)propertyType frameArray:(NSArray*)frameArray node:(CCNode *)node loop:(BOOL)loop;
|
||||
|
||||
@end
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
/*
|
||||
* SpriteBuilder: http://www.spritebuilder.org
|
||||
*
|
||||
* Copyright (c) 2012 Zynga Inc.
|
||||
* Copyright (c) 2013 Apportable Inc.
|
||||
*
|
||||
* 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 <Foundation/Foundation.h>
|
||||
#import "cocos2d.h"
|
||||
#import "CCAnimationManager.h"
|
||||
|
||||
__attribute__ ((deprecated))
|
||||
@interface CCBAnimationManager : CCAnimationManager
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* SpriteBuilder: http://www.spritebuilder.org
|
||||
*
|
||||
* Copyright (c) 2012 Zynga Inc.
|
||||
* Copyright (c) 2013 Apportable Inc.
|
||||
*
|
||||
* 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 "CCBAnimationManager.h"
|
||||
|
||||
@implementation CCBAnimationManager
|
||||
|
||||
|
||||
@end
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
* SpriteBuilder: http://www.spritebuilder.org
|
||||
*
|
||||
* Copyright (c) 2012 Zynga Inc.
|
||||
* Copyright (c) 2013 Apportable Inc.
|
||||
*
|
||||
* 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 <Foundation/Foundation.h>
|
||||
|
||||
@class CCActionSequence;
|
||||
|
||||
@interface CCBKeyframe : NSObject
|
||||
|
||||
@property (nonatomic,strong) id value;
|
||||
@property (nonatomic,assign) float time;
|
||||
@property (nonatomic,assign) int easingType;
|
||||
@property (nonatomic,assign) float easingOpt;
|
||||
@property (nonatomic,strong) CCActionSequence* frameActions;
|
||||
|
||||
@end
|
|
@ -1,53 +0,0 @@
|
|||
/*
|
||||
* SpriteBuilder: http://www.spritebuilder.org
|
||||
*
|
||||
* Copyright (c) 2012 Zynga Inc.
|
||||
* Copyright (c) 2013 Apportable Inc.
|
||||
*
|
||||
* 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 "CCBKeyframe.h"
|
||||
|
||||
@implementation CCBKeyframe
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) return NULL;
|
||||
|
||||
// Defaults
|
||||
_value = nil;
|
||||
_time = 0.0f;
|
||||
_easingType = 0;
|
||||
_easingOpt = 0.0f;
|
||||
_frameActions = nil;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *) description {
|
||||
|
||||
NSString *description = [NSString localizedStringWithFormat:@"[CCAnimationKeyframe] value: %@, time: %f, easingType: %d, easingOpt: %f", self.value, self.time,self.easingType,self.easingOpt];
|
||||
|
||||
return description;
|
||||
}
|
||||
|
||||
|
||||
@end
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* SpriteBuilder: http://www.spritebuilder.org
|
||||
*
|
||||
* Copyright (c) 2013 Apportable Inc.
|
||||
*
|
||||
* 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 <Foundation/Foundation.h>
|
||||
|
||||
#define CCBLocalize(key) \
|
||||
[[CCBLocalizationManager sharedManager] localizedStringForKey:(key)]
|
||||
|
||||
@interface CCBLocalizationManager : NSObject
|
||||
{
|
||||
NSMutableDictionary* _translations;
|
||||
}
|
||||
|
||||
@property (nonatomic,readonly) NSDictionary* translations;
|
||||
|
||||
+ (id)sharedManager;
|
||||
|
||||
- (NSString*) localizedStringForKey:(NSString*)key;
|
||||
|
||||
@end
|
|
@ -1,111 +0,0 @@
|
|||
/*
|
||||
* SpriteBuilder: http://www.spritebuilder.org
|
||||
*
|
||||
* Copyright (c) 2013 Apportable Inc.
|
||||
*
|
||||
* 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 "CCBLocalizationManager.h"
|
||||
#import "CCBReader.h"
|
||||
|
||||
@implementation CCBLocalizationManager
|
||||
|
||||
@synthesize translations = _translations;
|
||||
|
||||
+ (id)sharedManager
|
||||
{
|
||||
static dispatch_once_t pred;
|
||||
static CCBLocalizationManager *loc = nil;
|
||||
dispatch_once(&pred, ^{
|
||||
loc = [[self alloc] init];
|
||||
});
|
||||
return loc;
|
||||
}
|
||||
|
||||
- (id) init
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) return NULL;
|
||||
|
||||
[self loadStringsFile:@"Strings.ccbLang"];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) loadStringsFile:(NSString*) file
|
||||
{
|
||||
// Load default localization dictionary
|
||||
NSString* path = [[CCFileUtils sharedFileUtils] fullPathForFilename:file];
|
||||
|
||||
// Load strings file
|
||||
NSDictionary* ser = [NSDictionary dictionaryWithContentsOfFile:path];
|
||||
|
||||
// Check that format of file is correct
|
||||
NSAssert([[ser objectForKey:@"fileType"] isEqualToString:@"SpriteBuilderTranslations"], @"Invalid file format for SpriteBuilder localizations");
|
||||
|
||||
// Check that file version is correct
|
||||
NSAssert([[ser objectForKey:@"fileVersion"] intValue] == 1, @"Translation file version is incompatible with this reader");
|
||||
|
||||
// Load available languages
|
||||
NSArray* languages = [ser objectForKey:@"activeLanguages"];
|
||||
|
||||
// Determine which language to use
|
||||
NSString* userLanguage = NULL;
|
||||
|
||||
NSArray* preferredLangs = [NSLocale preferredLanguages];
|
||||
for (NSString* preferredLang in preferredLangs)
|
||||
{
|
||||
if ([languages containsObject:preferredLang])
|
||||
{
|
||||
userLanguage = preferredLang;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Create dictionary for translations
|
||||
_translations = [[NSMutableDictionary alloc] init];
|
||||
|
||||
// Load translations
|
||||
if (userLanguage != NULL)
|
||||
{
|
||||
NSArray* translations = [ser objectForKey:@"translations"];
|
||||
|
||||
for (NSDictionary* translation in translations)
|
||||
{
|
||||
NSString* key = [translation objectForKey:@"key"];
|
||||
NSString* value = [(NSDictionary*)[translation objectForKey:@"translations"] objectForKey:userLanguage];
|
||||
|
||||
if (key != NULL && value != NULL)
|
||||
{
|
||||
[_translations setObject:value forKey:key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString*) localizedStringForKey:(NSString*)key
|
||||
{
|
||||
NSString* localizedString = [_translations objectForKey:key];
|
||||
if (!localizedString) localizedString = key;
|
||||
return localizedString;
|
||||
}
|
||||
|
||||
|
||||
@end
|
|
@ -1,173 +0,0 @@
|
|||
/*
|
||||
* SpriteBuilder: http://www.spritebuilder.org
|
||||
*
|
||||
* Copyright (c) 2012 Zynga Inc.
|
||||
* Copyright (c) 2013 Apportable Inc.
|
||||
*
|
||||
* 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 <Foundation/Foundation.h>
|
||||
|
||||
#if CCB_SPRITEKIT_READER
|
||||
#import "CCBSpriteKitCompatibility.h"
|
||||
#else
|
||||
#import "cocos2d.h"
|
||||
#endif
|
||||
|
||||
@class CCAnimationManager;
|
||||
|
||||
/**
|
||||
The CCBReader loads node graphs created by SpriteBuilder (or other editors using the same format). If you are using SpriteBuilder it's strongly recommended that you set up the CCFileUtils using the configureCCFileUtils method or use the Xcode project file created by SpriteBuilder.
|
||||
|
||||
Load your SpriteBuilder files using the load: method, if you are loading a scene use the loadAsScene: method, which will wrap the loaded node graph in a CCScene object.
|
||||
|
||||
You can optionally pass an owner object to the CCBReader. If you have assigned member variables in your ccb-files to the owner, they will be set on the owner object when the file is loaded.
|
||||
|
||||
When all loading is complete, the didLoadFromCCB method will be called on all loaded nodes (if it has been implemented).
|
||||
|
||||
If you are using animations a CCAnimationManager will be assigned to all ccb-file root node's animationManager property. The top CCAnimationManager is also assigned to the CCBReader's animationManager property.
|
||||
*/
|
||||
@interface CCBReader : NSObject
|
||||
{
|
||||
NSData* data;
|
||||
unsigned char* bytes;
|
||||
int currentByte;
|
||||
int currentBit;
|
||||
|
||||
NSMutableArray* stringCache;
|
||||
NSMutableSet* loadedSpriteSheets;
|
||||
|
||||
id owner;
|
||||
|
||||
CCAnimationManager* animationManager;
|
||||
NSMutableDictionary* actionManagers;
|
||||
NSMutableSet* animatedProps;
|
||||
NSMutableDictionary* nodeMapping;//Maps UUID -> Node
|
||||
}
|
||||
|
||||
/// -----------------------------------------------------------------------
|
||||
/// @name Setup
|
||||
/// -----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Call this method to configure the CCFileUtils to work correctly with SpriteBuilder. It will setup search paths for the resources to use with the current device and resolution. It assumes that the SpriteBuilder resources has been published to a directory named Published-iOS that has been added as a blue folder in Xcode.
|
||||
*/
|
||||
+ (void) configureCCFileUtils;
|
||||
|
||||
/// -----------------------------------------------------------------------
|
||||
/// @name Instantiation
|
||||
/// -----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Creates a new CCBReader.
|
||||
*
|
||||
* @return A new CCBReader.
|
||||
*/
|
||||
+ (CCBReader*) reader;
|
||||
|
||||
/// -----------------------------------------------------------------------
|
||||
/// @name Loading Files
|
||||
/// -----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Loads a ccbi-file with the specified name. Using the extension is optional, e.g. both MyNodeGraph and MyNodeGraph.ccbi will work.
|
||||
*
|
||||
* @param file Name of the file to load.
|
||||
*
|
||||
* @return The loaded node graph.
|
||||
*/
|
||||
- (CCNode*) load:(NSString*) file;
|
||||
|
||||
/**
|
||||
* Loads a ccbi-file with the specified name and owner. Using the extension is optional, e.g. both MyNodeGraph and MyNodeGraph.ccbi will work.
|
||||
*
|
||||
* @param file Name of the file to load.
|
||||
* @param owner The owner object used to load the file.
|
||||
*
|
||||
* @return The loaded node graph.
|
||||
*/
|
||||
- (CCNode*) load:(NSString*) file owner:(id)owner;
|
||||
|
||||
/**
|
||||
* Loads a ccbi-file from the provided NSData object. This method is useful if you load ccbi-files from the internet. If you are not using the owner variable, pass NULL.
|
||||
*
|
||||
* @param data Data object to load the ccbi-file from.
|
||||
* @param owner The owner object used to load the file, or NULL if not used.
|
||||
*
|
||||
* @return The loaded node graph.
|
||||
*/
|
||||
- (CCNode*) loadWithData:(NSData*) data owner:(id)owner;
|
||||
|
||||
/**
|
||||
* Loads a ccbi-file with the specified name. Using the extension is optional, e.g. both MyNodeGraph and MyNodeGraph.ccbi will work.
|
||||
*
|
||||
* @param file Name of the file to load.
|
||||
*
|
||||
* @return The loaded node graph.
|
||||
*/
|
||||
+ (CCNode*) load:(NSString*) file;
|
||||
|
||||
/**
|
||||
* Loads a ccbi-file with the specified name and owner. Using the extension is optional, e.g. both MyNodeGraph and MyNodeGraph.ccbi will work.
|
||||
*
|
||||
* @param file Name of the file to load.
|
||||
* @param owner The owner object used to load the file.
|
||||
*
|
||||
* @return The loaded node graph.
|
||||
*/
|
||||
+ (CCNode*) load:(NSString*) file owner:(id)owner;
|
||||
|
||||
/**
|
||||
* Loads a ccbi-file with the specified name and wraps it in a CCScene node. Using the extension is optional, e.g. both MyNodeGraph and MyNodeGraph.ccbi will work.
|
||||
*
|
||||
* @param file Name of the file to load.
|
||||
*
|
||||
* @return The loaded node graph.
|
||||
*/
|
||||
+ (CCScene*) loadAsScene:(NSString*) file;
|
||||
|
||||
/**
|
||||
* Loads a ccbi-file with the specified name and owner and wraps it in a CCScene node. Using the extension is optional, e.g. both MyNodeGraph and MyNodeGraph.ccbi will work.
|
||||
*
|
||||
* @param file Name of the file to load.
|
||||
* @param owner The owner object used to load the file.
|
||||
*
|
||||
* @return The loaded node graph.
|
||||
*/
|
||||
+ (CCScene*) loadAsScene:(NSString *)file owner:(id)owner;
|
||||
|
||||
/// -----------------------------------------------------------------------
|
||||
/// @name Animations
|
||||
/// -----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Once a ccb-file has been loaded, the animationManager property will be set to contain the top level CCAnimationManager
|
||||
*/
|
||||
@property (nonatomic,strong) CCAnimationManager* animationManager;
|
||||
|
||||
|
||||
// Internal use: override methods for Sprite Kit Reader subclass
|
||||
+(void) setSceneSize:(CGSize)sceneSize;
|
||||
-(CCNode*) nodeFromClassName:(NSString*)nodeClassName;
|
||||
-(CCScene*) createScene;
|
||||
-(void) readerDidSetSpriteFrame:(CCSpriteFrame*)spriteFrame node:(CCNode*)node;
|
||||
|
||||
@end
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -1,94 +0,0 @@
|
|||
//
|
||||
// CCBReader_Private.h
|
||||
// cocos2d-ios
|
||||
//
|
||||
// Created by Viktor on 11/13/13.
|
||||
//
|
||||
//
|
||||
|
||||
#import "CCBReader.h"
|
||||
|
||||
#define kCCBVersion 10
|
||||
|
||||
enum {
|
||||
kCCBPropTypePosition = 0,
|
||||
kCCBPropTypeSize,
|
||||
kCCBPropTypePoint,
|
||||
kCCBPropTypePointLock,
|
||||
kCCBPropTypeScaleLock,
|
||||
kCCBPropTypeDegrees,
|
||||
kCCBPropTypeInteger,
|
||||
kCCBPropTypeFloat,
|
||||
kCCBPropTypeFloatVar,
|
||||
kCCBPropTypeCheck,
|
||||
kCCBPropTypeSpriteFrame,
|
||||
kCCBPropTypeTexture,
|
||||
kCCBPropTypeByte,
|
||||
kCCBPropTypeColor3,
|
||||
kCCBPropTypeColor4FVar,
|
||||
kCCBPropTypeFlip,
|
||||
kCCBPropTypeBlendmode,
|
||||
kCCBPropTypeFntFile,
|
||||
kCCBPropTypeText,
|
||||
kCCBPropTypeFontTTF,
|
||||
kCCBPropTypeIntegerLabeled,
|
||||
kCCBPropTypeBlock,
|
||||
kCCBPropTypeAnimation,
|
||||
kCCBPropTypeCCBFile,
|
||||
kCCBPropTypeString,
|
||||
kCCBPropTypeBlockCCControl,
|
||||
kCCBPropTypeFloatScale,
|
||||
kCCBPropTypeFloatXY,
|
||||
kCCBPropTypeColor4,
|
||||
kCCBPropTypeNodeReference,
|
||||
kCCBPropTypeFloatCheck,
|
||||
|
||||
};
|
||||
|
||||
enum {
|
||||
kCCBFloat0 = 0,
|
||||
kCCBFloat1,
|
||||
kCCBFloatMinus1,
|
||||
kCCBFloat05,
|
||||
kCCBFloatInteger,
|
||||
kCCBFloatFull
|
||||
};
|
||||
|
||||
enum {
|
||||
kCCBPlatformAll = 0,
|
||||
kCCBPlatformIOS,
|
||||
kCCBPlatformMac
|
||||
};
|
||||
|
||||
enum {
|
||||
kCCBTargetTypeNone = 0,
|
||||
kCCBTargetTypeDocumentRoot = 1,
|
||||
kCCBTargetTypeOwner = 2,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
kCCBKeyframeEasingInstant,
|
||||
|
||||
kCCBKeyframeEasingLinear,
|
||||
|
||||
kCCBKeyframeEasingCubicIn,
|
||||
kCCBKeyframeEasingCubicOut,
|
||||
kCCBKeyframeEasingCubicInOut,
|
||||
|
||||
kCCBKeyframeEasingElasticIn,
|
||||
kCCBKeyframeEasingElasticOut,
|
||||
kCCBKeyframeEasingElasticInOut,
|
||||
|
||||
kCCBKeyframeEasingBounceIn,
|
||||
kCCBKeyframeEasingBounceOut,
|
||||
kCCBKeyframeEasingBounceInOut,
|
||||
|
||||
kCCBKeyframeEasingBackIn,
|
||||
kCCBKeyframeEasingBackOut,
|
||||
kCCBKeyframeEasingBackInOut,
|
||||
};
|
||||
|
||||
@interface CCBReader ()
|
||||
|
||||
@end
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* SpriteBuilder: http://www.spritebuilder.org
|
||||
*
|
||||
* Copyright (c) 2012 Zynga Inc.
|
||||
* Copyright (c) 2013 Apportable Inc.
|
||||
*
|
||||
* 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 <Foundation/Foundation.h>
|
||||
|
||||
@class CCBSequenceProperty;
|
||||
|
||||
@interface CCBSequence : NSObject
|
||||
|
||||
@property (nonatomic,assign) float duration;
|
||||
@property (nonatomic,assign) float time;
|
||||
@property (nonatomic,copy) NSString* name;
|
||||
@property (nonatomic,assign) int sequenceId;
|
||||
@property (nonatomic,assign) int chainedSequenceId;
|
||||
@property (nonatomic,assign) int loops; // @todo
|
||||
|
||||
// Channels
|
||||
@property (nonatomic,strong) CCBSequenceProperty* callbackChannel;
|
||||
@property (nonatomic,strong) CCBSequenceProperty* soundChannel;
|
||||
|
||||
@end
|
|
@ -1,40 +0,0 @@
|
|||
/*
|
||||
* SpriteBuilder: http://www.spritebuilder.org
|
||||
*
|
||||
* Copyright (c) 2012 Zynga Inc.
|
||||
* Copyright (c) 2013 Apportable Inc.
|
||||
*
|
||||
* 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 <Foundation/Foundation.h>
|
||||
#import "CCBReader_Private.h"
|
||||
|
||||
typedef NS_ENUM(NSInteger, CCBSequencePropertyType) {
|
||||
CCBSequencePropertyTypeSpriteFrame = kCCBPropTypeSpriteFrame
|
||||
};
|
||||
|
||||
@interface CCBSequenceProperty : NSObject
|
||||
|
||||
@property (nonatomic,strong) NSString* name;
|
||||
@property (nonatomic,assign) int type;
|
||||
@property (nonatomic,readonly) NSMutableArray* keyframes;
|
||||
|
||||
+ (NSString *)getPropertyNameFromTypeId:(CCBSequencePropertyType)propertyType;
|
||||
@end
|
|
@ -1,59 +0,0 @@
|
|||
/*
|
||||
* SpriteBuilder: http://www.spritebuilder.org
|
||||
*
|
||||
* Copyright (c) 2012 Zynga Inc.
|
||||
* Copyright (c) 2013 Apportable Inc.
|
||||
*
|
||||
* 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 "CCBSequenceProperty.h"
|
||||
|
||||
@implementation CCBSequenceProperty
|
||||
|
||||
- (id) init
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) return NULL;
|
||||
|
||||
_name = @"";
|
||||
_type = 0;
|
||||
_keyframes = [[NSMutableArray alloc] init];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
NSString *description = [NSString localizedStringWithFormat:@"[CCAnimationSequenceProperty] Name: %@, Type: %d, Keyframes: %@", self.name, self.type, self.keyframes];
|
||||
|
||||
return description;
|
||||
}
|
||||
|
||||
+ (NSString *)getPropertyNameFromTypeId:(CCBSequencePropertyType)propertyType {
|
||||
|
||||
switch(propertyType) {
|
||||
case CCBSequencePropertyTypeSpriteFrame:
|
||||
return @"spriteFrame";
|
||||
break;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* SpriteBuilder: http://www.spritebuilder.org
|
||||
*
|
||||
* Copyright (c) 2012 Zynga Inc.
|
||||
* Copyright (c) 2013 Apportable Inc.
|
||||
*
|
||||
* 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 "CCBSequence.h"
|
||||
|
||||
@implementation CCBSequence
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) return NULL;
|
||||
|
||||
// Defaults
|
||||
_duration = 0.0f;
|
||||
_time = 0.0f;
|
||||
_name = @"";
|
||||
_sequenceId = -1;
|
||||
_chainedSequenceId = -1;
|
||||
_loops = 0;
|
||||
|
||||
_callbackChannel = nil;
|
||||
_soundChannel = nil;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
@end
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* CocosBuilder: http://www.spritebuilder.org
|
||||
*
|
||||
* Copyright (c) 2012 Zynga Inc.
|
||||
* Copyright (c) 2013 Apportable Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// Unless this macro is defined in Build Settings CCBReader will assume it is working with cocos2d-iphone
|
||||
#ifndef CCB_SPRITEKIT_READER
|
||||
#define CCB_SPRITEKIT_READER 0
|
||||
#endif
|
||||
|
||||
#import "CCBReader.h"
|
||||
#import "CCBAnimationManager.h"
|
||||
#import "CCAnimationManager.h"
|
||||
#import "CCBLocalizationManager.h"
|
|
@ -1,245 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef CHIPMUNK_H
|
||||
#define CHIPMUNK_H
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define _USE_MATH_DEFINES
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
#ifdef __WIN32__
|
||||
// For alloca().
|
||||
#include <malloc.h>
|
||||
#else
|
||||
#include <alloca.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// NUKE
|
||||
#ifndef CP_ALLOW_PRIVATE_ACCESS
|
||||
#define CP_ALLOW_PRIVATE_ACCESS 0
|
||||
#endif
|
||||
|
||||
#if CP_ALLOW_PRIVATE_ACCESS == 1
|
||||
#define CP_PRIVATE(__symbol__) __symbol__
|
||||
#else
|
||||
#define CP_PRIVATE(__symbol__) __symbol__##_private
|
||||
#endif
|
||||
|
||||
void cpMessage(const char *condition, const char *file, int line, int isError, int isHardError, const char *message, ...);
|
||||
#ifdef NDEBUG
|
||||
#define cpAssertWarn(__condition__, ...)
|
||||
#else
|
||||
#define cpAssertWarn(__condition__, ...) if(!(__condition__)) cpMessage(#__condition__, __FILE__, __LINE__, 0, 0, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define cpAssertSoft(__condition__, ...)
|
||||
#else
|
||||
#define cpAssertSoft(__condition__, ...) if(!(__condition__)){cpMessage(#__condition__, __FILE__, __LINE__, 1, 0, __VA_ARGS__), abort();}
|
||||
#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 "cpBody.h"
|
||||
#include "cpShape.h"
|
||||
#include "cpPolyShape.h"
|
||||
|
||||
#include "cpArbiter.h"
|
||||
#include "cpConstraint.h"
|
||||
|
||||
#include "cpSpace.h"
|
||||
|
||||
// Chipmunk 7.0.0
|
||||
#define CP_VERSION_MAJOR 7
|
||||
#define CP_VERSION_MINOR 0
|
||||
#define CP_VERSION_RELEASE 0
|
||||
|
||||
/// Version string.
|
||||
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.
|
||||
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.
|
||||
cpFloat cpAreaForCircle(cpFloat r1, cpFloat r2);
|
||||
|
||||
/// Calculate the moment of inertia for a line segment.
|
||||
/// Beveling radius is not supported.
|
||||
cpFloat cpMomentForSegment(cpFloat m, cpVect a, cpVect b, cpFloat radius);
|
||||
|
||||
/// Calculate the area of a fattened (capsule shaped) line segment.
|
||||
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.
|
||||
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.
|
||||
cpFloat cpAreaForPoly(const int count, const cpVect *verts, cpFloat radius);
|
||||
|
||||
/// Calculate the natural centroid of a polygon.
|
||||
cpVect cpCentroidForPoly(const int count, const cpVect *verts);
|
||||
|
||||
/// Calculate the moment of inertia for a solid box.
|
||||
cpFloat cpMomentForBox(cpFloat m, cpFloat width, cpFloat height);
|
||||
|
||||
/// Calculate the moment of inertia for a solid box.
|
||||
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.
|
||||
int cpConvexHull(int count, const cpVect *verts, cpVect *result, int *first, cpFloat tol);
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include "malloc.h"
|
||||
#endif
|
||||
|
||||
/// 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
|
|
@ -1,100 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifdef CHIPMUNK_FFI
|
||||
|
||||
// Create non static inlined copies of Chipmunk functions, useful for working with dynamic FFIs
|
||||
// This file should only be included in chipmunk.c
|
||||
|
||||
// TODO: get rid of the reliance on static inlines.
|
||||
// They make a mess for FFIs.
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#if _MSC_VER >= 1600
|
||||
#define MAKE_REF(name) decltype(name) *_##name = name
|
||||
#else
|
||||
#define MAKE_REF(name)
|
||||
#endif
|
||||
#else
|
||||
#define MAKE_REF(name) __typeof__(name) *_##name = name
|
||||
#endif
|
||||
|
||||
#define MAKE_PROPERTIES_REF(struct, property) \
|
||||
MAKE_REF(struct##Get##property); MAKE_REF(struct##Set##property)
|
||||
|
||||
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(cpBBNewForCircle);
|
||||
MAKE_REF(cpBBIntersects);
|
||||
MAKE_REF(cpBBContainsBB);
|
||||
MAKE_REF(cpBBContainsVect);
|
||||
MAKE_REF(cpBBMerge);
|
||||
MAKE_REF(cpBBExpand);
|
||||
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);
|
||||
|
||||
#endif
|
|
@ -1,765 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifdef CHIPMUNK_H
|
||||
#error Cannot include chipmunk_private.h after chipmunk.h.
|
||||
#endif
|
||||
|
||||
#ifndef CHIPMUNK_PRIVATE_H
|
||||
#define CHIPMUNK_PRIVATE_H
|
||||
|
||||
#define CP_ALLOW_PRIVATE_ACCESS 1
|
||||
#include "chipmunk.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
|
||||
|
||||
struct cpArray {
|
||||
int num, max;
|
||||
void **arr;
|
||||
};
|
||||
|
||||
cpArray *cpArrayNew(int size);
|
||||
|
||||
void cpArrayFree(cpArray *arr);
|
||||
|
||||
void cpArrayPush(cpArray *arr, void *object);
|
||||
void *cpArrayPop(cpArray *arr);
|
||||
void cpArrayDeleteObj(cpArray *arr, void *obj);
|
||||
cpBool cpArrayContains(cpArray *arr, void *ptr);
|
||||
|
||||
void cpArrayFreeEach(cpArray *arr, void (freeFunc)(void*));
|
||||
|
||||
|
||||
//MARK: cpHashSet
|
||||
|
||||
typedef cpBool (*cpHashSetEqlFunc)(void *ptr, void *elt);
|
||||
typedef void *(*cpHashSetTransFunc)(void *ptr, void *data);
|
||||
|
||||
cpHashSet *cpHashSetNew(int size, cpHashSetEqlFunc eqlFunc);
|
||||
void cpHashSetSetDefaultValue(cpHashSet *set, void *default_value);
|
||||
|
||||
void cpHashSetFree(cpHashSet *set);
|
||||
|
||||
int cpHashSetCount(cpHashSet *set);
|
||||
void *cpHashSetInsert(cpHashSet *set, cpHashValue hash, void *ptr, cpHashSetTransFunc trans, void *data);
|
||||
void *cpHashSetRemove(cpHashSet *set, cpHashValue hash, void *ptr);
|
||||
void *cpHashSetFind(cpHashSet *set, cpHashValue hash, void *ptr);
|
||||
|
||||
typedef void (*cpHashSetIteratorFunc)(void *elt, void *data);
|
||||
void cpHashSetEach(cpHashSet *set, cpHashSetIteratorFunc func, void *data);
|
||||
|
||||
typedef cpBool (*cpHashSetFilterFunc)(void *elt, void *data);
|
||||
void cpHashSetFilter(cpHashSet *set, cpHashSetFilterFunc func, void *data);
|
||||
|
||||
|
||||
//MARK: Bodies
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
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
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
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
|
||||
|
||||
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];
|
||||
};
|
||||
|
||||
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
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
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->CP_PRIVATE(v), cpvmult(cpvperp(r1), a->CP_PRIVATE(w)));
|
||||
cpVect v2_sum = cpvadd(b->CP_PRIVATE(v), cpvmult(cpvperp(r2), b->CP_PRIVATE(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->CP_PRIVATE(v) = cpvadd(body->CP_PRIVATE(v), cpvmult(j, body->CP_PRIVATE(m_inv)));
|
||||
body->CP_PRIVATE(w) += body->CP_PRIVATE(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->CP_PRIVATE(v_bias) = cpvadd(body->CP_PRIVATE(v_bias), cpvmult(j, body->CP_PRIVATE(m_inv)));
|
||||
body->CP_PRIVATE(w_bias) += body->CP_PRIVATE(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->CP_PRIVATE(m_inv) + body->CP_PRIVATE(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->CP_PRIVATE(m_inv) + b->CP_PRIVATE(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->CP_PRIVATE(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->CP_PRIVATE(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
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
#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);
|
||||
|
||||
typedef struct cpPostStepCallback {
|
||||
cpPostStepFunc func;
|
||||
void *key;
|
||||
void *data;
|
||||
} cpPostStepCallback;
|
||||
|
||||
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
|
|
@ -1,269 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef CHIPMUNK_TYPES_H
|
||||
#define CHIPMUNK_TYPES_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
|
||||
#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
|
||||
#import <CoreGraphics/CGGeometry.h>
|
||||
#import <CoreGraphics/CGAffineTransform.h>
|
||||
#elif TARGET_OS_MAC
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#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
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846264338327950288
|
||||
#endif
|
||||
|
||||
#ifndef M_E
|
||||
#define M_E 2.71828182845904523536028747135266250
|
||||
#endif
|
||||
|
||||
|
||||
/// 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.
|
||||
typedef uintptr_t cpHashValue;
|
||||
|
||||
/// 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 cpLayers;
|
||||
#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
|
|
@ -1,66 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/* This header defines a number of "unsafe" operations on Chipmunk objects.
|
||||
* In this case "unsafe" is referring to operations which may reduce the
|
||||
* physical accuracy or numerical stability of the simulation, but will not
|
||||
* cause crashes.
|
||||
*
|
||||
* The prime example is mutating collision shapes. Chipmunk does not support
|
||||
* this directly. Mutating shapes using this API will caused objects in contact
|
||||
* to be pushed apart using Chipmunk's overlap solver, but not using real
|
||||
* persistent velocities. Probably not what you meant, but perhaps close enough.
|
||||
*/
|
||||
|
||||
/// @defgroup unsafe Chipmunk Unsafe Shape Operations
|
||||
/// These functions are used for mutating collision shapes.
|
||||
/// Chipmunk does not have any way to get velocity information on changing shapes,
|
||||
/// so the results will be unrealistic. You must explicity include the chipmunk_unsafe.h header to use them.
|
||||
/// @{
|
||||
|
||||
#ifndef CHIPMUNK_UNSAFE_H
|
||||
#define CHIPMUNK_UNSAFE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// Set the radius of a circle shape.
|
||||
void cpCircleShapeSetRadius(cpShape *shape, cpFloat radius);
|
||||
/// Set the offset of a circle shape.
|
||||
void cpCircleShapeSetOffset(cpShape *shape, cpVect offset);
|
||||
|
||||
/// Set the endpoints of a segment shape.
|
||||
void cpSegmentShapeSetEndpoints(cpShape *shape, cpVect a, cpVect b);
|
||||
/// Set the radius of a segment shape.
|
||||
void cpSegmentShapeSetRadius(cpShape *shape, cpFloat radius);
|
||||
|
||||
/// Set the vertexes of a poly shape.
|
||||
void cpPolyShapeSetVerts(cpShape *shape, int count, cpVect *verts, cpTransform transform);
|
||||
void cpPolyShapeSetVertsRaw(cpShape *shape, int count, cpVect *verts);
|
||||
/// Set the radius of a poly shape.
|
||||
void cpPolyShapeSetRadius(cpShape *shape, cpFloat radius);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
/// @}
|
|
@ -1,126 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/// @defgroup cpArbiter cpArbiter
|
||||
/// The cpArbiter struct controls pairs of colliding shapes.
|
||||
/// They are also used in conjuction with collision handler callbacks
|
||||
/// allowing you to retrieve information on the collision and control it.
|
||||
/// @{
|
||||
|
||||
#define CP_MAX_CONTACTS_PER_ARBITER 2
|
||||
|
||||
// TODO: Document
|
||||
cpFloat cpArbiterGetRestitution(const cpArbiter *arb);
|
||||
void cpArbiterSetRestitution(cpArbiter *arb, cpFloat restitution);
|
||||
cpFloat cpArbiterGetFriction(const cpArbiter *arb);
|
||||
void cpArbiterSetFriction(cpArbiter *arb, cpFloat friction);
|
||||
|
||||
// Get the relative surface velocity of the two shapes in contact.
|
||||
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.
|
||||
void cpArbiterSetSurfaceVelocity(cpArbiter *arb, cpVect vr);
|
||||
|
||||
cpDataPointer cpArbiterGetUserData(const cpArbiter *arb);
|
||||
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.
|
||||
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.
|
||||
cpFloat cpArbiterTotalKE(const cpArbiter *arb);
|
||||
|
||||
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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!
|
||||
void cpArbiterSetContactPointSet(cpArbiter *arb, cpContactPointSet *set);
|
||||
|
||||
/// Returns true if this is the first step a pair of objects started colliding.
|
||||
cpBool cpArbiterIsFirstContact(const cpArbiter *arb);
|
||||
/// Returns true if in separate callback due to a shape being removed from the space.
|
||||
cpBool cpArbiterIsRemoval(const cpArbiter *arb);
|
||||
|
||||
/// Get the number of contact points for this arbiter.
|
||||
int cpArbiterGetCount(const cpArbiter *arb);
|
||||
/// Get the normal of the collision.
|
||||
cpVect cpArbiterGetNormal(const cpArbiter *arb);
|
||||
/// Get the position of the @c ith contact point on the surface of the first shape.
|
||||
cpVect cpArbiterGetPointA(const cpArbiter *arb, int i);
|
||||
/// Get the position of the @c ith contact point on the surface of the second shape.
|
||||
cpVect cpArbiterGetPointB(const cpArbiter *arb, int i);
|
||||
/// Get the depth of the @c ith contact point.
|
||||
cpFloat cpArbiterGetDepth(const cpArbiter *arb, int i);
|
||||
|
||||
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);
|
||||
|
||||
/// @}
|
|
@ -1,169 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef CHIPMUNK_BB_H
|
||||
#define CHIPMUNK_BB_H
|
||||
|
||||
#include "chipmunk_types.h"
|
||||
#include "cpVect.h"
|
||||
|
||||
/// @defgroup cpBBB cpBB
|
||||
/// Chipmunk's axis-aligned 2D bounding box type along with a few handy routines.
|
||||
/// @{
|
||||
|
||||
/// Chipmunk's axis-aligned 2D bounding box type. (left, bottom, right, top)
|
||||
typedef struct cpBB{
|
||||
cpFloat l, b, r ,t;
|
||||
} cpBB;
|
||||
|
||||
/// Convenience constructor for cpBB structs.
|
||||
static inline cpBB cpBBNew(const cpFloat l, const cpFloat b, const cpFloat r, const cpFloat t)
|
||||
{
|
||||
cpBB bb = {l, b, r, t};
|
||||
return bb;
|
||||
}
|
||||
|
||||
/// Constructs a cpBB centered on a point with the given extents (half sizes).
|
||||
static inline cpBB
|
||||
cpBBNewForExtents(const cpVect c, const cpFloat hw, const cpFloat hh)
|
||||
{
|
||||
return cpBBNew(c.x - hw, c.y - hh, c.x + hw, c.y + hh);
|
||||
}
|
||||
|
||||
/// Constructs a cpBB for a circle with the given position and radius.
|
||||
static inline cpBB cpBBNewForCircle(const cpVect p, const cpFloat r)
|
||||
{
|
||||
return cpBBNewForExtents(p, r, r);
|
||||
}
|
||||
|
||||
/// Returns true if @c a and @c b intersect.
|
||||
static inline cpBool cpBBIntersects(const cpBB a, const cpBB b)
|
||||
{
|
||||
return (a.l <= b.r && b.l <= a.r && a.b <= b.t && b.b <= a.t);
|
||||
}
|
||||
|
||||
/// Returns true if @c other lies completely within @c bb.
|
||||
static inline cpBool cpBBContainsBB(const cpBB bb, const cpBB other)
|
||||
{
|
||||
return (bb.l <= other.l && bb.r >= other.r && bb.b <= other.b && bb.t >= other.t);
|
||||
}
|
||||
|
||||
/// Returns true if @c bb contains @c v.
|
||||
static inline cpBool cpBBContainsVect(const cpBB bb, const cpVect v)
|
||||
{
|
||||
return (bb.l <= v.x && bb.r >= v.x && bb.b <= v.y && bb.t >= v.y);
|
||||
}
|
||||
|
||||
/// Returns a bounding box that holds both bounding boxes.
|
||||
static inline cpBB cpBBMerge(const cpBB a, const cpBB b){
|
||||
return cpBBNew(
|
||||
cpfmin(a.l, b.l),
|
||||
cpfmin(a.b, b.b),
|
||||
cpfmax(a.r, b.r),
|
||||
cpfmax(a.t, b.t)
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns a bounding box that holds both @c bb and @c v.
|
||||
static inline cpBB cpBBExpand(const cpBB bb, const cpVect v){
|
||||
return cpBBNew(
|
||||
cpfmin(bb.l, v.x),
|
||||
cpfmin(bb.b, v.y),
|
||||
cpfmax(bb.r, v.x),
|
||||
cpfmax(bb.t, v.y)
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns the center of a bounding box.
|
||||
static inline cpVect
|
||||
cpBBCenter(cpBB bb)
|
||||
{
|
||||
return cpvlerp(cpv(bb.l, bb.b), cpv(bb.r, bb.t), 0.5f);
|
||||
}
|
||||
|
||||
/// Returns the area of the bounding box.
|
||||
static inline cpFloat cpBBArea(cpBB bb)
|
||||
{
|
||||
return (bb.r - bb.l)*(bb.t - bb.b);
|
||||
}
|
||||
|
||||
/// Merges @c a and @c b and returns the area of the merged bounding box.
|
||||
static inline cpFloat cpBBMergedArea(cpBB a, cpBB b)
|
||||
{
|
||||
return (cpfmax(a.r, b.r) - cpfmin(a.l, b.l))*(cpfmax(a.t, b.t) - cpfmin(a.b, b.b));
|
||||
}
|
||||
|
||||
/// Returns the fraction along the segment query the cpBB is hit. Returns INFINITY if it doesn't hit.
|
||||
static inline cpFloat cpBBSegmentQuery(cpBB bb, cpVect a, cpVect b)
|
||||
{
|
||||
cpFloat idx = 1.0f/(b.x - a.x);
|
||||
cpFloat tx1 = (bb.l == a.x ? -INFINITY : (bb.l - a.x)*idx);
|
||||
cpFloat tx2 = (bb.r == a.x ? INFINITY : (bb.r - a.x)*idx);
|
||||
cpFloat txmin = cpfmin(tx1, tx2);
|
||||
cpFloat txmax = cpfmax(tx1, tx2);
|
||||
|
||||
cpFloat idy = 1.0f/(b.y - a.y);
|
||||
cpFloat ty1 = (bb.b == a.y ? -INFINITY : (bb.b - a.y)*idy);
|
||||
cpFloat ty2 = (bb.t == a.y ? INFINITY : (bb.t - a.y)*idy);
|
||||
cpFloat tymin = cpfmin(ty1, ty2);
|
||||
cpFloat tymax = cpfmax(ty1, ty2);
|
||||
|
||||
if(tymin <= txmax && txmin <= tymax){
|
||||
cpFloat min = cpfmax(txmin, tymin);
|
||||
cpFloat max = cpfmin(txmax, tymax);
|
||||
|
||||
if(0.0 <= max && min <= 1.0) return cpfmax(min, 0.0);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
///@}
|
||||
|
||||
#endif
|
|
@ -1,180 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/// @defgroup cpBody cpBody
|
||||
/// Chipmunk's rigid body type. Rigid bodies hold the physical properties of an object like
|
||||
/// it's mass, and position and velocity of it's center of gravity. They don't have an shape on their own.
|
||||
/// They are given a shape by creating collision shapes (cpShape) that point to the body.
|
||||
/// @{
|
||||
|
||||
typedef enum cpBodyType {
|
||||
CP_BODY_TYPE_DYNAMIC,
|
||||
CP_BODY_TYPE_KINEMATIC,
|
||||
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.
|
||||
cpBody* cpBodyAlloc(void);
|
||||
/// Initialize a cpBody.
|
||||
cpBody* cpBodyInit(cpBody *body, cpFloat mass, cpFloat moment);
|
||||
/// Allocate and initialize a cpBody.
|
||||
cpBody* cpBodyNew(cpFloat mass, cpFloat moment);
|
||||
|
||||
/// Allocate and initialize a cpBody, and set it as a kinematic body.
|
||||
cpBody* cpBodyNewKinematic(void);
|
||||
/// Allocate and initialize a cpBody, and set it as a static body.
|
||||
cpBody* cpBodyNewStatic(void);
|
||||
|
||||
/// Destroy a cpBody.
|
||||
void cpBodyDestroy(cpBody *body);
|
||||
/// Destroy and free a cpBody.
|
||||
void cpBodyFree(cpBody *body);
|
||||
|
||||
// Defined in cpSpace.c
|
||||
/// Wake up a sleeping or idle body.
|
||||
void cpBodyActivate(cpBody *body);
|
||||
/// Wake up any sleeping or idle bodies touching a static body.
|
||||
void cpBodyActivateStatic(cpBody *body, cpShape *filter);
|
||||
|
||||
/// Force a body to fall asleep immediately.
|
||||
void cpBodySleep(cpBody *body);
|
||||
/// Force a body to fall asleep immediately along with other bodies in a group.
|
||||
void cpBodySleepWithGroup(cpBody *body, cpBody *group);
|
||||
|
||||
/// Returns true if the body is sleeping.
|
||||
cpBool cpBodyIsSleeping(const cpBody *body);
|
||||
|
||||
/// Get the type of the body.
|
||||
cpBodyType cpBodyGetType(cpBody *body);
|
||||
/// Set the type of the body.
|
||||
void cpBodySetType(cpBody *body, cpBodyType type);
|
||||
|
||||
/// Get the space this body is added to.
|
||||
cpSpace* cpBodyGetSpace(const cpBody *body);
|
||||
|
||||
/// Get the mass of the body.
|
||||
cpFloat cpBodyGetMass(const cpBody *body);
|
||||
/// Set the mass of the body.
|
||||
void cpBodySetMass(cpBody *body, cpFloat m);
|
||||
|
||||
/// Get the moment of inertia of the body.
|
||||
cpFloat cpBodyGetMoment(const cpBody *body);
|
||||
/// Set the moment of inertia of the body.
|
||||
void cpBodySetMoment(cpBody *body, cpFloat i);
|
||||
|
||||
/// Set the position of a body.
|
||||
cpVect cpBodyGetPosition(const cpBody *body);
|
||||
/// Set the position of the body.
|
||||
void cpBodySetPosition(cpBody *body, cpVect pos);
|
||||
|
||||
/// Get the offset of the center of gravity in body local coordinates.
|
||||
cpVect cpBodyGetCenterOfGravity(const cpBody *body);
|
||||
/// Set the offset of the center of gravity in body local coordinates.
|
||||
void cpBodySetCenterOfGravity(cpBody *body, cpVect cog);
|
||||
|
||||
/// Get the velocity of the body.
|
||||
cpVect cpBodyGetVelocity(const cpBody *body);
|
||||
/// Set the velocity of the body.
|
||||
void cpBodySetVelocity(cpBody *body, cpVect velocity);
|
||||
|
||||
/// Get the force applied to the body for the next time step.
|
||||
cpVect cpBodyGetForce(const cpBody *body);
|
||||
/// Set the force applied to the body for the next time step.
|
||||
void cpBodySetForce(cpBody *body, cpVect force);
|
||||
|
||||
/// Get the angle of the body.
|
||||
cpFloat cpBodyGetAngle(const cpBody *body);
|
||||
/// Set the angle of a body.
|
||||
void cpBodySetAngle(cpBody *body, cpFloat a);
|
||||
|
||||
/// Get the angular velocity of the body.
|
||||
cpFloat cpBodyGetAngularVelocity(const cpBody *body);
|
||||
/// Set the angular velocity of the body.
|
||||
void cpBodySetAngularVelocity(cpBody *body, cpFloat angularVelocity);
|
||||
|
||||
/// Get the torque applied to the body for the next time step.
|
||||
cpFloat cpBodyGetTorque(const cpBody *body);
|
||||
/// Set the torque applied to the body for the next time step.
|
||||
void cpBodySetTorque(cpBody *body, cpFloat torque);
|
||||
|
||||
/// Get the rotation vector of the body. (The x basis vector of it's transform.)
|
||||
cpVect cpBodyGetRotation(const cpBody *body);
|
||||
|
||||
/// Get the user data pointer assigned to the body.
|
||||
cpDataPointer cpBodyGetUserData(const cpBody *body);
|
||||
/// Set the user data pointer assigned to the body.
|
||||
void cpBodySetUserData(cpBody *body, cpDataPointer userData);
|
||||
|
||||
/// Set the callback used to update a body's velocity.
|
||||
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.
|
||||
void cpBodySetPositionUpdateFunc(cpBody *body, cpBodyPositionFunc positionFunc);
|
||||
|
||||
/// Default velocity integration function..
|
||||
void cpBodyUpdateVelocity(cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt);
|
||||
/// Default position integration function.
|
||||
void cpBodyUpdatePosition(cpBody *body, cpFloat dt);
|
||||
|
||||
/// Convert body relative/local coordinates to absolute/world coordinates.
|
||||
cpVect cpBodyLocalToWorld(const cpBody *body, const cpVect point);
|
||||
/// Convert body absolute/world coordinates to relative/local coordinates.
|
||||
cpVect cpBodyWorldToLocal(const cpBody *body, const cpVect point);
|
||||
|
||||
/// Apply a force to a body. Both the force and point are expressed in world coordinates.
|
||||
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.
|
||||
void cpBodyApplyForceAtLocalPoint(cpBody *body, cpVect force, cpVect point);
|
||||
|
||||
/// Apply an impulse to a body. Both the impulse and point are expressed in world coordinates.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
cpVect cpBodyGetVelocityAtLocalPoint(const cpBody *body, cpVect point);
|
||||
|
||||
/// Get the amount of kinetic energy contained by the body.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
void cpBodyEachArbiter(cpBody *body, cpBodyArbiterIteratorFunc func, void *data);
|
||||
|
||||
///@}
|
|
@ -1,95 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/// @defgroup cpConstraint cpConstraint
|
||||
/// @{
|
||||
|
||||
/// Callback function type that gets called before solving a joint.
|
||||
typedef void (*cpConstraintPreSolveFunc)(cpConstraint *constraint, cpSpace *space);
|
||||
/// Callback function type that gets called after solving a joint.
|
||||
typedef void (*cpConstraintPostSolveFunc)(cpConstraint *constraint, cpSpace *space);
|
||||
|
||||
/// Destroy a constraint.
|
||||
void cpConstraintDestroy(cpConstraint *constraint);
|
||||
/// Destroy and free a constraint.
|
||||
void cpConstraintFree(cpConstraint *constraint);
|
||||
|
||||
/// Get the cpSpace this constraint is added to.
|
||||
cpSpace* cpConstraintGetSpace(const cpConstraint *constraint);
|
||||
|
||||
/// Get the first body the constraint is attached to.
|
||||
cpBody* cpConstraintGetBodyA(const cpConstraint *constraint);
|
||||
|
||||
/// Get the second body the constraint is attached to.
|
||||
cpBody* cpConstraintGetBodyB(const cpConstraint *constraint);
|
||||
|
||||
/// Get the maximum force that this constraint is allowed to use.
|
||||
cpFloat cpConstraintGetMaxForce(const cpConstraint *constraint);
|
||||
/// Set the maximum force that this constraint is allowed to use. (defaults to INFINITY)
|
||||
void cpConstraintSetMaxForce(cpConstraint *constraint, cpFloat maxForce);
|
||||
|
||||
/// Get rate at which joint error is corrected.
|
||||
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.
|
||||
void cpConstraintSetErrorBias(cpConstraint *constraint, cpFloat errorBias);
|
||||
|
||||
/// Get the maximum rate at which joint error is corrected.
|
||||
cpFloat cpConstraintGetMaxBias(const cpConstraint *constraint);
|
||||
/// Set the maximum rate at which joint error is corrected. (defaults to INFINITY)
|
||||
void cpConstraintSetMaxBias(cpConstraint *constraint, cpFloat maxBias);
|
||||
|
||||
/// Get if the two bodies connected by the constraint are allowed to collide or not.
|
||||
cpBool cpConstraintGetCollideBodies(const cpConstraint *constraint);
|
||||
/// Set if the two bodies connected by the constraint are allowed to collide or not. (defaults to cpFalse)
|
||||
void cpConstraintSetCollideBodies(cpConstraint *constraint, cpBool collideBodies);
|
||||
|
||||
/// Get the pre-solve function that is called before the solver runs.
|
||||
cpConstraintPreSolveFunc cpConstraintGetPreSolveFunc(const cpConstraint *constraint);
|
||||
/// Set the pre-solve function that is called before the solver runs.
|
||||
void cpConstraintSetPreSolveFunc(cpConstraint *constraint, cpConstraintPreSolveFunc preSolveFunc);
|
||||
|
||||
/// Get the post-solve function that is called before the solver runs.
|
||||
cpConstraintPostSolveFunc cpConstraintGetPostSolveFunc(const cpConstraint *constraint);
|
||||
/// Set the post-solve function that is called before the solver runs.
|
||||
void cpConstraintSetPostSolveFunc(cpConstraint *constraint, cpConstraintPostSolveFunc postSolveFunc);
|
||||
|
||||
/// Get the user definable data pointer for this constraint
|
||||
cpDataPointer cpConstraintGetUserData(const cpConstraint *constraint);
|
||||
/// Set the user definable data pointer for this constraint
|
||||
void cpConstraintSetUserData(cpConstraint *constraint, cpDataPointer userData);
|
||||
|
||||
/// Get the last impulse applied by this constraint.
|
||||
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"
|
||||
|
||||
///@}
|
|
@ -1,58 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/// @defgroup cpDampedRotarySpring cpDampedRotarySpring
|
||||
/// @{
|
||||
|
||||
/// Check if a constraint is a damped rotary springs.
|
||||
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.
|
||||
cpDampedRotarySpring* cpDampedRotarySpringAlloc(void);
|
||||
/// Initialize a damped rotary spring.
|
||||
cpDampedRotarySpring* cpDampedRotarySpringInit(cpDampedRotarySpring *joint, cpBody *a, cpBody *b, cpFloat restAngle, cpFloat stiffness, cpFloat damping);
|
||||
/// Allocate and initialize a damped rotary spring.
|
||||
cpConstraint* cpDampedRotarySpringNew(cpBody *a, cpBody *b, cpFloat restAngle, cpFloat stiffness, cpFloat damping);
|
||||
|
||||
/// Get the rest length of the spring.
|
||||
cpFloat cpDampedRotarySpringGetRestAngle(const cpConstraint *constraint);
|
||||
/// Set the rest length of the spring.
|
||||
void cpDampedRotarySpringSetRestAngle(cpConstraint *constraint, cpFloat restAngle);
|
||||
|
||||
/// Get the stiffness of the spring in force/distance.
|
||||
cpFloat cpDampedRotarySpringGetStiffness(const cpConstraint *constraint);
|
||||
/// Set the stiffness of the spring in force/distance.
|
||||
void cpDampedRotarySpringSetStiffness(cpConstraint *constraint, cpFloat stiffness);
|
||||
|
||||
/// Get the damping of the spring.
|
||||
cpFloat cpDampedRotarySpringGetDamping(const cpConstraint *constraint);
|
||||
/// Set the damping of the spring.
|
||||
void cpDampedRotarySpringSetDamping(cpConstraint *constraint, cpFloat damping);
|
||||
|
||||
/// Get the damping of the spring.
|
||||
cpDampedRotarySpringTorqueFunc cpDampedRotarySpringGetSpringTorqueFunc(const cpConstraint *constraint);
|
||||
/// Set the damping of the spring.
|
||||
void cpDampedRotarySpringSetSpringTorqueFunc(cpConstraint *constraint, cpDampedRotarySpringTorqueFunc springTorqueFunc);
|
||||
|
||||
/// @}
|
|
@ -1,68 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/// @defgroup cpDampedSpring cpDampedSpring
|
||||
/// @{
|
||||
|
||||
/// Check if a constraint is a slide joint.
|
||||
cpBool cpConstraintIsDampedSpring(const cpConstraint *constraint);
|
||||
|
||||
/// Function type used for damped spring force callbacks.
|
||||
typedef cpFloat (*cpDampedSpringForceFunc)(cpConstraint *spring, cpFloat dist);
|
||||
|
||||
/// Allocate a damped spring.
|
||||
cpDampedSpring* cpDampedSpringAlloc(void);
|
||||
/// Initialize a damped spring.
|
||||
cpDampedSpring* cpDampedSpringInit(cpDampedSpring *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat restLength, cpFloat stiffness, cpFloat damping);
|
||||
/// Allocate and initialize a damped spring.
|
||||
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.
|
||||
cpVect cpDampedSpringGetAnchorA(const cpConstraint *constraint);
|
||||
/// Set the location of the first anchor relative to the first body.
|
||||
void cpDampedSpringSetAnchorA(cpConstraint *constraint, cpVect anchorA);
|
||||
|
||||
/// Get the location of the second anchor relative to the second body.
|
||||
cpVect cpDampedSpringGetAnchorB(const cpConstraint *constraint);
|
||||
/// Set the location of the second anchor relative to the second body.
|
||||
void cpDampedSpringSetAnchorB(cpConstraint *constraint, cpVect anchorB);
|
||||
|
||||
/// Get the rest length of the spring.
|
||||
cpFloat cpDampedSpringGetRestLength(const cpConstraint *constraint);
|
||||
/// Set the rest length of the spring.
|
||||
void cpDampedSpringSetRestLength(cpConstraint *constraint, cpFloat restLength);
|
||||
|
||||
/// Get the stiffness of the spring in force/distance.
|
||||
cpFloat cpDampedSpringGetStiffness(const cpConstraint *constraint);
|
||||
/// Set the stiffness of the spring in force/distance.
|
||||
void cpDampedSpringSetStiffness(cpConstraint *constraint, cpFloat stiffness);
|
||||
|
||||
/// Get the damping of the spring.
|
||||
cpFloat cpDampedSpringGetDamping(const cpConstraint *constraint);
|
||||
/// Set the damping of the spring.
|
||||
void cpDampedSpringSetDamping(cpConstraint *constraint, cpFloat damping);
|
||||
|
||||
/// Get the damping of the spring.
|
||||
cpDampedSpringForceFunc cpDampedSpringGetSpringForceFunc(const cpConstraint *constraint);
|
||||
/// Set the damping of the spring.
|
||||
void cpDampedSpringSetSpringForceFunc(cpConstraint *constraint, cpDampedSpringForceFunc springForceFunc);
|
||||
|
||||
/// @}
|
|
@ -1,45 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/// @defgroup cpGearJoint cpGearJoint
|
||||
/// @{
|
||||
|
||||
/// Check if a constraint is a damped rotary springs.
|
||||
cpBool cpConstraintIsGearJoint(const cpConstraint *constraint);
|
||||
|
||||
/// Allocate a gear joint.
|
||||
cpGearJoint* cpGearJointAlloc(void);
|
||||
/// Initialize a gear joint.
|
||||
cpGearJoint* cpGearJointInit(cpGearJoint *joint, cpBody *a, cpBody *b, cpFloat phase, cpFloat ratio);
|
||||
/// Allocate and initialize a gear joint.
|
||||
cpConstraint* cpGearJointNew(cpBody *a, cpBody *b, cpFloat phase, cpFloat ratio);
|
||||
|
||||
/// Get the phase offset of the gears.
|
||||
cpFloat cpGearJointGetPhase(const cpConstraint *constraint);
|
||||
/// Set the phase offset of the gears.
|
||||
void cpGearJointSetPhase(cpConstraint *constraint, cpFloat phase);
|
||||
|
||||
/// Get the angular distance of each ratchet.
|
||||
cpFloat cpGearJointGetRatio(const cpConstraint *constraint);
|
||||
/// Set the ratio of a gear joint.
|
||||
void cpGearJointSetRatio(cpConstraint *constraint, cpFloat ratio);
|
||||
|
||||
/// @}
|
|
@ -1,50 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/// @defgroup cpGrooveJoint cpGrooveJoint
|
||||
/// @{
|
||||
|
||||
/// Check if a constraint is a slide joint.
|
||||
cpBool cpConstraintIsGrooveJoint(const cpConstraint *constraint);
|
||||
|
||||
/// Allocate a groove joint.
|
||||
cpGrooveJoint* cpGrooveJointAlloc(void);
|
||||
/// Initialize a groove joint.
|
||||
cpGrooveJoint* cpGrooveJointInit(cpGrooveJoint *joint, cpBody *a, cpBody *b, cpVect groove_a, cpVect groove_b, cpVect anchorB);
|
||||
/// Allocate and initialize a groove joint.
|
||||
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.
|
||||
cpVect cpGrooveJointGetGrooveA(const cpConstraint *constraint);
|
||||
/// Set the first endpoint of the groove relative to the first body.
|
||||
void cpGrooveJointSetGrooveA(cpConstraint *constraint, cpVect grooveA);
|
||||
|
||||
/// Get the first endpoint of the groove relative to the first body.
|
||||
cpVect cpGrooveJointGetGrooveB(const cpConstraint *constraint);
|
||||
/// Set the first endpoint of the groove relative to the first body.
|
||||
void cpGrooveJointSetGrooveB(cpConstraint *constraint, cpVect grooveB);
|
||||
|
||||
/// Get the location of the second anchor relative to the second body.
|
||||
cpVect cpGrooveJointGetAnchorB(const cpConstraint *constraint);
|
||||
/// Set the location of the second anchor relative to the second body.
|
||||
void cpGrooveJointSetAnchorB(cpConstraint *constraint, cpVect anchorB);
|
||||
|
||||
/// @}
|
|
@ -1,50 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/// @defgroup cpPinJoint cpPinJoint
|
||||
/// @{
|
||||
|
||||
/// Check if a constraint is a pin joint.
|
||||
cpBool cpConstraintIsPinJoint(const cpConstraint *constraint);
|
||||
|
||||
/// Allocate a pin joint.
|
||||
cpPinJoint* cpPinJointAlloc(void);
|
||||
/// Initialize a pin joint.
|
||||
cpPinJoint* cpPinJointInit(cpPinJoint *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB);
|
||||
/// Allocate and initialize a pin joint.
|
||||
cpConstraint* cpPinJointNew(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB);
|
||||
|
||||
/// Get the location of the first anchor relative to the first body.
|
||||
cpVect cpPinJointGetAnchorA(const cpConstraint *constraint);
|
||||
/// Set the location of the first anchor relative to the first body.
|
||||
void cpPinJointSetAnchorA(cpConstraint *constraint, cpVect anchorA);
|
||||
|
||||
/// Get the location of the second anchor relative to the second body.
|
||||
cpVect cpPinJointGetAnchorB(const cpConstraint *constraint);
|
||||
/// Set the location of the second anchor relative to the second body.
|
||||
void cpPinJointSetAnchorB(cpConstraint *constraint, cpVect anchorB);
|
||||
|
||||
/// Get the distance the joint will maintain between the two anchors.
|
||||
cpFloat cpPinJointGetDist(const cpConstraint *constraint);
|
||||
/// Set the distance the joint will maintain between the two anchors.
|
||||
void cpPinJointSetDist(cpConstraint *constraint, cpFloat dist);
|
||||
|
||||
///@}
|
|
@ -1,47 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/// @defgroup cpPivotJoint cpPivotJoint
|
||||
/// @{
|
||||
|
||||
/// Check if a constraint is a slide joint.
|
||||
cpBool cpConstraintIsPivotJoint(const cpConstraint *constraint);
|
||||
|
||||
/// Allocate a pivot joint
|
||||
cpPivotJoint* cpPivotJointAlloc(void);
|
||||
/// Initialize a pivot joint.
|
||||
cpPivotJoint* cpPivotJointInit(cpPivotJoint *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB);
|
||||
/// Allocate and initialize a pivot joint.
|
||||
cpConstraint* cpPivotJointNew(cpBody *a, cpBody *b, cpVect pivot);
|
||||
/// Allocate and initialize a pivot joint with specific anchors.
|
||||
cpConstraint* cpPivotJointNew2(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB);
|
||||
|
||||
/// Get the location of the first anchor relative to the first body.
|
||||
cpVect cpPivotJointGetAnchorA(const cpConstraint *constraint);
|
||||
/// Set the location of the first anchor relative to the first body.
|
||||
void cpPivotJointSetAnchorA(cpConstraint *constraint, cpVect anchorA);
|
||||
|
||||
/// Get the location of the second anchor relative to the second body.
|
||||
cpVect cpPivotJointGetAnchorB(const cpConstraint *constraint);
|
||||
/// Set the location of the second anchor relative to the second body.
|
||||
void cpPivotJointSetAnchorB(cpConstraint *constraint, cpVect anchorB);
|
||||
|
||||
/// @}
|
|
@ -1,52 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/// @defgroup cpPolyShape cpPolyShape
|
||||
/// @{
|
||||
|
||||
/// Allocate a polygon shape.
|
||||
cpPolyShape* cpPolyShapeAlloc(void);
|
||||
/// Initialize a polygon shape with rounded corners.
|
||||
/// A convex hull will be created from the vertexes.
|
||||
cpPolyShape* cpPolyShapeInit(cpPolyShape *poly, cpBody *body, int count, const cpVect *verts, cpTransform transform, cpFloat radius);
|
||||
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.
|
||||
cpShape* cpPolyShapeNew(cpBody *body, int count, const cpVect *verts, cpTransform transform, cpFloat radius);
|
||||
cpShape* cpPolyShapeNewRaw(cpBody *body, int count, const cpVect *verts, cpFloat radius);
|
||||
|
||||
/// Initialize a box shaped polygon shape with rounded corners.
|
||||
cpPolyShape* cpBoxShapeInit(cpPolyShape *poly, cpBody *body, cpFloat width, cpFloat height, cpFloat radius);
|
||||
/// Initialize an offset box shaped polygon shape with rounded corners.
|
||||
cpPolyShape* cpBoxShapeInit2(cpPolyShape *poly, cpBody *body, cpBB box, cpFloat radius);
|
||||
/// Allocate and initialize a box shaped polygon shape.
|
||||
cpShape* cpBoxShapeNew(cpBody *body, cpFloat width, cpFloat height, cpFloat radius);
|
||||
/// Allocate and initialize an offset box shaped polygon shape.
|
||||
cpShape* cpBoxShapeNew2(cpBody *body, cpBB box, cpFloat radius);
|
||||
|
||||
/// Get the number of verts in a polygon shape.
|
||||
int cpPolyShapeGetCount(const cpShape *shape);
|
||||
/// Get the @c ith vertex of a polygon shape.
|
||||
cpVect cpPolyShapeGetVert(const cpShape *shape, int index);
|
||||
/// Get the radius of a polygon shape.
|
||||
cpFloat cpPolyShapeGetRadius(const cpShape *shape);
|
||||
|
||||
/// @}
|
|
@ -1,50 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/// @defgroup cpRatchetJoint cpRatchetJoint
|
||||
/// @{
|
||||
|
||||
/// Check if a constraint is a damped rotary springs.
|
||||
cpBool cpConstraintIsRatchetJoint(const cpConstraint *constraint);
|
||||
|
||||
/// Allocate a ratchet joint.
|
||||
cpRatchetJoint* cpRatchetJointAlloc(void);
|
||||
/// Initialize a ratched joint.
|
||||
cpRatchetJoint* cpRatchetJointInit(cpRatchetJoint *joint, cpBody *a, cpBody *b, cpFloat phase, cpFloat ratchet);
|
||||
/// Allocate and initialize a ratchet joint.
|
||||
cpConstraint* cpRatchetJointNew(cpBody *a, cpBody *b, cpFloat phase, cpFloat ratchet);
|
||||
|
||||
/// Get the angle of the current ratchet tooth.
|
||||
cpFloat cpRatchetJointGetAngle(const cpConstraint *constraint);
|
||||
/// Set the angle of the current ratchet tooth.
|
||||
void cpRatchetJointSetAngle(cpConstraint *constraint, cpFloat angle);
|
||||
|
||||
/// Get the phase offset of the ratchet.
|
||||
cpFloat cpRatchetJointGetPhase(const cpConstraint *constraint);
|
||||
/// Get the phase offset of the ratchet.
|
||||
void cpRatchetJointSetPhase(cpConstraint *constraint, cpFloat phase);
|
||||
|
||||
/// Get the angular distance of each ratchet.
|
||||
cpFloat cpRatchetJointGetRatchet(const cpConstraint *constraint);
|
||||
/// Set the angular distance of each ratchet.
|
||||
void cpRatchetJointSetRatchet(cpConstraint *constraint, cpFloat ratchet);
|
||||
|
||||
/// @}
|
|
@ -1,45 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/// @defgroup cpRotaryLimitJoint cpRotaryLimitJoint
|
||||
/// @{
|
||||
|
||||
/// Check if a constraint is a damped rotary springs.
|
||||
cpBool cpConstraintIsRotaryLimitJoint(const cpConstraint *constraint);
|
||||
|
||||
/// Allocate a damped rotary limit joint.
|
||||
cpRotaryLimitJoint* cpRotaryLimitJointAlloc(void);
|
||||
/// Initialize a damped rotary limit joint.
|
||||
cpRotaryLimitJoint* cpRotaryLimitJointInit(cpRotaryLimitJoint *joint, cpBody *a, cpBody *b, cpFloat min, cpFloat max);
|
||||
/// Allocate and initialize a damped rotary limit joint.
|
||||
cpConstraint* cpRotaryLimitJointNew(cpBody *a, cpBody *b, cpFloat min, cpFloat max);
|
||||
|
||||
/// Get the minimum distance the joint will maintain between the two anchors.
|
||||
cpFloat cpRotaryLimitJointGetMin(const cpConstraint *constraint);
|
||||
/// Set the minimum distance the joint will maintain between the two anchors.
|
||||
void cpRotaryLimitJointSetMin(cpConstraint *constraint, cpFloat min);
|
||||
|
||||
/// Get the maximum distance the joint will maintain between the two anchors.
|
||||
cpFloat cpRotaryLimitJointGetMax(const cpConstraint *constraint);
|
||||
/// Set the maximum distance the joint will maintain between the two anchors.
|
||||
void cpRotaryLimitJointSetMax(cpConstraint *constraint, cpFloat max);
|
||||
|
||||
/// @}
|
|
@ -1,189 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/// @defgroup cpShape cpShape
|
||||
/// The cpShape struct defines the shape of a rigid body.
|
||||
/// @{
|
||||
|
||||
/// Nearest 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 same as 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, 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;
|
||||
|
||||
typedef struct cpShapeFilter {
|
||||
cpGroup group;
|
||||
cpBitmask categories;
|
||||
cpBitmask mask;
|
||||
} cpShapeFilter;
|
||||
|
||||
static const cpShapeFilter CP_SHAPE_FILTER_ALL = {CP_NO_GROUP, CP_ALL_CATEGORIES, CP_ALL_CATEGORIES};
|
||||
static const cpShapeFilter CP_SHAPE_FILTER_NONE = {CP_NO_GROUP, ~CP_ALL_CATEGORIES, ~CP_ALL_CATEGORIES};
|
||||
|
||||
static inline cpShapeFilter
|
||||
cpShapeFilterNew(cpGroup group, cpBitmask categories, cpBitmask mask)
|
||||
{
|
||||
cpShapeFilter filter = {group, categories, mask};
|
||||
return filter;
|
||||
}
|
||||
|
||||
/// Destroy a shape.
|
||||
void cpShapeDestroy(cpShape *shape);
|
||||
/// Destroy and Free a shape.
|
||||
void cpShapeFree(cpShape *shape);
|
||||
|
||||
/// Update, cache and return the bounding box of a shape based on the body it's attached to.
|
||||
cpBB cpShapeCacheBB(cpShape *shape);
|
||||
/// Update, cache and return the bounding box of a shape with an explicit transformation.
|
||||
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.
|
||||
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.
|
||||
cpBool cpShapeSegmentQuery(const cpShape *shape, cpVect a, cpVect b, cpFloat radius, cpSegmentQueryInfo *info);
|
||||
|
||||
/// Return contact information about two shapes.
|
||||
cpContactPointSet cpShapesCollide(const cpShape *a, const cpShape *b);
|
||||
|
||||
/// The cpSpace this body is added to.
|
||||
cpSpace* cpShapeGetSpace(const cpShape *shape);
|
||||
|
||||
/// The cpBody this shape is connected to.
|
||||
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.
|
||||
void cpShapeSetBody(cpShape *shape, cpBody *body);
|
||||
|
||||
/// Get the mass of the shape if you are having Chipmunk calculate mass properties for you.
|
||||
cpFloat cpShapeGetMass(cpShape *shape);
|
||||
/// Set the mass of this shape to have Chipmunk calculate mass properties for you.
|
||||
void cpShapeSetMass(cpShape *shape, cpFloat mass);
|
||||
|
||||
/// Get the density of the shape if you are having Chipmunk calculate mass properties for you.
|
||||
cpFloat cpShapeGetDensity(cpShape *shape);
|
||||
/// Set the density of this shape to have Chipmunk calculate mass properties for you.
|
||||
void cpShapeSetDensity(cpShape *shape, cpFloat density);
|
||||
|
||||
/// Get the calculated moment of inertia for this shape.
|
||||
cpFloat cpShapeGetMoment(cpShape *shape);
|
||||
/// Get the calculated area of this shape.
|
||||
cpFloat cpShapeGetArea(cpShape *shape);
|
||||
/// Get the centroid of this shape.
|
||||
cpVect cpShapeGetCenterOfGravity(cpShape *shape);
|
||||
|
||||
/// Get the bounding box that contains the shape given it's current position and angle.
|
||||
cpBB cpShapeGetBB(const cpShape *shape);
|
||||
|
||||
/// Get if the shape is set to be a sensor or not.
|
||||
cpBool cpShapeGetSensor(const cpShape *shape);
|
||||
/// Set if the shape is a sensor or not.
|
||||
void cpShapeSetSensor(cpShape *shape, cpBool sensor);
|
||||
|
||||
/// Get the elasticity of this shape.
|
||||
cpFloat cpShapeGetElasticity(const cpShape *shape);
|
||||
/// Set the elasticity of this shape.
|
||||
void cpShapeSetElasticity(cpShape *shape, cpFloat elasticity);
|
||||
|
||||
/// Get the friction of this shape.
|
||||
cpFloat cpShapeGetFriction(const cpShape *shape);
|
||||
/// Set the friction of this shape.
|
||||
void cpShapeSetFriction(cpShape *shape, cpFloat friction);
|
||||
|
||||
/// Get the surface velocity of this shape.
|
||||
cpVect cpShapeGetSurfaceVelocity(const cpShape *shape);
|
||||
/// Set the surface velocity of this shape.
|
||||
void cpShapeSetSurfaceVelocity(cpShape *shape, cpVect surfaceVelocity);
|
||||
|
||||
/// Get the user definable data pointer of this shape.
|
||||
cpDataPointer cpShapeGetUserData(const cpShape *shape);
|
||||
/// Set the user definable data pointer of this shape.
|
||||
void cpShapeSetUserData(cpShape *shape, cpDataPointer userData);
|
||||
|
||||
/// Set the collision type of this shape.
|
||||
cpCollisionType cpShapeGetCollisionType(const cpShape *shape);
|
||||
/// Get the collision type of this shape.
|
||||
void cpShapeSetCollisionType(cpShape *shape, cpCollisionType collisionType);
|
||||
|
||||
/// Get the collision filtering parameters of this shape.
|
||||
cpShapeFilter cpShapeGetFilter(const cpShape *shape);
|
||||
/// Set the collision filtering parameters of this shape.
|
||||
void cpShapeSetFilter(cpShape *shape, cpShapeFilter filter);
|
||||
|
||||
|
||||
/// @}
|
||||
/// @defgroup cpCircleShape cpCircleShape
|
||||
|
||||
/// Allocate a circle shape.
|
||||
cpCircleShape* cpCircleShapeAlloc(void);
|
||||
/// Initialize a circle shape.
|
||||
cpCircleShape* cpCircleShapeInit(cpCircleShape *circle, cpBody *body, cpFloat radius, cpVect offset);
|
||||
/// Allocate and initialize a circle shape.
|
||||
cpShape* cpCircleShapeNew(cpBody *body, cpFloat radius, cpVect offset);
|
||||
|
||||
/// Get the offset of a circle shape.
|
||||
cpVect cpCircleShapeGetOffset(const cpShape *shape);
|
||||
/// Get the radius of a circle shape.
|
||||
cpFloat cpCircleShapeGetRadius(const cpShape *shape);
|
||||
|
||||
/// @}
|
||||
/// @defgroup cpSegmentShape cpSegmentShape
|
||||
|
||||
/// Allocate a segment shape.
|
||||
cpSegmentShape* cpSegmentShapeAlloc(void);
|
||||
/// Initialize a segment shape.
|
||||
cpSegmentShape* cpSegmentShapeInit(cpSegmentShape *seg, cpBody *body, cpVect a, cpVect b, cpFloat radius);
|
||||
/// Allocate and initialize a segment shape.
|
||||
cpShape* cpSegmentShapeNew(cpBody *body, cpVect a, cpVect b, cpFloat radius);
|
||||
|
||||
/// Let Chipmunk know about the geometry of adjacent segments to avoid colliding with endcaps.
|
||||
void cpSegmentShapeSetNeighbors(cpShape *shape, cpVect prev, cpVect next);
|
||||
|
||||
/// Get the first endpoint of a segment shape.
|
||||
cpVect cpSegmentShapeGetA(const cpShape *shape);
|
||||
/// Get the second endpoint of a segment shape.
|
||||
cpVect cpSegmentShapeGetB(const cpShape *shape);
|
||||
/// Get the normal of a segment shape.
|
||||
cpVect cpSegmentShapeGetNormal(const cpShape *shape);
|
||||
/// Get the first endpoint of a segment shape.
|
||||
cpFloat cpSegmentShapeGetRadius(const cpShape *shape);
|
||||
|
||||
/// @}
|
|
@ -1,43 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/// @defgroup cpSimpleMotor cpSimpleMotor
|
||||
/// @{
|
||||
|
||||
/// Opaque struct type for damped rotary springs.
|
||||
typedef struct cpSimpleMotor cpSimpleMotor;
|
||||
|
||||
/// Check if a constraint is a damped rotary springs.
|
||||
cpBool cpConstraintIsSimpleMotor(const cpConstraint *constraint);
|
||||
|
||||
/// Allocate a simple motor.
|
||||
cpSimpleMotor* cpSimpleMotorAlloc(void);
|
||||
/// initialize a simple motor.
|
||||
cpSimpleMotor* cpSimpleMotorInit(cpSimpleMotor *joint, cpBody *a, cpBody *b, cpFloat rate);
|
||||
/// Allocate and initialize a simple motor.
|
||||
cpConstraint* cpSimpleMotorNew(cpBody *a, cpBody *b, cpFloat rate);
|
||||
|
||||
/// Get the rate of the motor.
|
||||
cpFloat cpSimpleMotorGetRate(const cpConstraint *constraint);
|
||||
/// Set the rate of the motor.
|
||||
void cpSimpleMotorSetRate(cpConstraint *constraint, cpFloat rate);
|
||||
|
||||
/// @}
|
|
@ -1,55 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/// @defgroup cpSlideJoint cpSlideJoint
|
||||
/// @{
|
||||
|
||||
/// Check if a constraint is a slide joint.
|
||||
cpBool cpConstraintIsSlideJoint(const cpConstraint *constraint);
|
||||
|
||||
/// Allocate a slide joint.
|
||||
cpSlideJoint* cpSlideJointAlloc(void);
|
||||
/// Initialize a slide joint.
|
||||
cpSlideJoint* cpSlideJointInit(cpSlideJoint *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat min, cpFloat max);
|
||||
/// Allocate and initialize a slide joint.
|
||||
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.
|
||||
cpVect cpSlideJointGetAnchorA(const cpConstraint *constraint);
|
||||
/// Set the location of the first anchor relative to the first body.
|
||||
void cpSlideJointSetAnchorA(cpConstraint *constraint, cpVect anchorA);
|
||||
|
||||
/// Get the location of the second anchor relative to the second body.
|
||||
cpVect cpSlideJointGetAnchorB(const cpConstraint *constraint);
|
||||
/// Set the location of the second anchor relative to the second body.
|
||||
void cpSlideJointSetAnchorB(cpConstraint *constraint, cpVect anchorB);
|
||||
|
||||
/// Get the minimum distance the joint will maintain between the two anchors.
|
||||
cpFloat cpSlideJointGetMin(const cpConstraint *constraint);
|
||||
/// Set the minimum distance the joint will maintain between the two anchors.
|
||||
void cpSlideJointSetMin(cpConstraint *constraint, cpFloat min);
|
||||
|
||||
/// Get the maximum distance the joint will maintain between the two anchors.
|
||||
cpFloat cpSlideJointGetMax(const cpConstraint *constraint);
|
||||
/// Set the maximum distance the joint will maintain between the two anchors.
|
||||
void cpSlideJointSetMax(cpConstraint *constraint, cpFloat max);
|
||||
|
||||
/// @}
|
|
@ -1,284 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/// @defgroup cpSpace cpSpace
|
||||
/// @{
|
||||
|
||||
//MARK: Definitions
|
||||
|
||||
typedef struct cpContactBufferHeader cpContactBufferHeader;
|
||||
typedef void (*cpSpaceArbiterApplyImpulseFunc)(cpArbiter *arb);
|
||||
|
||||
/// 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 cpCollisionHandler {
|
||||
const cpCollisionType typeA, typeB;
|
||||
cpCollisionBeginFunc beginFunc;
|
||||
cpCollisionPreSolveFunc preSolveFunc;
|
||||
cpCollisionPostSolveFunc postSolveFunc;
|
||||
cpCollisionSeparateFunc separateFunc;
|
||||
cpDataPointer userData;
|
||||
};
|
||||
|
||||
// TODO: Make timestep a parameter?
|
||||
|
||||
|
||||
//MARK: Memory and Initialization
|
||||
|
||||
/// Allocate a cpSpace.
|
||||
cpSpace* cpSpaceAlloc(void);
|
||||
/// Initialize a cpSpace.
|
||||
cpSpace* cpSpaceInit(cpSpace *space);
|
||||
/// Allocate and initialize a cpSpace.
|
||||
cpSpace* cpSpaceNew(void);
|
||||
|
||||
/// Destroy a cpSpace.
|
||||
void cpSpaceDestroy(cpSpace *space);
|
||||
/// Destroy and free a cpSpace.
|
||||
void cpSpaceFree(cpSpace *space);
|
||||
|
||||
|
||||
//MARK: Properties
|
||||
|
||||
/// Number of iterations to use in the impulse solver to solve contacts and other constraints.
|
||||
int cpSpaceGetIterations(const cpSpace *space);
|
||||
void cpSpaceSetIterations(cpSpace *space, int iterations);
|
||||
|
||||
/// Gravity to pass to rigid bodies when integrating velocity.
|
||||
cpVect cpSpaceGetGravity(const cpSpace *space);
|
||||
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.
|
||||
cpFloat cpSpaceGetDamping(const cpSpace *space);
|
||||
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.
|
||||
cpFloat cpSpaceGetIdleSpeedThreshold(const cpSpace *space);
|
||||
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.
|
||||
cpFloat cpSpaceGetSleepTimeThreshold(const cpSpace *space);
|
||||
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.
|
||||
cpFloat cpSpaceGetCollisionSlop(const cpSpace *space);
|
||||
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.
|
||||
cpFloat cpSpaceGetCollisionBias(const cpSpace *space);
|
||||
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.
|
||||
cpTimestamp cpSpaceGetCollisionPersistence(const cpSpace *space);
|
||||
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.
|
||||
cpDataPointer cpSpaceGetUserData(const cpSpace *space);
|
||||
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.
|
||||
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.
|
||||
cpFloat cpSpaceGetCurrentTimeStep(const cpSpace *space);
|
||||
|
||||
/// returns true from inside a callback when objects cannot be added/removed.
|
||||
cpBool cpSpaceIsLocked(cpSpace *space);
|
||||
|
||||
|
||||
//MARK: Collision Handlers
|
||||
|
||||
cpCollisionHandler *cpSpaceAddDefaultCollisionHandler(cpSpace *space);
|
||||
cpCollisionHandler *cpSpaceAddCollisionHandler(cpSpace *space, cpCollisionType a, cpCollisionType b);
|
||||
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.
|
||||
cpShape* cpSpaceAddShape(cpSpace *space, cpShape *shape);
|
||||
/// Add a rigid body to the simulation.
|
||||
cpBody* cpSpaceAddBody(cpSpace *space, cpBody *body);
|
||||
/// Add a constraint to the simulation.
|
||||
cpConstraint* cpSpaceAddConstraint(cpSpace *space, cpConstraint *constraint);
|
||||
|
||||
/// Remove a collision shape from the simulation.
|
||||
void cpSpaceRemoveShape(cpSpace *space, cpShape *shape);
|
||||
/// Remove a rigid body from the simulation.
|
||||
void cpSpaceRemoveBody(cpSpace *space, cpBody *body);
|
||||
/// Remove a constraint from the simulation.
|
||||
void cpSpaceRemoveConstraint(cpSpace *space, cpConstraint *constraint);
|
||||
|
||||
/// Test if a collision shape has been added to the space.
|
||||
cpBool cpSpaceContainsShape(cpSpace *space, cpShape *shape);
|
||||
/// Test if a rigid body has been added to the space.
|
||||
cpBool cpSpaceContainsBody(cpSpace *space, cpBody *body);
|
||||
/// Test if a constraint has been added to the space.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
void cpSpaceEachConstraint(cpSpace *space, cpSpaceConstraintIteratorFunc func, void *data);
|
||||
|
||||
|
||||
//MARK: Indexing
|
||||
|
||||
/// Update the collision detection info for the static shapes in the space.
|
||||
void cpSpaceReindexStatic(cpSpace *space);
|
||||
/// Update the collision detection data for a specific shape in the space.
|
||||
void cpSpaceReindexShape(cpSpace *space, cpShape *shape);
|
||||
/// Update the collision detection data for all shapes attached to a body.
|
||||
void cpSpaceReindexShapesForBody(cpSpace *space, cpBody *body);
|
||||
|
||||
/// Switch the space to use a spatial has as it's spatial index.
|
||||
void cpSpaceUseSpatialHash(cpSpace *space, cpFloat dim, int count);
|
||||
|
||||
|
||||
//MARK: Time Stepping
|
||||
|
||||
/// Step the space forward in time by @c dt.
|
||||
void cpSpaceStep(cpSpace *space, cpFloat dt);
|
||||
|
||||
|
||||
//MARK: Debug API
|
||||
|
||||
#ifndef CP_SPACE_DISABLE_DEBUG_API
|
||||
|
||||
typedef struct cpSpaceDebugColor {
|
||||
float r, g, b, a;
|
||||
} cpSpaceDebugColor;
|
||||
|
||||
typedef void (*cpSpaceDebugDrawCircleImpl)(cpVect pos, cpFloat angle, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor, cpDataPointer *data);
|
||||
typedef void (*cpSpaceDebugDrawSegmentImpl)(cpVect a, cpVect b, cpSpaceDebugColor color, cpDataPointer *data);
|
||||
typedef void (*cpSpaceDebugDrawFatSegmentImpl)(cpVect a, cpVect b, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor, cpDataPointer *data);
|
||||
typedef void (*cpSpaceDebugDrawPolygonImpl)(int count, const cpVect *verts, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor, cpDataPointer *data);
|
||||
typedef void (*cpSpaceDebugDrawDotImpl)(cpFloat size, cpVect pos, cpSpaceDebugColor color, cpDataPointer *data);
|
||||
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;
|
||||
|
||||
typedef struct cpSpaceDebugDrawOptions {
|
||||
cpSpaceDebugDrawCircleImpl drawCircle;
|
||||
cpSpaceDebugDrawSegmentImpl drawSegment;
|
||||
cpSpaceDebugDrawFatSegmentImpl drawFatSegment;
|
||||
cpSpaceDebugDrawPolygonImpl drawPolygon;
|
||||
cpSpaceDebugDrawDotImpl drawDot;
|
||||
|
||||
cpSpaceDebugDrawFlags flags;
|
||||
cpSpaceDebugColor shapeOutlineColor;
|
||||
cpSpaceDebugDrawColorForShapeImpl colorForShape;
|
||||
cpSpaceDebugColor constraintColor;
|
||||
cpSpaceDebugColor collisionPointColor;
|
||||
|
||||
cpDataPointer data;
|
||||
} cpSpaceDebugDrawOptions;
|
||||
|
||||
void cpSpaceDebugDraw(cpSpace *space, cpSpaceDebugDrawOptions *options);
|
||||
|
||||
#endif
|
||||
|
||||
/// @}
|
|
@ -1,227 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
@defgroup cpSpatialIndex cpSpatialIndex
|
||||
|
||||
Spatial indexes are data structures that are used to accelerate collision detection
|
||||
and spatial queries. Chipmunk provides a number of spatial index algorithms to pick from
|
||||
and they are programmed in a generic way so that you can use them for holding more than
|
||||
just cpShape structs.
|
||||
|
||||
It works by using @c void pointers to the objects you add and using a callback to ask your code
|
||||
for bounding boxes when it needs them. Several types of queries can be performed an index as well
|
||||
as reindexing and full collision information. All communication to the spatial indexes is performed
|
||||
through callback functions.
|
||||
|
||||
Spatial indexes should be treated as opaque structs.
|
||||
This meanns you shouldn't be reading any of the struct fields.
|
||||
@{
|
||||
*/
|
||||
|
||||
//MARK: Spatial Index
|
||||
|
||||
/// Spatial index bounding box callback function type.
|
||||
/// The spatial index calls this function and passes you a pointer to an object you added
|
||||
/// when it needs to get the bounding box associated with that object.
|
||||
typedef cpBB (*cpSpatialIndexBBFunc)(void *obj);
|
||||
/// Spatial index/object iterator callback function type.
|
||||
typedef void (*cpSpatialIndexIteratorFunc)(void *obj, void *data);
|
||||
/// Spatial query callback function type.
|
||||
typedef cpCollisionID (*cpSpatialIndexQueryFunc)(void *obj1, void *obj2, cpCollisionID id, void *data);
|
||||
/// Spatial segment query callback function type.
|
||||
typedef cpFloat (*cpSpatialIndexSegmentQueryFunc)(void *obj1, void *obj2, void *data);
|
||||
|
||||
|
||||
typedef struct cpSpatialIndexClass cpSpatialIndexClass;
|
||||
typedef struct cpSpatialIndex cpSpatialIndex;
|
||||
|
||||
/// @private
|
||||
struct cpSpatialIndex {
|
||||
cpSpatialIndexClass *klass;
|
||||
|
||||
cpSpatialIndexBBFunc bbfunc;
|
||||
|
||||
cpSpatialIndex *staticIndex, *dynamicIndex;
|
||||
};
|
||||
|
||||
|
||||
//MARK: Spatial Hash
|
||||
|
||||
typedef struct cpSpaceHash cpSpaceHash;
|
||||
|
||||
/// Allocate a spatial hash.
|
||||
cpSpaceHash* cpSpaceHashAlloc(void);
|
||||
/// Initialize a spatial hash.
|
||||
cpSpatialIndex* cpSpaceHashInit(cpSpaceHash *hash, cpFloat celldim, int numcells, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex);
|
||||
/// Allocate and initialize a spatial hash.
|
||||
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.
|
||||
void cpSpaceHashResize(cpSpaceHash *hash, cpFloat celldim, int numcells);
|
||||
|
||||
//MARK: AABB Tree
|
||||
|
||||
typedef struct cpBBTree cpBBTree;
|
||||
|
||||
/// Allocate a bounding box tree.
|
||||
cpBBTree* cpBBTreeAlloc(void);
|
||||
/// Initialize a bounding box tree.
|
||||
cpSpatialIndex* cpBBTreeInit(cpBBTree *tree, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex);
|
||||
/// Allocate and initialize a bounding box tree.
|
||||
cpSpatialIndex* cpBBTreeNew(cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex);
|
||||
|
||||
/// Perform a static top down optimization of the tree.
|
||||
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.
|
||||
void cpBBTreeSetVelocityFunc(cpSpatialIndex *index, cpBBTreeVelocityFunc func);
|
||||
|
||||
//MARK: Single Axis Sweep
|
||||
|
||||
typedef struct cpSweep1D cpSweep1D;
|
||||
|
||||
/// Allocate a 1D sort and sweep broadphase.
|
||||
cpSweep1D* cpSweep1DAlloc(void);
|
||||
/// Initialize a 1D sort and sweep broadphase.
|
||||
cpSpatialIndex* cpSweep1DInit(cpSweep1D *sweep, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex);
|
||||
/// Allocate and initialize a 1D sort and sweep broadphase.
|
||||
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.
|
||||
void cpSpatialIndexFree(cpSpatialIndex *index);
|
||||
/// Collide the objects in @c dynamicIndex against the objects in @c staticIndex using the query callback function.
|
||||
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);
|
||||
}
|
||||
|
||||
///@}
|
|
@ -1,197 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#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)
|
||||
);
|
||||
}
|
||||
|
||||
// Miscelaneous (but useful) transformation matrices.
|
||||
|
||||
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
|
|
@ -1,230 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef CHIPMUNK_VECT_H
|
||||
#define CHIPMUNK_VECT_H
|
||||
|
||||
#include "chipmunk_types.h"
|
||||
|
||||
/// @defgroup cpVect cpVect
|
||||
/// Chipmunk's 2D vector type along with a handy 2D vector math lib.
|
||||
/// @{
|
||||
|
||||
/// Constant for the zero vector.
|
||||
static const cpVect cpvzero = {0.0f,0.0f};
|
||||
|
||||
/// Convenience constructor for cpVect structs.
|
||||
static inline cpVect cpv(const cpFloat x, const cpFloat y)
|
||||
{
|
||||
cpVect v = {x, y};
|
||||
return v;
|
||||
}
|
||||
|
||||
/// Check if two vectors are equal. (Be careful when comparing floating point numbers!)
|
||||
static inline cpBool cpveql(const cpVect v1, const cpVect v2)
|
||||
{
|
||||
return (v1.x == v2.x && v1.y == v2.y);
|
||||
}
|
||||
|
||||
/// Add two vectors
|
||||
static inline cpVect cpvadd(const cpVect v1, const cpVect v2)
|
||||
{
|
||||
return cpv(v1.x + v2.x, v1.y + v2.y);
|
||||
}
|
||||
|
||||
/// Subtract two vectors.
|
||||
static inline cpVect cpvsub(const cpVect v1, const cpVect v2)
|
||||
{
|
||||
return cpv(v1.x - v2.x, v1.y - v2.y);
|
||||
}
|
||||
|
||||
/// Negate a vector.
|
||||
static inline cpVect cpvneg(const cpVect v)
|
||||
{
|
||||
return cpv(-v.x, -v.y);
|
||||
}
|
||||
|
||||
/// Scalar multiplication.
|
||||
static inline cpVect cpvmult(const cpVect v, const cpFloat s)
|
||||
{
|
||||
return cpv(v.x*s, v.y*s);
|
||||
}
|
||||
|
||||
/// Vector dot product.
|
||||
static inline cpFloat cpvdot(const cpVect v1, const cpVect v2)
|
||||
{
|
||||
return v1.x*v2.x + v1.y*v2.y;
|
||||
}
|
||||
|
||||
/// 2D vector cross product analog.
|
||||
/// The cross product of 2D vectors results in a 3D vector with only a z component.
|
||||
/// This function returns the magnitude of the z value.
|
||||
static inline cpFloat cpvcross(const cpVect v1, const cpVect v2)
|
||||
{
|
||||
return v1.x*v2.y - v1.y*v2.x;
|
||||
}
|
||||
|
||||
/// Returns a perpendicular vector. (90 degree rotation)
|
||||
static inline cpVect cpvperp(const cpVect v)
|
||||
{
|
||||
return cpv(-v.y, v.x);
|
||||
}
|
||||
|
||||
/// Returns a perpendicular vector. (-90 degree rotation)
|
||||
static inline cpVect cpvrperp(const cpVect v)
|
||||
{
|
||||
return cpv(v.y, -v.x);
|
||||
}
|
||||
|
||||
/// Returns the vector projection of v1 onto v2.
|
||||
static inline cpVect cpvproject(const cpVect v1, const cpVect v2)
|
||||
{
|
||||
return cpvmult(v2, cpvdot(v1, v2)/cpvdot(v2, v2));
|
||||
}
|
||||
|
||||
/// Returns the unit length vector for the given angle (in radians).
|
||||
static inline cpVect cpvforangle(const cpFloat a)
|
||||
{
|
||||
return cpv(cpfcos(a), cpfsin(a));
|
||||
}
|
||||
|
||||
/// Returns the angular direction v is pointing in (in radians).
|
||||
static inline cpFloat cpvtoangle(const cpVect v)
|
||||
{
|
||||
return cpfatan2(v.y, v.x);
|
||||
}
|
||||
|
||||
/// Uses complex number multiplication to rotate v1 by v2. Scaling will occur if v1 is not a unit vector.
|
||||
static inline cpVect cpvrotate(const cpVect v1, const cpVect v2)
|
||||
{
|
||||
return cpv(v1.x*v2.x - v1.y*v2.y, v1.x*v2.y + v1.y*v2.x);
|
||||
}
|
||||
|
||||
/// Inverse of cpvrotate().
|
||||
static inline cpVect cpvunrotate(const cpVect v1, const cpVect v2)
|
||||
{
|
||||
return cpv(v1.x*v2.x + v1.y*v2.y, v1.y*v2.x - v1.x*v2.y);
|
||||
}
|
||||
|
||||
/// Returns the squared length of v. Faster than cpvlength() when you only need to compare lengths.
|
||||
static inline cpFloat cpvlengthsq(const cpVect v)
|
||||
{
|
||||
return cpvdot(v, v);
|
||||
}
|
||||
|
||||
/// Returns the length of v.
|
||||
static inline cpFloat cpvlength(const cpVect v)
|
||||
{
|
||||
return cpfsqrt(cpvdot(v, v));
|
||||
}
|
||||
|
||||
/// Linearly interpolate between v1 and v2.
|
||||
static inline cpVect cpvlerp(const cpVect v1, const cpVect v2, const cpFloat t)
|
||||
{
|
||||
return cpvadd(cpvmult(v1, 1.0f - t), cpvmult(v2, t));
|
||||
}
|
||||
|
||||
/// Returns a normalized copy of v.
|
||||
static inline cpVect cpvnormalize(const cpVect v)
|
||||
{
|
||||
// Neat trick I saw somewhere to avoid div/0.
|
||||
return cpvmult(v, 1.0f/(cpvlength(v) + CPFLOAT_MIN));
|
||||
}
|
||||
|
||||
/// Spherical linearly interpolate between v1 and v2.
|
||||
static inline cpVect
|
||||
cpvslerp(const cpVect v1, const cpVect v2, const cpFloat t)
|
||||
{
|
||||
cpFloat dot = cpvdot(cpvnormalize(v1), cpvnormalize(v2));
|
||||
cpFloat omega = cpfacos(cpfclamp(dot, -1.0f, 1.0f));
|
||||
|
||||
if(omega < 1e-3){
|
||||
// If the angle between two vectors is very small, lerp instead to avoid precision issues.
|
||||
return cpvlerp(v1, v2, t);
|
||||
} else {
|
||||
cpFloat denom = 1.0f/cpfsin(omega);
|
||||
return cpvadd(cpvmult(v1, cpfsin((1.0f - t)*omega)*denom), cpvmult(v2, cpfsin(t*omega)*denom));
|
||||
}
|
||||
}
|
||||
|
||||
/// Spherical linearly interpolate between v1 towards v2 by no more than angle a radians
|
||||
static inline cpVect
|
||||
cpvslerpconst(const cpVect v1, const cpVect v2, const cpFloat a)
|
||||
{
|
||||
cpFloat dot = cpvdot(cpvnormalize(v1), cpvnormalize(v2));
|
||||
cpFloat omega = cpfacos(cpfclamp(dot, -1.0f, 1.0f));
|
||||
|
||||
return cpvslerp(v1, v2, cpfmin(a, omega)/omega);
|
||||
}
|
||||
|
||||
/// Clamp v to length len.
|
||||
static inline cpVect cpvclamp(const cpVect v, const cpFloat len)
|
||||
{
|
||||
return (cpvdot(v,v) > len*len) ? cpvmult(cpvnormalize(v), len) : v;
|
||||
}
|
||||
|
||||
/// Linearly interpolate between v1 towards v2 by distance d.
|
||||
static inline cpVect cpvlerpconst(cpVect v1, cpVect v2, cpFloat d)
|
||||
{
|
||||
return cpvadd(v1, cpvclamp(cpvsub(v2, v1), d));
|
||||
}
|
||||
|
||||
/// Returns the distance between v1 and v2.
|
||||
static inline cpFloat cpvdist(const cpVect v1, const cpVect v2)
|
||||
{
|
||||
return cpvlength(cpvsub(v1, v2));
|
||||
}
|
||||
|
||||
/// Returns the squared distance between v1 and v2. Faster than cpvdist() when you only need to compare distances.
|
||||
static inline cpFloat cpvdistsq(const cpVect v1, const cpVect v2)
|
||||
{
|
||||
return cpvlengthsq(cpvsub(v1, v2));
|
||||
}
|
||||
|
||||
/// Returns true if the distance between v1 and v2 is less than dist.
|
||||
static inline cpBool cpvnear(const cpVect v1, const cpVect v2, const cpFloat dist)
|
||||
{
|
||||
return cpvdistsq(v1, v2) < dist*dist;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
/// @defgroup cpMat2x2 cpMat2x2
|
||||
/// 2x2 matrix type used for tensors and such.
|
||||
/// @{
|
||||
|
||||
// NUKE
|
||||
static inline cpMat2x2
|
||||
cpMat2x2New(cpFloat a, cpFloat b, cpFloat c, cpFloat d)
|
||||
{
|
||||
cpMat2x2 m = {a, b, c, d};
|
||||
return m;
|
||||
}
|
||||
|
||||
static inline cpVect
|
||||
cpMat2x2Transform(cpMat2x2 m, cpVect v)
|
||||
{
|
||||
return cpv(v.x*m.a + v.y*m.b, v.x*m.c + v.y*m.d);
|
||||
}
|
||||
|
||||
///@}
|
||||
|
||||
#endif
|
|
@ -1,318 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#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); {
|
||||
vfprintf(stderr, message, vargs);
|
||||
fprintf(stderr, "\n");
|
||||
} va_end(vargs);
|
||||
|
||||
fprintf(stderr, "\tFailed condition: %s\n", condition);
|
||||
fprintf(stderr, "\tSource:%s:%d\n", file, line);
|
||||
}
|
||||
|
||||
#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)M_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)M_PI*r + 2.0f*cpvdist(a, b));
|
||||
}
|
||||
|
||||
cpFloat
|
||||
cpMomentForPoly(cpFloat m, const int count, const cpVect *verts, cpVect offset, cpFloat r)
|
||||
{
|
||||
// TODO account for radius.
|
||||
if(count == 2) return cpMomentForSegment(m, verts[0], verts[1], 0.0f);
|
||||
|
||||
cpFloat sum1 = 0.0f;
|
||||
cpFloat sum2 = 0.0f;
|
||||
for(int i=0; i<count; i++){
|
||||
cpVect v1 = cpvadd(verts[i], offset);
|
||||
cpVect v2 = cpvadd(verts[(i+1)%count], offset);
|
||||
|
||||
cpFloat a = cpvcross(v2, v1);
|
||||
cpFloat b = cpvdot(v1, v1) + cpvdot(v1, v2) + cpvdot(v2, v2);
|
||||
|
||||
sum1 += a*b;
|
||||
sum2 += a;
|
||||
}
|
||||
|
||||
return (m*sum1)/(6.0f*sum2);
|
||||
}
|
||||
|
||||
cpFloat
|
||||
cpAreaForPoly(const int count, const cpVect *verts, cpFloat r)
|
||||
{
|
||||
cpFloat area = 0.0f;
|
||||
cpFloat perimeter = 0.0f;
|
||||
for(int i=0; i<count; i++){
|
||||
cpVect v1 = verts[i];
|
||||
cpVect v2 = verts[(i+1)%count];
|
||||
|
||||
area += cpvcross(v1, v2);
|
||||
perimeter += cpvdist(v1, v2);
|
||||
}
|
||||
|
||||
return r*(M_PI*cpfabs(r) + perimeter) + area/2.0f;
|
||||
}
|
||||
|
||||
cpVect
|
||||
cpCentroidForPoly(const int count, const cpVect *verts)
|
||||
{
|
||||
cpFloat sum = 0.0f;
|
||||
cpVect vsum = cpvzero;
|
||||
|
||||
for(int i=0; i<count; i++){
|
||||
cpVect v1 = verts[i];
|
||||
cpVect v2 = verts[(i+1)%count];
|
||||
cpFloat cross = cpvcross(v1, v2);
|
||||
|
||||
sum += cross;
|
||||
vsum = cpvadd(vsum, cpvmult(cpvadd(v1, v2), cross));
|
||||
}
|
||||
|
||||
return cpvmult(vsum, 1.0f/(3.0f*sum));
|
||||
}
|
||||
|
||||
//void
|
||||
//cpRecenterPoly(const int count, cpVect *verts){
|
||||
// cpVect centroid = cpCentroidForPoly(count, verts);
|
||||
//
|
||||
// for(int i=0; i<count; i++){
|
||||
// verts[i] = cpvsub(verts[i], centroid);
|
||||
// }
|
||||
//}
|
||||
|
||||
cpFloat
|
||||
cpMomentForBox(cpFloat m, cpFloat width, cpFloat height)
|
||||
{
|
||||
return m*(width*width + height*height)/12.0f;
|
||||
}
|
||||
|
||||
cpFloat
|
||||
cpMomentForBox2(cpFloat m, cpBB box)
|
||||
{
|
||||
cpFloat width = box.r - box.l;
|
||||
cpFloat height = box.t - box.b;
|
||||
cpVect offset = cpvmult(cpv(box.l + box.r, box.b + box.t), 0.5f);
|
||||
|
||||
// TODO: NaN when offset is 0 and m is INFINITY
|
||||
return cpMomentForBox(m, width, height) + m*cpvlengthsq(offset);
|
||||
}
|
||||
|
||||
//MARK: Quick Hull
|
||||
|
||||
void
|
||||
cpLoopIndexes(const cpVect *verts, int count, int *start, int *end)
|
||||
{
|
||||
(*start) = (*end) = 0;
|
||||
cpVect min = verts[0];
|
||||
cpVect max = min;
|
||||
|
||||
for(int i=1; i<count; i++){
|
||||
cpVect v = verts[i];
|
||||
|
||||
if(v.x < min.x || (v.x == min.x && v.y < min.y)){
|
||||
min = v;
|
||||
(*start) = i;
|
||||
} else if(v.x > max.x || (v.x == max.x && v.y > max.y)){
|
||||
max = v;
|
||||
(*end) = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define SWAP(__A__, __B__) {cpVect __TMP__ = __A__; __A__ = __B__; __B__ = __TMP__;}
|
||||
|
||||
static int
|
||||
QHullPartition(cpVect *verts, int count, cpVect a, cpVect b, cpFloat tol)
|
||||
{
|
||||
if(count == 0) return 0;
|
||||
|
||||
cpFloat max = 0;
|
||||
int pivot = 0;
|
||||
|
||||
cpVect delta = cpvsub(b, a);
|
||||
cpFloat valueTol = tol*cpvlength(delta);
|
||||
|
||||
int head = 0;
|
||||
for(int tail = count-1; head <= tail;){
|
||||
cpFloat value = cpvcross(cpvsub(verts[head], a), delta);
|
||||
if(value > valueTol){
|
||||
if(value > max){
|
||||
max = value;
|
||||
pivot = head;
|
||||
}
|
||||
|
||||
head++;
|
||||
} else {
|
||||
SWAP(verts[head], verts[tail]);
|
||||
tail--;
|
||||
}
|
||||
}
|
||||
|
||||
// move the new pivot to the front if it's not already there.
|
||||
if(pivot != 0) SWAP(verts[0], verts[pivot]);
|
||||
return head;
|
||||
}
|
||||
|
||||
static int
|
||||
QHullReduce(cpFloat tol, cpVect *verts, int count, cpVect a, cpVect pivot, cpVect b, cpVect *result)
|
||||
{
|
||||
if(count < 0){
|
||||
return 0;
|
||||
} else if(count == 0) {
|
||||
result[0] = pivot;
|
||||
return 1;
|
||||
} else {
|
||||
int left_count = QHullPartition(verts, count, a, pivot, tol);
|
||||
int index = QHullReduce(tol, verts + 1, left_count - 1, a, verts[0], pivot, result);
|
||||
|
||||
result[index++] = pivot;
|
||||
|
||||
int right_count = QHullPartition(verts + left_count, count - left_count, pivot, b, tol);
|
||||
return index + QHullReduce(tol, verts + left_count + 1, right_count - 1, pivot, verts[left_count], b, result + index);
|
||||
}
|
||||
}
|
||||
|
||||
// QuickHull seemed like a neat algorithm, and efficient-ish for large input sets.
|
||||
// My implementation performs an in place reduction using the result array as scratch space.
|
||||
int
|
||||
cpConvexHull(int count, const cpVect *verts, cpVect *result, int *first, cpFloat tol)
|
||||
{
|
||||
if(verts != result){
|
||||
// Copy the line vertexes into the empty part of the result polyline to use as a scratch buffer.
|
||||
memcpy(result, verts, count*sizeof(cpVect));
|
||||
}
|
||||
|
||||
// Degenerate case, all points are the same.
|
||||
int start, end;
|
||||
cpLoopIndexes(verts, count, &start, &end);
|
||||
if(start == end){
|
||||
if(first) (*first) = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
SWAP(result[0], result[start]);
|
||||
SWAP(result[1], result[end == 0 ? start : end]);
|
||||
|
||||
cpVect a = result[0];
|
||||
cpVect b = result[1];
|
||||
|
||||
if(first) (*first) = start;
|
||||
return QHullReduce(tol, result + 2, count - 2, a, b, a, result + 1) + 1;
|
||||
}
|
||||
|
||||
//MARK: Alternate Block Iterators
|
||||
|
||||
#if defined(__has_extension)
|
||||
#if __has_extension(blocks)
|
||||
|
||||
static void IteratorFunc(void *ptr, void (^block)(void *ptr)){block(ptr);}
|
||||
|
||||
void cpSpaceEachBody_b(cpSpace *space, void (^block)(cpBody *body)){
|
||||
cpSpaceEachBody(space, (cpSpaceBodyIteratorFunc)IteratorFunc, block);
|
||||
}
|
||||
|
||||
void cpSpaceEachShape_b(cpSpace *space, void (^block)(cpShape *shape)){
|
||||
cpSpaceEachShape(space, (cpSpaceShapeIteratorFunc)IteratorFunc, block);
|
||||
}
|
||||
|
||||
void cpSpaceEachConstraint_b(cpSpace *space, void (^block)(cpConstraint *constraint)){
|
||||
cpSpaceEachConstraint(space, (cpSpaceConstraintIteratorFunc)IteratorFunc, block);
|
||||
}
|
||||
|
||||
static void BodyIteratorFunc(cpBody *body, void *ptr, void (^block)(void *ptr)){block(ptr);}
|
||||
|
||||
void cpBodyEachShape_b(cpBody *body, void (^block)(cpShape *shape)){
|
||||
cpBodyEachShape(body, (cpBodyShapeIteratorFunc)BodyIteratorFunc, block);
|
||||
}
|
||||
|
||||
void cpBodyEachConstraint_b(cpBody *body, void (^block)(cpConstraint *constraint)){
|
||||
cpBodyEachConstraint(body, (cpBodyConstraintIteratorFunc)BodyIteratorFunc, block);
|
||||
}
|
||||
|
||||
void cpBodyEachArbiter_b(cpBody *body, void (^block)(cpArbiter *arbiter)){
|
||||
cpBodyEachArbiter(body, (cpBodyArbiterIteratorFunc)BodyIteratorFunc, block);
|
||||
}
|
||||
|
||||
static void PointQueryIteratorFunc(cpShape *shape, cpVect p, cpFloat d, cpVect g, cpSpacePointQueryBlock block){block(shape, p, d, g);}
|
||||
void cpSpacePointQuery_b(cpSpace *space, cpVect point, cpFloat maxDistance, cpShapeFilter filter, cpSpacePointQueryBlock block){
|
||||
cpSpacePointQuery(space, point, maxDistance, filter, (cpSpacePointQueryFunc)PointQueryIteratorFunc, block);
|
||||
}
|
||||
|
||||
static void SegmentQueryIteratorFunc(cpShape *shape, cpVect p, cpVect n, cpFloat t, cpSpaceSegmentQueryBlock block){block(shape, p, n, t);}
|
||||
void cpSpaceSegmentQuery_b(cpSpace *space, cpVect start, cpVect end, cpFloat radius, cpShapeFilter filter, cpSpaceSegmentQueryBlock block){
|
||||
cpSpaceSegmentQuery(space, start, end, radius, filter, (cpSpaceSegmentQueryFunc)SegmentQueryIteratorFunc, block);
|
||||
}
|
||||
|
||||
void cpSpaceBBQuery_b(cpSpace *space, cpBB bb, cpShapeFilter filter, cpSpaceBBQueryBlock block){
|
||||
cpSpaceBBQuery(space, bb, filter, (cpSpaceBBQueryFunc)IteratorFunc, block);
|
||||
}
|
||||
|
||||
static void ShapeQueryIteratorFunc(cpShape *shape, cpContactPointSet *points, cpSpaceShapeQueryBlock block){block(shape, points);}
|
||||
cpBool cpSpaceShapeQuery_b(cpSpace *space, cpShape *shape, cpSpaceShapeQueryBlock block){
|
||||
return cpSpaceShapeQuery(space, shape, (cpSpaceShapeQueryFunc)ShapeQueryIteratorFunc, block);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "chipmunk_ffi.h"
|
|
@ -1,496 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "chipmunk/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; i<set.count; i++){
|
||||
// Contact points are relative to body CoGs;
|
||||
cpVect p1 = cpvadd(arb->body_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; i<count; i++){
|
||||
// Convert back to CoG relative offsets.
|
||||
cpVect p1 = set->points[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); i<count; i++){
|
||||
struct cpContact *con = &contacts[i];
|
||||
sum = cpvadd(sum, cpvrotate(n, cpv(con->jnAcc, 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); i<count; i++){
|
||||
struct cpContact *con = &contacts[i];
|
||||
cpFloat jnAcc = con->jnAcc;
|
||||
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; i<info->count; 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; j<arb->count; 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; i<arb->count; 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; i<arb->count; 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; i<arb->count; 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)));
|
||||
}
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#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 *= 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; i<arr->num; 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; i<arr->num; i++) freeFunc(arr->arr[i]);
|
||||
}
|
||||
|
||||
cpBool
|
||||
cpArrayContains(cpArray *arr, void *ptr)
|
||||
{
|
||||
for(int i=0; i<arr->num; i++)
|
||||
if(arr->arr[i] == ptr) return cpTrue;
|
||||
|
||||
return cpFalse;
|
||||
}
|
|
@ -1,896 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "stdlib.h"
|
||||
#include "stdio.h"
|
||||
|
||||
#include "chipmunk/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; i<count; i++) PairRecycle(tree, buffer + i);
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
ThreadUnlink(Thread thread)
|
||||
{
|
||||
Pair *next = thread.next;
|
||||
Pair *prev = thread.prev;
|
||||
|
||||
if(next){
|
||||
if(next->a.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; i<count; i++) NodeRecycle(tree, buffer + i);
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
NodeSetA(Node *node, Node *value)
|
||||
{
|
||||
node->A = 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; i<count; i++) bb = cpBBMerge(bb, nodes[i]->bb);
|
||||
|
||||
// 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; i<count; i++){
|
||||
bounds[2*i + 0] = nodes[i]->bb.l;
|
||||
bounds[2*i + 1] = nodes[i]->bb.r;
|
||||
}
|
||||
} else {
|
||||
for(int i=0; i<count; i++){
|
||||
bounds[2*i + 0] = nodes[i]->bb.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; i<count; i++) node = SubtreeInsert(node, nodes[i], tree);
|
||||
return node;
|
||||
}
|
||||
|
||||
// Recurse and build the node!
|
||||
return NodeNew(tree,
|
||||
partitionNodes(tree, nodes, right),
|
||||
partitionNodes(tree, nodes + right, count - right)
|
||||
);
|
||||
}
|
||||
|
||||
//static void
|
||||
//cpBBTreeOptimizeIncremental(cpBBTree *tree, int passes)
|
||||
//{
|
||||
// for(int i=0; i<passes; i++){
|
||||
// Node *root = tree->root;
|
||||
// Node *node = root;
|
||||
// int bit = 0;
|
||||
// unsigned int path = tree->opath;
|
||||
//
|
||||
// while(!NodeIsLeaf(node)){
|
||||
// node = (path&(1<<bit) ? node->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 <GLUT/glut.h>
|
||||
|
||||
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
|
|
@ -1,626 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <float.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#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 = 1.0f/mass;
|
||||
cpAssertSaneBody(body);
|
||||
}
|
||||
|
||||
cpFloat
|
||||
cpBodyGetMoment(const cpBody *body)
|
||||
{
|
||||
return body->i;
|
||||
}
|
||||
|
||||
void
|
||||
cpBodySetMoment(cpBody *body, cpFloat moment)
|
||||
{
|
||||
cpAssertHard(moment >= 0.0f, "Moment of Inertia must be positive.");
|
||||
|
||||
cpBodyActivate(body);
|
||||
body->i = moment;
|
||||
body->i_inv = 1.0f/moment;
|
||||
cpAssertSaneBody(body);
|
||||
}
|
||||
|
||||
cpVect
|
||||
cpBodyGetRotation(const cpBody *body)
|
||||
{
|
||||
return cpv(body->transform.a, body->transform.b);
|
||||
}
|
||||
|
||||
void
|
||||
cpBodyAddShape(cpBody *body, cpShape *shape)
|
||||
{
|
||||
cpShape *next = body->shapeList;
|
||||
if(next) next->prev = shape;
|
||||
|
||||
shape->next = next;
|
||||
body->shapeList = shape;
|
||||
|
||||
if(shape->massInfo.m > 0.0f){
|
||||
cpBodyAccumulateMassFromShapes(body);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
cpBodyRemoveShape(cpBody *body, cpShape *shape)
|
||||
{
|
||||
cpShape *prev = shape->prev;
|
||||
cpShape *next = shape->next;
|
||||
|
||||
if(prev){
|
||||
prev->next = next;
|
||||
} else {
|
||||
body->shapeList = next;
|
||||
}
|
||||
|
||||
if(next){
|
||||
next->prev = prev;
|
||||
}
|
||||
|
||||
shape->prev = NULL;
|
||||
shape->next = NULL;
|
||||
|
||||
if(cpBodyGetType(body) == CP_BODY_TYPE_DYNAMIC && shape->massInfo.m > 0.0f){
|
||||
cpBodyAccumulateMassFromShapes(body);
|
||||
}
|
||||
}
|
||||
|
||||
static cpConstraint *
|
||||
filterConstraints(cpConstraint *node, cpBody *body, cpConstraint *filter)
|
||||
{
|
||||
if(node == filter){
|
||||
return cpConstraintNext(node, body);
|
||||
} else if(node->a == body){
|
||||
node->next_a = filterConstraints(node->next_a, body, filter);
|
||||
} else {
|
||||
node->next_b = filterConstraints(node->next_b, body, filter);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
void
|
||||
cpBodyRemoveConstraint(cpBody *body, cpConstraint *constraint)
|
||||
{
|
||||
body->constraintList = filterConstraints(body->constraintList, body, constraint);
|
||||
}
|
||||
|
||||
// 'p' is the position of the CoG
|
||||
static void
|
||||
SetTransform(cpBody *body, cpVect p, cpFloat a)
|
||||
{
|
||||
cpVect rot = cpvforangle(a);
|
||||
cpVect c = body->cog;
|
||||
|
||||
body->transform = cpTransformNewTranspose(
|
||||
rot.x, -rot.y, p.x - (c.x*rot.x - c.y*rot.y),
|
||||
rot.y, rot.x, p.y - (c.x*rot.y + c.y*rot.x)
|
||||
);
|
||||
}
|
||||
|
||||
static inline cpFloat
|
||||
SetAngle(cpBody *body, cpFloat a)
|
||||
{
|
||||
body->a = a;
|
||||
cpAssertSaneBody(body);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
cpVect
|
||||
cpBodyGetPosition(const cpBody *body)
|
||||
{
|
||||
return cpTransformPoint(body->transform, cpvzero);
|
||||
}
|
||||
|
||||
void
|
||||
cpBodySetPosition(cpBody *body, cpVect position)
|
||||
{
|
||||
cpBodyActivate(body);
|
||||
cpVect p = body->p = cpvadd(cpTransformVect(body->transform, body->cog), position);
|
||||
cpAssertSaneBody(body);
|
||||
|
||||
SetTransform(body, p, body->a);
|
||||
}
|
||||
|
||||
cpVect
|
||||
cpBodyGetCenterOfGravity(const cpBody *body)
|
||||
{
|
||||
return body->cog;
|
||||
}
|
||||
|
||||
void
|
||||
cpBodySetCenterOfGravity(cpBody *body, cpVect cog)
|
||||
{
|
||||
cpBodyActivate(body);
|
||||
body->cog = cog;
|
||||
cpAssertSaneBody(body);
|
||||
}
|
||||
|
||||
cpVect
|
||||
cpBodyGetVelocity(const cpBody *body)
|
||||
{
|
||||
return body->v;
|
||||
}
|
||||
|
||||
void
|
||||
cpBodySetVelocity(cpBody *body, cpVect velocity)
|
||||
{
|
||||
cpBodyActivate(body);
|
||||
body->v = velocity;
|
||||
cpAssertSaneBody(body);
|
||||
}
|
||||
|
||||
cpVect
|
||||
cpBodyGetForce(const cpBody *body)
|
||||
{
|
||||
return body->f;
|
||||
}
|
||||
|
||||
void
|
||||
cpBodySetForce(cpBody *body, cpVect force)
|
||||
{
|
||||
cpBodyActivate(body);
|
||||
body->f = force;
|
||||
cpAssertSaneBody(body);
|
||||
}
|
||||
|
||||
cpFloat
|
||||
cpBodyGetAngle(const cpBody *body)
|
||||
{
|
||||
return body->a;
|
||||
}
|
||||
|
||||
void
|
||||
cpBodySetAngle(cpBody *body, cpFloat angle)
|
||||
{
|
||||
cpBodyActivate(body);
|
||||
SetAngle(body, angle);
|
||||
|
||||
SetTransform(body, body->p, angle);
|
||||
}
|
||||
|
||||
cpFloat
|
||||
cpBodyGetAngularVelocity(const cpBody *body)
|
||||
{
|
||||
return body->w;
|
||||
}
|
||||
|
||||
void
|
||||
cpBodySetAngularVelocity(cpBody *body, cpFloat angularVelocity)
|
||||
{
|
||||
cpBodyActivate(body);
|
||||
body->w = angularVelocity;
|
||||
cpAssertSaneBody(body);
|
||||
}
|
||||
|
||||
cpFloat
|
||||
cpBodyGetTorque(const cpBody *body)
|
||||
{
|
||||
return body->t;
|
||||
}
|
||||
|
||||
void
|
||||
cpBodySetTorque(cpBody *body, cpFloat torque)
|
||||
{
|
||||
cpBodyActivate(body);
|
||||
body->t = torque;
|
||||
cpAssertSaneBody(body);
|
||||
}
|
||||
|
||||
cpDataPointer
|
||||
cpBodyGetUserData(const cpBody *body)
|
||||
{
|
||||
return body->userData;
|
||||
}
|
||||
|
||||
void
|
||||
cpBodySetUserData(cpBody *body, cpDataPointer userData)
|
||||
{
|
||||
body->userData = userData;
|
||||
}
|
||||
|
||||
void
|
||||
cpBodySetVelocityUpdateFunc(cpBody *body, cpBodyVelocityFunc velocityFunc)
|
||||
{
|
||||
body->velocity_func = velocityFunc;
|
||||
}
|
||||
|
||||
void
|
||||
cpBodySetPositionUpdateFunc(cpBody *body, cpBodyPositionFunc positionFunc)
|
||||
{
|
||||
body->position_func = positionFunc;
|
||||
}
|
||||
|
||||
void
|
||||
cpBodyUpdateVelocity(cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt)
|
||||
{
|
||||
// Skip kinematic bodies.
|
||||
if(cpBodyGetType(body) == CP_BODY_TYPE_KINEMATIC) return;
|
||||
|
||||
cpAssertSoft(body->m > 0.0f && body->i > 0.0f, "Body's mass and moment must be positive to simulate. (Mass: %f Moment: %f)", body->m, body->i);
|
||||
|
||||
body->v = cpvadd(cpvmult(body->v, damping), cpvmult(cpvadd(gravity, cpvmult(body->f, body->m_inv)), dt));
|
||||
body->w = body->w*damping + body->t*body->i_inv*dt;
|
||||
|
||||
// Reset forces.
|
||||
body->f = cpvzero;
|
||||
body->t = 0.0f;
|
||||
|
||||
cpAssertSaneBody(body);
|
||||
}
|
||||
|
||||
void
|
||||
cpBodyUpdatePosition(cpBody *body, cpFloat dt)
|
||||
{
|
||||
cpVect p = body->p = cpvadd(body->p, cpvmult(cpvadd(body->v, body->v_bias), dt));
|
||||
cpFloat a = SetAngle(body, body->a + (body->w + body->w_bias)*dt);
|
||||
SetTransform(body, p, a);
|
||||
|
||||
body->v_bias = cpvzero;
|
||||
body->w_bias = 0.0f;
|
||||
|
||||
cpAssertSaneBody(body);
|
||||
}
|
||||
|
||||
cpVect
|
||||
cpBodyLocalToWorld(const cpBody *body, const cpVect point)
|
||||
{
|
||||
return cpTransformPoint(body->transform, point);
|
||||
}
|
||||
|
||||
cpVect
|
||||
cpBodyWorldToLocal(const cpBody *body, const cpVect point)
|
||||
{
|
||||
return cpTransformPoint(cpTransformRigidInverse(body->transform), point);
|
||||
}
|
||||
|
||||
void
|
||||
cpBodyApplyForceAtWorldPoint(cpBody *body, cpVect force, cpVect point)
|
||||
{
|
||||
cpBodyActivate(body);
|
||||
body->f = cpvadd(body->f, force);
|
||||
|
||||
cpVect r = cpvsub(point, cpTransformPoint(body->transform, body->cog));
|
||||
body->t += cpvcross(r, force);
|
||||
}
|
||||
|
||||
void
|
||||
cpBodyApplyForceAtLocalPoint(cpBody *body, cpVect force, cpVect point)
|
||||
{
|
||||
cpBodyApplyForceAtWorldPoint(body, cpTransformVect(body->transform, force), cpTransformPoint(body->transform, point));
|
||||
}
|
||||
|
||||
void
|
||||
cpBodyApplyImpulseAtWorldPoint(cpBody *body, cpVect impulse, cpVect point)
|
||||
{
|
||||
cpBodyActivate(body);
|
||||
|
||||
cpVect r = cpvsub(point, cpTransformPoint(body->transform, body->cog));
|
||||
apply_impulse(body, impulse, r);
|
||||
}
|
||||
|
||||
void
|
||||
cpBodyApplyImpulseAtLocalPoint(cpBody *body, cpVect impulse, cpVect point)
|
||||
{
|
||||
cpBodyApplyImpulseAtWorldPoint(body, cpTransformVect(body->transform, impulse), cpTransformPoint(body->transform, point));
|
||||
}
|
||||
|
||||
cpVect
|
||||
cpBodyGetVelocityAtLocalPoint(const cpBody *body, cpVect point)
|
||||
{
|
||||
cpVect r = cpTransformVect(body->transform, cpvsub(point, body->cog));
|
||||
return cpvadd(body->v, cpvmult(cpvperp(r), body->w));
|
||||
}
|
||||
|
||||
cpVect
|
||||
cpBodyGetVelocityAtWorldPoint(const cpBody *body, cpVect point)
|
||||
{
|
||||
cpVect r = cpvsub(point, cpTransformPoint(body->transform, body->cog));
|
||||
return cpvadd(body->v, cpvmult(cpvperp(r), body->w));
|
||||
}
|
||||
|
||||
cpFloat
|
||||
cpBodyKineticEnergy(const cpBody *body)
|
||||
{
|
||||
// Need to do some fudging to avoid NaNs
|
||||
cpFloat vsq = cpvdot(body->v, body->v);
|
||||
cpFloat wsq = body->w*body->w;
|
||||
return (vsq ? vsq*body->m : 0.0f) + (wsq ? wsq*body->i : 0.0f);
|
||||
}
|
||||
|
||||
void
|
||||
cpBodyEachShape(cpBody *body, cpBodyShapeIteratorFunc func, void *data)
|
||||
{
|
||||
cpShape *shape = body->shapeList;
|
||||
while(shape){
|
||||
cpShape *next = shape->next;
|
||||
func(body, shape, data);
|
||||
shape = next;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
cpBodyEachConstraint(cpBody *body, cpBodyConstraintIteratorFunc func, void *data)
|
||||
{
|
||||
cpConstraint *constraint = body->constraintList;
|
||||
while(constraint){
|
||||
cpConstraint *next = cpConstraintNext(constraint, body);
|
||||
func(body, constraint, data);
|
||||
constraint = next;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
cpBodyEachArbiter(cpBody *body, cpBodyArbiterIteratorFunc func, void *data)
|
||||
{
|
||||
cpArbiter *arb = body->arbiterList;
|
||||
while(arb){
|
||||
cpArbiter *next = cpArbiterNext(arb, body);
|
||||
|
||||
cpBool swapped = arb->swapped; {
|
||||
arb->swapped = (body == arb->body_b);
|
||||
func(body, arb, data);
|
||||
} arb->swapped = swapped;
|
||||
|
||||
arb = next;
|
||||
}
|
||||
}
|
|
@ -1,734 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "chipmunk/chipmunk_private.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 ENABLE_CACHING 1
|
||||
|
||||
#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<count; i++){
|
||||
cpVect v = planes[i].v0;
|
||||
cpFloat d = cpvdot(v, n);
|
||||
if(d > 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 d = cpvlength(p);
|
||||
cpVect n = cpvmult(p, 1.0f/(d + CPFLOAT_MIN));
|
||||
|
||||
struct ClosestPoints points = {pa, pb, n, d, id};
|
||||
return points;
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: EPA Functions
|
||||
|
||||
static inline cpFloat
|
||||
ClosestDist(const cpVect v0,const cpVect v1)
|
||||
{
|
||||
return cpvlengthsq(LerpT(v0, v1, ClosestT(v0, v1)));
|
||||
}
|
||||
|
||||
static inline cpBool
|
||||
CheckArea(cpVect v1, cpVect v2)
|
||||
{
|
||||
return (v1.x*v2.y) > (v1.y*v2.x);
|
||||
}
|
||||
|
||||
// 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<count; i=j, j++){
|
||||
cpFloat d = ClosestDist(hull[i].ab, hull[j].ab);
|
||||
if(d < minDist){
|
||||
minDist = d;
|
||||
mini = i;
|
||||
}
|
||||
}
|
||||
|
||||
struct MinkowskiPoint v0 = hull[mini];
|
||||
struct MinkowskiPoint v1 = hull[(mini + 1)%count];
|
||||
cpAssertSoft(!cpveql(v0.ab, v1.ab), "Internal Error: EPA vertexes are the same (%d and %d)", mini, (mini + 1)%count);
|
||||
|
||||
// Check if there is a point on the minkowski difference beyond this edge.
|
||||
struct MinkowskiPoint p = Support(ctx, cpvperp(cpvsub(v1.ab, v0.ab)));
|
||||
|
||||
#if DRAW_EPA
|
||||
cpVect verts[count];
|
||||
for(int i=0; i<count; i++) verts[i] = hull[i].ab;
|
||||
|
||||
ChipmunkDebugDrawPolygon(count, verts, 0.0, RGBAColor(1, 1, 0, 1), RGBAColor(1, 1, 0, 0.25));
|
||||
ChipmunkDebugDrawSegment(v0.ab, v1.ab, RGBAColor(1, 0, 0, 1));
|
||||
|
||||
ChipmunkDebugDrawDot(5, p.ab, LAColor(1, 1));
|
||||
#endif
|
||||
|
||||
if(CheckArea(cpvsub(v1.ab, v0.ab), cpvadd(cpvsub(p.ab, v0.ab), cpvsub(p.ab, v1.ab))) && iteration < MAX_EPA_ITERATIONS){
|
||||
// Rebuild the convex hull by inserting p.
|
||||
struct MinkowskiPoint *hull2 = (struct MinkowskiPoint *)alloca((count + 1)*sizeof(struct MinkowskiPoint));
|
||||
int count2 = 1;
|
||||
hull2[0] = p;
|
||||
|
||||
for(int i=0; i<count; i++){
|
||||
int index = (mini + 1 + i)%count;
|
||||
|
||||
cpVect h0 = hull2[count2 - 1].ab;
|
||||
cpVect h1 = hull[index].ab;
|
||||
cpVect h2 = (i + 1 < count ? hull[(index + 1)%count] : p).ab;
|
||||
|
||||
if(CheckArea(cpvsub(h2, h0), cpvadd(cpvsub(h1, h0), cpvsub(h1, h2)))){
|
||||
hull2[count2] = hull[index];
|
||||
count2++;
|
||||
}
|
||||
}
|
||||
|
||||
return EPARecurse(ctx, count2, hull2, iteration + 1);
|
||||
} else {
|
||||
// Could not find a new point to insert, so we have found the closest edge of the minkowski difference.
|
||||
cpAssertWarn(iteration < WARN_EPA_ITERATIONS, "High EPA iterations: %d", iteration);
|
||||
return ClosestPointsNew(v0, v1);
|
||||
}
|
||||
}
|
||||
|
||||
// Find the closest points on the surface of two overlapping shapes using the EPA algorithm.
|
||||
// EPA is called from GJK when two shapes overlap.
|
||||
// This is moderately expensive step! Avoid it by adding radii to your shapes so their inner polygons won't overlap.
|
||||
static struct ClosestPoints
|
||||
EPA(const struct SupportContext *ctx, const struct MinkowskiPoint v0, const struct MinkowskiPoint v1, const struct MinkowskiPoint v2)
|
||||
{
|
||||
// TODO: allocate a NxM array here and do an in place convex hull reduction in EPARecurse
|
||||
struct MinkowskiPoint hull[3] = {v0, v1, v2};
|
||||
return EPARecurse(ctx, 3, hull, 1);
|
||||
}
|
||||
|
||||
//MARK: GJK Functions.
|
||||
|
||||
// Recursive implementatino of the GJK loop.
|
||||
static inline struct ClosestPoints
|
||||
GJKRecurse(const struct SupportContext *ctx, const struct MinkowskiPoint v0, const struct MinkowskiPoint v1, const int iteration)
|
||||
{
|
||||
if(iteration > MAX_GJK_ITERATIONS){
|
||||
cpAssertWarn(iteration < WARN_GJK_ITERATIONS, "High GJK iterations: %d", iteration);
|
||||
return ClosestPointsNew(v0, v1);
|
||||
}
|
||||
|
||||
cpVect delta = cpvsub(v1.ab, v0.ab);
|
||||
if(CheckArea(delta, cpvadd(v0.ab, v1.ab))){
|
||||
// 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(delta) : 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(
|
||||
CheckArea(cpvsub(v1.ab, p.ab), cpvadd(v1.ab, p.ab)) &&
|
||||
CheckArea(cpvadd(v0.ab, p.ab), cpvsub(v0.ab, p.ab))
|
||||
){
|
||||
// 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(cpvdot(p.ab, n) <= cpfmax(cpvdot(v0.ab, n), cpvdot(v1.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; i<count1; i++){
|
||||
for(int j=0; j<count2; j++){
|
||||
cpVect v = cpvsub(ShapePoint(ctx->shape2, 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 && ENABLE_CACHING){
|
||||
// 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);
|
||||
|
||||
cpFloat e1_denom = 1.0f/(d_e1_b - d_e1_a);
|
||||
cpFloat e2_denom = 1.0f/(d_e2_b - d_e2_a);
|
||||
|
||||
// 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; i<info.count; i++){
|
||||
// cpVect r1 = info.arr[i].r1;
|
||||
// cpVect r2 = info.arr[i].r2;
|
||||
// cpVect mid = cpvlerp(r1, r2, 0.5f);
|
||||
//
|
||||
// ChipmunkDebugDrawSegment(r1, mid, RGBAColor(1, 0, 0, 1));
|
||||
// ChipmunkDebugDrawSegment(r2, mid, RGBAColor(0, 0, 1, 1));
|
||||
// }
|
||||
// }
|
||||
|
||||
return info;
|
||||
}
|
|
@ -1,173 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "chipmunk/chipmunk_private.h"
|
||||
|
||||
// TODO: Comment me!
|
||||
|
||||
void cpConstraintDestroy(cpConstraint *constraint){}
|
||||
|
||||
void
|
||||
cpConstraintFree(cpConstraint *constraint)
|
||||
{
|
||||
if(constraint){
|
||||
cpConstraintDestroy(constraint);
|
||||
cpfree(constraint);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
cpConstraintInit(cpConstraint *constraint, const cpConstraintClass *klass, cpBody *a, cpBody *b)
|
||||
{
|
||||
constraint->klass = 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);
|
||||
}
|
|
@ -1,178 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "chipmunk/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;
|
||||
}
|
|
@ -1,216 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "chipmunk/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;
|
||||
}
|
|
@ -1,145 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "chipmunk/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;
|
||||
}
|
|
@ -1,197 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "chipmunk/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;
|
||||
}
|
|
@ -1,253 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "chipmunk/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; i<set->size; 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; i<count; i++) recycleBin(set, buffer + i);
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
cpHashSetCount(cpHashSet *set)
|
||||
{
|
||||
return set->entries;
|
||||
}
|
||||
|
||||
void *
|
||||
cpHashSetInsert(cpHashSet *set, cpHashValue hash, void *ptr, cpHashSetTransFunc trans, void *data)
|
||||
{
|
||||
cpHashValue idx = hash%set->size;
|
||||
|
||||
// Find the bin with the matching element.
|
||||
cpHashSetBin *bin = set->table[idx];
|
||||
while(bin && !set->eql(ptr, bin->elt))
|
||||
bin = bin->next;
|
||||
|
||||
// Create it if necessary.
|
||||
if(!bin){
|
||||
bin = getUnusedBin(set);
|
||||
bin->hash = hash;
|
||||
bin->elt = (trans ? trans(ptr, data) : data);
|
||||
|
||||
bin->next = set->table[idx];
|
||||
set->table[idx] = bin;
|
||||
|
||||
set->entries++;
|
||||
if(setIsFull(set)) cpHashSetResize(set);
|
||||
}
|
||||
|
||||
return bin->elt;
|
||||
}
|
||||
|
||||
void *
|
||||
cpHashSetRemove(cpHashSet *set, cpHashValue hash, void *ptr)
|
||||
{
|
||||
cpHashValue idx = hash%set->size;
|
||||
|
||||
cpHashSetBin **prev_ptr = &set->table[idx];
|
||||
cpHashSetBin *bin = set->table[idx];
|
||||
|
||||
// Find the bin
|
||||
while(bin && !set->eql(ptr, bin->elt)){
|
||||
prev_ptr = &bin->next;
|
||||
bin = bin->next;
|
||||
}
|
||||
|
||||
// Remove it if it exists.
|
||||
if(bin){
|
||||
// Update the previous linked list pointer
|
||||
(*prev_ptr) = bin->next;
|
||||
set->entries--;
|
||||
|
||||
void *elt = bin->elt;
|
||||
recycleBin(set, bin);
|
||||
|
||||
return elt;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *
|
||||
cpHashSetFind(cpHashSet *set, cpHashValue hash, void *ptr)
|
||||
{
|
||||
cpHashValue idx = hash%set->size;
|
||||
cpHashSetBin *bin = set->table[idx];
|
||||
while(bin && !set->eql(ptr, bin->elt))
|
||||
bin = bin->next;
|
||||
|
||||
return (bin ? bin->elt : set->default_value);
|
||||
}
|
||||
|
||||
void
|
||||
cpHashSetEach(cpHashSet *set, cpHashSetIteratorFunc func, void *data)
|
||||
{
|
||||
for(unsigned int i=0; i<set->size; 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; i<set->size; 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,172 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "chipmunk/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;
|
||||
}
|
|
@ -1,152 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "chipmunk/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;
|
||||
}
|
|
@ -1,317 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "chipmunk/chipmunk_private.h"
|
||||
#include "chipmunk_unsafe.h"
|
||||
|
||||
cpPolyShape *
|
||||
cpPolyShapeAlloc(void)
|
||||
{
|
||||
return (cpPolyShape *)cpcalloc(1, sizeof(cpPolyShape));
|
||||
}
|
||||
|
||||
static void
|
||||
cpPolyShapeDestroy(cpPolyShape *poly)
|
||||
{
|
||||
if(poly->count > CP_POLY_SHAPE_INLINE_ALLOC){
|
||||
cpfree(poly->planes);
|
||||
}
|
||||
}
|
||||
|
||||
static cpBB
|
||||
cpPolyShapeCacheData(cpPolyShape *poly, cpTransform transform)
|
||||
{
|
||||
int count = poly->count;
|
||||
struct cpSplittingPlane *dst = poly->planes;
|
||||
struct cpSplittingPlane *src = dst + count;
|
||||
|
||||
cpFloat l = (cpFloat)INFINITY, r = -(cpFloat)INFINITY;
|
||||
cpFloat b = (cpFloat)INFINITY, t = -(cpFloat)INFINITY;
|
||||
|
||||
for(int i=0; i<count; i++){
|
||||
cpVect v = cpTransformPoint(transform, src[i].v0);
|
||||
cpVect n = cpTransformVect(transform, src[i].n);
|
||||
|
||||
dst[i].v0 = v;
|
||||
dst[i].n = n;
|
||||
|
||||
l = cpfmin(l, v.x);
|
||||
r = cpfmax(r, v.x);
|
||||
b = cpfmin(b, v.y);
|
||||
t = cpfmax(t, v.y);
|
||||
}
|
||||
|
||||
cpFloat radius = poly->r;
|
||||
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<count; i++){
|
||||
cpVect v1 = planes[i].v0;
|
||||
if(cpvdot(planes[i].n, cpvsub(p, v1)) > 0.0f) outside = cpTrue;
|
||||
|
||||
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; i<count; i++){
|
||||
cpVect n = planes[i].n;
|
||||
cpFloat an = cpvdot(a, n);
|
||||
cpFloat d = an - cpvdot(planes[i].v0, n) - rsum;
|
||||
if(d < 0.0f) continue;
|
||||
|
||||
cpFloat bn = cpvdot(b, n);
|
||||
cpFloat t = d/(an - bn);
|
||||
if(t < 0.0f || 1.0f < t) continue;
|
||||
|
||||
cpVect point = cpvlerp(a, b, t);
|
||||
cpFloat dt = cpvcross(n, point);
|
||||
cpFloat dtMin = cpvcross(n, planes[(i - 1 + count)%count].v0);
|
||||
cpFloat dtMax = cpvcross(n, planes[i].v0);
|
||||
|
||||
if(dtMin <= dt && dt <= dtMax){
|
||||
info->shape = (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; i<count; i++){
|
||||
cpSegmentQueryInfo circle_info = {NULL, b, cpvzero, 1.0f};
|
||||
CircleSegmentQuery(&poly->shape, 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; i<count; i++){
|
||||
cpVect a = verts[(i - 1 + count)%count];
|
||||
cpVect b = verts[i];
|
||||
cpVect n = cpvnormalize(cpvrperp(cpvsub(b, a)));
|
||||
|
||||
poly->planes[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; i<count; i++) hullVerts[i] = cpTransformPoint(transform, verts[i]);
|
||||
|
||||
unsigned int hullCount = cpConvexHull(count, hullVerts, hullVerts, NULL, 0.0);
|
||||
return cpPolyShapeInitRaw(poly, body, hullCount, hullVerts, radius);
|
||||
}
|
||||
|
||||
cpPolyShape *
|
||||
cpPolyShapeInitRaw(cpPolyShape *poly, cpBody *body, int count, const cpVect *verts, cpFloat radius)
|
||||
{
|
||||
cpShapeInit((cpShape *)poly, &polyClass, body, cpPolyShapeMassInfo(0.0f, count, verts, radius));
|
||||
|
||||
SetVerts(poly, count, verts);
|
||||
poly->r = 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);
|
||||
}
|
||||
|
||||
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; i<count; i++) hullVerts[i] = cpTransformPoint(transform, verts[i]);
|
||||
|
||||
unsigned int hullCount = cpConvexHull(count, hullVerts, hullVerts, NULL, 0.0);
|
||||
cpPolyShapeSetVertsRaw(shape, hullCount, hullVerts);
|
||||
}
|
||||
|
||||
void
|
||||
cpPolyShapeSetVertsRaw(cpShape *shape, int count, cpVect *verts)
|
||||
{
|
||||
cpAssertHard(shape->klass == &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);
|
||||
}
|
|
@ -1,179 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "chipmunk/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;
|
||||
}
|
|
@ -1,160 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "chipmunk/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;
|
||||
}
|
|
@ -1,603 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "chipmunk/chipmunk_private.h"
|
||||
#include "chipmunk_unsafe.h"
|
||||
|
||||
#define CP_DefineShapeGetter(struct, type, member, name) \
|
||||
CP_DeclareShapeGetter(struct, type, name){ \
|
||||
cpAssertHard(shape->klass == &struct##Class, "shape is not a "#struct); \
|
||||
return ((struct *)shape)->member; \
|
||||
}
|
||||
|
||||
cpShape *
|
||||
cpShapeInit(cpShape *shape, const cpShapeClass *klass, cpBody *body, struct cpShapeMassInfo massInfo)
|
||||
{
|
||||
shape->klass = klass;
|
||||
|
||||
shape->body = body;
|
||||
shape->massInfo = massInfo;
|
||||
|
||||
shape->sensor = 0;
|
||||
|
||||
shape->e = 0.0f;
|
||||
shape->u = 0.0f;
|
||||
shape->surfaceV = cpvzero;
|
||||
|
||||
shape->type = 0;
|
||||
shape->filter.group = CP_NO_GROUP;
|
||||
shape->filter.categories = CP_ALL_CATEGORIES;
|
||||
shape->filter.mask = CP_ALL_CATEGORIES;
|
||||
|
||||
shape->userData = NULL;
|
||||
|
||||
shape->space = NULL;
|
||||
|
||||
shape->next = NULL;
|
||||
shape->prev = NULL;
|
||||
|
||||
return shape;
|
||||
}
|
||||
|
||||
void
|
||||
cpShapeDestroy(cpShape *shape)
|
||||
{
|
||||
if(shape->klass && shape->klass->destroy) shape->klass->destroy(shape);
|
||||
}
|
||||
|
||||
void
|
||||
cpShapeFree(cpShape *shape)
|
||||
{
|
||||
if(shape){
|
||||
cpShapeDestroy(shape);
|
||||
cpfree(shape);
|
||||
}
|
||||
}
|
||||
|
||||
cpSpace *
|
||||
cpShapeGetSpace(const cpShape *shape)
|
||||
{
|
||||
return shape->space;
|
||||
}
|
||||
|
||||
cpBody *
|
||||
cpShapeGetBody(const cpShape *shape)
|
||||
{
|
||||
return shape->body;
|
||||
}
|
||||
|
||||
void
|
||||
cpShapeSetBody(cpShape *shape, cpBody *body)
|
||||
{
|
||||
cpAssertHard(!cpShapeActive(shape), "You cannot change the body on an active shape. You must remove the shape from the space before changing the body.");
|
||||
shape->body = body;
|
||||
}
|
||||
|
||||
cpFloat cpShapeGetMass(cpShape *shape){ return shape->massInfo.m; }
|
||||
|
||||
void
|
||||
cpShapeSetMass(cpShape *shape, cpFloat mass){
|
||||
cpBody *body = shape->body;
|
||||
cpBodyActivate(body);
|
||||
|
||||
shape->massInfo.m = mass;
|
||||
cpBodyAccumulateMassFromShapes(body);
|
||||
}
|
||||
|
||||
cpFloat cpShapeGetDensity(cpShape *shape){ return shape->massInfo.m/shape->massInfo.area; }
|
||||
void cpShapeSetDensity(cpShape *shape, cpFloat density){ cpShapeSetMass(shape, density*shape->massInfo.area); }
|
||||
|
||||
cpFloat cpShapeGetMoment(cpShape *shape){ return shape->massInfo.m*shape->massInfo.i; }
|
||||
cpFloat cpShapeGetArea(cpShape *shape){ return shape->massInfo.area; }
|
||||
cpVect cpShapeGetCenterOfGravity(cpShape *shape) { return shape->massInfo.cog; }
|
||||
|
||||
cpBB
|
||||
cpShapeGetBB(const cpShape *shape)
|
||||
{
|
||||
return shape->bb;
|
||||
}
|
||||
|
||||
cpBool
|
||||
cpShapeGetSensor(const cpShape *shape)
|
||||
{
|
||||
return shape->sensor;
|
||||
}
|
||||
|
||||
void
|
||||
cpShapeSetSensor(cpShape *shape, cpBool sensor)
|
||||
{
|
||||
cpBodyActivate(shape->body);
|
||||
shape->sensor = sensor;
|
||||
}
|
||||
|
||||
cpFloat
|
||||
cpShapeGetElasticity(const cpShape *shape)
|
||||
{
|
||||
return shape->e;
|
||||
}
|
||||
|
||||
void
|
||||
cpShapeSetElasticity(cpShape *shape, cpFloat elasticity)
|
||||
{
|
||||
cpAssertHard(elasticity >= 0.0f, "Elasticity must be positive and non-zero.");
|
||||
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 and non-zero.");
|
||||
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; i<info.count; i++){
|
||||
// cpCollideShapesInfo() returns contacts with absolute positions.
|
||||
cpVect p1 = contacts[i].r1;
|
||||
cpVect p2 = 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), set.normal);
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
cpCircleShape *
|
||||
cpCircleShapeAlloc(void)
|
||||
{
|
||||
return (cpCircleShape *)cpcalloc(1, sizeof(cpCircleShape));
|
||||
}
|
||||
|
||||
static cpBB
|
||||
cpCircleShapeCacheData(cpCircleShape *circle, cpTransform transform)
|
||||
{
|
||||
cpVect c = circle->tc = cpTransformPoint(transform, circle->c);
|
||||
return cpBBNewForCircle(c, circle->r);
|
||||
}
|
||||
|
||||
static void
|
||||
cpCircleShapePointQuery(cpCircleShape *circle, cpVect p, cpPointQueryInfo *info)
|
||||
{
|
||||
cpVect delta = cpvsub(p, circle->tc);
|
||||
cpFloat d = cpvlength(delta);
|
||||
cpFloat r = circle->r;
|
||||
|
||||
info->shape = (cpShape *)circle;
|
||||
info->point = cpvadd(circle->tc, cpvmult(delta, r/d)); // TODO: div/0
|
||||
info->distance = d - r;
|
||||
|
||||
// Use up for the gradient if the distance is very small.
|
||||
info->gradient = (d > MAGIC_EPSILON ? cpvmult(delta, 1.0f/d) : cpv(0.0f, 1.0f));
|
||||
}
|
||||
|
||||
static void
|
||||
cpCircleShapeSegmentQuery(cpCircleShape *circle, cpVect a, cpVect b, cpFloat radius, cpSegmentQueryInfo *info)
|
||||
{
|
||||
CircleSegmentQuery((cpShape *)circle, circle->tc, circle->r, a, b, radius, info);
|
||||
}
|
||||
|
||||
static struct cpShapeMassInfo
|
||||
cpCircleShapeMassInfo(cpFloat mass, cpFloat radius, cpVect center)
|
||||
{
|
||||
struct cpShapeMassInfo info = {
|
||||
mass, cpMomentForCircle(1.0f, 0.0f, radius, cpvzero),
|
||||
center,
|
||||
cpAreaForCircle(0.0f, radius),
|
||||
};
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
static const cpShapeClass cpCircleShapeClass = {
|
||||
CP_CIRCLE_SHAPE,
|
||||
(cpShapeCacheDataImpl)cpCircleShapeCacheData,
|
||||
NULL,
|
||||
(cpShapePointQueryImpl)cpCircleShapePointQuery,
|
||||
(cpShapeSegmentQueryImpl)cpCircleShapeSegmentQuery,
|
||||
};
|
||||
|
||||
cpCircleShape *
|
||||
cpCircleShapeInit(cpCircleShape *circle, cpBody *body, cpFloat radius, cpVect offset)
|
||||
{
|
||||
circle->c = offset;
|
||||
circle->r = radius;
|
||||
|
||||
cpShapeInit((cpShape *)circle, &cpCircleShapeClass, body, cpCircleShapeMassInfo(0.0f, radius, offset));
|
||||
|
||||
return circle;
|
||||
}
|
||||
|
||||
cpShape *
|
||||
cpCircleShapeNew(cpBody *body, cpFloat radius, cpVect offset)
|
||||
{
|
||||
return (cpShape *)cpCircleShapeInit(cpCircleShapeAlloc(), body, radius, offset);
|
||||
}
|
||||
|
||||
cpVect
|
||||
cpCircleShapeGetOffset(const cpShape *shape)
|
||||
{
|
||||
cpAssertHard(shape->klass == &cpCircleShapeClass, "Shape is not a circle shape.");
|
||||
return ((cpCircleShape *)shape)->c;
|
||||
}
|
||||
|
||||
cpFloat
|
||||
cpCircleShapeGetRadius(const cpShape *shape)
|
||||
{
|
||||
cpAssertHard(shape->klass == &cpCircleShapeClass, "Shape is not a circle shape.");
|
||||
return ((cpCircleShape *)shape)->r;
|
||||
}
|
||||
|
||||
|
||||
cpSegmentShape *
|
||||
cpSegmentShapeAlloc(void)
|
||||
{
|
||||
return (cpSegmentShape *)cpcalloc(1, sizeof(cpSegmentShape));
|
||||
}
|
||||
|
||||
static cpBB
|
||||
cpSegmentShapeCacheData(cpSegmentShape *seg, cpTransform transform)
|
||||
{
|
||||
seg->ta = cpTransformPoint(transform, seg->a);
|
||||
seg->tb = cpTransformPoint(transform, seg->b);
|
||||
seg->tn = cpTransformVect(transform, seg->n);
|
||||
|
||||
cpFloat l,r,b,t;
|
||||
|
||||
if(seg->ta.x < seg->tb.x){
|
||||
l = seg->ta.x;
|
||||
r = seg->tb.x;
|
||||
} else {
|
||||
l = seg->tb.x;
|
||||
r = seg->ta.x;
|
||||
}
|
||||
|
||||
if(seg->ta.y < seg->tb.y){
|
||||
b = seg->ta.y;
|
||||
t = seg->tb.y;
|
||||
} else {
|
||||
b = seg->tb.y;
|
||||
t = seg->ta.y;
|
||||
}
|
||||
|
||||
cpFloat rad = seg->r;
|
||||
return cpBBNew(l - rad, b - rad, r + rad, t + rad);
|
||||
}
|
||||
|
||||
static void
|
||||
cpSegmentShapePointQuery(cpSegmentShape *seg, cpVect p, cpPointQueryInfo *info)
|
||||
{
|
||||
cpVect closest = cpClosetPointOnSegment(p, seg->ta, seg->tb);
|
||||
|
||||
cpVect delta = cpvsub(p, closest);
|
||||
cpFloat d = cpvlength(delta);
|
||||
cpFloat r = seg->r;
|
||||
cpVect g = cpvmult(delta, 1.0f/d);
|
||||
|
||||
info->shape = (cpShape *)seg;
|
||||
info->point = (d ? cpvadd(closest, cpvmult(g, r)) : closest);
|
||||
info->distance = d - r;
|
||||
|
||||
// Use the segment's normal if the distance is very small.
|
||||
info->gradient = (d > MAGIC_EPSILON ? g : seg->n);
|
||||
}
|
||||
|
||||
static void
|
||||
cpSegmentShapeSegmentQuery(cpSegmentShape *seg, cpVect a, cpVect b, cpFloat r2, cpSegmentQueryInfo *info)
|
||||
{
|
||||
cpVect n = seg->tn;
|
||||
cpFloat d = cpvdot(cpvsub(seg->ta, a), n);
|
||||
cpFloat r = seg->r + r2;
|
||||
|
||||
cpVect flipped_n = (d > 0.0f ? cpvneg(n) : n);
|
||||
cpVect seg_offset = cpvsub(cpvmult(flipped_n, r), a);
|
||||
|
||||
// Make the endpoints relative to 'a' and move them by the thickness of the segment.
|
||||
cpVect seg_a = cpvadd(seg->ta, seg_offset);
|
||||
cpVect seg_b = cpvadd(seg->tb, seg_offset);
|
||||
cpVect delta = cpvsub(b, a);
|
||||
|
||||
if(cpvcross(delta, seg_a)*cpvcross(delta, seg_b) <= 0.0f){
|
||||
cpFloat d_offset = d + (d > 0.0f ? -r : r);
|
||||
cpFloat ad = -d_offset;
|
||||
cpFloat bd = cpvdot(delta, n) - d_offset;
|
||||
|
||||
if(ad*bd < 0.0f){
|
||||
cpFloat t = ad/(ad - bd);
|
||||
|
||||
info->shape = (cpShape *)seg;
|
||||
info->point = cpvsub(cpvlerp(a, b, t), cpvmult(flipped_n, r2));
|
||||
info->normal = flipped_n;
|
||||
info->alpha = t;
|
||||
}
|
||||
} else if(r != 0.0f){
|
||||
cpSegmentQueryInfo info1 = {NULL, b, cpvzero, 1.0f};
|
||||
cpSegmentQueryInfo info2 = {NULL, b, cpvzero, 1.0f};
|
||||
CircleSegmentQuery((cpShape *)seg, seg->ta, seg->r, a, b, r2, &info1);
|
||||
CircleSegmentQuery((cpShape *)seg, seg->tb, seg->r, a, b, r2, &info2);
|
||||
|
||||
if(info1.alpha < info2.alpha){
|
||||
(*info) = info1;
|
||||
} else {
|
||||
(*info) = info2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct cpShapeMassInfo
|
||||
cpSegmentShapeMassInfo(cpFloat mass, cpVect a, cpVect b, cpFloat r)
|
||||
{
|
||||
struct cpShapeMassInfo info = {
|
||||
mass, cpMomentForBox(1.0f, cpvdist(a, b) + 2.0f*r, 2.0f*r), // TODO is an approximation.
|
||||
cpvlerp(a, b, 0.5f),
|
||||
cpAreaForSegment(a, b, r),
|
||||
};
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
static const cpShapeClass cpSegmentShapeClass = {
|
||||
CP_SEGMENT_SHAPE,
|
||||
(cpShapeCacheDataImpl)cpSegmentShapeCacheData,
|
||||
NULL,
|
||||
(cpShapePointQueryImpl)cpSegmentShapePointQuery,
|
||||
(cpShapeSegmentQueryImpl)cpSegmentShapeSegmentQuery,
|
||||
};
|
||||
|
||||
cpSegmentShape *
|
||||
cpSegmentShapeInit(cpSegmentShape *seg, cpBody *body, cpVect a, cpVect b, cpFloat r)
|
||||
{
|
||||
seg->a = a;
|
||||
seg->b = b;
|
||||
seg->n = cpvrperp(cpvnormalize(cpvsub(b, a)));
|
||||
|
||||
seg->r = r;
|
||||
|
||||
seg->a_tangent = cpvzero;
|
||||
seg->b_tangent = cpvzero;
|
||||
|
||||
cpShapeInit((cpShape *)seg, &cpSegmentShapeClass, body, cpSegmentShapeMassInfo(0.0f, a, b, r));
|
||||
|
||||
return seg;
|
||||
}
|
||||
|
||||
cpShape*
|
||||
cpSegmentShapeNew(cpBody *body, cpVect a, cpVect b, cpFloat r)
|
||||
{
|
||||
return (cpShape *)cpSegmentShapeInit(cpSegmentShapeAlloc(), body, a, b, r);
|
||||
}
|
||||
|
||||
cpVect
|
||||
cpSegmentShapeGetA(const cpShape *shape)
|
||||
{
|
||||
cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape.");
|
||||
return ((cpSegmentShape *)shape)->a;
|
||||
}
|
||||
|
||||
cpVect
|
||||
cpSegmentShapeGetB(const cpShape *shape)
|
||||
{
|
||||
cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape.");
|
||||
return ((cpSegmentShape *)shape)->b;
|
||||
}
|
||||
|
||||
cpVect
|
||||
cpSegmentShapeGetNormal(const cpShape *shape)
|
||||
{
|
||||
cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape.");
|
||||
return ((cpSegmentShape *)shape)->n;
|
||||
}
|
||||
|
||||
cpFloat
|
||||
cpSegmentShapeGetRadius(const cpShape *shape)
|
||||
{
|
||||
cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape.");
|
||||
return ((cpSegmentShape *)shape)->r;
|
||||
}
|
||||
|
||||
void
|
||||
cpSegmentShapeSetNeighbors(cpShape *shape, cpVect prev, cpVect next)
|
||||
{
|
||||
cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape.");
|
||||
cpSegmentShape *seg = (cpSegmentShape *)shape;
|
||||
|
||||
seg->a_tangent = cpvsub(prev, seg->a);
|
||||
seg->b_tangent = cpvsub(next, seg->b);
|
||||
}
|
||||
|
||||
// Unsafe API (chipmunk_unsafe.h)
|
||||
|
||||
// TODO setters should wake the shape up?
|
||||
|
||||
void
|
||||
cpCircleShapeSetRadius(cpShape *shape, cpFloat radius)
|
||||
{
|
||||
cpAssertHard(shape->klass == &cpCircleShapeClass, "Shape is not a circle shape.");
|
||||
cpCircleShape *circle = (cpCircleShape *)shape;
|
||||
|
||||
circle->r = radius;
|
||||
|
||||
cpFloat mass = shape->massInfo.m;
|
||||
shape->massInfo = cpCircleShapeMassInfo(mass, circle->r, circle->c);
|
||||
if(mass > 0.0f) cpBodyAccumulateMassFromShapes(shape->body);
|
||||
}
|
||||
|
||||
void
|
||||
cpCircleShapeSetOffset(cpShape *shape, cpVect offset)
|
||||
{
|
||||
cpAssertHard(shape->klass == &cpCircleShapeClass, "Shape is not a circle shape.");
|
||||
cpCircleShape *circle = (cpCircleShape *)shape;
|
||||
|
||||
circle->c = offset;
|
||||
|
||||
cpFloat mass = shape->massInfo.m;
|
||||
shape->massInfo = cpCircleShapeMassInfo(shape->massInfo.m, circle->r, circle->c);
|
||||
if(mass > 0.0f) cpBodyAccumulateMassFromShapes(shape->body);
|
||||
}
|
||||
|
||||
void
|
||||
cpSegmentShapeSetEndpoints(cpShape *shape, cpVect a, cpVect b)
|
||||
{
|
||||
cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape.");
|
||||
cpSegmentShape *seg = (cpSegmentShape *)shape;
|
||||
|
||||
seg->a = a;
|
||||
seg->b = b;
|
||||
seg->n = cpvperp(cpvnormalize(cpvsub(b, a)));
|
||||
|
||||
cpFloat mass = shape->massInfo.m;
|
||||
shape->massInfo = cpSegmentShapeMassInfo(shape->massInfo.m, seg->a, seg->b, seg->r);
|
||||
if(mass > 0.0f) cpBodyAccumulateMassFromShapes(shape->body);
|
||||
}
|
||||
|
||||
void
|
||||
cpSegmentShapeSetRadius(cpShape *shape, cpFloat radius)
|
||||
{
|
||||
cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape.");
|
||||
cpSegmentShape *seg = (cpSegmentShape *)shape;
|
||||
|
||||
seg->r = radius;
|
||||
|
||||
cpFloat mass = shape->massInfo.m;
|
||||
shape->massInfo = cpSegmentShapeMassInfo(shape->massInfo.m, seg->a, seg->b, seg->r);
|
||||
if(mass > 0.0f) cpBodyAccumulateMassFromShapes(shape->body);
|
||||
}
|
|
@ -1,123 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "chipmunk/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;
|
||||
}
|
|
@ -1,195 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "chipmunk/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;
|
||||
}
|
|
@ -1,701 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#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));
|
||||
(*copy) = (*handler);
|
||||
|
||||
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;
|
||||
space->defaultHandler = cpCollisionHandlerDoNothing;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
space->defaultHandler = cpCollisionHandlerDefault;
|
||||
}
|
||||
}
|
||||
|
||||
cpCollisionHandler *cpSpaceAddDefaultCollisionHandler(cpSpace *space)
|
||||
{
|
||||
cpSpaceUseWildcardDefaultHandler(space);
|
||||
return &space->defaultHandler;
|
||||
}
|
||||
|
||||
cpCollisionHandler *cpSpaceAddCollisionHandler(cpSpace *space, cpCollisionType a, cpCollisionType b)
|
||||
{
|
||||
cpHashValue hash = CP_HASH_PAIR(a, b);
|
||||
// TODO should use space->defaultHandler values instead?
|
||||
cpCollisionHandler temp = {a, b, DefaultBegin, DefaultPreSolve, DefaultPostSolve, DefaultSeparate, NULL};
|
||||
|
||||
cpHashSet *handlers = space->collisionHandlers;
|
||||
cpCollisionHandler *handler = cpHashSetFind(handlers, hash, &temp);
|
||||
return (handler ? handler : cpHashSetInsert(handlers, hash, &temp, (cpHashSetTransFunc)handlerSetTrans, NULL));
|
||||
}
|
||||
|
||||
cpCollisionHandler *
|
||||
cpSpaceAddWildcardHandler(cpSpace *space, cpCollisionType type)
|
||||
{
|
||||
cpSpaceUseWildcardDefaultHandler(space);
|
||||
|
||||
cpHashValue hash = CP_HASH_PAIR(type, CP_WILDCARD_COLLISION_TYPE);
|
||||
cpCollisionHandler temp = {type, CP_WILDCARD_COLLISION_TYPE, AlwaysCollide, AlwaysCollide, DoNothing, DoNothing, NULL};
|
||||
|
||||
cpHashSet *handlers = space->collisionHandlers;
|
||||
cpCollisionHandler *handler = cpHashSetFind(handlers, hash, &temp);
|
||||
return (handler ? handler : cpHashSetInsert(handlers, hash, &temp, (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; i<bodies->num; i++){
|
||||
func((cpBody *)bodies->arr[i], data);
|
||||
}
|
||||
|
||||
cpArray *otherBodies = space->staticBodies;
|
||||
for(int i=0; i<otherBodies->num; i++){
|
||||
func((cpBody *)otherBodies->arr[i], data);
|
||||
}
|
||||
|
||||
cpArray *components = space->sleepingComponents;
|
||||
for(int i=0; i<components->num; 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; i<constraints->num; 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;
|
||||
}
|
|
@ -1,349 +0,0 @@
|
|||
/* Copyright (c) 2007 Scott Lembcke
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#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; i<bodies->num; 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; i<bodies->num; 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; i<count; i++){
|
||||
cpArbiter *arb = (cpArbiter*)arbiters->arr[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; i<constraints->num; 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; i<bodies->num;){
|
||||
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);
|
||||
}
|
|
@ -1,189 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "chipmunk/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; i<count; i++) verts[i] = planes[i].v0;
|
||||
options->drawPolygon(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; i<spring_count; i++){
|
||||
cpVect v = spring_verts[i];
|
||||
verts[i] = cpv(cpvdot(v, r1) + a.x, cpvdot(v, r2) + a.y);
|
||||
}
|
||||
|
||||
for(int i=0; i<spring_count-1; i++){
|
||||
options->drawSegment(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; i<arbiters->num; i++){
|
||||
cpArbiter *arb = (cpArbiter*)arbiters->arr[i];
|
||||
cpVect n = arb->n;
|
||||
|
||||
for(int j=0; j<arb->count; 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
|
|
@ -1,634 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "chipmunk/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; i<count; i++) cpArrayPush(hash->pooledHandles, 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; i<hash->numcells; 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; i<count; i++) recycleBin(hash, buffer + i);
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: Memory Management Functions
|
||||
|
||||
cpSpaceHash *
|
||||
cpSpaceHashAlloc(void)
|
||||
{
|
||||
return (cpSpaceHash *)cpcalloc(1, sizeof(cpSpaceHash));
|
||||
}
|
||||
|
||||
// Frees the old table, and allocate a new one.
|
||||
static void
|
||||
cpSpaceHashAllocTable(cpSpaceHash *hash, int numcells)
|
||||
{
|
||||
cpfree(hash->table);
|
||||
|
||||
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 <GLUT/glut.h>
|
||||
|
||||
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
|
|
@ -1,246 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "chipmunk/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;
|
||||
}
|
|
@ -1,444 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "chipmunk/chipmunk_private.h"
|
||||
|
||||
//MARK: Post Step Callback Functions
|
||||
|
||||
cpPostStepCallback *
|
||||
cpSpaceGetPostStepCallback(cpSpace *space, void *key)
|
||||
{
|
||||
cpArray *arr = space->postStepCallbacks;
|
||||
for(int i=0; i<arr->num; 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; i<count; i++){
|
||||
cpSpaceActivateBody(space, (cpBody *)waking->arr[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; i<arr->num; 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; i<count; i++) cpArrayPush(space->pooledArbiters, 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.
|
||||
!(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; i<arbiters->num; 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; i<bodies->num; 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; i<arbiters->num; i++){
|
||||
cpArbiterPreStep((cpArbiter *)arbiters->arr[i], dt, slop, biasCoef);
|
||||
}
|
||||
|
||||
for(int i=0; i<constraints->num; 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; i<bodies->num; 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; i<arbiters->num; i++){
|
||||
cpArbiterApplyCachedImpulse((cpArbiter *)arbiters->arr[i], dt_coef);
|
||||
}
|
||||
|
||||
for(int i=0; i<constraints->num; i++){
|
||||
cpConstraint *constraint = (cpConstraint *)constraints->arr[i];
|
||||
constraint->klass->applyCachedImpulse(constraint, dt_coef);
|
||||
}
|
||||
|
||||
// Run the impulse solver.
|
||||
for(int i=0; i<space->iterations; i++){
|
||||
for(int j=0; j<arbiters->num; j++){
|
||||
cpArbiterApplyImpulse((cpArbiter *)arbiters->arr[j]);
|
||||
}
|
||||
|
||||
for(int j=0; j<constraints->num; j++){
|
||||
cpConstraint *constraint = (cpConstraint *)constraints->arr[j];
|
||||
constraint->klass->applyImpulse(constraint, dt);
|
||||
}
|
||||
}
|
||||
|
||||
// Run the constraint post-solve callbacks
|
||||
for(int i=0; i<constraints->num; 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; i<arbiters->num; i++){
|
||||
cpArbiter *arb = (cpArbiter *) arbiters->arr[i];
|
||||
|
||||
cpCollisionHandler *handler = arb->handler;
|
||||
handler->postSolveFunc(arb, space, handler->userData);
|
||||
}
|
||||
} cpSpaceUnlock(space, cpTrue);
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "chipmunk/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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,254 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "chipmunk/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; i<count; i++) func(table[i].obj, data);
|
||||
}
|
||||
|
||||
static int
|
||||
cpSweep1DContains(cpSweep1D *sweep, void *obj, cpHashValue hashid)
|
||||
{
|
||||
TableCell *table = sweep->table;
|
||||
for(int i=0, count=sweep->num; i<count; i++){
|
||||
if(table[i].obj == obj) return cpTrue;
|
||||
}
|
||||
|
||||
return cpFalse;
|
||||
}
|
||||
|
||||
//MARK: Basic Operations
|
||||
|
||||
static void
|
||||
cpSweep1DInsert(cpSweep1D *sweep, void *obj, cpHashValue hashid)
|
||||
{
|
||||
if(sweep->num == 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; i<count; i++){
|
||||
if(table[i].obj == obj){
|
||||
int num = --sweep->num;
|
||||
|
||||
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; i<count; i++){
|
||||
TableCell cell = table[i];
|
||||
if(BoundsOverlap(bounds, cell.bounds) && obj != cell.obj) func(obj, cell.obj, 0, data);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cpSweep1DSegmentQuery(cpSweep1D *sweep, void *obj, cpVect a, cpVect b, cpFloat t_exit, cpSpatialIndexSegmentQueryFunc func, void *data)
|
||||
{
|
||||
cpBB bb = cpBBExpand(cpBBNew(a.x, a.y, a.x, a.y), b);
|
||||
Bounds bounds = BBToBounds(sweep, bb);
|
||||
|
||||
TableCell *table = sweep->table;
|
||||
for(int i=0, count=sweep->num; i<count; i++){
|
||||
TableCell cell = table[i];
|
||||
if(BoundsOverlap(bounds, cell.bounds)) func(obj, cell.obj, data);
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: Reindex/Query
|
||||
|
||||
static int
|
||||
TableSort(TableCell *a, TableCell *b)
|
||||
{
|
||||
return (a->bounds.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; i<count; i++) table[i] = MakeTableCell(sweep, table[i].obj);
|
||||
qsort(table, count, sizeof(TableCell), (int (*)(const void *, const void *))TableSort); // TODO: use insertion sort instead
|
||||
|
||||
for(int i=0; i<count; i++){
|
||||
TableCell cell = table[i];
|
||||
cpFloat max = cell.bounds.max;
|
||||
|
||||
for(int j=i+1; table[j].bounds.min < max && j<count; j++){
|
||||
func(cell.obj, table[j].obj, 0, data);
|
||||
}
|
||||
}
|
||||
|
||||
// Reindex query is also responsible for colliding against the static index.
|
||||
// Fortunately there is a helper function for that.
|
||||
cpSpatialIndexCollideStatic((cpSpatialIndex *)sweep, sweep->spatialIndex.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;}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
// Used for resizing hash tables.
|
||||
// Values approximately double.
|
||||
// http://planetmath.org/encyclopedia/GoodHashTablePrimes.html
|
||||
static int primes[] = {
|
||||
5,
|
||||
13,
|
||||
23,
|
||||
47,
|
||||
97,
|
||||
193,
|
||||
389,
|
||||
769,
|
||||
1543,
|
||||
3079,
|
||||
6151,
|
||||
12289,
|
||||
24593,
|
||||
49157,
|
||||
98317,
|
||||
196613,
|
||||
393241,
|
||||
786433,
|
||||
1572869,
|
||||
3145739,
|
||||
6291469,
|
||||
12582917,
|
||||
25165843,
|
||||
50331653,
|
||||
100663319,
|
||||
201326611,
|
||||
402653189,
|
||||
805306457,
|
||||
1610612741,
|
||||
0,
|
||||
};
|
||||
|
||||
static inline int
|
||||
next_prime(int n)
|
||||
{
|
||||
int i = 0;
|
||||
while(n > primes[i]){
|
||||
i++;
|
||||
cpAssertHard(primes[i], "Tried to resize a hash table to a size greater than 1610612741 O_o"); // realistically this should never happen
|
||||
}
|
||||
|
||||
return primes[i];
|
||||
}
|
|
@ -1,192 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
@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 <ChipmunkBaseObject>
|
||||
|
||||
/// 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
|
|
@ -1,405 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
Constraints connect two ChipmunkBody objects together. Most often constraints are simple joints, but can also be things like motors, friction generators or servos.
|
||||
|
||||
@htmlonly
|
||||
<object width="425" height="344">
|
||||
<param name="movie" value="http://www.youtube.com/v/ZgJJZTS0aMM?fs=1&hl=en_US&rel=0"></param>
|
||||
<param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param>
|
||||
<embed src="http://www.youtube.com/v/ZgJJZTS0aMM?fs=1&hl=en_US&rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"></embed>
|
||||
</object>
|
||||
@endhtmlonly
|
||||
*/
|
||||
@interface ChipmunkConstraint : NSObject <ChipmunkBaseObject> {
|
||||
@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 <a href="http://en.wikipedia.org/wiki/Young's_modulus">young's modulus</a> 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 <a href="http://en.wikipedia.org/wiki/Young's_modulus">young's modulus</a> 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 <a href="http://en.wikipedia.org/wiki/Young's_modulus">young's modulus</a> 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 <a href="http://en.wikipedia.org/wiki/Young's_modulus">young's modulus</a> 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 <a href="http://en.wikipedia.org/wiki/Young's_modulus">young's modulus</a> 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 <a href="http://en.wikipedia.org/wiki/Young's_modulus">young's modulus</a> 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
|
|
@ -1,5 +0,0 @@
|
|||
// Copyright 2013 Howling Moon Software. All rights reserved.
|
||||
// See http://chipmunk2d.net/legal.php for more information.
|
||||
|
||||
//#define ChipmunkGetObject(s) s->data
|
||||
//#define ChipmunkGetData(s) [s->data data]
|
|
@ -1,136 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#import "ObjectiveChipmunk/ObjectiveChipmunk.h"
|
||||
|
||||
@interface ChipmunkGrab : NSObject<ChipmunkObject> {
|
||||
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
|
|
@ -1,249 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
@class ChipmunkPointQueryInfo;
|
||||
@class ChipmunkSegmentQueryInfo;
|
||||
|
||||
|
||||
/// Abstract base class for collsion shape types.
|
||||
@interface ChipmunkShape : NSObject <ChipmunkBaseObject> {
|
||||
@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
|
|
@ -1,293 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
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 locked;
|
||||
|
||||
/**
|
||||
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<ChipmunkObject> *)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<ChipmunkObject> *)obj;
|
||||
|
||||
/// Check if a space already contains a particular object:
|
||||
-(BOOL)contains:(NSObject<ChipmunkObject> *)obj;
|
||||
|
||||
/// If the space is locked and it's unsafe to call add: it will call addPostStepAddition: instead.
|
||||
- (id)smartAdd:(NSObject<ChipmunkObject> *)obj;
|
||||
|
||||
/// If the space is locked and it's unsafe to call remove: it will call addPostStepRemoval: instead.
|
||||
- (id)smartRemove:(NSObject<ChipmunkObject> *)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</code></pre>
|
||||
@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<ChipmunkObject> *)obj;
|
||||
|
||||
/// Remove the Chipmunk Object from the space at the end of the step.
|
||||
- (void)addPostStepRemoval:(NSObject<ChipmunkObject> *)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
|
||||
|
||||
//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__); \
|
||||
}
|
||||
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#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
|
||||
#undef CP_ALLOW_PRIVATE_ACCESS
|
||||
#import "chipmunk/chipmunk_private.h"
|
||||
#else
|
||||
#import "chipmunk/chipmunk.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 <NSFastEnumeration>)chipmunkObjects;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
/// A category to have NSArray implement the ChipmunkObject protocol.
|
||||
/// They make for very easy containers.
|
||||
@interface NSArray(ChipmunkObject) <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 <ChipmunkObject>
|
||||
|
||||
- (void)addToSpace:(ChipmunkSpace *)space;
|
||||
- (void)removeFromSpace:(ChipmunkSpace *)space;
|
||||
|
||||
@end
|
||||
|
||||
#import "ChipmunkBody.h"
|
||||
#import "ChipmunkShape.h"
|
||||
#import "ChipmunkConstraint.h"
|
||||
#import "ChipmunkSpace.h"
|
||||
#import "ChipmunkMultiGrab.h"
|
|
@ -1,206 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#define CP_ALLOW_PRIVATE_ACCESS 1
|
||||
#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
|
|
@ -1,482 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#define CP_ALLOW_PRIVATE_ACCESS 1
|
||||
#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
|
|
@ -1,264 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#import "ChipmunkMultiGrab.h"
|
||||
|
||||
|
||||
// A constraint subclass that tracks a grab point
|
||||
@interface ChipmunkGrab() <ChipmunkObject>
|
||||
|
||||
@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
|
|
@ -1,331 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#define CP_ALLOW_PRIVATE_ACCESS 1
|
||||
#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
|
|
@ -1,590 +0,0 @@
|
|||
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#define CP_ALLOW_PRIVATE_ACCESS
|
||||
#import "ObjectiveChipmunk/ObjectiveChipmunk.h"
|
||||
|
||||
#import <objc/message.h>
|
||||
#import <TargetConditionals.h>
|
||||
|
||||
#ifdef CHIPMUNK_PRO_TRIAL
|
||||
#if TARGET_OS_IPHONE == 1
|
||||
#import <UIKit/UIKit.h>
|
||||
#else
|
||||
#import <AppKit/AppKit.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// 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<ChipmunkObject>
|
||||
|
||||
@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<NSFastEnumeration>)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
|
||||
|
||||
#ifdef CHIPMUNK_PRO_TRIAL
|
||||
static NSString *dialogTitle = @"Chipmunk Pro Trial";
|
||||
static NSString *dialogMessage = @"This copy of Chipmunk Pro is a trial, please consider purchasing if you continue using it.";
|
||||
|
||||
+(void)initialize
|
||||
{
|
||||
[super initialize];
|
||||
|
||||
static BOOL done = FALSE;
|
||||
if(done) return; else done = TRUE;
|
||||
|
||||
#if TARGET_OS_IPHONE == 1
|
||||
UIAlertView *alert = [[UIAlertView alloc]
|
||||
initWithTitle:dialogTitle
|
||||
message:dialogMessage
|
||||
delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil
|
||||
];
|
||||
|
||||
[alert show];
|
||||
[alert release];
|
||||
#else
|
||||
[self performSelectorOnMainThread:@selector(dialog) withObject:nil waitUntilDone:FALSE];
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#if TARGET_OS_IPHONE != 1
|
||||
+(void)dialog
|
||||
{
|
||||
[NSApplication sharedApplication];
|
||||
[[NSAlert
|
||||
alertWithMessageText:dialogTitle
|
||||
defaultButton:@"OK"
|
||||
alternateButton:nil
|
||||
otherButton:nil
|
||||
informativeTextWithFormat:dialogMessage
|
||||
] runModal];
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
+(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;
|
||||
}
|
||||
|
||||
- (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 {
|
||||
// Use a fast space instead if the class is available.
|
||||
// However if you don't specify -ObjC as a linker flag the dynamic substitution won't work.
|
||||
Class hastySpace = NSClassFromString(@"ChipmunkHastySpace");
|
||||
if(hastySpace && [self isMemberOfClass:[ChipmunkSpace class]]){
|
||||
[self release];
|
||||
return [[hastySpace alloc] init];
|
||||
} else {
|
||||
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);}
|
||||
|
||||
- (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<ChipmunkObject> *)obj
|
||||
{
|
||||
if([obj conformsToProtocol:@protocol(ChipmunkBaseObject)]){
|
||||
[(NSObject<ChipmunkBaseObject> *)obj addToSpace:self];
|
||||
} else if([obj conformsToProtocol:@protocol(ChipmunkObject)]){
|
||||
for(NSObject<ChipmunkBaseObject> *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<ChipmunkObject> *)obj
|
||||
{
|
||||
if([obj conformsToProtocol:@protocol(ChipmunkBaseObject)]){
|
||||
[(NSObject<ChipmunkBaseObject> *)obj removeFromSpace:self];
|
||||
} else if([obj conformsToProtocol:@protocol(ChipmunkObject)]){
|
||||
for(NSObject<ChipmunkBaseObject> *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<ChipmunkObject> *)obj
|
||||
{
|
||||
return [_children containsObject:obj];
|
||||
}
|
||||
|
||||
- (NSObject<ChipmunkObject> *)smartAdd:(NSObject<ChipmunkObject> *)obj
|
||||
{
|
||||
if(cpSpaceIsLocked(_space)){
|
||||
[self addPostStepAddition:obj];
|
||||
} else {
|
||||
[self add:obj];
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
- (NSObject<ChipmunkObject> *)smartRemove:(NSObject<ChipmunkObject> *)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<ChipmunkObject> *)obj
|
||||
{
|
||||
[self addPostStepCallback:self selector:@selector(add:) key:obj];
|
||||
}
|
||||
|
||||
- (void)addPostStepRemoval:(NSObject<ChipmunkObject> *)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
|
|
@ -1,24 +0,0 @@
|
|||
CocosBuilder Reader: http://www.cocosbuilder.com
|
||||
|
||||
Copyright (c) 2010-2011 - Viktor Lidholt
|
||||
Copyright (c) 2011-2012 - Zynga Inc. and contributors
|
||||
(see each file to see the different copyright owners)
|
||||
|
||||
|
||||
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.
|
|
@ -1,19 +0,0 @@
|
|||
Copyright (c) 2007-2014 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.
|
|
@ -1,24 +0,0 @@
|
|||
cocos2d for iPhone: http://www.cocos2d-iphone.org
|
||||
|
||||
Copyright (c) 2008-2011 - Ricardo Quesada and contributors
|
||||
Copyright (c) 2011-2012 - Zynga Inc. and contributors
|
||||
(see each file to see the different copyright owners)
|
||||
|
||||
|
||||
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.
|
|
@ -1,23 +0,0 @@
|
|||
//
|
||||
// AppDelegate+OALAction_Private.h
|
||||
// ObjectAL
|
||||
//
|
||||
// Created by Karl Stenerud on 3/18/12.
|
||||
// Copyright (c) 2012 Stenerud. All rights reserved.
|
||||
//
|
||||
|
||||
#import "OALAction.h"
|
||||
|
||||
/** \cond */
|
||||
@interface OALAction ()
|
||||
|
||||
@property(nonatomic,readwrite,assign) id target;
|
||||
|
||||
@property(nonatomic,readwrite,assign) float duration;
|
||||
|
||||
@property(nonatomic,readwrite,assign) bool running;
|
||||
|
||||
@property(nonatomic,readwrite,assign) bool runningInManager;
|
||||
|
||||
@end
|
||||
/** \endcond */
|
|
@ -1,325 +0,0 @@
|
|||
//
|
||||
// OALAction.h
|
||||
// ObjectAL
|
||||
//
|
||||
// Created by Karl Stenerud on 10-09-18.
|
||||
//
|
||||
// Copyright (c) 2009 Karl Stenerud. All rights reserved.
|
||||
//
|
||||
// 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 remain in place
|
||||
// in this source code.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// Attribution is not required, but appreciated :)
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "ObjectALConfig.h"
|
||||
|
||||
|
||||
#if OBJECTAL_CFG_USE_COCOS2D_ACTIONS
|
||||
|
||||
#pragma mark Cocos2d Subclassing
|
||||
|
||||
#import "cocos2d.h"
|
||||
|
||||
/** Generates common code required to subclass from a cocos2d action
|
||||
* while maintaining the functionality of an OALAction.
|
||||
*/
|
||||
#define COCOS2D_SUBCLASS_HEADER(CLASS_A,CLASS_B) \
|
||||
@interface CLASS_A: CLASS_B \
|
||||
{ \
|
||||
bool started_; \
|
||||
} \
|
||||
\
|
||||
@property(nonatomic,readonly,assign) bool running; \
|
||||
- (void) runWithTarget:(id) target; \
|
||||
- (void) prepareWithTarget:(id) target; \
|
||||
- (void) stopAction; \
|
||||
- (void) updateCompletion:(float) proportionComplete; \
|
||||
\
|
||||
@end
|
||||
|
||||
|
||||
|
||||
/** Generates common code required to subclass from a cocos2d action
|
||||
* while maintaining the functionality of an OALAction.
|
||||
*/
|
||||
#define COCOS2D_SUBCLASS(CLASS_A) \
|
||||
@implementation CLASS_A \
|
||||
\
|
||||
- (id) init \
|
||||
{ \
|
||||
return [self initWithDuration:0]; \
|
||||
} \
|
||||
\
|
||||
-(void) startWithTarget:(id) targetIn \
|
||||
{ \
|
||||
[super startWithTarget:targetIn]; \
|
||||
[self prepareWithTarget:targetIn]; \
|
||||
started_ = YES; \
|
||||
[self runWithTarget:targetIn]; \
|
||||
} \
|
||||
\
|
||||
- (void) update:(float) proportionComplete \
|
||||
{ \
|
||||
[super update:proportionComplete]; \
|
||||
[self updateCompletion:proportionComplete]; \
|
||||
} \
|
||||
\
|
||||
- (bool) running \
|
||||
{ \
|
||||
return !self.isDone; \
|
||||
} \
|
||||
\
|
||||
- (void) runWithTarget:(id) targetIn \
|
||||
{ \
|
||||
if(!started_) \
|
||||
{ \
|
||||
[[[CCDirector sharedDirector] actionManager] addAction:self target:targetIn paused:NO]; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
- (void) stopAction \
|
||||
{ \
|
||||
[[[CCDirector sharedDirector] actionManager] removeAction:self]; \
|
||||
} \
|
||||
|
||||
#endif /* OBJECTAL_CFG_USE_COCOS2D_ACTIONS */
|
||||
|
||||
|
||||
|
||||
/* There are two versions of the actions which can be used: ObjectAL and Cocos2d.
|
||||
* It's usually more convenient when using Cocos2d to have all actions as part of
|
||||
* the Cocos2d action system. You can set this in ObjectALConfig.h
|
||||
*/
|
||||
#if !OBJECTAL_CFG_USE_COCOS2D_ACTIONS
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark OALAction (ObjectAL version)
|
||||
|
||||
/**
|
||||
* Represents an action that can be performed on an object.
|
||||
*/
|
||||
@interface OALAction : NSObject
|
||||
{
|
||||
/** \cond */
|
||||
float duration_;
|
||||
float elapsed_;
|
||||
bool running_;
|
||||
/** \endcond */
|
||||
|
||||
/** If TRUE, this action is running via OALActionManager. */
|
||||
bool runningInManager_;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark Properties
|
||||
|
||||
/** The target to perform the action on. WEAK REFERENCE. */
|
||||
@property(nonatomic,readonly,assign) id target;
|
||||
|
||||
/** The duration of the action, in seconds. */
|
||||
@property(nonatomic,readonly,assign) float duration;
|
||||
|
||||
/** The amount of time that has elapsed for this action, in seconds. */
|
||||
@property(nonatomic,readwrite,assign) float elapsed;
|
||||
|
||||
/** If true, the action is currently running. */
|
||||
@property(nonatomic,readonly,assign) bool running;
|
||||
|
||||
|
||||
#pragma mark Object Management
|
||||
|
||||
/** Initialize an action.
|
||||
*
|
||||
* @param duration The duration of this action in seconds.
|
||||
* @return The initialized action.
|
||||
*/
|
||||
- (id) initWithDuration:(float) duration;
|
||||
|
||||
|
||||
#pragma mark Functions
|
||||
|
||||
/** Run this action on a target.
|
||||
*
|
||||
* @param target The target to run the action on.
|
||||
*/
|
||||
- (void) runWithTarget:(id) target;
|
||||
|
||||
/** Called by runWithTraget to do any final preparations before running.
|
||||
* Subclasses must ensure that duration is valid when this method returns.
|
||||
*
|
||||
* @param target The target to run the action on.
|
||||
*/
|
||||
- (void) prepareWithTarget:(id) target;
|
||||
|
||||
|
||||
/** Called by runWithTarget to start the action running.
|
||||
*/
|
||||
- (void) startAction;
|
||||
|
||||
/** Called by OALActionManager to update this action's progress.
|
||||
*
|
||||
* @param proportionComplete The proportion of this action's duration that has elapsed.
|
||||
*/
|
||||
- (void) updateCompletion:(float) proportionComplete;
|
||||
|
||||
/** Stop this action.
|
||||
*/
|
||||
- (void) stopAction;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#else /* !OBJECTAL_CFG_USE_COCOS2D_ACTIONS */
|
||||
|
||||
COCOS2D_SUBCLASS_HEADER(OALAction, CCActionInterval);
|
||||
|
||||
#endif /* !OBJECTAL_CFG_USE_COCOS2D_ACTIONS */
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark OALPropertyAction
|
||||
|
||||
@interface OALPropertyAction: OALAction
|
||||
|
||||
/** The value that the property in the target will hold at the start of the action. */
|
||||
@property(nonatomic,readwrite,assign) float startValue;
|
||||
|
||||
/** The value that the property in the target will hold at the end of the action. */
|
||||
@property(nonatomic,readwrite,assign) float endValue;
|
||||
|
||||
|
||||
#pragma mark Object Management
|
||||
|
||||
/** Create a new action using the default function.
|
||||
* The start value will be the current value of the target this action is applied to.
|
||||
*
|
||||
* @param duration The duration of this action in seconds.
|
||||
* @param propertyKey The property to modify.
|
||||
* @param endValue The "ending" value that this action will converge upon when setting the target's property.
|
||||
* @return A new action.
|
||||
*/
|
||||
+ (id) actionWithDuration:(float) duration
|
||||
propertyKey:(NSString*) propertyKey
|
||||
endValue:(float) endValue;
|
||||
|
||||
/** Create a new action.
|
||||
*
|
||||
* @param duration The duration of this action in seconds.
|
||||
* @param propertyKey The property to modify.
|
||||
* @param startValue The "starting" value that this action will diverge from when setting the target's
|
||||
* property. If NAN, use the current value from the target.
|
||||
* @param endValue The "ending" value that this action will converge upon when setting the target's property.
|
||||
* @return A new action.
|
||||
*/
|
||||
+ (id) actionWithDuration:(float) duration
|
||||
propertyKey:(NSString*) propertyKey
|
||||
startValue:(float) startValue
|
||||
endValue:(float) endValue;
|
||||
|
||||
/** Initialize an action using the default function.
|
||||
* The start value will be the current value of the target this action is applied to.
|
||||
*
|
||||
* @param duration The duration of this action in seconds.
|
||||
* @param propertyKey The property to modify.
|
||||
* @param endValue The "ending" value that this action will converge upon when setting the target's property.
|
||||
* @return The initialized action.
|
||||
*/
|
||||
- (id) initWithDuration:(float) duration
|
||||
propertyKey:(NSString*) propertyKey
|
||||
endValue:(float) endValue;
|
||||
|
||||
/** Initialize an action.
|
||||
*
|
||||
* @param duration The duration of this action in seconds.
|
||||
* @param propertyKey The property to modify.
|
||||
* @param startValue The "starting" value that this action will diverge from when setting the target's
|
||||
* property. If NAN, use the current value from the target.
|
||||
* @param endValue The "ending" value that this action will converge upon when setting the target's property.
|
||||
* @return The initialized action.
|
||||
*/
|
||||
- (id) initWithDuration:(float) duration
|
||||
propertyKey:(NSString*) propertyKey
|
||||
startValue:(float) startValue
|
||||
endValue:(float) endValue;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark OALEaseAction
|
||||
|
||||
typedef float (*EaseFunctionPtr)(float);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
kOALEaseIn,
|
||||
kOALEaseOut,
|
||||
kOALEaseInOut,
|
||||
} OALEasePhase;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
kOALEaseShapeSine,
|
||||
kOALEaseShapeExponential,
|
||||
} OALEaseShape;
|
||||
|
||||
/**
|
||||
* Applies an easing function to another action.
|
||||
* Normally, an action progresses at a linear rate. An ease changes that to a
|
||||
* curve.
|
||||
*/
|
||||
@interface OALEaseAction: OALAction
|
||||
{
|
||||
OALAction* action_;
|
||||
}
|
||||
|
||||
/** Create a new ease action.
|
||||
*
|
||||
* @param shape The shape of the curve to apply.
|
||||
* @param phase What phase of the action to apply the curve to.
|
||||
* @action The action to apple the curve to.
|
||||
* @return A new action.
|
||||
*/
|
||||
+ (OALEaseAction*) actionWithShape:(OALEaseShape) shape
|
||||
phase:(OALEasePhase) phase
|
||||
action:(OALAction*) action;
|
||||
|
||||
/** Initialize an ease action.
|
||||
*
|
||||
* @param shape The shape of the curve to apply.
|
||||
* @param phase What phase of the action to apply the curve to.
|
||||
* @action The action to apple the curve to.
|
||||
* @return The initialized action.
|
||||
*/
|
||||
- (id) initWithShape:(OALEaseShape) shape
|
||||
phase:(OALEasePhase) phase
|
||||
action:(OALAction*) action;
|
||||
|
||||
/** Get a pointer to an ease function of the specified shape and phase.
|
||||
*
|
||||
* @param shape The shape of the curve to apply.
|
||||
* @param phase What phase of the action to apply the curve to.
|
||||
* @return a pointer to the appropriate function.
|
||||
*/
|
||||
+ (EaseFunctionPtr) easeFunctionForShape:(OALEaseShape) shape
|
||||
phase:(OALEasePhase) phase;
|
||||
|
||||
@end
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user