massive refactor

This commit is contained in:
John Alanbrook 2024-10-29 12:41:17 -05:00
parent c89f28dcec
commit 7c324c1b33
200 changed files with 258 additions and 90311 deletions

304
Makefile
View file

@ -1,297 +1,13 @@
PROCS != nproc --all debug: FORCE
MAKEFLAGS = -j $(PROCS) meson setup build_dbg -Dbuildtype=debugoptimized
UNAME != uname meson compile -C build_dbg
MAKEDIR != pwd
# Options
# NDEBUG --- build with debugging symbols and logging
ifeq ($(ARCH),) release: FORCE
ARCH != uname -m meson setup -Dbuildtype=release -Db_lto=true -Db_lto_threads=4 -Db_ndebug=true -Db_pgo=use build_release
endif meson compile -C build_release
CXX:=$(CC) sanitize: FORCE
meson setup -Db_sanitize=address -Db_sanitize=memory -Db_sanitize=leak -Db_sanitize=undefined build_sani
meson compile -C build_sani
OPT ?= 0 FORCE:
LD = $(CC)
STEAM = steam/sdk
STEAMAPI =
LDFLAGS += -lstdc++
ifeq ($(CROSS)$(CC), emcc)
LDFLAGS += --shell-file shell.html --embed-file ld56@/ #--closure 1
CPPFLAGS += -Wbad-function-cast -Wcast-function-type -sSTACK_SIZE=1MB -sALLOW_MEMORY_GROWTH -sINITIAL_MEMORY=128MB -pthread -s USE_PTHREADS=1
NDEBUG = 1
ARCH:= wasm
endif
ifdef NEDITOR
CPPFLAGS += -DNO_EDITOR
endif
ifdef NFLAC
CPPFLAGS += -DNFLAC
endif
ifdef NMP3
CPPFLAGS += -DNMP3
endif
ifdef NSVG
CPPFLAGS += -DNSVG
endif
ifdef NQOA
CPPFLAGS += -DNQOA
endif
ifdef NDEBUG
CPPFLAGS += -DNDEBUG
else
CPPFLAGS += -g -DDUMP
INFO :=$(INFO)_dbg
endif
ifdef NSTEAM
CPPFLAGS += -DNSTEAM
endif
ifdef LEAK
CPPFLAGS += -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer -DLEAK
INFO := $(INFO)_leak
endif
ifeq ($(OPT),small)
CPPFLAGS += -Os -flto -fno-ident -fno-asynchronous-unwind-tables -ffunction-sections -fdata-sections
LDFLAGS += -flto
INFO :=$(INFO)_small
else ifeq ($(OPT), 1)
CPPFLAGS += -O3 -flto
INFO :=$(INFO)_opt
else
CPPFLAGS += -O2
endif
CXXFLAGS += -std=c++14
CPPFLAGS += -DHAVE_CEIL -DCP_USE_CGTYPES=0 -DCP_USE_DOUBLES=0 -DHAVE_FLOOR -DHAVE_FMOD -DHAVE_LRINT -DHAVE_LRINTF $(includeflag) $(WARNING_FLAGS) -I. -DPROSPERON_VER=\"$(SEM)\" -DPROSPERON_COM=\"$(COM)\" -DPROSPERON_DATE=\"$(DATE)\" -DPROSPERON_INFO=\"$(INFO)\" -Wno-narrowing -Wno-int-conversion #-DENABLE_SINC_MEDIUM_CONVERTER -DENABLE_SINC_FAST_CONVERTER -DCP_COLLISION_TYPE_TYPE=uintptr_t -DCP_BITMASK_TYPE=uintptr_t
CPPFLAGS += -DCONFIG_VERSION=\"2024-02-14\" -DCONFIG_BIGNUM #for quickjs
# ENABLE_SINC_[BEST|FAST|MEDIUM]_CONVERTER
# default, fast and medium available in game at runtime; best available in editor
INFO := $(INFO)_$(ARCH)
ifeq ($(OS), Windows_NT) # then WINDOWS
PLATFORM := win64
DEPS += resource.o
STEAMAPI := steam_api64
LDFLAGS += -mwin32 -static
CPPFLAGS += -mwin32
LDLIBS += mingw32 kernel32 d3d11 user32 shell32 dxgi gdi32 ws2_32 ole32 winmm setupapi m pthread
INFO :=$(INFO)_win
EXT = .exe
else ifeq ($(OS), IOS)
CC = /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang
SDK = iphoneos
SDK_PATH = /Applications/Xcode.app/Contents/Developer/Platforms/$(SDK).platform/Developer/SDKs/$(SDK).sdk
CFLAGS += -isysroot $(SDK_PATH) -miphoneos-version-min=13.0
LDFLAGS += -isysroot $(SDK_PATH) -miphoneos-version-min=13.0
LDFLAGS += -framework Foundation -framework UIKit -framework AudioToolbox -framework Metal -framework MetalKit -framework AVFoundation
CFLAGS += -x objective-c -DIOS
INFO :=$(INFO)_ios
else ifeq ($(OS), wasm) # Then WEB
OS := Web
LDFLAGS += -sUSE_WEBGPU -pthread
CPPFLAGS += -DNSTEAM -pthread
CFLAGS += -pthread
LDLIBS += GL openal c m dl
STEAMAPI :=
EXT = .html
else
UNAME != uname -s
ifeq ($(UNAME), Linux) # then LINUX
OS := Linux
PLATFORM := linux64
LDFLAGS += -pthread -rdynamic
LDLIBS += pthread c m dl X11 Xi Xcursor EGL asound GL
INFO :=$(INFO)_linux
STEAMAPI := steam_api
endif
ifeq ($(UNAME), Darwin)
OS := macos
PLATFORM := osx
CPPFLAGS += -arch $(ARCH)
CFLAGS += -x objective-c
LDFLAGS += -framework Cocoa -framework QuartzCore -framework AudioToolbox -framework Metal -framework MetalKit
INFO :=$(INFO)_macos
STEAMAPI := steam_api
endif
endif
ifdef NSTEAM
STEAMAPI =
endif
# All other sources
OBJS != find source -type f -name '*.c' | grep -vE 'test|tool|example|fuzz|main' | grep -vE 'quickjs'
CPPOBJS != find source -type f -name '*.cpp' | grep -vE 'test|tool|example|fuzz|main|GraphEditor|ImCurveEdit'
OBJS += $(CPPOBJS)
OBJS += source/engine/yugine.c
OBJS += $(shell find source/engine -type f -name '*.m')
QUICKJS := source/engine/thirdparty/quickjs
OBJS += $(addprefix $(QUICKJS)/, libregexp.c quickjs.c libunicode.c cutils.c libbf.c)
OBJS := $(patsubst %.cpp, %$(INFO).o, $(OBJS))
OBJS := $(patsubst %.c, %$(INFO).o,$(OBJS))
OBJS := $(patsubst %.m, %$(INFO).o, $(OBJS))
engineincs != find source/engine -maxdepth 1 -type d
includeflag != find source -type d -name include
includeflag += $(engineincs) source/engine/thirdparty/sokol source/engine/thirdparty/stb source/engine/thirdparty/cgltf source/engine/thirdparty/TinySoundFont source/engine/thirdparty/dr_libs source/engine/thirdparty/imgui
includeflag += $(STEAM)/public
includeflag += source
includeflag := $(addprefix -I, $(includeflag))
WARNING_FLAGS = -Wno-incompatible-function-pointer-types -Wno-incompatible-pointer-types
# For vanilla compilation, remove _
ifeq ($(INFO),_)
INFO :=
endif
APP = prosperon
NAME = $(APP)$(INFO)$(EXT)
SEM != git describe --tags --abbrev=0
COM != git rev-parse --short HEAD
DATE != date +%s
LDLIBS += $(STEAMAPI)
LDLIBS := $(addprefix -l, $(LDLIBS))
LDPATHS := $(STEAM)/redistributable_bin/$(PLATFORM)
LDPATHS := $(addprefix -L, $(LDPATHS))
DEPENDS = $(OBJS:.o=.d)
ifndef VERBOSE
.SILENT:
endif
DEPFLAGS = -MT $(@:.d=.o) -MM -MG $< -o $@
%$(INFO).d: %.c
@echo Making deps $@
$(CROSS)$(CC) $(CPPFLAGS) $(DEPFLAGS)
%$(INFO).d: %.cpp
@echo Making deps $@
$(CROSS)$(CXX) $(CPPFLAGS) $(DEPFLAGS)
%$(INFO).d: %.m
@echo Making deps $@
$(CROSS)$(CC) $(CPPFLAGS) $(DEPFLAGS)
ifneq ($(MAKECMDGOALS), clean)
include $(DEPENDS)
endif
.DEFAULT_GOAL := all
all: $(NAME) core.zip
cp -f $(NAME) $(APP)$(EXT)
$(APP): $(NAME) core.zip
cp -f $(NAME) $(APP)
$(NAME): $(OBJS) $(DEPS)
@echo Linking $(NAME)
$(CROSS)$(LD) $^ $(CPPFLAGS) $(LDFLAGS) -L. $(LDPATHS) $(LDLIBS) -o $@
@echo Finished build
%$(INFO).o: %.c
@echo Making C object $@
$(CROSS)$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
%$(INFO).o: %.cpp
@echo Making C++ object $@
$(CROSS)$(CXX) $(CPPFLAGS) $(CXXFLAGS) -fpermissive -c $< -o $@
%$(INFO).o: %.m
@echo Making Objective-C object $@
$(CROSS)$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
CORE != (ls icons/* fonts/* shaders/*.cg scripts/*.js*)
packer: tools/packer.c source/engine/miniz.c
@echo Making packer
$(CC) -O2 $^ -Isource/engine -o packer
core.zip: packer $(CORE)
@echo Packing core.zip
./packer $@ $(CORE)
ICNSIZE = 16 32 128 256 512 1024
ICNNAME := $(addsuffix .png, $(ICNSIZE))
ICON = icons/moon.gif
icon.ico: $(ICON)
for i in $(ICNSIZE); do magick $^ -thumbnail $${i}x$${i} $${i}.png; done
magick $(ICNNAME) icon.ico
rm $(ICNNAME)
resource.o: resource.rc resource.manifest icon.ico
$(CROSS)windres -i $< -o $@
crossios:
make OS=IOS ARCH=arm64 DEBUG=$(DEBUG) OPT=$(OPT)
Prosperon.icns: $(ICON)
mkdir -p Prosperon.iconset
for i in $(ICNSIZE); do magick $^ -size $${i}x$${i} Prosperon.iconset/icon_$${i}x$${i}.png; done
iconutil -c icns Prosperon.iconset
crossmac: Prosperon.icns
make ARCH=arm64 DEBUG=$(DEBUG) OPT=$(OPT)
mv $(APP) mac_arm64
make ARCH=x86_64 DEBUG=$(DEBUG) OPT=$(OPT)
mv $(APP) mac_x86_64
lipo mac_arm64 mac_x86_64 -create -output $(APP)_mac
rm mac_arm64 mac_x86_64
rm -rf Prosperon.app
mkdir Prosperon.app
mkdir Prosperon.app/Contents
mkdir Prosperon.app/Contents/MacOS
mkdir Prosperon.app/Contents/Resources
mv $(NAME) Prosperon.app/Contents/MacOS/Prosperon
cp Info.plist Prosperon.app/Contents
cp Prosperon.icns Prosperon.app/Contents/Resources
crosswin:
make CROSS=x86_64-w64-mingw32- OS=Windows_NT CC=gcc
crossweb:
make CC=emcc OS=wasm
mv $(APP).html index.html
clean:
@echo Cleaning project
rm -f core.zip jso cdb packer TAGS $(APP)* *.icns *.ico
find . -type f -name "*.[oad]" -delete
rm -rf Prosperon.app
docs: doc/prosperon.org
make -C doc
mv doc/html .
api: $(APP)
./prosperon run 'for (var i in globalThis) say(i)' | xargs -I {} ./prosperon api {} > docs/api/{}.md
TAGINC != find . -name "*.[chj]"
tags: $(TAGINC)
@echo Making tags.
@etags $(TAGINC)
MYFILES != (ls scripts/*.js* source/engine/*.[ch] source/engine/sound/*.[ch])
pretty:
clang-format -i $(MYFILES)

View file

@ -1,173 +0,0 @@
# Mum
#### padding
**array**
[
0,
0
]
#### offset
**array**
[
0,
0
]
#### font
**string**
#### selectable
**boolean**
#### selected
**boolean**
#### font_size
**number**
#### text_align
**string**
#### scale
**number**
#### angle
**number**
#### anchor
**array**
[
0,
1
]
#### hovered
**object**
#### text_shadow
**object**
#### text_outline
**number**
#### color
**array**
[
1,
1,
1,
1
]
#### margin
**array**
[
0,
0
]
#### width
**number**
#### height
**number**
#### max_width
**number**
#### max_height
**number**
#### image_repeat
**boolean**
#### image_repeat_offset
**array**
[
0,
0
]
#### debug
**boolean**
#### make(def)
#### prestart()
#### start()
#### extend(def)
#### text(def)
#### button(def)
#### window(def)
#### image(def)
#### column(def)
#### debug_colors
**object**

View file

@ -1,15 +0,0 @@
# Tween
#### default
**object**
#### start(obj, target, tvals, options)
#### make(obj, target, tvals, options)

3
globcore.sh Executable file
View file

@ -0,0 +1,3 @@
#!/bin/sh
ls icons/* fonts/* shaders/*.cg scripts/*.js*

View file

@ -1,57 +0,0 @@
this.phys = physics.kinematic;
this.dir_view2world = function (dir) {
return dir.scale(this.zoom);
};
this.view2world = function (pos) {
var useren = window.rendersize.scale(this.zoom);
if (window.mode === window.modetypes.stretch) {
pos = pos.scale([window.rendersize.x / window.size.x, window.rendersize.y / window.size.y]);
pos = pos.sub(window.rendersize.scale(0.5));
pos = pos.scale(this.zoom);
pos = pos.add(this.pos);
}
if (window.mode === window.modetypes.full) {
pos = pos.sub(window.size.scale(0.5));
pos = pos.scale(this.zoom);
pos = pos.add(this.pos);
}
if (window.mode === window.modetypes.expand) {
pos = pos.sub(window.size.scale(0.5));
pos = pos.scale([window.rendersize.x / window.size.x, window.rendersize.y / window.size.y]);
}
return pos;
};
this.world2view = function (pos) {
if (window.mode === window.modetypes.stretch) {
pos = pos.sub(this.pos);
pos = pos.scale(1.0 / this.zoom);
pos = pos.add(window.rendersize.scale(0.5));
}
if (window.mode === window.modetypes.full) {
pos = pos.sub(this.pos);
pos = pos.scale(1 / this.zoom);
pos = pos.add(window.size.scale(0.5));
}
if (window.mode === window.modetypes.expand) {
}
return pos;
};
this.screenright = function () {
return this.view2world(window.size).x;
};
this.screenleft = function () {
return this.view2world([0, 0]).x;
};
var zoom = 1;
Object.mixin(self, {
set zoom(x) {
zoom = x;
if (zoom <= 0.1) zoom = 0.1;
},
get zoom() {
return zoom;
},
});

View file

@ -42,9 +42,10 @@ var sprite = {
set diffuse(x) {}, set diffuse(x) {},
anim_speed: 1, anim_speed: 1,
play(str, loop = true, reverse = false) { play(str, loop = true, reverse = false) {
if (!this.animset) return;
str ??= this.anim; str ??= this.anim;
if (!str) return; if (!str) return;
if (typeof str === 'string') if (typeof str === 'string')
str = this.animset[str]; str = this.animset[str];
@ -97,6 +98,7 @@ var sprite = {
if (this.anim) this.stop(); if (this.anim) this.stop();
this.sync(); this.sync();
this.play(); this.play();
// this.transform.scale = [this.image.texture.width, this.image.texture.height];
}, },
stop() { stop() {
this.del_anim?.(); this.del_anim?.();
@ -241,7 +243,9 @@ sprite.inputs.kp1 = function () {
component.sprite = function (obj) { component.sprite = function (obj) {
var sp = Object.create(sprite); var sp = Object.create(sprite);
sp.gameobject = obj; sp.gameobject = obj;
sp.transform = obj.transform; sp.transform = os.make_transform();
sp.transform.scale = 18;
sp.transform.parent = obj.transform;
sp.guid = prosperon.guid(); sp.guid = prosperon.guid();
allsprites.push(sp); allsprites.push(sp);
sprite_addbucket(sp); sprite_addbucket(sp);
@ -250,6 +254,8 @@ component.sprite = function (obj) {
sprite.shade = [1, 1, 1, 1]; sprite.shade = [1, 1, 1, 1];
return {component};
Object.mixin(os.make_seg2d(), { Object.mixin(os.make_seg2d(), {
sync() { sync() {
this.set_endpoints(this.points[0], this.points[1]); this.set_endpoints(this.points[0], this.points[1]);
@ -697,7 +703,7 @@ var edge2d = {
}; };
component.edge2d = function (obj) { component.edge2d = function (obj) {
if (!obj.body) obj.rigidify(); // if (!obj.body) obj.rigidify();
var edge = Object.create(edge2d); var edge = Object.create(edge2d);
edge.body = obj.body; edge.body = obj.body;
return edge; return edge;
@ -865,7 +871,7 @@ edge2d.inputs.rb.rep = true;
function shape_maker(maker) { function shape_maker(maker) {
return function (obj) { return function (obj) {
if (!obj.body) obj.rigidify(); // if (!obj.body) obj.rigidify();
return maker(obj.body); return maker(obj.body);
}; };
} }

View file

@ -151,6 +151,7 @@ function find_ext(file, ext, root = "") {
if (!file) return; if (!file) return;
var file_ext = file.ext(); var file_ext = file.ext();
if (ext.some(x => x === file_ext)) return file; if (ext.some(x => x === file_ext)) return file;
for (var e of ext) { for (var e of ext) {
var nf = `${file}.${e}`; var nf = `${file}.${e}`;
@ -313,6 +314,11 @@ globalThis.use = function use(file) {
return ret; return ret;
}; };
globalThis.spinup = function spinup(file)
{
// spin up this file into its own entity
}
function stripped_use(file, script) { function stripped_use(file, script) {
file = Resources.find_script(file); file = Resources.find_script(file);

View file

@ -33,10 +33,6 @@ var entity = {
return undefined; return undefined;
}, },
rigidify() {
this.body = os.make_body(this.transform);
},
path_from(o) { path_from(o) {
var p = this.toString(); var p = this.toString();
var c = this.master; var c = this.master;

View file

@ -1,5 +1,7 @@
// Layout code // Layout code
// Contain is for how it will treat its children. If they should be laid out as a row, or column, or in a flex style, etc. // Contain is for how it will treat its children. If they should be laid out as a row, or column, or in a flex style, etc.
var lay_ctx = layout.make_context();
layout.contain = {}; layout.contain = {};
layout.contain.row = 0x002; layout.contain.row = 0x002;
layout.contain.column = 0x003; layout.contain.column = 0x003;
@ -63,9 +65,9 @@ clay.normalizeSpacing = function(spacing) {
clay.draw = function(size, fn, ) clay.draw = function(size, fn, )
{ {
layout.reset(); lay_ctx.reset();
boxes = []; boxes = [];
var root = layout.item({ var root = lay_ctx.item({
size:size, size:size,
contain: layout.contain.row, contain: layout.contain.row,
}); });
@ -76,12 +78,12 @@ clay.draw = function(size, fn, )
config:root_config config:root_config
}); });
fn(); fn();
layout.run(); lay_ctx.run();
// Adjust bounding boxes for padding // Adjust bounding boxes for padding
for (var i = 0; i < boxes.length; i++) { for (var i = 0; i < boxes.length; i++) {
var box = boxes[i]; var box = boxes[i];
box.content = layout.get_rect(box.id); box.content = lay_ctx.get_rect(box.id);
box.boundingbox = Object.assign({}, box.content); box.boundingbox = Object.assign({}, box.content);
var padding = clay.normalizeSpacing(box.config.padding || 0); var padding = clay.normalizeSpacing(box.config.padding || 0);
@ -107,8 +109,6 @@ clay.draw = function(size, fn, )
return boxes; return boxes;
} }
var last_config;
function create_view_fn(base_config) function create_view_fn(base_config)
{ {
var base = Object.assign(Object.create(clay_base), base_config); var base = Object.assign(Object.create(clay_base), base_config);
@ -173,12 +173,12 @@ var add_item = function(config)
l:margin.l+padding.l l:margin.l+padding.l
}; };
var item = layout.item(use_config); var item = lay_ctx.item(use_config);
boxes.push({ boxes.push({
id:item, id:item,
config:use_config config:use_config
}); });
layout.insert(root_item,item); lay_ctx.insert(root_item,item);
// Increment the parent's child index // Increment the parent's child index
root_config._childIndex++; root_config._childIndex++;
@ -271,3 +271,6 @@ layout.draw_commands = function(cmds, pos = [0,0])
// render.rectangle(cmd.marginbox, [0,0,1,0.1]); // render.rectangle(cmd.marginbox, [0,0,1,0.1]);
} }
} }
return layout;

View file

@ -1,3 +1,5 @@
var layout = use("layout.js");
this.hud = function () { this.hud = function () {
layout.draw_commands(clay.draw([], _ => { layout.draw_commands(clay.draw([], _ => {
clay.text("No game yet! Make game.js to get started!"); clay.text("No game yet! Make game.js to get started!");

View file

@ -29,7 +29,6 @@ emitter.spawn = function (t) {
var par = this.dead.shift(); var par = this.dead.shift();
if (par) { if (par) {
par.body.pos = t.pos;
par.transform.scale = this.scale; par.transform.scale = this.scale;
this.particles.push(par); this.particles.push(par);
par.time = 0; par.time = 0;
@ -45,9 +44,6 @@ emitter.spawn = function (t) {
color: this.color, color: this.color,
}; };
par.body = os.make_body(par.transform);
par.body.pos = t.pos;
par.transform.scale = this.scale; par.transform.scale = this.scale;
this.particles.push(par); this.particles.push(par);

View file

@ -9,7 +9,7 @@ var HIT = {
}; };
*/ */
physics.pos_query = function (pos, start = world, give = 10) { export function pos_query(pos, start = world, give = 10) {
var ret; var ret;
ret = physics.point_query_nearest(pos, 0); ret = physics.point_query_nearest(pos, 0);
@ -21,7 +21,7 @@ physics.pos_query = function (pos, start = world, give = 10) {
}); });
}; };
physics.box_point_query = function (box, points) { export function box_point_query(box, points) {
if (!box || !points) return []; if (!box || !points) return [];
var bbox = bbox.fromcwh(box.pos, box.wh); var bbox = bbox.fromcwh(box.pos, box.wh);
var inside = []; var inside = [];

View file

@ -59,7 +59,7 @@ game.engine_start = function (s) {
game.startengine = 1; game.startengine = 1;
gggstart( gggstart(
function () { function () {
global.mixin("scripts/sound.js"); // global.mixin("scripts/sound.js");
world_start(); world_start();
window.set_icon(game.texture("moon").texture); window.set_icon(game.texture("moon").texture);
Object.readonly(window.__proto__, "vsync"); Object.readonly(window.__proto__, "vsync");
@ -76,8 +76,6 @@ game.engine_start = function (s) {
camera.size = game.size; camera.size = game.size;
gamestate.camera = camera; gamestate.camera = camera;
globalThis.imgui = render.imgui_init();
s(); s();
shape.quad = { shape.quad = {
@ -461,9 +459,8 @@ global.mixin("scripts/color");
global.mixin("scripts/tween"); global.mixin("scripts/tween");
global.mixin("scripts/ai"); global.mixin("scripts/ai");
global.mixin("scripts/particle"); global.mixin("scripts/particle");
global.mixin("scripts/physics"); //global.mixin("scripts/physics");
global.mixin("scripts/geometry"); global.mixin("scripts/geometry");
global.mixin("scripts/layout");
/* /*
Factory for creating registries. Register one with 'X.register', Factory for creating registries. Register one with 'X.register',
@ -628,9 +625,6 @@ function world_start() {
game.cam = world; game.cam = world;
} }
global.mixin("scripts/physics");
window.title = `Prosperon v${prosperon.version}`; window.title = `Prosperon v${prosperon.version}`;
window.size = [500, 500]; window.size = [500, 500];

View file

@ -670,10 +670,11 @@ render.init = function () {
render.textshader = textshader; render.textshader = textshader;
os.make_circle2d().draw = function () { /* os.make_circle2d().draw = function () {
render.circle(this.body().transform().pos, this.radius, [1, 1, 0, 1]); render.circle(this.body().transform().pos, this.radius, [1, 1, 0, 1]);
}; };
var disabled = [148 / 255, 148 / 255, 148 / 255, 1]; var disabled = [148 / 255, 148 / 255, 148 / 255, 1];
var sleep = [1, 140 / 255, 228 / 255, 1]; var sleep = [1, 140 / 255, 228 / 255, 1];
var dynamic = [1, 70 / 255, 46 / 255, 1]; var dynamic = [1, 70 / 255, 46 / 255, 1];
@ -698,6 +699,7 @@ render.init = function () {
var b = this.bodyB(); var b = this.bodyB();
render.line([a.transform().pos.xy, b.transform().pos.xy], [0, 1, 1, 1], 1); render.line([a.transform().pos.xy, b.transform().pos.xy], [0, 1, 1, 1], 1);
}; };
*/
}; };
render.draw_sprites = true; render.draw_sprites = true;
@ -706,7 +708,6 @@ render.draw_hud = true;
render.draw_gui = true; render.draw_gui = true;
render.draw_gizmos = true; render.draw_gizmos = true;
render.buckets = [];
render.sprites = function render_sprites() { render.sprites = function render_sprites() {
profile.report("sprites"); profile.report("sprites");
profile.report("drawing"); profile.report("drawing");
@ -848,7 +849,7 @@ render.coordinate = function render_coordinate(pos, size, color) {
var queued_shader; var queued_shader;
var queued_pipe; var queued_pipe;
render.rectangle = function render_rectangle(rect, color, shader = polyssboshader, pipe = base_pipeline) { render.rectangle = function render_rectangle(rect, color = Color.white, shader = polyssboshader, pipe = base_pipeline) {
var transform = os.make_transform(); var transform = os.make_transform();
var wh = [rect.width, rect.height]; var wh = [rect.width, rect.height];
var poly = poly_e(); var poly = poly_e();
@ -863,14 +864,6 @@ render.rectangle = function render_rectangle(rect, color, shader = polyssboshade
check_flush(flush_poly); check_flush(flush_poly);
}; };
render.box = function render_box(pos, wh, color = Color.white) {
var poly = poly_e();
poly.transform.move(pos);
poly.transform.scale = [wh.x, wh.y, 1];
poly.color = color;
check_flush(flush_poly);
};
render.window = function render_window(pos, wh, color) { render.window = function render_window(pos, wh, color) {
render.box(pos.add(wh.scale(0.5)), wh, color); render.box(pos.add(wh.scale(0.5)), wh, color);
}; };
@ -1276,7 +1269,8 @@ var imdebug = function () {
}; };
var imgui_fn = function () { var imgui_fn = function () {
render.imgui_new(window.size.x, window.size.y, 0.01); imgui.init();
imgui.newframe(window.size.x, window.size.y, 0.01);
if (debug.console) if (debug.console)
debug.console = imgui.window("console", _ => { debug.console = imgui.window("console", _ => {
imgui.text(console.transcript); imgui.text(console.transcript);
@ -1332,7 +1326,7 @@ var imgui_fn = function () {
}); });
prosperon.imgui(); prosperon.imgui();
render.imgui_end(); imgui.endframe();
}; };
// figure out the highest resolution we can render at that's an integer // figure out the highest resolution we can render at that's an integer
@ -1440,7 +1434,7 @@ prosperon.process = function process() {
sst = profile.now(); sst = profile.now();
if (sim.mode === "play" || sim.mode === "step") { if (sim.mode === "play" || sim.mode === "step") {
profile.report("physics"); /* profile.report("physics");
physlag += dt; physlag += dt;
while (physlag > physics.delta) { while (physlag > physics.delta) {
@ -1448,9 +1442,11 @@ prosperon.process = function process() {
prosperon.phys2d_step(physics.delta * game.timescale); prosperon.phys2d_step(physics.delta * game.timescale);
prosperon.physupdate(physics.delta * game.timescale); prosperon.physupdate(physics.delta * game.timescale);
} }
profile.endreport("physics"); profile.endreport("physics");
profile.pushdata(profile.data.cpu.physics, profile.now() - sst); profile.pushdata(profile.data.cpu.physics, profile.now() - sst);
sst = profile.now(); sst = profile.now();
*/
} }
profile.report("render"); profile.report("render");

View file

@ -1,91 +0,0 @@
#include "2dphysics.h"
#include "gameobject.h"
#include "stb_ds.h"
#include "jsffi.h"
cpSpace *space = NULL;
static JSValue *fns = NULL;
static JSValue *hits = NULL;
void phys2d_init()
{
space = cpSpaceNew();
}
void phys2d_update(float deltaT) {
cpSpaceStep(space, deltaT);
arrsetlen(fns,0);
arrsetlen(hits,0);
}
JSValue arb2js(cpArbiter *arb)
{
cpBody *body1;
cpBody *body2;
cpArbiterGetBodies(arb, &body1, &body2);
cpShape *shape1;
cpShape *shape2;
cpArbiterGetShapes(arb, &shape1, &shape2);
JSValue j = *(JSValue*)cpShapeGetUserData(shape2);
JSValue jg = body2go(body2)->ref;
HMM_Vec2 srfv;
srfv.cp = cpArbiterGetSurfaceVelocity(arb);
JSValue obj = JS_NewObject(js);
JS_SetPropertyStr(js, obj, "normal", vec22js((HMM_Vec2)cpArbiterGetNormal(arb)));
JS_SetPropertyStr(js, obj, "obj", JS_DupValue(js,jg));
JS_SetPropertyStr(js, obj, "shape", JS_DupValue(js, j));
JS_SetPropertyStr(js, obj, "point", vec22js((HMM_Vec2)cpArbiterGetPointA(arb, 0)));
JS_SetPropertyStr(js, obj, "velocity", vec22js(srfv));
JS_SetPropertyStr(js, obj, "impulse", vec22js((HMM_Vec2)cpArbiterTotalImpulse(arb)));
JS_SetPropertyStr(js, obj, "ke", number2js(cpArbiterTotalKE(arb)));
return obj;
}
void phys_run_post(cpSpace *space, JSValue *fn, JSValue *hit)
{
script_call_sym(*fn, 1, hit);
JS_FreeValue(js, *hit);
JS_FreeValue(js, *fn);
}
void register_hit(cpArbiter *arb, gameobject *go, const char *name)
{
if (JS_IsUndefined(go->ref)) return;
JSValue cb = JS_GetPropertyStr(js, go->ref, name);
if (!JS_IsUndefined(cb)) {
JSValue jarb = arb2js(arb);
arrput(fns, JS_DupValue(js,cb));
arrput(hits, jarb);
cpSpaceAddPostStepCallback(space, phys_run_post, fns+arrlen(fns)-1, hits+arrlen(hits)-1);
}
cpShape *s1, *s2;
cpArbiterGetShapes(arb, &s1, &s2);
JSValue j1 = *(JSValue*)cpShapeGetUserData(s1);
JSValue j2 = *(JSValue*)cpShapeGetUserData(s2);
cb = JS_GetPropertyStr(js, j1, name);
if (!JS_IsUndefined(cb)) {
JSValue jarb = arb2js(arb);
arrput(fns, JS_DupValue(js,cb));
arrput(hits, jarb);
cpSpaceAddPostStepCallback(space, phys_run_post, fns+arrlen(fns)-1, hits+arrlen(hits)-1);
}
}
int script_phys_cb_begin(cpArbiter *arb, cpSpace *space, gameobject *go) { register_hit(arb, go, "collide"); return 1; }
void script_phys_cb_separate(cpArbiter *arb, cpSpace *space, gameobject *go) { register_hit(arb, go, "separate"); }
void phys2d_setup_handlers(gameobject *go) {
cpCollisionHandler *handler = cpSpaceAddWildcardHandler(space, (cpCollisionType)go);
handler->userData = go;
handler->beginFunc = script_phys_cb_begin;
handler->separateFunc = script_phys_cb_separate;
}

View file

@ -1,15 +0,0 @@
#ifndef TWODPHYSICS_H
#define TWODPHYSICS_H
#include <chipmunk/chipmunk.h>
#include "gameobject.h"
#include "script.h"
extern cpSpace *space;
void phys2d_init();
void phys2d_update(float deltaT);
void phys2d_setup_handlers(gameobject *go);
JSValue arb2js(cpArbiter *arb);
#endif

View file

@ -223,11 +223,11 @@ typedef union HMM_Vec2 {
float Width, Height; float Width, Height;
}; };
cpVect cp;
float Elements[2]; float Elements[2];
float e[2]; float e[2];
cpVect cp;
} HMM_Vec2; } HMM_Vec2;
typedef union HMM_Vec3 { typedef union HMM_Vec3 {
@ -260,11 +260,6 @@ typedef union HMM_Vec3 {
float _Ignored4; float _Ignored4;
}; };
struct {
cpVect cp;
float _Ignored5;
};
struct struct
{ {
float _Ignored1; float _Ignored1;
@ -283,6 +278,12 @@ typedef union HMM_Vec3 {
HMM_Vec2 VW; HMM_Vec2 VW;
}; };
struct
{
HMM_Vec2 cp;
float _Ignored5;
};
float Elements[3]; float Elements[3];
float e[3]; float e[3];

File diff suppressed because it is too large Load diff

View file

@ -22,7 +22,7 @@ typedef struct sampler {
struct anim_channel { struct anim_channel {
HMM_Vec4 *target; HMM_Vec4 *target;
int comps; int comps;
sampler *sampler; struct sampler *sampler;
}; };
typedef struct animation { typedef struct animation {

View file

@ -13,10 +13,6 @@
#define CUTE_ASEPRITE_IMPLEMENTATION #define CUTE_ASEPRITE_IMPLEMENTATION
#include "cute_aseprite.h" #include "cute_aseprite.h"
#define LAY_FLOAT 1
#define LAY_IMPLEMENTATION
#include "layout.h"
#define STB_PERLIN_IMPLEMENTATION #define STB_PERLIN_IMPLEMENTATION
#include "stb_perlin.h" #include "stb_perlin.h"

View file

@ -1,12 +1,9 @@
#include "datastream.h" #include "datastream.h"
#include "config.h" #include "config.h"
#include "dsp.h"
#include "iir.h"
#include "limits.h" #include "limits.h"
#include "log.h" #include "log.h"
#include "resources.h" #include "resources.h"
#include "sound.h"
#include "texture.h" #include "texture.h"
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
@ -24,10 +21,10 @@ void datastream_free(datastream *ds)
free(ds); free(ds);
} }
void soundstream_fillbuf(struct datastream *ds, soundbyte *buf, int frames) { //void soundstream_fillbuf(struct datastream *ds, soundbyte *buf, int frames) {
for (int i = 0; i < frames*CHANNELS; i++) // for (int i = 0; i < frames*CHANNELS; i++)
buf[i] = ringshift(ds->ring); // buf[i] = ringshift(ds->ring);
} //}
static void render_frame(plm_t *mpeg, plm_frame_t *frame, struct datastream *ds) { static void render_frame(plm_t *mpeg, plm_frame_t *frame, struct datastream *ds) {
if (ds->dirty) return; if (ds->dirty) return;
@ -41,8 +38,8 @@ static void render_frame(plm_t *mpeg, plm_frame_t *frame, struct datastream *ds)
} }
static void render_audio(plm_t *mpeg, plm_samples_t *samples, struct datastream *ds) { static void render_audio(plm_t *mpeg, plm_samples_t *samples, struct datastream *ds) {
for (int i = 0; i < samples->count * CHANNELS; i++) // for (int i = 0; i < samples->count * CHANNELS; i++)
ringpush(ds->ring, samples->interleaved[i]); // ringpush(ds->ring, samples->interleaved[i]);
} }
struct datastream *ds_openvideo(const char *path) struct datastream *ds_openvideo(const char *path)
@ -77,8 +74,8 @@ struct datastream *ds_openvideo(const char *path)
return ds; return ds;
ds->ring = ringnew(ds->ring, 8192); // ds->ring = ringnew(ds->ring, 8192);
plugin_node(make_node(ds, soundstream_fillbuf, NULL), masterbus); // plugin_node(make_node(ds, soundstream_fillbuf, NULL), masterbus);
plm_set_audio_decode_callback(ds->plm, render_audio, ds); plm_set_audio_decode_callback(ds->plm, render_audio, ds);
plm_set_loop(ds->plm, false); plm_set_loop(ds->plm, false);
@ -87,7 +84,7 @@ struct datastream *ds_openvideo(const char *path)
plm_set_audio_stream(ds->plm, 0); plm_set_audio_stream(ds->plm, 0);
// Adjust the audio lead time according to the audio_spec buffer size // Adjust the audio lead time according to the audio_spec buffer size
plm_set_audio_lead_time(ds->plm, BUF_FRAMES / SAMPLERATE); // plm_set_audio_lead_time(ds->plm, BUF_FRAMES / SAMPLERATE);
return ds; return ds;
} }

View file

@ -3,7 +3,6 @@
#include <pl_mpeg.h> #include <pl_mpeg.h>
#include <stdint.h> #include <stdint.h>
#include "dsp.h"
#include "sokol/sokol_gfx.h" #include "sokol/sokol_gfx.h"
@ -18,7 +17,6 @@ struct datastream {
int width; int width;
int height; int height;
int dirty; int dirty;
soundbyte *ring;
}; };
typedef struct datastream datastream; typedef struct datastream datastream;

View file

@ -1,37 +1,13 @@
#include "gameobject.h" #include "gameobject.h"
#include "2dphysics.h"
#include <string.h>
#include "log.h"
#include "math.h" #include "math.h"
#include <chipmunk/chipmunk.h>
#include "stb_ds.h" #include "stb_ds.h"
transform go2t(gameobject *go)
{
transform t = {0};
t.pos.cp = cpBodyGetPosition(go->body);
t.rotation = angle2rotation(cpBodyGetAngle(go->body));
t.scale = go->t->scale;
return t;
}
gameobject *body2go(cpBody *b)
{
return cpBodyGetUserData(b);
}
gameobject *shape2go(cpShape *s)
{
cpBody *b = cpShapeGetBody(s);
return cpBodyGetUserData(b);
}
void gameobject_apply(gameobject *go) { *go->t = go2t(go); }
static void velocityFn(cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt) static void velocityFn(cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt)
{ {
gameobject *go = body2go(body); /* gameobject *go = body2go(body);
gameobject_apply(go); gameobject_apply(go);
cpVect pos = cpBodyGetPosition(body); cpVect pos = cpBodyGetPosition(body);
HMM_Vec2 g = warp_force((HMM_Vec3){pos.x, pos.y, 0}, go->warp_mask).xy; HMM_Vec2 g = warp_force((HMM_Vec3){pos.x, pos.y, 0}, go->warp_mask).xy;
@ -53,42 +29,5 @@ static void velocityFn(cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt
if (fabs(av) > go->maxangularvelocity) if (fabs(av) > go->maxangularvelocity)
cpBodySetAngularVelocity(body, copysignf(go->maxangularvelocity, av)); cpBodySetAngularVelocity(body, copysignf(go->maxangularvelocity, av));
} }
} */
gameobject *MakeGameobject() {
gameobject *ngo = malloc(sizeof(*ngo));
gameobject go = {
.maxvelocity = INFINITY,
.maxangularvelocity = INFINITY,
.damping = INFINITY,
.timescale = 1.0,
.ref = JS_UNDEFINED,
.warp_mask = ~0,
};
go.body = cpSpaceAddBody(space, cpBodyNew(1, 1));
cpBodySetVelocityUpdateFunc(go.body, velocityFn);
*ngo = go;
cpBodySetUserData(go.body, ngo);
phys2d_setup_handlers(ngo);
return ngo;
}
void rm_body_shapes(cpBody *body, cpShape *shape, void *data) {
cpSpaceRemoveShape(space, shape);
}
void rm_body_constraints(cpBody *body, cpConstraint *c, void *data)
{
cpSpaceRemoveConstraint(space, c);
}
void gameobject_free(gameobject *go) {
go->ref = JS_UNDEFINED;
cpBodyEachShape(go->body, rm_body_shapes, NULL);
cpBodyEachConstraint(go->body, rm_body_constraints, NULL);
cpSpaceRemoveBody(space, go->body);
cpBodyFree(go->body);
free(go);
} }

View file

@ -1,12 +1,6 @@
#ifndef GAMEOBJECT_H #ifndef GAMEOBJECT_H
#define GAMEOBJECT_H #define GAMEOBJECT_H
#include "quickjs/quickjs.h"
#include "HandmadeMath.h"
#include "transform.h"
#include "script.h"
#include "warp.h"
#define dag_rm(p,c) do{\ #define dag_rm(p,c) do{\
for (int i = arrlen(p->children)-1; i--; i >=0) {\ for (int i = arrlen(p->children)-1; i--; i >=0) {\
if (p->children[i] == c) { \ if (p->children[i] == c) { \
@ -27,15 +21,12 @@
}while(0) }while(0)
struct gameobject { struct gameobject {
cpBody *body; /* NULL if this object is dead; has 2d position and rotation, relative to global 0 */
float damping; float damping;
float timescale; float timescale;
float maxvelocity; float maxvelocity;
float maxangularvelocity; float maxangularvelocity;
unsigned int layer; unsigned int layer;
unsigned int warp_mask; unsigned int warp_mask;
JSValue ref;
transform *t; // the transform this body controls
}; };
/* /*
@ -55,16 +46,4 @@ struct gameobject {
typedef struct gameobject gameobject; typedef struct gameobject gameobject;
gameobject *MakeGameobject();
void gameobject_apply(gameobject *go);
void gameobject_free(gameobject *go);
transform go2t(gameobject *go);
HMM_Vec3 go_pos(gameobject *go);
gameobject *shape2go(cpShape *s);
gameobject *body2go(cpBody *b);
void go_shape_apply(cpBody *body, cpShape *shape, gameobject *go);
#endif #endif

View file

@ -1,937 +0,0 @@
#include "gui.h"
#include "render.h"
#include "sokol/sokol_app.h"
#include "log.h"
#include "imgui.h"
#include "implot.h"
#include "imnodes.h"
#include "stb_ds.h"
#define SOKOL_IMPL
#include "sokol/util/sokol_imgui.h"
#include "sokol/util/sokol_gfx_imgui.h"
#include <stdlib.h>
static sgimgui_t sgimgui;
#include "jsffi.h"
static int wantkeys = 0;
static int wantmouse = 0;
int num_to_yaxis(int y)
{
switch(y) {
case 0:
return ImAxis_Y1;
case 1:
return ImAxis_Y2;
case 2:
return ImAxis_Y3;
}
return ImAxis_Y1;
}
int num_to_xaxis(int x)
{
switch(x) {
case 0:
return ImAxis_X1;
case 1:
return ImAxis_X2;
case 2:
return ImAxis_X3;
}
return ImAxis_X1;
}
JSC_SCALL(imgui_window,
bool active = true;
ImGui::Begin(str, &active);
script_call_sym(argv[1], 0, NULL);
ImGui::End();
ret = boolean2js(active);
)
JSC_SCALL(imgui_menu,
if (ImGui::BeginMenu(str)) {
script_call_sym(argv[1], 0, NULL);
ImGui::EndMenu();
}
)
JSC_CCALL(imgui_menubar,
if (ImGui::BeginMenuBar()) {
script_call_sym(argv[0], 0, NULL);
ImGui::EndMenuBar();
}
)
JSC_CCALL(imgui_mainmenubar,
if (ImGui::BeginMainMenuBar()) {
script_call_sym(argv[0], 0, NULL);
ImGui::EndMainMenuBar();
}
)
JSC_CCALL(imgui_menuitem,
char *name = js2strdup(argv[0]);
char *keyfn = JS_IsUndefined(argv[1]) ? NULL : js2strdup(argv[1]);
bool on = JS_IsUndefined(argv[3]) ? false : js2boolean(argv[3]);
if (ImGui::MenuItem(JS_Is(argv[0]) ? name : "##empty" ,keyfn, &on))
script_call_sym(argv[2], 0, NULL);
if (JS_Is(argv[0])) free(name);
if (keyfn) free(keyfn);
return boolean2js(on);
)
JSC_SCALL(imgui_plot,
if (ImPlot::BeginPlot(str)) {
script_call_sym(argv[1], 0, NULL);
ImPlot::EndPlot();
}
)
#define PLOT_FN(NAME, FN, ADD, SHADED) JSC_SCALL(imgui_##NAME, \
fill_plotdata(argv[1], argv[3]); \
bool shaded = js2boolean(argv[2]);\
int flag = 0; \
if (shaded) flag = SHADED; \
ImPlot::FN(str, &plotdata[0].x, &plotdata[0].y, arrlen(plotdata), ADD flag, 0, sizeof(HMM_Vec2)); \
) \
static HMM_Vec2 *plotdata = NULL;
void fill_plotdata(JSValue v, JSValue last)
{
arrsetlen(plotdata, 0);
if (JS_IsArray(js,js_getpropidx(v, 0))) {
for (int i = 0; i < js_arrlen(v); i++)
arrput(plotdata, js2vec2(js_getpropidx(v, i)));
}
else {
// Fill it with the x axis being the array index
for (int i = 0; i < js_arrlen(v); i++) {
if (JS_IsUndefined(js_getpropidx(v,i))) continue;
HMM_Vec2 c = (HMM_Vec2){i, js2number(js_getpropidx(v,i))};
arrput(plotdata, c);
}
}
if (!JS_IsUndefined(last)) {
int frame = js2number(last);
HMM_Vec2 c = (HMM_Vec2){frame, arrlast(plotdata).y};
arrput(plotdata, c);
}
}
PLOT_FN(lineplot, PlotLine,,ImPlotLineFlags_Shaded)
PLOT_FN(scatterplot, PlotScatter,,0)
PLOT_FN(stairplot, PlotStairs,,ImPlotStairsFlags_Shaded)
PLOT_FN(digitalplot, PlotDigital,,0)
static HMM_Vec3 *shadedata = NULL;
JSC_SCALL(imgui_shadedplot,
arrsetlen(plotdata,js_arrlen(argv[1]));
for (int i = 0; i < js_arrlen(argv[1]); i++) {
HMM_Vec3 c;
c.x = i;
c.y = js2number(js_getpropidx(argv[1],i));
c.z = js2number(js_getpropidx(argv[2],i));
arrput(shadedata, c);
}
ImPlot::PlotShaded(str, &shadedata[0].x, &shadedata[0].y, &shadedata[0].z, arrlen(shadedata), 0, 0, sizeof(HMM_Vec3));
)
JSC_SCALL(imgui_barplot,
fill_plotdata(argv[1], JS_UNDEFINED);
ImPlot::PlotBars(str, &plotdata[0].x, &plotdata[0].y, js_arrlen(argv[1]), js2number(argv[2]), 0, 0, sizeof(HMM_Vec2));
)
static double *histodata = NULL;
void fill_histodata(JSValue v)
{
arrsetlen(histodata, js_arrlen(v));
for (int i = 0; i < js_arrlen(v); i++)
histodata[i] = js2number(js_getpropidx(v, i));
}
JSC_SCALL(imgui_histogramplot,
fill_histodata(argv[1]);
ImPlot::PlotHistogram(str, histodata, js_arrlen(argv[1]));
)
JSC_SCALL(imgui_heatplot,
fill_histodata(argv[1]);
int rows = js2number(argv[2]);
int cols = js2number(argv[3]);
if (rows*cols == (int)js_arrlen(argv[1]))
ImPlot::PlotHeatmap(str, histodata, rows, cols);
)
JSC_CCALL(imgui_pieplot,
if (js_arrlen(argv[0]) != js_arrlen(argv[1])) return JS_UNDEFINED;
const char *labels[js_arrlen(argv[0])];
for (int i = 0; i < js_arrlen(argv[0]); i++)
labels[i] = js2str(js_getpropidx(argv[0], i));
fill_histodata(argv[1]);
ImPlot::PlotPieChart(labels, histodata, js_arrlen(argv[1]), js2number(argv[2]), js2number(argv[3]), js2number(argv[4]));
for (int i = 0; i < js_arrlen(argv[0]); i++)
JS_FreeCString(js,labels[i]);
)
JSC_SCALL(imgui_textplot,
HMM_Vec2 v = js2vec2(argv[1]);
ImPlot::PlotText(str, v.x, v.y);
)
JSC_CCALL(imgui_inplot,
HMM_Vec2 v = js2vec2(argv[0]);
ImPlotRect lm = ImPlot::GetPlotLimits();
if (v.x > lm.X.Min && v.x < lm.X.Max && v.y > lm.Y.Min && v.y < lm.Y.Max)
return boolean2js(true);
return boolean2js(false);
)
JSC_CCALL(imgui_plothovered,
return boolean2js(ImPlot::IsPlotHovered());
)
JSC_SSCALL(imgui_plotaxes,
ImPlot::SetupAxes(str,str2);
)
JSC_CCALL(imgui_plotmousepos,
ImPlotPoint p = ImPlot::GetPlotMousePos();
return vec22js(HMM_Vec2{p.x,p.y});
)
JSC_CCALL(imgui_axeslimits,
ImPlot::SetupAxesLimits(js2number(argv[0]), js2number(argv[1]), js2number(argv[2]), js2number(argv[3]));
)
JSC_CCALL(imgui_fitaxis,
ImPlot::SetNextAxisToFit((js2number(argv[0]) == 0) ? ImAxis_X1 : ImAxis_Y1);
)
JSC_SSCALL(imgui_textinput,
char buffer[512];
if (JS_IsUndefined(argv[1]))
buffer[0] = 0;
else
strncpy(buffer, str2, 512);
ImGui::InputText(str, buffer, sizeof(buffer));
if (strcmp(buffer, str2))
ret = str2js(buffer);
else
ret = JS_DupValue(js,argv[1]);
)
JSC_SSCALL(imgui_textbox,
char buffer[512];
if (JS_IsUndefined(argv[1]))
buffer[0] = 0;
else
strncpy(buffer, str2, 512);
ImGui::InputTextMultiline(str, buffer, sizeof(buffer));
if (strcmp(buffer, str2))
ret = str2js(buffer);
else
ret = JS_DupValue(js,argv[1]);
)
JSC_SCALL(imgui_text, ImGui::Text(str) )
JSC_SCALL(imgui_button,
if (ImGui::Button(str))
script_call_sym(argv[1], 0, NULL);
)
JSC_CCALL(imgui_sokol_gfx,
sgimgui_draw(&sgimgui);
if (ImGui::BeginMenu("sokol-gfx")) {
ImGui::MenuItem("Capabilities", 0, &sgimgui.caps_window.open);
ImGui::MenuItem("Frame Stats", 0, &sgimgui.frame_stats_window.open);
ImGui::MenuItem("Buffers", 0, &sgimgui.buffer_window.open);
ImGui::MenuItem("Images", 0, &sgimgui.image_window.open);
ImGui::MenuItem("Samplers", 0, &sgimgui.sampler_window.open);
ImGui::MenuItem("Shaders", 0, &sgimgui.shader_window.open);
ImGui::MenuItem("Pipelines", 0, &sgimgui.pipeline_window.open);
ImGui::MenuItem("Attachments", 0, &sgimgui.attachments_window.open);
ImGui::MenuItem("Calls", 0, &sgimgui.capture_window.open);
ImGui::EndMenu();
}
)
JSC_SCALL(imgui_slider,
float low = JS_IsUndefined(argv[2]) ? 0.0 : js2number(argv[2]);
float high = JS_IsUndefined(argv[3]) ? 1.0 : js2number(argv[3]);
if (JS_IsArray(js, argv[1])) {
int n = js_arrlen(argv[1]);
float a[n];
js2floatarr(argv[1], n, a);
switch(n) {
case 2:
ImGui::SliderFloat2(str, a, low, high);
break;
case 3:
ImGui::SliderFloat3(str, a, low, high);
break;
case 4:
ImGui::SliderFloat3(str, a, low, high);
break;
}
ret = floatarr2js(n, a);
} else {
float val = js2number(argv[1]);
ImGui::SliderFloat(str, &val, low, high, "%.3f");
ret = number2js(val);
}
)
JSC_SCALL(imgui_intslider,
int low = JS_IsUndefined(argv[2]) ? 0.0 : js2number(argv[2]);
int high = JS_IsUndefined(argv[3]) ? 1.0 : js2number(argv[3]);
if (JS_IsArray(js, argv[1])) {
int n = js_arrlen(argv[1]);
float a[n];
js2floatarr(argv[1], n, a);
int b[n];
for (int i = 0; i < n; i++)
b[i] = a[i];
switch(n) {
case 2:
ImGui::SliderInt2(str, b, low, high);
break;
case 3:
ImGui::SliderInt3(str, b, low, high);
break;
case 4:
ImGui::SliderInt3(str, b, low, high);
break;
}
for (int i = 0; i < n; i++)
a[i] = b[i];
ret = floatarr2js(n, a);
} else {
int val = js2number(argv[1]);
ImGui::SliderInt(str, &val, low, high);
ret = number2js(val);
}
)
JSC_SCALL(imgui_checkbox,
bool val = js2boolean(argv[1]);
ImGui::Checkbox(str, &val);
ret = boolean2js(val);
)
JSC_CCALL(imgui_pushid,
ImGui::PushID(js2number(argv[0]));
)
JSC_CCALL(imgui_popid, ImGui::PopID(); )
JSC_CCALL(imgui_image,
texture *tex = js2texture(argv[0]);
simgui_image_desc_t sg = {};
sg.image = tex->id;
sg.sampler = std_sampler;
simgui_image_t ss = simgui_make_image(&sg);
ImGui::Image(simgui_imtextureid(ss), ImVec2(tex->width, tex->height), ImVec2(0,0), ImVec2(1,1));
simgui_destroy_image(ss);
)
JSC_SCALL(imgui_imagebutton,
texture *tex = js2texture(argv[1]);
simgui_image_desc_t sg = {};
sg.image = tex->id;
sg.sampler = std_sampler;
simgui_image_t ss = simgui_make_image(&sg);
if (ImGui::ImageButton(str, simgui_imtextureid(ss), ImVec2(tex->width, tex->height)))
script_call_sym(argv[2], 0, NULL);
simgui_destroy_image(ss);
)
JSC_CCALL(imgui_sameline, ImGui::SameLine(js2number(argv[0])) )
JSC_CCALL(imgui_columns, ImGui::Columns(js2number(argv[0])) )
JSC_CCALL(imgui_nextcolumn, ImGui::NextColumn() )
JSC_SCALL(imgui_collapsingheader, ret = boolean2js(ImGui::CollapsingHeader(str)) )
JSC_SCALL(imgui_radio, ret = boolean2js(ImGui::RadioButton(str, js2boolean(argv[1]))))
JSC_SCALL(imgui_tree,
if (ImGui::TreeNode(str)) {
script_call_sym(argv[1],0,NULL);
ImGui::TreePop();
}
)
JSC_SCALL(imgui_tabbar,
if (ImGui::BeginTabBar(str)) {
script_call_sym(argv[1],0,NULL);
ImGui::EndTabBar();
}
)
JSC_SCALL(imgui_tab,
if (ImGui::BeginTabItem(str)) {
script_call_sym(argv[1],0,NULL);
ImGui::EndTabItem();
}
)
JSC_SCALL(imgui_listbox,
char **arr = js2strarr(argv[1]);
int n = js_arrlen(argv[1]);
int idx = js2number(argv[2]);
ImGui::ListBox(str, &idx, arr, n, 4);
for (int i = 0; i < n; i++)
free(arr[i]);
// arrfree(arr); // TODO: Doesn't this need freed?
ret = number2js(idx);
)
JSC_SCALL(imgui_int,
int n = js2number(argv[1]);
ImGui::InputInt(str, &n);
ret = number2js(n);
)
JSC_SCALL(imgui_open_popup,
ImGui::OpenPopup(str);
)
JSC_SCALL(imgui_popup,
if (ImGui::BeginPopup(str)) {
script_call_sym(argv[1],0,NULL);
ImGui::EndPopup();
}
)
JSC_CCALL(imgui_close_popup,
ImGui::CloseCurrentPopup();
)
JSC_SCALL(imgui_modal,
if (ImGui::BeginPopupModal(str)) {
script_call_sym(argv[1],0,NULL);
ImGui::EndPopup();
}
)
JSC_SCALL(imgui_context,
if (ImGui::BeginPopupContextItem(str)) {
script_call_sym(argv[1],0,NULL);
ImGui::EndPopup();
}
)
JSC_SCALL(imgui_table,
int flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_SizingStretchProp;
bool sort = false;
if (!JS_IsUndefined(argv[3])) sort = true;
if (sort) flags |= ImGuiTableFlags_Sortable;
if (ImGui::BeginTable(str, js2number(argv[1]), flags)) {
script_call_sym(argv[2],0,NULL);
if (sort) {
ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs();
if (sort_specs && sort_specs->SpecsDirty) {
for (int i = 0; i < sort_specs->SpecsCount; i++)
{
const ImGuiTableColumnSortSpecs* spec = &sort_specs->Specs[i];
JSValue send[2];
send[0] = number2js(spec->ColumnIndex);
send[1] = boolean2js(spec->SortDirection == ImGuiSortDirection_Ascending);
script_call_sym(argv[3], 2, send);
JS_FreeValue(js, send[0]);
JS_FreeValue(js, send[1]);
}
sort_specs->SpecsDirty = false;
}
}
ImGui::EndTable();
}
)
JSC_CCALL(imgui_tablenextrow, ImGui::TableNextRow())
JSC_CCALL(imgui_tablenextcolumn, ImGui::TableNextColumn())
JSC_SCALL(imgui_tablesetupcolumn, ImGui::TableSetupColumn(str))
JSC_CCALL(imgui_tableheadersrow, ImGui::TableHeadersRow())
JSC_CCALL(imgui_tableangledheadersrow, ImGui::TableAngledHeadersRow())
JSC_SCALL(imgui_dnd,
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) {
double n = js2number(argv[1]);
ImGui::SetDragDropPayload(str, &n, sizeof(n));
script_call_sym(argv[2],0,NULL);
ImGui::EndDragDropSource();
}
)
JSC_SCALL(imgui_dndtarget,
if (ImGui::BeginDragDropTarget()) {
if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload(str)) {
JSValue n = number2js(*(double*)payload->Data);
script_call_sym(argv[1], 1, &n);
}
ImGui::EndDragDropTarget();
}
)
JSC_SCALL(imgui_color,
int n = js_arrlen(argv[1]);
float color[n];
js2floatarr(argv[1],n,color);
if (n == 3)
ImGui::ColorEdit3(str, color);
else if (n == 4)
ImGui::ColorEdit4(str, color);
ret = floatarr2js(n, color);
)
JSC_CCALL(imgui_startnode,
ImNodes::BeginNodeEditor();
script_call_sym(argv[0],0,NULL);
ImNodes::EndNodeEditor();
int start_attr;
int end_attr;
if (ImNodes::IsLinkCreated(&start_attr, &end_attr))
{
JSValue ab[2];
ab[0] = number2js(start_attr);
ab[1] = number2js(end_attr);
script_call_sym(argv[1], 2, ab);
for (int i = 0; i < 2; i++) JS_FreeValue(js, ab[i]);
}
int node_id;
if (ImNodes::IsNodeHovered(&node_id))
{
JSValue a = number2js(node_id);
script_call_sym(argv[2],1,&a);
JS_FreeValue(js,a);
}
int link_id;
if (ImNodes::IsLinkHovered(&link_id))
{
JSValue a = number2js(link_id);
script_call_sym(argv[3],1,&a);
JS_FreeValue(js,a);
}
)
JSC_CCALL(imgui_node,
ImNodes::BeginNode(js2number(argv[0]));
script_call_sym(argv[1],0,NULL);
ImNodes::EndNode();
)
JSC_CCALL(imgui_nodein,
ImNodes::BeginInputAttribute(js2number(argv[0]));
script_call_sym(argv[1],0,NULL);
ImNodes::EndInputAttribute();
)
JSC_CCALL(imgui_nodeout,
ImNodes::BeginOutputAttribute(js2number(argv[0]));
script_call_sym(argv[1],0,NULL);
ImNodes::EndOutputAttribute();
)
JSC_CCALL(imgui_nodelink,
ImNodes::Link(js2number(argv[0]), js2number(argv[1]), js2number(argv[2]));
)
JSC_CCALL(imgui_nodemini, ImNodes::MiniMap(js2number(argv[0])))
ImVec2 js2imvec2(JSValue v)
{
HMM_Vec2 va = js2vec2(v);
return ImVec2(va.x,va.y);
}
JSValue imvec22js(ImVec2 v)
{
return vec22js(HMM_Vec2({v.x,v.y}));
}
ImPlotPoint js2plotvec2(JSValue v)
{
HMM_Vec2 va = js2vec2(v);
return ImPlotPoint(va.x,va.y);
}
JSValue plotvec22js(ImPlotPoint v)
{
return vec22js(HMM_Vec2{v.x,v.y});
}
ImVec4 js2imvec4(JSValue v)
{
HMM_Vec4 va = js2vec4(v);
return ImVec4(va.x, va.y, va.z, va.w);
}
ImU32 js2imu32(JSValue v)
{
return ImGui::ColorConvertFloat4ToU32(js2imvec4(v));
}
JSC_CCALL(imgui_rectfilled,
ImGui::GetWindowDrawList()->AddRectFilled(js2imvec2(argv[0]), js2imvec2(argv[1]), js2imu32(argv[2]));
)
JSC_CCALL(imgui_line,
ImGui::GetWindowDrawList()->AddLine(js2imvec2(argv[0]), js2imvec2(argv[1]),js2imu32(argv[2]));
)
JSC_CCALL(imgui_point,
ImGui::GetWindowDrawList()->AddCircleFilled(js2imvec2(argv[0]), js2number(argv[1]), js2imu32(argv[2]));
)
JSC_CCALL(imgui_cursorscreenpos,
ImVec2 v = ImGui::GetCursorScreenPos();
HMM_Vec2 va;
va.x = v.x;
va.y = v.y;
return vec22js(va);
)
JSC_CCALL(imgui_setcursorscreenpos,
ImGui::SetCursorScreenPos(js2imvec2(argv[0]));
)
JSC_CCALL(imgui_contentregionavail,
ImVec2 v = ImGui::GetContentRegionAvail();
HMM_Vec2 va;
va.x = v.x;
va.y = v.y;
return vec22js(va);
)
JSC_CCALL(imgui_beziercubic,
ImGui::GetWindowDrawList()->AddBezierCubic(js2imvec2(argv[0]), js2imvec2(argv[1]), js2imvec2(argv[2]), js2imvec2(argv[3]), js2imu32(argv[4]), js2number(argv[5]));
)
JSC_CCALL(imgui_bezierquad,
ImGui::GetWindowDrawList()->AddBezierQuadratic(js2imvec2(argv[0]), js2imvec2(argv[1]), js2imvec2(argv[2]), js2imu32(argv[3]), js2number(argv[4]));
)
JSC_SCALL(imgui_drawtext,
ImGui::GetWindowDrawList()->AddText(js2imvec2(argv[1]), js2imu32(argv[2]), str);
)
JSC_CCALL(imgui_rect,
ImGui::GetWindowDrawList()->AddRect(js2imvec2(argv[0]), js2imvec2(argv[1]), js2imu32(argv[2]));
)
JSC_CCALL(imgui_mousehoveringrect,
return boolean2js(ImGui::IsMouseHoveringRect(js2imvec2(argv[0]), js2imvec2(argv[1])));
)
JSC_CCALL(imgui_mouseclicked,
return boolean2js(ImGui::IsMouseClicked(js2number(argv[0])));
)
JSC_CCALL(imgui_mousedown,
return boolean2js(ImGui::IsMouseDown(js2number(argv[0])));
)
JSC_CCALL(imgui_mousereleased,
return boolean2js(ImGui::IsMouseReleased(js2number(argv[0])));
)
JSC_CCALL(imgui_mousedragging,
return boolean2js(ImGui::IsMouseDragging(js2number(argv[0])));
)
JSC_CCALL(imgui_mousedelta,
ImVec2 dm = ImGui::GetIO().MouseDelta;
return vec22js((HMM_Vec2){dm.x,dm.y});
)
JSC_CCALL(imgui_dummy,
ImGui::Dummy(js2imvec2(argv[0]));
)
JSC_SCALL(imgui_invisiblebutton,
ImGui::InvisibleButton(str, js2imvec2(argv[1]));
)
JSC_CCALL(imgui_width,
ImGui::PushItemWidth(js2number(argv[0]));
)
JSC_CCALL(imgui_windowpos,
return imvec22js(ImGui::GetWindowPos());
)
JSC_CCALL(imgui_plotpos,
return plotvec22js(ImPlot::GetPlotPos());
)
JSC_CCALL(imgui_plot2pixels,
return imvec22js(ImPlot::PlotToPixels(js2plotvec2(argv[0])));
)
JSC_CCALL(imgui_plotlimits,
ImPlotRect lim = ImPlot::GetPlotLimits();
JSValue xlim = JS_NewObject(js);
js_setpropstr(xlim, "min", number2js(lim.X.Min));
js_setpropstr(xlim, "max", number2js(lim.X.Max));
JSValue ylim = JS_NewObject(js);
js_setpropstr(ylim, "min", number2js(lim.Y.Min));
js_setpropstr(ylim, "max", number2js(lim.Y.Max));
JSValue limits = JS_NewObject(js);
js_setpropstr(limits, "x", xlim);
js_setpropstr(limits, "y", ylim);
return limits;
)
static JSValue axis_formatter = JS_UNDEFINED;
static JSValue axis_fmts[10];
void jsformatter(double value, char *buff, int size, JSValue *fmt)
{
JSValue v = number2js(value);
const char *str = js2str(script_call_sym_ret(*fmt, 1, &v));
strncpy(buff,str, size);
JS_FreeCString(js, str);
}
JSC_CCALL(imgui_axisfmt,
int y = num_to_yaxis(js2number(argv[0]));
if (!JS_IsUndefined(axis_fmts[y])) {
JS_FreeValue(js, axis_fmts[y]);
axis_fmts[y] = JS_UNDEFINED;
}
axis_fmts[y] = JS_DupValue(js,argv[1]);
ImPlot::SetupAxisFormat(y, (ImPlotFormatter)jsformatter, (void*)(axis_fmts+y));
)
#define FSTAT(KEY) js_setpropstr(v, #KEY, number2js(stats.KEY));
JSC_CCALL(imgui_framestats,
JSValue v = JS_NewObject(js);
sg_frame_stats stats = sg_query_frame_stats();
FSTAT(num_passes)
FSTAT(num_apply_viewport)
FSTAT(num_apply_scissor_rect)
FSTAT(num_apply_pipeline)
FSTAT(num_apply_bindings)
FSTAT(num_apply_uniforms)
FSTAT(num_draw)
FSTAT(num_update_buffer)
FSTAT(num_append_buffer)
FSTAT(num_update_image)
FSTAT(size_apply_uniforms)
FSTAT(size_update_buffer)
FSTAT(size_append_buffer)
FSTAT(size_update_image)
return v;
)
JSC_CCALL(imgui_setaxes,
int x = num_to_xaxis(js2number(argv[0]));
int y = num_to_yaxis(js2number(argv[1]));
ImPlot::SetAxes(x,y);
)
JSC_CCALL(imgui_setupaxis,
ImPlot::SetupAxis(num_to_yaxis(js2number(argv[0])));
)
JSC_SCALL(imgui_setclipboard,
ImGui::SetClipboardText(str);
)
static const JSCFunctionListEntry js_imgui_funcs[] = {
MIST_FUNC_DEF(imgui, windowpos, 0),
MIST_FUNC_DEF(imgui, plot2pixels, 1),
MIST_FUNC_DEF(imgui, plotpos, 0),
MIST_FUNC_DEF(imgui, plotlimits, 0),
MIST_FUNC_DEF(imgui, setaxes, 2),
MIST_FUNC_DEF(imgui, setupaxis, 1),
MIST_FUNC_DEF(imgui, framestats, 0),
MIST_FUNC_DEF(imgui, inplot, 1),
MIST_FUNC_DEF(imgui, window, 2),
MIST_FUNC_DEF(imgui, menu, 2),
MIST_FUNC_DEF(imgui, sameline, 1),
MIST_FUNC_DEF(imgui, int, 2),
MIST_FUNC_DEF(imgui, pushid, 1),
MIST_FUNC_DEF(imgui, popid, 0),
MIST_FUNC_DEF(imgui, slider, 4),
MIST_FUNC_DEF(imgui, intslider, 4),
MIST_FUNC_DEF(imgui, menubar, 1),
MIST_FUNC_DEF(imgui, mainmenubar, 1),
MIST_FUNC_DEF(imgui, menuitem, 3),
MIST_FUNC_DEF(imgui, radio, 2),
MIST_FUNC_DEF(imgui, image, 1),
MIST_FUNC_DEF(imgui, imagebutton, 2),
MIST_FUNC_DEF(imgui, textinput, 2),
MIST_FUNC_DEF(imgui, textbox, 2),
MIST_FUNC_DEF(imgui, button, 2),
MIST_FUNC_DEF(imgui, checkbox, 2),
MIST_FUNC_DEF(imgui, text, 1),
MIST_FUNC_DEF(imgui, plot, 1),
MIST_FUNC_DEF(imgui, lineplot, 4),
MIST_FUNC_DEF(imgui, scatterplot, 4),
MIST_FUNC_DEF(imgui, stairplot, 4),
MIST_FUNC_DEF(imgui, digitalplot, 4),
MIST_FUNC_DEF(imgui, shadedplot, 4),
MIST_FUNC_DEF(imgui, barplot, 3),
MIST_FUNC_DEF(imgui, pieplot, 5),
MIST_FUNC_DEF(imgui, textplot, 2),
MIST_FUNC_DEF(imgui, histogramplot, 2),
MIST_FUNC_DEF(imgui, plotaxes, 2),
MIST_FUNC_DEF(imgui, plotmousepos, 0),
MIST_FUNC_DEF(imgui, plothovered, 0),
MIST_FUNC_DEF(imgui, axeslimits, 4),
MIST_FUNC_DEF(imgui, fitaxis, 1),
MIST_FUNC_DEF(imgui, sokol_gfx, 0),
MIST_FUNC_DEF(imgui, columns, 1),
MIST_FUNC_DEF(imgui, nextcolumn, 0),
MIST_FUNC_DEF(imgui, collapsingheader, 1),
MIST_FUNC_DEF(imgui, tree, 2),
MIST_FUNC_DEF(imgui, listbox, 3),
MIST_FUNC_DEF(imgui, axisfmt, 2),
MIST_FUNC_DEF(imgui, tabbar, 2),
MIST_FUNC_DEF(imgui, tab, 2),
MIST_FUNC_DEF(imgui, open_popup, 1),
MIST_FUNC_DEF(imgui, modal, 2),
MIST_FUNC_DEF(imgui, popup, 2),
MIST_FUNC_DEF(imgui, close_popup,0),
MIST_FUNC_DEF(imgui, context,2),
MIST_FUNC_DEF(imgui, table, 4),
MIST_FUNC_DEF(imgui, tablenextcolumn,0),
MIST_FUNC_DEF(imgui, tablenextrow,0),
MIST_FUNC_DEF(imgui, tableheadersrow, 0),
MIST_FUNC_DEF(imgui, tableangledheadersrow, 0),
MIST_FUNC_DEF(imgui, tablesetupcolumn, 1),
MIST_FUNC_DEF(imgui, dnd, 3),
MIST_FUNC_DEF(imgui, dndtarget, 2),
MIST_FUNC_DEF(imgui, color, 2),
MIST_FUNC_DEF(imgui, startnode, 1),
MIST_FUNC_DEF(imgui, node, 2),
MIST_FUNC_DEF(imgui, nodein, 2),
MIST_FUNC_DEF(imgui, nodeout, 2),
MIST_FUNC_DEF(imgui, nodelink, 3),
MIST_FUNC_DEF(imgui, nodemini, 1),
MIST_FUNC_DEF(imgui, mousehoveringrect, 2),
MIST_FUNC_DEF(imgui, mouseclicked, 1),
MIST_FUNC_DEF(imgui, mousedown, 1),
MIST_FUNC_DEF(imgui, mousereleased, 1),
MIST_FUNC_DEF(imgui, mousedragging, 1),
MIST_FUNC_DEF(imgui, mousedelta, 0),
MIST_FUNC_DEF(imgui, rect, 3),
MIST_FUNC_DEF(imgui, rectfilled, 3),
MIST_FUNC_DEF(imgui, line, 3),
MIST_FUNC_DEF(imgui, bezierquad, 5),
MIST_FUNC_DEF(imgui, beziercubic, 6),
MIST_FUNC_DEF(imgui, point, 3),
MIST_FUNC_DEF(imgui, drawtext, 3),
MIST_FUNC_DEF(imgui, cursorscreenpos, 0),
MIST_FUNC_DEF(imgui, setcursorscreenpos, 1),
MIST_FUNC_DEF(imgui, contentregionavail, 0),
MIST_FUNC_DEF(imgui, dummy, 1),
MIST_FUNC_DEF(imgui, invisiblebutton, 2),
MIST_FUNC_DEF(imgui, width, 1),
MIST_FUNC_DEF(imgui, setclipboard, 1),
};
static int started = 0;
JSValue gui_init(JSContext *js)
{
simgui_desc_t sdesc = {
.image_pool_size = 1024
};
simgui_setup(&sdesc);
ImGuiIO& io = ImGui::GetIO();
io.IniFilename = ".prosperon/imgui.ini";
ImGui::LoadIniSettingsFromDisk(".prosperon/imgui.ini");
sgimgui_desc_t desc = {0};
sgimgui_init(&sgimgui, &desc);
sgimgui.frame_stats_window.disable_sokol_imgui_stats = true;
ImPlot::CreateContext();
ImNodes::CreateContext();
JSValue imgui = JS_NewObject(js);
JS_SetPropertyFunctionList(js, imgui, js_imgui_funcs, countof(js_imgui_funcs));
started = 1;
sg_enable_frame_stats();
return imgui;
}
void gui_input(sapp_event *e)
{
if (!started) return;
simgui_handle_event(e);
ImGuiIO io = ImGui::GetIO();
wantkeys = io.WantCaptureKeyboard;
wantmouse = io.WantCaptureMouse;
}
void gui_newframe(int x, int y, float dt)
{
simgui_frame_desc_t frame = {
.width = x,
.height = y,
.delta_time = dt
};
simgui_new_frame(&frame);
}
void gui_endframe()
{
simgui_render();
}
void gui_exit()
{
sgimgui_discard(&sgimgui);
}
int gui_wantmouse() { return wantmouse; }
int gui_wantkeys() { return wantkeys; }

View file

@ -1,25 +0,0 @@
#ifndef GUI_H
#define GUI_H
#include "jsffi.h"
#include "sokol/sokol_app.h"
#ifdef __cplusplus
extern "C" {
#endif
JSValue gui_init(JSContext *js);
void gui_newframe(int x, int y, float dt);
void gfx_gui();
void gui_input(sapp_event *e);
void gui_endframe();
void gui_exit();
int gui_wantmouse();
int gui_wantkeys();
#ifdef __cplusplus
}
#endif
#endif

File diff suppressed because it is too large Load diff

View file

@ -9,6 +9,7 @@ extern "C" {
#include "HandmadeMath.h" #include "HandmadeMath.h"
#include <stdarg.h> #include <stdarg.h>
#include <chipmunk/chipmunk.h> #include <chipmunk/chipmunk.h>
#include "qjs_macros.h"
void script_report_gc_time(double t, double startmem, double mem); void script_report_gc_time(double t, double startmem, double mem);
@ -16,144 +17,6 @@ int JS_Is(JSValue v);
extern JSValue cpShape2js(cpShape *s); extern JSValue cpShape2js(cpShape *s);
#define MIST_CFUNC_DEF(name, length, func1, props) { name, props, JS_DEF_CFUNC, 0, .u = { .func = { length, JS_CFUNC_generic, { .generic = func1 } } } }
#define MIST_FUNC_DEF(TYPE, FN, LEN) MIST_CFUNC_DEF(#FN, LEN, js_##TYPE##_##FN, JS_PROP_C_W_E)
#define PROTO_FUNC_DEF(TYPE, FN, LEN) MIST_CFUNC_DEF(#FN, LEN, js_##TYPE##_##FN, 0)
#define JS_SETSIG JSContext *js, JSValue self, JSValue val
#define JSC_CCALL(NAME, ...) JSValue js_##NAME (JSContext *js, JSValue self, int argc, JSValue *argv) { \
JSValue ret = JS_UNDEFINED; \
__VA_ARGS__ ;\
return ret; \
}
#define JSC_SCALL(NAME, ...) JSC_CCALL(NAME, \
const char *str = js2str(argv[0]); \
__VA_ARGS__ ;\
JS_FreeCString(js,str); \
)
#define JSC_SSCALL(NAME, ...) JSC_CCALL(NAME, \
const char *str = js2str(argv[0]); \
const char *str2 = js2str(argv[1]); \
__VA_ARGS__ ; \
JS_FreeCString(js,str2); \
JS_FreeCString(js,str); \
) \
#define MIST_CGETSET_BASE(name, fgetter, fsetter, props) { name, props, JS_DEF_CGETSET, 0, .u = { .getset = { .get = { .getter = fgetter }, .set = { .setter = fsetter } } } }
#define MIST_CGETSET_DEF(name, fgetter, fsetter) MIST_CGETSET_BASE(name, fgetter, fsetter, JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)
#define MIST_CGETET_HID(name, fgetter, fsetter) MIST_CGETSET_BASE(name, fgetter, fsetter, JS_PROP_CONFIGURABLE)
#define MIST_GET(name, fgetter) { #fgetter , JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE, JS_DEF_CGETSET, 0, .u = { .getset = { .get = { .getter = js_##name##_get_##fgetter } } } }
#define CGETSET_ADD_NAME(ID, ENTRY, NAME) MIST_CGETSET_DEF(#NAME, js_##ID##_get_##ENTRY, js_##ID##_set_##ENTRY)
#define CGETSET_ADD(ID, ENTRY) MIST_CGETSET_DEF(#ENTRY, js_##ID##_get_##ENTRY, js_##ID##_set_##ENTRY)
#define CGETSET_ADD_HID(ID, ENTRY) MIST_CGETSET_BASE(#ENTRY, js_##ID##_get_##ENTRY, js_##ID##_set_##ENTRY, JS_PROP_CONFIGURABLE)
#define JSC_DCALL(FN) JSValue js_##FN (JSContext *js, JSValue self, int argc, JSValue *argv) { FN(); return JS_UNDEFINED; }
#define JSC_1CALL(FN) JSValue js_##FN (JSContext *js, JSValue self, int argc, JSValue *argv) { FN(js2number(argv[0])); return JS_UNDEFINED; }
#define JSC_2CALL(FN) JSValue js_##FN (JSContext *js, JSValue self, int argc, JSValue *argv) { FN(js2number(argv[0]), js2number(argv[1])); return JS_UNDEFINED; }
#define JSC_3CALL(FN) JSValue js_##FN (JSContext *js, JSValue self, int argc, JSValue *argv) { FN(js2number(argv[0]), js2number(argv[1]), js2number(argv[2])); return JS_UNDEFINED; }
#define JSC_4CALL(FN) JSValue js_##FN (JSContext *js, JSValue self, int argc, JSValue *argv) { FN(js2number(argv[0]), js2number(argv[1]), js2number(argv[2]), js2number(argv[3])); return JS_UNDEFINED; }
#define JSC_5CALL(FN) JSValue js_##FN (JSContext *js, JSValue self, int argc, JSValue *argv) { FN(js2number(argv[0]), js2number(argv[1]), js2number(argv[2]), js2number(argv[3]), js2number(argv[4])); return JS_UNDEFINED; }
#define JSC_6CALL(FN) JSValue js_##FN (JSContext *js, JSValue self, int argc, JSValue *argv) { FN(js2number(argv[0]), js2number(argv[1]), js2number(argv[2]), js2number(argv[3]), js2number(argv[4]), js2number(argv[5])); return JS_UNDEFINED; }
#define JSC_7CALL(FN) JSValue js_##FN (JSContext *js, JSValue self, int argc, JSValue *argv) { FN(js2number(argv[0]), js2number(argv[1]), js2number(argv[2]), js2number(argv[3]), js2number(argv[4]), js2number(argv[5]), js2number(argv[6])); return JS_UNDEFINED; }
#define GETSETPAIR(ID, ENTRY, TYPE, FN) \
JSValue js_##ID##_set_##ENTRY (JS_SETSIG) { \
js2##ID (self)->ENTRY = js2##TYPE (val); \
{FN;} \
return JS_UNDEFINED; \
} \
\
JSValue js_##ID##_get_##ENTRY (JSContext *js, JSValue self) { \
return TYPE##2js(js2##ID (self)->ENTRY); \
} \
#define JSC_GETSET(ID, ENTRY, TYPE) GETSETPAIR( ID , ENTRY , TYPE , ; )
#define JSC_GETSET_APPLY(ID, ENTRY, TYPE) GETSETPAIR(ID, ENTRY, TYPE, ID##_apply(js2##ID (self));)
#define JSC_GETSET_CALLBACK(ID, ENTRY) \
JSValue js_##ID##_set_##ENTRY (JS_SETSIG) { \
JSValue fn = js2##ID (self)->ENTRY; \
if (!JS_IsUndefined(fn)) JS_FreeValue(js, fn); \
js2##ID (self)->ENTRY = JS_DupValue(js, val); \
return JS_UNDEFINED; \
}\
JSValue js_##ID##_get_##ENTRY (JSContext *js, JSValue self) { return JS_DupValue(js, js2##ID (self)->ENTRY); } \
#define JSC_GETSET_GLOBAL(ENTRY, TYPE) \
JSValue js_global_set_##ENTRY (JS_SETSIG) { \
ENTRY = js2##TYPE (val); \
return JS_UNDEFINED; \
} \
\
JSValue js_global_get_##ENTRY (JSContext *js, JSValue self) { \
return TYPE##2js(ENTRY); \
} \
#define JSC_GETSET_BODY(ENTRY, CPENTRY, TYPE) \
JSValue js_gameobject_set_##ENTRY (JS_SETSIG) { \
cpBody *b = js2gameobject(self)->body; \
cpBodySet##CPENTRY (b, js2##TYPE (val)); \
return JS_UNDEFINED; \
} \
\
JSValue js_gameobject_get_##ENTRY (JSContext *js, JSValue self) { \
cpBody *b = js2gameobject(self)->body; \
return TYPE##2js (cpBodyGet##CPENTRY (b)); \
} \
#define JSC_GET(ID, ENTRY, TYPE) \
JSValue js_##ID##_get_##ENTRY (JSContext *js, JSValue self) { \
return TYPE##2js(js2##ID (self)->ENTRY); } \
#define QJSCLASS(TYPE)\
static JSClassID js_##TYPE##_id;\
static int js_##TYPE##_count = 0; \
static void js_##TYPE##_finalizer(JSRuntime *rt, JSValue val){\
TYPE *n = JS_GetOpaque(val, js_##TYPE##_id);\
js_##TYPE##_count--; \
TYPE##_free(n);}\
static JSClassDef js_##TYPE##_class = {\
#TYPE,\
.finalizer = js_##TYPE##_finalizer,\
};\
TYPE *js2##TYPE (JSValue val) { \
if (JS_IsUndefined(val)) return NULL; \
assert(JS_GetClassID(val) == js_##TYPE##_id); \
return JS_GetOpaque(val,js_##TYPE##_id); \
}\
JSValue TYPE##2js(TYPE *n) { \
JSValue j = JS_NewObjectClass(js,js_##TYPE##_id);\
JS_SetOpaque(j,n);\
js_##TYPE##_count++; \
return j; }\
\
static JSValue js_##TYPE##_memid (JSContext *js, JSValue self) { return str2js("%p", js2##TYPE(self)); } \
static JSValue js_##TYPE##_memsize (JSContext *js, JSValue self) { return number2js(sizeof(TYPE)); } \
static JSValue js_##TYPE##__count (JSContext *js, JSValue self) { return number2js(js_##TYPE##_count); } \
#define QJSGLOBALCLASS(NAME) \
JSValue NAME = JS_NewObject(js); \
JS_SetPropertyFunctionList(js, NAME, js_##NAME##_funcs, countof(js_##NAME##_funcs)); \
JS_SetPropertyStr(js, globalThis, #NAME, NAME); \
/* Defines a class and uses its function list as its prototype */
#define QJSCLASSPREP_FUNCS(TYPE) \
JS_NewClassID(&js_##TYPE##_id);\
JS_NewClass(JS_GetRuntime(js), js_##TYPE##_id, &js_##TYPE##_class);\
JSValue TYPE##_proto = JS_NewObject(js); \
JS_SetPropertyFunctionList(js, TYPE##_proto, js_##TYPE##_funcs, countof(js_##TYPE##_funcs)); \
JS_SetPropertyStr(js, TYPE##_proto, "memid", JS_NewCFunction(js, &js_##TYPE##_memid, "memid", 0)); \
JS_SetPropertyStr(js, TYPE##_proto, "memsize", JS_NewCFunction(js, &js_##TYPE##_memsize, "memsize", 0)); \
JS_SetPropertyStr(js, TYPE##_proto, "_count", JS_NewCFunction(js, &js_##TYPE##__count, "_count", 0)); \
JS_SetPropertyStr(js, globalThis, #TYPE "_proto", JS_DupValue(js,TYPE##_proto)); \
JS_SetClassProto(js, js_##TYPE##_id, TYPE##_proto); \
#define countof(x) (sizeof(x)/sizeof((x)[0]))
void ffi_load(); void ffi_load();
JSValue vec22js(HMM_Vec2 v); JSValue vec22js(HMM_Vec2 v);

View file

@ -1,101 +0,0 @@
#include "kim.h"
#define KIM_CONT 0x80
#define KIM_DATA 0x7f
#define CONTINUE(CHAR) (CHAR>>7)
int utf8_bytes(char *s)
{
int bytes = __builtin_clz(~(*s));
if (!bytes) return 1;
return bytes-24;
}
int utf8_count(char *s)
{
int count = 0;
char *p = s;
while(*s) {
count++;
s += utf8_bytes(s);
}
return count;
}
/* decode and advance s, returning the character cde */
int decode_utf8(char **s) {
int k = **s ? __builtin_clz(~(**s << 24)) : 0; // Count # of leading 1 bits.
int mask = (1 << (8 - k)) - 1; // All 1's with k leading 0's.
int value = **s & mask;
for (++(*s), --k; k > 0 && **s; --k, ++(*s)) { // Note that k = #total bytes, or 0.
value <<= 6;
value += (**s & 0x3F);
}
return value;
}
/* Write and advance s with code in utf-8 */
void encode_utf8(char **s, int code) {
char val[4];
int lead_byte_max = 0x7F;
int val_index = 0;
while (code > lead_byte_max) {
val[val_index++] = (code & 0x3F) | 0x80;
code >>= 6;
lead_byte_max >>= (val_index == 1 ? 2 : 1);
}
val[val_index++] = (code & lead_byte_max) | (~lead_byte_max << 1);
while (val_index--) {
**s = val[val_index];
(*s)++;
}
}
/* write and advance s with code in kim */
void encode_kim(char **s, int code)
{
if (code < KIM_CONT) {
**s = 0 | (KIM_DATA & code);
(*s)++;
return;
}
int bits = ((32 - __builtin_clz(code) + 6) / 7) * 7;
while (bits > 7) {
bits -= 7;
**s = KIM_CONT | KIM_DATA & (code >> bits);
(*s)++;
}
**s = KIM_DATA & code;
(*s)++;
}
/* decode and advance s, returning the character code */
int decode_kim(char **s)
{
int rune = **s & KIM_DATA;
while (CONTINUE(**s)) {
rune <<= 7;
(*s)++;
rune |= **s & KIM_DATA;
}
(*s)++;
return rune;
}
/* write a null-terminated utf8 stream into a kim string */
void utf8_to_kim(char **utf, char **kim)
{
while (**utf)
encode_kim(kim, decode_utf8(utf));
}
/* write number of runes from a kim stream int a utf8 stream */
void kim_to_utf8(char **kim, char **utf, int runes)
{
for (int i = 0; i < runes; i++)
encode_utf8(utf, decode_kim(kim));
}

View file

@ -1,13 +0,0 @@
#ifndef KIM_H
#define KIM_H
int utf8_bytes(char *s);
int utf8_count(char *s);
int decode_utf8(char **s);
void encode_utf8(char **s, int code);
void encode_kim(char **s, int code);
int decode_kim(char **s);
void utf8_to_kim(char **utf, char **kim);
void kim_to_utf8(char **kim, char **utf, int runes);
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,289 +0,0 @@
#include "nota.h"
#include "stdio.h"
#include "math.h"
#include "string.h"
#include "stdlib.h"
#include "limits.h"
#include "kim.h"
#define NOTA_CONT 0x80
#define NOTA_DATA 0x7f
#define NOTA_INT_DATA 0x07
#define NOTA_INT_SIGN(CHAR) (CHAR & (1<<3))
#define NOTA_SIG_SIGN(CHAR) (CHAR & (1<<3))
#define NOTA_EXP_SIGN(CHAR) (CHAR & (1<<4))
#define NOTA_TYPE 0x70
#define NOTA_HEAD_DATA 0x0f
#define CONTINUE(CHAR) (CHAR>>7)
#define UTF8_DATA 0x3f
/* define this to use native string instead of kim. Bytes are encoded instead of runes */
#define NOTA_UTF8
int nota_type(char *nota) { return *nota & NOTA_TYPE; }
char *nota_skip(char *nota)
{
while (CONTINUE(*nota))
nota++;
return nota+1;
}
char *nota_read_num(long long *n, char *nota)
{
if (!n)
return nota_skip(nota);
*n = 0;
*n |= (*nota) & NOTA_HEAD_DATA;
while (CONTINUE(*(nota++)))
*n = (*n<<7) | (*nota) & NOTA_DATA;
return nota;
}
int nota_bits(long long n, int sb)
{
if (n == 0) return sb;
int bits = sizeof(n)*CHAR_BIT - __builtin_clzll(n);
bits-=sb; /* start bit */
return ((bits + 6) / 7)*7 + sb;
}
char *nota_continue_num(long long n, char *nota, int sb)
{
int bits = nota_bits(n, sb);
bits -= sb;
if (bits > 0)
nota[0] |= NOTA_CONT;
else
nota[0] &= ~NOTA_CONT;
int shex = (~0) << sb;
nota[0] &= shex; /* clear shex bits */
nota[0] |= (~shex) & (n>>bits);
int i = 1;
while (bits > 0) {
bits -= 7;
int head = bits == 0 ? 0 : NOTA_CONT;
nota[i] = head | (NOTA_DATA & (n >> bits));
i++;
}
return &nota[i];
}
void print_nota_hex(char *nota)
{
while (*nota) {
printf("%02X ", (unsigned char)(*nota));
nota++;
}
printf("\n");
return;
long long chars = 0;
if (!((*nota>>4 & 0x07) ^ NOTA_TEXT>>4))
nota_read_num(&chars, nota);
if ((*nota>>5) == 2 || (*nota>>5) == 6)
chars = 1;
for (int i = 0; i < chars+1; i++) {
do {
printf("%02X ", (unsigned char)(*nota));
} while(CONTINUE(*(nota++)));
}
printf("\n");
}
char *nota_write_int(long long n, char *nota)
{
char sign = 0;
if (n < 0) {
sign = 0x08;
n *= -1;
}
nota[0] = NOTA_INT | sign;
return nota_continue_num(n, nota, 3);
}
#define NOTA_DBL_PREC 6
#define xstr(s) str(s)
#define str(s) #s
char *nota_write_float(double n, char *nota)
{
if (n == 0)
return nota_write_int(0, nota);
int sign = n < 0 ? ~0 : 0;
if (sign) n *= -1;
char ns[2+NOTA_DBL_PREC+5];
snprintf(ns, 2+NOTA_DBL_PREC+5, "%." xstr (NOTA_DBL_PREC) "e", n);
long long e = atoll(&ns[2+NOTA_DBL_PREC+1]);
ns[2+NOTA_DBL_PREC] = 0;
char *z = ns + 1 + NOTA_DBL_PREC;
while (*z == '0')
z--;
*(z+1) = 0;
int expadd = (ns+strlen(ns)) - strchr(ns,'.') - 1;
e-=expadd;
ns[1] = ns[0];
long long sig = atoll(ns+1);
if (e == 0)
return nota_write_int(sig * (sign ? -1 : 1), nota);
int expsign = e < 0 ? ~0 : 0;
if (expsign) e *= -1;
nota[0] = NOTA_FLOAT;
nota[0] |= 0x10 & expsign;
nota[0] |= 0x08 & sign;
char *c = nota_continue_num(e, nota, 3);
return nota_continue_num(sig, c, 7);
}
char *nota_read_float(double *d, char *nota)
{
long long sig = 0;
long long e = 0;
char *c = nota;
e = (*c) & NOTA_INT_DATA; /* first three bits */
while (CONTINUE(*c)) {
e = (e<<7) | (*c) & NOTA_DATA;
c++;
}
c++;
do
sig = (sig<<7) | *c & NOTA_DATA;
while (CONTINUE(*(c++)));
if (NOTA_SIG_SIGN(*nota)) sig *= -1;
if (NOTA_EXP_SIGN(*nota)) e *= -1;
*d = (double)sig * pow(10.0, e);
return c;
}
char *nota_read_int(long long *n, char *nota)
{
if (!n)
return nota_skip(nota);
*n = 0;
char *c = nota;
*n |= (*c) & NOTA_INT_DATA; /* first three bits */
while (CONTINUE(*(c++)))
*n = (*n<<7) | (*c) & NOTA_DATA;
if (NOTA_INT_SIGN(*nota)) *n *= -1;
return c;
}
/* n is the number of bits */
char *nota_write_blob(unsigned long long n, char *nota)
{
nota[0] = NOTA_BLOB;
return nota_continue_num(n, nota, 4);
}
char *nota_write_array(unsigned long long n, char *nota)
{
nota[0] = NOTA_ARR;
return nota_continue_num(n, nota, 4);
}
char *nota_read_array(long long *len, char *nota)
{
if (!len) return nota;
return nota_read_num(len, nota);
}
char *nota_read_record(long long *len, char *nota)
{
if (!len) return nota;
return nota_read_num(len, nota);
}
char *nota_read_blob(long long *len, char *nota)
{
if (!len) return nota;
return nota_read_num(len, nota);
}
char *nota_write_record(unsigned long long n, char *nota)
{
nota[0] = NOTA_REC;
return nota_continue_num(n, nota, 4);
}
char *nota_write_sym(int sym, char *nota)
{
*nota = NOTA_SYM | sym;
return nota+1;
}
char *nota_read_sym(int *sym, char *nota)
{
if (*sym) *sym = (*nota) & 0x0f;
return nota+1;
}
char *nota_read_text(char **text, char *nota)
{
long long chars;
nota = nota_read_num(&chars, nota);
#ifdef NOTA_UTF8
*text = calloc(chars+1,1);
memcpy(*text, nota, chars);
nota += chars;
#else
char utf[chars*4];
char *pp = utf;
kim_to_utf8(&nota, &pp, chars);
*pp = 0;
*text = strdup(utf);
#endif
return nota;
}
char *nota_write_text(const char *s, char *nota)
{
nota[0] = NOTA_TEXT;
#ifdef NOTA_UTF8
long long n = strlen(s);
nota = nota_continue_num(n,nota,4);
memcpy(nota, s, n);
return nota+n;
#else
long long n = utf8_count(s);
nota = nota_continue_num(n,nota,4);
utf8_to_kim(&s, &nota);
return nota;
#endif
}

View file

@ -1,43 +0,0 @@
#ifndef NOTA_H
#define NOTA_H
#define NOTA_BLOB 0x00
#define NOTA_TEXT 0x10
#define NOTA_ARR 0x20
#define NOTA_REC 0x30
#define NOTA_FLOAT 0x40
#define NOTA_INT 0x60
#define NOTA_SYM 0x70
#define NOTA_FALSE 0x00
#define NOTA_TRUE 0x01
#define NOTA_NULL 0x02
#define NOTA_INF 0x03
#define NOTA_PRIVATE 0x08
#define NOTA_SYSTEM 0x09
typedef struct NOTA {
char *head;
} NOTA;
int nota_type(char *nota);
char *nota_read_blob(long long *len, char *nota);
char *nota_read_text(char **text, char *nota);
char *nota_read_array(long long *len, char *nota);
char *nota_read_record(long long *len, char *nota);
char *nota_read_float(double *d, char *nota);
char *nota_read_int(long long *l, char *nota);
char *nota_read_sym(int *sym, char *nota);
void print_nota_hex(char *nota);
char *nota_write_blob(unsigned long long n, char *nota);
char *nota_write_text(const char *s, char *nota);
char *nota_write_array(unsigned long long n, char *nota);
char *nota_write_record(unsigned long long n, char *nota);
char *nota_write_float(double n, char *nota);
char *nota_write_int(long long n, char *nota);
char *nota_write_sym(int sym, char *nota);
#endif

View file

@ -1,871 +0,0 @@
/* See end of file for license */
#ifndef POCKETMOD_H_INCLUDED
#define POCKETMOD_H_INCLUDED
#ifdef __cplusplus
extern "C" {
#endif
typedef struct pocketmod_context pocketmod_context;
int pocketmod_init(pocketmod_context *c, const void *data, int size, int rate);
int pocketmod_render(pocketmod_context *c, void *buffer, int size);
int pocketmod_loop_count(pocketmod_context *c);
#ifndef POCKETMOD_MAX_CHANNELS
#define POCKETMOD_MAX_CHANNELS 32
#endif
#ifndef POCKETMOD_MAX_SAMPLES
#define POCKETMOD_MAX_SAMPLES 31
#endif
typedef struct {
signed char *data; /* Sample data buffer */
unsigned int length; /* Data length (in bytes) */
} _pocketmod_sample;
typedef struct {
unsigned char dirty; /* Pitch/volume dirty flags */
unsigned char sample; /* Sample number (0..31) */
unsigned char volume; /* Base volume without tremolo (0..64) */
unsigned char balance; /* Stereo balance (0..255) */
unsigned short period; /* Note period (113..856) */
unsigned short delayed; /* Delayed note period (113..856) */
unsigned short target; /* Target period (for tone portamento) */
unsigned char finetune; /* Note finetune (0..15) */
unsigned char loop_count; /* E6x loop counter */
unsigned char loop_line; /* E6x target line */
unsigned char lfo_step; /* Vibrato/tremolo LFO step counter */
unsigned char lfo_type[2]; /* LFO type for vibrato/tremolo */
unsigned char effect; /* Current effect (0x0..0xf or 0xe0..0xef) */
unsigned char param; /* Raw effect parameter value */
unsigned char param3; /* Parameter memory for 3xx */
unsigned char param4; /* Parameter memory for 4xy */
unsigned char param7; /* Parameter memory for 7xy */
unsigned char param9; /* Parameter memory for 9xx */
unsigned char paramE1; /* Parameter memory for E1x */
unsigned char paramE2; /* Parameter memory for E2x */
unsigned char paramEA; /* Parameter memory for EAx */
unsigned char paramEB; /* Parameter memory for EBx */
unsigned char real_volume; /* Volume (with tremolo adjustment) */
float position; /* Position in sample data buffer */
float increment; /* Position increment per output sample */
} _pocketmod_chan;
struct pocketmod_context
{
/* Read-only song data */
_pocketmod_sample samples[POCKETMOD_MAX_SAMPLES];
unsigned char *source; /* Pointer to source MOD data */
unsigned char *order; /* Pattern order table */
unsigned char *patterns; /* Start of pattern data */
unsigned char length; /* Patterns in the order (1..128) */
unsigned char reset; /* Pattern to loop back to (0..127) */
unsigned char num_patterns; /* Patterns in the file (1..128) */
unsigned char num_samples; /* Sample count (15 or 31) */
unsigned char num_channels; /* Channel count (1..32) */
/* Timing variables */
int samples_per_second; /* Sample rate (set by user) */
int ticks_per_line; /* A.K.A. song speed (initially 6) */
float samples_per_tick; /* Depends on sample rate and BPM */
/* Loop detection state */
unsigned char visited[16]; /* Bit mask of previously visited patterns */
int loop_count; /* How many times the song has looped */
/* Render state */
_pocketmod_chan channels[POCKETMOD_MAX_CHANNELS];
unsigned char pattern_delay;/* EEx pattern delay counter */
unsigned int lfo_rng; /* RNG used for the random LFO waveform */
/* Position in song (from least to most granular) */
signed char pattern; /* Current pattern in order */
signed char line; /* Current line in pattern */
short tick; /* Current tick in line */
float sample; /* Current sample in tick */
};
#ifdef POCKETMOD_IMPLEMENTATION
/* Memorize a parameter unless the new value is zero */
#define POCKETMOD_MEM(dst, src) do { \
(dst) = (src) ? (src) : (dst); \
} while (0)
/* Same thing, but memorize each nibble separately */
#define POCKETMOD_MEM2(dst, src) do { \
(dst) = (((src) & 0x0f) ? ((src) & 0x0f) : ((dst) & 0x0f)) \
| (((src) & 0xf0) ? ((src) & 0xf0) : ((dst) & 0xf0)); \
} while (0)
/* Shortcut to sample metadata (sample must be nonzero) */
#define POCKETMOD_SAMPLE(c, sample) ((c)->source + 12 + 30 * (sample))
/* Channel dirty flags */
#define POCKETMOD_PITCH 0x01
#define POCKETMOD_VOLUME 0x02
/* The size of one sample in bytes */
#define POCKETMOD_SAMPLE_SIZE sizeof(float[2])
/* Finetune adjustment table. Three octaves for each finetune setting. */
static const signed char _pocketmod_finetune[16][36] = {
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{ -6, -6, -5, -5, -4, -3, -3, -3, -3, -3, -3, -3, -3, -3, -2, -3, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0},
{-12,-12,-10,-11, -8, -8, -7, -7, -6, -6, -6, -6, -6, -6, -5, -5, -4, -4, -4, -3, -3, -3, -3, -2, -3, -3, -2, -3, -3, -2, -2, -2, -2, -2, -2, -1},
{-18,-17,-16,-16,-13,-12,-12,-11,-10,-10,-10, -9, -9, -9, -8, -8, -7, -6, -6, -5, -5, -5, -5, -4, -5, -4, -3, -4, -4, -3, -3, -3, -3, -2, -2, -2},
{-24,-23,-21,-21,-18,-17,-16,-15,-14,-13,-13,-12,-12,-12,-11,-10, -9, -8, -8, -7, -7, -7, -7, -6, -6, -6, -5, -5, -5, -4, -4, -4, -4, -3, -3, -3},
{-30,-29,-26,-26,-23,-21,-20,-19,-18,-17,-17,-16,-15,-14,-13,-13,-11,-11,-10, -9, -9, -9, -8, -7, -8, -7, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4},
{-36,-34,-32,-31,-27,-26,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-11,-10,-10, -9, -9, -9, -7, -8, -7, -6, -6, -6, -6, -5, -5, -4},
{-42,-40,-37,-36,-32,-30,-29,-27,-25,-24,-23,-22,-21,-20,-18,-18,-16,-15,-14,-13,-13,-12,-12,-10,-10,-10, -9, -9, -9, -8, -7, -7, -7, -6, -6, -5},
{ 51, 48, 46, 42, 42, 38, 36, 34, 32, 30, 24, 27, 25, 24, 23, 21, 21, 19, 18, 17, 16, 15, 14, 14, 12, 12, 12, 10, 10, 10, 9, 8, 8, 8, 7, 7},
{ 44, 42, 40, 37, 37, 35, 32, 31, 29, 27, 25, 24, 22, 21, 20, 19, 18, 17, 16, 15, 15, 14, 13, 12, 11, 10, 10, 9, 9, 9, 8, 7, 7, 7, 6, 6},
{ 38, 36, 34, 32, 31, 30, 28, 27, 25, 24, 22, 21, 19, 18, 17, 16, 16, 15, 14, 13, 13, 12, 11, 11, 9, 9, 9, 8, 7, 7, 7, 6, 6, 6, 5, 5},
{ 31, 30, 29, 26, 26, 25, 24, 22, 21, 20, 18, 17, 16, 15, 14, 13, 13, 12, 12, 11, 11, 10, 9, 9, 8, 7, 8, 7, 6, 6, 6, 5, 5, 5, 5, 5},
{ 25, 24, 23, 21, 21, 20, 19, 18, 17, 16, 14, 14, 13, 12, 11, 10, 11, 10, 10, 9, 9, 8, 7, 7, 6, 6, 6, 5, 5, 5, 5, 4, 4, 4, 3, 4},
{ 19, 18, 17, 16, 16, 15, 15, 14, 13, 12, 11, 10, 9, 9, 9, 8, 8, 18, 7, 7, 7, 6, 5, 6, 5, 4, 5, 4, 4, 4, 4, 3, 3, 3, 3, 3},
{ 12, 12, 12, 10, 11, 11, 10, 10, 9, 8, 7, 7, 6, 6, 6, 5, 6, 5, 5, 5, 5, 4, 4, 4, 3, 3, 3, 3, 2, 3, 3, 2, 2, 2, 2, 2},
{ 6, 6, 6, 5, 6, 6, 6, 5, 5, 5, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1}
};
/* Min/max helper functions */
static int _pocketmod_min(int x, int y) { return x < y ? x : y; }
static int _pocketmod_max(int x, int y) { return x > y ? x : y; }
/* Clamp a volume value to the 0..64 range */
static int _pocketmod_clamp_volume(int x)
{
x = _pocketmod_max(x, 0x00);
x = _pocketmod_min(x, 0x40);
return x;
}
/* Zero out a block of memory */
static void _pocketmod_zero(void *data, int size)
{
char *byte = data, *end = byte + size;
while (byte != end) { *byte++ = 0; }
}
/* Convert a period (at finetune = 0) to a note index in 0..35 */
static int _pocketmod_period_to_note(int period)
{
switch (period) {
case 856: return 0; case 808: return 1; case 762: return 2;
case 720: return 3; case 678: return 4; case 640: return 5;
case 604: return 6; case 570: return 7; case 538: return 8;
case 508: return 9; case 480: return 10; case 453: return 11;
case 428: return 12; case 404: return 13; case 381: return 14;
case 360: return 15; case 339: return 16; case 320: return 17;
case 302: return 18; case 285: return 19; case 269: return 20;
case 254: return 21; case 240: return 22; case 226: return 23;
case 214: return 24; case 202: return 25; case 190: return 26;
case 180: return 27; case 170: return 28; case 160: return 29;
case 151: return 30; case 143: return 31; case 135: return 32;
case 127: return 33; case 120: return 34; case 113: return 35;
default: return 0;
}
}
/* Table-based sine wave oscillator */
static int _pocketmod_sin(int step)
{
/* round(sin(x * pi / 32) * 255) for x in 0..15 */
static const unsigned char sin[16] = {
0x00, 0x19, 0x32, 0x4a, 0x62, 0x78, 0x8e, 0xa2,
0xb4, 0xc5, 0xd4, 0xe0, 0xec, 0xf4, 0xfa, 0xfe
};
int x = sin[step & 0x0f];
x = (step & 0x1f) < 0x10 ? x : 0xff - x;
return step < 0x20 ? x : -x;
}
/* Oscillators for vibrato/tremolo effects */
static int _pocketmod_lfo(pocketmod_context *c, _pocketmod_chan *ch, int step)
{
switch (ch->lfo_type[ch->effect == 7] & 3) {
case 0: return _pocketmod_sin(step & 0x3f); /* Sine */
case 1: return 0xff - ((step & 0x3f) << 3); /* Saw */
case 2: return (step & 0x3f) < 0x20 ? 0xff : -0xff; /* Square */
case 3: return (c->lfo_rng & 0x1ff) - 0xff; /* Random */
default: return 0; /* Hush little compiler */
}
}
static void _pocketmod_update_pitch(pocketmod_context *c, _pocketmod_chan *ch)
{
/* Don't do anything if the period is zero */
ch->increment = 0.0f;
if (ch->period) {
float period = ch->period;
/* Apply vibrato (if active) */
if (ch->effect == 0x4 || ch->effect == 0x6) {
int step = (ch->param4 >> 4) * ch->lfo_step;
int rate = ch->param4 & 0x0f;
period += _pocketmod_lfo(c, ch, step) * rate / 128.0f;
/* Apply arpeggio (if active) */
} else if (ch->effect == 0x0 && ch->param) {
static const float arpeggio[16] = { /* 2^(X/12) for X in 0..15 */
1.000000f, 1.059463f, 1.122462f, 1.189207f,
1.259921f, 1.334840f, 1.414214f, 1.498307f,
1.587401f, 1.681793f, 1.781797f, 1.887749f,
2.000000f, 2.118926f, 2.244924f, 2.378414f
};
int step = (ch->param >> ((2 - c->tick % 3) << 2)) & 0x0f;
period /= arpeggio[step];
}
/* Calculate sample buffer position increment */
ch->increment = 3546894.6f / (period * c->samples_per_second);
}
/* Clear the pitch dirty flag */
ch->dirty &= ~POCKETMOD_PITCH;
}
static void _pocketmod_update_volume(pocketmod_context *c, _pocketmod_chan *ch)
{
int volume = ch->volume;
if (ch->effect == 0x7) {
int step = ch->lfo_step * (ch->param7 >> 4);
volume += _pocketmod_lfo(c, ch, step) * (ch->param7 & 0x0f) >> 6;
}
ch->real_volume = _pocketmod_clamp_volume(volume);
ch->dirty &= ~POCKETMOD_VOLUME;
}
static void _pocketmod_pitch_slide(_pocketmod_chan *ch, int amount)
{
int max = 856 + _pocketmod_finetune[ch->finetune][ 0];
int min = 113 + _pocketmod_finetune[ch->finetune][35];
ch->period += amount;
ch->period = _pocketmod_max(ch->period, min);
ch->period = _pocketmod_min(ch->period, max);
ch->dirty |= POCKETMOD_PITCH;
}
static void _pocketmod_volume_slide(_pocketmod_chan *ch, int param)
{
/* Undocumented quirk: If both x and y are nonzero, then the value of x */
/* takes precedence. (Yes, there are songs that rely on this behavior.) */
int change = (param & 0xf0) ? (param >> 4) : -(param & 0x0f);
ch->volume = _pocketmod_clamp_volume(ch->volume + change);
ch->dirty |= POCKETMOD_VOLUME;
}
static void _pocketmod_next_line(pocketmod_context *c)
{
unsigned char (*data)[4];
int i, pos, pattern_break = -1;
/* When entering a new pattern order index, mark it as "visited" */
if (c->line == 0) {
c->visited[c->pattern >> 3] |= 1 << (c->pattern & 7);
}
/* Move to the next pattern if this was the last line */
if (++c->line == 64) {
if (++c->pattern == c->length) {
c->pattern = c->reset;
}
c->line = 0;
}
/* Find the pattern data for the current line */
pos = (c->order[c->pattern] * 64 + c->line) * c->num_channels * 4;
data = (unsigned char(*)[4]) (c->patterns + pos);
for (i = 0; i < c->num_channels; i++) {
/* Decode columns */
int sample = (data[i][0] & 0xf0) | (data[i][2] >> 4);
int period = ((data[i][0] & 0x0f) << 8) | data[i][1];
int effect = ((data[i][2] & 0x0f) << 8) | data[i][3];
/* Memorize effect parameter values */
_pocketmod_chan *ch = &c->channels[i];
ch->effect = (effect >> 8) != 0xe ? (effect >> 8) : (effect >> 4);
ch->param = (effect >> 8) != 0xe ? (effect & 0xff) : (effect & 0x0f);
/* Set sample */
if (sample) {
if (sample <= POCKETMOD_MAX_SAMPLES) {
unsigned char *sample_data = POCKETMOD_SAMPLE(c, sample);
ch->sample = sample;
ch->finetune = sample_data[2] & 0x0f;
ch->volume = _pocketmod_min(sample_data[3], 0x40);
if (ch->effect != 0xED) {
ch->dirty |= POCKETMOD_VOLUME;
}
} else {
ch->sample = 0;
}
}
/* Set note */
if (period) {
int note = _pocketmod_period_to_note(period);
period += _pocketmod_finetune[ch->finetune][note];
if (ch->effect != 0x3) {
if (ch->effect != 0xED) {
ch->period = period;
ch->dirty |= POCKETMOD_PITCH;
ch->position = 0.0f;
ch->lfo_step = 0;
} else {
ch->delayed = period;
}
}
}
/* Handle pattern effects */
switch (ch->effect) {
/* Memorize parameters */
case 0x3: POCKETMOD_MEM(ch->param3, ch->param); /* Fall through */
case 0x5: POCKETMOD_MEM(ch->target, period); break;
case 0x4: POCKETMOD_MEM2(ch->param4, ch->param); break;
case 0x7: POCKETMOD_MEM2(ch->param7, ch->param); break;
case 0xE1: POCKETMOD_MEM(ch->paramE1, ch->param); break;
case 0xE2: POCKETMOD_MEM(ch->paramE2, ch->param); break;
case 0xEA: POCKETMOD_MEM(ch->paramEA, ch->param); break;
case 0xEB: POCKETMOD_MEM(ch->paramEB, ch->param); break;
/* 8xx: Set stereo balance (nonstandard) */
case 0x8: {
ch->balance = ch->param;
} break;
/* 9xx: Set sample offset */
case 0x9: {
if (period != 0 || sample != 0) {
ch->param9 = ch->param ? ch->param : ch->param9;
ch->position = ch->param9 << 8;
}
} break;
/* Bxx: Jump to pattern */
case 0xB: {
c->pattern = ch->param < c->length ? ch->param : 0;
c->line = -1;
} break;
/* Cxx: Set volume */
case 0xC: {
ch->volume = _pocketmod_clamp_volume(ch->param);
ch->dirty |= POCKETMOD_VOLUME;
} break;
/* Dxy: Pattern break */
case 0xD: {
pattern_break = (ch->param >> 4) * 10 + (ch->param & 15);
} break;
/* E4x: Set vibrato waveform */
case 0xE4: {
ch->lfo_type[0] = ch->param;
} break;
/* E5x: Set sample finetune */
case 0xE5: {
ch->finetune = ch->param;
ch->dirty |= POCKETMOD_PITCH;
} break;
/* E6x: Pattern loop */
case 0xE6: {
if (ch->param) {
if (!ch->loop_count) {
ch->loop_count = ch->param;
c->line = ch->loop_line;
} else if (--ch->loop_count) {
c->line = ch->loop_line;
}
} else {
ch->loop_line = c->line - 1;
}
} break;
/* E7x: Set tremolo waveform */
case 0xE7: {
ch->lfo_type[1] = ch->param;
} break;
/* E8x: Set stereo balance (nonstandard) */
case 0xE8: {
ch->balance = ch->param << 4;
} break;
/* EEx: Pattern delay */
case 0xEE: {
c->pattern_delay = ch->param;
} break;
/* Fxx: Set speed */
case 0xF: {
if (ch->param != 0) {
if (ch->param < 0x20) {
c->ticks_per_line = ch->param;
} else {
float rate = c->samples_per_second;
c->samples_per_tick = rate / (0.4f * ch->param);
}
}
} break;
default: break;
}
}
/* Pattern breaks are handled here, so that only one jump happens even */
/* when multiple Dxy commands appear on the same line. (You guessed it: */
/* There are songs that rely on this behavior!) */
if (pattern_break != -1) {
c->line = (pattern_break < 64 ? pattern_break : 0) - 1;
if (++c->pattern == c->length) {
c->pattern = c->reset;
}
}
}
static void _pocketmod_next_tick(pocketmod_context *c)
{
int i;
/* Move to the next line if this was the last tick */
if (++c->tick == c->ticks_per_line) {
if (c->pattern_delay > 0) {
c->pattern_delay--;
} else {
_pocketmod_next_line(c);
}
c->tick = 0;
}
/* Make per-tick adjustments for all channels */
for (i = 0; i < c->num_channels; i++) {
_pocketmod_chan *ch = &c->channels[i];
int param = ch->param;
/* Advance the LFO random number generator */
c->lfo_rng = 0x0019660d * c->lfo_rng + 0x3c6ef35f;
/* Handle effects that may happen on any tick of a line */
switch (ch->effect) {
/* 0xy: Arpeggio */
case 0x0: {
ch->dirty |= POCKETMOD_PITCH;
} break;
/* E9x: Retrigger note every x ticks */
case 0xE9: {
if (!(param && c->tick % param)) {
ch->position = 0.0f;
ch->lfo_step = 0;
}
} break;
/* ECx: Cut note after x ticks */
case 0xEC: {
if (c->tick == param) {
ch->volume = 0;
ch->dirty |= POCKETMOD_VOLUME;
}
} break;
/* EDx: Delay note for x ticks */
case 0xED: {
if (c->tick == param && ch->sample) {
ch->dirty |= POCKETMOD_VOLUME | POCKETMOD_PITCH;
ch->period = ch->delayed;
ch->position = 0.0f;
ch->lfo_step = 0;
}
} break;
default: break;
}
/* Handle effects that only happen on the first tick of a line */
if (c->tick == 0) {
switch (ch->effect) {
case 0xE1: _pocketmod_pitch_slide(ch, -ch->paramE1); break;
case 0xE2: _pocketmod_pitch_slide(ch, +ch->paramE2); break;
case 0xEA: _pocketmod_volume_slide(ch, ch->paramEA << 4); break;
case 0xEB: _pocketmod_volume_slide(ch, ch->paramEB & 15); break;
default: break;
}
/* Handle effects that are not applied on the first tick of a line */
} else {
switch (ch->effect) {
/* 1xx: Portamento up */
case 0x1: {
_pocketmod_pitch_slide(ch, -param);
} break;
/* 2xx: Portamento down */
case 0x2: {
_pocketmod_pitch_slide(ch, +param);
} break;
/* 5xy: Volume slide + tone portamento */
case 0x5: {
_pocketmod_volume_slide(ch, param);
} /* Fall through */
/* 3xx: Tone portamento */
case 0x3: {
int rate = ch->param3;
int order = ch->period < ch->target;
int closer = ch->period + (order ? rate : -rate);
int new_order = closer < ch->target;
ch->period = new_order == order ? closer : ch->target;
ch->dirty |= POCKETMOD_PITCH;
} break;
/* 6xy: Volume slide + vibrato */
case 0x6: {
_pocketmod_volume_slide(ch, param);
} /* Fall through */
/* 4xy: Vibrato */
case 0x4: {
ch->lfo_step++;
ch->dirty |= POCKETMOD_PITCH;
} break;
/* 7xy: Tremolo */
case 0x7: {
ch->lfo_step++;
ch->dirty |= POCKETMOD_VOLUME;
} break;
/* Axy: Volume slide */
case 0xA: {
_pocketmod_volume_slide(ch, param);
} break;
default: break;
}
}
/* Update channel volume/pitch if either is out of date */
if (ch->dirty & POCKETMOD_VOLUME) { _pocketmod_update_volume(c, ch); }
if (ch->dirty & POCKETMOD_PITCH) { _pocketmod_update_pitch(c, ch); }
}
}
static void _pocketmod_render_channel(pocketmod_context *c,
_pocketmod_chan *chan,
float *output,
int samples_to_write)
{
/* Gather some loop data */
_pocketmod_sample *sample = &c->samples[chan->sample - 1];
unsigned char *data = POCKETMOD_SAMPLE(c, chan->sample);
const int loop_start = ((data[4] << 8) | data[5]) << 1;
const int loop_length = ((data[6] << 8) | data[7]) << 1;
const int loop_end = loop_length > 2 ? loop_start + loop_length : 0xffffff;
const float sample_end = 1 + _pocketmod_min(loop_end, sample->length);
/* Calculate left/right levels */
const float volume = chan->real_volume / (float) (128 * 64 * 4);
const float level_l = volume * (1.0f - chan->balance / 255.0f);
const float level_r = volume * (0.0f + chan->balance / 255.0f);
/* Write samples */
int i, num;
do {
/* Calculate how many samples we can write in one go */
num = (sample_end - chan->position) / chan->increment;
num = _pocketmod_min(num, samples_to_write);
/* Resample and write 'num' samples */
for (i = 0; i < num; i++) {
int x0 = chan->position;
#ifdef POCKETMOD_NO_INTERPOLATION
float s = sample->data[x0];
#else
int x1 = x0 + 1 - loop_length * (x0 + 1 >= loop_end);
float t = chan->position - x0;
float s = (1.0f - t) * sample->data[x0] + t * sample->data[x1];
#endif
chan->position += chan->increment;
*output++ += level_l * s;
*output++ += level_r * s;
}
/* Rewind the sample when reaching the loop point */
if (chan->position >= loop_end) {
chan->position -= loop_length;
/* Cut the sample if the end is reached */
} else if (chan->position >= sample->length) {
chan->position = -1.0f;
break;
}
samples_to_write -= num;
} while (num > 0);
}
static int _pocketmod_ident(pocketmod_context *c, unsigned char *data, int size)
{
int i, j;
/* 31-instrument files are at least 1084 bytes long */
if (size >= 1084) {
/* The format tag is located at offset 1080 */
unsigned char *tag = data + 1080;
/* List of recognized format tags (possibly incomplete) */
static const struct {
char name[5];
char channels;
} tags[] = {
/* TODO: FLT8 intentionally omitted because I haven't been able */
/* to find a specimen to test its funky pattern pairing format */
{"M.K.", 4}, {"M!K!", 4}, {"FLT4", 4}, {"4CHN", 4},
{"OKTA", 8}, {"OCTA", 8}, {"CD81", 8}, {"FA08", 8},
{"1CHN", 1}, {"2CHN", 2}, {"3CHN", 3}, {"4CHN", 4},
{"5CHN", 5}, {"6CHN", 6}, {"7CHN", 7}, {"8CHN", 8},
{"9CHN", 9}, {"10CH", 10}, {"11CH", 11}, {"12CH", 12},
{"13CH", 13}, {"14CH", 14}, {"15CH", 15}, {"16CH", 16},
{"17CH", 17}, {"18CH", 18}, {"19CH", 19}, {"20CH", 20},
{"21CH", 21}, {"22CH", 22}, {"23CH", 23}, {"24CH", 24},
{"25CH", 25}, {"26CH", 26}, {"27CH", 27}, {"28CH", 28},
{"29CH", 29}, {"30CH", 30}, {"31CH", 31}, {"32CH", 32}
};
/* Check the format tag to determine if this is a 31-sample MOD */
for (i = 0; i < (int) (sizeof(tags) / sizeof(*tags)); i++) {
if (tags[i].name[0] == tag[0] && tags[i].name[1] == tag[1]
&& tags[i].name[2] == tag[2] && tags[i].name[3] == tag[3]) {
c->num_channels = tags[i].channels;
c->length = data[950];
c->reset = data[951];
c->order = &data[952];
c->patterns = &data[1084];
c->num_samples = 31;
return 1;
}
}
}
/* A 15-instrument MOD has to be at least 600 bytes long */
if (size < 600) {
return 0;
}
/* Check that the song title only contains ASCII bytes (or null) */
for (i = 0; i < 20; i++) {
if (data[i] != '\0' && (data[i] < ' ' || data[i] > '~')) {
return 0;
}
}
/* Check that sample names only contain ASCII bytes (or null) */
for (i = 0; i < 15; i++) {
for (j = 0; j < 22; j++) {
char chr = data[20 + i * 30 + j];
if (chr != '\0' && (chr < ' ' || chr > '~')) {
return 0;
}
}
}
/* It looks like we have an older 15-instrument MOD */
c->length = data[470];
c->reset = data[471];
c->order = &data[472];
c->patterns = &data[600];
c->num_samples = 15;
c->num_channels = 4;
return 1;
}
int pocketmod_init(pocketmod_context *c, const void *data, int size, int rate)
{
int i, remaining, header_bytes, pattern_bytes;
unsigned char *byte = (unsigned char*) c;
signed char *sample_data;
/* Check that arguments look more or less sane */
if (!c || !data || rate <= 0 || size <= 0) {
return 0;
}
/* Zero out the whole context and identify the MOD type */
_pocketmod_zero(c, sizeof(pocketmod_context));
c->source = (unsigned char*) data;
if (!_pocketmod_ident(c, c->source, size)) {
return 0;
}
/* Check that we are compiled with support for enough channels */
if (c->num_channels > POCKETMOD_MAX_CHANNELS) {
return 0;
}
/* Check that we have enough sample slots for this file */
if (POCKETMOD_MAX_SAMPLES < 31) {
byte = (unsigned char*) data + 20;
for (i = 0; i < c->num_samples; i++) {
unsigned int length = 2 * ((byte[22] << 8) | byte[23]);
if (i >= POCKETMOD_MAX_SAMPLES && length > 2) {
return 0; /* Can't fit this sample */
}
byte += 30;
}
}
/* Check that the song length is in valid range (1..128) */
if (c->length == 0 || c->length > 128) {
return 0;
}
/* Make sure that the reset pattern doesn't take us out of bounds */
if (c->reset >= c->length) {
c->reset = 0;
}
/* Count how many patterns there are in the file */
c->num_patterns = 0;
for (i = 0; i < 128 && c->order[i] < 128; i++) {
c->num_patterns = _pocketmod_max(c->num_patterns, c->order[i]);
}
pattern_bytes = 256 * c->num_channels * ++c->num_patterns;
header_bytes = (int) ((char*) c->patterns - (char*) data);
/* Check that each pattern in the order is within file bounds */
for (i = 0; i < c->length; i++) {
if (header_bytes + 256 * c->num_channels * c->order[i] > size) {
return 0; /* Reading this pattern would be a buffer over-read! */
}
}
/* Check that the pattern data doesn't extend past the end of the file */
if (header_bytes + pattern_bytes > size) {
return 0;
}
/* Load sample payload data, truncating ones that extend outside the file */
remaining = size - header_bytes - pattern_bytes;
sample_data = (signed char*) data + header_bytes + pattern_bytes;
for (i = 0; i < c->num_samples; i++) {
unsigned char *data = POCKETMOD_SAMPLE(c, i + 1);
unsigned int length = ((data[0] << 8) | data[1]) << 1;
_pocketmod_sample *sample = &c->samples[i];
sample->data = sample_data;
sample->length = _pocketmod_min(length > 2 ? length : 0, remaining);
sample_data += sample->length;
remaining -= sample->length;
}
/* Set up ProTracker default panning for all channels */
for (i = 0; i < c->num_channels; i++) {
c->channels[i].balance = 0x80 + ((((i + 1) >> 1) & 1) ? 0x20 : -0x20);
}
/* Prepare to render from the start */
c->ticks_per_line = 6;
c->samples_per_second = rate;
c->samples_per_tick = rate / 50.0f;
c->lfo_rng = 0xbadc0de;
c->line = -1;
c->tick = c->ticks_per_line - 1;
_pocketmod_next_tick(c);
return 1;
}
int pocketmod_render(pocketmod_context *c, void *buffer, int buffer_size)
{
int i, samples_rendered = 0;
int samples_remaining = buffer_size / POCKETMOD_SAMPLE_SIZE;
if (c && buffer) {
float (*output)[2] = (float(*)[2]) buffer;
while (samples_remaining > 0) {
/* Calculate the number of samples left in this tick */
int num = (int) (c->samples_per_tick - c->sample);
num = _pocketmod_min(num + !num, samples_remaining);
/* Render and mix 'num' samples from each channel */
_pocketmod_zero(output, num * POCKETMOD_SAMPLE_SIZE);
for (i = 0; i < c->num_channels; i++) {
_pocketmod_chan *chan = &c->channels[i];
if (chan->sample != 0 && chan->position >= 0.0f) {
_pocketmod_render_channel(c, chan, *output, num);
}
}
samples_remaining -= num;
samples_rendered += num;
output += num;
/* Advance song position by 'num' samples */
if ((c->sample += num) >= c->samples_per_tick) {
c->sample -= c->samples_per_tick;
_pocketmod_next_tick(c);
/* Stop if a new pattern was reached */
if (c->line == 0 && c->tick == 0) {
/* Increment loop counter as needed */
if (c->visited[c->pattern >> 3] & (1 << (c->pattern & 7))) {
_pocketmod_zero(c->visited, sizeof(c->visited));
c->loop_count++;
}
break;
}
}
}
}
return samples_rendered * POCKETMOD_SAMPLE_SIZE;
}
int pocketmod_loop_count(pocketmod_context *c)
{
return c->loop_count;
}
#endif /* #ifdef POCKETMOD_IMPLEMENTATION */
#ifdef __cplusplus
}
#endif
#endif /* #ifndef POCKETMOD_H_INCLUDED */
/*******************************************************************************
MIT License
Copyright (c) 2018 rombankzero
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION 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

@ -17,7 +17,6 @@
#include "sokol/sokol_gfx.h" #include "sokol/sokol_gfx.h"
#include "sokol/util/sokol_gl.h" #include "sokol/util/sokol_gl.h"
#include "gui.h"
static HMM_Vec2 lastuse = {0}; static HMM_Vec2 lastuse = {0};

View file

@ -141,34 +141,6 @@ void resources_init() {
#endif #endif
} }
char *get_filename_from_path(char *path, int extension) {
char *dirpos = strrchr(path, '/');
if (!dirpos)
dirpos = path;
char *end = strrchr(path, '\0');
int offset = 0;
if (!extension) {
char *ext = strrchr(path, '.');
offset = end - ext;
YughInfo("Making %s without extension ...");
}
char *filename = malloc(sizeof(char) * (end - dirpos - offset + 1));
strncpy(filename, dirpos, end - dirpos - offset);
return filename;
}
char *dirname(const char *path)
{
const char *dirpos = strrchr(path, '/');
if (!dirpos) return ".";
char *dir = malloc(dirpos-path+1);
strncpy(dir,path,dirpos-path);
return dir;
}
char *seprint(char *fmt, ...) char *seprint(char *fmt, ...)
{ {
va_list args; va_list args;
@ -227,28 +199,7 @@ char **ls(const char *path)
return ls_paths; return ls_paths;
} }
void pack_start(const char *name)
{
// memset(&ar, 0, sizeof(ar));
// int status = mz_zip_writer_init_file(&ar, name, 0);
}
void pack_add(const char *path)
{
// mz_zip_writer_add_file(&ar, path, path, NULL, 0, MZ_BEST_COMPRESSION);
}
void pack_end()
{
// mz_zip_writer_finalize_archive(&ar);
// mz_zip_writer_end(&ar);
}
#else #else
void pack_start(const char *name){}
void pack_add(const char *path){}
void pack_end() {}
void fill_extensions(char *paths, const char *path, const char *ext) void fill_extensions(char *paths, const char *path, const char *ext)
{}; {};
char **ls(const char *path) { return NULL; } char **ls(const char *path) { return NULL; }

View file

@ -10,8 +10,6 @@ extern char *DATA_PATH;
extern int LOADED_GAME; extern int LOADED_GAME;
void resources_init(); void resources_init();
char *get_filename_from_path(char *path, int extension);
char *dirname(const char *path);
char *str_replace_ext(const char *s, const char *newext); char *str_replace_ext(const char *s, const char *newext);
FILE *res_open(char *path, const char *tag); FILE *res_open(char *path, const char *tag);
char **ls(const char *path); char **ls(const char *path);

File diff suppressed because it is too large Load diff

View file

@ -4,6 +4,10 @@
#include "stb_ds.h" #include "stb_ds.h"
#include "resources.h" #include "resources.h"
#include <sokol/sokol_time.h> #include <sokol/sokol_time.h>
#include <inttypes.h>
#include <limits.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdarg.h> #include <stdarg.h>
@ -18,10 +22,86 @@ JSRuntime *rt = NULL;
static JSValue report_gc; static JSValue report_gc;
static uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename)
{
FILE *f;
uint8_t *buf;
size_t buf_len;
long lret;
f = fopen(filename, "rb");
if (!f)
return NULL;
if (fseek(f, 0, SEEK_END) < 0)
goto fail;
lret = ftell(f);
if (lret < 0)
goto fail;
/* XXX: on Linux, ftell() return LONG_MAX for directories */
if (lret == LONG_MAX) {
errno = EISDIR;
goto fail;
}
buf_len = lret;
if (fseek(f, 0, SEEK_SET) < 0)
goto fail;
if (ctx)
buf = js_malloc(ctx, buf_len + 1);
else
buf = malloc(buf_len + 1);
if (!buf)
goto fail;
if (fread(buf, 1, buf_len, f) != buf_len) {
errno = EIO;
if (ctx)
js_free(ctx, buf);
else
free(buf);
fail:
fclose(f);
return NULL;
}
buf[buf_len] = '\0';
fclose(f);
*pbuf_len = buf_len;
return buf;
}
static JSModuleDef *js_module_loader(JSContext *ctx,
const char *module_name, void *opaque)
{
JSModuleDef *m;
size_t buf_len;
uint8_t *buf;
JSValue func_val;
buf = js_load_file(ctx, &buf_len, module_name);
if (!buf) {
JS_ThrowReferenceError(ctx, "could not load module filename '%s'",
module_name);
return NULL;
}
/* compile the module */
func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name,
JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
js_free(ctx, buf);
if (JS_IsException(func_val))
return NULL;
/* XXX: could propagate the exception */
js_module_set_import_meta(ctx, func_val, 1, 0);
/* the module is already referenced, so we must free it */
m = JS_VALUE_GET_PTR(func_val);
JS_FreeValue(ctx, func_val);
return m;
}
void script_startup() { void script_startup() {
rt = JS_NewRuntime(); rt = JS_NewRuntime();
js = JS_NewContext(rt); js = JS_NewContext(rt);
JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL);
ffi_load(); ffi_load();
size_t len; size_t len;

View file

@ -1,622 +0,0 @@
#include "dsp.h"
#include "sound.h"
#include "limits.h"
#include "math.h"
#include "stdlib.h"
#include "iir.h"
#include "log.h"
#include "stb_ds.h"
#include "smbPitchShift.h"
#include "pthread.h"
#define PI 3.14159265
int SAMPLERATE = 48000;
int BUF_FRAMES = 2048;
int CHANNELS = 2;
dsp_node *masterbus = NULL;
void iir_free(struct dsp_iir *iir)
{
free(iir->a);
free(iir->b);
free(iir->x);
free(iir->y);
free(iir);
}
void interleave(soundbyte *a, soundbyte *b, soundbyte *restrict stereo, int frames)
{
for (int i = 0; i < frames; i++) {
stereo[i*2] = a[i];
stereo[i*2+1] = b[i];
}
}
void deinterleave(soundbyte *restrict stereo, soundbyte *restrict out, int frames, int channels, int chout)
{
chout--;
for (int i = 0; i < frames; i++)
out[i] = stereo[i*channels+chout];
}
void mono_to_stero(soundbyte *a, soundbyte *stereo, int frames)
{
interleave(a,a,stereo, frames);
}
void mono_expand(soundbyte *restrict buffer, int to, int frames)
{
soundbyte hold[frames];
memcpy(hold, buffer, sizeof(soundbyte)*frames);
for (int i = 0; i < frames; i++)
for (int j = 0; j < to; j++)
buffer[i*to+j] = hold[i];
}
dsp_node *dsp_mixer_node()
{
return make_node(NULL, NULL, NULL);
}
void dsp_init()
{
masterbus = dsp_limiter(1.0);
}
soundbyte *dsp_node_out(dsp_node *node)
{
zero_soundbytes(node->cache, BUF_FRAMES*CHANNELS);
if (node->off) return node->cache;
/* Sum all inputs */
for (int i = 0; i < arrlen(node->ins); i++) {
soundbyte *out = dsp_node_out(node->ins[i]);
sum_soundbytes(node->cache, out, BUF_FRAMES*CHANNELS);
}
/* If there's a filter, run it */
if (!node->pass && node->proc)
node->proc(node->data, node->cache, BUF_FRAMES);
scale_soundbytes(node->cache, node->gain, BUF_FRAMES*CHANNELS);
pan_frames(node->cache, node->pan, BUF_FRAMES);
return node->cache;
}
void filter_am_mod(dsp_node *restrict mod, soundbyte *restrict buffer, int frames)
{
soundbyte *m = dsp_node_out(mod);
for (int i = 0; i < frames*CHANNELS; i++) buffer[i] *= m[i];
}
dsp_node *dsp_am_mod(dsp_node *mod)
{
return make_node(mod, filter_am_mod, node_free);
}
/* Add b into a */
void sum_soundbytes(soundbyte *restrict a, soundbyte *restrict b, int samples)
{
for (int i = 0; i < samples; i++) a[i] += b[i];
}
void norm_soundbytes(soundbyte *a, float lvl, int samples)
{
float tar = lvl;
float max = 0 ;
for (int i = 0; i < samples; i++) max = fabsf(a[i]) > max ? fabsf(a[i]) : max;
float mult = max/tar;
scale_soundbytes(a, mult, samples);
}
void scale_soundbytes(soundbyte *a, float scale, int samples)
{
if (scale == 1) return;
for (int i = 0; i < samples; i++) a[i] *= scale;
}
void zero_soundbytes(soundbyte *restrict a, int samples) { memset(a, 0, sizeof(soundbyte)*samples); }
void set_soundbytes(soundbyte *a, soundbyte *b, int samples)
{
zero_soundbytes(a, samples);
sum_soundbytes(a,b,samples);
}
void dsp_node_run(dsp_node *node)
{
zero_soundbytes(node->cache, BUF_FRAMES*CHANNELS);
for (int i = 0; i < arrlen(node->ins); i++) {
soundbyte *out = dsp_node_out(node->ins[i]);
sum_soundbytes(node->cache, out, BUF_FRAMES);
}
}
static int node_count = 0;
dsp_node *make_node(void *data, void (*proc)(void *data, soundbyte *out, int samples), void (*fr)(void *data))
{
dsp_node *self = malloc(sizeof(dsp_node));
memset(self, 0, sizeof(*self));
self->data = data;
self->cache = calloc(BUF_FRAMES*CHANNELS*sizeof(soundbyte),1);
self->proc = proc;
self->pass = 0;
self->gain = 1;
return self;
}
void node_free(dsp_node *node)
{
if (node == masterbus) {
YughWarn("Attempted to delete the master bus.");
return; /* Simple check to not delete the masterbus */
}
pthread_mutex_lock(&soundrun);
// unplug_node(node);
if (node->data) {
if (node->data_free)
node->data_free(node->data);
else
free(node->data);
}
free(node);
pthread_mutex_unlock(&soundrun);
}
void dsp_node_free(dsp_node *node) { node_free(node); }
void plugin_node(dsp_node *from, dsp_node *to)
{
if (from->out) return;
arrput(to->ins, from);
from->out = to;
}
/* Unplug the given node from its output */
void unplug_node(dsp_node *node)
{
if (!node->out) return;
for (int i = 0; arrlen(node->out->ins); i++)
if (node == node->out->ins[i]) {
arrdelswap(node->out->ins, i);
node->out = NULL;
return;
}
}
float sin_phasor(float p)
{
return sin(2*PI*p);
}
float square_phasor(float p)
{
return lround(p);
}
float saw_phasor(float p)
{
return 2*p-1;
}
float tri_phasor(float p)
{
return 4*(p * 0.5f ? p : (1-p)) - 1;
}
void filter_phasor(phasor *p, soundbyte *buffer, int frames)
{
for (int i = 0; i < frames; i++) {
buffer[i] = p->filter(p->phase) * p->amp;
p->phase += p->freq/SAMPLERATE;
}
p->phase = p->phase - (int)p->phase;
mono_expand(buffer, CHANNELS, frames);
}
dsp_node *dsp_phasor(float amp, float freq, float (*filter)(float))
{
phasor *p = malloc(sizeof(*p));
p->amp = amp;
p->freq = freq;
p->phase = 0;
p->filter = filter;
return make_node(p, filter_phasor, NULL);
}
void filter_rectify(void *restrict data, soundbyte *restrict out, int n)
{
for (int i = 0; i < n; i++) out[i] = fabsf(out[i]);
}
dsp_node *dsp_rectify()
{
return make_node(NULL, filter_rectify, NULL);
}
soundbyte sample_whitenoise()
{
return ((float)rand()/(float)(RAND_MAX/2))-1;
}
void gen_whitenoise(void *data, soundbyte *out, int n)
{
for (int i = 0; i < n; i++)
out[i] = sample_whitenoise();
mono_expand(out, CHANNELS, n);
}
dsp_node *dsp_whitenoise()
{
return make_node(NULL, gen_whitenoise, NULL);
}
void gen_pinknoise(struct dsp_iir *pink, soundbyte *out, int n)
{
gen_whitenoise(NULL, out, n);
for (int i = 0; i < n*CHANNELS; i++) {
soundbyte sum = 0;
for (int j = 0; j < 6; j++) {
pink->x[j] = pink->x[j]*pink->b[j] + out[i]*pink->a[j];
sum += pink->x[j];
}
pink->x[6] = out[i] * 0.115926;
out[i] = sum + out[i] * 0.5362 + pink->x[6];
out[i] *= 0.11;
}
/* * https://www.firstpr.com.au/dsp/pink-noise/
b0 = 0.99886 * b0 + white * 0.0555179;
b1 = 0.99332 * b1 + white * 0.0750759;
b2 = 0.96900 * b2 + white * 0.1538520;
b3 = 0.86650 * b3 + white * 0.3104856;
b4 = 0.55000 * b4 + white * 0.5329522;
b5 = -0.7616 * b5 - white * 0.0168980;
pink = b0 + b1 + b2 + b3 + b4 + b5 + b6 + white * 0.5362;
b6 = white * 0.115926;
*/
}
dsp_node *dsp_pinknoise()
{
struct dsp_iir *pink = malloc(sizeof(*pink));
*pink = make_iir(6);
float pinka[7] = {0.0555179, 0.0750759, 0.1538520, 0.3104856, 0.5329522, -0.0168980, 0.115926};
float pinkb[7] = {0.99886, 0.99332, 0.969, 0.8665, 0.55, -0.7616, 0.115926};
memcpy(pink->a, pinka, 7*sizeof(float));
memcpy(pink->b, pinkb, 7*sizeof(float));
return make_node(pink, gen_pinknoise, iir_free);
}
void filter_rednoise(soundbyte *restrict last, soundbyte *out, int frames)
{
gen_whitenoise(NULL, out, frames);
for (int i = 0; i < frames*CHANNELS; i++) {
out[i] = (*last + (0.02*out[i]))/1.02;
*last = out[i];
out[i] *= 3.5;
}
}
dsp_node *dsp_rednoise()
{
soundbyte *last = malloc(sizeof(soundbyte));
*last = 0;
return make_node(last,filter_rednoise, NULL);
}
void filter_pitchshift(float *restrict octaves, soundbyte *buffer, int frames)
{
soundbyte ch1[frames];
for (int i = 0; i < frames; i++)
ch1[i] = buffer[i*CHANNELS];
smbPitchShift(*octaves, frames, 1024, 4, SAMPLERATE, ch1, buffer);
mono_expand(buffer, CHANNELS, frames);
}
dsp_node *dsp_pitchshift(float octaves)
{
float *oct = malloc(sizeof(float));
*oct = octaves;
return make_node(oct, filter_pitchshift, NULL);
}
soundbyte iir_filter(struct dsp_iir iir, soundbyte val)
{
iir.y[0] = 0.0;
iir.x[0] = val;
for (int i = 0; i < iir.n; i++)
iir.y[0] += iir.a[i] * iir.x[i];
for (int i = 1; i < iir.n; i++)
iir.y[0] -= iir.b[i] * iir.y[i];
/* Shift values in */
for (int i = iir.n-1; i > 0; i--) {
iir.x[i] = iir.x[i-1];
iir.y[i] = iir.y[i-1];
}
return iir.y[0];
}
void filter_iir(struct dsp_iir *iir, soundbyte *buffer, int frames)
{
for (int i = 0; i < frames; i++) {
soundbyte v = iir_filter(*iir, buffer[i*CHANNELS]);
for (int j = 0; j < CHANNELS; j++) buffer[i*CHANNELS+j] = v;
}
}
dsp_node *dsp_lpf(float freq)
{
struct dsp_iir *iir = malloc(sizeof(*iir));
*iir = bqlp_dcof(2*freq/SAMPLERATE, 5);
return make_node(iir, filter_iir, iir_free);
}
dsp_node *dsp_hpf(float freq)
{
struct dsp_iir *iir = malloc(sizeof(*iir));
*iir = bqhp_dcof(2*freq/SAMPLERATE,5);
return make_node(iir, filter_iir, iir_free);
}
void filter_delay(delay *d, soundbyte *buf, int frames)
{
for (int i = 0; i < frames*CHANNELS; i++) {
buf[i] += ringshift(d->ring)*d->decay;
ringpush(d->ring, buf[i]);
}
}
void delay_free(delay *d)
{
ringfree(d->ring);
free(d);
}
dsp_node *dsp_delay(double sec, double decay)
{
delay *d = malloc(sizeof(*d));
d->ms_delay = sec;
d->decay = decay;
d->ring = NULL;
d->ring = ringnew(d->ring, sec*CHANNELS*SAMPLERATE*2); /* Circular buffer size is enough to have the delay */
ringheader(d->ring)->write += CHANNELS*SAMPLERATE*sec;
return make_node(d, filter_delay, delay_free);
}
void filter_fwd_delay(delay *restrict d, soundbyte *restrict buf, int frames)
{
for (int i = 0; i < frames*CHANNELS; i++) {
ringpush(d->ring, buf[i]);
buf[i] += ringshift(d->ring)*d->decay;
}
}
dsp_node *dsp_fwd_delay(double sec, double decay)
{
delay *d = malloc(sizeof(*d));
d-> ms_delay = sec;
d->decay = decay;
d->ring = NULL;
d->ring = ringnew(d->ring, sec*CHANNELS*SAMPLERATE*2);
ringheader(d->ring)->write += CHANNELS*SAMPLERATE*sec;
return make_node(d, filter_fwd_delay, delay_free);
}
/* Get decay constant for a given pole */
/* Samples to decay 1 time constant is exp(-1/timeconstant) */
double tau2pole(double tau)
{
return exp(-1/(tau*SAMPLERATE));
}
void dsp_adsr_fillbuf(struct dsp_adsr *adsr, soundbyte *out, int n)
{
soundbyte val;
for (int i = 0; i < n; i++) {
if (adsr->time > adsr->rls) {
adsr->out = 0.f;
goto fin;
}
if (adsr->time > adsr->sus) {
// Release phase
adsr->out = adsr->rls_t * adsr->out;
goto fin;
}
if (adsr->time > adsr->dec) {
// Sustain phase
adsr->out = adsr->sus_pwr;
goto fin;
}
if (adsr->time > adsr->atk) {
// Decay phase
adsr->out = (1 - adsr->dec_t) * adsr->sus_pwr + adsr->dec_t * adsr->out;
goto fin;
}
// Attack phase
adsr->out = (1-adsr->atk_t) + adsr->atk_t * adsr->out;
fin:
val = SHRT_MAX * adsr->out;
out[i*CHANNELS] = out[i*CHANNELS+1] = val;
adsr->time += (double)(1000.f / SAMPLERATE);
}
}
dsp_node *dsp_adsr(unsigned int atk, unsigned int dec, unsigned int sus, unsigned int rls)
{
struct dsp_adsr *adsr = malloc(sizeof(*adsr));
adsr->atk = atk;
/* decay to 3 tau */
adsr->atk_t = tau2pole(atk / 3000.f);
adsr->dec = dec + adsr->atk;
adsr->dec_t = tau2pole(dec / 3000.f);
adsr->sus = sus + adsr->dec;
adsr->sus_pwr = 0.8f;
adsr->rls = rls + adsr->sus;
adsr->rls_t = tau2pole(rls / 3000.f);
return make_node(adsr, dsp_adsr_fillbuf, NULL);
}
void filter_noise_gate(float *restrict floor, soundbyte *restrict out, int frames)
{
for (int i = 0; i < frames*CHANNELS; i++) out[i] = fabsf(out[i]) < *floor ? 0.0 : out[i];
}
dsp_node *dsp_noise_gate(float floor)
{
float *v = malloc(sizeof(float));
*v = floor;
return make_node(v, filter_noise_gate, NULL);
}
void filter_limiter(float *restrict ceil, soundbyte *restrict out, int n)
{
for (int i = 0; i < n*CHANNELS; i++) out[i] = fabsf(out[i]) > *ceil ? *ceil : out[i];
}
dsp_node *dsp_limiter(float ceil)
{
float *v = malloc(sizeof(float));
*v = ceil;
return make_node(v, filter_limiter, NULL);
}
void dsp_compressor_fillbuf(struct dsp_compressor *comp, soundbyte *out, int n)
{
float val;
float db;
db = comp->target * (val - comp->threshold) / comp->ratio;
for (int i = 0; i < n; i++) {
val = float2db(out[i*CHANNELS]);
if (val < comp->threshold) {
comp->target = comp->rls_tau * comp->target;
val += db;
} else {
comp->target = (1 - comp->atk_tau) + comp->atk_tau * comp->target; // TODO: Bake in the 1 - atk_tau
val -= db;
}
// Apply same compression to both channels
out[i*CHANNELS] = out[i*CHANNELS+1] = db2float(val) * ( out[i*CHANNELS] > 0 ? 1 : -1);
}
}
dsp_node *dsp_compressor()
{
struct dsp_compressor new;
new.ratio = 4000;
new.atk = 50;
new.rls = 250;
new.target = 0.f;
new.threshold = -3.f;
new.atk_tau = tau2pole(new.atk / 3000.f);
new.rls_tau = tau2pole(new.rls / 3000.f);
struct dsp_compressor *c = malloc(sizeof(*c));
*c = new;
return make_node(c, dsp_compressor_fillbuf, NULL);
}
/* Assumes 2 channels in a frame */
void pan_frames(soundbyte *out, float deg, int frames)
{
if (deg == 0.f) return;
if (deg < -1) deg = -1.f;
else if (deg > 1) deg = 1.f;
float db1, db2;
if (deg > 0) {
db1 = pct2db(1 - deg);
db2 = pct2db(deg);
for (int i = 0; i < frames; i++) {
soundbyte L = out[i*2];
soundbyte R = out[i*2+1];
out[i*2] = fgain(L, db1);
out[i*2+1] = (R + fgain(L, db2))/2;
}
} else {
db1 = pct2db(1 + deg);
db2 = pct2db(-1*deg);
for (int i = 0; i < frames; i++) {
soundbyte L = out[i*2];
soundbyte R = out[i*2+1];
out[i*2+1] = fgain(R,db1);
out[i*2] = fgain(L, db1) + fgain(R, db2);
}
}
}
void dsp_mono(void *p, soundbyte *restrict out, int n)
{
for (int i = 0; i < n; i++) {
soundbyte val = (out[i*CHANNELS] + out[i*CHANNELS+1]) / 2;
for (int j = 0; j < CHANNELS; j++)
out[i*CHANNELS+j] = val;
}
}
#define ROUND(f) ((float)((f>0.0)?floor(f+0.5):ceil(f-0.5)))
void filter_bitcrush(struct bitcrush *restrict b, soundbyte *restrict out, int frames)
{
int max = pow(2,b->depth) - 1;
int step = SAMPLERATE/b->sr;
int i = 0;
while (i < frames) {
float left = ROUND((out[0]+1.0)*max)/(max-1.0);
float right = ROUND((out[1]+1.0)*max)/(max-1.0);
for (int j = 0; j < step && i < frames; j++) {
out[0] = left;
out[1] = right;
out += CHANNELS;
i++;
}
}
}
dsp_node *dsp_bitcrush(float sr, float res)
{
struct bitcrush *b = malloc(sizeof(*b));
b->sr = sr;
b->depth = res;
return make_node(b, filter_bitcrush, NULL);
}

View file

@ -1,126 +0,0 @@
#ifndef DSP_H
#define DSP_H
extern int SAMPLERATE;
extern int BUF_FRAMES;
extern int CHANNELS;
#include "sound.h"
#include "cbuf.h"
#include "script.h"
#include "iir.h"
/* a DSP node, when processed, sums its inputs, and stores the result of proc in its cache */
typedef struct dsp_node {
void (*proc)(void *dsp, soundbyte *buf, int samples); /* processor */
void *data; /* Node specific data to use in the proc function, passed in as dsp */
void (*data_free)(void *data);
soundbyte *cache;
struct dsp_node **ins; /* Array of in nodes */
struct dsp_node *out; /* node this one is connected to */
int pass; /* True if the filter should be bypassed */
int off; /* True if the filter shouldn't output */
float gain; /* Between 0 and 1, to attenuate this output */
float pan; /* Between -100 and +100, panning left to right in the speakers */
} dsp_node;
void dsp_init();
/* Get the output of a node */
soundbyte *dsp_node_out(dsp_node *node);
void dsp_node_run(dsp_node *node);
dsp_node *make_node(void *data, void (*proc)(void *data, soundbyte *out, int samples), void (*fr)(void *data));
void plugin_node(dsp_node *from, dsp_node *to);
void unplug_node(dsp_node *from);
void node_free(dsp_node *node);
void dsp_node_free(dsp_node *node);
void filter_iir(struct dsp_iir *iir, soundbyte *buffer, int frames);
void scale_soundbytes(soundbyte *a, float scale, int frames);
void sum_soundbytes(soundbyte *a, soundbyte *b, int frames);
void zero_soundbytes(soundbyte *a, int frames);
void set_soundbytes(soundbyte *a, soundbyte *b, int frames);
dsp_node *dsp_mixer_node();
dsp_node *dsp_am_mod(dsp_node *mod);
dsp_node *dsp_rectify();
extern dsp_node *masterbus;
dsp_node *dsp_hpf(float freq);
dsp_node *dsp_lpf(float freq);
/* atk, dec, sus, rls specify the time, in miliseconds, the phase begins */
typedef struct dsp_adsr {
unsigned int atk;
double atk_t;
unsigned int dec;
double dec_t;
unsigned int sus;
float sus_pwr; // Between 0 and 1
unsigned int rls;
double rls_t;
double time; /* Current time of the filter */
float out;
} adsr;
dsp_node *dsp_adsr(unsigned int atk, unsigned int dec, unsigned int sus, unsigned int rls);
typedef struct {
unsigned int ms_delay;
float decay; /* Each echo should be multiplied by this number */
soundbyte *ring;
} delay;
dsp_node *dsp_delay(double sec, double decay);
dsp_node *dsp_fwd_delay(double sec, double decay);
dsp_node *dsp_pitchshift(float octaves);
typedef struct dsp_compressor {
double ratio;
double threshold;
float target;
unsigned int atk; /* Milliseconds */
double atk_tau;
unsigned int rls; /* MIlliseconds */
double rls_tau;
} compressor;
dsp_node *dsp_compressor();
dsp_node *dsp_limiter(float ceil);
dsp_node *dsp_noise_gate(float floor);
struct phasor phasor_make(unsigned int sr, float freq);
dsp_node *dsp_whitenoise();
dsp_node *dsp_pinknoise();
dsp_node *dsp_rednoise();
float sin_phasor(float p);
float square_phasor(float p);
float saw_phasor(float p);
float tri_phasor(float p);
dsp_node *dsp_reverb();
dsp_node *dsp_sinewave(float amp, float freq);
dsp_node *dsp_square(float amp, float freq, int sr, int ch);
typedef struct {
float amp;
float freq;
float phase; /* from 0 to 1, marking where we are */
float (*filter)(float phase);
} phasor;
typedef struct bitcrush {
float sr;
float depth;
} bitcrush;
dsp_node *dsp_bitcrush(float sr, float res);
void dsp_mono(void *p, soundbyte *out, int n);
void pan_frames(soundbyte *out, float deg, int frames);
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,66 +0,0 @@
#ifndef IIR_H
#define IIR_H
#include "sound.h"
struct dsp_iir {
int n; // Amount of constants
float *a;
float *b;
float *x;
float *y;
};
struct dsp_iir make_iir(int order);
double *binomial_mult( int n, double *p );
double *trinomial_mult( int n, double *b, double *c );
double *dcof_bwlp( int n, double fcf );
double *dcof_bwhp( int n, double fcf );
double *dcof_bwbp( int n, double f1f, double f2f );
double *dcof_bwbs( int n, double f1f, double f2f );
int *ccof_bwlp( int n );
int *ccof_bwhp( int n );
int *ccof_bwbp( int n );
double *ccof_bwbs( int n, double f1f, double f2f );
double sf_bwlp( int n, double fcf );
double sf_bwhp( int n, double fcf );
double sf_bwbp( int n, double f1f, double f2f );
double sf_bwbs( int n, double f1f, double f2f );
float *fir_lp(int n, double fcf);
float *fir_hp(int n, double fcf);
float *fir_bpf(int n, double fcf1, double fcf2);
struct dsp_iir sp_lp(double fcf);
struct dsp_iir sp_hp(double fcf);
double chevy_pct_to_e(double pct);
struct dsp_iir make_iir(int order);
struct dsp_iir bqlp_dcof(double fcf, float Q);
struct dsp_iir bqhp_dcof(double fcf, float Q);
struct dsp_iir bqbpq_dcof(double fcf, float Q);
struct dsp_iir bqbp_dcof(double fcf, float Q);
struct dsp_iir bqnotch_dcof(double fcf, float Q);
struct dsp_iir bqapf_dcof(double fcf, float Q);
struct dsp_iir bqpeq_dcof(double fcf, float Q, float dbgain);
struct dsp_iir bqls_dcof(double fcf, float Q, float dbgain);
struct dsp_iir bqhs_dcof(double fcf, float Q, float dbgain);
struct dsp_iir p2_bwlp(double fcf);
struct dsp_iir p2_bwhp(double fcf);
struct dsp_iir p2_cdlp(double fcf);
struct dsp_iir p2_cdhp(double fcf);
struct dsp_iir p2_beslp(double fcf);
struct dsp_iir p2_beshp(double fcf);
struct dsp_iir che_lp(int order, double fcf, double e);
struct dsp_iir che_hp(int order, double fcf, double e);
struct dsp_iir che_bp(int order, double s, double fcf1, double fcf2, double e);
struct dsp_iir che_notch(int order, double s, double fcf1, double fcf2, double e);
#endif

View file

@ -1,98 +0,0 @@
#include "music.h"
#include "dsp.h"
#include "tsf.h"
#include "tml.h"
#include "sound.h"
#include "log.h"
#include "resources.h"
#include <stdlib.h>
#include "stb_ds.h"
#define TSF_BLOCK 32
static struct {
char *key;
tsf *value;
} *sf_hash = NULL;
void dsp_midi_fillbuf(struct dsp_midi_song *restrict song, void *restrict out, int n)
{
soundbyte *o = out;
tml_message *midi = song->midi;
for (int i = 0; i < n; i += TSF_BLOCK) {
while (midi && song->time >= midi->time) {
switch (midi->type) {
case TML_PROGRAM_CHANGE:
tsf_channel_set_presetnumber(song->sf, midi->channel, midi->program, (midi->channel == 9));
break;
case TML_NOTE_ON:
tsf_channel_note_on(song->sf, midi->channel, midi->key, midi->velocity / 127.f);
break;
case TML_NOTE_OFF:
tsf_channel_note_off(song->sf, midi->channel, midi->key);
break;
case TML_PITCH_BEND:
tsf_channel_set_pitchwheel(song->sf, midi->channel, midi->pitch_bend);
break;
case TML_CONTROL_CHANGE:
tsf_channel_midi_control(song->sf, midi->channel, midi->control, midi->control_value);
break;
}
midi = midi->next;
}
tsf_render_float(song->sf, o, TSF_BLOCK, 0);
o += TSF_BLOCK*CHANNELS;
song->time += TSF_BLOCK * (1000.f/SAMPLERATE);
}
song->midi = midi;
}
tsf *make_soundfont(const char *path)
{
int idx = shgeti(sf_hash, path);
if (idx != -1) return sf_hash[idx].value;
size_t rawlen;
void *raw = slurp_file(path, &rawlen);
tsf *sf = tsf_load_memory(raw,rawlen);
free(raw);
if (!sf) { YughWarn("Soundfont %s not found.", sf); return NULL; }
tsf_set_output(sf, TSF_STEREO_INTERLEAVED, SAMPLERATE, 0.f);
// Preset on 10th MIDI channel to use percussion sound bank if possible
tsf_channel_set_bank_preset(sf, 0, 128, 0);
shput(sf_hash, path, sf);
return sf;
}
void dsp_midi_free(struct dsp_midi_song *ms)
{
free(ms->midi);
tsf_close(ms->sf);
free(ms);
}
dsp_node *dsp_midi(const char *midi, tsf *sf)
{
size_t rawlen;
void *raw = slurp_file(midi, &rawlen);
struct dsp_midi_song *ms = malloc(sizeof(*ms));
ms->time = 0.0;
ms->midi = tml_load_memory(raw, rawlen);
ms->sf = tsf_copy(sf);
return make_node(ms, dsp_midi_fillbuf, dsp_midi_free);
}
void play_song(const char *midi, const char *sf)
{
plugin_node(dsp_midi(midi, make_soundfont(sf)), masterbus);
}

View file

@ -1,20 +0,0 @@
#ifndef MUSIC_H
#define MUSIC_H
#include "tsf.h"
#include "tml.h"
#include "dsp.h"
typedef struct dsp_midi_song {
float bpm;
double time;
tml_message *midi;
tsf *sf;
} midi;
dsp_node *dsp_midi(const char *midi, tsf *sf);
tsf *make_soundfont(const char *sf);
void play_song(const char *midi, const char *sf);
void dsp_midi_fillbuf(struct dsp_midi_song *song, void *out, int n);
#endif

View file

@ -1,316 +0,0 @@
/****************************************************************************
*
* NAME: smbPitchShift.c
* VERSION: 1.2
* HOME URL: http://blogs.zynaptiq.com/bernsee
* KNOWN BUGS: none
*
* SYNOPSIS: Routine for doing pitch shifting while maintaining
* duration using the Short Time Fourier Transform.
*
* DESCRIPTION: The routine takes a pitchShift factor value which is between 0.5
* (one octave down) and 2. (one octave up). A value of exactly 1 does not change
* the pitch. numSampsToProcess tells the routine how many samples in indata[0...
* numSampsToProcess-1] should be pitch shifted and moved to outdata[0 ...
* numSampsToProcess-1]. The two buffers can be identical (ie. it can process the
* data in-place). fftFrameSize defines the FFT frame size used for the
* processing. Typical values are 1024, 2048 and 4096. It may be any value <=
* MAX_FRAME_LENGTH but it MUST be a power of 2. osamp is the STFT
* oversampling factor which also determines the overlap between adjacent STFT
* frames. It should at least be 4 for moderate scaling ratios. A value of 32 is
* recommended for best quality. sampleRate takes the sample rate for the signal
* in unit Hz, ie. 44100 for 44.1 kHz audio. The data passed to the routine in
* indata[] should be in the range [-1.0, 1.0), which is also the output range
* for the data, make sure you scale the data accordingly (for 16bit signed integers
* you would have to divide (and multiply) by 32768).
*
* COPYRIGHT 1999-2015 Stephan M. Bernsee <s.bernsee [AT] zynaptiq [DOT] com>
*
* The Wide Open License (WOL)
*
* Permission to use, copy, modify, distribute and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice and this license appear in all source copies.
* THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY OF
* ANY KIND. See http://www.dspguru.com/wol.htm for more information.
*
*****************************************************************************/
#include <string.h>
#include <math.h>
#include <stdio.h>
#define MAX_FRAME_LENGTH 8192
#define false 0
#define true 1
void smbFft(float *fftBuffer, long fftFrameSize, long sign);
double smbAtan2(double x, double y);
// -----------------------------------------------------------------------------------------------------------------
void smbPitchShift(float pitchShift, long numSampsToProcess, long fftFrameSize, long osamp, float sampleRate, float *indata, float *outdata)
/*
Routine smbPitchShift(). See top of file for explanation
Purpose: doing pitch shifting while maintaining duration using the Short
Time Fourier Transform.
Author: (c)1999-2015 Stephan M. Bernsee <s.bernsee [AT] zynaptiq [DOT] com>
*/
{
static float gInFIFO[MAX_FRAME_LENGTH];
static float gOutFIFO[MAX_FRAME_LENGTH];
static float gFFTworksp[2*MAX_FRAME_LENGTH];
static float gLastPhase[MAX_FRAME_LENGTH/2+1];
static float gSumPhase[MAX_FRAME_LENGTH/2+1];
static float gOutputAccum[2*MAX_FRAME_LENGTH];
static float gAnaFreq[MAX_FRAME_LENGTH];
static float gAnaMagn[MAX_FRAME_LENGTH];
static float gSynFreq[MAX_FRAME_LENGTH];
static float gSynMagn[MAX_FRAME_LENGTH];
static long gRover = false, gInit = false;
double magn, phase, tmp, window, real, imag;
double freqPerBin, expct;
long i,k, qpd, index, inFifoLatency, stepSize, fftFrameSize2;
/* set up some handy variables */
fftFrameSize2 = fftFrameSize/2;
stepSize = fftFrameSize/osamp;
freqPerBin = sampleRate/(double)fftFrameSize;
expct = 2.*M_PI*(double)stepSize/(double)fftFrameSize;
inFifoLatency = fftFrameSize-stepSize;
if (gRover == false) gRover = inFifoLatency;
/* initialize our static arrays */
if (gInit == false) {
memset(gInFIFO, 0, MAX_FRAME_LENGTH*sizeof(float));
memset(gOutFIFO, 0, MAX_FRAME_LENGTH*sizeof(float));
memset(gFFTworksp, 0, 2*MAX_FRAME_LENGTH*sizeof(float));
memset(gLastPhase, 0, (MAX_FRAME_LENGTH/2+1)*sizeof(float));
memset(gSumPhase, 0, (MAX_FRAME_LENGTH/2+1)*sizeof(float));
memset(gOutputAccum, 0, 2*MAX_FRAME_LENGTH*sizeof(float));
memset(gAnaFreq, 0, MAX_FRAME_LENGTH*sizeof(float));
memset(gAnaMagn, 0, MAX_FRAME_LENGTH*sizeof(float));
gInit = true;
}
/* main processing loop */
for (i = 0; i < numSampsToProcess; i++){
/* As long as we have not yet collected enough data just read in */
gInFIFO[gRover] = indata[i];
outdata[i] = gOutFIFO[gRover-inFifoLatency];
gRover++;
/* now we have enough data for processing */
if (gRover >= fftFrameSize) {
gRover = inFifoLatency;
/* do windowing and re,im interleave */
for (k = 0; k < fftFrameSize;k++) {
window = -.5*cos(2.*M_PI*(double)k/(double)fftFrameSize)+.5;
gFFTworksp[2*k] = gInFIFO[k] * window;
gFFTworksp[2*k+1] = 0.;
}
/* ***************** ANALYSIS ******************* */
/* do transform */
smbFft(gFFTworksp, fftFrameSize, -1);
/* this is the analysis step */
for (k = 0; k <= fftFrameSize2; k++) {
/* de-interlace FFT buffer */
real = gFFTworksp[2*k];
imag = gFFTworksp[2*k+1];
/* compute magnitude and phase */
magn = 2.*sqrt(real*real + imag*imag);
phase = atan2(imag,real);
/* compute phase difference */
tmp = phase - gLastPhase[k];
gLastPhase[k] = phase;
/* subtract expected phase difference */
tmp -= (double)k*expct;
/* map delta phase into +/- Pi interval */
qpd = tmp/M_PI;
if (qpd >= 0) qpd += qpd&1;
else qpd -= qpd&1;
tmp -= M_PI*(double)qpd;
/* get deviation from bin frequency from the +/- Pi interval */
tmp = osamp*tmp/(2.*M_PI);
/* compute the k-th partials' true frequency */
tmp = (double)k*freqPerBin + tmp*freqPerBin;
/* store magnitude and true frequency in analysis arrays */
gAnaMagn[k] = magn;
gAnaFreq[k] = tmp;
}
/* ***************** PROCESSING ******************* */
/* this does the actual pitch shifting */
memset(gSynMagn, 0, fftFrameSize*sizeof(float));
memset(gSynFreq, 0, fftFrameSize*sizeof(float));
for (k = 0; k <= fftFrameSize2; k++) {
index = k*pitchShift;
if (index <= fftFrameSize2) {
gSynMagn[index] += gAnaMagn[k];
gSynFreq[index] = gAnaFreq[k] * pitchShift;
}
}
/* ***************** SYNTHESIS ******************* */
/* this is the synthesis step */
for (k = 0; k <= fftFrameSize2; k++) {
/* get magnitude and true frequency from synthesis arrays */
magn = gSynMagn[k];
tmp = gSynFreq[k];
/* subtract bin mid frequency */
tmp -= (double)k*freqPerBin;
/* get bin deviation from freq deviation */
tmp /= freqPerBin;
/* take osamp into account */
tmp = 2.*M_PI*tmp/osamp;
/* add the overlap phase advance back in */
tmp += (double)k*expct;
/* accumulate delta phase to get bin phase */
gSumPhase[k] += tmp;
phase = gSumPhase[k];
/* get real and imag part and re-interleave */
gFFTworksp[2*k] = magn*cos(phase);
gFFTworksp[2*k+1] = magn*sin(phase);
}
/* zero negative frequencies */
for (k = fftFrameSize+2; k < 2*fftFrameSize; k++) gFFTworksp[k] = 0.;
/* do inverse transform */
smbFft(gFFTworksp, fftFrameSize, 1);
/* do windowing and add to output accumulator */
for(k=0; k < fftFrameSize; k++) {
window = -.5*cos(2.*M_PI*(double)k/(double)fftFrameSize)+.5;
gOutputAccum[k] += 2.*window*gFFTworksp[2*k]/(fftFrameSize2*osamp);
}
for (k = 0; k < stepSize; k++) gOutFIFO[k] = gOutputAccum[k];
/* shift accumulator */
memmove(gOutputAccum, gOutputAccum+stepSize, fftFrameSize*sizeof(float));
/* move input FIFO */
for (k = 0; k < inFifoLatency; k++) gInFIFO[k] = gInFIFO[k+stepSize];
}
}
}
// -----------------------------------------------------------------------------------------------------------------
void smbFft(float *fftBuffer, long fftFrameSize, long sign)
/*
FFT routine, (C)1996 S.M.Bernsee. Sign = -1 is FFT, 1 is iFFT (inverse)
Fills fftBuffer[0...2*fftFrameSize-1] with the Fourier transform of the
time domain data in fftBuffer[0...2*fftFrameSize-1]. The FFT array takes
and returns the cosine and sine parts in an interleaved manner, ie.
fftBuffer[0] = cosPart[0], fftBuffer[1] = sinPart[0], asf. fftFrameSize
must be a power of 2. It expects a complex input signal (see footnote 2),
ie. when working with 'common' audio signals our input signal has to be
passed as {in[0],0.,in[1],0.,in[2],0.,...} asf. In that case, the transform
of the frequencies of interest is in fftBuffer[0...fftFrameSize].
*/
{
float wr, wi, arg, *p1, *p2, temp;
float tr, ti, ur, ui, *p1r, *p1i, *p2r, *p2i;
long i, bitm, j, le, le2, k;
for (i = 2; i < 2*fftFrameSize-2; i += 2) {
for (bitm = 2, j = 0; bitm < 2*fftFrameSize; bitm <<= 1) {
if (i & bitm) j++;
j <<= 1;
}
if (i < j) {
p1 = fftBuffer+i; p2 = fftBuffer+j;
temp = *p1; *(p1++) = *p2;
*(p2++) = temp; temp = *p1;
*p1 = *p2; *p2 = temp;
}
}
for (k = 0, le = 2; k < (long)(log(fftFrameSize)/log(2.)+.5); k++) {
le <<= 1;
le2 = le>>1;
ur = 1.0;
ui = 0.0;
arg = M_PI / (le2>>1);
wr = cos(arg);
wi = sign*sin(arg);
for (j = 0; j < le2; j += 2) {
p1r = fftBuffer+j; p1i = p1r+1;
p2r = p1r+le2; p2i = p2r+1;
for (i = j; i < 2*fftFrameSize; i += le) {
tr = *p2r * ur - *p2i * ui;
ti = *p2r * ui + *p2i * ur;
*p2r = *p1r - tr; *p2i = *p1i - ti;
*p1r += tr; *p1i += ti;
p1r += le; p1i += le;
p2r += le; p2i += le;
}
tr = ur*wr - ui*wi;
ui = ur*wi + ui*wr;
ur = tr;
}
}
}
// -----------------------------------------------------------------------------------------------------------------
/*
12/12/02, smb
PLEASE NOTE:
There have been some reports on domain errors when the atan2() function was used
as in the above code. Usually, a domain error should not interrupt the program flow
(maybe except in Debug mode) but rather be handled "silently" and a global variable
should be set according to this error. However, on some occasions people ran into
this kind of scenario, so a replacement atan2() function is provided here.
If you are experiencing domain errors and your program stops, simply replace all
instances of atan2() with calls to the smbAtan2() function below.
*/
double smbAtan2(double x, double y)
{
double signx;
if (x > 0.) signx = 1.;
else signx = -1.;
if (x == 0.) return 0.;
if (y == 0.) return signx * M_PI / 2.;
return atan2(x, y);
}
// -----------------------------------------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------------------------------------

View file

@ -1,327 +0,0 @@
#include "sound.h"
#include "limits.h"
#include "log.h"
#include "math.h"
#include "music.h"
#include "resources.h"
#include "string.h"
#include "time.h"
#include <stdlib.h>
#include "pthread.h"
#include "jsffi.h"
pthread_mutex_t soundrun = PTHREAD_MUTEX_INITIALIZER;
#include "stb_ds.h"
#include "dsp.h"
#define POCKETMOD_IMPLEMENTATION
#include "pocketmod.h"
#include "sokol/sokol_audio.h"
#define TSF_NO_STDIO
#define TSF_IMPLEMENTATION
#include "tsf.h"
#define TML_NO_STDIO
#define TML_IMPLEMENTATION
#include "tml.h"
#define DR_WAV_IMPLEMENTATION
#include "dr_wav.h"
#ifndef NFLAC
#define DR_FLAC_IMPLEMENTATION
#define DR_FLAC_NO_STDIO
#include "dr_flac.h"
#endif
#ifndef NMP3
#define DR_MP3_NO_STDIO
#define DR_MP3_IMPLEMENTATION
#include "dr_mp3.h"
#endif
#ifndef NQOA
#define QOA_IMPLEMENTATION
#include "qoa.h"
#endif
void short_to_float_array(const short *in, float *out, int frames, int channels)
{
for (int i = 0; i < frames * channels; i++)
out[i] = (float)in[i] / 32768.0f;
}
void float_to_short_array(float *in, short *out, int frames, int channels)
{
for (int i = 0; i < frames*channels; i++)
out[i] = (float)in[i]*32768;
}
void change_channels(struct pcm *w, int ch) {
if (w->ch == ch) return;
soundbyte *data = w->data;
int samples = ch * w->frames;
soundbyte *new = malloc(sizeof(soundbyte) * samples);
if (ch > w->ch) {
/* Sets all new channels equal to the first one */
for (int i = 0; i < w->frames; i++) {
for (int j = 0; j < ch; j++)
new[i * ch + j] = data[i];
}
} else {
/* Simple method; just use first N channels present in wav */
for (int i = 0; i < w->frames; i++)
for (int j = 0; j < ch; j++)
new[i * ch + j] = data[i * ch + j];
}
free(w->data);
w->data = new;
}
void resample_pcm(soundbyte *in, soundbyte *out, int in_frames, int out_frames, int channels)
{
float ratio = (float)in_frames / out_frames;
for (int i = 0; i < out_frames; i++) {
// Find the position in the input buffer.
float in_pos = i * ratio;
int in_index = (int)in_pos; // Get the integer part of the position.
float frac = in_pos - in_index; // Get the fractional part for interpolation.
for (int ch = 0; ch < channels; ch++) {
// Linear interpolation between two input samples.
soundbyte sample1 = in[in_index * channels + ch];
soundbyte sample2 = in[(in_index + 1) * channels + ch];
out[i * channels + ch] = (soundbyte)((1.0f - frac) * sample1 + frac * sample2);
}
}
}
void change_samplerate(struct pcm *w, int rate) {
if (rate == w->samplerate) return;
float ratio = (float)rate / w->samplerate;
int outframes = w->frames * ratio;
soundbyte *resampled = malloc(w->ch*outframes*sizeof(soundbyte));
resample_pcm(w->data, resampled, w->frames, outframes, w->ch);
free(w->data);
w->data = resampled;
w->frames = outframes;
w->samplerate = rate;
}
void push_sound(soundbyte *buffer, int frames, int chan) {
pthread_mutex_lock(&soundrun);
set_soundbytes(buffer, dsp_node_out(masterbus), frames*chan);
pthread_mutex_unlock(&soundrun);
}
void filter_mod(pocketmod_context *mod, soundbyte *buffer, int frames)
{
pocketmod_render(mod, buffer, frames*CHANNELS*sizeof(soundbyte));
}
dsp_node *dsp_mod(const char *path)
{
size_t modsize;
void *data = slurp_file(path, &modsize);
pocketmod_context *mod = malloc(sizeof(*mod));
pocketmod_init(mod, data, modsize, SAMPLERATE);
return make_node(mod, filter_mod, NULL);
}
void sound_init() {
dsp_init();
saudio_setup(&(saudio_desc){
.stream_cb = push_sound,
.sample_rate = SAMPLERATE,
.num_channels = CHANNELS,
.buffer_frames = BUF_FRAMES,
.logger.func = sg_logging,
});
SAMPLERATE = saudio_sample_rate();
CHANNELS = saudio_channels();
BUF_FRAMES = saudio_buffer_frames();
}
struct pcm *make_pcm(const char *wav) {
if (!wav) return NULL;
char *ext = strrchr(wav, '.')+1;
if(!ext) {
YughWarn("No extension detected for %s.", wav);
return NULL;
}
size_t rawlen;
void *raw = slurp_file(wav, &rawlen);
if (!raw) {
YughWarn("Could not find file %s.", wav);
return NULL;
}
struct pcm *mwav = malloc(sizeof(*mwav));
if (!strcmp(ext, "wav"))
mwav->data = drwav_open_memory_and_read_pcm_frames_f32(raw, rawlen, &mwav->ch, &mwav->samplerate, &mwav->frames, NULL);
else if (!strcmp(ext, "flac")) {
#ifndef NFLAC
mwav->data = drflac_open_memory_and_read_pcm_frames_f32(raw, rawlen, &mwav->ch, &mwav->samplerate, &mwav->frames, NULL);
#else
YughWarn("Could not load %s because Primum was built without FLAC support.", wav);
#endif
}
else if (!strcmp(ext, "mp3")) {
#ifndef NMP3
drmp3_config cnf;
mwav->data = drmp3_open_memory_and_read_pcm_frames_f32(raw, rawlen, &cnf, &mwav->frames, NULL);
mwav->ch = cnf.channels;
mwav->samplerate = cnf.sampleRate;
#else
YughWarn("Could not load %s because Primum was built without MP3 support.", wav);
#endif
}
else if (!strcmp(ext, "qoa")) {
#ifndef NQOA
qoa_desc qoa;
short *qoa_data = qoa_decode(raw, rawlen, &qoa);
mwav->ch = qoa.channels;
mwav->samplerate = qoa.samplerate;
mwav->frames = qoa.samples/mwav->ch;
mwav->data = malloc(sizeof(soundbyte) * mwav->frames * mwav->ch);
short_to_float_array(qoa_data, mwav->data, mwav->frames,mwav->ch);
free(qoa_data);
#else
YughWarn("Could not load %s because Primum was built without QOA support.", wav);
#endif
} else {
YughWarn("File with unknown type '%s'.", wav);
free (raw);
free(mwav);
return NULL;
}
free(raw);
return mwav;
}
void pcm_format(pcm *pcm, int samplerate, int channels)
{
change_samplerate(pcm, samplerate);
change_channels(pcm, channels);
}
void save_qoa(char *file, pcm *pcm)
{
qoa_desc q;
short *out = malloc(sizeof(short)*pcm->ch*pcm->frames);
float_to_short_array(pcm->data, out, pcm->frames, pcm->ch);
q.channels = pcm->ch;
q.samples = pcm->frames;
q.samplerate = pcm->samplerate;
int encoded = qoa_write(file, out, &q);
free(out);
}
void save_wav(char *file, pcm *pcm)
{
drwav wav;
drwav_data_format fmt = {0};
fmt.format = DR_WAVE_FORMAT_PCM;
fmt.channels = pcm->ch;
fmt.sampleRate = pcm->samplerate;
fmt.bitsPerSample = 32;
drwav_int32 *out = malloc(sizeof(*out)*pcm->ch*pcm->frames);
drwav_f32_to_s32(out, pcm->data, pcm->frames*pcm->ch);
drwav_init_file_write_sequential_pcm_frames(&wav, file, &fmt, pcm->frames, NULL);
drwav_write_pcm_frames(&wav, pcm->frames, out);
drwav_uninit(&wav);
free(out);
}
void pcm_free(pcm *pcm)
{
free(pcm->data);
free(pcm);
}
void sound_fillbuf(struct sound *s, soundbyte *buf, int n) {
int frames = s->data->frames - s->frame;
if (frames == 0) return;
int end = 0;
if (frames > n)
frames = n;
else
end = 1;
soundbyte *in = s->data->data;
for (int i = 0; i < frames; i++) {
for (int j = 0; j < CHANNELS; j++)
buf[i * CHANNELS + j] = in[s->frame*CHANNELS + j];
s->frame++;
}
if(end) {
if (s->loop)
s->frame = 0;
}
}
void free_source(struct sound *s)
{
free(s);
}
struct dsp_node *dsp_source(pcm *pcm)
{
if (!pcm) return NULL;
struct sound *self = malloc(sizeof(*self));
self->frame = 0;
self->data = pcm;
self->loop = false;
dsp_node *n = make_node(self, sound_fillbuf, free_source);
return n;
}
int sound_finished(const struct sound *s) {
return s->frame == s->data->frames;
}
float short2db(short val) {
return 20 * log10(abs(val) / SHRT_MAX);
}
short db2short(float db) {
return pow(10, db / 20.f) * SHRT_MAX;
}
short short_gain(short val, float db) {
return (short)(pow(10, db / 20.f) * val);
}
float float2db(float val) { return 20 * log10(fabsf(val)); }
float db2float(float db) { return pow(10, db/20); }
float fgain(float val, float db) {
return pow(10,db/20.f)*val;
}
float pct2db(float pct) {
if (pct <= 0) return -72.f;
return 10 * log2(pct);
}
float pct2mult(float pct) {
if (pct <= 0) return 0.f;
return pow(10, 0.5 * log2(pct));
}

View file

@ -1,54 +0,0 @@
#ifndef SOUND_H
#define SOUND_H
#include "script.h"
#include "pthread.h"
typedef float soundbyte;
extern pthread_mutex_t soundrun;
struct dsp_node;
/* A bookmark into a wav, actually playing the sound */
typedef struct sound {
unsigned int frame; /* Pointing to the current frame on the wav */
struct pcm *data;
int loop;
} sound;
/* Represents a sound file source, fulled loaded*/
typedef struct pcm {
unsigned int ch;
unsigned int samplerate;
unsigned long long frames;
soundbyte *data;
} pcm;
void sound_init();
void audio_open(const char *device);
void audio_close();
struct pcm *make_pcm(const char *file);
void pcm_free(pcm *pcm);
void pcm_norm_gain(struct pcm *w, double lv);
void pcm_format(pcm *pcm, int samplerate, int channels);
struct dsp_node *dsp_source(pcm *pcm);
struct dsp_node *dsp_mod(const char *path);
int sound_finished(const struct sound *s);
float short2db(short val);
short db2short(float db);
short short_gain(short val, float db);
float fgain(float val, float db);
float float2db(float val);
float db2float(float db);
float pct2db(float pct);
float pct2mult(float pct);
void save_qoa(char *file, pcm *pcm);
void save_wav(char *file, pcm *pcm);
#endif

View file

@ -1,103 +0,0 @@
#include "steam.h"
#ifndef NSTEAM
#include <steam/steam_api.h>
#include <steam/steam_api_flat.h>
#include "jsffi.h"
ISteamUserStats *stats = NULL;
ISteamApps *app = NULL;
ISteamRemoteStorage *remote = NULL;
ISteamUGC *ugc = NULL;
static JSValue js_steam_init(JSContext *js, JSValue this_v, int argc, JSValue *argv)
{
SteamErrMsg err;
SteamAPI_InitEx(&err);
JSValue str = str2js(err);
stats = SteamAPI_SteamUserStats();
app = SteamAPI_SteamApps();
remote = SteamAPI_SteamRemoteStorage();
ugc = SteamAPI_SteamUGC();
return str;
}
static const JSCFunctionListEntry js_steam_funcs[] = {
MIST_FUNC_DEF(steam, init, 1),
};
JSC_SCALL(achievement_get32,
int32 data;
SteamAPI_ISteamUserStats_GetStatInt32(stats, str, &data);
return number2js(data);
)
JSC_SCALL(achievement_get,
bool data;
SteamAPI_ISteamUserStats_GetAchievement(stats, str, &data);
return boolean2js(data);
)
JSC_SCALL(achievement_set,
return boolean2js(SteamAPI_ISteamUserStats_SetAchievement(stats, str));
)
JSC_CCALL(achievement_num,
return number2js(SteamAPI_ISteamUserStats_GetNumAchievements(stats));
)
JSC_SCALL(achievement_clear, SteamAPI_ISteamUserStats_ClearAchievement(stats, str))
JSC_SCALL(achievement_user_get,
bool a;
boolean2js(SteamAPI_ISteamUserStats_GetUserAchievement(stats, js2uint64(argv[1]), str, &a));
ret = boolean2js(a);
)
static const JSCFunctionListEntry js_achievement_funcs[] = {
MIST_FUNC_DEF(achievement, clear, 1),
MIST_FUNC_DEF(achievement, get32, 2),
MIST_FUNC_DEF(achievement, get, 2),
MIST_FUNC_DEF(achievement, set, 1),
MIST_FUNC_DEF(achievement, num, 0),
MIST_FUNC_DEF(achievement, user_get, 2),
};
JSC_CCALL(cloud_app_enabled,
return boolean2js(SteamAPI_ISteamRemoteStorage_IsCloudEnabledForApp(remote))
)
JSC_CCALL(cloud_enable, SteamAPI_ISteamRemoteStorage_SetCloudEnabledForApp(remote, js2boolean(self)))
JSC_CCALL(cloud_account_enabled, return boolean2js(SteamAPI_ISteamRemoteStorage_IsCloudEnabledForAccount(remote)))
static const JSCFunctionListEntry js_cloud_funcs[] = {
MIST_FUNC_DEF(cloud, app_enabled, 0),
MIST_FUNC_DEF(cloud, account_enabled, 0),
MIST_FUNC_DEF(cloud, enable, 1)
};
JSC_CCALL(app_owner,
uint64_t own = SteamAPI_ISteamApps_GetAppOwner(app);
return JS_NewBigUint64(js, own);
)
static const JSCFunctionListEntry js_app_funcs[] = {
MIST_FUNC_DEF(app, owner, 0),
};
JSValue js_init_steam(JSContext *js)
{
JSValue steam = JS_NewObject(js);
JS_SetPropertyFunctionList(js, steam, js_steam_funcs, countof(js_steam_funcs));
JSValue achievements = JS_NewObject(js);
JS_SetPropertyFunctionList(js, achievements, js_achievement_funcs, countof(js_achievement_funcs));
js_setpropstr(steam, "achievements", achievements);
JSValue app = JS_NewObject(js);
JS_SetPropertyFunctionList(js, app, js_app_funcs, countof(js_app_funcs));
js_setpropstr(steam, "app", app);
JSValue cloud = JS_NewObject(js);
JS_SetPropertyFunctionList(js, cloud, js_cloud_funcs, countof(js_cloud_funcs));
js_setpropstr(steam, "cloud", cloud);
return steam;
}
#endif

View file

@ -1,15 +0,0 @@
#ifndef QJS_STEAM_H
#define QJS_STEAM_H
#include "jsffi.h"
#ifdef __cplusplus
extern "C" {
#endif
JSValue js_init_steam(JSContext *js);
#ifdef __cplusplus
}
#endif
#endif

View file

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

View file

@ -1,300 +0,0 @@
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

@ -1,119 +0,0 @@
<!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

@ -1,22 +0,0 @@
<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

@ -1,38 +0,0 @@
<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

@ -1,23 +0,0 @@
<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

@ -1,18 +0,0 @@
<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

@ -1,64 +0,0 @@
<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

@ -1,17 +0,0 @@
<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

@ -1,14 +0,0 @@
<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

@ -1,15 +0,0 @@
<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

@ -1,25 +0,0 @@
<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

@ -1,25 +0,0 @@
<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

@ -1,12 +0,0 @@
<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.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

View file

@ -1,72 +0,0 @@
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

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

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

View file

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

@ -1,450 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// All of the struct definitions for Chipmunk should be considered part of the private API.
// However, it is very valuable to know the struct sizes for preallocating memory.
#ifndef CHIPMUNK_STRUCTS_H
#define CHIPMUNK_STRUCTS_H
#include "chipmunk/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

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,70 +0,0 @@
// Copyright 2013 Howling Moon Software. All rights reserved.
// See http://chipmunk2d.net/legal.php for more information.
// Polylines are just arrays of vertexes.
// They are looped if the first vertex is equal to the last.
// cpPolyline structs are intended to be passed by value and destroyed when you are done with them.
typedef struct cpPolyline {
int count, capacity;
cpVect verts[];
} cpPolyline;
/// Destroy and free a polyline instance.
CP_EXPORT void cpPolylineFree(cpPolyline *line);
/// Returns true if the first vertex is equal to the last.
CP_EXPORT cpBool cpPolylineIsClosed(cpPolyline *line);
/**
Returns a copy of a polyline simplified by using the Douglas-Peucker algorithm.
This works very well on smooth or gently curved shapes, but not well on straight edged or angular shapes.
*/
CP_EXPORT cpPolyline *cpPolylineSimplifyCurves(cpPolyline *line, cpFloat tol);
/**
Returns a copy of a polyline simplified by discarding "flat" vertexes.
This works well on 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

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

View file

@ -1,11 +0,0 @@
#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

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

View file

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

View file

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

View file

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

View file

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

View file

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

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