Organized files

This commit is contained in:
2022-01-19 22:43:21 +00:00
parent 3dcaf6df81
commit a7378e1d17
248 changed files with 539445 additions and 264 deletions

View File

@@ -2,7 +2,7 @@
#include "gameobject.h"
#include "input.h"
#include <SDL.h>
#include <SDL2/SDL.h>
const float CAMERA_MINSPEED = 1.f;
const float CAMERA_MAXSPEED = 300.f;

View File

@@ -15,4 +15,7 @@
#define MSAA_SAMPLES 2
#endif

View File

@@ -131,7 +131,7 @@ void ds_seek(struct datastream *ds, uint32_t time)
plm_seek(ds->plm, time, false);
}
void ds_fwdframes(struct datastream *ds, int frames)
void ds_advanceframes(struct datastream *ds, int frames)
{
for (int i = 0; i < frames; i++) {
plm_frame_t *frame = plm_decode_video(ds->plm);
@@ -139,8 +139,6 @@ void ds_fwdframes(struct datastream *ds, int frames)
}
}
void ds_pause(struct datastream *ds)
{
ds->playing = false;
@@ -171,3 +169,8 @@ double ds_remainingtime(struct datastream *ds)
else
return 0.f;
}
double ds_length(struct datastream *ds)
{
return plm_get_duration(ds->plm);
}

View File

@@ -1,7 +1,7 @@
#ifndef DATASTREAM_H
#define DATASTREAM_H
#include <SDL_audio.h>
#include <SDL2/SDL_audio.h>
typedef struct plm_t plm_t;
@@ -22,9 +22,11 @@ void ds_openvideo(struct datastream *ds, const char *path,
const char *adriver);
void ds_advance(struct datastream *ds, uint32_t ms);
void ds_seek(struct datastream *ds, uint32_t time);
void ds_advanceframes(struct datastream *ds, int frames);
void ds_pause(struct datastream *ds);
void ds_stop(struct datastream *ds);
int ds_videodone(struct datastream *ds);
double ds_remainingtime(struct datastream *ds);
double ds_length(struct datastream *ds);
#endif

154
source/engine/debugdraw.c Normal file
View File

@@ -0,0 +1,154 @@
#include "debugdraw.h"
#include "openglrender.h"
#include "shader.h"
static uint32_t circleVBO;
static uint32_t circleVAO;
static struct mShader *circleShader;
static uint32_t gridVBO;
static uint32_t gridVAO;
static struct mShader *gridShader;
static uint32_t rectVBO;
static uint32_t rectVAO;
static struct mShader *rectShader;
void debugdraw_init()
{
circleShader = MakeShader("circlevert.glsl", "circlefrag.glsl");
shader_setUBO(circleShader, "Projection", 0);
glGenBuffers(1, &circleVBO);
glGenVertexArrays(1, &circleVAO);
float gridverts[] = {
-1.f, -1.f,
1.f, -1.f,
-1.f, 1.f,
1.f, 1.f
};
gridShader = MakeShader("gridvert.glsl", "gridfrag.glsl");
shader_setUBO(gridShader, "Projection", 0);
glGenBuffers(1, &gridVBO);
glGenVertexArrays(1, &gridVAO);
glBindVertexArray(gridVAO);
glBindBuffer(GL_ARRAY_BUFFER, gridVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(gridverts), &gridverts,
GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, NULL);
rectShader = MakeShader("linevert.glsl", "linefrag.glsl");
shader_setUBO(rectShader, "Projection", 0);
glGenBuffers(1, &rectVBO);
glGenVertexArrays(1, &rectVAO);
}
void draw_line(int x1, int y1, int x2, int y2)
{
shader_use(rectShader);
float verts[] = {
x1, y1,
x2, y2
};
glBindBuffer(GL_ARRAY_BUFFER, rectVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(verts), &verts, GL_DYNAMIC_DRAW);
glBindVertexArray(rectVAO);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, NULL);
glDrawArrays(GL_LINE_STRIP, 0, 2);
}
void draw_edge(float *points, int n)
{
shader_use(rectShader);
glBindBuffer(GL_ARRAY_BUFFER, rectVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * n * 2, points,
GL_DYNAMIC_DRAW);
glBindVertexArray(rectVAO);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, NULL);
glDrawArrays(GL_LINE_STRIP, 0, n);
}
void draw_circle(int x, int y, float radius, int pixels)
{
shader_use(circleShader);
float verts[] = {
x - radius, y - radius, -1.f, -1.f,
x + radius, y - radius, 1.f, -1.f,
x - radius, y + radius, -1.f, 1.f,
x + radius, y + radius, 1.f, 1.f
};
glBindBuffer(GL_ARRAY_BUFFER, circleVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(verts), &verts, GL_DYNAMIC_DRAW);
shader_setfloat(circleShader, "radius", radius);
shader_setint(circleShader, "thickness", pixels);
glBindVertexArray(circleVAO);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, NULL);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
void draw_rect(int x, int y, int w, int h)
{
float hw = w / 2.f;
float hh = h / 2.f;
float verts[] = {
x - hw, y - hh,
x + hw, y - hh,
x + hw, y + hh,
x - hw, y + hh
};
shader_use(rectShader);
glBindBuffer(GL_ARRAY_BUFFER, rectVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(verts), &verts, GL_DYNAMIC_DRAW);
glBindVertexArray(rectVAO);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, NULL);
glDrawArrays(GL_LINE_LOOP, 0, 4);
}
void draw_grid(int width, int span)
{
shader_use(gridShader);
shader_setint(gridShader, "thickness", width);
shader_setint(gridShader, "span", span);
glBindVertexArray(gridVAO);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
void draw_point(int x, int y, float r)
{
draw_circle(x, y, r, r);
}
void draw_poly(float *points, int n)
{
shader_use(rectShader);
glBindBuffer(GL_ARRAY_BUFFER, rectVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * n * 2, points,
GL_DYNAMIC_DRAW);
glBindVertexArray(rectVAO);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, NULL);
glDrawArrays(GL_LINE_LOOP, 0, n);
}
void debugdraw_flush()
{
}

16
source/engine/debugdraw.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef DEBUGDRAW_H
#define DEBUGDRAW_H
void debugdraw_init();
void draw_line(int x1, int y1, int x2, int y2);
void draw_edge(float *points, int n);
void draw_circle(int x, int y, float radius, int pixels);
void draw_grid(int width, int span);
void draw_rect(int x, int y, int w, int h);
void draw_point(int x, int y, float r);
void draw_poly(float *points, int n);
void debugdraw_flush(); /* This is called once per frame to draw all queued elements */
#endif

View File

@@ -1,24 +1,11 @@
#define PL_MPEG_IMPLEMENTATION
#define CGLTF_IMPLEMENTATION
#define GL_GLEXT_PROTOTYPES
//#define MATHC_USE_INT16
//#define MATHC_FLOATING_POINT_TYPE GLfloat
//#define MATHC_USE_DOUBLE_FLOATING_POINT
#define STB_DS_IMPLEMENTATION
#include <stb_ds.h>
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
#include <pl_mpeg.h>
#include "engine.h"
#ifdef EDITOR
#include "editor.h"
#endif
#include <SDL.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_mixer.h>
#include "openglrender.h"
#include "window.h"
#include "camera.h"
@@ -27,96 +14,63 @@
#include "2dphysics.h"
#include "gameobject.h"
#include "registry.h"
#include "log.h"
#include "resources.h"
#define FPS30 33
#define FPS60 17
#define FPS120 8;
#define FPS144 7
#define FPS300 3
unsigned int frameCount = 0;
Uint32 lastTick = 0;
Uint32 frameTick = 0;
Uint32 elapsed = 0;
uint32_t lastTick = 0;
uint32_t frameTick = 0;
uint32_t elapsed = 0;
Uint32 physMS = FPS144;
Uint32 physlag = 0;
Uint32 renderMS = FPS144;
Uint32 renderlag = 0;
uint32_t physMS = FPS144;
uint32_t physlag = 0;
uint32_t renderMS = FPS144;
uint32_t renderlag = 0;
// TODO: Init on the heap
struct mCamera camera = {0};
#include "engine.h"
int main(int argc, char **args)
void engine_init()
{
script_init();
registry_init();
gameobjects = vec_make(sizeof(struct mGameObject), 100);
prefabs = vec_make(MAXNAME, 25);
// TODO: Init these on the heap instead
struct mCamera camera = { 0 };
camera.speed = 500;
stbi_set_flip_vertically_on_load(1);
resources_init();
openglInit();
sprite_initialize();
#ifdef EDITOR
editor_init(window);
#endif
phys2d_init();
quit = false;
SDL_Event e;
//While application is running
while (!quit) {
frameTick = SDL_GetTicks();
elapsed = frameTick - lastTick;
lastTick = frameTick;
deltaT = elapsed / 1000.f;
physlag += elapsed;
renderlag += elapsed;
input_poll();
if (physlag >= physMS) {
phys2d_update(physMS / 1000.f);
physlag -= physMS;
}
if (renderlag >= renderMS) {
if (physOn) {
vec_walk(gameobjects, gameobject_update);
}
camera_2d_update(&camera, renderMS / 1000.f);
openglRender(&camera);
#ifdef EDITOR
editor_render();
#endif
window_swap(window);
renderlag -= renderMS;
}
//Initialize SDL
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
YughLog(0, SDL_LOG_PRIORITY_ERROR,
"SDL could not initialize! SDL Error: %s", SDL_GetError());
}
//Use OpenGL 3.3
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK,
SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 2); /* How many x MSAA */
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 1);
resources_init();
script_init();
registry_init();
init_gameobjects();
prefabs = vec_make(MAXNAME, 25);
camera.speed = 500;
stbi_set_flip_vertically_on_load(1);
phys2d_init();
gui_init();
sound_init();
Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048);
}
void engine_stop()
{
SDL_StopTextInput();
SDL_Quit();
return 0;
}
}

19
source/engine/engine.h Normal file
View File

@@ -0,0 +1,19 @@
#ifndef ENGINE_H
#define ENGINE_H
#define FPS30 33
#define FPS60 17
#define FPS120 8;
#define FPS144 7
#define FPS300 3
#include <stb_ds.h>
#include <stb_image.h>
#include <pl_mpeg.h>
void engine_init();
void engine_stop();
#endif

View File

@@ -19,10 +19,13 @@ static uint32_t VAO = 0;
unsigned char ttf_buffer[24 << 20];
unsigned char temp_bitmap[512 * 512];
struct sFont MakeFont(const char *fontfile, int height)
static struct sFont *font;
static struct mShader *shader;
struct sFont *MakeFont(const char *fontfile, int height)
{
struct sFont newfont = { 0 };
newfont.height = height;
struct sFont *newfont = calloc(1, sizeof(struct sFont));
newfont->height = height;
char fontpath[256];
snprintf(fontpath, 256, "fonts/%s", fontfile);
@@ -42,7 +45,7 @@ struct sFont MakeFont(const char *fontfile, int height)
bitmap =
stbtt_GetCodepointBitmap(&fontinfo, 0,
stbtt_ScaleForPixelHeight(&fontinfo,
newfont.
newfont->
height), c,
&w, &h, 0, 0);
@@ -52,7 +55,7 @@ struct sFont MakeFont(const char *fontfile, int height)
glTexImage2D(GL_TEXTURE_2D,
0, GL_RED, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, bitmap);
glGenerateMipmap(GL_TEXTURE_2D);
//glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
@@ -85,8 +88,6 @@ struct sFont MakeFont(const char *fontfile, int height)
glBindVertexArray(0);
return newfont;
}
void sdrawCharacter(struct Character c, mfloat_t cursor[2], float scale,
@@ -156,9 +157,13 @@ void sdrawCharacter(struct Character c, mfloat_t cursor[2], float scale,
}
void renderText(struct sFont font, struct mShader *shader,
const char *text, mfloat_t pos[2], float scale,
mfloat_t color[3], float lw)
void text_settype(struct sFont *mfont, struct mShader *mshader)
{
font = mfont;
shader = mshader;
}
void renderText(const char *text, mfloat_t pos[2], float scale, mfloat_t color[3], float lw)
{
shader_use(shader);
shader_setvec3(shader, "textColor", color);
@@ -193,7 +198,7 @@ void renderText(struct sFont font, struct mShader *shader,
&& (cursor[0] + ((ch.Advance >> 6) * scale) - pos[0] >=
lw)) {
cursor[0] = pos[0];
cursor[1] -= scale * font.height;
cursor[1] -= scale * font->height;
} else {
// now advance cursors for next glyph (note that advance is number of 1/64 pixels)
@@ -211,7 +216,7 @@ void renderText(struct sFont font, struct mShader *shader,
// Now wordStart and stringPos surround the word, go through them. If the word that's about to be drawn goes past the line width, go to next line
if (lw > 0 && (cursor[0] + wordWidth - pos[0] >= lw)) {
cursor[0] = pos[0];
cursor[1] -= scale * font.height;
cursor[1] -= scale * font->height;
}
while (wordstart < line) { // Go through
@@ -229,7 +234,7 @@ void renderText(struct sFont font, struct mShader *shader,
}
cursor[1] -= scale * font.height;
cursor[1] -= scale * font->height;
line = strtok(NULL, "\n");
}

View File

@@ -18,10 +18,11 @@ struct sFont {
uint32_t height;
};
struct sFont MakeFont(const char *fontfile, int height);
struct sFont *MakeFont(const char *fontfile, int height);
void sdrawCharacter(struct Character c, mfloat_t cursor[2], float scale,
struct mShader *shader, float color[3]);
void renderText(struct sFont font, struct mShader *shader,
void text_settype(struct sFont *font, struct mShader *shader);
void renderText(
const char *text, mfloat_t pos[2], float scale,
mfloat_t color[3], float lw);

View File

@@ -16,6 +16,11 @@ struct mGameObject *updateGO = NULL;
const int nameBuf[MAXNAME] = { 0 };
const int prefabNameBuf[MAXNAME] = { 0 };
void init_gameobjects()
{
gameobjects = vec_make(sizeof(struct mGameObject), 100);
}
struct mGameObject *get_gameobject_from_id(int id)
{
return vec_get(gameobjects, id - 1);

View File

@@ -44,6 +44,7 @@ struct mGameObject {
};
struct mGameObject *MakeGameobject();
void init_gameobjects();
void gameobject_delete(int id);
void clear_gameobjects();
int number_of_gameobjects();

View File

@@ -1,7 +1,7 @@
#include "input.h"
#include "window.h"
#include <SDL.h>
#include <SDL2/SDL.h>
int32_t mouseWheelX = 0;
int32_t mouseWheelY = 0;
@@ -22,10 +22,10 @@ void input_poll()
currentKeystates = SDL_GetKeyboardState(NULL);
while (SDL_PollEvent(&e)) {
window_handle_event(window, &e);
window_all_handle_events(&e);
#ifdef EDITOR
editor_input(&e);
//editor_input(&e);
#endif
}

View File

@@ -1,7 +1,7 @@
#ifndef INPUT_H
#define INPUT_H
#include <SDL.h>
#include <SDL2/SDL.h>
#include <stdint.h>
extern int32_t mouseWheelX;

View File

@@ -1,7 +1,7 @@
#ifndef LOG_H
#define LOG_H
#include <SDL_log.h>
#include <SDL2/SDL_log.h>
#define ERROR_BUFFER 2048

View File

@@ -658,6 +658,7 @@ Matrix 4×4 representation:
mfloat_t basis[3][3]);
mfloat_t *vec3_rotate_quat(mfloat_t * result, const mfloat_t * v,
const mfloat_t * q);

View File

@@ -3,7 +3,6 @@
#include "mesh.h"
#include "resources.h"
#include "shader.h"
#include <SDL_image.h>
#include <cgltf.h>
#include <string.h>

View File

@@ -1,7 +1,6 @@
#include "openglrender.h"
#include <SDL.h>
#include <SDL_image.h>
#include <SDL2/SDL.h>
#include "sprite.h"
#include "shader.h"
#include "font.h"
@@ -37,7 +36,7 @@ struct mShader *animSpriteShader = NULL;
static struct mShader *textShader;
static struct mShader *diffuseShader;
struct sFont stdFont;
struct sFont *stdFont;
static struct mShader *debugDepthQuad;
static struct mShader *debugColorPickShader;
@@ -94,34 +93,17 @@ static struct mSprite *tanim = NULL;
static unsigned int projUBO;
void openglInit()
void openglInit(struct mSDLWindow *window)
{
//Initialize SDL
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER)) {
YughLog(0, SDL_LOG_PRIORITY_ERROR,
"SDL could not initialize! SDL Error: %s", SDL_GetError());
}
//Use OpenGL 3.3
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK,
SDL_GL_CONTEXT_PROFILE_CORE);
window_makecurrent(window);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 2); /* How many x MSAA */
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 1);
// TODO: Add non starter initializtion return here for some reason?
window = MakeSDLWindow("Untitled Game", 1920, 1080,
SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN |
SDL_WINDOW_RESIZABLE);
//Use Vsync
if (SDL_GL_SetSwapInterval(1)) {
YughLog(0, SDL_LOG_PRIORITY_WARN,
"Unable to set VSync! SDL Error: %s", SDL_GetError());
}
sprite_initialize();
////// MAKE SHADERS
outlineShader = MakeShader("outlinevert.glsl", "outline.glsl");
@@ -161,7 +143,7 @@ void openglInit()
}
void openglRender(struct mCamera *mcamera)
void openglRender(struct mSDLWindow *window, struct mCamera *mcamera)
{
//////////// 2D projection
mfloat_t projection[16] = { 0.f };
@@ -640,7 +622,8 @@ void openglRender3d(struct mSDLWindow *window, struct mCamera *mcamera)
shader_setmat4(textShader, "projection", window->projection);
mfloat_t fontpos[2] = { 25.f, 25.f };
mfloat_t fontcolor[3] = { 0.5f, 0.8f, 0.2f };
renderText(stdFont, textShader, "Sample text", fontpos, 0.4f,
text_settype(stdFont, textShader);
renderText("Sample text", fontpos, 0.4f,
fontcolor, -1.f);
sprite_draw(tsprite);

View File

@@ -2,6 +2,7 @@
#define OPENGL_RENDER_H
#include "render.h"
#include "window.h"
struct mCamera;
struct mSDLWindow;
@@ -42,8 +43,8 @@ enum RenderMode {
OBJECTPICKER
};
void openglInit();
void openglRender(struct mCamera *camera);
void openglInit(struct mSDLWindow *window);
void openglRender(struct mSDLWindow *window, struct mCamera *camera);
void openglInit3d(struct mSDLWindow *window);
void openglRender3d(struct mSDLWindow *window, struct mCamera *camera);

View File

@@ -1,25 +0,0 @@
#include "pinball.h"
#include "gameobject.h"
#include "input.h"
struct flipper *pinball_flipper_make(struct mGameObject *go)
{
struct flipper *new = calloc(1, sizeof(struct flipper));
pinball_flipper_init(new, go);
return new;
}
void pinball_flipper_init(struct flipper *flip, struct mGameObject *go)
{
cpBodySetAngle(go->body, flip->angle1);
}
void pinball_flipper_update(struct flipper *flip, struct mGameObject *go)
{
if ((flip->left && currentKeystates[SDL_SCANCODE_LSHIFT])
|| currentKeystates[SDL_SCANCODE_RSHIFT]) {
cpBodySetAngle(go->body, flip->angle2);
} else
cpBodySetAngle(go->body, flip->angle1);
}

View File

@@ -1,18 +0,0 @@
#ifndef PINBALL_H
#define PINBALL_H
struct mGameObject;
struct flipper {
float angle1;
float angle2;
float flipspeed;
int left;
};
struct flipper *pinball_flipper_make(struct mGameObject *go);
void pinball_flipper_init(struct flipper *flip, struct mGameObject *go);
void pinball_flipper_update(struct flipper *flip, struct mGameObject *go);
#endif

View File

@@ -3,7 +3,7 @@
#define GL_GLEXT_PROTOTYPES
#include <GL/glew.h>
#include <SDL_opengl.h>
#include <SDL2/SDL_opengl.h>
#endif

View File

@@ -9,6 +9,8 @@
#include <ftw.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
char *DATA_PATH = NULL;
char *PREF_PATH = NULL;
@@ -29,13 +31,15 @@ char pathbuf[MAXPATH];
void resources_init()
{
char *dpath = SDL_GetBasePath();
DATA_PATH = malloc(strlen(dpath) + 1);
strcpy(DATA_PATH, dpath);
DATA_PATH = malloc(256);
getcwd(DATA_PATH, 256);
strncat(DATA_PATH, "/", 256);
char *ppath = SDL_GetPrefPath("Odplot", "Test Game");
PREF_PATH = malloc(strlen(ppath) + 1);
strcpy(PREF_PATH, ppath);
PREF_PATH = SDL_GetPrefPath("Odplot", "Test Game");
if (!PREF_PATH)
PREF_PATH = strdup("./tmp/");
}
char *get_filename_from_path(char *path, int extension)
@@ -87,7 +91,7 @@ static int ext_check(const char *path, const struct stat *sb, int typeflag,
return 0;
}
void fill_extensions(struct vec *vec, char *path, const char *ext)
void fill_extensions(struct vec *vec, const char *path, const char *ext)
{
c_vec = vec;
cur_ext = ext;
@@ -109,4 +113,11 @@ FILE *path_open(const char *fmt, const char *tag, ...)
FILE *f = fopen(pathbuf, tag);
return f;
}
char *make_path(char *file)
{
strncpy(pathbuf, DATA_PATH, MAXPATH);
strncat(pathbuf, file, MAXPATH);
return pathbuf;
}

View File

@@ -18,10 +18,11 @@ void resources_init();
extern struct vec *prefabs;
void findPrefabs();
void fill_extensions(struct vec *vec, char *path, const char *ext);
void fill_extensions(struct vec *vec, const char *path, const char *ext);
char *get_filename_from_path(char *path, int extension);
char *get_directory_from_path(char *path);
FILE *res_open(char *path, const char *tag);
FILE *path_open(const char *fmt, const char *tag, ...);
char *make_path(char *file);
#endif

View File

@@ -56,8 +56,8 @@ GLuint load_shader_from_file(char *path, int type)
{
char spath[MAXPATH] = {'\0'};
sprintf(spath, "%s%s%s", DATA_PATH, "shaders/", path);
FILE *f = fopen(spath, "r'");
sprintf(spath, "%s%s", "shaders/", path);
FILE *f = fopen(make_path(spath), "r'");
if (!path)
perror(spath), exit(1);

View File

@@ -2,7 +2,6 @@
#include "shader.h"
#include "camera.h"
#include <SDL_image.h>
#include <string.h>
#include "openglrender.h"

94
source/engine/sound.c Normal file
View File

@@ -0,0 +1,94 @@
#include "sound.h"
const char *audioDriver;
static int mus_ch = -1;
void sound_init()
{
int flags = MIX_INIT_MP3 | MIX_INIT_OGG;
int err = Mix_Init(flags);
if (err&flags != flags) {
printf("MIX did not init!!");
}
mus_ch = Mix_AllocateChannels(1);
}
void audio_open(const char *device)
{
Mix_OpenAudioDevice(44100, MIX_DEFAULT_FORMAT, 2, 2048, device, 0);
}
void audio_close()
{
Mix_CloseAudio();
}
struct sound *make_sound(const char *wav)
{
struct sound *new = calloc(1, sizeof(struct sound));
new->sound = Mix_LoadWAV(wav);
return new;
}
struct music *make_music(const char *ogg)
{
struct music *sound = calloc(1, sizeof(struct music));
sound->music = Mix_LoadMUS(make_path(ogg));
return sound;
}
void play_sound(struct sound *sound)
{
Mix_VolumeChunk(sound->sound, sound->volume);
Mix_PlayChannel(-1, sound->sound, 0);
}
void play_music(struct sound *music)
{
Mix_PlayChannel(mus_ch, music->sound, -1);
}
void music_set(struct sound *music)
{
}
void music_volume(unsigned char vol)
{
Mix_Volume(mus_ch, vol);
}
int music_playing()
{
return Mix_Playing(mus_ch);
}
int music_paused()
{
return Mix_Paused(mus_ch);
}
void music_resume()
{
Mix_Resume(mus_ch);
}
void music_pause()
{
Mix_Pause(mus_ch);
}
void music_stop()
{
Mix_HaltChannel(mus_ch);
}
void audio_init()
{
audioDriver = SDL_GetAudioDeviceName(0,0);
}

46
source/engine/sound.h Normal file
View File

@@ -0,0 +1,46 @@
#ifndef SOUND_H
#define SOUND_H
#include <SDL2/SDL_mixer.h>
struct sound {
Mix_Chunk *sound;
unsigned char volume;
};
struct music {
Mix_Music *music;
unsigned char volume;
};
struct player {
};
extern const char *audioDriver;
void sound_init();
void audio_open(const char *device);
void audio_close();
struct sound *make_sound(const char *wav);
struct music *make_music(const char *ogg);
void play_sound(struct sound *sound);
const char *get_audio_driver();
void play_music(struct sound *music);
void music_set(struct sound *music);
int music_playing();
int music_paused();
void music_volume(unsigned char vol);
void music_resume();
void music_pause();
void music_stop();
void audio_init();
#endif

View File

@@ -9,6 +9,8 @@
#include "gameobject.h"
#include <string.h>
static struct mGameObject *gui_go = NULL;
/*
static struct mShader *spriteShader = NULL;
@@ -48,13 +50,13 @@ void sprite_init(struct mSprite *sprite, struct mGameObject *go)
void sprite_loadtex(struct mSprite *sprite, const char *path)
{
sprite->tex = texture_loadfromfile(sprite->tex, path);
sprite->tex = texture_loadfromfile(path);
}
void sprite_loadanim(struct mSprite *sprite, const char *path,
struct Anim2D anim)
{
sprite->tex = texture_loadfromfile(sprite->tex, path);
sprite->tex = texture_loadfromfile(path);
sprite->anim = anim;
sprite->anim.timer =
SDL_AddTimer(sprite->anim.ms, incrementAnimFrame, sprite);
@@ -64,6 +66,10 @@ void sprite_loadanim(struct mSprite *sprite, const char *path,
*/
}
void sprite_settex(struct mSprite *sprite, struct Texture *tex)
{
sprite->tex = tex;
}
Uint32 incrementAnimFrame(Uint32 interval, struct mSprite *sprite)
{
@@ -204,3 +210,14 @@ void video_draw(struct datastream *stream, mfloat_t position[2],
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindVertexArray(0);
}
void gui_init()
{
gui_go = MakeGameobject();
}
struct mSprite *gui_makesprite()
{
struct mSprite *new = MakeSprite(gui_go);
return new;
}

View File

@@ -1,11 +1,12 @@
#ifndef SPRITE_H
#define SPRITE_H
#include <SDL_timer.h>
#include <SDL2/SDL_timer.h>
#include "mathc.h"
struct datastream;
struct mGameObject;
struct Texture;
struct Anim2D {
int frames;
@@ -34,6 +35,7 @@ void sprite_init(struct mSprite *sprite, struct mGameObject *go);
void sprite_loadtex(struct mSprite *sprite, const char *path);
void sprite_loadanim(struct mSprite *sprite, const char *path,
struct Anim2D anim);
void sprite_settex(struct mSprite *sprite, struct Texture *tex);
void sprite_initalize();
void sprite_draw(struct mSprite *sprite);
void spriteanim_draw(struct mSprite *sprite);
@@ -41,6 +43,9 @@ void video_draw(struct datastream *ds, mfloat_t pos[2], mfloat_t size[2],
float rotate, mfloat_t color[3]);
Uint32 incrementAnimFrame(Uint32 interval, struct mSprite *sprite);
struct mSprite *gui_makesprite();
void gui_init();
void sprite_draw_all();

View File

@@ -1,5 +1,4 @@
#include "static_actor.h"
#include "editorstate.h"
//ADDMAKE(StaticActor);

View File

@@ -1,12 +1,10 @@
#include "texture.h"
#define STBI_FAILURE_USERMSG
#include <SDL2/SDL.h>
#include <stdio.h>
#include <stb_image.h>
#include <stb_ds.h>
#include <GL/glew.h>
#include <SDL_image.h>
#include "log.h"
static struct {
@@ -14,14 +12,13 @@ static struct {
struct Texture *value;
} *texhash = NULL;
struct Texture *texture_loadfromfile(struct Texture *tex, const char *path)
struct Texture *texture_pullfromfile(const char *path)
{
int index = shgeti(texhash, path);
if (index != -1)
return texhash[index].value;
tex = calloc(1, sizeof(*tex));
struct Texture *tex = calloc(1, sizeof(*tex));
tex->path = malloc(strlen(path) + 1);
strncpy(tex->path, path, strlen(path) + 1);
tex->flipy = 0;
@@ -30,7 +27,38 @@ struct Texture *texture_loadfromfile(struct Texture *tex, const char *path)
tex->anim.frames = 1;
tex->anim.ms = 1;
int n;
stbi_set_flip_vertically_on_load(0);
unsigned char *data = stbi_load(path, &tex->width, &tex->height, &n, 4);
if (stbi_failure_reason()) {
YughLog(0, 3, "STBI failed to load file %s with message: %s",
tex->path, stbi_failure_reason());
}
tex->data = data;
shput(texhash, tex->path, tex);
glGenTextures(1, &tex->id);
return tex;
}
struct Texture *texture_loadfromfile(const char *path)
{
struct Texture *new = texture_pullfromfile(path);
tex_gpu_load(new);
return new;
}
void tex_pull(struct Texture *tex)
{
uint8_t n;
stbi_set_flip_vertically_on_load(0);
tex->data = stbi_load(tex->path, &tex->width, &tex->height, &n, 4);
@@ -38,22 +66,27 @@ struct Texture *texture_loadfromfile(struct Texture *tex, const char *path)
if (stbi_failure_reason())
YughLog(0, 3, "STBI failed to load file %s with message: %s",
tex->path, stbi_failure_reason());
shput(texhash, tex->path, tex);
tex_gpu_load(tex);
return tex;
}
void tex_reload(struct Texture *tex)
void tex_flush(struct Texture *tex)
{
tex_free(tex);
free(tex->data);
}
void tex_gpu_reload(struct Texture *tex)
{
tex_gpu_free(tex);
tex_gpu_load(tex);
}
void tex_free(struct Texture *tex)
{
free(tex->data);
free(tex->path);
free(tex);
}
void tex_gpu_load(struct Texture *tex)
{
if (tex->anim.frames >= 0) {
@@ -172,7 +205,9 @@ void tex_anim_set(struct TexAnimation *anim)
tex_anim_calc_uv(anim);
}
void tex_free(struct Texture *tex)
void tex_gpu_free(struct Texture *tex)
{
if (tex->id != 0) {
glDeleteTextures(1, &tex->id);

View File

@@ -1,7 +1,7 @@
#ifndef TEXTURE_H
#define TEXTURE_H
#include <SDL_timer.h>
#include <SDL2/SDL_timer.h>
struct Rect {
float x;
@@ -36,8 +36,8 @@ struct Texture {
char *type;
unsigned int id;
char *path;
unsigned int width;
unsigned int height;
int width;
int height;
short flipy;
unsigned char *data;
@@ -45,14 +45,14 @@ struct Texture {
struct TexAnim anim;
};
struct Texture *texture_loadfromfile(struct Texture *tex,
const char *path);
struct Texture *tex_pullfromfile(const char *path);
struct Texture *texture_loadfromfile(const char *path);
void tex_gpu_load(struct Texture *tex);
void tex_reload(struct Texture *tex);
void tex_gpu_reload(struct Texture *tex);
void tex_gpu_free(struct Texture *tex);
void tex_free(struct Texture *tex);
void tex_flush(struct Texture *tex);
void tex_pull(struct Texture *tex);
void tex_bind(struct Texture *tex);
unsigned int powof2(unsigned int num);
int ispow2(int num);

View File

@@ -0,0 +1,19 @@
Copyright (c) 2007-2015 Scott Lembcke and Howling Moon Software
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,300 @@
What's new in 7.0.3:
* MISC: Replacing GLFW with Sokol in the demo application. No need to push GLFW binaries and has a nice x-platform renderer to build on.
* MISC: Fixed some 'const' warnings for MSCV.
What's new in 7.0.2:
* MISC: Merging pull requests. Build fixes and the like.
What's new in 7.0.1:
* BUG: Remove references to M_PI sinces it's not actually part of C and causes problems with MSVC.
* BUG: Build fixes for Mac/CMake and MSVC 13.
* BUG: Move to using __declspec(dllexport) for Windows builds.
* BUG: Fixed a precision issue with the EPA algorithm that would cause excessive iteration.
* BUG: cpPolyshapeNewRaw() was undefined.
* BUG: Changing gravity will wake up all objects in a space.
What's new in 7.0.0:
* All features from Chipmunk Pro are now free and open source! (threaded and NEON solver, autogeometry)
* API: Lots of cleanup to the API naming for better consistency.
* API: Renamed nearest point queries to simply point queries.
* API: Removed many deprecated functions.
* API: Struct definitions have become fully opaque instead of mangling names with the CP_PRIVATE() macro.
* API: Replaced templated accessor functions with concrete ones. Should be simpler to deal with for FFIs.
* API: Optional automatic mass properties for shapes. Calculates the moment of inertia and center of gravity for you.
* API: Optional anchor point for bodies that is separate from the center of gravity.
* API: Added radius parameters to many functions dealing with shapes (moment calculation, initialization, etc).
* API: The convex hull and winding is automatically calculated when creating a poly shape.
* API: Added a cpShapesCollide() function to check overlap of arbitrary shapes.
* API: cpShape filter property to supersede layers and groups.
* API: Collision handlers now return a collision handler struct to make it simpler to set up callbacks.
* API: Wildcard collision types.
* API: The cpArbiterTotalImpulseWithFriction() function was renamed to cpArbiterTotalImpulse(). The old useless cpArbiterTotalImpulse() implementation was removed.
* API: Contacts now store the colliding point on the surface of both shapes.
* API: cpArbiterIsRemoval() to check if a separate callback is called due to a removal and not a true separating collision.
* API: Arbiters now only store one normal per pair of colliding shapes.
* API: cpBBNewForExtents().
* API: Added a concrete kinematic body type to replace the confusing "rogue" body concept.
* API: Added a 2x3 affine transform type, cpTransform.
* API: Added a new debug rendering API.
* MISC: Numerous improvements to the collision detection.
* MISC: cpPolyline structs are passed by reference instead of value. (I've regretted that decision for years!)
What's new in 6.2.2:
* Fixed some issues on arm64.
* PRO: Added a 64 bit NEON solver to use on arm64.
What's new in 6.2.1:
* Added Android support to the CMake files. (Thanks Eric Wing!)
* Added a MSVC 2012 project file. (Thanks Leonid Usov!)
* Merged a fix for VAOs on Windows. (Thanks Leonid Usov!)
* Merged a couple of other minor fixes.
* BUG: Fixed a crash issue with the ChipmunkTileCache and ChipmunkPointCloudSampler classes. (Pro only).
What's new in 6.2.0:
* Collision detection now primarily uses the GJK and EPA algorithms instead of SAT. Internally this was a rather huge change. o_O
* Improved collision point quality and better collision point identification.
* All shape types can now be given a rounding radius.
* Collisions are now guaranteed to have a maximum of 2 collision points.
* Poly to poly collision performance is slightly better when they have a radius. Slightly worse with none.
* Implemented smoothed segment collisions to prevent colliding with the "cracks" between segment shapes.
* API: (Officially) added cpSegmentShapeSetNeighbors() used to enable smoothed line collisions.
* API: Added cpBBCenter() to get the center of a bounding box.
* API: Added cpPolyShapeInit2() and cpPolyShapeNew2() to create poly shapes with a radius. (Horrible names yes, but it will go away in Chipmunk 7)
* API: Added cpBoxShapeInit3() and cpBoxShapeNew3() to create boxes with a radius.
* API: Added cpPolyShapeGetRadius() and cpPolyShapeSetRadius() (the latter only in chipmunk_unsafe.h).
* API: Added cpNearestPointQueryInfo.g which returns the gradient of the signed distance field for the shape.
* BUG: cpMomentForPoly() will now return a correct value for degenerate 2 vertex polygons.
* BUG: Fixed an issue where certain segment query calls would return a t value of 0 instead of 1 for a missed query.
* MISC: Passing cpvzero to cpvnormalize() will now return cpvzero. No need to worry about NaNs or cpvnormalize_safe().
* MISC: Demo app now uses GLFW instead of GLUT, and has improved drawing and text rendering routines.
What's new in 6.1.5:
* API: Added cpArbiter*SurfaceVelocity() to allow for custom surface velocity calculation.
* API: Added cpArbiteSetContactPointSet() to allow changing the contact geometry on the fly.
* API: Added cpSpaceConvertBodyToStatic() and cpSpaceConvertBodyToDynamic().
* API: Added [ChipmunkBody velocityAt*Point:] methods to wrap their C equivalents. (Pro only)
* API: Added overridable [ChipmunkBody updateVelocity:...] and [ChipmunkBody updatePosition:] methods. (Pro only)
* API: Added .space properties to ChipmunkBody, ChipmunkShape and ChipmunkConstaint to wrap their C equivalents. (Pro only)
* API: Added overridable [ChipmunkConstraint preSolve:] and [ChipmunkConstraint postSolve:] methods. (Pro only)
* API: Added an ChipmunkMultiGrab.grabSort property that allows you to prioritize which shape is grabbed when there is overlap. (Pro only)
* MISC: Segment queries started inside of a shape now return t=0 and n=cpvzero instead of being undefined.
* MISC: Cleaned up a lot of common assertion messages to be more clear.
* MISC: Added a new demo called Shatter.
* MISC: Added a crushing force estimation example to the ContactGraph demo and a static/dynamic conversion example to Plink.
* MISC: Modified the Sticky demo to use the new cpArbiteSetContactPointSet() to avoid the use of unnecessary sensor shapes.
* MISC: [ChipmunkSpace addBounds:...] now returns a NSArray of the bounding segments. (Pro only)
What's new in 6.1.4:
* MISC: Fixed a build script issue that was preventing the documentation from being generated.
What's new in 6.1.3:
* BUG: Fixed a couple of very specific but fatal bugs that occur when sleeping is enabled and filtering collisions.
* BUG: Fixed an issue with cpvslerp() between very similar vectors.
* BUG: Fixed an issue with grab friction in ChipmunkMultiGrab. (Pro only)
* MISC: Implemented the cpConstraintGetImpulse() functionality for spring joints.
* MISC: Added more functions to chipmunk_ffi.h
What's new in 6.1.2:
* API: Added a cpArbiter.data pointer. Now you can tag collisions with custom persistent data.
* API: Added segment to segment collisions (thanks to LegoCylon)
* API: cpSpaceAddPostStepCallback() now returns false if the callback was a duplicate.
* API: Added the ChipmunkAbstractSampler.marchThreshold property instead of hardcoding it to 0.5.
* API: Added ChipmunkGrooveJoint properties for the groove and joint anchors.
* API: ChipmunkMultiGrab now returns information about grabbed shapes.
* BUG: Fixed a minor (non-crashing, non-leaking) memory pooling issue with reindexing lots of static shapes.
* BUG: Fixed an issue with the slerp functions that would cause them to return incorrect results when given non-unit length input.
* BUG: Fixed a precision bug with the ChipmunkImage sampler classes that could cause artifacts or miss small features.
* BUG: Fixed a number of properties in Objective-Chipmunk that should have been nonatomic.
* BUG: Fixed a number of types in Objective-Chipmunk that were incorrectly id that should have been cpGroup, cpCollisionType etc. It's now possible to redefine them at compile time if you wish.
* MISC: Dropped armv6 support in favor of armv7s on iOS. (You can switch it back easily if you need.)
* MISC: Updated iOS build scripts to guess the latest SDK.
* MISC: Added the "Sticky Surfaces" demo as a cpArbiter.data example.
* MISC: Updated Objective-Chipmunk build scripts to always use the latest iOS SDK.
What's new in 6.1.1:
* API: Renamed the new block based iterators as soon as possible to match the Apple convention ("_b" suffix).
What's new in 6.1.0:
* API: Added a pthread based, multi-threaded solver to accelerate the game on multi-core systems. (Pro only)
* API: Added cpConvexHull() and CP_CONVEX_HULL() for generating convex hulls.
* API: Added cpPolylineConvexDecomposition_BETA() to generate an approximate concave decomposition of a polyline. (Pro only)
* API: Added [ChipmunkPolyline toConvexHull:] to generate approximate convex hulls. (Pro only).
* API: Added [ChipmunkPolylineSet toConvexHulls_BETA:]. (Pro only)
* API: Added nearest point queries.
* API: Added a push mode to ChipmunkMultiGrab so touches can interact with the scene even if they didn't initially touch a shape. (Pro only)
* API: Added optional block based iterators.
* API: Added a space property to cpBody, cpShape and cpConstraint types.
* BUG: Fixed an issue with changing the floating point and vector type on OS X.
* BUG: Fixed a pixel offset in ChipmunkImageSampler that could cause minor sampling artifacts. (Pro only)
* BUG: Fixed an issue where cpShape and cpConstraint structs could have garbage space pointers if cpcalloc() was redefined.
* BUG: Fixed assertions in cpArbiter getters to correctly reflect a contact count of 0 from separate() callbacks.
* BUG: Fixed a regression relating to registering post-step() callbacks from other post-step() callbacks.
* BUG: Fixed a minor memory leak for sleeping bodies when destroying a space.
* MISC: Point queries are now deprecated in preference to point queries.
* MISC: cpSpatialIndexPointQuery() was redundant and has been removed. Use cpSpatialIndexQuery() instead.
* MISC: cpShape*Query() functions now accept a NULL info pointer if you don't want detailed query info.
* MISC: The enableContactGraph property of cpSpace is deprecated and always be true.
* MISC: Added a new demos of the convex hull functions and a self balancing Unicycle.
What's new in 6.0.3:
* API: Added a cpBBForCircle() convenience function.
* API: Added cpBBSegmentQuery() to check where a segment hits a cpBB.
* API: Added cpBodyGetVelAtWorldPoint() and cpBodyGetVelAtLocalPoint() to get point velocities on a body.
* API: Added cpArbiterTotalKE() to calculate the energy lost due to a collision. Great for calculating damage accurately.
* API: Added methods to get an ObjC pointer from a C chipmunk struct.
* API: Added a CHIPMUNK_ARBITER_GET_BODIES() macro for Objective-Chipmunk.
* API: The Objective-Chipmunk headers are now ARC compatible.
* API: Added a [ChipmunkSpace contains:] method to check if a ChipmunkObject has been added to the space or not.
* API: Added a cpBBNewForCircle() function.
* API: Added a cpBBSegmentQuery() function for raycasting againsts AABBs.
* BUG: Fixed a regression with ChipmunkSpace.bodies and ChipmunkSpace.shapes that caused crashes.
* BUG: Fixed a rare bug with postStep() callbacks and iterators.
* BUG: Fixed a border case in cpBBIntersectsSegment() that could cause missed segment queries.
* MISC: Added some new assertions for error conditions that were previously uncaught.
* MISC: Accelerated segment queries in cpBBTree by sorting the nodes.
* MISC: Added a new "Slice" demo that lets you cut up a polygon.
* MISC: Added NEON optimizations for Chipmunk Pro. Expect running on most ARM platforms to be 25-35% faster for contact heavy simulations.
* MISC: All ChipmunkObject instances added to a space are now retained, even composite ones.
What's new in 6.0.2:
* API: Added cpSpaceIsLocked() to check if you are in a callback or not.
* API: Removed the long deprecated [ChipmunkSpace addShapeAHandler:] and [ChipmunkSpace addShapeBHandler:] methods.
* API: The ChipmunkObject protocol now can return any id<NSFastEnumeration> object instead of just an NSSet.
* API: The largely useless [ChipmunkSpace addBaseObjects:] and [ChipmunkSpace removeBaseObjects:] methods were removed.
* API: Added [ChipmunkSpace smartAdd:] and [ChipmunkSpace smartRemove:] methods for a consistent API to remove objects inside and out of callbacks.
* API: Added [ChipmunkSpace addPostStepBlock:key:] to complement [ChipmunkSpace addPostStepCallback:selector:key:].
* API: Added [ChipmunkSpace addPostStepAddition:].
* API: Objective-Chipmunk collision handlers no longer retain their target to avoid reference cycles.
* API: Added callbacks to joints.
* BUG: Soft errors (only checked when debug mode is enabled) and warnings were disabled. Whoops.
* BUG: cpShapeIsSensor() was incorrectly named in chipmunk_ffi.h.
* BUG: It should be safe to call cpActivateBody() from an space iterator callback now.
* MISC: Very nice bouyancy demo added based on callbacks.
* MISC: Breakable Joints demo showing how to use the new joint callbacks.
* MISC: Player demo updated and greatly enhanced by Chipmunk 6 features.
* MISC: Changed adding a static body to a space from a warning to a hard error.
* MISC: cpGroup and cpCollisionType now default to uintptr_t so you can safely use pointers instead of ints for these types.
* MISC: Updated the MSVC10 project file.
* MISC: Updated the FFI defs.
What's new in 6.0.1:
* BUG: Calling cpBodySetPos() on a sleeping body was delaying the Separate() handler callback if one existed.
* BUG: Fixed a bug where Separate() handler callbacks were not occuring when removing shapes.
* BUG: Calling cpBodyApplyForce() or cpBodyResetForces() was not activating sleeping bodies.
* API: Added cpSpaceEachConstraint().
* API: Added a "CurrentTimeStep" property to cpSpace to retrieve the current (or most recent) timestep.
* MISC: Got rid of anonymous unions so that it is C99 clean again.
What's new in 6.0.0:
Chipmunk 6.x's API is not quite 100% compatible with 5.x. Make sure you read the list of changes carefully.
Keep in mind that this is a x.0.0 release and that it's likely there are still some bugs I don't know about yet. I've spent a lot of effort rewritting the collision detection, sleeping, and contact graph algorithms that have required large changes and cleanup to the 5.x codebase. I've ironed out all the bugs that I know of, and the beta test went well. So it's finally time for 6!
* API: Chipmunk now has hard runtime assertions that aren't disabled in release mode for many error conditions. Most people have been using release builds of Chipmunk during development and were missing out on very important error checking.
* API: Access to the private API has been disabled by default now and much of the private API has changed. I've added official APIs for all the uses of the private API I knew of.
* API: Added accessor functions for every property on every type. As Chipmunk's complexity has grown, it's become more difficult to ignore accessors. You are encouraged to use them, but are not required to.
* API: Added cpSpaceEachBody() and cpSpaceEachShape() to iterate bodies/shapes in a space.
* API: Added cpSpaceReindexShapesForBody() to reindex all the shapes attached to a particular body.
* API: Added a 'data' pointer to spaces now too.
* API: cpSpace.staticBody is a pointer to the static body instead of a static reference.
* API: The globals cp_bias_coef, cp_collision_slop, cp_contact_persistence have been moved to properties of a space. (collisionBias, collisionSlop, collisionPersistence respectively)
* API: Added cpBodyActivateStatic() to wake up bodies touching a static body with an optional shape filter parameter.
* API: Added cpBodyEachShape() and cpBodyEachConstraint() iterators to iterate the active shapes/constraints attached to a body.
* API: Added cpBodyEeachArbiter() to iterate the collision pairs a body is involved in. This makes it easy to perform grounding checks or find how much collision force is being applied to an object.
* API: The error correction applied by the collision bias and joint bias is now timestep independent and the units have completely changed.
* FIX: Units of damping for springs are correct regardless of the number of iterations. Previously they were only correct if you had 1 or 2 iterations.
* MISC: Numerous changes to help make Chipmunk work better with variable timesteps. Use of constant timesteps is still highly recommended, but it is now easier to change the time scale without introducing artifacts.
* MISC: Performance! Chipmunk 6 should be way faster than Chipmunk 5 for almost any game.
* MISC: Chipmunk supports multiple spatial indexes and uses a bounding box tree similar to the one found in the Bullet physics library by default. This should provide much better performance for scenes with objects of differening size and works without any tuning for any scale.
What's new in 5.3.5
* FIX: Fixed spelling of cpArbiterGetDepth(). Was cpArbiteGetDepth() before. Apparently nobody ever used this function.
* FIX: Added defines for M_PI and M_E. Apparently these values were never part of the C standard math library. Who knew!?
* FIX: Added a guard to cpBodyActivate() so that it's a noop for rouge bodies.
* FIX: Shape queries now work with (and against) sensor shapes.
* FIX: Fixed an issue where removing a collision handler while a separate() callback was waiting to fire the next step would cause crashes.
* FIX: Fixed an issue where the default callback would not be called for sensor shapes.
* FIX: Resetting or applying forces or impulses on a body causes it to wake up now.
* MISC: Added a check that a space was not locked when adding or removing a callback.
* MISC: Removed cpmalloc from the API and replaced all occurences with cpcalloc
* MISC: Added a benchmarking mode to the demo app. -trial runs it in time trial mode and -bench makes it run some benchmarking demos.
What's new in 5.3.4:
* FIX: cpBodyActivate() can now be called from collision and query callbacks. This way you can use the setter functions to change properties without indirectly calling cpBodyActivate() and causing an assertion.
* FIX: cpArbiterGetContactPointSet() was returning the collision points for the normals.
* FIX: cpSpaceEachBody() now includes sleeping bodies.
* FIX: Shapes attached to static rogue bodies created with cpBodyNewStatic() are added as static shapes.
* MISC: Applied a user patch to update the MSVC project and add a .def file.
What's new in 5.3.3:
* API: Added cpArbiteGetCount() to return the number of contact points.
* API: Added helper functions for calculating areas of Chipmunk shapes as well as calculating polygon centroids and centering polygons on their centroid.
* API: Shape queries. Query a shape to test for collisions if it were to be inserted into a space.
* API: cpBodyInitStatic() and cpBodyNewStatic() for creating additional static (rogue) bodies.
* API: cpBodySleepWithGroup() to allow you to create groups of sleeping objects that are woken up together.
* API: Added overloaded *, +, - and == operators for C++ users.
* API: Added cpSpaceActivateShapesTouchingShape() to query for and activate any shapes touching a given shape. Useful if you ever need to move a static body.
* FIX: Fixed an extremely rare memory bug in the collision cache.
* FIX: Fixed a memory leak in Objective-Chipmunk that could cause ChipmunkSpace objects to be leaked.
* MISC: C struct fields and function that are considered private have been explicitly marked as such. Defining CP_ALLOW_PRIVATE_ACCESS to 0 in Chipmunk.h will let you test which parts of the private API that you are using and give me feedback about how to build proper APIs in Chipmunk 6 for what you are trying to do.
* MISC: Allow CGPoints to be used as cpVect on Mac OS X as well as iOS.
What's new in 5.3.2:
* FIX: Collision begin callbacks were being called continuously for sensors or collisions rejected from the pre-solve callback.
* FIX: Plugged a nasty memory leak when adding post-step callbacks.
* FIX: Shapes were being added to the spatial hash using an uninitialized bounding box in some cases.
* FIX: Perfectly aligned circle shapes now push each other apart.
* FIX: cpBody setter functions now call cpBodyActivate().
* FIX: Collision handler targets are released in Objective-Chipmunk when they are no longer needed instead of waiting for the space to be deallocated.
* API: cpSpaceSegmentQuery() no longer returns a boolean. Use cpSpaceSegmentQueryFirst() instead as it's more efficient.
* NEW: cpSpaceRehashShape() Rehash an individual shape, active or static.
* NEW: cpBodySleep() Force a body to fall asleep immediately.
* NEW: cpConstraintGetImpulse() Return the most recent impulse applied by a constraint.
* NEW: Added setter functions for the groove joint endpoints.
* MISC: A number of other minor optimizations and fixes.
What's new in 5.3.1:
* NEW: Added a brand new tutorial for Objective-Chipmunk: SimpleObjectiveChipmunk that can be found in the Objective-Chipmunk folder.
* NEW: Proper API docs for Objective-Chipmunk.
* NEW: Updated the included Objective-Chipmunk library.
* FIX: Fixed a rare memory crash in the sensor demo.
* FIX: Fixed some warnings that users submitted.
What's new in 5.3.0:
* FIX: Fixed the source so it can compile as C, C++, Objective-C, and Objective-C++.
* FIX: Fixed cp_contact_persistence. It was broken so that it would forget collision solutions after 1 frame instead of respecting the value set.
* OPTIMIZATION: Several minor optimizations have been added. Though performance should only differ by a few percent.
* OPTIMIZATION: Chipmunk now supports putting bodies to sleep when they become inactive.
* API: Elastic iterations are now deprecated as they should no longer be necessary.
* API: Added API elements to support body sleeping.
* API: Added a statically allocated static body to each space for attaching static shapes to.
* API: Static shapes attached to the space's static body can simply be added to the space using cpSpaceAddShape().
* NEW: New MSVC projects.
* NEW: Added boolean and time stamp types for clarity.
What's new in 5.2.0:
* OPTIMIZATION: Chipmunk structs used within the solver are now allocated linearly in large blocks. This is much more CPU cache friendly. Programs have seen up to 50% performance improvements though 15-20% should be expected.
* API: Shape references in cpArbiter structs changed to private_a and private_b to discourage accessing the fields directly and getting them out of order. You should be using cpArbiterGetShapes() or CP_ARBITER_GET_SHAPES() to access the shapes in the correct order.
* API: Added assertion error messages as well as warnings and covered many new assertion cases.
* FIX: separate() callbacks are called before shapes are removed from the space to prevent dangling pointers.
* NEW: Added convenience functions for creating box shapes and calculating moments.
What's new in 5.1.0:
* FIX: fixed a NaN issue that was causing raycasts for horizontal or vertical lines to end up in an infinite loop
* FIX: fixed a number of memory leaks
* FIX: fixed warnings for various compiler/OS combinations
* API: Rejecting a collision from a begin() callback permanently rejects the collision until separation
* API: Erroneous collision type parameterns removed from cpSpaceDefaulteCollisionHandler()
* MOVE: FFI declarations of inlined functions into their own header
* MOVE: Rearranged the project structure to separate out the header files into a separate include/ directory.
* NEW: Added a static library target for the iPhone.
* NEW: Type changes when building on the iPhone to make it friendlier to other iPhone APIs
* NEW: Added an AABB query to complement point and segment queries
* NEW: CP_NO_GROUP and CP_ALL_LAYERS constants
What's new in 5.0.0:
* Brand new Joint/Constraint API: New constraints can be added easily and are much more flexible than the old joint system
* Efficient Segment Queries - Like raycasting, but with line segments.
* Brand new collision callback API: Collision begin/separate events, API for removal of objects within callbacks, more programable control over collision handling.

View File

@@ -0,0 +1,119 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Chipmunk Game Dynamics Documentation</title>
<link rel="stylesheet" type="text/css" href="stylesheet.css" />
</head>
<body>
<h1>Example Code Snippets:</h1>
<!-- Transformation Example -->
<a name="Transform" />
<h2>Getting a Transformation from a Rigid Body:</h2>
<p>You can quickly and easily build a transformation matrix from a Chipmunk body. The following code is for OpenGL, but it should be trivial to modify for DirectX or affine transforms. (Note that OpenGL matrices are column-major)</p>
<pre style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;font-size:small; font-family:monospace; ">cpVect pos = body-&gt;p;
cpVect rot = body-&gt;rot;
GLFloat matrix[<span style="color:#0000ff;">16</span>] = {
rot.x, rot.y, <span style="color:#0000ff;">0.0f</span>, <span style="color:#0000ff;">0.0f</span>,
-rot.y, rot.x, <span style="color:#0000ff;">0.0f</span>, <span style="color:#0000ff;">0.0f</span>,
<span style="color:#0000ff;">0.0f</span>, <span style="color:#0000ff;">0.0f</span>, <span style="color:#0000ff;">1.0f</span>, <span style="color:#0000ff;">0.0f</span>,
pos.x, pos.y, <span style="color:#0000ff;">0.0f</span>, <span style="color:#0000ff;">1.0f</span>,
};
<span style="color:#003369;">glMultMatrixf</span>(matrix.farr);
</pre>
<!-- Collision Callbacks Example -->
<a name="CollisionCallbacks" />
<h2>Collision Callbacks:</h2>
<p>This snippet demonstrates several Chipmunk collision callback features. It defines a collision handler that is called when collision shapes start touching and also a post-step callback to remove the collision shape and body.</p>
<pre style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;font-size:small; font-family:monospace; "><strong><span style="color:#881350;">static</span></strong> <strong><span style="color:#881350;">void</span></strong>
<span style="color:#003369;">postStepRemove</span>(cpSpace *space, cpShape *shape, <strong><span style="color:#881350;">void</span></strong> *unused)
{
<span style="color:#003369;">cpSpaceRemoveBody</span>(space, shape-&gt;body);
<span style="color:#003369;">cpSpaceRemoveShape</span>(space, shape);
<span style="color:#003369;">cpShapeFree</span>(shape);
<span style="color:#003369;">cpBodyFree</span>(shape-&gt;body);
}
<strong><span style="color:#881350;">static</span></strong> <strong><span style="color:#881350;">int</span></strong>
<span style="color:#003369;">begin</span>(cpArbiter *arb, cpSpace *space, <strong><span style="color:#881350;">void</span></strong> *unused)
{
<em><span style="color:#236e25;">// Get the cpShapes involved in the collision
</span></em> <em><span style="color:#236e25;">// The order will be the same as you defined in the handler definition
</span></em> <em><span style="color:#236e25;">// a-&gt;collision_type will be BULLET_TYPE and b-&gt;collision_type will be MONSTER_TYPE
</span></em> cpShape *a, *b; <span style="color:#003369;">cpArbiterGetShapes</span>(arb, &amp;a, &amp;b);
<em><span style="color:#236e25;">// Alternatively you can use the CP_ARBITER_GET_SHAPES() macro
</span></em> <em><span style="color:#236e25;">// It defines and sets the variables for you.
</span></em> <em><span style="color:#236e25;">//CP_ARBITER_GET_SHAPES(arb, a, b);
</span></em>
<em><span style="color:#236e25;">// Add a post step callback to safely remove the body and shape from the space.
</span></em> <em><span style="color:#236e25;">// Calling cpSpaceRemove*() directly from a collision handler callback can cause crashes.
</span></em> <span style="color:#003369;">cpSpaceAddPostStepCallback</span>(space, (cpPostStepFunc)postStepRemove, b, <strong><span style="color:#881350;">NULL</span></strong>);
<em><span style="color:#236e25;">// The object is dead, don&rsquo;t process the collision further
</span></em> <strong><span style="color:#881350;">return</span></strong> <span style="color:#0000ff;">0</span>;
}
<span style="color:#683821;">#define BULLET_TYPE </span><span style="color:#0000ff;">1</span><span style="color:#683821;">
#define MONSTER_TYPE </span><span style="color:#0000ff;">2</span><span style="color:#683821;">
</span>
<em><span style="color:#236e25;">// Define a collision handler for bullets and monsters
// Kill the monster by removing it&rsquo;s shape and body from the space as soon as it&rsquo;s hit by a bullet
</span></em><span style="color:#003369;">cpSpaceAddCollisionHandler</span>(space, BULLET_TYPE, MONSTER_TYPE, begin, <strong><span style="color:#881350;">NULL</span></strong>, <strong><span style="color:#881350;">NULL</span></strong>, <strong><span style="color:#881350;">NULL</span></strong>, <strong><span style="color:#881350;">NULL</span></strong>);</pre>
<p>For more callback examples, see the <a href="http://code.google.com/p/chipmunk-physics/source/browse/trunk/Demo/OneWay.c">One Way Platform Demo</a>, <a href="http://code.google.com/p/chipmunk-physics/source/browse/trunk/Demo/Sensors.c">Sensors Demo</a>, or the <a href="http://code.google.com/p/chipmunk-physics/source/browse/trunk/Demo/Player.c">Player Demo</a>.</p>
<!-- Query Examples -->
<a name="Query" />
<h2>Query Examples:</h2>
<p>The following example is taken directly from <a href="http://code.google.com/p/chipmunk-physics/source/browse/trunk/Demo/ChipmunkDemo.c#319">ChipmunkDemo.c</a>. When the mouse is clicked, a point query is performed to see if there is a shape under the mouse. If there is, it adds a joint to the body that links it to the mouse's movement.</p>
<pre style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;font-size:small; font-family:monospace; "><strong><span style="color:#881350;">static</span></strong> <strong><span style="color:#881350;">void</span></strong>
<span style="color:#003369;">click</span>(<strong><span style="color:#881350;">int</span></strong> button, <strong><span style="color:#881350;">int</span></strong> state, <strong><span style="color:#881350;">int</span></strong> x, <strong><span style="color:#881350;">int</span></strong> y)
{
<strong><span style="color:#881350;">if</span></strong>(button == GLUT_LEFT_BUTTON){
<strong><span style="color:#881350;">if</span></strong>(state == GLUT_DOWN){
cpVect point = <span style="color:#003369;">mouseToSpace</span>(x, y);
cpShape *shape = <span style="color:#003369;">cpSpacePointQueryFirst</span>(space, point, GRABABLE_MASK_BIT, <span style="color:#0000ff;">0</span>);
<strong><span style="color:#881350;">if</span></strong>(shape){
cpBody *body = shape-&gt;body;
mouseJoint = <span style="color:#003369;">cpPivotJointNew2</span>(mouseBody, body, cpvzero, <span style="color:#003369;">cpBodyWorld2Local</span>(body, point));
mouseJoint-&gt;maxForce = <span style="color:#0000ff;">50000.0f</span>;
mouseJoint-&gt;biasCoef = <span style="color:#0000ff;">0.15f</span>;
<span style="color:#003369;">cpSpaceAddConstraint</span>(space, mouseJoint);
}
} <strong><span style="color:#881350;">else</span></strong> <strong><span style="color:#881350;">if</span></strong>(mouseJoint){
<span style="color:#003369;">cpSpaceRemoveConstraint</span>(space, mouseJoint);
<span style="color:#003369;">cpConstraintFree</span>(mouseJoint);
mouseJoint = <strong><span style="color:#881350;">NULL</span></strong>;
}
}
}</pre>
<p>Perform a segment query to see if a laser beam hits a shape. We want to draw particles at both the position where the beam enters and exits the shape.</p>
<pre style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;font-size:small; font-family:monospace; ">cpVect a = <span style="color:#003369;">cpv</span>(...), b = <span style="color:#003369;">cpv</span>(...);
cpSegmentQueryInfo info = {};
<strong><span style="color:#881350;">if</span></strong>(<span style="color:#003369;">cpSpaceSegmentQueryFirst</span>(space, a, b, -<span style="color:#0000ff;">1</span>, <span style="color:#0000ff;">0</span>, &amp;info)){
cpSegmentQueryInfo info2;
<span style="color:#003369;">cpShapeSegmentQuery</span>(info.shape, b, a, &amp;info2);
cpVect enterPoint = <span style="color:#003369;">cpSegmentQueryHitPoint</span>(a, b, info);
cpVect exitPoint = <span style="color:#003369;">cpSegmentQueryHitPoint</span>(b, a, info2);
}</pre>
</body>
</html>

View File

@@ -0,0 +1,22 @@
<pre style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;font-size:small; font-family:monospace; "><em><span style="color:#236e25;">// Create the joint and set it's max force property.
</span></em>breakableJoint = <span style="color:#003369;">cpSpaceAddConstraint</span>(space, <span style="color:#003369;">cpPinJointNew</span>(body1, body2, <span style="color:#003369;">cpv</span>(<span style="color:#0000ff;">15</span>,<span style="color:#0000ff;">0</span>), <span style="color:#003369;">cpv</span>(-<span style="color:#0000ff;">15</span>,<span style="color:#0000ff;">0</span>)));
<span style="color:#003369;">cpConstraintSetMaxForce</span>(breakableJoint, <span style="color:#0000ff;">4000</span>);
<em><span style="color:#236e25;">// In your update function:
// Step your space normally...
</span></em>cpFloat dt = <span style="color:#0000ff;">1.0</span>/<span style="color:#0000ff;">60.0</span>;
<span style="color:#003369;">cpSpaceStep</span>(space, dt);
<strong><span style="color:#881350;">if</span></strong>(breakableJoint){
<em><span style="color:#236e25;">// Convert the impulse to a force by dividing it by the timestep.
</span></em> cpFloat force = <span style="color:#003369;">cpConstraintGetImpulse</span>(breakableJoint)/dt;
cpFloat maxForce = <span style="color:#003369;">cpConstraintGetMaxForce</span>(breakableJoint);
<em><span style="color:#236e25;">// If the force is almost as big as the joint's max force, break it.
</span></em> <strong><span style="color:#881350;">if</span></strong>(force &gt; <span style="color:#0000ff;">0.9</span>*maxForce){
<span style="color:#003369;">cpSpaceRemoveConstraint</span>(space, breakableJoint);
breakableJoint = <strong><span style="color:#881350;">NULL</span></strong>;
}
}
</pre>

View File

@@ -0,0 +1,38 @@
<div style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;font-size:small; font-family:monospace; "><span style="color:#881350;">static</span> <span style="color:#881350;">void</span><br />
<span style="color:#003369;">postStepRemove</span>(cpSpace *space, cpShape *shape, <span style="color:#881350;">void</span> *unused)<br />
{<br />
&nbsp;&nbsp;<span style="color:#003369;">cpSpaceRemoveShape</span>(space, shape);<br />
&nbsp;&nbsp;<span style="color:#003369;">cpSpaceRemoveBody</span>(space, shape-&gt;body);<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;<span style="color:#003369;">cpShapeFree</span>(shape);<br />
&nbsp;&nbsp;<span style="color:#003369;">cpBodyFree</span>(shape-&gt;body);<br />
}<br />
<br />
<span style="color:#881350;">static</span> <span style="color:#881350;">int</span><br />
<span style="color:#003369;">begin</span>(cpArbiter *arb, cpSpace *space, <span style="color:#881350;">void</span> *data)<br />
{<br />
&nbsp;&nbsp;<span style="color:#236e25;">// Get the cpShapes involved in the collision<br />
</span>&nbsp;&nbsp;<span style="color:#236e25;">// The order will be the same as you defined in the handler definition<br />
</span>&nbsp;&nbsp;<span style="color:#236e25;">// a-&gt;collision_type will be BULLET_TYPE and b-&gt;collision_type will be MONSTER_TYPE<br />
</span>&nbsp;&nbsp;<span style="color:#003369;">CP_ARBITER_GET_SHAPES</span>(arb, a, b);<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;<span style="color:#236e25;">// The macro expands exactly as if you had typed this:<br />
</span>&nbsp;&nbsp;<span style="color:#236e25;">// cpShape *a, *b; cpArbiterGetShapes(arb, &amp;a, &amp;b);<br />
</span>&nbsp;&nbsp;<br />
&nbsp;&nbsp;<span style="color:#236e25;">// Add a post step callback to safely remove the body and shape from the space.<br />
</span>&nbsp;&nbsp;<span style="color:#236e25;">// Calling cpSpaceRemove*() directly from a collision handler callback can cause crashes.<br />
</span>&nbsp;&nbsp;<span style="color:#003369;">cpSpaceAddPostStepCallback</span>(space, (cpPostStepFunc)postStepRemove, b, <span style="color:#881350;">NULL</span>);<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;<span style="color:#236e25;">// The object is dead, don&rsquo;t process the collision further<br />
</span>&nbsp;&nbsp;<span style="color:#881350;">return</span> <span style="color:#0000ff;">0</span>;<br />
}<br />
<br />
<span style="color:#683821;">#define BULLET_TYPE </span><span style="color:#0000ff;">1</span><span style="color:#683821;"><br />
#define MONSTER_TYPE </span><span style="color:#0000ff;">2</span><span style="color:#683821;"><br />
</span><br />
<span style="color:#236e25;">// Define a collision handler for bullets and monsters<br />
// Kill the monster by removing it&rsquo;s shape and body from the space as soon as it&rsquo;s hit by a bullet <br />
</span>cpCollisionHandler *handler = <span style="color:#003369;">cpSpaceAddCollisionHandler</span>(space, BULLET_TYPE, MONSTER_TYPE);<br />
handler-&gt;beginFunc = begin;<br />
<br />
</div>

View File

@@ -0,0 +1,23 @@
<pre style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;font-size:small; font-family:monospace; "><strong><span style="color:#881350;">struct</span></strong> CrushingContext {
cpFloat magnitudeSum;
cpVect vectorSum;
};
<strong><span style="color:#881350;">static</span></strong> <strong><span style="color:#881350;">void</span></strong>
<span style="color:#003369;">EstimateCrushingHelper</span>(cpBody *body, cpArbiter *arb, <strong><span style="color:#881350;">struct</span></strong> CrushingContext *context)
{
cpVect j = <span style="color:#003369;">cpArbiterTotalImpulseWithFriction</span>(arb);
context-&gt;magnitudeSum += <span style="color:#003369;">cpvlength</span>(j);
context-&gt;vectorSum = <span style="color:#003369;">cpvadd</span>(context-&gt;vectorSum, j);
}
cpFloat
<span style="color:#003369;">EstimateCrushForce</span>(cpBody *body, cpFloat dt)
{
<strong><span style="color:#881350;">struct</span></strong> CrushingContext crush = {<span style="color:#0000ff;">0.0f</span>, cpvzero};
<span style="color:#003369;">cpBodyEachArbiter</span>(body, (cpBodyArbiterIteratorFunc)EstimateCrushingHelper, &amp;crush);
<span style="color:#236e25;"><em>// Compare the vector sum magnitude and magnitude sum to see if
</em></span> <span style="color:#236e25;"><em>// how much the collision forces oppose one another.
</em></span> cpFloat crushForce = (crush.magnitudeSum - <span style="color:#003369;">cpvlength</span>(crush.vectorSum))*dt;
}</pre>

View File

@@ -0,0 +1,18 @@
<pre style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;font-size:small; font-family:monospace; "><span style="color:#236e25;"><em>// This example is pulled from the Plink demo.
</em></span><strong><span style="color:#881350;">if</span></strong>(ChipmunkDemoRightDown){
<span style="color:#236e25;"><em>// Find the shape under the mouse.
</em></span> cpShape *nearest = <span style="color:#003369;">cpSpaceNearestPointQueryNearest</span>(space, ChipmunkDemoMouse, <span style="color:#0000ff;">0.0</span>, GRABABLE_MASK_BIT, CP_NO_GROUP, <strong><span style="color:#881350;">NULL</span></strong>);
<strong><span style="color:#881350;">if</span></strong>(nearest){
cpBody *body = <span style="color:#003369;">cpShapeGetBody</span>(nearest);
<strong><span style="color:#881350;">if</span></strong>(<span style="color:#003369;">cpBodyIsStatic</span>(body)){
<span style="color:#236e25;"><em>// If the body is static, convert it to dynamic and add it to the space.
</em></span> <span style="color:#003369;">cpSpaceConvertBodyToDynamic</span>(space, body, pentagon_mass, pentagon_moment);
<span style="color:#003369;">cpSpaceAddBody</span>(space, body);
} <strong><span style="color:#881350;">else</span></strong> {
<span style="color:#236e25;"><em>// If the body is dynamic, remove it from the space and convert it to static.
</em></span> <span style="color:#003369;">cpSpaceRemoveBody</span>(space, body);
<span style="color:#003369;">cpSpaceConvertBodyToStatic</span>(space, body);
}
}
}
</pre>

View File

@@ -0,0 +1,64 @@
<div style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;font-size:small; font-family:monospace; "><span style="color:#78492a;">#include </span><span style="color:#f4181b;">&lt;stdio.h&gt;</span><span style="color:#78492a;"><br />
#include </span><span style="color:#f4181b;">&lt;chipmunk.h&gt;</span><span style="color:#78492a;"><br />
</span><br />
<span style="color:#a71790;">int</span> <span style="color:#003668;">main</span>(<span style="color:#a71790;">void</span>){<br />
&nbsp;&nbsp;<em><span style="color:#236e25;">// cpVect is a 2D vector and cpv() is a shortcut for initializing them.<br />
</span></em>&nbsp;&nbsp;cpVect gravity = <span style="color:#003668;">cpv</span>(<span style="color:#0000ff;">0</span>, -<span style="color:#0000ff;">100</span>);<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;<em><span style="color:#236e25;">// Create an empty space.<br />
</span></em>&nbsp;&nbsp;cpSpace *space = <span style="color:#003668;">cpSpaceNew</span>();<br />
&nbsp;&nbsp;<span style="color:#003668;">cpSpaceSetGravity</span>(space, gravity);<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;<em><span style="color:#236e25;">// Add a static line segment shape for the ground.<br />
</span></em>&nbsp;&nbsp;<em><span style="color:#236e25;">// We'll make it slightly tilted so the ball will roll off.<br />
</span></em>&nbsp;&nbsp;<em><span style="color:#236e25;">// We attach it to a static body to tell Chipmunk it shouldn't be movable.<br />
</span></em>&nbsp;&nbsp;cpShape *ground = <span style="color:#003668;">cpSegmentShapeNew</span>(<span style="color:#003668;">cpSpaceGetStaticBody</span>(space), <span style="color:#003668;">cpv</span>(-<span style="color:#0000ff;">20</span>, <span style="color:#0000ff;">5</span>), <span style="color:#003668;">cpv</span>(<span style="color:#0000ff;">20</span>, -<span style="color:#0000ff;">5</span>), <span style="color:#0000ff;">0</span>);<br />
&nbsp;&nbsp;<span style="color:#003668;">cpShapeSetFriction</span>(ground, <span style="color:#0000ff;">1</span>);<br />
&nbsp;&nbsp;<span style="color:#003668;">cpSpaceAddShape</span>(space, ground);<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;<em><span style="color:#236e25;">// Now let's make a ball that falls onto the line and rolls off.<br />
</span></em>&nbsp;&nbsp;<em><span style="color:#236e25;">// First we need to make a cpBody to hold the physical properties of the object.<br />
</span></em>&nbsp;&nbsp;<em><span style="color:#236e25;">// These include the mass, position, velocity, angle, etc. of the object.<br />
</span></em>&nbsp;&nbsp;<em><span style="color:#236e25;">// Then we attach collision shapes to the cpBody to give it a size and shape.<br />
</span></em>&nbsp;&nbsp;<br />
&nbsp;&nbsp;cpFloat radius = <span style="color:#0000ff;">5</span>;<br />
&nbsp;&nbsp;cpFloat mass = <span style="color:#0000ff;">1</span>;<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;<em><span style="color:#236e25;">// The moment of inertia is like mass for rotation<br />
</span></em>&nbsp;&nbsp;<em><span style="color:#236e25;">// Use the cpMomentFor*() functions to help you approximate it.<br />
</span></em>&nbsp;&nbsp;cpFloat moment = <span style="color:#003668;">cpMomentForCircle</span>(mass, <span style="color:#0000ff;">0</span>, radius, cpvzero);<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;<em><span style="color:#236e25;">// The cpSpaceAdd*() functions return the thing that you are adding.<br />
</span></em>&nbsp;&nbsp;<em><span style="color:#236e25;">// It's convenient to create and add an object in one line.<br />
</span></em>&nbsp;&nbsp;cpBody *ballBody = <span style="color:#003668;">cpSpaceAddBody</span>(space, <span style="color:#003668;">cpBodyNew</span>(mass, moment));<br />
&nbsp;&nbsp;<span style="color:#003668;">cpBodySetPosition</span>(ballBody, <span style="color:#003668;">cpv</span>(<span style="color:#0000ff;">0</span>, <span style="color:#0000ff;">15</span>));<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;<em><span style="color:#236e25;">// Now we create the collision shape for the ball.<br />
</span></em>&nbsp;&nbsp;<em><span style="color:#236e25;">// You can create multiple collision shapes that point to the same body.<br />
</span></em>&nbsp;&nbsp;<em><span style="color:#236e25;">// They will all be attached to the body and move around to follow it.<br />
</span></em>&nbsp;&nbsp;cpShape *ballShape = <span style="color:#003668;">cpSpaceAddShape</span>(space, <span style="color:#003668;">cpCircleShapeNew</span>(ballBody, radius, cpvzero));<br />
&nbsp;&nbsp;<span style="color:#003668;">cpShapeSetFriction</span>(ballShape, <span style="color:#0000ff;">0.7</span>);<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;<em><span style="color:#236e25;">// Now that it's all set up, we simulate all the objects in the space by<br />
</span></em>&nbsp;&nbsp;<em><span style="color:#236e25;">// stepping forward through time in small increments called steps.<br />
</span></em>&nbsp;&nbsp;<em><span style="color:#236e25;">// It is *highly* recommended to use a fixed size time step.<br />
</span></em>&nbsp;&nbsp;cpFloat timeStep = <span style="color:#0000ff;">1.0</span>/<span style="color:#0000ff;">60.0</span>;<br />
&nbsp;&nbsp;<span style="color:#a71790;">for</span>(cpFloat time = <span style="color:#0000ff;">0</span>; time &lt; <span style="color:#0000ff;">2</span>; time += timeStep){<br />
&nbsp;&nbsp;&nbsp;&nbsp;cpVect pos = <span style="color:#003668;">cpBodyGetPosition</span>(ballBody);<br />
&nbsp;&nbsp;&nbsp;&nbsp;cpVect vel = <span style="color:#003668;">cpBodyGetVelocity</span>(ballBody);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#003668;">printf</span>(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#f4181b;">&quot;Time is %5.2f. ballBody is at (%5.2f, %5.2f). It's velocity is (%5.2f, %5.2f)\n&quot;</span>,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;time, pos.x, pos.y, vel.x, vel.y<br />
&nbsp;&nbsp;&nbsp;&nbsp;);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#003668;">cpSpaceStep</span>(space, timeStep);<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;<em><span style="color:#236e25;">// Clean up our objects and exit!<br />
</span></em>&nbsp;&nbsp;<span style="color:#003668;">cpShapeFree</span>(ballShape);<br />
&nbsp;&nbsp;<span style="color:#003668;">cpBodyFree</span>(ballBody);<br />
&nbsp;&nbsp;<span style="color:#003668;">cpShapeFree</span>(ground);<br />
&nbsp;&nbsp;<span style="color:#003668;">cpSpaceFree</span>(space);<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;<span style="color:#a71790;">return</span> <span style="color:#0000ff;">0</span>;<br />
}</div>

View File

@@ -0,0 +1,17 @@
<div style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;font-size:small; font-family:monospace; "><em><span style="color:#236e25;">// Faked top down friction.<br />
</span></em><br />
<em><span style="color:#236e25;">// A pivot joint configured this way will calculate friction against the ground for games with a top down perspective.<br />
// Because the joint correction is disabled, the joint will not recenter itself and only apply to the velocity.<br />
// The force the joint applies when changing the velocity will be clamped by the max force<br />
// and this causes it to work exactly like friction!<br />
</span></em>cpConstraint *pivot = <span style="color:#003668;">cpSpaceAddConstraint</span>(space, <span style="color:#003668;">cpPivotJointNew2</span>(staticBody, body, cpvzero, cpvzero));<br />
<span style="color:#003668;">cpConstraintSetMaxBias</span>(pivot, <span style="color:#0000ff;">0.0f</span>); <em><span style="color:#236e25;">// disable joint correction<br />
</span></em><span style="color:#003668;">cpConstraintSetMaxForce</span>(pivot, <span style="color:#0000ff;">1000.0f</span>);<br />
<br />
<em><span style="color:#236e25;">// The pivot joint doesn't apply rotational forces, use a gear joint with a ratio of 1.0 for that.<br />
</span></em>cpConstraint *gear = <span style="color:#003668;">cpSpaceAddConstraint</span>(space, <span style="color:#003668;">cpGearJointNew</span>(staticBody, body, <span style="color:#0000ff;">0.0f</span>, <span style="color:#0000ff;">1.0f</span>));<br />
<span style="color:#003668;">cpConstraintSetMaxBias</span>(gear, <span style="color:#0000ff;">0.0f</span>); <em><span style="color:#236e25;">// disable joint correction<br />
</span></em><span style="color:#003668;">cpConstraintSetMaxForce</span>(gear, <span style="color:#0000ff;">5000.0f</span>);<br />
<br />
<em><span style="color:#236e25;">// Also, instead of connecting the joints to a static body, you can connect them to an infinite mass rogue body.<br />
// You can then use the rogue body as a control body to the connected body. See the Tank demo as an example.</span></em></div>

View File

@@ -0,0 +1,14 @@
<pre style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;font-size:small; font-family:monospace; "><em><span style="color:#236e25;">// Moment for a solid circle with a mass of 2 and radius 5.
</span></em>cpFloat circle1 = <span style="color:#003369;">cpMomentForCircle</span>(<span style="color:#0000ff;">2</span>, <span style="color:#0000ff;">0</span>, <span style="color:#0000ff;">5</span>, cpvzero);
<em><span style="color:#236e25;">// Moment for a hollow circle with a mass of 1, inner radius of 2 and outer radius of 6.
</span></em>cpFloat circle2 = <span style="color:#003369;">cpMomentForCircle</span>(<span style="color:#0000ff;">1</span>, <span style="color:#0000ff;">2</span>, <span style="color:#0000ff;">6</span>, cpvzero);
<em><span style="color:#236e25;">// Moment for a solid circle with a mass of 1, radius of 3 and
// centered 3 units along the x axis from the center of gravity.
</span></em>cpFloat circle3 = <span style="color:#003369;">cpMomentForCircle</span>(<span style="color:#0000ff;">2</span>, <span style="color:#0000ff;">0</span>, <span style="color:#0000ff;">5</span>, <span style="color:#003369;">cpv</span>(<span style="color:#0000ff;">3</span>, <span style="color:#0000ff;">0</span>));
<em><span style="color:#236e25;">// Composite object. 1x4 box centered on the center of gravity and a circle sitting on top.
// Just add the moments together.
</span></em>cpFloat composite = <span style="color:#003369;">cpMomentForBox</span>(boxMass, <span style="color:#0000ff;">1</span>, <span style="color:#0000ff;">4</span>) + <span style="color:#003369;">cpMomentForCircle</span>(circleMass, <span style="color:#0000ff;">0</span>, <span style="color:#0000ff;">1</span>, <span style="color:#003369;">cpv</span>(<span style="color:#0000ff;">0</span>, <span style="color:#0000ff;">3</span>));
</pre>

View File

@@ -0,0 +1,15 @@
<div style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;font-size:small; font-family:monospace; "><br />
<span style="color:#236e25;">// Callback function<br />
</span><span style="color:#881350;">static</span> cpBool <span style="color:#003369;">PlaySoundOnImpact</span>(cpArbiter *arb, cpSpace *space, <span style="color:#881350;">void</span> *data){<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#003369;">PlayCrashSound</span>();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#881350;">return</span> cpTrue;<br />
}<br />
<br />
<span style="color:#236e25;">// When setting up, reference your callback function:<br />
</span>{<br />
&nbsp;&nbsp;&nbsp;&nbsp;...<br />
&nbsp;&nbsp;&nbsp;&nbsp;cpCollisionHandler *handler = <span style="color:#003369;">cpSpaceAddCollisionHandler</span>(space, PLAYER, WALL);<br />
&nbsp;&nbsp;&nbsp;&nbsp;handler-&gt;postSolveFunc = PlaySoundOnImpact;<br />
&nbsp;&nbsp;&nbsp;&nbsp;...<br />
} &nbsp;&nbsp;<br />
</div>

View File

@@ -0,0 +1,25 @@
<pre style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;font-size:small; font-family:monospace; "><em><span style="color:#236e25;">// Construct a pile of boxes.
// Force them to sleep until the first time they are touched.
// Group them together so that touching any box wakes all of them.
</span></em>cpFloat size = <span style="color:#0000ff;">20</span>;
cpFloat mass = <span style="color:#0000ff;">1</span>;
cpFloat moment = <span style="color:#003369;">cpMomentForBox</span>(mass, size, size);
cpBody *lastBody = <strong><span style="color:#881350;">NULL</span></strong>;
<strong><span style="color:#881350;">for</span></strong>(<strong><span style="color:#881350;">int</span></strong> i=<span style="color:#0000ff;">0</span>; i&lt;<span style="color:#0000ff;">5</span>; i++){
cpBody *body = <span style="color:#003369;">cpSpaceAddBody</span>(space, <span style="color:#003369;">cpBodyNew</span>(mass, moment));
<span style="color:#003369;">cpBodySetPos</span>(body, <span style="color:#003369;">cpv</span>(<span style="color:#0000ff;">0</span>, i*size));
cpShape *shape = <span style="color:#003369;">cpSpaceAddShape</span>(space, <span style="color:#003369;">cpBoxShapeNew</span>(body, size, size));
<span style="color:#003369;">cpShapeSetFriction</span>(shape, <span style="color:#0000ff;">0.7</span>);
<em><span style="color:#236e25;">// You can use any sleeping body as a group identifier.
</span></em> <em><span style="color:#236e25;">// Here we just keep a reference to the last body we initialized.
</span></em> <em><span style="color:#236e25;">// Passing NULL as the group starts a new sleeping group.
</span></em> <em><span style="color:#236e25;">// You MUST do this after completely initializing the object.
</span></em> <em><span style="color:#236e25;">// Attaching shapes or calling setter functions will wake the body back up.
</span></em> <span style="color:#003369;">cpBodySleepWithGroup</span>(body, lastBody);
lastBody = body;
}
</pre>

View File

@@ -0,0 +1,25 @@
<pre style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;font-size:small; font-family:monospace; "><strong><span style="color:#881350;">int</span></strong> first = <span style="color:#0000ff;">0</span>;
<em><span style="color:#236e25;">// Create space to store the convex hull.
// An alloca(), or a variable length array would be a better, but not always portable choice.
</span></em>cpVect *hullVerts = (cpVect *)<span style="color:#003369;">calloc</span>(vertCount, <strong><span style="color:#881350;">sizeof</span></strong>(cpVect));
<strong><span style="color:#881350;">int</span></strong> hullCount = <span style="color:#003369;">cpConvexHull</span>(vertCount, verts, hullVerts, &amp;first, <span style="color:#0000ff;">0.0</span>);
<em><span style="color:#236e25;">// hullVerts[0] will be equal to verts[first] here.
// If you don't care, pass NULL instead of the 'first' pointer.
</span></em>
cpBody *body = <span style="color:#003369;">cpBodyNew</span>(mass, <span style="color:#003369;">cpMomentForPoly</span>(mass, hullCount, hullVerts, cpvzero));
cpShape *shape = <span style="color:#003369;">cpPolyShapeNew</span>(body, hullCount, hullVerts, cpvzero);
<span style="color:#003369;">free</span>(hullVerts);
<em><span style="color:#236e25;">// *********
// Altenatively you can use the CP_CONVEX_HULL() macro to save yourself a little work
</span></em>
<em><span style="color:#236e25;">// The macro will declare the hullCount and hullVerts variables.
// hullVerts is allocated on the stack and does not need to be freed.
</span></em><span style="color:#003369;">CP_CONVEX_HULL</span>(count, verts, hullCount, hullVerts)
cpBody *body = <span style="color:#003369;">cpBodyNew</span>(mass, <span style="color:#003369;">cpMomentForPoly</span>(mass, hullCount, hullVerts, cpvzero));
cpShape *shape = <span style="color:#003369;">cpPolyShapeNew</span>(body, hullCount, hullVerts, cpvzero);
</pre>

View File

@@ -0,0 +1,12 @@
<pre style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;font-size:small; font-family:monospace; "><em><span style="color:#236e25;">// Code snippet to check if all bodies in the space are sleeping
</span></em>
<em><span style="color:#236e25;">// This function is called once for each body in the space.
</span></em><strong><span style="color:#881350;">static</span></strong> <strong><span style="color:#881350;">void</span></strong> <span style="color:#003369;">EachBody</span>(cpBody *body, cpBool *allSleeping){
<strong><span style="color:#881350;">if</span></strong>(!<span style="color:#003369;">cpBodyIsSleeping</span>(body)) *allSleeping = cpFalse;
}
<em><span style="color:#236e25;">// Then in your tick method, do this:
</span></em>cpBool allSleeping = true;
<span style="color:#003369;">cpSpaceEachBody</span>(space, (cpSpaceBodyIteratorFunc)EachBody, &amp;allSleeping);
<span style="color:#003369;">printf</span>(<span style="color:#760f15;">&quot;All are sleeping: %s\n&quot;</span>, allSleeping ? <span style="color:#760f15;">&quot;true&quot;</span> : <span style="color:#760f15;">&quot;false&quot;</span>);
</pre>

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@@ -0,0 +1,72 @@
h1, h2 {
padding: 0.25em;
border-radius: 0.25em;
}
h1 {
background-color: #89b4cb;
}
h2 {
background-color: lightGrey;
}
p {
margin-left: 1em;
}
p.expl {
margin-left: 2em;
}
code {
color: #191970
}
.HideShow {
background-color: lightGrey;
padding: 0.5em;
border-radius: 1em;
border: 2px grey solid;
}
.PopOpen {
border-radius: 1em;
border: 1px grey solid;
}
pre {
border-radius: 0.75em;
background-color: #F0F0F0;
padding: 0.5em;
margin-left: 1em;
}
/*ul {
border-radius: 0.75em;
background-color: #F0F0F0;
margin-left: 1em;
}*/
table {
border: 2px solid black;
border-collapse: collapse;
margin-left: 1em;
}
table td, th {
border: 1px black solid;
padding: 0.5em;
}
h1 a:link, h2 a:link, h3 a:link, h1 a:visited, h2 a:visited, h3 a:visited {
text-decoration:none;
color:black;
background-color:transparent
}
h1 a:hover, h2 a:hover, h3 a:hover, h1 a:active, h2 a:active, h3 a:active {
text-decoration:underline;
color:black;
background-color:transparent
}

View File

@@ -0,0 +1,234 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef CHIPMUNK_H
#define CHIPMUNK_H
#include <stdlib.h>
#include <math.h>
#ifndef alloca
#ifdef _WIN32
#include <malloc.h>
#elif defined(__FreeBSD__)
/* already included in <stdlib.h> */
#else
#include <alloca.h>
#endif
#endif
#ifdef _WIN32
#define CP_EXPORT __declspec(dllexport)
#else
#define CP_EXPORT
#endif
#ifdef __cplusplus
extern "C" {
#endif
CP_EXPORT void cpMessage(const char *condition, const char *file, int line, int isError, int isHardError, const char *message, ...);
#ifdef NDEBUG
#define cpAssertWarn(__condition__, ...)
#define cpAssertSoft(__condition__, ...)
#else
#define cpAssertSoft(__condition__, ...) if(!(__condition__)){cpMessage(#__condition__, __FILE__, __LINE__, 1, 0, __VA_ARGS__); abort();}
#define cpAssertWarn(__condition__, ...) if(!(__condition__)) cpMessage(#__condition__, __FILE__, __LINE__, 0, 0, __VA_ARGS__)
#endif
// Hard assertions are used in situations where the program definitely will crash anyway, and the reason is inexpensive to detect.
#define cpAssertHard(__condition__, ...) if(!(__condition__)){cpMessage(#__condition__, __FILE__, __LINE__, 1, 1, __VA_ARGS__); abort();}
#include "chipmunk_types.h"
/// @defgroup misc Misc
/// @{
/// Allocated size for various Chipmunk buffers
#ifndef CP_BUFFER_BYTES
#define CP_BUFFER_BYTES (32*1024)
#endif
#ifndef cpcalloc
/// Chipmunk calloc() alias.
#define cpcalloc calloc
#endif
#ifndef cprealloc
/// Chipmunk realloc() alias.
#define cprealloc realloc
#endif
#ifndef cpfree
/// Chipmunk free() alias.
#define cpfree free
#endif
typedef struct cpArray cpArray;
typedef struct cpHashSet cpHashSet;
typedef struct cpBody cpBody;
typedef struct cpShape cpShape;
typedef struct cpCircleShape cpCircleShape;
typedef struct cpSegmentShape cpSegmentShape;
typedef struct cpPolyShape cpPolyShape;
typedef struct cpConstraint cpConstraint;
typedef struct cpPinJoint cpPinJoint;
typedef struct cpSlideJoint cpSlideJoint;
typedef struct cpPivotJoint cpPivotJoint;
typedef struct cpGrooveJoint cpGrooveJoint;
typedef struct cpDampedSpring cpDampedSpring;
typedef struct cpDampedRotarySpring cpDampedRotarySpring;
typedef struct cpRotaryLimitJoint cpRotaryLimitJoint;
typedef struct cpRatchetJoint cpRatchetJoint;
typedef struct cpGearJoint cpGearJoint;
typedef struct cpSimpleMotorJoint cpSimpleMotorJoint;
typedef struct cpCollisionHandler cpCollisionHandler;
typedef struct cpContactPointSet cpContactPointSet;
typedef struct cpArbiter cpArbiter;
typedef struct cpSpace cpSpace;
#include "cpVect.h"
#include "cpBB.h"
#include "cpTransform.h"
#include "cpSpatialIndex.h"
#include "cpArbiter.h"
#include "cpBody.h"
#include "cpShape.h"
#include "cpPolyShape.h"
#include "cpConstraint.h"
#include "cpSpace.h"
// Chipmunk 7.0.3
#define CP_VERSION_MAJOR 7
#define CP_VERSION_MINOR 0
#define CP_VERSION_RELEASE 3
/// Version string.
CP_EXPORT extern const char *cpVersionString;
/// Calculate the moment of inertia for a circle.
/// @c r1 and @c r2 are the inner and outer diameters. A solid circle has an inner diameter of 0.
CP_EXPORT cpFloat cpMomentForCircle(cpFloat m, cpFloat r1, cpFloat r2, cpVect offset);
/// Calculate area of a hollow circle.
/// @c r1 and @c r2 are the inner and outer diameters. A solid circle has an inner diameter of 0.
CP_EXPORT cpFloat cpAreaForCircle(cpFloat r1, cpFloat r2);
/// Calculate the moment of inertia for a line segment.
/// Beveling radius is not supported.
CP_EXPORT cpFloat cpMomentForSegment(cpFloat m, cpVect a, cpVect b, cpFloat radius);
/// Calculate the area of a fattened (capsule shaped) line segment.
CP_EXPORT cpFloat cpAreaForSegment(cpVect a, cpVect b, cpFloat radius);
/// Calculate the moment of inertia for a solid polygon shape assuming it's center of gravity is at it's centroid. The offset is added to each vertex.
CP_EXPORT cpFloat cpMomentForPoly(cpFloat m, int count, const cpVect *verts, cpVect offset, cpFloat radius);
/// Calculate the signed area of a polygon. A Clockwise winding gives positive area.
/// This is probably backwards from what you expect, but matches Chipmunk's the winding for poly shapes.
CP_EXPORT cpFloat cpAreaForPoly(const int count, const cpVect *verts, cpFloat radius);
/// Calculate the natural centroid of a polygon.
CP_EXPORT cpVect cpCentroidForPoly(const int count, const cpVect *verts);
/// Calculate the moment of inertia for a solid box.
CP_EXPORT cpFloat cpMomentForBox(cpFloat m, cpFloat width, cpFloat height);
/// Calculate the moment of inertia for a solid box.
CP_EXPORT cpFloat cpMomentForBox2(cpFloat m, cpBB box);
/// Calculate the convex hull of a given set of points. Returns the count of points in the hull.
/// @c result must be a pointer to a @c cpVect array with at least @c count elements. If @c verts == @c result, then @c verts will be reduced inplace.
/// @c first is an optional pointer to an integer to store where the first vertex in the hull came from (i.e. verts[first] == result[0])
/// @c tol is the allowed amount to shrink the hull when simplifying it. A tolerance of 0.0 creates an exact hull.
CP_EXPORT int cpConvexHull(int count, const cpVect *verts, cpVect *result, int *first, cpFloat tol);
/// Convenience macro to work with cpConvexHull.
/// @c count and @c verts is the input array passed to cpConvexHull().
/// @c count_var and @c verts_var are the names of the variables the macro creates to store the result.
/// The output vertex array is allocated on the stack using alloca() so it will be freed automatically, but cannot be returned from the current scope.
#define CP_CONVEX_HULL(__count__, __verts__, __count_var__, __verts_var__) \
cpVect *__verts_var__ = (cpVect *)alloca(__count__*sizeof(cpVect)); \
int __count_var__ = cpConvexHull(__count__, __verts__, __verts_var__, NULL, 0.0); \
/// Returns the closest point on the line segment ab, to the point p.
static inline cpVect
cpClosetPointOnSegment(const cpVect p, const cpVect a, const cpVect b)
{
cpVect delta = cpvsub(a, b);
cpFloat t = cpfclamp01(cpvdot(delta, cpvsub(p, b))/cpvlengthsq(delta));
return cpvadd(b, cpvmult(delta, t));
}
#if defined(__has_extension)
#if __has_extension(blocks)
// Define alternate block based alternatives for a few of the callback heavy functions.
// Collision handlers are post-step callbacks are not included to avoid memory management issues.
// If you want to use blocks for those and are aware of how to correctly manage the memory, the implementation is trivial.
void cpSpaceEachBody_b(cpSpace *space, void (^block)(cpBody *body));
void cpSpaceEachShape_b(cpSpace *space, void (^block)(cpShape *shape));
void cpSpaceEachConstraint_b(cpSpace *space, void (^block)(cpConstraint *constraint));
void cpBodyEachShape_b(cpBody *body, void (^block)(cpShape *shape));
void cpBodyEachConstraint_b(cpBody *body, void (^block)(cpConstraint *constraint));
void cpBodyEachArbiter_b(cpBody *body, void (^block)(cpArbiter *arbiter));
typedef void (^cpSpacePointQueryBlock)(cpShape *shape, cpVect point, cpFloat distance, cpVect gradient);
void cpSpacePointQuery_b(cpSpace *space, cpVect point, cpFloat maxDistance, cpShapeFilter filter, cpSpacePointQueryBlock block);
typedef void (^cpSpaceSegmentQueryBlock)(cpShape *shape, cpVect point, cpVect normal, cpFloat alpha);
void cpSpaceSegmentQuery_b(cpSpace *space, cpVect start, cpVect end, cpFloat radius, cpShapeFilter filter, cpSpaceSegmentQueryBlock block);
typedef void (^cpSpaceBBQueryBlock)(cpShape *shape);
void cpSpaceBBQuery_b(cpSpace *space, cpBB bb, cpShapeFilter filter, cpSpaceBBQueryBlock block);
typedef void (^cpSpaceShapeQueryBlock)(cpShape *shape, cpContactPointSet *points);
cpBool cpSpaceShapeQuery_b(cpSpace *space, cpShape *shape, cpSpaceShapeQueryBlock block);
#endif
#endif
//@}
#ifdef __cplusplus
}
/*
static inline cpVect operator *(const cpVect v, const cpFloat s){return cpvmult(v, s);}
static inline cpVect operator +(const cpVect v1, const cpVect v2){return cpvadd(v1, v2);}
static inline cpVect operator -(const cpVect v1, const cpVect v2){return cpvsub(v1, v2);}
static inline cpBool operator ==(const cpVect v1, const cpVect v2){return cpveql(v1, v2);}
static inline cpVect operator -(const cpVect v){return cpvneg(v);}
*/
#endif
#endif

View File

@@ -0,0 +1,105 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifdef CHIPMUNK_FFI
// Create non static inlined copies of Chipmunk functions, useful for working with dynamic FFIs
// For many languages, it may be faster to reimplement these functions natively instead.
// Note: This file should only be included by chipmunk.c.
#ifdef _MSC_VER
#if _MSC_VER >= 1600
#define MAKE_REF(name) CP_EXPORT decltype(name) *_##name = name
#else
#define MAKE_REF(name)
#endif
#else
#define MAKE_REF(name) __typeof__(name) *_##name = name
#endif
#ifdef __cplusplus
extern "C" {
#endif
MAKE_REF(cpv); // makes a variable named _cpv that contains the function pointer for cpv()
MAKE_REF(cpveql);
MAKE_REF(cpvadd);
MAKE_REF(cpvneg);
MAKE_REF(cpvsub);
MAKE_REF(cpvmult);
MAKE_REF(cpvdot);
MAKE_REF(cpvcross);
MAKE_REF(cpvperp);
MAKE_REF(cpvrperp);
MAKE_REF(cpvproject);
MAKE_REF(cpvforangle);
MAKE_REF(cpvtoangle);
MAKE_REF(cpvrotate);
MAKE_REF(cpvunrotate);
MAKE_REF(cpvlengthsq);
MAKE_REF(cpvlength);
MAKE_REF(cpvlerp);
MAKE_REF(cpvnormalize);
MAKE_REF(cpvclamp);
MAKE_REF(cpvlerpconst);
MAKE_REF(cpvdist);
MAKE_REF(cpvdistsq);
MAKE_REF(cpvnear);
MAKE_REF(cpfmax);
MAKE_REF(cpfmin);
MAKE_REF(cpfabs);
MAKE_REF(cpfclamp);
MAKE_REF(cpflerp);
MAKE_REF(cpflerpconst);
MAKE_REF(cpBBNew);
MAKE_REF(cpBBNewForExtents);
MAKE_REF(cpBBNewForCircle);
MAKE_REF(cpBBIntersects);
MAKE_REF(cpBBContainsBB);
MAKE_REF(cpBBContainsVect);
MAKE_REF(cpBBMerge);
MAKE_REF(cpBBExpand);
MAKE_REF(cpBBCenter);
MAKE_REF(cpBBArea);
MAKE_REF(cpBBMergedArea);
MAKE_REF(cpBBSegmentQuery);
MAKE_REF(cpBBIntersectsSegment);
MAKE_REF(cpBBClampVect);
MAKE_REF(cpSpatialIndexDestroy);
MAKE_REF(cpSpatialIndexCount);
MAKE_REF(cpSpatialIndexEach);
MAKE_REF(cpSpatialIndexContains);
MAKE_REF(cpSpatialIndexInsert);
MAKE_REF(cpSpatialIndexRemove);
MAKE_REF(cpSpatialIndexReindex);
MAKE_REF(cpSpatialIndexReindexObject);
MAKE_REF(cpSpatialIndexSegmentQuery);
MAKE_REF(cpSpatialIndexQuery);
MAKE_REF(cpSpatialIndexReindexQuery);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,344 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef CHIPMUNK_PRIVATE_H
#define CHIPMUNK_PRIVATE_H
#include "chipmunk/chipmunk.h"
#include "chipmunk/chipmunk_structs.h"
#define CP_HASH_COEF (3344921057ul)
#define CP_HASH_PAIR(A, B) ((cpHashValue)(A)*CP_HASH_COEF ^ (cpHashValue)(B)*CP_HASH_COEF)
// TODO: Eww. Magic numbers.
#define MAGIC_EPSILON 1e-5
//MARK: cpArray
cpArray *cpArrayNew(int size);
void cpArrayFree(cpArray *arr);
void cpArrayPush(cpArray *arr, void *object);
void *cpArrayPop(cpArray *arr);
void cpArrayDeleteObj(cpArray *arr, void *obj);
cpBool cpArrayContains(cpArray *arr, void *ptr);
void cpArrayFreeEach(cpArray *arr, void (freeFunc)(void*));
//MARK: cpHashSet
typedef cpBool (*cpHashSetEqlFunc)(const void *ptr, const void *elt);
typedef void *(*cpHashSetTransFunc)(const void *ptr, void *data);
cpHashSet *cpHashSetNew(int size, cpHashSetEqlFunc eqlFunc);
void cpHashSetSetDefaultValue(cpHashSet *set, void *default_value);
void cpHashSetFree(cpHashSet *set);
int cpHashSetCount(cpHashSet *set);
const void *cpHashSetInsert(cpHashSet *set, cpHashValue hash, const void *ptr, cpHashSetTransFunc trans, void *data);
const void *cpHashSetRemove(cpHashSet *set, cpHashValue hash, const void *ptr);
const void *cpHashSetFind(cpHashSet *set, cpHashValue hash, const void *ptr);
typedef void (*cpHashSetIteratorFunc)(void *elt, void *data);
void cpHashSetEach(cpHashSet *set, cpHashSetIteratorFunc func, void *data);
typedef cpBool (*cpHashSetFilterFunc)(void *elt, void *data);
void cpHashSetFilter(cpHashSet *set, cpHashSetFilterFunc func, void *data);
//MARK: Bodies
void cpBodyAddShape(cpBody *body, cpShape *shape);
void cpBodyRemoveShape(cpBody *body, cpShape *shape);
//void cpBodyAccumulateMassForShape(cpBody *body, cpShape *shape);
void cpBodyAccumulateMassFromShapes(cpBody *body);
void cpBodyRemoveConstraint(cpBody *body, cpConstraint *constraint);
//MARK: Spatial Index Functions
cpSpatialIndex *cpSpatialIndexInit(cpSpatialIndex *index, cpSpatialIndexClass *klass, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex);
//MARK: Arbiters
cpArbiter* cpArbiterInit(cpArbiter *arb, cpShape *a, cpShape *b);
static inline struct cpArbiterThread *
cpArbiterThreadForBody(cpArbiter *arb, cpBody *body)
{
return (arb->body_a == body ? &arb->thread_a : &arb->thread_b);
}
void cpArbiterUnthread(cpArbiter *arb);
void cpArbiterUpdate(cpArbiter *arb, struct cpCollisionInfo *info, cpSpace *space);
void cpArbiterPreStep(cpArbiter *arb, cpFloat dt, cpFloat bias, cpFloat slop);
void cpArbiterApplyCachedImpulse(cpArbiter *arb, cpFloat dt_coef);
void cpArbiterApplyImpulse(cpArbiter *arb);
//MARK: Shapes/Collisions
cpShape *cpShapeInit(cpShape *shape, const cpShapeClass *klass, cpBody *body, struct cpShapeMassInfo massInfo);
static inline cpBool
cpShapeActive(cpShape *shape)
{
// checks if the shape is added to a shape list.
// TODO could this just check the space now?
return (shape->prev || (shape->body && shape->body->shapeList == shape));
}
// Note: This function returns contact points with r1/r2 in absolute coordinates, not body relative.
struct cpCollisionInfo cpCollide(const cpShape *a, const cpShape *b, cpCollisionID id, struct cpContact *contacts);
static inline void
CircleSegmentQuery(cpShape *shape, cpVect center, cpFloat r1, cpVect a, cpVect b, cpFloat r2, cpSegmentQueryInfo *info)
{
cpVect da = cpvsub(a, center);
cpVect db = cpvsub(b, center);
cpFloat rsum = r1 + r2;
cpFloat qa = cpvdot(da, da) - 2.0f*cpvdot(da, db) + cpvdot(db, db);
cpFloat qb = cpvdot(da, db) - cpvdot(da, da);
cpFloat det = qb*qb - qa*(cpvdot(da, da) - rsum*rsum);
if(det >= 0.0f){
cpFloat t = (-qb - cpfsqrt(det))/(qa);
if(0.0f<= t && t <= 1.0f){
cpVect n = cpvnormalize(cpvlerp(da, db, t));
info->shape = shape;
info->point = cpvsub(cpvlerp(a, b, t), cpvmult(n, r2));
info->normal = n;
info->alpha = t;
}
}
}
static inline cpBool
cpShapeFilterReject(cpShapeFilter a, cpShapeFilter b)
{
// Reject the collision if:
return (
// They are in the same non-zero group.
(a.group != 0 && a.group == b.group) ||
// One of the category/mask combinations fails.
(a.categories & b.mask) == 0 ||
(b.categories & a.mask) == 0
);
}
void cpLoopIndexes(const cpVect *verts, int count, int *start, int *end);
//MARK: Constraints
// TODO naming conventions here
void cpConstraintInit(cpConstraint *constraint, const struct cpConstraintClass *klass, cpBody *a, cpBody *b);
static inline void
cpConstraintActivateBodies(cpConstraint *constraint)
{
cpBody *a = constraint->a; cpBodyActivate(a);
cpBody *b = constraint->b; cpBodyActivate(b);
}
static inline cpVect
relative_velocity(cpBody *a, cpBody *b, cpVect r1, cpVect r2){
cpVect v1_sum = cpvadd(a->v, cpvmult(cpvperp(r1), a->w));
cpVect v2_sum = cpvadd(b->v, cpvmult(cpvperp(r2), b->w));
return cpvsub(v2_sum, v1_sum);
}
static inline cpFloat
normal_relative_velocity(cpBody *a, cpBody *b, cpVect r1, cpVect r2, cpVect n){
return cpvdot(relative_velocity(a, b, r1, r2), n);
}
static inline void
apply_impulse(cpBody *body, cpVect j, cpVect r){
body->v = cpvadd(body->v, cpvmult(j, body->m_inv));
body->w += body->i_inv*cpvcross(r, j);
}
static inline void
apply_impulses(cpBody *a , cpBody *b, cpVect r1, cpVect r2, cpVect j)
{
apply_impulse(a, cpvneg(j), r1);
apply_impulse(b, j, r2);
}
static inline void
apply_bias_impulse(cpBody *body, cpVect j, cpVect r)
{
body->v_bias = cpvadd(body->v_bias, cpvmult(j, body->m_inv));
body->w_bias += body->i_inv*cpvcross(r, j);
}
static inline void
apply_bias_impulses(cpBody *a , cpBody *b, cpVect r1, cpVect r2, cpVect j)
{
apply_bias_impulse(a, cpvneg(j), r1);
apply_bias_impulse(b, j, r2);
}
static inline cpFloat
k_scalar_body(cpBody *body, cpVect r, cpVect n)
{
cpFloat rcn = cpvcross(r, n);
return body->m_inv + body->i_inv*rcn*rcn;
}
static inline cpFloat
k_scalar(cpBody *a, cpBody *b, cpVect r1, cpVect r2, cpVect n)
{
cpFloat value = k_scalar_body(a, r1, n) + k_scalar_body(b, r2, n);
cpAssertSoft(value != 0.0, "Unsolvable collision or constraint.");
return value;
}
static inline cpMat2x2
k_tensor(cpBody *a, cpBody *b, cpVect r1, cpVect r2)
{
cpFloat m_sum = a->m_inv + b->m_inv;
// start with Identity*m_sum
cpFloat k11 = m_sum, k12 = 0.0f;
cpFloat k21 = 0.0f, k22 = m_sum;
// add the influence from r1
cpFloat a_i_inv = a->i_inv;
cpFloat r1xsq = r1.x * r1.x * a_i_inv;
cpFloat r1ysq = r1.y * r1.y * a_i_inv;
cpFloat r1nxy = -r1.x * r1.y * a_i_inv;
k11 += r1ysq; k12 += r1nxy;
k21 += r1nxy; k22 += r1xsq;
// add the influnce from r2
cpFloat b_i_inv = b->i_inv;
cpFloat r2xsq = r2.x * r2.x * b_i_inv;
cpFloat r2ysq = r2.y * r2.y * b_i_inv;
cpFloat r2nxy = -r2.x * r2.y * b_i_inv;
k11 += r2ysq; k12 += r2nxy;
k21 += r2nxy; k22 += r2xsq;
// invert
cpFloat det = k11*k22 - k12*k21;
cpAssertSoft(det != 0.0, "Unsolvable constraint.");
cpFloat det_inv = 1.0f/det;
return cpMat2x2New(
k22*det_inv, -k12*det_inv,
-k21*det_inv, k11*det_inv
);
}
static inline cpFloat
bias_coef(cpFloat errorBias, cpFloat dt)
{
return 1.0f - cpfpow(errorBias, dt);
}
//MARK: Spaces
#define cpAssertSpaceUnlocked(space) \
cpAssertHard(!space->locked, \
"This operation cannot be done safely during a call to cpSpaceStep() or during a query. " \
"Put these calls into a post-step callback." \
);
void cpSpaceSetStaticBody(cpSpace *space, cpBody *body);
extern cpCollisionHandler cpCollisionHandlerDoNothing;
void cpSpaceProcessComponents(cpSpace *space, cpFloat dt);
void cpSpacePushFreshContactBuffer(cpSpace *space);
struct cpContact *cpContactBufferGetArray(cpSpace *space);
void cpSpacePushContacts(cpSpace *space, int count);
cpPostStepCallback *cpSpaceGetPostStepCallback(cpSpace *space, void *key);
cpBool cpSpaceArbiterSetFilter(cpArbiter *arb, cpSpace *space);
void cpSpaceFilterArbiters(cpSpace *space, cpBody *body, cpShape *filter);
void cpSpaceActivateBody(cpSpace *space, cpBody *body);
void cpSpaceLock(cpSpace *space);
void cpSpaceUnlock(cpSpace *space, cpBool runPostStep);
static inline void
cpSpaceUncacheArbiter(cpSpace *space, cpArbiter *arb)
{
const cpShape *a = arb->a, *b = arb->b;
const cpShape *shape_pair[] = {a, b};
cpHashValue arbHashID = CP_HASH_PAIR((cpHashValue)a, (cpHashValue)b);
cpHashSetRemove(space->cachedArbiters, arbHashID, shape_pair);
cpArrayDeleteObj(space->arbiters, arb);
}
static inline cpArray *
cpSpaceArrayForBodyType(cpSpace *space, cpBodyType type)
{
return (type == CP_BODY_TYPE_STATIC ? space->staticBodies : space->dynamicBodies);
}
void cpShapeUpdateFunc(cpShape *shape, void *unused);
cpCollisionID cpSpaceCollideShapes(cpShape *a, cpShape *b, cpCollisionID id, cpSpace *space);
//MARK: Foreach loops
static inline cpConstraint *
cpConstraintNext(cpConstraint *node, cpBody *body)
{
return (node->a == body ? node->next_a : node->next_b);
}
#define CP_BODY_FOREACH_CONSTRAINT(bdy, var)\
for(cpConstraint *var = bdy->constraintList; var; var = cpConstraintNext(var, bdy))
static inline cpArbiter *
cpArbiterNext(cpArbiter *node, cpBody *body)
{
return (node->body_a == body ? node->thread_a.next : node->thread_b.next);
}
#define CP_BODY_FOREACH_ARBITER(bdy, var)\
for(cpArbiter *var = bdy->arbiterList; var; var = cpArbiterNext(var, bdy))
#define CP_BODY_FOREACH_SHAPE(body, var)\
for(cpShape *var = body->shapeList; var; var = var->next)
#define CP_BODY_FOREACH_COMPONENT(root, var)\
for(cpBody *var = root; var; var = var->sleeping.next)
#endif

View File

@@ -0,0 +1,450 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// All of the struct definitions for Chipmunk should be considered part of the private API.
// However, it is very valuable to know the struct sizes for preallocating memory.
#ifndef CHIPMUNK_STRUCTS_H
#define CHIPMUNK_STRUCTS_H
#include "chipmunk/chipmunk.h"
struct cpArray {
int num, max;
void **arr;
};
struct cpBody {
// Integration functions
cpBodyVelocityFunc velocity_func;
cpBodyPositionFunc position_func;
// mass and it's inverse
cpFloat m;
cpFloat m_inv;
// moment of inertia and it's inverse
cpFloat i;
cpFloat i_inv;
// center of gravity
cpVect cog;
// position, velocity, force
cpVect p;
cpVect v;
cpVect f;
// Angle, angular velocity, torque (radians)
cpFloat a;
cpFloat w;
cpFloat t;
cpTransform transform;
cpDataPointer userData;
// "pseudo-velocities" used for eliminating overlap.
// Erin Catto has some papers that talk about what these are.
cpVect v_bias;
cpFloat w_bias;
cpSpace *space;
cpShape *shapeList;
cpArbiter *arbiterList;
cpConstraint *constraintList;
struct {
cpBody *root;
cpBody *next;
cpFloat idleTime;
} sleeping;
};
enum cpArbiterState {
// Arbiter is active and its the first collision.
CP_ARBITER_STATE_FIRST_COLLISION,
// Arbiter is active and its not the first collision.
CP_ARBITER_STATE_NORMAL,
// Collision has been explicitly ignored.
// Either by returning false from a begin collision handler or calling cpArbiterIgnore().
CP_ARBITER_STATE_IGNORE,
// Collison is no longer active. A space will cache an arbiter for up to cpSpace.collisionPersistence more steps.
CP_ARBITER_STATE_CACHED,
// Collison arbiter is invalid because one of the shapes was removed.
CP_ARBITER_STATE_INVALIDATED,
};
struct cpArbiterThread {
struct cpArbiter *next, *prev;
};
struct cpContact {
cpVect r1, r2;
cpFloat nMass, tMass;
cpFloat bounce; // TODO: look for an alternate bounce solution.
cpFloat jnAcc, jtAcc, jBias;
cpFloat bias;
cpHashValue hash;
};
struct cpCollisionInfo {
const cpShape *a, *b;
cpCollisionID id;
cpVect n;
int count;
// TODO Should this be a unique struct type?
struct cpContact *arr;
};
struct cpArbiter {
cpFloat e;
cpFloat u;
cpVect surface_vr;
cpDataPointer data;
const cpShape *a, *b;
cpBody *body_a, *body_b;
struct cpArbiterThread thread_a, thread_b;
int count;
struct cpContact *contacts;
cpVect n;
// Regular, wildcard A and wildcard B collision handlers.
cpCollisionHandler *handler, *handlerA, *handlerB;
cpBool swapped;
cpTimestamp stamp;
enum cpArbiterState state;
};
struct cpShapeMassInfo {
cpFloat m;
cpFloat i;
cpVect cog;
cpFloat area;
};
typedef enum cpShapeType{
CP_CIRCLE_SHAPE,
CP_SEGMENT_SHAPE,
CP_POLY_SHAPE,
CP_NUM_SHAPES
} cpShapeType;
typedef cpBB (*cpShapeCacheDataImpl)(cpShape *shape, cpTransform transform);
typedef void (*cpShapeDestroyImpl)(cpShape *shape);
typedef void (*cpShapePointQueryImpl)(const cpShape *shape, cpVect p, cpPointQueryInfo *info);
typedef void (*cpShapeSegmentQueryImpl)(const cpShape *shape, cpVect a, cpVect b, cpFloat radius, cpSegmentQueryInfo *info);
typedef struct cpShapeClass cpShapeClass;
struct cpShapeClass {
cpShapeType type;
cpShapeCacheDataImpl cacheData;
cpShapeDestroyImpl destroy;
cpShapePointQueryImpl pointQuery;
cpShapeSegmentQueryImpl segmentQuery;
};
struct cpShape {
const cpShapeClass *klass;
cpSpace *space;
cpBody *body;
struct cpShapeMassInfo massInfo;
cpBB bb;
cpBool sensor;
cpFloat e;
cpFloat u;
cpVect surfaceV;
cpDataPointer userData;
cpCollisionType type;
cpShapeFilter filter;
cpShape *next;
cpShape *prev;
cpHashValue hashid;
};
struct cpCircleShape {
cpShape shape;
cpVect c, tc;
cpFloat r;
};
struct cpSegmentShape {
cpShape shape;
cpVect a, b, n;
cpVect ta, tb, tn;
cpFloat r;
cpVect a_tangent, b_tangent;
};
struct cpSplittingPlane {
cpVect v0, n;
};
#define CP_POLY_SHAPE_INLINE_ALLOC 6
struct cpPolyShape {
cpShape shape;
cpFloat r;
int count;
// The untransformed planes are appended at the end of the transformed planes.
struct cpSplittingPlane *planes;
// Allocate a small number of splitting planes internally for simple poly.
struct cpSplittingPlane _planes[2*CP_POLY_SHAPE_INLINE_ALLOC];
};
typedef void (*cpConstraintPreStepImpl)(cpConstraint *constraint, cpFloat dt);
typedef void (*cpConstraintApplyCachedImpulseImpl)(cpConstraint *constraint, cpFloat dt_coef);
typedef void (*cpConstraintApplyImpulseImpl)(cpConstraint *constraint, cpFloat dt);
typedef cpFloat (*cpConstraintGetImpulseImpl)(cpConstraint *constraint);
typedef struct cpConstraintClass {
cpConstraintPreStepImpl preStep;
cpConstraintApplyCachedImpulseImpl applyCachedImpulse;
cpConstraintApplyImpulseImpl applyImpulse;
cpConstraintGetImpulseImpl getImpulse;
} cpConstraintClass;
struct cpConstraint {
const cpConstraintClass *klass;
cpSpace *space;
cpBody *a, *b;
cpConstraint *next_a, *next_b;
cpFloat maxForce;
cpFloat errorBias;
cpFloat maxBias;
cpBool collideBodies;
cpConstraintPreSolveFunc preSolve;
cpConstraintPostSolveFunc postSolve;
cpDataPointer userData;
};
struct cpPinJoint {
cpConstraint constraint;
cpVect anchorA, anchorB;
cpFloat dist;
cpVect r1, r2;
cpVect n;
cpFloat nMass;
cpFloat jnAcc;
cpFloat bias;
};
struct cpSlideJoint {
cpConstraint constraint;
cpVect anchorA, anchorB;
cpFloat min, max;
cpVect r1, r2;
cpVect n;
cpFloat nMass;
cpFloat jnAcc;
cpFloat bias;
};
struct cpPivotJoint {
cpConstraint constraint;
cpVect anchorA, anchorB;
cpVect r1, r2;
cpMat2x2 k;
cpVect jAcc;
cpVect bias;
};
struct cpGrooveJoint {
cpConstraint constraint;
cpVect grv_n, grv_a, grv_b;
cpVect anchorB;
cpVect grv_tn;
cpFloat clamp;
cpVect r1, r2;
cpMat2x2 k;
cpVect jAcc;
cpVect bias;
};
struct cpDampedSpring {
cpConstraint constraint;
cpVect anchorA, anchorB;
cpFloat restLength;
cpFloat stiffness;
cpFloat damping;
cpDampedSpringForceFunc springForceFunc;
cpFloat target_vrn;
cpFloat v_coef;
cpVect r1, r2;
cpFloat nMass;
cpVect n;
cpFloat jAcc;
};
struct cpDampedRotarySpring {
cpConstraint constraint;
cpFloat restAngle;
cpFloat stiffness;
cpFloat damping;
cpDampedRotarySpringTorqueFunc springTorqueFunc;
cpFloat target_wrn;
cpFloat w_coef;
cpFloat iSum;
cpFloat jAcc;
};
struct cpRotaryLimitJoint {
cpConstraint constraint;
cpFloat min, max;
cpFloat iSum;
cpFloat bias;
cpFloat jAcc;
};
struct cpRatchetJoint {
cpConstraint constraint;
cpFloat angle, phase, ratchet;
cpFloat iSum;
cpFloat bias;
cpFloat jAcc;
};
struct cpGearJoint {
cpConstraint constraint;
cpFloat phase, ratio;
cpFloat ratio_inv;
cpFloat iSum;
cpFloat bias;
cpFloat jAcc;
};
struct cpSimpleMotor {
cpConstraint constraint;
cpFloat rate;
cpFloat iSum;
cpFloat jAcc;
};
typedef struct cpContactBufferHeader cpContactBufferHeader;
typedef void (*cpSpaceArbiterApplyImpulseFunc)(cpArbiter *arb);
struct cpSpace {
int iterations;
cpVect gravity;
cpFloat damping;
cpFloat idleSpeedThreshold;
cpFloat sleepTimeThreshold;
cpFloat collisionSlop;
cpFloat collisionBias;
cpTimestamp collisionPersistence;
cpDataPointer userData;
cpTimestamp stamp;
cpFloat curr_dt;
cpArray *dynamicBodies;
cpArray *staticBodies;
cpArray *rousedBodies;
cpArray *sleepingComponents;
cpHashValue shapeIDCounter;
cpSpatialIndex *staticShapes;
cpSpatialIndex *dynamicShapes;
cpArray *constraints;
cpArray *arbiters;
cpContactBufferHeader *contactBuffersHead;
cpHashSet *cachedArbiters;
cpArray *pooledArbiters;
cpArray *allocatedBuffers;
unsigned int locked;
cpBool usesWildcards;
cpHashSet *collisionHandlers;
cpCollisionHandler defaultHandler;
cpBool skipPostStep;
cpArray *postStepCallbacks;
cpBody *staticBody;
cpBody _staticBody;
};
typedef struct cpPostStepCallback {
cpPostStepFunc func;
void *key;
void *data;
} cpPostStepCallback;
#endif

View File

@@ -0,0 +1,268 @@
/* 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
#include <CoreGraphics/CGGeometry.h>
#include <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
#define CP_PI ((cpFloat)3.14159265358979323846264338327950288)
/// Return the max of two cpFloats.
static inline cpFloat cpfmax(cpFloat a, cpFloat b)
{
return (a > b) ? a : b;
}
/// Return the min of two cpFloats.
static inline cpFloat cpfmin(cpFloat a, cpFloat b)
{
return (a < b) ? a : b;
}
/// Return the absolute value of a cpFloat.
static inline cpFloat cpfabs(cpFloat f)
{
return (f < 0) ? -f : f;
}
/// Clamp @c f to be between @c min and @c max.
static inline cpFloat cpfclamp(cpFloat f, cpFloat min, cpFloat max)
{
return cpfmin(cpfmax(f, min), max);
}
/// Clamp @c f to be between 0 and 1.
static inline cpFloat cpfclamp01(cpFloat f)
{
return cpfmax(0.0f, cpfmin(f, 1.0f));
}
/// Linearly interpolate (or extrapolate) between @c f1 and @c f2 by @c t percent.
static inline cpFloat cpflerp(cpFloat f1, cpFloat f2, cpFloat t)
{
return f1*(1.0f - t) + f2*t;
}
/// Linearly interpolate from @c f1 to @c f2 by no more than @c d.
static inline cpFloat cpflerpconst(cpFloat f1, cpFloat f2, cpFloat d)
{
return f1 + cpfclamp(f2 - f1, -d, d);
}
/// Hash value type.
#ifdef CP_HASH_VALUE_TYPE
typedef CP_HASH_VALUE_TYPE cpHashValue;
#else
typedef uintptr_t cpHashValue;
#endif
/// Type used internally to cache colliding object info for cpCollideShapes().
/// Should be at least 32 bits.
typedef uint32_t cpCollisionID;
// Oh C, how we love to define our own boolean types to get compiler compatibility
/// Chipmunk's boolean type.
#ifdef CP_BOOL_TYPE
typedef CP_BOOL_TYPE cpBool;
#else
typedef unsigned char cpBool;
#endif
#ifndef cpTrue
/// true value.
#define cpTrue 1
#endif
#ifndef cpFalse
/// false value.
#define cpFalse 0
#endif
#ifdef CP_DATA_POINTER_TYPE
typedef CP_DATA_POINTER_TYPE cpDataPointer;
#else
/// Type used for user data pointers.
typedef void * cpDataPointer;
#endif
#ifdef CP_COLLISION_TYPE_TYPE
typedef CP_COLLISION_TYPE_TYPE cpCollisionType;
#else
/// Type used for cpSpace.collision_type.
typedef uintptr_t cpCollisionType;
#endif
#ifdef CP_GROUP_TYPE
typedef CP_GROUP_TYPE cpGroup;
#else
/// Type used for cpShape.group.
typedef uintptr_t cpGroup;
#endif
#ifdef CP_BITMASK_TYPE
typedef CP_BITMASK_TYPE cpBitmask;
#else
/// Type used for cpShapeFilter category and mask.
typedef unsigned int cpBitmask;
#endif
#ifdef CP_TIMESTAMP_TYPE
typedef CP_TIMESTAMP_TYPE cpTimestamp;
#else
/// Type used for various timestamps in Chipmunk.
typedef unsigned int cpTimestamp;
#endif
#ifndef CP_NO_GROUP
/// Value for cpShape.group signifying that a shape is in no group.
#define CP_NO_GROUP ((cpGroup)0)
#endif
#ifndef CP_ALL_CATEGORIES
/// Value for cpShape.layers signifying that a shape is in every layer.
#define CP_ALL_CATEGORIES (~(cpBitmask)0)
#endif
#ifndef CP_WILDCARD_COLLISION_TYPE
/// cpCollisionType value internally reserved for hashing wildcard handlers.
#define CP_WILDCARD_COLLISION_TYPE (~(cpCollisionType)0)
#endif
/// @}
// CGPoints are structurally the same, and allow
// easy interoperability with other Cocoa libraries
#if CP_USE_CGTYPES
typedef CGPoint cpVect;
#else
/// Chipmunk's 2D vector type.
/// @addtogroup cpVect
typedef struct cpVect{cpFloat x,y;} cpVect;
#endif
#if CP_USE_CGTYPES
typedef CGAffineTransform cpTransform;
#else
/// Column major affine transform.
typedef struct cpTransform {
cpFloat a, b, c, d, tx, ty;
} cpTransform;
#endif
// NUKE
typedef struct cpMat2x2 {
// Row major [[a, b][c d]]
cpFloat a, b, c, d;
} cpMat2x2;
#endif

View File

@@ -0,0 +1,66 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/* This header defines a number of "unsafe" operations on Chipmunk objects.
* In this case "unsafe" is referring to operations which may reduce the
* physical accuracy or numerical stability of the simulation, but will not
* cause crashes.
*
* The prime example is mutating collision shapes. Chipmunk does not support
* this directly. Mutating shapes using this API will caused objects in contact
* to be pushed apart using Chipmunk's overlap solver, but not using real
* persistent velocities. Probably not what you meant, but perhaps close enough.
*/
/// @defgroup unsafe Chipmunk Unsafe Shape Operations
/// These functions are used for mutating collision shapes.
/// Chipmunk does not have any way to get velocity information on changing shapes,
/// so the results will be unrealistic. You must explicity include the chipmunk_unsafe.h header to use them.
/// @{
#ifndef CHIPMUNK_UNSAFE_H
#define CHIPMUNK_UNSAFE_H
#ifdef __cplusplus
extern "C" {
#endif
/// Set the radius of a circle shape.
CP_EXPORT void cpCircleShapeSetRadius(cpShape *shape, cpFloat radius);
/// Set the offset of a circle shape.
CP_EXPORT void cpCircleShapeSetOffset(cpShape *shape, cpVect offset);
/// Set the endpoints of a segment shape.
CP_EXPORT void cpSegmentShapeSetEndpoints(cpShape *shape, cpVect a, cpVect b);
/// Set the radius of a segment shape.
CP_EXPORT void cpSegmentShapeSetRadius(cpShape *shape, cpFloat radius);
/// Set the vertexes of a poly shape.
CP_EXPORT void cpPolyShapeSetVerts(cpShape *shape, int count, cpVect *verts, cpTransform transform);
CP_EXPORT void cpPolyShapeSetVertsRaw(cpShape *shape, int count, cpVect *verts);
/// Set the radius of a poly shape.
CP_EXPORT void cpPolyShapeSetRadius(cpShape *shape, cpFloat radius);
#ifdef __cplusplus
}
#endif
#endif
/// @}

View File

@@ -0,0 +1,145 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/// @defgroup cpArbiter cpArbiter
/// The cpArbiter struct tracks pairs of colliding shapes.
/// They are also used in conjuction with collision handler callbacks
/// allowing you to retrieve information on the collision or change it.
/// A unique arbiter value is used for each pair of colliding objects. It persists until the shapes separate.
/// @{
#define CP_MAX_CONTACTS_PER_ARBITER 2
/// Get the restitution (elasticity) that will be applied to the pair of colliding objects.
CP_EXPORT cpFloat cpArbiterGetRestitution(const cpArbiter *arb);
/// Override the restitution (elasticity) that will be applied to the pair of colliding objects.
CP_EXPORT void cpArbiterSetRestitution(cpArbiter *arb, cpFloat restitution);
/// Get the friction coefficient that will be applied to the pair of colliding objects.
CP_EXPORT cpFloat cpArbiterGetFriction(const cpArbiter *arb);
/// Override the friction coefficient that will be applied to the pair of colliding objects.
CP_EXPORT void cpArbiterSetFriction(cpArbiter *arb, cpFloat friction);
// Get the relative surface velocity of the two shapes in contact.
CP_EXPORT cpVect cpArbiterGetSurfaceVelocity(cpArbiter *arb);
// Override the relative surface velocity of the two shapes in contact.
// By default this is calculated to be the difference of the two surface velocities clamped to the tangent plane.
CP_EXPORT void cpArbiterSetSurfaceVelocity(cpArbiter *arb, cpVect vr);
/// Get the user data pointer associated with this pair of colliding objects.
CP_EXPORT cpDataPointer cpArbiterGetUserData(const cpArbiter *arb);
/// Set a user data point associated with this pair of colliding objects.
/// If you need to perform any cleanup for this pointer, you must do it yourself, in the separate callback for instance.
CP_EXPORT void cpArbiterSetUserData(cpArbiter *arb, cpDataPointer userData);
/// Calculate the total impulse including the friction that was applied by this arbiter.
/// This function should only be called from a post-solve, post-step or cpBodyEachArbiter callback.
CP_EXPORT cpVect cpArbiterTotalImpulse(const cpArbiter *arb);
/// Calculate the amount of energy lost in a collision including static, but not dynamic friction.
/// This function should only be called from a post-solve, post-step or cpBodyEachArbiter callback.
CP_EXPORT cpFloat cpArbiterTotalKE(const cpArbiter *arb);
/// Mark a collision pair to be ignored until the two objects separate.
/// Pre-solve and post-solve callbacks will not be called, but the separate callback will be called.
CP_EXPORT cpBool cpArbiterIgnore(cpArbiter *arb);
/// Return the colliding shapes involved for this arbiter.
/// The order of their cpSpace.collision_type values will match
/// the order set when the collision handler was registered.
CP_EXPORT void cpArbiterGetShapes(const cpArbiter *arb, cpShape **a, cpShape **b);
/// A macro shortcut for defining and retrieving the shapes from an arbiter.
#define CP_ARBITER_GET_SHAPES(__arb__, __a__, __b__) cpShape *__a__, *__b__; cpArbiterGetShapes(__arb__, &__a__, &__b__);
/// Return the colliding bodies involved for this arbiter.
/// The order of the cpSpace.collision_type the bodies are associated with values will match
/// the order set when the collision handler was registered.
CP_EXPORT void cpArbiterGetBodies(const cpArbiter *arb, cpBody **a, cpBody **b);
/// A macro shortcut for defining and retrieving the bodies from an arbiter.
#define CP_ARBITER_GET_BODIES(__arb__, __a__, __b__) cpBody *__a__, *__b__; cpArbiterGetBodies(__arb__, &__a__, &__b__);
/// A struct that wraps up the important collision data for an arbiter.
struct cpContactPointSet {
/// The number of contact points in the set.
int count;
/// The normal of the collision.
cpVect normal;
/// The array of contact points.
struct {
/// The position of the contact on the surface of each shape.
cpVect pointA, pointB;
/// Penetration distance of the two shapes. Overlapping means it will be negative.
/// This value is calculated as cpvdot(cpvsub(point2, point1), normal) and is ignored by cpArbiterSetContactPointSet().
cpFloat distance;
} points[CP_MAX_CONTACTS_PER_ARBITER];
};
/// Return a contact set from an arbiter.
CP_EXPORT cpContactPointSet cpArbiterGetContactPointSet(const cpArbiter *arb);
/// Replace the contact point set for an arbiter.
/// This can be a very powerful feature, but use it with caution!
CP_EXPORT void cpArbiterSetContactPointSet(cpArbiter *arb, cpContactPointSet *set);
/// Returns true if this is the first step a pair of objects started colliding.
CP_EXPORT cpBool cpArbiterIsFirstContact(const cpArbiter *arb);
/// Returns true if the separate callback is due to a shape being removed from the space.
CP_EXPORT cpBool cpArbiterIsRemoval(const cpArbiter *arb);
/// Get the number of contact points for this arbiter.
CP_EXPORT int cpArbiterGetCount(const cpArbiter *arb);
/// Get the normal of the collision.
CP_EXPORT cpVect cpArbiterGetNormal(const cpArbiter *arb);
/// Get the position of the @c ith contact point on the surface of the first shape.
CP_EXPORT cpVect cpArbiterGetPointA(const cpArbiter *arb, int i);
/// Get the position of the @c ith contact point on the surface of the second shape.
CP_EXPORT cpVect cpArbiterGetPointB(const cpArbiter *arb, int i);
/// Get the depth of the @c ith contact point.
CP_EXPORT cpFloat cpArbiterGetDepth(const cpArbiter *arb, int i);
/// If you want a custom callback to invoke the wildcard callback for the first collision type, you must call this function explicitly.
/// You must decide how to handle the wildcard's return value since it may disagree with the other wildcard handler's return value or your own.
CP_EXPORT cpBool cpArbiterCallWildcardBeginA(cpArbiter *arb, cpSpace *space);
/// If you want a custom callback to invoke the wildcard callback for the second collision type, you must call this function explicitly.
/// You must decide how to handle the wildcard's return value since it may disagree with the other wildcard handler's return value or your own.
CP_EXPORT cpBool cpArbiterCallWildcardBeginB(cpArbiter *arb, cpSpace *space);
/// If you want a custom callback to invoke the wildcard callback for the first collision type, you must call this function explicitly.
/// You must decide how to handle the wildcard's return value since it may disagree with the other wildcard handler's return value or your own.
CP_EXPORT cpBool cpArbiterCallWildcardPreSolveA(cpArbiter *arb, cpSpace *space);
/// If you want a custom callback to invoke the wildcard callback for the second collision type, you must call this function explicitly.
/// You must decide how to handle the wildcard's return value since it may disagree with the other wildcard handler's return value or your own.
CP_EXPORT cpBool cpArbiterCallWildcardPreSolveB(cpArbiter *arb, cpSpace *space);
/// If you want a custom callback to invoke the wildcard callback for the first collision type, you must call this function explicitly.
CP_EXPORT void cpArbiterCallWildcardPostSolveA(cpArbiter *arb, cpSpace *space);
/// If you want a custom callback to invoke the wildcard callback for the second collision type, you must call this function explicitly.
CP_EXPORT void cpArbiterCallWildcardPostSolveB(cpArbiter *arb, cpSpace *space);
/// If you want a custom callback to invoke the wildcard callback for the first collision type, you must call this function explicitly.
CP_EXPORT void cpArbiterCallWildcardSeparateA(cpArbiter *arb, cpSpace *space);
/// If you want a custom callback to invoke the wildcard callback for the second collision type, you must call this function explicitly.
CP_EXPORT void cpArbiterCallWildcardSeparateB(cpArbiter *arb, cpSpace *space);
/// @}

View File

@@ -0,0 +1,187 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef CHIPMUNK_BB_H
#define CHIPMUNK_BB_H
#include "chipmunk_types.h"
#include "cpVect.h"
/// @defgroup cpBBB cpBB
/// Chipmunk's axis-aligned 2D bounding box type along with a few handy routines.
/// @{
/// Chipmunk's axis-aligned 2D bounding box type. (left, bottom, right, top)
typedef struct cpBB{
cpFloat l, b, r ,t;
} cpBB;
/// Convenience constructor for cpBB structs.
static inline cpBB cpBBNew(const cpFloat l, const cpFloat b, const cpFloat r, const cpFloat t)
{
cpBB bb = {l, b, r, t};
return bb;
}
/// Constructs a cpBB centered on a point with the given extents (half sizes).
static inline cpBB
cpBBNewForExtents(const cpVect c, const cpFloat hw, const cpFloat hh)
{
return cpBBNew(c.x - hw, c.y - hh, c.x + hw, c.y + hh);
}
/// Constructs a cpBB for a circle with the given position and radius.
static inline cpBB cpBBNewForCircle(const cpVect p, const cpFloat r)
{
return cpBBNewForExtents(p, r, r);
}
/// Returns true if @c a and @c b intersect.
static inline cpBool cpBBIntersects(const cpBB a, const cpBB b)
{
return (a.l <= b.r && b.l <= a.r && a.b <= b.t && b.b <= a.t);
}
/// Returns true if @c other lies completely within @c bb.
static inline cpBool cpBBContainsBB(const cpBB bb, const cpBB other)
{
return (bb.l <= other.l && bb.r >= other.r && bb.b <= other.b && bb.t >= other.t);
}
/// Returns true if @c bb contains @c v.
static inline cpBool cpBBContainsVect(const cpBB bb, const cpVect v)
{
return (bb.l <= v.x && bb.r >= v.x && bb.b <= v.y && bb.t >= v.y);
}
/// Returns a bounding box that holds both bounding boxes.
static inline cpBB cpBBMerge(const cpBB a, const cpBB b){
return cpBBNew(
cpfmin(a.l, b.l),
cpfmin(a.b, b.b),
cpfmax(a.r, b.r),
cpfmax(a.t, b.t)
);
}
/// Returns a bounding box that holds both @c bb and @c v.
static inline cpBB cpBBExpand(const cpBB bb, const cpVect v){
return cpBBNew(
cpfmin(bb.l, v.x),
cpfmin(bb.b, v.y),
cpfmax(bb.r, v.x),
cpfmax(bb.t, v.y)
);
}
/// Returns the center of a bounding box.
static inline cpVect
cpBBCenter(cpBB bb)
{
return cpvlerp(cpv(bb.l, bb.b), cpv(bb.r, bb.t), 0.5f);
}
/// Returns the area of the bounding box.
static inline cpFloat cpBBArea(cpBB bb)
{
return (bb.r - bb.l)*(bb.t - bb.b);
}
/// Merges @c a and @c b and returns the area of the merged bounding box.
static inline cpFloat cpBBMergedArea(cpBB a, cpBB b)
{
return (cpfmax(a.r, b.r) - cpfmin(a.l, b.l))*(cpfmax(a.t, b.t) - cpfmin(a.b, b.b));
}
/// Returns the fraction along the segment query the cpBB is hit. Returns INFINITY if it doesn't hit.
static inline cpFloat cpBBSegmentQuery(cpBB bb, cpVect a, cpVect b)
{
cpVect delta = cpvsub(b, a);
cpFloat tmin = -INFINITY, tmax = INFINITY;
if(delta.x == 0.0f){
if(a.x < bb.l || bb.r < a.x) return INFINITY;
} else {
cpFloat t1 = (bb.l - a.x)/delta.x;
cpFloat t2 = (bb.r - a.x)/delta.x;
tmin = cpfmax(tmin, cpfmin(t1, t2));
tmax = cpfmin(tmax, cpfmax(t1, t2));
}
if(delta.y == 0.0f){
if(a.y < bb.b || bb.t < a.y) return INFINITY;
} else {
cpFloat t1 = (bb.b - a.y)/delta.y;
cpFloat t2 = (bb.t - a.y)/delta.y;
tmin = cpfmax(tmin, cpfmin(t1, t2));
tmax = cpfmin(tmax, cpfmax(t1, t2));
}
if(tmin <= tmax && 0.0f <= tmax && tmin <= 1.0f){
return cpfmax(tmin, 0.0f);
} else {
return INFINITY;
}
}
/// Return true if the bounding box intersects the line segment with ends @c a and @c b.
static inline cpBool cpBBIntersectsSegment(cpBB bb, cpVect a, cpVect b)
{
return (cpBBSegmentQuery(bb, a, b) != INFINITY);
}
/// Clamp a vector to a bounding box.
static inline cpVect
cpBBClampVect(const cpBB bb, const cpVect v)
{
return cpv(cpfclamp(v.x, bb.l, bb.r), cpfclamp(v.y, bb.b, bb.t));
}
/// Wrap a vector to a bounding box.
static inline cpVect
cpBBWrapVect(const cpBB bb, const cpVect v)
{
cpFloat dx = cpfabs(bb.r - bb.l);
cpFloat modx = cpfmod(v.x - bb.l, dx);
cpFloat x = (modx > 0.0f) ? modx : modx + dx;
cpFloat dy = cpfabs(bb.t - bb.b);
cpFloat mody = cpfmod(v.y - bb.b, dy);
cpFloat y = (mody > 0.0f) ? mody : mody + dy;
return cpv(x + bb.l, y + bb.b);
}
/// Returns a bounding box offseted by @c v.
static inline cpBB
cpBBOffset(const cpBB bb, const cpVect v)
{
return cpBBNew(
bb.l + v.x,
bb.b + v.y,
bb.r + v.x,
bb.t + v.y
);
}
///@}
#endif

View File

@@ -0,0 +1,189 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/// @defgroup cpBody cpBody
/// Chipmunk's rigid body type. Rigid bodies hold the physical properties of an object like
/// it's mass, and position and velocity of it's center of gravity. They don't have an shape on their own.
/// They are given a shape by creating collision shapes (cpShape) that point to the body.
/// @{
typedef enum cpBodyType {
/// A dynamic body is one that is affected by gravity, forces, and collisions.
/// This is the default body type.
CP_BODY_TYPE_DYNAMIC,
/// A kinematic body is an infinite mass, user controlled body that is not affected by gravity, forces or collisions.
/// Instead the body only moves based on it's velocity.
/// Dynamic bodies collide normally with kinematic bodies, though the kinematic body will be unaffected.
/// Collisions between two kinematic bodies, or a kinematic body and a static body produce collision callbacks, but no collision response.
CP_BODY_TYPE_KINEMATIC,
/// A static body is a body that never (or rarely) moves. If you move a static body, you must call one of the cpSpaceReindex*() functions.
/// Chipmunk uses this information to optimize the collision detection.
/// Static bodies do not produce collision callbacks when colliding with other static bodies.
CP_BODY_TYPE_STATIC,
} cpBodyType;
/// Rigid body velocity update function type.
typedef void (*cpBodyVelocityFunc)(cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt);
/// Rigid body position update function type.
typedef void (*cpBodyPositionFunc)(cpBody *body, cpFloat dt);
/// Allocate a cpBody.
CP_EXPORT cpBody* cpBodyAlloc(void);
/// Initialize a cpBody.
CP_EXPORT cpBody* cpBodyInit(cpBody *body, cpFloat mass, cpFloat moment);
/// Allocate and initialize a cpBody.
CP_EXPORT cpBody* cpBodyNew(cpFloat mass, cpFloat moment);
/// Allocate and initialize a cpBody, and set it as a kinematic body.
CP_EXPORT cpBody* cpBodyNewKinematic(void);
/// Allocate and initialize a cpBody, and set it as a static body.
CP_EXPORT cpBody* cpBodyNewStatic(void);
/// Destroy a cpBody.
CP_EXPORT void cpBodyDestroy(cpBody *body);
/// Destroy and free a cpBody.
CP_EXPORT void cpBodyFree(cpBody *body);
// Defined in cpSpace.c
/// Wake up a sleeping or idle body.
CP_EXPORT void cpBodyActivate(cpBody *body);
/// Wake up any sleeping or idle bodies touching a static body.
CP_EXPORT void cpBodyActivateStatic(cpBody *body, cpShape *filter);
/// Force a body to fall asleep immediately.
CP_EXPORT void cpBodySleep(cpBody *body);
/// Force a body to fall asleep immediately along with other bodies in a group.
CP_EXPORT void cpBodySleepWithGroup(cpBody *body, cpBody *group);
/// Returns true if the body is sleeping.
CP_EXPORT cpBool cpBodyIsSleeping(const cpBody *body);
/// Get the type of the body.
CP_EXPORT cpBodyType cpBodyGetType(cpBody *body);
/// Set the type of the body.
CP_EXPORT void cpBodySetType(cpBody *body, cpBodyType type);
/// Get the space this body is added to.
CP_EXPORT cpSpace* cpBodyGetSpace(const cpBody *body);
/// Get the mass of the body.
CP_EXPORT cpFloat cpBodyGetMass(const cpBody *body);
/// Set the mass of the body.
CP_EXPORT void cpBodySetMass(cpBody *body, cpFloat m);
/// Get the moment of inertia of the body.
CP_EXPORT cpFloat cpBodyGetMoment(const cpBody *body);
/// Set the moment of inertia of the body.
CP_EXPORT void cpBodySetMoment(cpBody *body, cpFloat i);
/// Set the position of a body.
CP_EXPORT cpVect cpBodyGetPosition(const cpBody *body);
/// Set the position of the body.
CP_EXPORT void cpBodySetPosition(cpBody *body, cpVect pos);
/// Get the offset of the center of gravity in body local coordinates.
CP_EXPORT cpVect cpBodyGetCenterOfGravity(const cpBody *body);
/// Set the offset of the center of gravity in body local coordinates.
CP_EXPORT void cpBodySetCenterOfGravity(cpBody *body, cpVect cog);
/// Get the velocity of the body.
CP_EXPORT cpVect cpBodyGetVelocity(const cpBody *body);
/// Set the velocity of the body.
CP_EXPORT void cpBodySetVelocity(cpBody *body, cpVect velocity);
/// Get the force applied to the body for the next time step.
CP_EXPORT cpVect cpBodyGetForce(const cpBody *body);
/// Set the force applied to the body for the next time step.
CP_EXPORT void cpBodySetForce(cpBody *body, cpVect force);
/// Get the angle of the body.
CP_EXPORT cpFloat cpBodyGetAngle(const cpBody *body);
/// Set the angle of a body.
CP_EXPORT void cpBodySetAngle(cpBody *body, cpFloat a);
/// Get the angular velocity of the body.
CP_EXPORT cpFloat cpBodyGetAngularVelocity(const cpBody *body);
/// Set the angular velocity of the body.
CP_EXPORT void cpBodySetAngularVelocity(cpBody *body, cpFloat angularVelocity);
/// Get the torque applied to the body for the next time step.
CP_EXPORT cpFloat cpBodyGetTorque(const cpBody *body);
/// Set the torque applied to the body for the next time step.
CP_EXPORT void cpBodySetTorque(cpBody *body, cpFloat torque);
/// Get the rotation vector of the body. (The x basis vector of it's transform.)
CP_EXPORT cpVect cpBodyGetRotation(const cpBody *body);
/// Get the user data pointer assigned to the body.
CP_EXPORT cpDataPointer cpBodyGetUserData(const cpBody *body);
/// Set the user data pointer assigned to the body.
CP_EXPORT void cpBodySetUserData(cpBody *body, cpDataPointer userData);
/// Set the callback used to update a body's velocity.
CP_EXPORT void cpBodySetVelocityUpdateFunc(cpBody *body, cpBodyVelocityFunc velocityFunc);
/// Set the callback used to update a body's position.
/// NOTE: It's not generally recommended to override this unless you call the default position update function.
CP_EXPORT void cpBodySetPositionUpdateFunc(cpBody *body, cpBodyPositionFunc positionFunc);
/// Default velocity integration function..
CP_EXPORT void cpBodyUpdateVelocity(cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt);
/// Default position integration function.
CP_EXPORT void cpBodyUpdatePosition(cpBody *body, cpFloat dt);
/// Convert body relative/local coordinates to absolute/world coordinates.
CP_EXPORT cpVect cpBodyLocalToWorld(const cpBody *body, const cpVect point);
/// Convert body absolute/world coordinates to relative/local coordinates.
CP_EXPORT cpVect cpBodyWorldToLocal(const cpBody *body, const cpVect point);
/// Apply a force to a body. Both the force and point are expressed in world coordinates.
CP_EXPORT void cpBodyApplyForceAtWorldPoint(cpBody *body, cpVect force, cpVect point);
/// Apply a force to a body. Both the force and point are expressed in body local coordinates.
CP_EXPORT void cpBodyApplyForceAtLocalPoint(cpBody *body, cpVect force, cpVect point);
/// Apply an impulse to a body. Both the impulse and point are expressed in world coordinates.
CP_EXPORT void cpBodyApplyImpulseAtWorldPoint(cpBody *body, cpVect impulse, cpVect point);
/// Apply an impulse to a body. Both the impulse and point are expressed in body local coordinates.
CP_EXPORT void cpBodyApplyImpulseAtLocalPoint(cpBody *body, cpVect impulse, cpVect point);
/// Get the velocity on a body (in world units) at a point on the body in world coordinates.
CP_EXPORT cpVect cpBodyGetVelocityAtWorldPoint(const cpBody *body, cpVect point);
/// Get the velocity on a body (in world units) at a point on the body in local coordinates.
CP_EXPORT cpVect cpBodyGetVelocityAtLocalPoint(const cpBody *body, cpVect point);
/// Get the amount of kinetic energy contained by the body.
CP_EXPORT cpFloat cpBodyKineticEnergy(const cpBody *body);
/// Body/shape iterator callback function type.
typedef void (*cpBodyShapeIteratorFunc)(cpBody *body, cpShape *shape, void *data);
/// Call @c func once for each shape attached to @c body and added to the space.
CP_EXPORT void cpBodyEachShape(cpBody *body, cpBodyShapeIteratorFunc func, void *data);
/// Body/constraint iterator callback function type.
typedef void (*cpBodyConstraintIteratorFunc)(cpBody *body, cpConstraint *constraint, void *data);
/// Call @c func once for each constraint attached to @c body and added to the space.
CP_EXPORT void cpBodyEachConstraint(cpBody *body, cpBodyConstraintIteratorFunc func, void *data);
/// Body/arbiter iterator callback function type.
typedef void (*cpBodyArbiterIteratorFunc)(cpBody *body, cpArbiter *arbiter, void *data);
/// Call @c func once for each arbiter that is currently active on the body.
CP_EXPORT void cpBodyEachArbiter(cpBody *body, cpBodyArbiterIteratorFunc func, void *data);
///@}

View File

@@ -0,0 +1,95 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/// @defgroup cpConstraint cpConstraint
/// @{
/// Callback function type that gets called before solving a joint.
typedef void (*cpConstraintPreSolveFunc)(cpConstraint *constraint, cpSpace *space);
/// Callback function type that gets called after solving a joint.
typedef void (*cpConstraintPostSolveFunc)(cpConstraint *constraint, cpSpace *space);
/// Destroy a constraint.
CP_EXPORT void cpConstraintDestroy(cpConstraint *constraint);
/// Destroy and free a constraint.
CP_EXPORT void cpConstraintFree(cpConstraint *constraint);
/// Get the cpSpace this constraint is added to.
CP_EXPORT cpSpace* cpConstraintGetSpace(const cpConstraint *constraint);
/// Get the first body the constraint is attached to.
CP_EXPORT cpBody* cpConstraintGetBodyA(const cpConstraint *constraint);
/// Get the second body the constraint is attached to.
CP_EXPORT cpBody* cpConstraintGetBodyB(const cpConstraint *constraint);
/// Get the maximum force that this constraint is allowed to use.
CP_EXPORT cpFloat cpConstraintGetMaxForce(const cpConstraint *constraint);
/// Set the maximum force that this constraint is allowed to use. (defaults to INFINITY)
CP_EXPORT void cpConstraintSetMaxForce(cpConstraint *constraint, cpFloat maxForce);
/// Get rate at which joint error is corrected.
CP_EXPORT cpFloat cpConstraintGetErrorBias(const cpConstraint *constraint);
/// Set rate at which joint error is corrected.
/// Defaults to pow(1.0 - 0.1, 60.0) meaning that it will
/// correct 10% of the error every 1/60th of a second.
CP_EXPORT void cpConstraintSetErrorBias(cpConstraint *constraint, cpFloat errorBias);
/// Get the maximum rate at which joint error is corrected.
CP_EXPORT cpFloat cpConstraintGetMaxBias(const cpConstraint *constraint);
/// Set the maximum rate at which joint error is corrected. (defaults to INFINITY)
CP_EXPORT void cpConstraintSetMaxBias(cpConstraint *constraint, cpFloat maxBias);
/// Get if the two bodies connected by the constraint are allowed to collide or not.
CP_EXPORT cpBool cpConstraintGetCollideBodies(const cpConstraint *constraint);
/// Set if the two bodies connected by the constraint are allowed to collide or not. (defaults to cpFalse)
CP_EXPORT void cpConstraintSetCollideBodies(cpConstraint *constraint, cpBool collideBodies);
/// Get the pre-solve function that is called before the solver runs.
CP_EXPORT cpConstraintPreSolveFunc cpConstraintGetPreSolveFunc(const cpConstraint *constraint);
/// Set the pre-solve function that is called before the solver runs.
CP_EXPORT void cpConstraintSetPreSolveFunc(cpConstraint *constraint, cpConstraintPreSolveFunc preSolveFunc);
/// Get the post-solve function that is called before the solver runs.
CP_EXPORT cpConstraintPostSolveFunc cpConstraintGetPostSolveFunc(const cpConstraint *constraint);
/// Set the post-solve function that is called before the solver runs.
CP_EXPORT void cpConstraintSetPostSolveFunc(cpConstraint *constraint, cpConstraintPostSolveFunc postSolveFunc);
/// Get the user definable data pointer for this constraint
CP_EXPORT cpDataPointer cpConstraintGetUserData(const cpConstraint *constraint);
/// Set the user definable data pointer for this constraint
CP_EXPORT void cpConstraintSetUserData(cpConstraint *constraint, cpDataPointer userData);
/// Get the last impulse applied by this constraint.
CP_EXPORT cpFloat cpConstraintGetImpulse(cpConstraint *constraint);
#include "cpPinJoint.h"
#include "cpSlideJoint.h"
#include "cpPivotJoint.h"
#include "cpGrooveJoint.h"
#include "cpDampedSpring.h"
#include "cpDampedRotarySpring.h"
#include "cpRotaryLimitJoint.h"
#include "cpRatchetJoint.h"
#include "cpGearJoint.h"
#include "cpSimpleMotor.h"
///@}

View File

@@ -0,0 +1,58 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/// @defgroup cpDampedRotarySpring cpDampedRotarySpring
/// @{
/// Check if a constraint is a damped rotary springs.
CP_EXPORT cpBool cpConstraintIsDampedRotarySpring(const cpConstraint *constraint);
/// Function type used for damped rotary spring force callbacks.
typedef cpFloat (*cpDampedRotarySpringTorqueFunc)(struct cpConstraint *spring, cpFloat relativeAngle);
/// Allocate a damped rotary spring.
CP_EXPORT cpDampedRotarySpring* cpDampedRotarySpringAlloc(void);
/// Initialize a damped rotary spring.
CP_EXPORT cpDampedRotarySpring* cpDampedRotarySpringInit(cpDampedRotarySpring *joint, cpBody *a, cpBody *b, cpFloat restAngle, cpFloat stiffness, cpFloat damping);
/// Allocate and initialize a damped rotary spring.
CP_EXPORT cpConstraint* cpDampedRotarySpringNew(cpBody *a, cpBody *b, cpFloat restAngle, cpFloat stiffness, cpFloat damping);
/// Get the rest length of the spring.
CP_EXPORT cpFloat cpDampedRotarySpringGetRestAngle(const cpConstraint *constraint);
/// Set the rest length of the spring.
CP_EXPORT void cpDampedRotarySpringSetRestAngle(cpConstraint *constraint, cpFloat restAngle);
/// Get the stiffness of the spring in force/distance.
CP_EXPORT cpFloat cpDampedRotarySpringGetStiffness(const cpConstraint *constraint);
/// Set the stiffness of the spring in force/distance.
CP_EXPORT void cpDampedRotarySpringSetStiffness(cpConstraint *constraint, cpFloat stiffness);
/// Get the damping of the spring.
CP_EXPORT cpFloat cpDampedRotarySpringGetDamping(const cpConstraint *constraint);
/// Set the damping of the spring.
CP_EXPORT void cpDampedRotarySpringSetDamping(cpConstraint *constraint, cpFloat damping);
/// Get the damping of the spring.
CP_EXPORT cpDampedRotarySpringTorqueFunc cpDampedRotarySpringGetSpringTorqueFunc(const cpConstraint *constraint);
/// Set the damping of the spring.
CP_EXPORT void cpDampedRotarySpringSetSpringTorqueFunc(cpConstraint *constraint, cpDampedRotarySpringTorqueFunc springTorqueFunc);
/// @}

View File

@@ -0,0 +1,68 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/// @defgroup cpDampedSpring cpDampedSpring
/// @{
/// Check if a constraint is a slide joint.
CP_EXPORT cpBool cpConstraintIsDampedSpring(const cpConstraint *constraint);
/// Function type used for damped spring force callbacks.
typedef cpFloat (*cpDampedSpringForceFunc)(cpConstraint *spring, cpFloat dist);
/// Allocate a damped spring.
CP_EXPORT cpDampedSpring* cpDampedSpringAlloc(void);
/// Initialize a damped spring.
CP_EXPORT cpDampedSpring* cpDampedSpringInit(cpDampedSpring *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat restLength, cpFloat stiffness, cpFloat damping);
/// Allocate and initialize a damped spring.
CP_EXPORT cpConstraint* cpDampedSpringNew(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat restLength, cpFloat stiffness, cpFloat damping);
/// Get the location of the first anchor relative to the first body.
CP_EXPORT cpVect cpDampedSpringGetAnchorA(const cpConstraint *constraint);
/// Set the location of the first anchor relative to the first body.
CP_EXPORT void cpDampedSpringSetAnchorA(cpConstraint *constraint, cpVect anchorA);
/// Get the location of the second anchor relative to the second body.
CP_EXPORT cpVect cpDampedSpringGetAnchorB(const cpConstraint *constraint);
/// Set the location of the second anchor relative to the second body.
CP_EXPORT void cpDampedSpringSetAnchorB(cpConstraint *constraint, cpVect anchorB);
/// Get the rest length of the spring.
CP_EXPORT cpFloat cpDampedSpringGetRestLength(const cpConstraint *constraint);
/// Set the rest length of the spring.
CP_EXPORT void cpDampedSpringSetRestLength(cpConstraint *constraint, cpFloat restLength);
/// Get the stiffness of the spring in force/distance.
CP_EXPORT cpFloat cpDampedSpringGetStiffness(const cpConstraint *constraint);
/// Set the stiffness of the spring in force/distance.
CP_EXPORT void cpDampedSpringSetStiffness(cpConstraint *constraint, cpFloat stiffness);
/// Get the damping of the spring.
CP_EXPORT cpFloat cpDampedSpringGetDamping(const cpConstraint *constraint);
/// Set the damping of the spring.
CP_EXPORT void cpDampedSpringSetDamping(cpConstraint *constraint, cpFloat damping);
/// Get the damping of the spring.
CP_EXPORT cpDampedSpringForceFunc cpDampedSpringGetSpringForceFunc(const cpConstraint *constraint);
/// Set the damping of the spring.
CP_EXPORT void cpDampedSpringSetSpringForceFunc(cpConstraint *constraint, cpDampedSpringForceFunc springForceFunc);
/// @}

View File

@@ -0,0 +1,45 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/// @defgroup cpGearJoint cpGearJoint
/// @{
/// Check if a constraint is a damped rotary springs.
CP_EXPORT cpBool cpConstraintIsGearJoint(const cpConstraint *constraint);
/// Allocate a gear joint.
CP_EXPORT cpGearJoint* cpGearJointAlloc(void);
/// Initialize a gear joint.
CP_EXPORT cpGearJoint* cpGearJointInit(cpGearJoint *joint, cpBody *a, cpBody *b, cpFloat phase, cpFloat ratio);
/// Allocate and initialize a gear joint.
CP_EXPORT cpConstraint* cpGearJointNew(cpBody *a, cpBody *b, cpFloat phase, cpFloat ratio);
/// Get the phase offset of the gears.
CP_EXPORT cpFloat cpGearJointGetPhase(const cpConstraint *constraint);
/// Set the phase offset of the gears.
CP_EXPORT void cpGearJointSetPhase(cpConstraint *constraint, cpFloat phase);
/// Get the angular distance of each ratchet.
CP_EXPORT cpFloat cpGearJointGetRatio(const cpConstraint *constraint);
/// Set the ratio of a gear joint.
CP_EXPORT void cpGearJointSetRatio(cpConstraint *constraint, cpFloat ratio);
/// @}

View File

@@ -0,0 +1,50 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/// @defgroup cpGrooveJoint cpGrooveJoint
/// @{
/// Check if a constraint is a slide joint.
CP_EXPORT cpBool cpConstraintIsGrooveJoint(const cpConstraint *constraint);
/// Allocate a groove joint.
CP_EXPORT cpGrooveJoint* cpGrooveJointAlloc(void);
/// Initialize a groove joint.
CP_EXPORT cpGrooveJoint* cpGrooveJointInit(cpGrooveJoint *joint, cpBody *a, cpBody *b, cpVect groove_a, cpVect groove_b, cpVect anchorB);
/// Allocate and initialize a groove joint.
CP_EXPORT cpConstraint* cpGrooveJointNew(cpBody *a, cpBody *b, cpVect groove_a, cpVect groove_b, cpVect anchorB);
/// Get the first endpoint of the groove relative to the first body.
CP_EXPORT cpVect cpGrooveJointGetGrooveA(const cpConstraint *constraint);
/// Set the first endpoint of the groove relative to the first body.
CP_EXPORT void cpGrooveJointSetGrooveA(cpConstraint *constraint, cpVect grooveA);
/// Get the first endpoint of the groove relative to the first body.
CP_EXPORT cpVect cpGrooveJointGetGrooveB(const cpConstraint *constraint);
/// Set the first endpoint of the groove relative to the first body.
CP_EXPORT void cpGrooveJointSetGrooveB(cpConstraint *constraint, cpVect grooveB);
/// Get the location of the second anchor relative to the second body.
CP_EXPORT cpVect cpGrooveJointGetAnchorB(const cpConstraint *constraint);
/// Set the location of the second anchor relative to the second body.
CP_EXPORT void cpGrooveJointSetAnchorB(cpConstraint *constraint, cpVect anchorB);
/// @}

View File

@@ -0,0 +1,27 @@
// Copyright 2013 Howling Moon Software. All rights reserved.
// See http://chipmunk2d.net/legal.php for more information.
/// cpHastySpace is exclusive to Chipmunk Pro
/// Currently it enables ARM NEON optimizations in the solver, but in the future will include other optimizations such as
/// a multi-threaded solver and multi-threaded collision broadphases.
struct cpHastySpace;
typedef struct cpHastySpace cpHastySpace;
/// Create a new hasty space.
/// On ARM platforms that support NEON, this will enable the vectorized solver.
/// cpHastySpace also supports multiple threads, but runs single threaded by default for determinism.
CP_EXPORT cpSpace *cpHastySpaceNew(void);
CP_EXPORT void cpHastySpaceFree(cpSpace *space);
/// Set the number of threads to use for the solver.
/// Currently Chipmunk is limited to 2 threads as using more generally provides very minimal performance gains.
/// Passing 0 as the thread count on iOS or OS X will cause Chipmunk to automatically detect the number of threads it should use.
/// On other platforms passing 0 for the thread count will set 1 thread.
CP_EXPORT void cpHastySpaceSetThreads(cpSpace *space, unsigned long threads);
/// Returns the number of threads the solver is using to run.
CP_EXPORT unsigned long cpHastySpaceGetThreads(cpSpace *space);
/// When stepping a hasty space, you must use this function.
CP_EXPORT void cpHastySpaceStep(cpSpace *space, cpFloat dt);

View File

@@ -0,0 +1,28 @@
// Copyright 2013 Howling Moon Software. All rights reserved.
// See http://chipmunk2d.net/legal.php for more information.
/// Function type used as a callback from the marching squares algorithm to sample an image function.
/// It passes you the point to sample and your context pointer, and you return the density.
typedef cpFloat (*cpMarchSampleFunc)(cpVect point, void *data);
/// Function type used as a callback from the marching squares algorithm to output a line segment.
/// It passes you the two endpoints and your context pointer.
typedef void (*cpMarchSegmentFunc)(cpVect v0, cpVect v1, void *data);
/// Trace an anti-aliased contour of an image along a particular threshold.
/// The given number of samples will be taken and spread across the bounding box area using the sampling function and context.
/// The segment function will be called for each segment detected that lies along the density contour for @c threshold.
CP_EXPORT void cpMarchSoft(
cpBB bb, unsigned long x_samples, unsigned long y_samples, cpFloat threshold,
cpMarchSegmentFunc segment, void *segment_data,
cpMarchSampleFunc sample, void *sample_data
);
/// Trace an aliased curve of an image along a particular threshold.
/// The given number of samples will be taken and spread across the bounding box area using the sampling function and context.
/// The segment function will be called for each segment detected that lies along the density contour for @c threshold.
CP_EXPORT void cpMarchHard(
cpBB bb, unsigned long x_samples, unsigned long y_samples, cpFloat threshold,
cpMarchSegmentFunc segment, void *segment_data,
cpMarchSampleFunc sample, void *sample_data
);

View File

@@ -0,0 +1,50 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/// @defgroup cpPinJoint cpPinJoint
/// @{
/// Check if a constraint is a pin joint.
CP_EXPORT cpBool cpConstraintIsPinJoint(const cpConstraint *constraint);
/// Allocate a pin joint.
CP_EXPORT cpPinJoint* cpPinJointAlloc(void);
/// Initialize a pin joint.
CP_EXPORT cpPinJoint* cpPinJointInit(cpPinJoint *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB);
/// Allocate and initialize a pin joint.
CP_EXPORT cpConstraint* cpPinJointNew(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB);
/// Get the location of the first anchor relative to the first body.
CP_EXPORT cpVect cpPinJointGetAnchorA(const cpConstraint *constraint);
/// Set the location of the first anchor relative to the first body.
CP_EXPORT void cpPinJointSetAnchorA(cpConstraint *constraint, cpVect anchorA);
/// Get the location of the second anchor relative to the second body.
CP_EXPORT cpVect cpPinJointGetAnchorB(const cpConstraint *constraint);
/// Set the location of the second anchor relative to the second body.
CP_EXPORT void cpPinJointSetAnchorB(cpConstraint *constraint, cpVect anchorB);
/// Get the distance the joint will maintain between the two anchors.
CP_EXPORT cpFloat cpPinJointGetDist(const cpConstraint *constraint);
/// Set the distance the joint will maintain between the two anchors.
CP_EXPORT void cpPinJointSetDist(cpConstraint *constraint, cpFloat dist);
///@}

View File

@@ -0,0 +1,47 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/// @defgroup cpPivotJoint cpPivotJoint
/// @{
/// Check if a constraint is a slide joint.
CP_EXPORT cpBool cpConstraintIsPivotJoint(const cpConstraint *constraint);
/// Allocate a pivot joint
CP_EXPORT cpPivotJoint* cpPivotJointAlloc(void);
/// Initialize a pivot joint.
CP_EXPORT cpPivotJoint* cpPivotJointInit(cpPivotJoint *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB);
/// Allocate and initialize a pivot joint.
CP_EXPORT cpConstraint* cpPivotJointNew(cpBody *a, cpBody *b, cpVect pivot);
/// Allocate and initialize a pivot joint with specific anchors.
CP_EXPORT cpConstraint* cpPivotJointNew2(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB);
/// Get the location of the first anchor relative to the first body.
CP_EXPORT cpVect cpPivotJointGetAnchorA(const cpConstraint *constraint);
/// Set the location of the first anchor relative to the first body.
CP_EXPORT void cpPivotJointSetAnchorA(cpConstraint *constraint, cpVect anchorA);
/// Get the location of the second anchor relative to the second body.
CP_EXPORT cpVect cpPivotJointGetAnchorB(const cpConstraint *constraint);
/// Set the location of the second anchor relative to the second body.
CP_EXPORT void cpPivotJointSetAnchorB(cpConstraint *constraint, cpVect anchorB);
/// @}

View File

@@ -0,0 +1,56 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/// @defgroup cpPolyShape cpPolyShape
/// @{
/// Allocate a polygon shape.
CP_EXPORT cpPolyShape* cpPolyShapeAlloc(void);
/// Initialize a polygon shape with rounded corners.
/// A convex hull will be created from the vertexes.
CP_EXPORT cpPolyShape* cpPolyShapeInit(cpPolyShape *poly, cpBody *body, int count, const cpVect *verts, cpTransform transform, cpFloat radius);
/// Initialize a polygon shape with rounded corners.
/// The vertexes must be convex with a counter-clockwise winding.
CP_EXPORT cpPolyShape* cpPolyShapeInitRaw(cpPolyShape *poly, cpBody *body, int count, const cpVect *verts, cpFloat radius);
/// Allocate and initialize a polygon shape with rounded corners.
/// A convex hull will be created from the vertexes.
CP_EXPORT cpShape* cpPolyShapeNew(cpBody *body, int count, const cpVect *verts, cpTransform transform, cpFloat radius);
/// Allocate and initialize a polygon shape with rounded corners.
/// The vertexes must be convex with a counter-clockwise winding.
CP_EXPORT cpShape* cpPolyShapeNewRaw(cpBody *body, int count, const cpVect *verts, cpFloat radius);
/// Initialize a box shaped polygon shape with rounded corners.
CP_EXPORT cpPolyShape* cpBoxShapeInit(cpPolyShape *poly, cpBody *body, cpFloat width, cpFloat height, cpFloat radius);
/// Initialize an offset box shaped polygon shape with rounded corners.
CP_EXPORT cpPolyShape* cpBoxShapeInit2(cpPolyShape *poly, cpBody *body, cpBB box, cpFloat radius);
/// Allocate and initialize a box shaped polygon shape.
CP_EXPORT cpShape* cpBoxShapeNew(cpBody *body, cpFloat width, cpFloat height, cpFloat radius);
/// Allocate and initialize an offset box shaped polygon shape.
CP_EXPORT cpShape* cpBoxShapeNew2(cpBody *body, cpBB box, cpFloat radius);
/// Get the number of verts in a polygon shape.
CP_EXPORT int cpPolyShapeGetCount(const cpShape *shape);
/// Get the @c ith vertex of a polygon shape.
CP_EXPORT cpVect cpPolyShapeGetVert(const cpShape *shape, int index);
/// Get the radius of a polygon shape.
CP_EXPORT cpFloat cpPolyShapeGetRadius(const cpShape *shape);
/// @}

View File

@@ -0,0 +1,70 @@
// Copyright 2013 Howling Moon Software. All rights reserved.
// See http://chipmunk2d.net/legal.php for more information.
// Polylines are just arrays of vertexes.
// They are looped if the first vertex is equal to the last.
// cpPolyline structs are intended to be passed by value and destroyed when you are done with them.
typedef struct cpPolyline {
int count, capacity;
cpVect verts[];
} cpPolyline;
/// Destroy and free a polyline instance.
CP_EXPORT void cpPolylineFree(cpPolyline *line);
/// Returns true if the first vertex is equal to the last.
CP_EXPORT cpBool cpPolylineIsClosed(cpPolyline *line);
/**
Returns a copy of a polyline simplified by using the Douglas-Peucker algorithm.
This works very well on smooth or gently curved shapes, but not well on straight edged or angular shapes.
*/
CP_EXPORT cpPolyline *cpPolylineSimplifyCurves(cpPolyline *line, cpFloat tol);
/**
Returns a copy of a polyline simplified by discarding "flat" vertexes.
This works well on straight edged or angular shapes, not as well on smooth shapes.
*/
CP_EXPORT cpPolyline *cpPolylineSimplifyVertexes(cpPolyline *line, cpFloat tol);
/// Get the convex hull of a polyline as a looped polyline.
CP_EXPORT cpPolyline *cpPolylineToConvexHull(cpPolyline *line, cpFloat tol);
/// Polyline sets are collections of polylines, generally built by cpMarchSoft() or cpMarchHard().
typedef struct cpPolylineSet {
int count, capacity;
cpPolyline **lines;
} cpPolylineSet;
/// Allocate a new polyline set.
CP_EXPORT cpPolylineSet *cpPolylineSetAlloc(void);
/// Initialize a new polyline set.
CP_EXPORT cpPolylineSet *cpPolylineSetInit(cpPolylineSet *set);
/// Allocate and initialize a polyline set.
CP_EXPORT cpPolylineSet *cpPolylineSetNew(void);
/// Destroy a polyline set.
CP_EXPORT void cpPolylineSetDestroy(cpPolylineSet *set, cpBool freePolylines);
/// Destroy and free a polyline set.
CP_EXPORT void cpPolylineSetFree(cpPolylineSet *set, cpBool freePolylines);
/**
Add a line segment to a polyline set.
A segment will either start a new polyline, join two others, or add to or loop an existing polyline.
This is mostly intended to be used as a callback directly from cpMarchSoft() or cpMarchHard().
*/
CP_EXPORT void cpPolylineSetCollectSegment(cpVect v0, cpVect v1, cpPolylineSet *lines);
/**
Get an approximate convex decomposition from a polyline.
Returns a cpPolylineSet of convex hulls that match the original shape to within 'tol'.
NOTE: If the input is a self intersecting polygon, the output might end up overly simplified.
*/
CP_EXPORT cpPolylineSet *cpPolylineConvexDecomposition(cpPolyline *line, cpFloat tol);
#define cpPolylineConvexDecomposition_BETA cpPolylineConvexDecomposition

View File

@@ -0,0 +1,50 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/// @defgroup cpRatchetJoint cpRatchetJoint
/// @{
/// Check if a constraint is a damped rotary springs.
CP_EXPORT cpBool cpConstraintIsRatchetJoint(const cpConstraint *constraint);
/// Allocate a ratchet joint.
CP_EXPORT cpRatchetJoint* cpRatchetJointAlloc(void);
/// Initialize a ratched joint.
CP_EXPORT cpRatchetJoint* cpRatchetJointInit(cpRatchetJoint *joint, cpBody *a, cpBody *b, cpFloat phase, cpFloat ratchet);
/// Allocate and initialize a ratchet joint.
CP_EXPORT cpConstraint* cpRatchetJointNew(cpBody *a, cpBody *b, cpFloat phase, cpFloat ratchet);
/// Get the angle of the current ratchet tooth.
CP_EXPORT cpFloat cpRatchetJointGetAngle(const cpConstraint *constraint);
/// Set the angle of the current ratchet tooth.
CP_EXPORT void cpRatchetJointSetAngle(cpConstraint *constraint, cpFloat angle);
/// Get the phase offset of the ratchet.
CP_EXPORT cpFloat cpRatchetJointGetPhase(const cpConstraint *constraint);
/// Get the phase offset of the ratchet.
CP_EXPORT void cpRatchetJointSetPhase(cpConstraint *constraint, cpFloat phase);
/// Get the angular distance of each ratchet.
CP_EXPORT cpFloat cpRatchetJointGetRatchet(const cpConstraint *constraint);
/// Set the angular distance of each ratchet.
CP_EXPORT void cpRatchetJointSetRatchet(cpConstraint *constraint, cpFloat ratchet);
/// @}

View File

@@ -0,0 +1,11 @@
#include "chipmunk/cpVect.h"
// This is a private header for functions (currently just one) that need strict floating point results.
// It was easier to put this in it's own file than to fiddle with 4 different compiler specific pragmas or attributes.
// "Fast math" should be disabled here.
// Check if c is to the left of segment (a, b).
cpBool cpCheckPointGreater(const cpVect a, const cpVect b, const cpVect c);
// Check if p is behind one of v0 or v1 on axis n.
cpBool cpCheckAxis(cpVect v0, cpVect v1, cpVect p, cpVect n);

View File

@@ -0,0 +1,45 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/// @defgroup cpRotaryLimitJoint cpRotaryLimitJoint
/// @{
/// Check if a constraint is a damped rotary springs.
CP_EXPORT cpBool cpConstraintIsRotaryLimitJoint(const cpConstraint *constraint);
/// Allocate a damped rotary limit joint.
CP_EXPORT cpRotaryLimitJoint* cpRotaryLimitJointAlloc(void);
/// Initialize a damped rotary limit joint.
CP_EXPORT cpRotaryLimitJoint* cpRotaryLimitJointInit(cpRotaryLimitJoint *joint, cpBody *a, cpBody *b, cpFloat min, cpFloat max);
/// Allocate and initialize a damped rotary limit joint.
CP_EXPORT cpConstraint* cpRotaryLimitJointNew(cpBody *a, cpBody *b, cpFloat min, cpFloat max);
/// Get the minimum distance the joint will maintain between the two anchors.
CP_EXPORT cpFloat cpRotaryLimitJointGetMin(const cpConstraint *constraint);
/// Set the minimum distance the joint will maintain between the two anchors.
CP_EXPORT void cpRotaryLimitJointSetMin(cpConstraint *constraint, cpFloat min);
/// Get the maximum distance the joint will maintain between the two anchors.
CP_EXPORT cpFloat cpRotaryLimitJointGetMax(const cpConstraint *constraint);
/// Set the maximum distance the joint will maintain between the two anchors.
CP_EXPORT void cpRotaryLimitJointSetMax(cpConstraint *constraint, cpFloat max);
/// @}

View File

@@ -0,0 +1,199 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/// @defgroup cpShape cpShape
/// The cpShape struct defines the shape of a rigid body.
/// @{
/// Point query info struct.
typedef struct cpPointQueryInfo {
/// The nearest shape, NULL if no shape was within range.
const cpShape *shape;
/// The closest point on the shape's surface. (in world space coordinates)
cpVect point;
/// The distance to the point. The distance is negative if the point is inside the shape.
cpFloat distance;
/// The gradient of the signed distance function.
/// The value should be similar to info.p/info.d, but accurate even for very small values of info.d.
cpVect gradient;
} cpPointQueryInfo;
/// Segment query info struct.
typedef struct cpSegmentQueryInfo {
/// The shape that was hit, or NULL if no collision occured.
const cpShape *shape;
/// The point of impact.
cpVect point;
/// The normal of the surface hit.
cpVect normal;
/// The normalized distance along the query segment in the range [0, 1].
cpFloat alpha;
} cpSegmentQueryInfo;
/// Fast collision filtering type that is used to determine if two objects collide before calling collision or query callbacks.
typedef struct cpShapeFilter {
/// Two objects with the same non-zero group value do not collide.
/// This is generally used to group objects in a composite object together to disable self collisions.
cpGroup group;
/// A bitmask of user definable categories that this object belongs to.
/// The category/mask combinations of both objects in a collision must agree for a collision to occur.
cpBitmask categories;
/// A bitmask of user definable category types that this object object collides with.
/// The category/mask combinations of both objects in a collision must agree for a collision to occur.
cpBitmask mask;
} cpShapeFilter;
/// Collision filter value for a shape that will collide with anything except CP_SHAPE_FILTER_NONE.
static const cpShapeFilter CP_SHAPE_FILTER_ALL = {CP_NO_GROUP, CP_ALL_CATEGORIES, CP_ALL_CATEGORIES};
/// Collision filter value for a shape that does not collide with anything.
static const cpShapeFilter CP_SHAPE_FILTER_NONE = {CP_NO_GROUP, ~CP_ALL_CATEGORIES, ~CP_ALL_CATEGORIES};
/// Create a new collision filter.
static inline cpShapeFilter
cpShapeFilterNew(cpGroup group, cpBitmask categories, cpBitmask mask)
{
cpShapeFilter filter = {group, categories, mask};
return filter;
}
/// Destroy a shape.
CP_EXPORT void cpShapeDestroy(cpShape *shape);
/// Destroy and Free a shape.
CP_EXPORT void cpShapeFree(cpShape *shape);
/// Update, cache and return the bounding box of a shape based on the body it's attached to.
CP_EXPORT cpBB cpShapeCacheBB(cpShape *shape);
/// Update, cache and return the bounding box of a shape with an explicit transformation.
CP_EXPORT cpBB cpShapeUpdate(cpShape *shape, cpTransform transform);
/// Perform a nearest point query. It finds the closest point on the surface of shape to a specific point.
/// The value returned is the distance between the points. A negative distance means the point is inside the shape.
CP_EXPORT cpFloat cpShapePointQuery(const cpShape *shape, cpVect p, cpPointQueryInfo *out);
/// Perform a segment query against a shape. @c info must be a pointer to a valid cpSegmentQueryInfo structure.
CP_EXPORT cpBool cpShapeSegmentQuery(const cpShape *shape, cpVect a, cpVect b, cpFloat radius, cpSegmentQueryInfo *info);
/// Return contact information about two shapes.
CP_EXPORT cpContactPointSet cpShapesCollide(const cpShape *a, const cpShape *b);
/// The cpSpace this body is added to.
CP_EXPORT cpSpace* cpShapeGetSpace(const cpShape *shape);
/// The cpBody this shape is connected to.
CP_EXPORT cpBody* cpShapeGetBody(const cpShape *shape);
/// Set the cpBody this shape is connected to.
/// Can only be used if the shape is not currently added to a space.
CP_EXPORT void cpShapeSetBody(cpShape *shape, cpBody *body);
/// Get the mass of the shape if you are having Chipmunk calculate mass properties for you.
CP_EXPORT cpFloat cpShapeGetMass(cpShape *shape);
/// Set the mass of this shape to have Chipmunk calculate mass properties for you.
CP_EXPORT void cpShapeSetMass(cpShape *shape, cpFloat mass);
/// Get the density of the shape if you are having Chipmunk calculate mass properties for you.
CP_EXPORT cpFloat cpShapeGetDensity(cpShape *shape);
/// Set the density of this shape to have Chipmunk calculate mass properties for you.
CP_EXPORT void cpShapeSetDensity(cpShape *shape, cpFloat density);
/// Get the calculated moment of inertia for this shape.
CP_EXPORT cpFloat cpShapeGetMoment(cpShape *shape);
/// Get the calculated area of this shape.
CP_EXPORT cpFloat cpShapeGetArea(cpShape *shape);
/// Get the centroid of this shape.
CP_EXPORT cpVect cpShapeGetCenterOfGravity(cpShape *shape);
/// Get the bounding box that contains the shape given it's current position and angle.
CP_EXPORT cpBB cpShapeGetBB(const cpShape *shape);
/// Get if the shape is set to be a sensor or not.
CP_EXPORT cpBool cpShapeGetSensor(const cpShape *shape);
/// Set if the shape is a sensor or not.
CP_EXPORT void cpShapeSetSensor(cpShape *shape, cpBool sensor);
/// Get the elasticity of this shape.
CP_EXPORT cpFloat cpShapeGetElasticity(const cpShape *shape);
/// Set the elasticity of this shape.
CP_EXPORT void cpShapeSetElasticity(cpShape *shape, cpFloat elasticity);
/// Get the friction of this shape.
CP_EXPORT cpFloat cpShapeGetFriction(const cpShape *shape);
/// Set the friction of this shape.
CP_EXPORT void cpShapeSetFriction(cpShape *shape, cpFloat friction);
/// Get the surface velocity of this shape.
CP_EXPORT cpVect cpShapeGetSurfaceVelocity(const cpShape *shape);
/// Set the surface velocity of this shape.
CP_EXPORT void cpShapeSetSurfaceVelocity(cpShape *shape, cpVect surfaceVelocity);
/// Get the user definable data pointer of this shape.
CP_EXPORT cpDataPointer cpShapeGetUserData(const cpShape *shape);
/// Set the user definable data pointer of this shape.
CP_EXPORT void cpShapeSetUserData(cpShape *shape, cpDataPointer userData);
/// Set the collision type of this shape.
CP_EXPORT cpCollisionType cpShapeGetCollisionType(const cpShape *shape);
/// Get the collision type of this shape.
CP_EXPORT void cpShapeSetCollisionType(cpShape *shape, cpCollisionType collisionType);
/// Get the collision filtering parameters of this shape.
CP_EXPORT cpShapeFilter cpShapeGetFilter(const cpShape *shape);
/// Set the collision filtering parameters of this shape.
CP_EXPORT void cpShapeSetFilter(cpShape *shape, cpShapeFilter filter);
/// @}
/// @defgroup cpCircleShape cpCircleShape
/// Allocate a circle shape.
CP_EXPORT cpCircleShape* cpCircleShapeAlloc(void);
/// Initialize a circle shape.
CP_EXPORT cpCircleShape* cpCircleShapeInit(cpCircleShape *circle, cpBody *body, cpFloat radius, cpVect offset);
/// Allocate and initialize a circle shape.
CP_EXPORT cpShape* cpCircleShapeNew(cpBody *body, cpFloat radius, cpVect offset);
/// Get the offset of a circle shape.
CP_EXPORT cpVect cpCircleShapeGetOffset(const cpShape *shape);
/// Get the radius of a circle shape.
CP_EXPORT cpFloat cpCircleShapeGetRadius(const cpShape *shape);
/// @}
/// @defgroup cpSegmentShape cpSegmentShape
/// Allocate a segment shape.
CP_EXPORT cpSegmentShape* cpSegmentShapeAlloc(void);
/// Initialize a segment shape.
CP_EXPORT cpSegmentShape* cpSegmentShapeInit(cpSegmentShape *seg, cpBody *body, cpVect a, cpVect b, cpFloat radius);
/// Allocate and initialize a segment shape.
CP_EXPORT cpShape* cpSegmentShapeNew(cpBody *body, cpVect a, cpVect b, cpFloat radius);
/// Let Chipmunk know about the geometry of adjacent segments to avoid colliding with endcaps.
CP_EXPORT void cpSegmentShapeSetNeighbors(cpShape *shape, cpVect prev, cpVect next);
/// Get the first endpoint of a segment shape.
CP_EXPORT cpVect cpSegmentShapeGetA(const cpShape *shape);
/// Get the second endpoint of a segment shape.
CP_EXPORT cpVect cpSegmentShapeGetB(const cpShape *shape);
/// Get the normal of a segment shape.
CP_EXPORT cpVect cpSegmentShapeGetNormal(const cpShape *shape);
/// Get the first endpoint of a segment shape.
CP_EXPORT cpFloat cpSegmentShapeGetRadius(const cpShape *shape);
/// @}

View File

@@ -0,0 +1,43 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/// @defgroup cpSimpleMotor cpSimpleMotor
/// @{
/// Opaque struct type for damped rotary springs.
typedef struct cpSimpleMotor cpSimpleMotor;
/// Check if a constraint is a damped rotary springs.
CP_EXPORT cpBool cpConstraintIsSimpleMotor(const cpConstraint *constraint);
/// Allocate a simple motor.
CP_EXPORT cpSimpleMotor* cpSimpleMotorAlloc(void);
/// initialize a simple motor.
CP_EXPORT cpSimpleMotor* cpSimpleMotorInit(cpSimpleMotor *joint, cpBody *a, cpBody *b, cpFloat rate);
/// Allocate and initialize a simple motor.
CP_EXPORT cpConstraint* cpSimpleMotorNew(cpBody *a, cpBody *b, cpFloat rate);
/// Get the rate of the motor.
CP_EXPORT cpFloat cpSimpleMotorGetRate(const cpConstraint *constraint);
/// Set the rate of the motor.
CP_EXPORT void cpSimpleMotorSetRate(cpConstraint *constraint, cpFloat rate);
/// @}

View File

@@ -0,0 +1,55 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/// @defgroup cpSlideJoint cpSlideJoint
/// @{
/// Check if a constraint is a slide joint.
CP_EXPORT cpBool cpConstraintIsSlideJoint(const cpConstraint *constraint);
/// Allocate a slide joint.
CP_EXPORT cpSlideJoint* cpSlideJointAlloc(void);
/// Initialize a slide joint.
CP_EXPORT cpSlideJoint* cpSlideJointInit(cpSlideJoint *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat min, cpFloat max);
/// Allocate and initialize a slide joint.
CP_EXPORT cpConstraint* cpSlideJointNew(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat min, cpFloat max);
/// Get the location of the first anchor relative to the first body.
CP_EXPORT cpVect cpSlideJointGetAnchorA(const cpConstraint *constraint);
/// Set the location of the first anchor relative to the first body.
CP_EXPORT void cpSlideJointSetAnchorA(cpConstraint *constraint, cpVect anchorA);
/// Get the location of the second anchor relative to the second body.
CP_EXPORT cpVect cpSlideJointGetAnchorB(const cpConstraint *constraint);
/// Set the location of the second anchor relative to the second body.
CP_EXPORT void cpSlideJointSetAnchorB(cpConstraint *constraint, cpVect anchorB);
/// Get the minimum distance the joint will maintain between the two anchors.
CP_EXPORT cpFloat cpSlideJointGetMin(const cpConstraint *constraint);
/// Set the minimum distance the joint will maintain between the two anchors.
CP_EXPORT void cpSlideJointSetMin(cpConstraint *constraint, cpFloat min);
/// Get the maximum distance the joint will maintain between the two anchors.
CP_EXPORT cpFloat cpSlideJointGetMax(const cpConstraint *constraint);
/// Set the maximum distance the joint will maintain between the two anchors.
CP_EXPORT void cpSlideJointSetMax(cpConstraint *constraint, cpFloat max);
/// @}

View File

@@ -0,0 +1,319 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/// @defgroup cpSpace cpSpace
/// @{
//MARK: Definitions
/// Collision begin event function callback type.
/// Returning false from a begin callback causes the collision to be ignored until
/// the the separate callback is called when the objects stop colliding.
typedef cpBool (*cpCollisionBeginFunc)(cpArbiter *arb, cpSpace *space, cpDataPointer userData);
/// Collision pre-solve event function callback type.
/// Returning false from a pre-step callback causes the collision to be ignored until the next step.
typedef cpBool (*cpCollisionPreSolveFunc)(cpArbiter *arb, cpSpace *space, cpDataPointer userData);
/// Collision post-solve event function callback type.
typedef void (*cpCollisionPostSolveFunc)(cpArbiter *arb, cpSpace *space, cpDataPointer userData);
/// Collision separate event function callback type.
typedef void (*cpCollisionSeparateFunc)(cpArbiter *arb, cpSpace *space, cpDataPointer userData);
/// Struct that holds function callback pointers to configure custom collision handling.
/// Collision handlers have a pair of types; when a collision occurs between two shapes that have these types, the collision handler functions are triggered.
struct cpCollisionHandler {
/// Collision type identifier of the first shape that this handler recognizes.
/// In the collision handler callback, the shape with this type will be the first argument. Read only.
const cpCollisionType typeA;
/// Collision type identifier of the second shape that this handler recognizes.
/// In the collision handler callback, the shape with this type will be the second argument. Read only.
const cpCollisionType typeB;
/// This function is called when two shapes with types that match this collision handler begin colliding.
cpCollisionBeginFunc beginFunc;
/// This function is called each step when two shapes with types that match this collision handler are colliding.
/// It's called before the collision solver runs so that you can affect a collision's outcome.
cpCollisionPreSolveFunc preSolveFunc;
/// This function is called each step when two shapes with types that match this collision handler are colliding.
/// It's called after the collision solver runs so that you can read back information about the collision to trigger events in your game.
cpCollisionPostSolveFunc postSolveFunc;
/// This function is called when two shapes with types that match this collision handler stop colliding.
cpCollisionSeparateFunc separateFunc;
/// This is a user definable context pointer that is passed to all of the collision handler functions.
cpDataPointer userData;
};
// TODO: Make timestep a parameter?
//MARK: Memory and Initialization
/// Allocate a cpSpace.
CP_EXPORT cpSpace* cpSpaceAlloc(void);
/// Initialize a cpSpace.
CP_EXPORT cpSpace* cpSpaceInit(cpSpace *space);
/// Allocate and initialize a cpSpace.
CP_EXPORT cpSpace* cpSpaceNew(void);
/// Destroy a cpSpace.
CP_EXPORT void cpSpaceDestroy(cpSpace *space);
/// Destroy and free a cpSpace.
CP_EXPORT void cpSpaceFree(cpSpace *space);
//MARK: Properties
/// Number of iterations to use in the impulse solver to solve contacts and other constraints.
CP_EXPORT int cpSpaceGetIterations(const cpSpace *space);
CP_EXPORT void cpSpaceSetIterations(cpSpace *space, int iterations);
/// Gravity to pass to rigid bodies when integrating velocity.
CP_EXPORT cpVect cpSpaceGetGravity(const cpSpace *space);
CP_EXPORT void cpSpaceSetGravity(cpSpace *space, cpVect gravity);
/// Damping rate expressed as the fraction of velocity bodies retain each second.
/// A value of 0.9 would mean that each body's velocity will drop 10% per second.
/// The default value is 1.0, meaning no damping is applied.
/// @note This damping value is different than those of cpDampedSpring and cpDampedRotarySpring.
CP_EXPORT cpFloat cpSpaceGetDamping(const cpSpace *space);
CP_EXPORT void cpSpaceSetDamping(cpSpace *space, cpFloat damping);
/// Speed threshold for a body to be considered idle.
/// The default value of 0 means to let the space guess a good threshold based on gravity.
CP_EXPORT cpFloat cpSpaceGetIdleSpeedThreshold(const cpSpace *space);
CP_EXPORT void cpSpaceSetIdleSpeedThreshold(cpSpace *space, cpFloat idleSpeedThreshold);
/// Time a group of bodies must remain idle in order to fall asleep.
/// Enabling sleeping also implicitly enables the the contact graph.
/// The default value of INFINITY disables the sleeping algorithm.
CP_EXPORT cpFloat cpSpaceGetSleepTimeThreshold(const cpSpace *space);
CP_EXPORT void cpSpaceSetSleepTimeThreshold(cpSpace *space, cpFloat sleepTimeThreshold);
/// Amount of encouraged penetration between colliding shapes.
/// Used to reduce oscillating contacts and keep the collision cache warm.
/// Defaults to 0.1. If you have poor simulation quality,
/// increase this number as much as possible without allowing visible amounts of overlap.
CP_EXPORT cpFloat cpSpaceGetCollisionSlop(const cpSpace *space);
CP_EXPORT void cpSpaceSetCollisionSlop(cpSpace *space, cpFloat collisionSlop);
/// Determines how fast overlapping shapes are pushed apart.
/// Expressed as a fraction of the error remaining after each second.
/// Defaults to pow(1.0 - 0.1, 60.0) meaning that Chipmunk fixes 10% of overlap each frame at 60Hz.
CP_EXPORT cpFloat cpSpaceGetCollisionBias(const cpSpace *space);
CP_EXPORT void cpSpaceSetCollisionBias(cpSpace *space, cpFloat collisionBias);
/// Number of frames that contact information should persist.
/// Defaults to 3. There is probably never a reason to change this value.
CP_EXPORT cpTimestamp cpSpaceGetCollisionPersistence(const cpSpace *space);
CP_EXPORT void cpSpaceSetCollisionPersistence(cpSpace *space, cpTimestamp collisionPersistence);
/// User definable data pointer.
/// Generally this points to your game's controller or game state
/// class so you can access it when given a cpSpace reference in a callback.
CP_EXPORT cpDataPointer cpSpaceGetUserData(const cpSpace *space);
CP_EXPORT void cpSpaceSetUserData(cpSpace *space, cpDataPointer userData);
/// The Space provided static body for a given cpSpace.
/// This is merely provided for convenience and you are not required to use it.
CP_EXPORT cpBody* cpSpaceGetStaticBody(const cpSpace *space);
/// Returns the current (or most recent) time step used with the given space.
/// Useful from callbacks if your time step is not a compile-time global.
CP_EXPORT cpFloat cpSpaceGetCurrentTimeStep(const cpSpace *space);
/// returns true from inside a callback when objects cannot be added/removed.
CP_EXPORT cpBool cpSpaceIsLocked(cpSpace *space);
//MARK: Collision Handlers
/// Create or return the existing collision handler that is called for all collisions that are not handled by a more specific collision handler.
CP_EXPORT cpCollisionHandler *cpSpaceAddDefaultCollisionHandler(cpSpace *space);
/// Create or return the existing collision handler for the specified pair of collision types.
/// If wildcard handlers are used with either of the collision types, it's the responibility of the custom handler to invoke the wildcard handlers.
CP_EXPORT cpCollisionHandler *cpSpaceAddCollisionHandler(cpSpace *space, cpCollisionType a, cpCollisionType b);
/// Create or return the existing wildcard collision handler for the specified type.
CP_EXPORT cpCollisionHandler *cpSpaceAddWildcardHandler(cpSpace *space, cpCollisionType type);
//MARK: Add/Remove objects
/// Add a collision shape to the simulation.
/// If the shape is attached to a static body, it will be added as a static shape.
CP_EXPORT cpShape* cpSpaceAddShape(cpSpace *space, cpShape *shape);
/// Add a rigid body to the simulation.
CP_EXPORT cpBody* cpSpaceAddBody(cpSpace *space, cpBody *body);
/// Add a constraint to the simulation.
CP_EXPORT cpConstraint* cpSpaceAddConstraint(cpSpace *space, cpConstraint *constraint);
/// Remove a collision shape from the simulation.
CP_EXPORT void cpSpaceRemoveShape(cpSpace *space, cpShape *shape);
/// Remove a rigid body from the simulation.
CP_EXPORT void cpSpaceRemoveBody(cpSpace *space, cpBody *body);
/// Remove a constraint from the simulation.
CP_EXPORT void cpSpaceRemoveConstraint(cpSpace *space, cpConstraint *constraint);
/// Test if a collision shape has been added to the space.
CP_EXPORT cpBool cpSpaceContainsShape(cpSpace *space, cpShape *shape);
/// Test if a rigid body has been added to the space.
CP_EXPORT cpBool cpSpaceContainsBody(cpSpace *space, cpBody *body);
/// Test if a constraint has been added to the space.
CP_EXPORT cpBool cpSpaceContainsConstraint(cpSpace *space, cpConstraint *constraint);
//MARK: Post-Step Callbacks
/// Post Step callback function type.
typedef void (*cpPostStepFunc)(cpSpace *space, void *key, void *data);
/// Schedule a post-step callback to be called when cpSpaceStep() finishes.
/// You can only register one callback per unique value for @c key.
/// Returns true only if @c key has never been scheduled before.
/// It's possible to pass @c NULL for @c func if you only want to mark @c key as being used.
CP_EXPORT cpBool cpSpaceAddPostStepCallback(cpSpace *space, cpPostStepFunc func, void *key, void *data);
//MARK: Queries
// TODO: Queries and iterators should take a cpSpace parametery.
// TODO: They should also be abortable.
/// Nearest point query callback function type.
typedef void (*cpSpacePointQueryFunc)(cpShape *shape, cpVect point, cpFloat distance, cpVect gradient, void *data);
/// Query the space at a point and call @c func for each shape found.
CP_EXPORT void cpSpacePointQuery(cpSpace *space, cpVect point, cpFloat maxDistance, cpShapeFilter filter, cpSpacePointQueryFunc func, void *data);
/// Query the space at a point and return the nearest shape found. Returns NULL if no shapes were found.
CP_EXPORT cpShape *cpSpacePointQueryNearest(cpSpace *space, cpVect point, cpFloat maxDistance, cpShapeFilter filter, cpPointQueryInfo *out);
/// Segment query callback function type.
typedef void (*cpSpaceSegmentQueryFunc)(cpShape *shape, cpVect point, cpVect normal, cpFloat alpha, void *data);
/// Perform a directed line segment query (like a raycast) against the space calling @c func for each shape intersected.
CP_EXPORT void cpSpaceSegmentQuery(cpSpace *space, cpVect start, cpVect end, cpFloat radius, cpShapeFilter filter, cpSpaceSegmentQueryFunc func, void *data);
/// Perform a directed line segment query (like a raycast) against the space and return the first shape hit. Returns NULL if no shapes were hit.
CP_EXPORT cpShape *cpSpaceSegmentQueryFirst(cpSpace *space, cpVect start, cpVect end, cpFloat radius, cpShapeFilter filter, cpSegmentQueryInfo *out);
/// Rectangle Query callback function type.
typedef void (*cpSpaceBBQueryFunc)(cpShape *shape, void *data);
/// Perform a fast rectangle query on the space calling @c func for each shape found.
/// Only the shape's bounding boxes are checked for overlap, not their full shape.
CP_EXPORT void cpSpaceBBQuery(cpSpace *space, cpBB bb, cpShapeFilter filter, cpSpaceBBQueryFunc func, void *data);
/// Shape query callback function type.
typedef void (*cpSpaceShapeQueryFunc)(cpShape *shape, cpContactPointSet *points, void *data);
/// Query a space for any shapes overlapping the given shape and call @c func for each shape found.
CP_EXPORT cpBool cpSpaceShapeQuery(cpSpace *space, cpShape *shape, cpSpaceShapeQueryFunc func, void *data);
//MARK: Iteration
/// Space/body iterator callback function type.
typedef void (*cpSpaceBodyIteratorFunc)(cpBody *body, void *data);
/// Call @c func for each body in the space.
CP_EXPORT void cpSpaceEachBody(cpSpace *space, cpSpaceBodyIteratorFunc func, void *data);
/// Space/body iterator callback function type.
typedef void (*cpSpaceShapeIteratorFunc)(cpShape *shape, void *data);
/// Call @c func for each shape in the space.
CP_EXPORT void cpSpaceEachShape(cpSpace *space, cpSpaceShapeIteratorFunc func, void *data);
/// Space/constraint iterator callback function type.
typedef void (*cpSpaceConstraintIteratorFunc)(cpConstraint *constraint, void *data);
/// Call @c func for each shape in the space.
CP_EXPORT void cpSpaceEachConstraint(cpSpace *space, cpSpaceConstraintIteratorFunc func, void *data);
//MARK: Indexing
/// Update the collision detection info for the static shapes in the space.
CP_EXPORT void cpSpaceReindexStatic(cpSpace *space);
/// Update the collision detection data for a specific shape in the space.
CP_EXPORT void cpSpaceReindexShape(cpSpace *space, cpShape *shape);
/// Update the collision detection data for all shapes attached to a body.
CP_EXPORT void cpSpaceReindexShapesForBody(cpSpace *space, cpBody *body);
/// Switch the space to use a spatial has as it's spatial index.
CP_EXPORT void cpSpaceUseSpatialHash(cpSpace *space, cpFloat dim, int count);
//MARK: Time Stepping
/// Step the space forward in time by @c dt.
CP_EXPORT void cpSpaceStep(cpSpace *space, cpFloat dt);
//MARK: Debug API
#ifndef CP_SPACE_DISABLE_DEBUG_API
/// Color type to use with the space debug drawing API.
typedef struct cpSpaceDebugColor {
float r, g, b, a;
} cpSpaceDebugColor;
/// Callback type for a function that draws a filled, stroked circle.
typedef void (*cpSpaceDebugDrawCircleImpl)(cpVect pos, cpFloat angle, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor, cpDataPointer data);
/// Callback type for a function that draws a line segment.
typedef void (*cpSpaceDebugDrawSegmentImpl)(cpVect a, cpVect b, cpSpaceDebugColor color, cpDataPointer data);
/// Callback type for a function that draws a thick line segment.
typedef void (*cpSpaceDebugDrawFatSegmentImpl)(cpVect a, cpVect b, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor, cpDataPointer data);
/// Callback type for a function that draws a convex polygon.
typedef void (*cpSpaceDebugDrawPolygonImpl)(int count, const cpVect *verts, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor, cpDataPointer data);
/// Callback type for a function that draws a dot.
typedef void (*cpSpaceDebugDrawDotImpl)(cpFloat size, cpVect pos, cpSpaceDebugColor color, cpDataPointer data);
/// Callback type for a function that returns a color for a given shape. This gives you an opportunity to color shapes based on how they are used in your engine.
typedef cpSpaceDebugColor (*cpSpaceDebugDrawColorForShapeImpl)(cpShape *shape, cpDataPointer data);
typedef enum cpSpaceDebugDrawFlags {
CP_SPACE_DEBUG_DRAW_SHAPES = 1<<0,
CP_SPACE_DEBUG_DRAW_CONSTRAINTS = 1<<1,
CP_SPACE_DEBUG_DRAW_COLLISION_POINTS = 1<<2,
} cpSpaceDebugDrawFlags;
/// Struct used with cpSpaceDebugDraw() containing drawing callbacks and other drawing settings.
typedef struct cpSpaceDebugDrawOptions {
/// Function that will be invoked to draw circles.
cpSpaceDebugDrawCircleImpl drawCircle;
/// Function that will be invoked to draw line segments.
cpSpaceDebugDrawSegmentImpl drawSegment;
/// Function that will be invoked to draw thick line segments.
cpSpaceDebugDrawFatSegmentImpl drawFatSegment;
/// Function that will be invoked to draw convex polygons.
cpSpaceDebugDrawPolygonImpl drawPolygon;
/// Function that will be invoked to draw dots.
cpSpaceDebugDrawDotImpl drawDot;
/// Flags that request which things to draw (collision shapes, constraints, contact points).
cpSpaceDebugDrawFlags flags;
/// Outline color passed to the drawing function.
cpSpaceDebugColor shapeOutlineColor;
/// Function that decides what fill color to draw shapes using.
cpSpaceDebugDrawColorForShapeImpl colorForShape;
/// Color passed to drawing functions for constraints.
cpSpaceDebugColor constraintColor;
/// Color passed to drawing functions for collision points.
cpSpaceDebugColor collisionPointColor;
/// User defined context pointer passed to all of the callback functions as the 'data' argument.
cpDataPointer data;
} cpSpaceDebugDrawOptions;
/// Debug draw the current state of the space using the supplied drawing options.
CP_EXPORT void cpSpaceDebugDraw(cpSpace *space, cpSpaceDebugDrawOptions *options);
#endif
/// @}

View File

@@ -0,0 +1,227 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
@defgroup cpSpatialIndex cpSpatialIndex
Spatial indexes are data structures that are used to accelerate collision detection
and spatial queries. Chipmunk provides a number of spatial index algorithms to pick from
and they are programmed in a generic way so that you can use them for holding more than
just cpShape structs.
It works by using @c void pointers to the objects you add and using a callback to ask your code
for bounding boxes when it needs them. Several types of queries can be performed an index as well
as reindexing and full collision information. All communication to the spatial indexes is performed
through callback functions.
Spatial indexes should be treated as opaque structs.
This meanns you shouldn't be reading any of the struct fields.
@{
*/
//MARK: Spatial Index
/// Spatial index bounding box callback function type.
/// The spatial index calls this function and passes you a pointer to an object you added
/// when it needs to get the bounding box associated with that object.
typedef cpBB (*cpSpatialIndexBBFunc)(void *obj);
/// Spatial index/object iterator callback function type.
typedef void (*cpSpatialIndexIteratorFunc)(void *obj, void *data);
/// Spatial query callback function type.
typedef cpCollisionID (*cpSpatialIndexQueryFunc)(void *obj1, void *obj2, cpCollisionID id, void *data);
/// Spatial segment query callback function type.
typedef cpFloat (*cpSpatialIndexSegmentQueryFunc)(void *obj1, void *obj2, void *data);
typedef struct cpSpatialIndexClass cpSpatialIndexClass;
typedef struct cpSpatialIndex cpSpatialIndex;
/// @private
struct cpSpatialIndex {
cpSpatialIndexClass *klass;
cpSpatialIndexBBFunc bbfunc;
cpSpatialIndex *staticIndex, *dynamicIndex;
};
//MARK: Spatial Hash
typedef struct cpSpaceHash cpSpaceHash;
/// Allocate a spatial hash.
CP_EXPORT cpSpaceHash* cpSpaceHashAlloc(void);
/// Initialize a spatial hash.
CP_EXPORT cpSpatialIndex* cpSpaceHashInit(cpSpaceHash *hash, cpFloat celldim, int numcells, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex);
/// Allocate and initialize a spatial hash.
CP_EXPORT cpSpatialIndex* cpSpaceHashNew(cpFloat celldim, int cells, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex);
/// Change the cell dimensions and table size of the spatial hash to tune it.
/// The cell dimensions should roughly match the average size of your objects
/// and the table size should be ~10 larger than the number of objects inserted.
/// Some trial and error is required to find the optimum numbers for efficiency.
CP_EXPORT void cpSpaceHashResize(cpSpaceHash *hash, cpFloat celldim, int numcells);
//MARK: AABB Tree
typedef struct cpBBTree cpBBTree;
/// Allocate a bounding box tree.
CP_EXPORT cpBBTree* cpBBTreeAlloc(void);
/// Initialize a bounding box tree.
CP_EXPORT cpSpatialIndex* cpBBTreeInit(cpBBTree *tree, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex);
/// Allocate and initialize a bounding box tree.
CP_EXPORT cpSpatialIndex* cpBBTreeNew(cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex);
/// Perform a static top down optimization of the tree.
CP_EXPORT void cpBBTreeOptimize(cpSpatialIndex *index);
/// Bounding box tree velocity callback function.
/// This function should return an estimate for the object's velocity.
typedef cpVect (*cpBBTreeVelocityFunc)(void *obj);
/// Set the velocity function for the bounding box tree to enable temporal coherence.
CP_EXPORT void cpBBTreeSetVelocityFunc(cpSpatialIndex *index, cpBBTreeVelocityFunc func);
//MARK: Single Axis Sweep
typedef struct cpSweep1D cpSweep1D;
/// Allocate a 1D sort and sweep broadphase.
CP_EXPORT cpSweep1D* cpSweep1DAlloc(void);
/// Initialize a 1D sort and sweep broadphase.
CP_EXPORT cpSpatialIndex* cpSweep1DInit(cpSweep1D *sweep, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex);
/// Allocate and initialize a 1D sort and sweep broadphase.
CP_EXPORT cpSpatialIndex* cpSweep1DNew(cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex);
//MARK: Spatial Index Implementation
typedef void (*cpSpatialIndexDestroyImpl)(cpSpatialIndex *index);
typedef int (*cpSpatialIndexCountImpl)(cpSpatialIndex *index);
typedef void (*cpSpatialIndexEachImpl)(cpSpatialIndex *index, cpSpatialIndexIteratorFunc func, void *data);
typedef cpBool (*cpSpatialIndexContainsImpl)(cpSpatialIndex *index, void *obj, cpHashValue hashid);
typedef void (*cpSpatialIndexInsertImpl)(cpSpatialIndex *index, void *obj, cpHashValue hashid);
typedef void (*cpSpatialIndexRemoveImpl)(cpSpatialIndex *index, void *obj, cpHashValue hashid);
typedef void (*cpSpatialIndexReindexImpl)(cpSpatialIndex *index);
typedef void (*cpSpatialIndexReindexObjectImpl)(cpSpatialIndex *index, void *obj, cpHashValue hashid);
typedef void (*cpSpatialIndexReindexQueryImpl)(cpSpatialIndex *index, cpSpatialIndexQueryFunc func, void *data);
typedef void (*cpSpatialIndexQueryImpl)(cpSpatialIndex *index, void *obj, cpBB bb, cpSpatialIndexQueryFunc func, void *data);
typedef void (*cpSpatialIndexSegmentQueryImpl)(cpSpatialIndex *index, void *obj, cpVect a, cpVect b, cpFloat t_exit, cpSpatialIndexSegmentQueryFunc func, void *data);
struct cpSpatialIndexClass {
cpSpatialIndexDestroyImpl destroy;
cpSpatialIndexCountImpl count;
cpSpatialIndexEachImpl each;
cpSpatialIndexContainsImpl contains;
cpSpatialIndexInsertImpl insert;
cpSpatialIndexRemoveImpl remove;
cpSpatialIndexReindexImpl reindex;
cpSpatialIndexReindexObjectImpl reindexObject;
cpSpatialIndexReindexQueryImpl reindexQuery;
cpSpatialIndexQueryImpl query;
cpSpatialIndexSegmentQueryImpl segmentQuery;
};
/// Destroy and free a spatial index.
CP_EXPORT void cpSpatialIndexFree(cpSpatialIndex *index);
/// Collide the objects in @c dynamicIndex against the objects in @c staticIndex using the query callback function.
CP_EXPORT void cpSpatialIndexCollideStatic(cpSpatialIndex *dynamicIndex, cpSpatialIndex *staticIndex, cpSpatialIndexQueryFunc func, void *data);
/// Destroy a spatial index.
static inline void cpSpatialIndexDestroy(cpSpatialIndex *index)
{
if(index->klass) index->klass->destroy(index);
}
/// Get the number of objects in the spatial index.
static inline int cpSpatialIndexCount(cpSpatialIndex *index)
{
return index->klass->count(index);
}
/// Iterate the objects in the spatial index. @c func will be called once for each object.
static inline void cpSpatialIndexEach(cpSpatialIndex *index, cpSpatialIndexIteratorFunc func, void *data)
{
index->klass->each(index, func, data);
}
/// Returns true if the spatial index contains the given object.
/// Most spatial indexes use hashed storage, so you must provide a hash value too.
static inline cpBool cpSpatialIndexContains(cpSpatialIndex *index, void *obj, cpHashValue hashid)
{
return index->klass->contains(index, obj, hashid);
}
/// Add an object to a spatial index.
/// Most spatial indexes use hashed storage, so you must provide a hash value too.
static inline void cpSpatialIndexInsert(cpSpatialIndex *index, void *obj, cpHashValue hashid)
{
index->klass->insert(index, obj, hashid);
}
/// Remove an object from a spatial index.
/// Most spatial indexes use hashed storage, so you must provide a hash value too.
static inline void cpSpatialIndexRemove(cpSpatialIndex *index, void *obj, cpHashValue hashid)
{
index->klass->remove(index, obj, hashid);
}
/// Perform a full reindex of a spatial index.
static inline void cpSpatialIndexReindex(cpSpatialIndex *index)
{
index->klass->reindex(index);
}
/// Reindex a single object in the spatial index.
static inline void cpSpatialIndexReindexObject(cpSpatialIndex *index, void *obj, cpHashValue hashid)
{
index->klass->reindexObject(index, obj, hashid);
}
/// Perform a rectangle query against the spatial index, calling @c func for each potential match.
static inline void cpSpatialIndexQuery(cpSpatialIndex *index, void *obj, cpBB bb, cpSpatialIndexQueryFunc func, void *data)
{
index->klass->query(index, obj, bb, func, data);
}
/// Perform a segment query against the spatial index, calling @c func for each potential match.
static inline void cpSpatialIndexSegmentQuery(cpSpatialIndex *index, void *obj, cpVect a, cpVect b, cpFloat t_exit, cpSpatialIndexSegmentQueryFunc func, void *data)
{
index->klass->segmentQuery(index, obj, a, b, t_exit, func, data);
}
/// Simultaneously reindex and find all colliding objects.
/// @c func will be called once for each potentially overlapping pair of objects found.
/// If the spatial index was initialized with a static index, it will collide it's objects against that as well.
static inline void cpSpatialIndexReindexQuery(cpSpatialIndex *index, cpSpatialIndexQueryFunc func, void *data)
{
index->klass->reindexQuery(index, func, data);
}
///@}

View File

@@ -0,0 +1,198 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef CHIPMUNK_TRANSFORM_H
#define CHIPMUNK_TRANSFORM_H
#include "chipmunk_types.h"
#include "cpVect.h"
#include "cpBB.h"
/// Identity transform matrix.
static const cpTransform cpTransformIdentity = {1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f};
/// Construct a new transform matrix.
/// (a, b) is the x basis vector.
/// (c, d) is the y basis vector.
/// (tx, ty) is the translation.
static inline cpTransform
cpTransformNew(cpFloat a, cpFloat b, cpFloat c, cpFloat d, cpFloat tx, cpFloat ty)
{
cpTransform t = {a, b, c, d, tx, ty};
return t;
}
/// Construct a new transform matrix in transposed order.
static inline cpTransform
cpTransformNewTranspose(cpFloat a, cpFloat c, cpFloat tx, cpFloat b, cpFloat d, cpFloat ty)
{
cpTransform t = {a, b, c, d, tx, ty};
return t;
}
/// Get the inverse of a transform matrix.
static inline cpTransform
cpTransformInverse(cpTransform t)
{
cpFloat inv_det = 1.0/(t.a*t.d - t.c*t.b);
return cpTransformNewTranspose(
t.d*inv_det, -t.c*inv_det, (t.c*t.ty - t.tx*t.d)*inv_det,
-t.b*inv_det, t.a*inv_det, (t.tx*t.b - t.a*t.ty)*inv_det
);
}
/// Multiply two transformation matrices.
static inline cpTransform
cpTransformMult(cpTransform t1, cpTransform t2)
{
return cpTransformNewTranspose(
t1.a*t2.a + t1.c*t2.b, t1.a*t2.c + t1.c*t2.d, t1.a*t2.tx + t1.c*t2.ty + t1.tx,
t1.b*t2.a + t1.d*t2.b, t1.b*t2.c + t1.d*t2.d, t1.b*t2.tx + t1.d*t2.ty + t1.ty
);
}
/// Transform an absolute point. (i.e. a vertex)
static inline cpVect
cpTransformPoint(cpTransform t, cpVect p)
{
return cpv(t.a*p.x + t.c*p.y + t.tx, t.b*p.x + t.d*p.y + t.ty);
}
/// Transform a vector (i.e. a normal)
static inline cpVect
cpTransformVect(cpTransform t, cpVect v)
{
return cpv(t.a*v.x + t.c*v.y, t.b*v.x + t.d*v.y);
}
/// Transform a cpBB.
static inline cpBB
cpTransformbBB(cpTransform t, cpBB bb)
{
cpVect center = cpBBCenter(bb);
cpFloat hw = (bb.r - bb.l)*0.5;
cpFloat hh = (bb.t - bb.b)*0.5;
cpFloat a = t.a*hw, b = t.c*hh, d = t.b*hw, e = t.d*hh;
cpFloat hw_max = cpfmax(cpfabs(a + b), cpfabs(a - b));
cpFloat hh_max = cpfmax(cpfabs(d + e), cpfabs(d - e));
return cpBBNewForExtents(cpTransformPoint(t, center), hw_max, hh_max);
}
/// Create a transation matrix.
static inline cpTransform
cpTransformTranslate(cpVect translate)
{
return cpTransformNewTranspose(
1.0, 0.0, translate.x,
0.0, 1.0, translate.y
);
}
/// Create a scale matrix.
static inline cpTransform
cpTransformScale(cpFloat scaleX, cpFloat scaleY)
{
return cpTransformNewTranspose(
scaleX, 0.0, 0.0,
0.0, scaleY, 0.0
);
}
/// Create a rotation matrix.
static inline cpTransform
cpTransformRotate(cpFloat radians)
{
cpVect rot = cpvforangle(radians);
return cpTransformNewTranspose(
rot.x, -rot.y, 0.0,
rot.y, rot.x, 0.0
);
}
/// Create a rigid transformation matrix. (transation + rotation)
static inline cpTransform
cpTransformRigid(cpVect translate, cpFloat radians)
{
cpVect rot = cpvforangle(radians);
return cpTransformNewTranspose(
rot.x, -rot.y, translate.x,
rot.y, rot.x, translate.y
);
}
/// Fast inverse of a rigid transformation matrix.
static inline cpTransform
cpTransformRigidInverse(cpTransform t)
{
return cpTransformNewTranspose(
t.d, -t.c, (t.c*t.ty - t.tx*t.d),
-t.b, t.a, (t.tx*t.b - t.a*t.ty)
);
}
//MARK: Miscellaneous (but useful) transformation matrices.
// See source for documentation...
static inline cpTransform
cpTransformWrap(cpTransform outer, cpTransform inner)
{
return cpTransformMult(cpTransformInverse(outer), cpTransformMult(inner, outer));
}
static inline cpTransform
cpTransformWrapInverse(cpTransform outer, cpTransform inner)
{
return cpTransformMult(outer, cpTransformMult(inner, cpTransformInverse(outer)));
}
static inline cpTransform
cpTransformOrtho(cpBB bb)
{
return cpTransformNewTranspose(
2.0/(bb.r - bb.l), 0.0, -(bb.r + bb.l)/(bb.r - bb.l),
0.0, 2.0/(bb.t - bb.b), -(bb.t + bb.b)/(bb.t - bb.b)
);
}
static inline cpTransform
cpTransformBoneScale(cpVect v0, cpVect v1)
{
cpVect d = cpvsub(v1, v0);
return cpTransformNewTranspose(
d.x, -d.y, v0.x,
d.y, d.x, v0.y
);
}
static inline cpTransform
cpTransformAxialScale(cpVect axis, cpVect pivot, cpFloat scale)
{
cpFloat A = axis.x*axis.y*(scale - 1.0);
cpFloat B = cpvdot(axis, pivot)*(1.0 - scale);
return cpTransformNewTranspose(
scale*axis.x*axis.x + axis.y*axis.y, A, axis.x*B,
A, axis.x*axis.x + scale*axis.y*axis.y, axis.y*B
);
}
#endif

View File

@@ -0,0 +1,230 @@
/* 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

View File

@@ -0,0 +1,331 @@
/* 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>
#if defined(ANDROID)
# include <android/log.h>
#endif
#include "chipmunk/chipmunk_private.h"
void
cpMessage(const char *condition, const char *file, int line, int isError, int isHardError, const char *message, ...)
{
fprintf(stderr, (isError ? "Aborting due to Chipmunk error: " : "Chipmunk warning: "));
va_list vargs;
va_start(vargs, message); {
#if defined(ANDROID)
__android_log_print( ANDROID_LOG_INFO, "Chipmunk", "%s(%d)", file, line );
__android_log_print( ANDROID_LOG_INFO, "Chipmunk", message, vargs );
#else
vfprintf(stderr, message, vargs);
fprintf(stderr, "\n");
#endif
} va_end(vargs);
#if defined(ANDROID)
__android_log_print(ANDROID_LOG_INFO, "Chipmunk", "\tFailed condition: %s\n", condition);
__android_log_print(ANDROID_LOG_INFO, "Chipmunk", "\tSource:%s:%d\n", file, line);
#else
fprintf(stderr, "\tFailed condition: %s\n", condition);
fprintf(stderr, "\tSource:%s:%d\n", file, line);
#endif
}
#define STR(s) #s
#define XSTR(s) STR(s)
const char *cpVersionString = XSTR(CP_VERSION_MAJOR) "." XSTR(CP_VERSION_MINOR) "." XSTR(CP_VERSION_RELEASE);
//MARK: Misc Functions
cpFloat
cpMomentForCircle(cpFloat m, cpFloat r1, cpFloat r2, cpVect offset)
{
return m*(0.5f*(r1*r1 + r2*r2) + cpvlengthsq(offset));
}
cpFloat
cpAreaForCircle(cpFloat r1, cpFloat r2)
{
return (cpFloat)CP_PI*cpfabs(r1*r1 - r2*r2);
}
cpFloat
cpMomentForSegment(cpFloat m, cpVect a, cpVect b, cpFloat r)
{
cpVect offset = cpvlerp(a, b, 0.5f);
// This approximates the shape as a box for rounded segments, but it's quite close.
cpFloat length = cpvdist(b, a) + 2.0f*r;
return m*((length*length + 4.0f*r*r)/12.0f + cpvlengthsq(offset));
}
cpFloat
cpAreaForSegment(cpVect a, cpVect b, cpFloat r)
{
return r*((cpFloat)CP_PI*r + 2.0f*cpvdist(a, b));
}
cpFloat
cpMomentForPoly(cpFloat m, int count, const cpVect *verts, cpVect offset, cpFloat r)
{
// TODO account for radius.
if(count == 2) return cpMomentForSegment(m, verts[0], verts[1], 0.0f);
cpFloat sum1 = 0.0f;
cpFloat sum2 = 0.0f;
for(int i=0; i<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*(CP_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/chipmunk_ffi.h"

View File

@@ -0,0 +1,498 @@
/* 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)));
}
}

View File

@@ -0,0 +1,101 @@
/* 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 = 3*(arr->max + 1)/2;
arr->arr = (void **)cprealloc(arr->arr, arr->max*sizeof(void*));
}
arr->arr[arr->num] = object;
arr->num++;
}
void *
cpArrayPop(cpArray *arr)
{
arr->num--;
void *value = arr->arr[arr->num];
arr->arr[arr->num] = NULL;
return value;
}
void
cpArrayDeleteObj(cpArray *arr, void *obj)
{
for(int i=0; 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;
}

View File

@@ -0,0 +1,896 @@
/* 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(void);
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

View File

@@ -0,0 +1,626 @@
/* 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 = mass == 0.0f ? INFINITY : 1.0f/mass;
cpAssertSaneBody(body);
}
cpFloat
cpBodyGetMoment(const cpBody *body)
{
return body->i;
}
void
cpBodySetMoment(cpBody *body, cpFloat moment)
{
cpAssertHard(moment >= 0.0f, "Moment of Inertia must be positive.");
cpBodyActivate(body);
body->i = moment;
body->i_inv = moment == 0.0f ? INFINITY : 1.0f/moment;
cpAssertSaneBody(body);
}
cpVect
cpBodyGetRotation(const cpBody *body)
{
return cpv(body->transform.a, body->transform.b);
}
void
cpBodyAddShape(cpBody *body, cpShape *shape)
{
cpShape *next = body->shapeList;
if(next) next->prev = shape;
shape->next = next;
body->shapeList = shape;
if(shape->massInfo.m > 0.0f){
cpBodyAccumulateMassFromShapes(body);
}
}
void
cpBodyRemoveShape(cpBody *body, cpShape *shape)
{
cpShape *prev = shape->prev;
cpShape *next = shape->next;
if(prev){
prev->next = next;
} else {
body->shapeList = next;
}
if(next){
next->prev = prev;
}
shape->prev = NULL;
shape->next = NULL;
if(cpBodyGetType(body) == CP_BODY_TYPE_DYNAMIC && shape->massInfo.m > 0.0f){
cpBodyAccumulateMassFromShapes(body);
}
}
static cpConstraint *
filterConstraints(cpConstraint *node, cpBody *body, cpConstraint *filter)
{
if(node == filter){
return cpConstraintNext(node, body);
} else if(node->a == body){
node->next_a = filterConstraints(node->next_a, body, filter);
} else {
node->next_b = filterConstraints(node->next_b, body, filter);
}
return node;
}
void
cpBodyRemoveConstraint(cpBody *body, cpConstraint *constraint)
{
body->constraintList = filterConstraints(body->constraintList, body, constraint);
}
// 'p' is the position of the CoG
static void
SetTransform(cpBody *body, cpVect p, cpFloat a)
{
cpVect rot = cpvforangle(a);
cpVect c = body->cog;
body->transform = cpTransformNewTranspose(
rot.x, -rot.y, p.x - (c.x*rot.x - c.y*rot.y),
rot.y, rot.x, p.y - (c.x*rot.y + c.y*rot.x)
);
}
static inline cpFloat
SetAngle(cpBody *body, cpFloat a)
{
body->a = a;
cpAssertSaneBody(body);
return a;
}
cpVect
cpBodyGetPosition(const cpBody *body)
{
return cpTransformPoint(body->transform, cpvzero);
}
void
cpBodySetPosition(cpBody *body, cpVect position)
{
cpBodyActivate(body);
cpVect p = body->p = cpvadd(cpTransformVect(body->transform, body->cog), position);
cpAssertSaneBody(body);
SetTransform(body, p, body->a);
}
cpVect
cpBodyGetCenterOfGravity(const cpBody *body)
{
return body->cog;
}
void
cpBodySetCenterOfGravity(cpBody *body, cpVect cog)
{
cpBodyActivate(body);
body->cog = cog;
cpAssertSaneBody(body);
}
cpVect
cpBodyGetVelocity(const cpBody *body)
{
return body->v;
}
void
cpBodySetVelocity(cpBody *body, cpVect velocity)
{
cpBodyActivate(body);
body->v = velocity;
cpAssertSaneBody(body);
}
cpVect
cpBodyGetForce(const cpBody *body)
{
return body->f;
}
void
cpBodySetForce(cpBody *body, cpVect force)
{
cpBodyActivate(body);
body->f = force;
cpAssertSaneBody(body);
}
cpFloat
cpBodyGetAngle(const cpBody *body)
{
return body->a;
}
void
cpBodySetAngle(cpBody *body, cpFloat angle)
{
cpBodyActivate(body);
SetAngle(body, angle);
SetTransform(body, body->p, angle);
}
cpFloat
cpBodyGetAngularVelocity(const cpBody *body)
{
return body->w;
}
void
cpBodySetAngularVelocity(cpBody *body, cpFloat angularVelocity)
{
cpBodyActivate(body);
body->w = angularVelocity;
cpAssertSaneBody(body);
}
cpFloat
cpBodyGetTorque(const cpBody *body)
{
return body->t;
}
void
cpBodySetTorque(cpBody *body, cpFloat torque)
{
cpBodyActivate(body);
body->t = torque;
cpAssertSaneBody(body);
}
cpDataPointer
cpBodyGetUserData(const cpBody *body)
{
return body->userData;
}
void
cpBodySetUserData(cpBody *body, cpDataPointer userData)
{
body->userData = userData;
}
void
cpBodySetVelocityUpdateFunc(cpBody *body, cpBodyVelocityFunc velocityFunc)
{
body->velocity_func = velocityFunc;
}
void
cpBodySetPositionUpdateFunc(cpBody *body, cpBodyPositionFunc positionFunc)
{
body->position_func = positionFunc;
}
void
cpBodyUpdateVelocity(cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt)
{
// Skip kinematic bodies.
if(cpBodyGetType(body) == CP_BODY_TYPE_KINEMATIC) return;
cpAssertSoft(body->m > 0.0f && body->i > 0.0f, "Body's mass and moment must be positive to simulate. (Mass: %f Moment: %f)", body->m, body->i);
body->v = cpvadd(cpvmult(body->v, damping), cpvmult(cpvadd(gravity, cpvmult(body->f, body->m_inv)), dt));
body->w = body->w*damping + body->t*body->i_inv*dt;
// Reset forces.
body->f = cpvzero;
body->t = 0.0f;
cpAssertSaneBody(body);
}
void
cpBodyUpdatePosition(cpBody *body, cpFloat dt)
{
cpVect p = body->p = cpvadd(body->p, cpvmult(cpvadd(body->v, body->v_bias), dt));
cpFloat a = SetAngle(body, body->a + (body->w + body->w_bias)*dt);
SetTransform(body, p, a);
body->v_bias = cpvzero;
body->w_bias = 0.0f;
cpAssertSaneBody(body);
}
cpVect
cpBodyLocalToWorld(const cpBody *body, const cpVect point)
{
return cpTransformPoint(body->transform, point);
}
cpVect
cpBodyWorldToLocal(const cpBody *body, const cpVect point)
{
return cpTransformPoint(cpTransformRigidInverse(body->transform), point);
}
void
cpBodyApplyForceAtWorldPoint(cpBody *body, cpVect force, cpVect point)
{
cpBodyActivate(body);
body->f = cpvadd(body->f, force);
cpVect r = cpvsub(point, cpTransformPoint(body->transform, body->cog));
body->t += cpvcross(r, force);
}
void
cpBodyApplyForceAtLocalPoint(cpBody *body, cpVect force, cpVect point)
{
cpBodyApplyForceAtWorldPoint(body, cpTransformVect(body->transform, force), cpTransformPoint(body->transform, point));
}
void
cpBodyApplyImpulseAtWorldPoint(cpBody *body, cpVect impulse, cpVect point)
{
cpBodyActivate(body);
cpVect r = cpvsub(point, cpTransformPoint(body->transform, body->cog));
apply_impulse(body, impulse, r);
}
void
cpBodyApplyImpulseAtLocalPoint(cpBody *body, cpVect impulse, cpVect point)
{
cpBodyApplyImpulseAtWorldPoint(body, cpTransformVect(body->transform, impulse), cpTransformPoint(body->transform, point));
}
cpVect
cpBodyGetVelocityAtLocalPoint(const cpBody *body, cpVect point)
{
cpVect r = cpTransformVect(body->transform, cpvsub(point, body->cog));
return cpvadd(body->v, cpvmult(cpvperp(r), body->w));
}
cpVect
cpBodyGetVelocityAtWorldPoint(const cpBody *body, cpVect point)
{
cpVect r = cpvsub(point, cpTransformPoint(body->transform, body->cog));
return cpvadd(body->v, cpvmult(cpvperp(r), body->w));
}
cpFloat
cpBodyKineticEnergy(const cpBody *body)
{
// Need to do some fudging to avoid NaNs
cpFloat vsq = cpvdot(body->v, body->v);
cpFloat wsq = body->w*body->w;
return (vsq ? vsq*body->m : 0.0f) + (wsq ? wsq*body->i : 0.0f);
}
void
cpBodyEachShape(cpBody *body, cpBodyShapeIteratorFunc func, void *data)
{
cpShape *shape = body->shapeList;
while(shape){
cpShape *next = shape->next;
func(body, shape, data);
shape = next;
}
}
void
cpBodyEachConstraint(cpBody *body, cpBodyConstraintIteratorFunc func, void *data)
{
cpConstraint *constraint = body->constraintList;
while(constraint){
cpConstraint *next = cpConstraintNext(constraint, body);
func(body, constraint, data);
constraint = next;
}
}
void
cpBodyEachArbiter(cpBody *body, cpBodyArbiterIteratorFunc func, void *data)
{
cpArbiter *arb = body->arbiterList;
while(arb){
cpArbiter *next = cpArbiterNext(arb, body);
cpBool swapped = arb->swapped; {
arb->swapped = (body == arb->body_b);
func(body, arb, data);
} arb->swapped = swapped;
arb = next;
}
}

View File

@@ -0,0 +1,726 @@
/* 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"
#include "chipmunk/cpRobust.h"
#if DEBUG && 0
#include "ChipmunkDemo.h"
#define DRAW_ALL 0
#define DRAW_GJK (0 || DRAW_ALL)
#define DRAW_EPA (0 || DRAW_ALL)
#define DRAW_CLOSEST (0 || DRAW_ALL)
#define DRAW_CLIP (0 || DRAW_ALL)
#define PRINT_LOG 0
#endif
#define MAX_GJK_ITERATIONS 30
#define MAX_EPA_ITERATIONS 30
#define WARN_GJK_ITERATIONS 20
#define WARN_EPA_ITERATIONS 20
static inline void
cpCollisionInfoPushContact(struct cpCollisionInfo *info, cpVect p1, cpVect p2, cpHashValue hash)
{
cpAssertSoft(info->count <= CP_MAX_CONTACTS_PER_ARBITER, "Internal error: Tried to push too many contacts.");
struct cpContact *con = &info->arr[info->count];
con->r1 = p1;
con->r2 = p2;
con->hash = hash;
info->count++;
}
//MARK: Support Points and Edges:
// Support points are the maximal points on a shape's perimeter along a certain axis.
// The GJK and EPA algorithms use support points to iteratively sample the surface of the two shapes' minkowski difference.
static inline int
PolySupportPointIndex(const int count, const struct cpSplittingPlane *planes, const cpVect n)
{
cpFloat max = -INFINITY;
int index = 0;
for(int i=0; i<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) + CPFLOAT_MIN), -1.0f, 1.0f);
}
// Basically the same as cpvlerp(), except t = [-1, 1]
static inline cpVect
LerpT(const cpVect a, const cpVect b, const cpFloat t)
{
cpFloat ht = 0.5f*t;
return cpvadd(cpvmult(a, 0.5f - ht), cpvmult(b, 0.5f + ht));
}
// Closest points on the surface of two shapes.
struct ClosestPoints {
// Surface points in absolute coordinates.
cpVect a, b;
// Minimum separating axis of the two shapes.
cpVect n;
// Signed distance between the points.
cpFloat d;
// Concatenation of the id's of the minkoski points.
cpCollisionID id;
};
// Calculate the closest points on two shapes given the closest edge on their minkowski difference to (0, 0)
static inline struct ClosestPoints
ClosestPointsNew(const struct MinkowskiPoint v0, const struct MinkowskiPoint v1)
{
// Find the closest p(t) on the minkowski difference to (0, 0)
cpFloat t = ClosestT(v0.ab, v1.ab);
cpVect p = LerpT(v0.ab, v1.ab, t);
// Interpolate the original support points using the same 't' value as above.
// This gives you the closest surface points in absolute coordinates. NEAT!
cpVect pa = LerpT(v0.a, v1.a, t);
cpVect pb = LerpT(v0.b, v1.b, t);
cpCollisionID id = (v0.id & 0xFFFF)<<16 | (v1.id & 0xFFFF);
// First try calculating the MSA from the minkowski difference edge.
// This gives us a nice, accurate MSA when the surfaces are close together.
cpVect delta = cpvsub(v1.ab, v0.ab);
cpVect n = cpvnormalize(cpvrperp(delta));
cpFloat d = cpvdot(n, p);
if(d <= 0.0f || (-1.0f < t && t < 1.0f)){
// If the shapes are overlapping, or we have a regular vertex/edge collision, we are done.
struct ClosestPoints points = {pa, pb, n, d, id};
return points;
} else {
// Vertex/vertex collisions need special treatment since the MSA won't be shared with an axis of the minkowski difference.
cpFloat d2 = cpvlength(p);
cpVect n2 = cpvmult(p, 1.0f/(d2 + CPFLOAT_MIN));
struct ClosestPoints points = {pa, pb, n2, d2, id};
return points;
}
}
//MARK: EPA Functions
static inline cpFloat
ClosestDist(const cpVect v0,const cpVect v1)
{
return cpvlengthsq(LerpT(v0, v1, ClosestT(v0, v1)));
}
// Recursive implementation of the EPA loop.
// Each recursion adds a point to the convex hull until it's known that we have the closest point on the surface.
static struct ClosestPoints
EPARecurse(const struct SupportContext *ctx, const int count, const struct MinkowskiPoint *hull, const int iteration)
{
int mini = 0;
cpFloat minDist = INFINITY;
// TODO: precalculate this when building the hull and save a step.
// Find the closest segment hull[i] and hull[i + 1] to (0, 0)
for(int j=0, i=count-1; j<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
// The usual exit condition is a duplicated vertex.
// Much faster to check the ids than to check the signed area.
cpBool duplicate = (p.id == v0.id || p.id == v1.id);
if(!duplicate && cpCheckPointGreater(v0.ab, v1.ab, p.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(cpCheckPointGreater(h0, h2, h1)){
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 a 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 implementation 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);
}
if(cpCheckPointGreater(v1.ab, v0.ab, cpvzero)){
// Origin is behind axis. Flip and try again.
return GJKRecurse(ctx, v1, v0, iteration);
} else {
cpFloat t = ClosestT(v0.ab, v1.ab);
cpVect n = (-1.0f < t && t < 1.0f ? cpvperp(cpvsub(v1.ab, v0.ab)) : cpvneg(LerpT(v0.ab, v1.ab, t)));
struct MinkowskiPoint p = Support(ctx, n);
#if DRAW_GJK
ChipmunkDebugDrawSegment(v0.ab, v1.ab, RGBAColor(1, 1, 1, 1));
cpVect c = cpvlerp(v0.ab, v1.ab, 0.5);
ChipmunkDebugDrawSegment(c, cpvadd(c, cpvmult(cpvnormalize(n), 5.0)), RGBAColor(1, 0, 0, 1));
ChipmunkDebugDrawDot(5.0, p.ab, LAColor(1, 1));
#endif
if(cpCheckPointGreater(p.ab, v0.ab, cpvzero) && cpCheckPointGreater(v1.ab, p.ab, cpvzero)){
// The triangle v0, p, v1 contains the origin. Use EPA to find the MSA.
cpAssertWarn(iteration < WARN_GJK_ITERATIONS, "High GJK->EPA iterations: %d", iteration);
return EPA(ctx, v0, p, v1);
} else {
if(cpCheckAxis(v0.ab, v1.ab, p.ab, n)){
// The edge v0, v1 that we already have is the closest to (0, 0) since p was not closer.
cpAssertWarn(iteration < WARN_GJK_ITERATIONS, "High GJK iterations: %d", iteration);
return ClosestPointsNew(v0, v1);
} else {
// p was closer to the origin than our existing edge.
// Need to figure out which existing point to drop.
if(ClosestDist(v0.ab, p.ab) < ClosestDist(p.ab, v1.ab)){
return GJKRecurse(ctx, v0, p, iteration + 1);
} else {
return GJKRecurse(ctx, p, v1, iteration + 1);
}
}
}
}
}
// Get a SupportPoint from a cached shape and index.
static struct SupportPoint
ShapePoint(const cpShape *shape, const int i)
{
switch(shape->klass->type){
case CP_CIRCLE_SHAPE: {
return SupportPointNew(((cpCircleShape *)shape)->tc, 0);
} case CP_SEGMENT_SHAPE: {
cpSegmentShape *seg = (cpSegmentShape *)shape;
return SupportPointNew(i == 0 ? seg->ta : seg->tb, i);
} case CP_POLY_SHAPE: {
cpPolyShape *poly = (cpPolyShape *)shape;
// Poly shapes may change vertex count.
int index = (i < poly->count ? i : 0);
return SupportPointNew(poly->planes[index].v0, index);
} default: {
return SupportPointNew(cpvzero, 0);
}
}
}
// Find the closest points between two shapes using the GJK algorithm.
static struct ClosestPoints
GJK(const struct SupportContext *ctx, cpCollisionID *id)
{
#if DRAW_GJK || DRAW_EPA
int count1 = 1;
int count2 = 1;
switch(ctx->shape1->klass->type){
case CP_SEGMENT_SHAPE: count1 = 2; break;
case CP_POLY_SHAPE: count1 = ((cpPolyShape *)ctx->shape1)->count; break;
default: break;
}
switch(ctx->shape2->klass->type){
case CP_SEGMENT_SHAPE: count1 = 2; break;
case CP_POLY_SHAPE: count2 = ((cpPolyShape *)ctx->shape2)->count; break;
default: break;
}
// draw the minkowski difference origin
cpVect origin = cpvzero;
ChipmunkDebugDrawDot(5.0, origin, RGBAColor(1,0,0,1));
int mdiffCount = count1*count2;
cpVect *mdiffVerts = alloca(mdiffCount*sizeof(cpVect));
for(int i=0; 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){
// Use the minkowski points from the last frame as a starting point using the cached indexes.
v0 = MinkowskiPointNew(ShapePoint(ctx->shape1, (*id>>24)&0xFF), ShapePoint(ctx->shape2, (*id>>16)&0xFF));
v1 = MinkowskiPointNew(ShapePoint(ctx->shape1, (*id>> 8)&0xFF), ShapePoint(ctx->shape2, (*id )&0xFF));
} else {
// No cached indexes, use the shapes' bounding box centers as a guess for a starting axis.
cpVect axis = cpvperp(cpvsub(cpBBCenter(ctx->shape1->bb), cpBBCenter(ctx->shape2->bb)));
v0 = Support(ctx, axis);
v1 = Support(ctx, cpvneg(axis));
}
struct ClosestPoints points = GJKRecurse(ctx, v0, v1, 1);
*id = points.id;
return points;
}
//MARK: Contact Clipping
// Given two support edges, find contact point pairs on their surfaces.
static inline void
ContactPoints(const struct Edge e1, const struct Edge e2, const struct ClosestPoints points, struct cpCollisionInfo *info)
{
cpFloat mindist = e1.r + e2.r;
if(points.d <= mindist){
#ifdef DRAW_CLIP
ChipmunkDebugDrawFatSegment(e1.a.p, e1.b.p, e1.r, RGBAColor(0, 1, 0, 1), LAColor(0, 0));
ChipmunkDebugDrawFatSegment(e2.a.p, e2.b.p, e2.r, RGBAColor(1, 0, 0, 1), LAColor(0, 0));
#endif
cpVect n = info->n = points.n;
// Distances along the axis parallel to n
cpFloat d_e1_a = cpvcross(e1.a.p, n);
cpFloat d_e1_b = cpvcross(e1.b.p, n);
cpFloat d_e2_a = cpvcross(e2.a.p, n);
cpFloat d_e2_b = cpvcross(e2.b.p, n);
// TODO + min isn't a complete fix.
cpFloat e1_denom = 1.0f/(d_e1_b - d_e1_a + CPFLOAT_MIN);
cpFloat e2_denom = 1.0f/(d_e2_b - d_e2_a + CPFLOAT_MIN);
// Project the endpoints of the two edges onto the opposing edge, clamping them as necessary.
// Compare the projected points to the collision normal to see if the shapes overlap there.
{
cpVect p1 = cpvadd(cpvmult(n, e1.r), cpvlerp(e1.a.p, e1.b.p, cpfclamp01((d_e2_b - d_e1_a)*e1_denom)));
cpVect p2 = cpvadd(cpvmult(n, -e2.r), cpvlerp(e2.a.p, e2.b.p, cpfclamp01((d_e1_a - d_e2_a)*e2_denom)));
cpFloat dist = cpvdot(cpvsub(p2, p1), n);
if(dist <= 0.0f){
cpHashValue hash_1a2b = CP_HASH_PAIR(e1.a.hash, e2.b.hash);
cpCollisionInfoPushContact(info, p1, p2, hash_1a2b);
}
}{
cpVect p1 = cpvadd(cpvmult(n, e1.r), cpvlerp(e1.a.p, e1.b.p, cpfclamp01((d_e2_a - d_e1_a)*e1_denom)));
cpVect p2 = cpvadd(cpvmult(n, -e2.r), cpvlerp(e2.a.p, e2.b.p, cpfclamp01((d_e1_b - d_e2_a)*e2_denom)));
cpFloat dist = cpvdot(cpvsub(p2, p1), n);
if(dist <= 0.0f){
cpHashValue hash_1b2a = CP_HASH_PAIR(e1.b.hash, e2.a.hash);
cpCollisionInfoPushContact(info, p1, p2, hash_1b2a);
}
}
}
}
//MARK: Collision Functions
typedef void (*CollisionFunc)(const cpShape *a, const cpShape *b, struct cpCollisionInfo *info);
// Collide circle shapes.
static void
CircleToCircle(const cpCircleShape *c1, const cpCircleShape *c2, struct cpCollisionInfo *info)
{
cpFloat mindist = c1->r + c2->r;
cpVect delta = cpvsub(c2->tc, c1->tc);
cpFloat distsq = cpvlengthsq(delta);
if(distsq < mindist*mindist){
cpFloat dist = cpfsqrt(distsq);
cpVect n = info->n = (dist ? cpvmult(delta, 1.0f/dist) : cpv(1.0f, 0.0f));
cpCollisionInfoPushContact(info, cpvadd(c1->tc, cpvmult(n, c1->r)), cpvadd(c2->tc, cpvmult(n, -c2->r)), 0);
}
}
static void
CircleToSegment(const cpCircleShape *circle, const cpSegmentShape *segment, struct cpCollisionInfo *info)
{
cpVect seg_a = segment->ta;
cpVect seg_b = segment->tb;
cpVect center = circle->tc;
// Find the closest point on the segment to the circle.
cpVect seg_delta = cpvsub(seg_b, seg_a);
cpFloat closest_t = cpfclamp01(cpvdot(seg_delta, cpvsub(center, seg_a))/cpvlengthsq(seg_delta));
cpVect closest = cpvadd(seg_a, cpvmult(seg_delta, closest_t));
// Compare the radii of the two shapes to see if they are colliding.
cpFloat mindist = circle->r + segment->r;
cpVect delta = cpvsub(closest, center);
cpFloat distsq = cpvlengthsq(delta);
if(distsq < mindist*mindist){
cpFloat dist = cpfsqrt(distsq);
// Handle coincident shapes as gracefully as possible.
cpVect n = info->n = (dist ? cpvmult(delta, 1.0f/dist) : segment->tn);
// Reject endcap collisions if tangents are provided.
cpVect rot = cpBodyGetRotation(segment->shape.body);
if(
(closest_t != 0.0f || cpvdot(n, cpvrotate(segment->a_tangent, rot)) >= 0.0) &&
(closest_t != 1.0f || cpvdot(n, cpvrotate(segment->b_tangent, rot)) >= 0.0)
){
cpCollisionInfoPushContact(info, cpvadd(center, cpvmult(n, circle->r)), cpvadd(closest, cpvmult(n, -segment->r)), 0);
}
}
}
static void
SegmentToSegment(const cpSegmentShape *seg1, const cpSegmentShape *seg2, struct cpCollisionInfo *info)
{
struct SupportContext context = {(cpShape *)seg1, (cpShape *)seg2, (SupportPointFunc)SegmentSupportPoint, (SupportPointFunc)SegmentSupportPoint};
struct ClosestPoints points = GJK(&context, &info->id);
#if DRAW_CLOSEST
#if PRINT_LOG
// ChipmunkDemoPrintString("Distance: %.2f\n", points.d);
#endif
ChipmunkDebugDrawDot(6.0, points.a, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawDot(6.0, points.b, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawSegment(points.a, points.b, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawSegment(points.a, cpvadd(points.a, cpvmult(points.n, 10.0)), RGBAColor(1, 0, 0, 1));
#endif
cpVect n = points.n;
cpVect rot1 = cpBodyGetRotation(seg1->shape.body);
cpVect rot2 = cpBodyGetRotation(seg2->shape.body);
// If the closest points are nearer than the sum of the radii...
if(
points.d <= (seg1->r + seg2->r) && (
// Reject endcap collisions if tangents are provided.
(!cpveql(points.a, seg1->ta) || cpvdot(n, cpvrotate(seg1->a_tangent, rot1)) <= 0.0) &&
(!cpveql(points.a, seg1->tb) || cpvdot(n, cpvrotate(seg1->b_tangent, rot1)) <= 0.0) &&
(!cpveql(points.b, seg2->ta) || cpvdot(n, cpvrotate(seg2->a_tangent, rot2)) >= 0.0) &&
(!cpveql(points.b, seg2->tb) || cpvdot(n, cpvrotate(seg2->b_tangent, rot2)) >= 0.0)
)
){
ContactPoints(SupportEdgeForSegment(seg1, n), SupportEdgeForSegment(seg2, cpvneg(n)), points, info);
}
}
static void
PolyToPoly(const cpPolyShape *poly1, const cpPolyShape *poly2, struct cpCollisionInfo *info)
{
struct SupportContext context = {(cpShape *)poly1, (cpShape *)poly2, (SupportPointFunc)PolySupportPoint, (SupportPointFunc)PolySupportPoint};
struct ClosestPoints points = GJK(&context, &info->id);
#if DRAW_CLOSEST
#if PRINT_LOG
// ChipmunkDemoPrintString("Distance: %.2f\n", points.d);
#endif
ChipmunkDebugDrawDot(3.0, points.a, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawDot(3.0, points.b, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawSegment(points.a, points.b, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawSegment(points.a, cpvadd(points.a, cpvmult(points.n, 10.0)), RGBAColor(1, 0, 0, 1));
#endif
// If the closest points are nearer than the sum of the radii...
if(points.d - poly1->r - poly2->r <= 0.0){
ContactPoints(SupportEdgeForPoly(poly1, points.n), SupportEdgeForPoly(poly2, cpvneg(points.n)), points, info);
}
}
static void
SegmentToPoly(const cpSegmentShape *seg, const cpPolyShape *poly, struct cpCollisionInfo *info)
{
struct SupportContext context = {(cpShape *)seg, (cpShape *)poly, (SupportPointFunc)SegmentSupportPoint, (SupportPointFunc)PolySupportPoint};
struct ClosestPoints points = GJK(&context, &info->id);
#if DRAW_CLOSEST
#if PRINT_LOG
// ChipmunkDemoPrintString("Distance: %.2f\n", points.d);
#endif
ChipmunkDebugDrawDot(3.0, points.a, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawDot(3.0, points.b, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawSegment(points.a, points.b, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawSegment(points.a, cpvadd(points.a, cpvmult(points.n, 10.0)), RGBAColor(1, 0, 0, 1));
#endif
cpVect n = points.n;
cpVect rot = cpBodyGetRotation(seg->shape.body);
if(
// If the closest points are nearer than the sum of the radii...
points.d - seg->r - poly->r <= 0.0 && (
// Reject endcap collisions if tangents are provided.
(!cpveql(points.a, seg->ta) || cpvdot(n, cpvrotate(seg->a_tangent, rot)) <= 0.0) &&
(!cpveql(points.a, seg->tb) || cpvdot(n, cpvrotate(seg->b_tangent, rot)) <= 0.0)
)
){
ContactPoints(SupportEdgeForSegment(seg, n), SupportEdgeForPoly(poly, cpvneg(n)), points, info);
}
}
static void
CircleToPoly(const cpCircleShape *circle, const cpPolyShape *poly, struct cpCollisionInfo *info)
{
struct SupportContext context = {(cpShape *)circle, (cpShape *)poly, (SupportPointFunc)CircleSupportPoint, (SupportPointFunc)PolySupportPoint};
struct ClosestPoints points = GJK(&context, &info->id);
#if DRAW_CLOSEST
ChipmunkDebugDrawDot(3.0, points.a, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawDot(3.0, points.b, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawSegment(points.a, points.b, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawSegment(points.a, cpvadd(points.a, cpvmult(points.n, 10.0)), RGBAColor(1, 0, 0, 1));
#endif
// If the closest points are nearer than the sum of the radii...
if(points.d <= circle->r + poly->r){
cpVect n = info->n = points.n;
cpCollisionInfoPushContact(info, cpvadd(points.a, cpvmult(n, circle->r)), cpvadd(points.b, cpvmult(n, -poly->r)), 0);
}
}
static void
CollisionError(const cpShape *circle, const cpShape *poly, struct cpCollisionInfo *info)
{
cpAssertHard(cpFalse, "Internal Error: Shape types are not sorted.");
}
static const CollisionFunc BuiltinCollisionFuncs[9] = {
(CollisionFunc)CircleToCircle,
CollisionError,
CollisionError,
(CollisionFunc)CircleToSegment,
(CollisionFunc)SegmentToSegment,
CollisionError,
(CollisionFunc)CircleToPoly,
(CollisionFunc)SegmentToPoly,
(CollisionFunc)PolyToPoly,
};
static const CollisionFunc *CollisionFuncs = BuiltinCollisionFuncs;
struct cpCollisionInfo
cpCollide(const cpShape *a, const cpShape *b, cpCollisionID id, struct cpContact *contacts)
{
struct cpCollisionInfo info = {a, b, id, cpvzero, 0, contacts};
// Make sure the shape types are in order.
if(a->klass->type > b->klass->type){
info.a = b;
info.b = a;
}
CollisionFuncs[info.a->klass->type + info.b->klass->type*CP_NUM_SHAPES](info.a, info.b, &info);
// if(0){
// for(int i=0; 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;
}

View File

@@ -0,0 +1,173 @@
/* 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);
}

View File

@@ -0,0 +1,178 @@
/* 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;
}

View File

@@ -0,0 +1,216 @@
/* 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;
}

View File

@@ -0,0 +1,145 @@
/* 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;
}

View File

@@ -0,0 +1,197 @@
/* 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;
}

View File

@@ -0,0 +1,253 @@
/* 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;
}
const void *
cpHashSetInsert(cpHashSet *set, cpHashValue hash, const void *ptr, cpHashSetTransFunc trans, void *data)
{
cpHashValue idx = hash%set->size;
// Find the bin with the matching element.
cpHashSetBin *bin = set->table[idx];
while(bin && !set->eql(ptr, bin->elt))
bin = bin->next;
// Create it if necessary.
if(!bin){
bin = getUnusedBin(set);
bin->hash = hash;
bin->elt = (trans ? trans(ptr, data) : data);
bin->next = set->table[idx];
set->table[idx] = bin;
set->entries++;
if(setIsFull(set)) cpHashSetResize(set);
}
return bin->elt;
}
const void *
cpHashSetRemove(cpHashSet *set, cpHashValue hash, const void *ptr)
{
cpHashValue idx = hash%set->size;
cpHashSetBin **prev_ptr = &set->table[idx];
cpHashSetBin *bin = set->table[idx];
// Find the bin
while(bin && !set->eql(ptr, bin->elt)){
prev_ptr = &bin->next;
bin = bin->next;
}
// Remove it if it exists.
if(bin){
// Update the previous linked list pointer
(*prev_ptr) = bin->next;
set->entries--;
const void *elt = bin->elt;
recycleBin(set, bin);
return elt;
}
return NULL;
}
const void *
cpHashSetFind(cpHashSet *set, cpHashValue hash, const void *ptr)
{
cpHashValue idx = hash%set->size;
cpHashSetBin *bin = set->table[idx];
while(bin && !set->eql(ptr, bin->elt))
bin = bin->next;
return (bin ? bin->elt : set->default_value);
}
void
cpHashSetEach(cpHashSet *set, cpHashSetIteratorFunc func, void *data)
{
for(unsigned int i=0; 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;
}
}
}

View File

@@ -0,0 +1,700 @@
// Copyright 2013 Howling Moon Software. All rights reserved.
// See http://chipmunk2d.net/legal.php for more information.
#include <stdlib.h>
#include <stdio.h>
//TODO: Move all the thread stuff to another file
//#include <sys/param.h >
#ifdef __APPLE__
#include <sys/sysctl.h>
#endif
#ifndef _WIN32
#include <pthread.h>
#elif defined(__MINGW32__)
#include <pthread.h>
#else
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <process.h> // _beginthreadex
#include <windows.h>
#ifndef ETIMEDOUT
#define ETIMEDOUT 1
#endif
// Simple pthread implementation for Windows
// Made from scratch to avoid the LGPL licence from pthread-win32
enum {
SIGNAL = 0,
BROADCAST = 1,
MAX_EVENTS = 2
};
typedef HANDLE pthread_t;
typedef struct
{
// Based on http://www.cs.wustl.edu/~schmidt/win32-cv-1.html since Windows has no condition variable until NT6
UINT waiters_count;
// Count of the number of waiters.
CRITICAL_SECTION waiters_count_lock;
// Serialize access to <waiters_count_>.
HANDLE events[MAX_EVENTS];
} pthread_cond_t;
typedef CRITICAL_SECTION pthread_mutex_t;
typedef struct {} pthread_condattr_t; // Dummy;
int pthread_cond_destroy(pthread_cond_t* cv)
{
CloseHandle(cv->events[BROADCAST]);
CloseHandle(cv->events[SIGNAL]);
DeleteCriticalSection(&cv->waiters_count_lock);
return 0;
}
int pthread_cond_init(pthread_cond_t* cv, const pthread_condattr_t* attr)
{
// Initialize the count to 0.
cv->waiters_count = 0;
// Create an auto-reset event.
cv->events[SIGNAL] = CreateEvent(NULL, // no security
FALSE, // auto-reset event
FALSE, // non-signaled initially
NULL); // unnamed
// Create a manual-reset event.
cv->events[BROADCAST] = CreateEvent(NULL, // no security
TRUE, // manual-reset
FALSE, // non-signaled initially
NULL); // unnamed
InitializeCriticalSection(&cv->waiters_count_lock);
return 0;
}
int pthread_cond_broadcast(pthread_cond_t *cv)
{
// Avoid race conditions.
EnterCriticalSection(&cv->waiters_count_lock);
int have_waiters = cv->waiters_count > 0;
LeaveCriticalSection(&cv->waiters_count_lock);
if (have_waiters)
SetEvent(cv->events[BROADCAST]);
return 0;
}
int pthread_cond_signal(pthread_cond_t* cv)
{
// Avoid race conditions.
EnterCriticalSection(&cv->waiters_count_lock);
int have_waiters = cv->waiters_count > 0;
LeaveCriticalSection(&cv->waiters_count_lock);
if (have_waiters)
SetEvent(cv->events[SIGNAL]);
return 0;
}
int pthread_cond_wait(pthread_cond_t* cv, pthread_mutex_t* external_mutex)
{
// Avoid race conditions.
EnterCriticalSection(&cv->waiters_count_lock);
cv->waiters_count++;
LeaveCriticalSection(&cv->waiters_count_lock);
// It's ok to release the <external_mutex> here since Win32
// manual-reset events maintain state when used with
// <SetEvent>. This avoids the "lost wakeup" bug...
LeaveCriticalSection(external_mutex);
// Wait for either event to become signaled due to <pthread_cond_signal>
// being called or <pthread_cond_broadcast> being called.
int result = WaitForMultipleObjects(2, cv->events, FALSE, INFINITE);
EnterCriticalSection(&cv->waiters_count_lock);
cv->waiters_count--;
int last_waiter =
result == WAIT_OBJECT_0 + BROADCAST
&& cv->waiters_count == 0;
LeaveCriticalSection(&cv->waiters_count_lock);
// Some thread called <pthread_cond_broadcast>.
if (last_waiter)
// We're the last waiter to be notified or to stop waiting, so
// reset the manual event.
ResetEvent(cv->events[BROADCAST]);
// Reacquire the <external_mutex>.
EnterCriticalSection(external_mutex);
return result == WAIT_TIMEOUT ? ETIMEDOUT : 0;
}
typedef struct {} pthread_mutexattr_t; //< Dummy
int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attr)
{
InitializeCriticalSection(mutex);
return 0;
}
int pthread_mutex_destroy(pthread_mutex_t* mutex)
{
DeleteCriticalSection(mutex);
return 0;
}
int pthread_mutex_lock(pthread_mutex_t* mutex)
{
EnterCriticalSection(mutex);
return 0;
}
int pthread_mutex_unlock(pthread_mutex_t* mutex)
{
LeaveCriticalSection(mutex);
return 0;
}
typedef struct {} pthread_attr_t;
typedef struct
{
void *(*start_routine) (void *);
void* arg;
} pthread_internal_thread;
unsigned int __stdcall ThreadProc(void* userdata)
{
pthread_internal_thread* ud = (pthread_internal_thread*) userdata;
ud->start_routine(ud->arg);
free(ud);
return 0;
}
int pthread_create(pthread_t* thread, const pthread_attr_t* attr, void *(*start_routine) (void *), void *arg)
{
pthread_internal_thread* ud = (pthread_internal_thread*) malloc(sizeof(pthread_internal_thread));
ud->start_routine = start_routine;
ud->arg = arg;
*thread = (HANDLE) (_beginthreadex(NULL, 0, &ThreadProc, ud, 0, NULL));
if (!*thread)
return 1;
return 0;
}
int pthread_join(pthread_t thread, void **value_ptr)
{
WaitForSingleObject(thread, INFINITE);
CloseHandle(thread);
return 0;
}
#endif
#include "chipmunk/chipmunk_private.h"
#include "chipmunk/cpHastySpace.h"
//MARK: ARM NEON Solver
#if __ARM_NEON__
#include <arm_neon.h>
// Tested and known to work fine with Clang 3.0 and GCC 4.2
// Doesn't work with Clang 1.6, and I have no idea why.
#if defined(__clang_major__) && __clang_major__ < 3
#error Compiler not supported.
#endif
#if CP_USE_DOUBLES
#if !__arm64
#error Cannot use CP_USE_DOUBLES on 32 bit ARM.
#endif
typedef float64_t cpFloat_t;
typedef float64x2_t cpFloatx2_t;
#define vld vld1q_f64
#define vdup_n vdupq_n_f64
#define vst vst1q_f64
#define vst_lane vst1q_lane_f64
#define vadd vaddq_f64
#define vsub vsubq_f64
#define vpadd vpaddq_f64
#define vmul vmulq_f64
#define vmul_n vmulq_n_f64
#define vneg vnegq_f64
#define vget_lane vgetq_lane_f64
#define vset_lane vsetq_lane_f64
#define vmin vminq_f64
#define vmax vmaxq_f64
#define vrev(__a) __builtin_shufflevector(__a, __a, 1, 0)
#else
typedef float32_t cpFloat_t;
typedef float32x2_t cpFloatx2_t;
#define vld vld1_f32
#define vdup_n vdup_n_f32
#define vst vst1_f32
#define vst_lane vst1_lane_f32
#define vadd vadd_f32
#define vsub vsub_f32
#define vpadd vpadd_f32
#define vmul vmul_f32
#define vmul_n vmul_n_f32
#define vneg vneg_f32
#define vget_lane vget_lane_f32
#define vset_lane vset_lane_f32
#define vmin vmin_f32
#define vmax vmax_f32
#define vrev vrev64_f32
#endif
// TODO could probably do better here, maybe using vcreate?
// especially for the constants
// Maybe use the {} notation for GCC/Clang?
static inline cpFloatx2_t
vmake(cpFloat_t x, cpFloat_t y)
{
// cpFloatx2_t v = {};
// v = vset_lane(x, v, 0);
// v = vset_lane(y, v, 1);
//
// return v;
// This might not be super compatible, but all the NEON headers use it...
return (cpFloatx2_t){x, y};
}
static void
cpArbiterApplyImpulse_NEON(cpArbiter *arb)
{
cpBody *a = arb->body_a;
cpBody *b = arb->body_b;
cpFloatx2_t surface_vr = vld((cpFloat_t *)&arb->surface_vr);
cpFloatx2_t n = vld((cpFloat_t *)&arb->n);
cpFloat_t friction = arb->u;
int numContacts = arb->count;
struct cpContact *contacts = arb->contacts;
for(int i=0; i<numContacts; i++){
struct cpContact *con = contacts + i;
cpFloatx2_t r1 = vld((cpFloat_t *)&con->r1);
cpFloatx2_t r2 = vld((cpFloat_t *)&con->r2);
cpFloatx2_t perp = vmake(-1.0, 1.0);
cpFloatx2_t r1p = vmul(vrev(r1), perp);
cpFloatx2_t r2p = vmul(vrev(r2), perp);
cpFloatx2_t vBias_a = vld((cpFloat_t *)&a->v_bias);
cpFloatx2_t vBias_b = vld((cpFloat_t *)&b->v_bias);
cpFloatx2_t wBias = vmake(a->w_bias, b->w_bias);
cpFloatx2_t vb1 = vadd(vBias_a, vmul_n(r1p, vget_lane(wBias, 0)));
cpFloatx2_t vb2 = vadd(vBias_b, vmul_n(r2p, vget_lane(wBias, 1)));
cpFloatx2_t vbr = vsub(vb2, vb1);
cpFloatx2_t v_a = vld((cpFloat_t *)&a->v);
cpFloatx2_t v_b = vld((cpFloat_t *)&b->v);
cpFloatx2_t w = vmake(a->w, b->w);
cpFloatx2_t v1 = vadd(v_a, vmul_n(r1p, vget_lane(w, 0)));
cpFloatx2_t v2 = vadd(v_b, vmul_n(r2p, vget_lane(w, 1)));
cpFloatx2_t vr = vsub(v2, v1);
cpFloatx2_t vbn_vrn = vpadd(vmul(vbr, n), vmul(vr, n));
cpFloatx2_t v_offset = vmake(con->bias, -con->bounce);
cpFloatx2_t jOld = vmake(con->jBias, con->jnAcc);
cpFloatx2_t jbn_jn = vmul_n(vsub(v_offset, vbn_vrn), con->nMass);
jbn_jn = vmax(vadd(jOld, jbn_jn), vdup_n(0.0));
cpFloatx2_t jApply = vsub(jbn_jn, jOld);
cpFloatx2_t t = vmul(vrev(n), perp);
cpFloatx2_t vrt_tmp = vmul(vadd(vr, surface_vr), t);
cpFloatx2_t vrt = vpadd(vrt_tmp, vrt_tmp);
cpFloatx2_t jtOld = {}; jtOld = vset_lane(con->jtAcc, jtOld, 0);
cpFloatx2_t jtMax = vrev(vmul_n(jbn_jn, friction));
cpFloatx2_t jt = vmul_n(vrt, -con->tMass);
jt = vmax(vneg(jtMax), vmin(vadd(jtOld, jt), jtMax));
cpFloatx2_t jtApply = vsub(jt, jtOld);
cpFloatx2_t i_inv = vmake(-a->i_inv, b->i_inv);
cpFloatx2_t nperp = vmake(1.0, -1.0);
cpFloatx2_t jBias = vmul_n(n, vget_lane(jApply, 0));
cpFloatx2_t jBiasCross = vmul(vrev(jBias), nperp);
cpFloatx2_t biasCrosses = vpadd(vmul(r1, jBiasCross), vmul(r2, jBiasCross));
wBias = vadd(wBias, vmul(i_inv, biasCrosses));
vBias_a = vsub(vBias_a, vmul_n(jBias, a->m_inv));
vBias_b = vadd(vBias_b, vmul_n(jBias, b->m_inv));
cpFloatx2_t j = vadd(vmul_n(n, vget_lane(jApply, 1)), vmul_n(t, vget_lane(jtApply, 0)));
cpFloatx2_t jCross = vmul(vrev(j), nperp);
cpFloatx2_t crosses = vpadd(vmul(r1, jCross), vmul(r2, jCross));
w = vadd(w, vmul(i_inv, crosses));
v_a = vsub(v_a, vmul_n(j, a->m_inv));
v_b = vadd(v_b, vmul_n(j, b->m_inv));
// TODO would moving these earlier help pipeline them better?
vst((cpFloat_t *)&a->v_bias, vBias_a);
vst((cpFloat_t *)&b->v_bias, vBias_b);
vst_lane((cpFloat_t *)&a->w_bias, wBias, 0);
vst_lane((cpFloat_t *)&b->w_bias, wBias, 1);
vst((cpFloat_t *)&a->v, v_a);
vst((cpFloat_t *)&b->v, v_b);
vst_lane((cpFloat_t *)&a->w, w, 0);
vst_lane((cpFloat_t *)&b->w, w, 1);
vst_lane((cpFloat_t *)&con->jBias, jbn_jn, 0);
vst_lane((cpFloat_t *)&con->jnAcc, jbn_jn, 1);
vst_lane((cpFloat_t *)&con->jtAcc, jt, 0);
}
}
#endif
//MARK: PThreads
// Right now using more than 2 threads probably wont help your performance any.
// If you are using a ridiculous number of iterations it could help though.
#define MAX_THREADS 2
struct ThreadContext {
pthread_t thread;
cpHastySpace *space;
unsigned long thread_num;
};
typedef void (*cpHastySpaceWorkFunction)(cpSpace *space, unsigned long worker, unsigned long worker_count);
struct cpHastySpace {
cpSpace space;
// Number of worker threads (including the main thread)
unsigned long num_threads;
// Number of worker threads currently executing. (also including the main thread)
unsigned long num_working;
// Number of constraints (plus contacts) that must exist per step to start the worker threads.
unsigned long constraint_count_threshold;
pthread_mutex_t mutex;
pthread_cond_t cond_work, cond_resume;
// Work function to invoke.
cpHastySpaceWorkFunction work;
struct ThreadContext workers[MAX_THREADS - 1];
};
static void *
WorkerThreadLoop(struct ThreadContext *context)
{
cpHastySpace *hasty = context->space;
unsigned long thread = context->thread_num;
unsigned long num_threads = hasty->num_threads;
for(;;){
pthread_mutex_lock(&hasty->mutex); {
if(--hasty->num_working == 0){
pthread_cond_signal(&hasty->cond_resume);
}
pthread_cond_wait(&hasty->cond_work, &hasty->mutex);
} pthread_mutex_unlock(&hasty->mutex);
cpHastySpaceWorkFunction func = hasty->work;
if(func){
hasty->work(&hasty->space, thread, num_threads);
} else {
break;
}
}
return NULL;
}
static void
RunWorkers(cpHastySpace *hasty, cpHastySpaceWorkFunction func)
{
hasty->num_working = hasty->num_threads - 1;
hasty->work = func;
if(hasty->num_working > 0){
pthread_mutex_lock(&hasty->mutex); {
pthread_cond_broadcast(&hasty->cond_work);
} pthread_mutex_unlock(&hasty->mutex);
func((cpSpace *)hasty, 0, hasty->num_threads);
pthread_mutex_lock(&hasty->mutex); {
if(hasty->num_working > 0){
pthread_cond_wait(&hasty->cond_resume, &hasty->mutex);
}
} pthread_mutex_unlock(&hasty->mutex);
} else {
func((cpSpace *)hasty, 0, hasty->num_threads);
}
hasty->work = NULL;
}
static void
Solver(cpSpace *space, unsigned long worker, unsigned long worker_count)
{
cpArray *constraints = space->constraints;
cpArray *arbiters = space->arbiters;
cpFloat dt = space->curr_dt;
unsigned long iterations = (space->iterations + worker_count - 1)/worker_count;
for(unsigned long i=0; i<iterations; i++){
for(int j=0; j<arbiters->num; j++){
cpArbiter *arb = (cpArbiter *)arbiters->arr[j];
#ifdef __ARM_NEON__
cpArbiterApplyImpulse_NEON(arb);
#else
cpArbiterApplyImpulse(arb);
#endif
}
for(int j=0; j<constraints->num; j++){
cpConstraint *constraint = (cpConstraint *)constraints->arr[j];
constraint->klass->applyImpulse(constraint, dt);
}
}
}
//MARK: Thread Management Functions
static void
HaltThreads(cpHastySpace *hasty)
{
pthread_mutex_t *mutex = &hasty->mutex;
pthread_mutex_lock(mutex); {
hasty->work = NULL; // NULL work function means break and exit
pthread_cond_broadcast(&hasty->cond_work);
} pthread_mutex_unlock(mutex);
for(unsigned long i=0; i<(hasty->num_threads-1); i++){
pthread_join(hasty->workers[i].thread, NULL);
}
}
void
cpHastySpaceSetThreads(cpSpace *space, unsigned long threads)
{
#if TARGET_IPHONE_SIMULATOR == 1
// Individual values appear to be written non-atomically when compiled as debug for the simulator.
// No idea why, so threads are disabled.
threads = 1;
#endif
cpHastySpace *hasty = (cpHastySpace *)space;
HaltThreads(hasty);
#ifdef __APPLE__
if(threads == 0){
size_t size = sizeof(threads);
sysctlbyname("hw.ncpu", &threads, &size, NULL, 0);
}
#else
if(threads == 0) threads = 1;
#endif
hasty->num_threads = (threads < MAX_THREADS ? threads : MAX_THREADS);
hasty->num_working = hasty->num_threads - 1;
// Create the worker threads and wait for them to signal ready.
if(hasty->num_working > 0){
pthread_mutex_lock(&hasty->mutex);
for(unsigned long i=0; i<(hasty->num_threads-1); i++){
hasty->workers[i].space = hasty;
hasty->workers[i].thread_num = i + 1;
pthread_create(&hasty->workers[i].thread, NULL, (void*(*)(void*))WorkerThreadLoop, &hasty->workers[i]);
}
pthread_cond_wait(&hasty->cond_resume, &hasty->mutex);
pthread_mutex_unlock(&hasty->mutex);
}
}
unsigned long
cpHastySpaceGetThreads(cpSpace *space)
{
return ((cpHastySpace *)space)->num_threads;
}
//MARK: Overriden cpSpace Functions.
cpSpace *
cpHastySpaceNew(void)
{
cpHastySpace *hasty = (cpHastySpace *)cpcalloc(1, sizeof(cpHastySpace));
cpSpaceInit((cpSpace *)hasty);
pthread_mutex_init(&hasty->mutex, NULL);
pthread_cond_init(&hasty->cond_work, NULL);
pthread_cond_init(&hasty->cond_resume, NULL);
// TODO magic number, should test this more thoroughly.
hasty->constraint_count_threshold = 50;
// Default to 1 thread for determinism.
hasty->num_threads = 1;
cpHastySpaceSetThreads((cpSpace *)hasty, 1);
return (cpSpace *)hasty;
}
void
cpHastySpaceFree(cpSpace *space)
{
cpHastySpace *hasty = (cpHastySpace *)space;
HaltThreads(hasty);
pthread_mutex_destroy(&hasty->mutex);
pthread_cond_destroy(&hasty->cond_work);
pthread_cond_destroy(&hasty->cond_resume);
cpSpaceFree(space);
}
void
cpHastySpaceStep(cpSpace *space, cpFloat dt)
{
// don't step if the timestep is 0!
if(dt == 0.0f) return;
space->stamp++;
cpFloat prev_dt = space->curr_dt;
space->curr_dt = dt;
cpArray *bodies = space->dynamicBodies;
cpArray *constraints = space->constraints;
cpArray *arbiters = space->arbiters;
// Reset and empty the arbiter list.
for(int i=0; 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.
cpHastySpace *hasty = (cpHastySpace *)space;
if((unsigned long)(arbiters->num + constraints->num) > hasty->constraint_count_threshold){
RunWorkers(hasty, Solver);
} else {
Solver(space, 0, 1);
}
// Run the constraint post-solve callbacks
for(int i=0; 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);
}

View File

@@ -0,0 +1,157 @@
// Copyright 2013 Howling Moon Software. All rights reserved.
// See http://chipmunk2d.net/legal.php for more information.
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "chipmunk/chipmunk.h"
#include "chipmunk/cpMarch.h"
typedef void (*cpMarchCellFunc)(
cpFloat t, cpFloat a, cpFloat b, cpFloat c, cpFloat d,
cpFloat x0, cpFloat x1, cpFloat y0, cpFloat y1,
cpMarchSegmentFunc segment, void *segment_data
);
// The looping and sample caching code is shared between cpMarchHard() and cpMarchSoft().
static void
cpMarchCells(
cpBB bb, unsigned long x_samples, unsigned long y_samples, cpFloat t,
cpMarchSegmentFunc segment, void *segment_data,
cpMarchSampleFunc sample, void *sample_data,
cpMarchCellFunc cell
){
cpFloat x_denom = 1.0/(cpFloat)(x_samples - 1);
cpFloat y_denom = 1.0/(cpFloat)(y_samples - 1);
// TODO range assertions and short circuit for 0 sized windows.
// Keep a copy of the previous row to avoid double lookups.
cpFloat *buffer = (cpFloat *)cpcalloc(x_samples, sizeof(cpFloat));
for(unsigned long i=0; i<x_samples; i++) buffer[i] = sample(cpv(cpflerp(bb.l, bb.r, i*x_denom), bb.b), sample_data);
for(unsigned long j=0; j<y_samples-1; j++){
cpFloat y0 = cpflerp(bb.b, bb.t, (j+0)*y_denom);
cpFloat y1 = cpflerp(bb.b, bb.t, (j+1)*y_denom);
cpFloat a, b = buffer[0];
cpFloat c, d = sample(cpv(bb.l, y1), sample_data);
buffer[0] = d;
for(unsigned long i=0; i<x_samples-1; i++){
cpFloat x0 = cpflerp(bb.l, bb.r, (i+0)*x_denom);
cpFloat x1 = cpflerp(bb.l, bb.r, (i+1)*x_denom);
a = b; b = buffer[i + 1];
c = d; d = sample(cpv(x1, y1), sample_data);
buffer[i + 1] = d;
cell(t, a, b, c, d, x0, x1, y0, y1, segment, segment_data);
}
}
cpfree(buffer);
}
// TODO should flip this around eventually.
static inline void
seg(cpVect v0, cpVect v1, cpMarchSegmentFunc f, void *data)
{
if(!cpveql(v0, v1)) f(v1, v0, data);
}
// Lerps between two positions based on their sample values.
static inline cpFloat
midlerp(cpFloat x0, cpFloat x1, cpFloat s0, cpFloat s1, cpFloat t)
{
return cpflerp(x0, x1, (t - s0)/(s1 - s0));
}
static void
cpMarchCellSoft(
cpFloat t, cpFloat a, cpFloat b, cpFloat c, cpFloat d,
cpFloat x0, cpFloat x1, cpFloat y0, cpFloat y1,
cpMarchSegmentFunc segment, void *segment_data
){
// TODO this switch part is super expensive, can it be NEONized?
switch((a>t)<<0 | (b>t)<<1 | (c>t)<<2 | (d>t)<<3){
case 0x1: seg(cpv(x0, midlerp(y0,y1,a,c,t)), cpv(midlerp(x0,x1,a,b,t), y0), segment, segment_data); break;
case 0x2: seg(cpv(midlerp(x0,x1,a,b,t), y0), cpv(x1, midlerp(y0,y1,b,d,t)), segment, segment_data); break;
case 0x3: seg(cpv(x0, midlerp(y0,y1,a,c,t)), cpv(x1, midlerp(y0,y1,b,d,t)), segment, segment_data); break;
case 0x4: seg(cpv(midlerp(x0,x1,c,d,t), y1), cpv(x0, midlerp(y0,y1,a,c,t)), segment, segment_data); break;
case 0x5: seg(cpv(midlerp(x0,x1,c,d,t), y1), cpv(midlerp(x0,x1,a,b,t), y0), segment, segment_data); break;
case 0x6: seg(cpv(midlerp(x0,x1,a,b,t), y0), cpv(x1, midlerp(y0,y1,b,d,t)), segment, segment_data);
seg(cpv(midlerp(x0,x1,c,d,t), y1), cpv(x0, midlerp(y0,y1,a,c,t)), segment, segment_data); break;
case 0x7: seg(cpv(midlerp(x0,x1,c,d,t), y1), cpv(x1, midlerp(y0,y1,b,d,t)), segment, segment_data); break;
case 0x8: seg(cpv(x1, midlerp(y0,y1,b,d,t)), cpv(midlerp(x0,x1,c,d,t), y1), segment, segment_data); break;
case 0x9: seg(cpv(x0, midlerp(y0,y1,a,c,t)), cpv(midlerp(x0,x1,a,b,t), y0), segment, segment_data);
seg(cpv(x1, midlerp(y0,y1,b,d,t)), cpv(midlerp(x0,x1,c,d,t), y1), segment, segment_data); break;
case 0xA: seg(cpv(midlerp(x0,x1,a,b,t), y0), cpv(midlerp(x0,x1,c,d,t), y1), segment, segment_data); break;
case 0xB: seg(cpv(x0, midlerp(y0,y1,a,c,t)), cpv(midlerp(x0,x1,c,d,t), y1), segment, segment_data); break;
case 0xC: seg(cpv(x1, midlerp(y0,y1,b,d,t)), cpv(x0, midlerp(y0,y1,a,c,t)), segment, segment_data); break;
case 0xD: seg(cpv(x1, midlerp(y0,y1,b,d,t)), cpv(midlerp(x0,x1,a,b,t), y0), segment, segment_data); break;
case 0xE: seg(cpv(midlerp(x0,x1,a,b,t), y0), cpv(x0, midlerp(y0,y1,a,c,t)), segment, segment_data); break;
default: break; // 0x0 and 0xF
}
}
void
cpMarchSoft(
cpBB bb, unsigned long x_samples, unsigned long y_samples, cpFloat t,
cpMarchSegmentFunc segment, void *segment_data,
cpMarchSampleFunc sample, void *sample_data
){
cpMarchCells(bb, x_samples, y_samples, t, segment, segment_data, sample, sample_data, cpMarchCellSoft);
}
// TODO should flip this around eventually.
static inline void
segs(cpVect a, cpVect b, cpVect c, cpMarchSegmentFunc f, void *data)
{
seg(b, c, f, data);
seg(a, b, f, data);
}
static void
cpMarchCellHard(
cpFloat t, cpFloat a, cpFloat b, cpFloat c, cpFloat d,
cpFloat x0, cpFloat x1, cpFloat y0, cpFloat y1,
cpMarchSegmentFunc segment, void *segment_data
){
// midpoints
cpFloat xm = cpflerp(x0, x1, 0.5f);
cpFloat ym = cpflerp(y0, y1, 0.5f);
switch((a>t)<<0 | (b>t)<<1 | (c>t)<<2 | (d>t)<<3){
case 0x1: segs(cpv(x0, ym), cpv(xm, ym), cpv(xm, y0), segment, segment_data); break;
case 0x2: segs(cpv(xm, y0), cpv(xm, ym), cpv(x1, ym), segment, segment_data); break;
case 0x3: seg(cpv(x0, ym), cpv(x1, ym), segment, segment_data); break;
case 0x4: segs(cpv(xm, y1), cpv(xm, ym), cpv(x0, ym), segment, segment_data); break;
case 0x5: seg(cpv(xm, y1), cpv(xm, y0), segment, segment_data); break;
case 0x6: segs(cpv(xm, y0), cpv(xm, ym), cpv(x0, ym), segment, segment_data);
segs(cpv(xm, y1), cpv(xm, ym), cpv(x1, ym), segment, segment_data); break;
case 0x7: segs(cpv(xm, y1), cpv(xm, ym), cpv(x1, ym), segment, segment_data); break;
case 0x8: segs(cpv(x1, ym), cpv(xm, ym), cpv(xm, y1), segment, segment_data); break;
case 0x9: segs(cpv(x1, ym), cpv(xm, ym), cpv(xm, y0), segment, segment_data);
segs(cpv(x0, ym), cpv(xm, ym), cpv(xm, y1), segment, segment_data); break;
case 0xA: seg(cpv(xm, y0), cpv(xm, y1), segment, segment_data); break;
case 0xB: segs(cpv(x0, ym), cpv(xm, ym), cpv(xm, y1), segment, segment_data); break;
case 0xC: seg(cpv(x1, ym), cpv(x0, ym), segment, segment_data); break;
case 0xD: segs(cpv(x1, ym), cpv(xm, ym), cpv(xm, y0), segment, segment_data); break;
case 0xE: segs(cpv(xm, y0), cpv(xm, ym), cpv(x0, ym), segment, segment_data); break;
default: break; // 0x0 and 0xF
}
}
void
cpMarchHard(
cpBB bb, unsigned long x_samples, unsigned long y_samples, cpFloat t,
cpMarchSegmentFunc segment, void *segment_data,
cpMarchSampleFunc sample, void *sample_data
){
cpMarchCells(bb, x_samples, y_samples, t, segment, segment_data, sample, sample_data, cpMarchCellHard);
}

View File

@@ -0,0 +1,172 @@
/* 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;
}

View File

@@ -0,0 +1,152 @@
/* 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;
}

View File

@@ -0,0 +1,324 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "chipmunk/chipmunk_private.h"
#include "chipmunk/chipmunk_unsafe.h"
cpPolyShape *
cpPolyShapeAlloc(void)
{
return (cpPolyShape *)cpcalloc(1, sizeof(cpPolyShape));
}
static void
cpPolyShapeDestroy(cpPolyShape *poly)
{
if(poly->count > CP_POLY_SHAPE_INLINE_ALLOC){
cpfree(poly->planes);
}
}
static cpBB
cpPolyShapeCacheData(cpPolyShape *poly, cpTransform transform)
{
int count = poly->count;
struct cpSplittingPlane *dst = poly->planes;
struct cpSplittingPlane *src = dst + count;
cpFloat l = (cpFloat)INFINITY, r = -(cpFloat)INFINITY;
cpFloat b = (cpFloat)INFINITY, t = -(cpFloat)INFINITY;
for(int i=0; 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;
outside = outside || (cpvdot(planes[i].n, cpvsub(p,v1)) > 0.0f);
cpVect closest = cpClosetPointOnSegment(p, v0, v1);
cpFloat dist = cpvdist(p, closest);
if(dist < minDist){
minDist = dist;
closestPoint = closest;
closestNormal = planes[i].n;
}
v0 = v1;
}
cpFloat dist = (outside ? minDist : -minDist);
cpVect g = cpvmult(cpvsub(p, closestPoint), 1.0f/dist);
info->shape = (cpShape *)poly;
info->point = cpvadd(closestPoint, cpvmult(g, r));
info->distance = dist - r;
// Use the normal of the closest segment if the distance is small.
info->gradient = (minDist > MAGIC_EPSILON ? g : closestNormal);
}
static void
cpPolyShapeSegmentQuery(cpPolyShape *poly, cpVect a, cpVect b, cpFloat r2, cpSegmentQueryInfo *info)
{
struct cpSplittingPlane *planes = poly->planes;
int count = poly->count;
cpFloat r = poly->r;
cpFloat rsum = r + r2;
for(int i=0; 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);
// Avoid divide by zero. (d is always positive)
cpFloat t = d/cpfmax(an - bn, CPFLOAT_MIN);
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);
}
cpShape *
cpPolyShapeNewRaw(cpBody *body, int count, const cpVect *verts, cpFloat radius)
{
return (cpShape *)cpPolyShapeInitRaw(cpPolyShapeAlloc(), body, count, verts, radius);
}
cpPolyShape *
cpBoxShapeInit(cpPolyShape *poly, cpBody *body, cpFloat width, cpFloat height, cpFloat radius)
{
cpFloat hw = width/2.0f;
cpFloat hh = height/2.0f;
return cpBoxShapeInit2(poly, body, cpBBNew(-hw, -hh, hw, hh), radius);
}
cpPolyShape *
cpBoxShapeInit2(cpPolyShape *poly, cpBody *body, cpBB box, cpFloat radius)
{
cpVect verts[] = {
cpv(box.r, box.b),
cpv(box.r, box.t),
cpv(box.l, box.t),
cpv(box.l, box.b),
};
return cpPolyShapeInitRaw(poly, body, 4, verts, radius);
}
cpShape *
cpBoxShapeNew(cpBody *body, cpFloat width, cpFloat height, cpFloat radius)
{
return (cpShape *)cpBoxShapeInit(cpPolyShapeAlloc(), body, width, height, radius);
}
cpShape *
cpBoxShapeNew2(cpBody *body, cpBB box, cpFloat radius)
{
return (cpShape *)cpBoxShapeInit2(cpPolyShapeAlloc(), body, box, radius);
}
int
cpPolyShapeGetCount(const cpShape *shape)
{
cpAssertHard(shape->klass == &polyClass, "Shape is not a poly shape.");
return ((cpPolyShape *)shape)->count;
}
cpVect
cpPolyShapeGetVert(const cpShape *shape, int i)
{
cpAssertHard(shape->klass == &polyClass, "Shape is not a poly shape.");
int count = cpPolyShapeGetCount(shape);
cpAssertHard(0 <= i && i < count, "Index out of range.");
return ((cpPolyShape *)shape)->planes[i + count].v0;
}
cpFloat
cpPolyShapeGetRadius(const cpShape *shape)
{
cpAssertHard(shape->klass == &polyClass, "Shape is not a poly shape.");
return ((cpPolyShape *)shape)->r;
}
// Unsafe API (chipmunk_unsafe.h)
void
cpPolyShapeSetVerts(cpShape *shape, int count, cpVect *verts, cpTransform transform)
{
cpVect *hullVerts = (cpVect *)alloca(count*sizeof(cpVect));
// Transform the verts before building the hull in case of a negative scale.
for(int i=0; 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);
}

Some files were not shown because too many files have changed in this diff Show More