You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

318 lines
8.3 KiB
C

/* The MIT License (MIT)
*
* Copyright (c) 2021 Stefano Trettel
*
* Software repository: MoonCCD, https://github.com/stetre/moonccd
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "internal.h"
static ud_t *Ud = NULL;
#define PAR 1
#define OBJ1 2
#define OBJ2 3
static void FirstDir(const void *obj1, const void *obj2, vec3_t *dir);
static void Support(const void *obj_, const vec3_t *dir, vec3_t *vec);
static void Center(const void *obj_, vec3_t *center);
static int freeccd(lua_State *L, ud_t *ud)
{
ccd_t *ccd = (ccd_t *)ud->handle;
if (!freeuserdata(L, ud, "ccdpar"))
return 0;
Free(L, ccd);
return 0;
}
static int newccd(lua_State *L, ccd_t *ccd, int ref[6])
{
ud_t *ud;
ud = newuserdata(L, ccd, CCDPAR_MT, "ccdpar");
ud->parent_ud = NULL;
ud->destructor = freeccd;
memcpy(ud->ref, ref, 6 * sizeof(int));
return 1;
}
static int New(lua_State *L)
{
ccd_t ccd, *ccdp;
int ref[6];
int t = lua_type(L, 1);
CCD_INIT(&ccd);
memset(ref, 0, 6 * sizeof(int));
switch (t)
{
case LUA_TNONE:
case LUA_TNIL:
return argerror(L, 1, ERR_NOTPRESENT);
case LUA_TTABLE:
break;
default:
return argerror(L, 1, ERR_TABLE);
}
#define checkfn(name, ccdfield, Func, ref) \
do \
{ \
lua_getfield(L, 1, name); \
if (lua_isfunction(L, -1)) \
/* this also unfererences any previous ref: */ \
{ \
Reference(L, -1, ref); \
ccd.ccdfield = Func; \
} \
else if (!lua_isnoneornil(L, -1)) \
return argerror(L, 1, ERR_FUNCTION); \
lua_pop(L, 1); \
} while (0)
checkfn("first_dir", first_dir, FirstDir, ref[0]);
checkfn("support1", support1, Support, ref[1]);
checkfn("support2", support2, Support, ref[2]);
checkfn("center1", center1, Center, ref[3]);
checkfn("center2", center2, Center, ref[4]);
#undef checkfn
lua_getfield(L, 1, "max_iterations");
ccd.max_iterations = luaL_optinteger(L, -1, ccd.max_iterations);
lua_pop(L, 1);
lua_getfield(L, 1, "epa_tolerance");
ccd.epa_tolerance = luaL_optnumber(L, -1, ccd.epa_tolerance);
lua_pop(L, 1);
lua_getfield(L, 1, "mpr_tolerance");
ccd.mpr_tolerance = luaL_optnumber(L, -1, ccd.mpr_tolerance);
lua_pop(L, 1);
lua_getfield(L, 1, "dist_tolerance");
ccd.dist_tolerance = luaL_optnumber(L, -1, ccd.dist_tolerance);
lua_pop(L, 1);
ccdp = Malloc(L, sizeof(ccd_t));
memcpy(ccdp, &ccd, sizeof(ccd_t));
return newccd(L, ccdp, ref);
}
static void FirstDir(const void *obj1, const void *obj2, vec3_t *dir)
{
#define L moonccd_L
int rc;
int t = lua_gettop(L);
(void)obj1;
(void)obj2;
lua_rawgeti(L, LUA_REGISTRYINDEX, Ud->ref[0]);
lua_pushvalue(L, OBJ1);
lua_pushvalue(L, OBJ2);
rc = lua_pcall(L, 2, 1, 0);
if (rc != LUA_OK)
lua_error(L);
checkvec3(L, -1, dir);
lua_settop(L, t);
#undef L
}
static void Support(const void *obj_, const vec3_t *dir, vec3_t *vec)
{
#define L moonccd_L
int ref, rc;
int obj = (ptrdiff_t)obj_;
int t = lua_gettop(L);
switch (obj)
{
case OBJ1:
ref = Ud->ref[1];
break;
case OBJ2:
ref = Ud->ref[2];
break;
default:
unexpected(L);
return;
}
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
lua_pushvalue(L, obj);
pushvec3(L, dir);
rc = lua_pcall(L, 2, 1, 0);
if (rc != LUA_OK)
lua_error(L);
checkvec3(L, -1, vec);
lua_settop(L, t);
#undef L
}
static void Center(const void *obj_, vec3_t *center)
{
#define L moonccd_L
int ref, rc;
int obj = (ptrdiff_t)obj_;
int t = lua_gettop(L);
switch (obj)
{
case OBJ1:
ref = Ud->ref[3];
break;
case OBJ2:
ref = Ud->ref[4];
break;
default:
unexpected(L);
return;
}
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
lua_pushvalue(L, obj);
rc = lua_pcall(L, 1, 1, 0);
if (rc != LUA_OK)
lua_error(L);
checkvec3(L, -1, center);
lua_settop(L, t);
#undef L
}
static int GJKIntersect(lua_State *L)
{
ccd_t *ccd = checkccd(L, PAR, &Ud);
luaL_checkany(L, OBJ1);
luaL_checkany(L, OBJ2);
lua_pushboolean(L, ccdGJKIntersect((void *)OBJ1, (void *)OBJ2, ccd));
return 1;
}
static int GJKSeparate(lua_State *L)
{
int rc;
vec3_t sep;
ccd_t *ccd = checkccd(L, PAR, &Ud);
luaL_checkany(L, OBJ1);
luaL_checkany(L, OBJ2);
rc = ccdGJKSeparate((void *)OBJ1, (void *)OBJ2, ccd, &sep);
switch (rc)
{
case 0:
lua_pushboolean(L, 1);
pushvec3(L, &sep);
return 2;
case -1:
lua_pushboolean(L, 0);
return 1;
case -2:
return errmemory(L);
default:
break;
}
return unexpected(L);
}
static int GJKPenetration(lua_State *L)
{
int rc;
double depth;
vec3_t dir, pos;
ccd_t *ccd = checkccd(L, PAR, &Ud);
luaL_checkany(L, OBJ1);
luaL_checkany(L, OBJ2);
rc = ccdGJKPenetration((void *)OBJ1, (void *)OBJ2, ccd, &depth, &dir, &pos);
switch (rc)
{
case 0:
lua_pushboolean(L, 1);
lua_pushnumber(L, depth);
pushvec3(L, &dir);
pushvec3(L, &pos);
return 4;
case -1:
lua_pushboolean(L, 0);
return 1;
case -2:
return errmemory(L);
default:
break;
}
return unexpected(L);
}
static int MPRIntersect(lua_State *L)
{
ccd_t *ccd = checkccd(L, PAR, &Ud);
luaL_checkany(L, OBJ1);
luaL_checkany(L, OBJ2);
lua_pushboolean(L, ccdMPRIntersect((void *)OBJ1, (void *)OBJ2, ccd));
return 1;
}
static int MPRPenetration(lua_State *L)
{
int rc;
double depth;
vec3_t dir, pos;
ccd_t *ccd = checkccd(L, PAR, &Ud);
luaL_checkany(L, OBJ1);
luaL_checkany(L, OBJ2);
rc = ccdMPRPenetration((void *)OBJ1, (void *)OBJ2, ccd, &depth, &dir, &pos);
switch (rc)
{
case 0:
lua_pushboolean(L, 1);
lua_pushnumber(L, depth);
pushvec3(L, &dir);
pushvec3(L, &pos);
return 4;
case -1:
lua_pushboolean(L, 0);
return 1;
case -2:
return errmemory(L);
default:
break;
}
return unexpected(L);
}
DESTROY_FUNC(ccd)
static const struct luaL_Reg Methods[] =
{
{"free", Destroy},
{"gjk_intersect", GJKIntersect},
{"gjk_separate", GJKSeparate},
{"gjk_penetration", GJKPenetration},
{"mpr_intersect", MPRIntersect},
{NULL, NULL} /* sentinel */
};
static const struct luaL_Reg MetaMethods[] =
{
{"__gc", Destroy},
{NULL, NULL} /* sentinel */
};
static const struct luaL_Reg Functions[] =
{
{"new", New},
{"free", Destroy},
{"gjk_intersect", GJKIntersect},
{"gjk_separate", GJKSeparate},
{"gjk_penetration", GJKPenetration},
{"mpr_intersect", MPRIntersect},
{"mpr_penetration", MPRPenetration},
{NULL, NULL} /* sentinel */
};
void moonccd_open_ccd(lua_State *L)
{
udata_define(L, CCDPAR_MT, Methods, MetaMethods);
luaL_setfuncs(L, Functions, 0);
}