308 lines
9.1 KiB
C
308 lines
9.1 KiB
C
/**
|
|
* Copyright (c) 2026 Dominic Masters
|
|
*
|
|
* This software is released under the MIT License.
|
|
* https://opensource.org/licenses/MIT
|
|
*/
|
|
|
|
#include "modulecamera.h"
|
|
#include "assert/assert.h"
|
|
#include "display/camera/camera.h"
|
|
#include "util/memory.h"
|
|
#include "util/string.h"
|
|
|
|
void moduleCamera(scriptcontext_t *context) {
|
|
assertNotNull(context, "Context cannot be NULL.");
|
|
|
|
// Create metatable for camera structure.
|
|
if(luaL_newmetatable(context->luaState, "camera_mt")) {
|
|
// Metatable methods
|
|
lua_pushcfunction(context->luaState, moduleCameraIndex);
|
|
lua_setfield(context->luaState, -2, "__index");
|
|
lua_pushcfunction(context->luaState, moduleCameraNewIndex);
|
|
lua_setfield(context->luaState, -2, "__newindex");
|
|
lua_pop(context->luaState, 1);
|
|
}
|
|
|
|
// Definitions
|
|
#define MODULE_CAMERA_SCRIPT_LEN 64
|
|
char_t buffer[MODULE_CAMERA_SCRIPT_LEN];
|
|
#define MODULE_CAMERA_DEF(str, val) \
|
|
snprintf( \
|
|
buffer, \
|
|
MODULE_CAMERA_SCRIPT_LEN, \
|
|
"%s = %d", \
|
|
str, \
|
|
val \
|
|
); \
|
|
scriptContextExec(context, buffer);
|
|
|
|
MODULE_CAMERA_DEF(
|
|
"CAMERA_PROJECTION_TYPE_PERSPECTIVE",
|
|
CAMERA_PROJECTION_TYPE_PERSPECTIVE
|
|
);
|
|
MODULE_CAMERA_DEF(
|
|
"CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED",
|
|
CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED
|
|
);
|
|
MODULE_CAMERA_DEF(
|
|
"CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC",
|
|
CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC
|
|
);
|
|
|
|
MODULE_CAMERA_DEF(
|
|
"CAMERA_VIEW_TYPE_MATRIX",
|
|
CAMERA_VIEW_TYPE_MATRIX
|
|
);
|
|
MODULE_CAMERA_DEF(
|
|
"CAMERA_VIEW_TYPE_LOOKAT",
|
|
CAMERA_VIEW_TYPE_LOOKAT
|
|
);
|
|
MODULE_CAMERA_DEF(
|
|
"CAMERA_VIEW_TYPE_2D",
|
|
CAMERA_VIEW_TYPE_2D
|
|
);
|
|
MODULE_CAMERA_DEF(
|
|
"CAMERA_VIEW_TYPE_LOOKAT_PIXEL_PERFECT",
|
|
CAMERA_VIEW_TYPE_LOOKAT_PIXEL_PERFECT
|
|
);
|
|
|
|
// Methods
|
|
lua_register(context->luaState, "cameraCreate", moduleCameraCreate);
|
|
lua_register(context->luaState, "cameraPushMatrix", moduleCameraPushMatrix);
|
|
lua_register(context->luaState, "cameraPopMatrix", moduleCameraPopMatrix);
|
|
}
|
|
|
|
int moduleCameraCreate(lua_State *L) {
|
|
assertNotNull(L, "Lua state cannot be NULL.");
|
|
|
|
scriptcontext_t *context = *(scriptcontext_t **)lua_getextraspace(L);
|
|
assertNotNull(context, "Script context cannot be NULL.");
|
|
|
|
// If we are provided a projection type, use it.
|
|
cameraprojectiontype_t projType = CAMERA_PROJECTION_TYPE_PERSPECTIVE;
|
|
if(lua_gettop(L) >= 1) {
|
|
projType = (cameraprojectiontype_t)luaL_checkinteger(L, 1);
|
|
}
|
|
|
|
// Create camera that Lua will own
|
|
camera_t *cam = (camera_t *)lua_newuserdata(L, sizeof(camera_t));
|
|
memoryZero(cam, sizeof(camera_t));
|
|
|
|
// Set metatable
|
|
luaL_getmetatable(L, "camera_mt");
|
|
lua_setmetatable(L, -2);
|
|
|
|
// Init camera
|
|
switch(projType) {
|
|
case CAMERA_PROJECTION_TYPE_PERSPECTIVE:
|
|
case CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED:
|
|
cameraInitPerspective(cam);
|
|
cam->projType = projType;
|
|
break;
|
|
|
|
case CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC:
|
|
cameraInitOrthographic(cam);
|
|
cam->projType = projType;
|
|
break;
|
|
|
|
default:
|
|
luaL_error(L, "Invalid camera projection type specified.");
|
|
break;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int moduleCameraPushMatrix(lua_State *L) {
|
|
assertNotNull(L, "Lua state cannot be NULL.");
|
|
assertTrue(lua_gettop(L) >= 1, "cameraPushMatrix requires 1 arg.");
|
|
assertTrue(lua_isuserdata(L, 1), "cameraPushMatrix arg must be userdata.");
|
|
|
|
// Camera should be provided (pointer to camera_t).
|
|
camera_t *cam = (camera_t *)luaL_checkudata(L, 1, "camera_mt");
|
|
assertNotNull(cam, "Camera pointer cannot be NULL.");
|
|
|
|
cameraPushMatrix(cam);
|
|
return 0;
|
|
}
|
|
|
|
int moduleCameraPopMatrix(lua_State *L) {
|
|
assertNotNull(L, "Lua state cannot be NULL.");
|
|
cameraPopMatrix();
|
|
return 0;
|
|
}
|
|
|
|
int moduleCameraIndex(lua_State *l) {
|
|
assertNotNull(l, "Lua state cannot be NULL.");
|
|
|
|
const char_t *key = luaL_checkstring(l, 2);
|
|
assertStrLenMin(key, 1, "Key cannot be empty.");
|
|
|
|
camera_t *cam = (camera_t *)luaL_checkudata(l, 1, "camera_mt");
|
|
assertNotNull(cam, "Camera pointer cannot be NULL.");
|
|
|
|
if(stringCompare(key, "near") == 0) {
|
|
lua_pushnumber(l, cam->nearClip);
|
|
return 1;
|
|
} else if(stringCompare(key, "far") == 0) {
|
|
lua_pushnumber(l, cam->farClip);
|
|
return 1;
|
|
} else if(stringCompare(key, "nearClip") == 0) {
|
|
lua_pushnumber(l, cam->nearClip);
|
|
return 1;
|
|
} else if(stringCompare(key, "farClip") == 0) {
|
|
lua_pushnumber(l, cam->farClip);
|
|
return 1;
|
|
}
|
|
|
|
// Perspective relative values
|
|
if(cam->projType == CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC) {
|
|
if(stringCompare(key, "left") == 0) {
|
|
lua_pushnumber(l, cam->orthographic.left);
|
|
return 1;
|
|
} else if(stringCompare(key, "right") == 0) {
|
|
lua_pushnumber(l, cam->orthographic.right);
|
|
return 1;
|
|
} else if(stringCompare(key, "top") == 0) {
|
|
lua_pushnumber(l, cam->orthographic.top);
|
|
return 1;
|
|
} else if(stringCompare(key, "bottom") == 0) {
|
|
lua_pushnumber(l, cam->orthographic.bottom);
|
|
return 1;
|
|
}
|
|
} else if(
|
|
cam->projType == CAMERA_PROJECTION_TYPE_PERSPECTIVE ||
|
|
cam->projType == CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED
|
|
) {
|
|
if(stringCompare(key, "fov") == 0) {
|
|
lua_pushnumber(l, cam->perspective.fov);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
// View type relative values.
|
|
if(cam->viewType == CAMERA_VIEW_TYPE_MATRIX) {
|
|
|
|
} else if(cam->viewType == CAMERA_VIEW_TYPE_LOOKAT) {
|
|
if(stringCompare(key, "position") == 0) {
|
|
vec3 *v = (vec3 *)lua_newuserdata(l, sizeof(vec3));
|
|
memoryCopy(v, &cam->lookat.position, sizeof(vec3));
|
|
luaL_getmetatable(l, "vec3_mt");
|
|
lua_setmetatable(l, -2);
|
|
return 1;
|
|
} else if(stringCompare(key, "target") == 0) {
|
|
vec3 *v = (vec3 *)lua_newuserdata(l, sizeof(vec3));
|
|
memoryCopy(v, &cam->lookat.target, sizeof(vec3));
|
|
luaL_getmetatable(l, "vec3_mt");
|
|
lua_setmetatable(l, -2);
|
|
return 1;
|
|
} else if(stringCompare(key, "up") == 0) {
|
|
vec3 *v = (vec3 *)lua_newuserdata(l, sizeof(vec3));
|
|
memoryCopy(v, &cam->lookat.up, sizeof(vec3));
|
|
luaL_getmetatable(l, "vec3_mt");
|
|
lua_setmetatable(l, -2);
|
|
return 1;
|
|
}
|
|
} else if(cam->viewType == CAMERA_VIEW_TYPE_LOOKAT_PIXEL_PERFECT) {
|
|
|
|
} else if(cam->viewType == CAMERA_VIEW_TYPE_2D) {
|
|
|
|
}
|
|
|
|
lua_pushnil(l);
|
|
return 1;
|
|
}
|
|
|
|
int moduleCameraNewIndex(lua_State *l) {
|
|
assertNotNull(l, "Lua state cannot be NULL.");
|
|
|
|
const char_t *key = luaL_checkstring(l, 2);
|
|
assertStrLenMin(key, 1, "Key cannot be empty.");
|
|
|
|
camera_t *cam = (camera_t *)luaL_checkudata(l, 1, "camera_mt");
|
|
assertNotNull(cam, "Camera pointer cannot be NULL.");
|
|
|
|
if(stringCompare(key, "near") == 0 || stringCompare(key, "nearClip") == 0) {
|
|
if(!lua_isnumber(l, 3)) {
|
|
luaL_error(l, "Camera near clip must be a number.");
|
|
}
|
|
cam->nearClip = (float_t)lua_tonumber(l, 3);
|
|
return 0;
|
|
}
|
|
|
|
if(stringCompare(key, "far") == 0 || stringCompare(key, "farClip") == 0) {
|
|
if(!lua_isnumber(l, 3)) {
|
|
luaL_error(l, "Camera far clip must be a number.");
|
|
}
|
|
cam->farClip = (float_t)lua_tonumber(l, 3);
|
|
return 0;
|
|
}
|
|
|
|
// Perspective relative values
|
|
if(cam->projType == CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC) {
|
|
if(stringCompare(key, "left") == 0) {
|
|
if(!lua_isnumber(l, 3)) {
|
|
luaL_error(l, "Camera orthographic left must be a number.");
|
|
}
|
|
cam->orthographic.left = (float_t)lua_tonumber(l, 3);
|
|
return 0;
|
|
}
|
|
|
|
if(stringCompare(key, "right") == 0) {
|
|
if(!lua_isnumber(l, 3)) {
|
|
luaL_error(l, "Camera orthographic right must be a number.");
|
|
}
|
|
cam->orthographic.right = (float_t)lua_tonumber(l, 3);
|
|
return 0;
|
|
}
|
|
|
|
if(stringCompare(key, "top") == 0) {
|
|
if(!lua_isnumber(l, 3)) {
|
|
luaL_error(l, "Camera orthographic top must be a number.");
|
|
}
|
|
cam->orthographic.top = (float_t)lua_tonumber(l, 3);
|
|
return 0;
|
|
}
|
|
|
|
if(stringCompare(key, "bottom") == 0) {
|
|
if(!lua_isnumber(l, 3)) {
|
|
luaL_error(l, "Camera orthographic bottom must be a number.");
|
|
}
|
|
cam->orthographic.bottom = (float_t)lua_tonumber(l, 3);
|
|
return 0;
|
|
}
|
|
} else if(
|
|
cam->projType == CAMERA_PROJECTION_TYPE_PERSPECTIVE ||
|
|
cam->projType == CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED
|
|
) {
|
|
if(stringCompare(key, "fov") == 0) {
|
|
if(!lua_isnumber(l, 3)) {
|
|
luaL_error(l, "Camera perspective FOV must be a number.");
|
|
}
|
|
cam->perspective.fov = (float_t)lua_tonumber(l, 3);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if(cam->viewType == CAMERA_VIEW_TYPE_LOOKAT) {
|
|
if(stringCompare(key, "position") == 0) {
|
|
vec3 *v = (vec3 *)luaL_checkudata(l, 3, "vec3_mt");
|
|
assertNotNull(v, "Vec3 pointer cannot be NULL.");
|
|
memoryCopy(&cam->lookat.position, v, sizeof(vec3));
|
|
return 0;
|
|
} else if(stringCompare(key, "target") == 0) {
|
|
vec3 *v = (vec3 *)luaL_checkudata(l, 3, "vec3_mt");
|
|
assertNotNull(v, "Vec3 pointer cannot be NULL.");
|
|
memoryCopy(&cam->lookat.target, v, sizeof(vec3));
|
|
return 0;
|
|
} else if(stringCompare(key, "up") == 0) {
|
|
vec3 *v = (vec3 *)luaL_checkudata(l, 3, "vec3_mt");
|
|
assertNotNull(v, "Vec3 pointer cannot be NULL.");
|
|
memoryCopy(&cam->lookat.up, v, sizeof(vec3));
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
} |