add quadtree; add neon to handmademath

This commit is contained in:
2025-01-14 09:36:09 -06:00
parent 04273b3d43
commit d8cad40c50
12 changed files with 670 additions and 75 deletions

View File

@@ -89,7 +89,7 @@ if get_option('enet')
endif
sources = []
src += ['anim.c', 'config.c', 'datastream.c','font.c','gameobject.c','HandmadeMath.c','jsffi.c','model.c','render.c','script.c','simplex.c','spline.c', 'timer.c', 'transform.c','warp.c','yugine.c', 'wildmatch.c', 'sprite.c']
src += ['anim.c', 'config.c', 'datastream.c','font.c','gameobject.c','HandmadeMath.c','jsffi.c','model.c','render.c','script.c','simplex.c','spline.c', 'timer.c', 'transform.c','warp.c','yugine.c', 'wildmatch.c', 'sprite.c', 'quadtree.c', 'aabb.c']
imsrc = ['GraphEditor.cpp','ImCurveEdit.cpp','ImGradient.cpp','imgui_draw.cpp','imgui_tables.cpp','imgui_widgets.cpp','imgui.cpp','ImGuizmo.cpp','imnodes.cpp','implot_items.cpp','implot.cpp', 'imgui_impl_sdlrenderer3.cpp', 'imgui_impl_sdl3.cpp', 'imgui_impl_sdlgpu3.cpp']

View File

@@ -21,6 +21,9 @@ frog = {
}
*/
var spritetree = os.make_quadtree([0,0], [10000,10000]);
globalThis.spritetree = spritetree;
var sprite = {
image: undefined,
get diffuse() { return this.image; },
@@ -92,6 +95,8 @@ var sprite = {
this.sync();
this.play();
this.transform.scale = [this.image.texture.width, this.image.texture.height];
// spritetree.remove(this);
},
stop() {
this.del_anim?.();

View File

@@ -756,9 +756,14 @@ function insertion_sort(arr, cmp)
return arr
}
var culled;
function sprites_to_queue(sprites, ysort = false)
{
return render._main.make_sprite_queue(allsprites, prosperon.camera, sprite_pipeline)
//var culled = spritetree.find(prosperon.camera.pos, prosperon.camera.size);
//var culled = spritetree.find(prosperon.camera.transform.pos,prosperon.camera.size)
var culled = os.cull_sprites(allsprites,prosperon.camera);
return render._main.make_sprite_queue(culled, prosperon.camera, sprite_pipeline)
var sprites = allsprites;
// for (var i = 0; i < sprites.length; i++)
// sprites[i].transform.clean();

View File

@@ -216,9 +216,11 @@ HMM_Vec4 HMM_AddV4(HMM_Vec4 Left, HMM_Vec4 Right) {
HMM_Vec4 Result;
#ifdef HANDMADE_MATH__USE_SSE
Result.SSE = _mm_add_ps(Left.SSE, Right.SSE);
#else
#ifdef HANDMADE_MATH__USE_NEON
Result.NEON = vaddq_f32(Left.NEON, Right.NEON);
#elif defined(HANDMADE_MATH__USE_SSE)
Result.SSE = _mm_add_ps(Left.SSE, Right.SSE);
#else
Result.X = Left.X + Right.X;
Result.Y = Left.Y + Right.Y;
Result.Z = Left.Z + Right.Z;
@@ -310,9 +312,11 @@ HMM_Vec4 HMM_MulV4(HMM_Vec4 Left, HMM_Vec4 Right) {
HMM_Vec4 Result;
#ifdef HANDMADE_MATH__USE_SSE
Result.SSE = _mm_mul_ps(Left.SSE, Right.SSE);
#else
#ifdef HANDMADE_MATH__USE_NEON
Result.NEON = vmulq_f32(Left.NEON, Right.NEON);
#elif defined(HANDMADE_MATH__USE_SSE)
Result.SSE = _mm_mul_ps(Left.SSE, Right.SSE);
#else
Result.X = Left.X * Right.X;
Result.Y = Left.Y * Right.Y;
Result.Z = Left.Z * Right.Z;
@@ -555,39 +559,63 @@ HMM_Vec4 HMM_LerpV4(HMM_Vec4 A, float Time, HMM_Vec4 B) {
/*
* SSE stuff
*/
HMM_Vec4 HMM_LinearCombineV4M4_P(HMM_Vec4 *Left, HMM_Mat4 *Right)
{
HMM_Vec4 Result;
#ifdef HANDMADE_MATH__USE_NEON
float32x4_t a = Left->NEON;
float x = vgetq_lane_f32(a, 0);
float y = vgetq_lane_f32(a, 1);
float z = vgetq_lane_f32(a, 2);
float w = vgetq_lane_f32(a, 3);
float32x4_t r0 = vmulq_n_f32(Right->Columns[0].NEON, x);
float32x4_t r1 = vmulq_n_f32(Right->Columns[1].NEON, y);
float32x4_t r2 = vmulq_n_f32(Right->Columns[2].NEON, z);
float32x4_t r3 = vmulq_n_f32(Right->Columns[3].NEON, w);
float32x4_t sum = vaddq_f32(r0, r1);
sum = vaddq_f32(sum, r2);
sum = vaddq_f32(sum, r3);
Result.NEON = sum;
#elif defined(HANDMADE_MATH__USE_SSE)
Result.SSE = _mm_mul_ps(_mm_shuffle_ps(Left.SSE, Left.SSE, 0x00), Right.Columns[0].SSE);
Result.SSE = _mm_add_ps(Result.SSE, _mm_mul_ps(_mm_shuffle_ps(Left.SSE, Left.SSE, 0x55), Right.Columns[1].SSE));
Result.SSE = _mm_add_ps(Result.SSE, _mm_mul_ps(_mm_shuffle_ps(Left.SSE, Left.SSE, 0xaa), Right.Columns[2].SSE));
Result.SSE = _mm_add_ps(Result.SSE, _mm_mul_ps(_mm_shuffle_ps(Left.SSE, Left.SSE, 0xff), Right.Columns[3].SSE));
#else
Result.X = Left.Elements[0] * Right.Columns[0].X;
Result.Y = Left.Elements[0] * Right.Columns[0].Y;
Result.Z = Left.Elements[0] * Right.Columns[0].Z;
Result.W = Left.Elements[0] * Right.Columns[0].W;
Result.X += Left.Elements[1] * Right.Columns[1].X;
Result.Y += Left.Elements[1] * Right.Columns[1].Y;
Result.Z += Left.Elements[1] * Right.Columns[1].Z;
Result.W += Left.Elements[1] * Right.Columns[1].W;
Result.X += Left.Elements[2] * Right.Columns[2].X;
Result.Y += Left.Elements[2] * Right.Columns[2].Y;
Result.Z += Left.Elements[2] * Right.Columns[2].Z;
Result.W += Left.Elements[2] * Right.Columns[2].W;
Result.X += Left.Elements[3] * Right.Columns[3].X;
Result.Y += Left.Elements[3] * Right.Columns[3].Y;
Result.Z += Left.Elements[3] * Right.Columns[3].Z;
Result.W += Left.Elements[3] * Right.Columns[3].W;
#endif
return Result;
}
HMM_Vec4 HMM_LinearCombineV4M4(HMM_Vec4 Left, HMM_Mat4 Right) {
HMM_Vec4 Result;
#ifdef HANDMADE_MATH__USE_SSE
Result.SSE = _mm_mul_ps(_mm_shuffle_ps(Left.SSE, Left.SSE, 0x00), Right.Columns[0].SSE);
Result.SSE = _mm_add_ps(Result.SSE, _mm_mul_ps(_mm_shuffle_ps(Left.SSE, Left.SSE, 0x55), Right.Columns[1].SSE));
Result.SSE = _mm_add_ps(Result.SSE, _mm_mul_ps(_mm_shuffle_ps(Left.SSE, Left.SSE, 0xaa), Right.Columns[2].SSE));
Result.SSE = _mm_add_ps(Result.SSE, _mm_mul_ps(_mm_shuffle_ps(Left.SSE, Left.SSE, 0xff), Right.Columns[3].SSE));
#else
Result.X = Left.Elements[0] * Right.Columns[0].X;
Result.Y = Left.Elements[0] * Right.Columns[0].Y;
Result.Z = Left.Elements[0] * Right.Columns[0].Z;
Result.W = Left.Elements[0] * Right.Columns[0].W;
Result.X += Left.Elements[1] * Right.Columns[1].X;
Result.Y += Left.Elements[1] * Right.Columns[1].Y;
Result.Z += Left.Elements[1] * Right.Columns[1].Z;
Result.W += Left.Elements[1] * Right.Columns[1].W;
Result.X += Left.Elements[2] * Right.Columns[2].X;
Result.Y += Left.Elements[2] * Right.Columns[2].Y;
Result.Z += Left.Elements[2] * Right.Columns[2].Z;
Result.W += Left.Elements[2] * Right.Columns[2].W;
Result.X += Left.Elements[3] * Right.Columns[3].X;
Result.Y += Left.Elements[3] * Right.Columns[3].Y;
Result.Z += Left.Elements[3] * Right.Columns[3].Z;
Result.W += Left.Elements[3] * Right.Columns[3].W;
#endif
return Result;
}
return HMM_LinearCombineV4M4_P(&Left, &Right);
}
/*
* 2x2 Matrices
@@ -1041,6 +1069,17 @@ HMM_Mat4 HMM_MulM4(HMM_Mat4 Left, HMM_Mat4 Right) {
return Result;
}
HMM_Mat4 HMM_MulM4_P(HMM_Mat4 *Left, HMM_Mat4 *Right)
{
HMM_Mat4 Result;
Result.Columns[0] = HMM_LinearCombineV4M4_P(&Right->Columns[0], Left);
Result.Columns[1] = HMM_LinearCombineV4M4_P(&Right->Columns[1], Left);
Result.Columns[2] = HMM_LinearCombineV4M4_P(&Right->Columns[2], Left);
Result.Columns[3] = HMM_LinearCombineV4M4_P(&Right->Columns[3], Left);
return Result;
}
HMM_Mat4 HMM_MulM4F(HMM_Mat4 Matrix, float Scalar) {
HMM_Mat4 Result;
@@ -1073,6 +1112,11 @@ HMM_Mat4 HMM_MulM4F(HMM_Mat4 Matrix, float Scalar) {
return Result;
}
HMM_Vec4 HMM_MulM4V4_P(HMM_Mat4 *Matrix, HMM_Vec4 *Vector)
{
return HMM_LinearCombineV4M4_P(Vector, Matrix);
}
HMM_Vec4 HMM_MulM4V4(HMM_Mat4 Matrix, HMM_Vec4 Vector) {
return HMM_LinearCombineV4M4(Vector, Matrix);
}

View File

@@ -98,23 +98,19 @@
#include <chipmunk/chipmunk.h>
/* let's figure out if SSE is really available (unless disabled anyway)
(it isn't on non-x86/x86_64 platforms or even x86 without explicit SSE support)
=> only use "#ifdef HANDMADE_MATH__USE_SSE" to check for SSE support below this block! */
#ifndef HANDMADE_MATH_NO_SSE
#ifdef _MSC_VER /* MSVC supports SSE in amd64 mode or _M_IX86_FP >= 1 (2 means SSE2) */
#if defined(_M_AMD64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 1)
#define HANDMADE_MATH__USE_SSE 1
#endif
#else /* not MSVC, probably GCC, clang, icc or something that doesn't support SSE anyway */
#ifdef __SSE__ /* they #define __SSE__ if it's supported */
#define HANDMADE_MATH__USE_SSE 1
#endif /* __SSE__ */
#endif /* not _MSC_VER */
#endif /* #ifndef HANDMADE_MATH_NO_SSE */
#ifdef HANDMADE_MATH__USE_SSE
#include <xmmintrin.h>
#if !defined(HANDMADE_MATH_NO_SIMD)
#if defined(__ARM_NEON) || defined(__ARM_NEON__)
#define HANDMADE_MATH__USE_NEON 1
#include <arm_neon.h>
#elif defined(_MSC_VER)
#if defined(_M_AMD64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 1)
#define HANDMADE_MATH__USE_SSE 1
#include <xmmintrin.h>
#endif
#elif defined(__SSE__)
#define HANDMADE_MATH__USE_SSE 1
#include <xmmintrin.h>
#endif
#endif
#ifdef _MSC_VER
@@ -390,11 +386,13 @@ typedef union HMM_Vec4 {
float Elements[4];
float e[4];
#ifdef HANDMADE_MATH__USE_SSE
__m128 SSE;
#endif
__m128 SSE;
#endif
#ifdef HANDMADE_MATH__USE_NEON
float32x4_t NEON;
#endif
} HMM_Vec4;
typedef union HMM_Mat2 {
@@ -579,8 +577,10 @@ HMM_Mat4 HMM_TransposeM4(HMM_Mat4 Matrix);
HMM_Mat4 HMM_AddM4(HMM_Mat4 Left, HMM_Mat4 Right);
HMM_Mat4 HMM_SubM4(HMM_Mat4 Left, HMM_Mat4 Right);
HMM_Mat4 HMM_MulM4(HMM_Mat4 Left, HMM_Mat4 Right);
HMM_Mat4 HMM_MulM4_P(HMM_Mat4 *Left, HMM_Mat4 *Right);
HMM_Mat4 HMM_MulM4F(HMM_Mat4 Matrix, float Scalar);
HMM_Vec4 HMM_MulM4V4(HMM_Mat4 Matrix, HMM_Vec4 Vector);
HMM_Vec4 HMM_MulM4V4_P(HMM_Mat4 *Matrix, HMM_Vec4 *Vector);
HMM_Mat4 HMM_DivM4F(HMM_Mat4 Matrix, float Scalar);
float HMM_DeterminantM4(HMM_Mat4 Matrix);

34
source/aabb.c Normal file
View File

@@ -0,0 +1,34 @@
#include <stdlib.h>
#include <math.h>
#include "aabb.h"
aabb*
aabb_new(float x, float y, float hW, float hH) {
aabb* a = malloc(sizeof(aabb));
a->center.x = x;
a->center.y = y;
a->dims.w = hW;
a->dims.h = hH;
return a;
}
void
aabb_free(aabb *a) {
free(a);
}
int
aabb_contains(aabb *a, float x, float y) {
return (x >= a->center.x-a->dims.w &&
x <= a->center.x+a->dims.w) &&
(y >= a->center.y-a->dims.h &&
y <= a->center.y+a->dims.h);
}
int
aabb_intersects(aabb *a, aabb *b) {
return (abs(a->center.x - b->center.x) < (a->dims.w + b->dims.w)) &&
(abs(a->center.y - b->center.y) < (a->dims.h + b->dims.h));
}

48
source/aabb.h Normal file
View File

@@ -0,0 +1,48 @@
/*
aabb.h
2014 JSK (kutani@projectkutani.com)
Simple (2D) axis-aligned bounding box implementation. Part of the Panic
Panic project.
Released to the public domain. See LICENSE for details.
*/
#ifndef _AABB_H
#define _AABB_H
/** \brief axis-aligned bounding box
Simple struct of four floats, divided into two sub-structs.
center {x, y} - The center point of the bounding box
dims {w, h} - The half-width and half-height of the box
*/
typedef struct aabb {
struct {
float x;
float y;
} center;
struct {
float w;
float h;
} dims;
} aabb;
/// Malloc's a new aabb struct
/*!
Mallocs a new aabb struct and sets center and dims to the passed
x, y, hW, and hH values.
*/
aabb* aabb_new(float x, float y, float hW, float hH);
/// Frees the passed aabb.
void aabb_free(aabb *a);
/// Checks if the point x,y lies within the passed aabb
int aabb_contains(aabb *a, float x, float y);
/// Checks if the two passed aabb's intersect
int aabb_intersects(aabb *a, aabb *b);
#endif

View File

@@ -31,6 +31,8 @@
#include "sprite.h"
#include "quadtree.h"
#include <SDL3/SDL.h>
#include <SDL3/SDL_gpu.h>
#include <SDL3/SDL_error.h>
@@ -131,6 +133,7 @@ static JSAtom clamp_border_atom;
static JSAtom vertex_atom;
static JSAtom index_atom;
static JSAtom indirect_atom;
static JSAtom rect_atom;
typedef struct texture_vertex {
float x, y, z;
@@ -1147,7 +1150,12 @@ QJSCLASS(SDL_GPUCopyPass)
QJSCLASS(SDL_GPURenderPass)
QJSCLASS(SDL_Cursor)
void qtree_free(JSRuntime *rt, qtree *tree)
{
qtree_destroy(*tree);
}
QJSCLASS(qtree)
int js_arrlen(JSContext *js,JSValue v) {
if (JS_IsUndefined(v)) return 0;
@@ -3400,6 +3408,7 @@ JSC_CCALL(renderer_make_sprite_mesh,
{0.0,1.0,1.0},
{1.0,1.0,1.0}
};
for (int j = 0; j < 4; j++)
posdata[base+j] = HMM_MulM3V3(trmat, base_quad[j]).xy;
@@ -3999,7 +4008,13 @@ static HMM_Vec3 base_quad[4] = {
{0.0,1.0,1.0},
{1.0,1.0,1.0}
};
static HMM_Vec4 base_quad_4[4] = {
{ 0.0,0.0, 1.0f, 1.0f },
{ 1,0,0.0, 1.0f, 1.0f },
{ 0.0,1.0, 1.0f, 1.0f },
{ 1.0,1.0, 1.0f, 1.0f }
};
static inline void add_quad(text_vert **verts, rect *restrict src, rect *restrict dst)
{
text_vert v = (text_vert){
@@ -4079,12 +4094,13 @@ JSC_CCALL(gpu_sort_sprite,
return number2js(js,0);
)
inline int sprite_in_view(HMM_Mat3 sprite, HMM_Mat4 camera)
inline int sprite_in_view(HMM_Mat4 sprite, HMM_Mat4 camera)
{
HMM_Mat4 camsprite = HMM_MulM4(camera, sprite);
int outside = 0;
for (int j = 0; j < 4; j++) {
HMM_Vec3 corner = HMM_MulM3V3(sprite, (HMM_Vec3){base_quad[j].x, base_quad[j].y, 1.0});
HMM_Vec4 clip = HMM_MulM4V4(camera, (HMM_Vec4){corner.x,corner.y,corner.z,1.0});
HMM_Vec4 clip = HMM_MulM4V4_P(&camsprite, &base_quad_4[j]);
if (clip.w <= 0.0) {
outside++;
continue;
@@ -4114,12 +4130,8 @@ JSC_CCALL(gpu_make_sprite_queue,
JSValue sub = JS_GetPropertyUint32(js, argv[0], i);
transform *t;
JS_GETATOM(js,t,sub,transform_atom,transform)
HMM_Mat3 trmat = transform2mat3_global(t);
if (!sprite_in_view(trmat,info.world_to_projection)) {
JS_FreeValue(js,sub);
continue;
}
HMM_Mat4 trmat = transform2mat4_global(t);
rect src;
HMM_Vec4 color;
@@ -4129,7 +4141,7 @@ JSC_CCALL(gpu_make_sprite_queue,
quad sprite_quad;
for (int j = 0; j < 4; j++)
sprite_quad.vert[j].pos = HMM_MulM3V3(trmat, base_quad[j]).xy;
sprite_quad.vert[j].pos = HMM_MulM4V4_P(&trmat, &(HMM_Vec4){base_quad[j].x,base_quad[j].y,1.0,1.0}).xy;
sprite_quad.vert[0].uv = (HMM_Vec2){ src.x, src.y + src.h };
sprite_quad.vert[1].uv = (HMM_Vec2){ src.x+src.w, src.y + src.h };
@@ -4150,7 +4162,7 @@ JSC_CCALL(gpu_make_sprite_queue,
JS_FreeValue(js, sub);
}
qsort(sprites, arrlen(sprites),sizeof(sprite),sort_sprite);
text_vert *buffer = NULL;
@@ -4185,6 +4197,17 @@ JSC_CCALL(gpu_make_sprite_queue,
} else count++;
JS_FreeValue(js,sprites[i].image);
}
if (count > 0) {
JSValue q = JS_NewObject(js);
JS_SetPropertyStr(js,q,"type", JS_NewString(js,"geometry"));
JS_SetPropertyStr(js,q,"mesh", JS_DupValue(js,mesh));
JS_SetPropertyStr(js,q,"pipeline", JS_DupValue(js,argv[2]));
JS_SetPropertyStr(js,q,"image", JS_DupValue(js,img));
JS_SetPropertyStr(js,q,"first_index", number2js(js,first_index));
JS_SetPropertyStr(js,q,"num_indices",number2js(js,count*6));
JS_SetPropertyUint32(js,ret,n++,q);
}
arrfree(sprites);
JS_FreeValue(js,mesh);
@@ -5933,6 +5956,17 @@ JSC_CCALL(transform_dirty,
return JS_NewBool(js,transform_dirty_chain(js2transform(js,self)));
)
JSC_CCALL(transform_torect,
transform *t = js2transform(js,self);
HMM_Mat4 m3 = transform2mat4_global(t);
rect r = {0};
r.x = m3.Columns[3].x;
r.y = m3.Columns[3].y;
r.w = m3.Columns[0].x;
r.h = m3.Columns[1].y;
return rect2js(js,r);
)
static const JSCFunctionListEntry js_transform_funcs[] = {
CGETSET_ADD(transform, pos),
CGETSET_ADD(transform, scale),
@@ -5950,6 +5984,7 @@ static const JSCFunctionListEntry js_transform_funcs[] = {
MIST_FUNC_DEF(transform, array, 0),
MIST_FUNC_DEF(transform, clean, 0),
MIST_FUNC_DEF(transform, dirty, 0),
MIST_FUNC_DEF(transform, torect, 0),
};
JSC_CCALL(datastream_time, return number2js(js,plm_get_time(js2datastream(js,self)->plm)); )
@@ -7026,7 +7061,7 @@ JSC_CCALL(os_cull_sprites,
JSValue sub = JS_GetPropertyUint32(js,sprites, i);
transform *t;
JS_GETATOM(js,t,sub,transform_atom,transform)
HMM_Mat3 trmat = t->gcache3;
HMM_Mat4 trmat = transform2mat4_global(t);
if (sprite_in_view(trmat, info.world_to_projection)) {
JS_SetPropertyUint32(js,ret,n,JS_DupValue(js,sub));
n++;
@@ -7035,6 +7070,26 @@ JSC_CCALL(os_cull_sprites,
}
)
static JSContext *global_js;
int js_qtree_cmp(JSValue *v, aabb *range)
{
rect rect;
JSValue val = *v;
JS_GETATOM(global_js,rect,val,rect_atom,rect)
return (rect.x + rect.w < range->center.x+range->dims.w
&& rect.x > range->center.x-range->dims.w
&& rect.y > range->center.y-range->dims.h
&& rect.y + rect.h < range->center.y + range->dims.h);
}
JSC_CCALL(os_make_quadtree,
HMM_Vec2 center = js2vec2(js,argv[0]);
HMM_Vec2 wh = js2vec2(js,argv[1]);
qtree tree = qtree_new(center.x,center.y,wh.x,wh.y, js_qtree_cmp);
return qtree2js(js,tree);
)
static const JSCFunctionListEntry js_os_funcs[] = {
MIST_FUNC_DEF(os, turbulence, 4),
MIST_FUNC_DEF(os, model_buffer, 1),
@@ -7051,6 +7106,7 @@ static const JSCFunctionListEntry js_os_funcs[] = {
MIST_FUNC_DEF(os, exit, 1),
MIST_FUNC_DEF(os, gc, 0),
MIST_FUNC_DEF(os, eval, 2),
MIST_FUNC_DEF(os, make_quadtree, 2),
MIST_FUNC_DEF(os, make_texture, 1),
MIST_FUNC_DEF(os, make_gif, 1),
MIST_FUNC_DEF(os, make_aseprite, 1),
@@ -7099,7 +7155,41 @@ static const JSCFunctionListEntry js_os_funcs[] = {
MIST_FUNC_DEF(os, cull_sprites, 2),
};
JSC_CCALL(qtree_insert,
qtree tree = js2qtree(js,self);
JSValue *item = malloc(sizeof(*item));
*item = JS_DupValue(js,argv[0]);
qtree_insert(tree, item);
)
JSC_CCALL(qtree_remove,
qtree tree = js2qtree(js,self);
JSValue item = argv[0];
)
JSC_CCALL(qtree_find,
qtree tree = js2qtree(js,self);
HMM_Vec2 c = js2vec2(js,argv[0]);
HMM_Vec2 wh = js2vec2(js,argv[1]);
c.x -= wh.x/2.0;
c.y -= wh.y/2.0;
uint32_t n;
JSValue **items = qtree_findInArea(tree, c.x, c.y, wh.x, wh.y, &n);
ret = JS_NewArray(js);
if (n > 0)
JS_SetPropertyUint32(js,ret,0,JS_DupValue(js,*items[0]));
/*
for (int i = 0; i < n; i++)
JS_SetPropertyUint32(js,ret,i, JS_DupValue(js,*items[i]));*/
)
static const JSCFunctionListEntry js_qtree_funcs[] = {
MIST_FUNC_DEF(qtree, insert, 1),
MIST_FUNC_DEF(qtree, remove, 1),
MIST_FUNC_DEF(qtree, find, 2),
};
static const JSCFunctionListEntry js_jssprite_funcs[] = {
};
@@ -7161,6 +7251,7 @@ void ffi_load(JSContext *js) {
JSValue globalThis = JS_GetGlobalObject(js);
QJSCLASSPREP_FUNCS(qtree)
QJSCLASSPREP_FUNCS(SDL_Window)
QJSCLASSPREP_FUNCS(SDL_Surface)
QJSCLASSPREP_FUNCS(SDL_Thread)
@@ -7312,8 +7403,10 @@ void ffi_load(JSContext *js) {
indirect_atom = JS_NewAtom(js, "indirect");
num_indices_atom = JS_NewAtom(js,"num_indices");
parent_atom = JS_NewAtom(js,"parent");
rect_atom = JS_NewAtom(js,"rect");
fill_event_atoms(js);
global_js = js;
JS_FreeValue(js,globalThis);
}

276
source/quadtree.c Normal file
View File

@@ -0,0 +1,276 @@
/*
quadtree.c
2014 JSK (kutani@projectkutani.com)
Part of the Panic Panic project.
Released to the public domain. See LICENSE for details.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include "aabb.h"
/// Default node size cap
#define QTREE_STDCAP 4
/// A function pointer def for determining if an element exists in a range
typedef int (*qtree_fnc)(void *ptr, aabb *range);
/// Quadtree node
typedef struct qnode {
uint16_t cnt; ///< Number of elements in this node
aabb bound; ///< Area this node covers
void **elist; ///< List of element pointers
struct qnode *nw; ///< NW quadrant of this node
struct qnode *ne; ///< NE quadrant of this node
struct qnode *sw; ///< SW quadrant of this node
struct qnode *se; ///< SE quadrant of this node
} qnode;
/// Quadtree container
typedef struct _qtree {
uint16_t maxnodecap; ///< Maximum element count per node
qnode *root; ///< Root node
qtree_fnc cmpfnc; ///< Element range compare function pointer
} _qtree;
typedef struct _qtree* qtree;
/// Simple container for returning found elements
typedef struct retlist {
uint32_t cnt; ///< Number of elements found
aabb range; ///< Range to use for searching
void **list; ///< Array of pointers to found elements
} retlist;
static void
retlist_add(retlist *r, void *p) {
r->list = realloc(r->list, sizeof(void*)*(r->cnt+1));
r->list[r->cnt] = p;
r->cnt++;
}
static uint16_t
qtree_getMaxNodeCnt(qtree q) {
uint16_t r;
r = q->maxnodecap;
return r;
}
static qnode*
qnode_new(qtree p, float x, float y, float hW, float hH) {
qnode *q = malloc(sizeof(qnode));
memset(q, 0, sizeof(qnode));
q->bound.center.x = x;
q->bound.center.y = y;
q->bound.dims.w = hW;
q->bound.dims.h = hH;
return q;
}
static void
qnode_free(qtree q, qnode *qn) {
if(qn->cnt)
free(qn->elist);
qn->cnt = 0;
if(qn->nw) {
qnode_free(q, qn->nw);
qnode_free(q, qn->ne);
qnode_free(q, qn->sw);
qnode_free(q, qn->se);
}
free(qn);
}
static void
add(qnode *q, void *p) {
q->elist = realloc(q->elist, sizeof(void*)*(q->cnt+1));
q->elist[q->cnt] = p;
q->cnt++;
}
static void
drop(qnode *q, uint16_t idx) {
void **narry = malloc(sizeof(void*)*(q->cnt-1));
// This is a little (lot) ugly; a pair of memcpy's would be
// better, but I had some problems with it
for(uint16_t i=0,skip=0; i<q->cnt; i++) {
if(i == idx) { skip++; continue; }
narry[i-skip] = q->elist[i];
}
void **old = q->elist;
q->elist = narry;
free(old);
q->cnt--;
}
static void
subdivide(qtree p, qnode *q) {
float cx = q->bound.center.x;
float cy = q->bound.center.y;
float hw = q->bound.dims.w/2;
float hh = q->bound.dims.h/2;
q->nw = qnode_new(p, cx-hw, cy-hh, hw, hh);
q->ne = qnode_new(p, cx+hw, cy-hh, hw, hh);
q->sw = qnode_new(p, cx-hw, cy+hh, hw, hh);
q->se = qnode_new(p, cx+hw, cy+hh, hw, hh);
}
static int
qnode_insert(qtree q, qnode *qn, void *ptr) {
int ret = 0;
if(! (q->cmpfnc)(ptr, &qn->bound))
goto QN_INS_EXIT;
if(qn->cnt < qtree_getMaxNodeCnt(q)) {
add(qn, ptr);
ret = 1;
goto QN_INS_EXIT;
}
if(! qn->nw)
subdivide(q, qn);
if(qnode_insert(q,qn->nw,ptr))
return 1;
else if(qnode_insert(q,qn->ne,ptr))
return 1;
else if(qnode_insert(q,qn->sw,ptr))
return 1;
else if(qnode_insert(q,qn->se,ptr))
return 1;
QN_INS_EXIT:
return ret;
}
static void*
qnode_remove(qtree q, qnode *qn, void *ptr) {
if(qn->cnt) {
for(uint16_t i=0; i<qn->cnt; i++) {
if(qn->elist[i] == ptr) {
drop(qn, i);
ptr = NULL;
goto QN_REM_EXIT;
}
}
}
if(! qn->nw)
return NULL;
if(qnode_remove(q, qn->nw, ptr)) return ptr;
if(qnode_remove(q, qn->ne, ptr)) return ptr;
if(qnode_remove(q, qn->sw, ptr)) return ptr;
if(qnode_remove(q, qn->se, ptr)) return ptr;
return NULL;
QN_REM_EXIT:
return ptr;
}
static void
qnode_getInRange(qtree q, qnode *qn, retlist *r) {
if(qn->cnt) {
if(! aabb_intersects(&qn->bound, &r->range))
goto QN_GET_EXIT;
for(uint16_t i=0; i<qn->cnt; i++)
if((q->cmpfnc)(qn->elist[i], &r->range))
retlist_add(r, qn->elist[i]);
}
if(! qn->nw)
goto QN_GET_EXIT;
qnode_getInRange(q, qn->nw, r);
qnode_getInRange(q, qn->ne, r);
qnode_getInRange(q, qn->sw, r);
qnode_getInRange(q, qn->se, r);
QN_GET_EXIT:
return;
}
/* exports */
qtree
qtree_new(float x, float y, float w, float h, qtree_fnc fnc) {
qtree q = malloc(sizeof(_qtree));
memset(q, 0, sizeof(_qtree));
q->maxnodecap = QTREE_STDCAP;
q->cmpfnc = fnc;
q->root = qnode_new(q, x+(w/2),y+(h/2),w/2,h/2);
return q;
}
void
qtree_destroy(qtree q) {
void *m;
if(q->root) qnode_free(q, q->root);
memset(q, 0, sizeof(_qtree));
free(q);
}
void
qtree_insert(qtree q, void *ptr) {
qnode_insert(q, q->root, ptr);
}
void
qtree_remove(qtree q, void *ptr) {
qnode_remove(q, q->root, ptr);
}
void
qtree_setMaxNodeCnt(qtree q, uint16_t cnt) {
q->maxnodecap = cnt || 1;
}
void
qtree_clear(qtree q) {
float x = q->root->bound.center.x;
float y = q->root->bound.center.y;
float w = q->root->bound.dims.w;
float h = q->root->bound.dims.h;
qnode *qn = q->root;
q->root = qnode_new(q, x, y, w, h);
qnode_free(q, qn);
}
void**
qtree_findInArea(qtree q, float x, float y, float w, float h, uint32_t *cnt) {
float hw = w/2;
float hh = h/2;
retlist ret;
memset(&ret, 0, sizeof(retlist));
ret.range.center.x = x+hw;
ret.range.center.y = y+hh;
ret.range.dims.w = hw;
ret.range.dims.h = hh;
qnode_getInRange(q, q->root, &ret);
*cnt = ret.cnt;
return ret.list;
}

76
source/quadtree.h Normal file
View File

@@ -0,0 +1,76 @@
/*
quadtree.h
2014 JSK (kutani@projectkutani.com)
Part of the Panic Panic project.
Released to the public domain. See LICENSE for details.
*/
#ifndef _QUADTREE_H
#define _QUADTREE_H
#ifndef _AABB_H
#include "aabb.h"
#endif
/// Opaque pointer to a quadtree data structure
typedef struct _qtree* qtree;
/// A function pointer def for determining if an element exists in a range
typedef int (*qtree_fnc)(void *ptr, aabb *range);
/// Create a new qtree
/*!
Creates a new qtree with a bound of w,h size, centered at x,y.
Uses the passed function pointer fnc to test elements against nodes
for insertion, and finding.
Returns a new qtree pointer.
*/
qtree qtree_new(float x, float y, float w, float h, qtree_fnc fnc);
void qtree_destroy(qtree q);
/// Insert an element
/*!
Inserts the passed element into quadtree q.
Uses the function passed to qtree_new() to determine where the
element should go.
*/
void qtree_insert(qtree q, void *ptr);
/// Removes an element from the quadtree
/*!
Performs a selective removal of the passed element.
Performs a naive pointer comparison and a depth-first search of the
tree, so this isn't very fast.
*/
void qtree_remove(qtree q, void *ptr);
/// Set the maximum number of elements per node
/*!
Sets the maximum elements per quadtree node.
The default is 4.
*/
void qtree_setMaxNodeCnt(qtree q, uint16_t cnt);
/// Resets a quadtree
/*!
Clears all nodes held by the quadtree and creates a fresh root node
with no elements assigned.
*/
void qtree_clear(qtree q);
/// Find all elements within a rectangular bound
/*!
Performs a search for any elements within the given x,y + w,h
bound. Returns an array of pointers to any elements (which should be
freed by the user), and places the number of elements in cnt.
*/
void** qtree_findInArea(qtree q, float x, float y, float w, float h, uint32_t *cnt);
#endif

View File

@@ -72,14 +72,27 @@ HMM_Mat3 transform2mat3(transform *t)
return t->cache3;
}
HMM_Mat4 transform2mat4_global(transform *t)
{
if (!transform_dirty_chain(t)) return t->gcache;
HMM_Mat4 tm = transform2mat(t);
if (t->parent)
t->gcache = HMM_MulM4(transform2mat(t->parent), tm);
else
t->gcache = tm;
return t->gcache;
}
HMM_Mat3 transform2mat3_global(transform *t)
{
if (!transform_dirty_chain(t)) return t->gcache3;
HMM_Mat3 tm = transform2mat3(t);
if (t->parent) {
if (t->parent)
t->gcache3 = HMM_MulM3(transform2mat3(t->parent), tm);
} else
else
t->gcache3 = tm;
return t->gcache3;

View File

@@ -48,6 +48,7 @@ HMM_Vec3 mat3_t_dir(HMM_Mat4 m, HMM_Vec3 dir);
HMM_Mat4 transform2mat(transform *t);
HMM_Mat3 transform2mat3(transform *t);
HMM_Mat4 transform2mat4_global(transform *t);
HMM_Mat3 transform2mat3_global(transform *t);
int transform_dirty_chain(transform *t);