|
|
|
@ -1,6 +1,11 @@
|
|
|
|
#include "editor_lua.h"
|
|
|
|
#include "editor_lua.h"
|
|
|
|
|
|
|
|
#include "ui_lua.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
|
|
|
|
#define EDITOR_LUA_CONTEXT "editor_context"
|
|
|
|
#define EDITOR_LUA_CONTEXT "editor_context"
|
|
|
|
|
|
|
|
#define CAMERA_LUA_META "camera_handle"
|
|
|
|
|
|
|
|
|
|
|
|
// ClientContext lives once, heap-allocated for the process, never moves —
|
|
|
|
// ClientContext lives once, heap-allocated for the process, never moves —
|
|
|
|
// unlike ui_lua.c's per-dispatch container/gpu pointers, one registry slot
|
|
|
|
// unlike ui_lua.c's per-dispatch container/gpu pointers, one registry slot
|
|
|
|
@ -12,78 +17,233 @@ static ClientContext* current_context(lua_State* L) {
|
|
|
|
return context;
|
|
|
|
return context;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Recomputes the camera's view and pushes it to the currently-rendered hex
|
|
|
|
// camera.main() wraps &context->camera (engine-owned, script can't destroy
|
|
|
|
// scene. Not gated behind any per-frame check - a script-driven camera move
|
|
|
|
// it); camera.create() wraps a script-owned Camera* malloc'd on create and
|
|
|
|
// with no held input must be visible immediately.
|
|
|
|
// freed on :destroy(). `camera == NULL` marks an already-destroyed handle,
|
|
|
|
static void refresh_camera(ClientContext* context) {
|
|
|
|
// so later method calls raise a clear error instead of touching freed
|
|
|
|
camera_update_view(&context->camera);
|
|
|
|
// memory - same stale-handle shape as ui_lua.c's element/overlay handles.
|
|
|
|
camera_sync_gpu(&context->camera, &context->render);
|
|
|
|
typedef struct CameraLuaHandleStruct {
|
|
|
|
update_hex_picking_inverse(&context->camera, &context->hex);
|
|
|
|
Camera* camera;
|
|
|
|
|
|
|
|
bool owned;
|
|
|
|
|
|
|
|
// Registry ref this handle holds on itself, LUA_NOREF for camera.main()'s
|
|
|
|
|
|
|
|
// handle. container_set_camera (ui.h) stores the raw Camera* with no
|
|
|
|
|
|
|
|
// Lua-visible reference back to this userdata ("caller keeps camera alive
|
|
|
|
|
|
|
|
// as long as it's attached" - the caller here is the script). Without
|
|
|
|
|
|
|
|
// this self-pin, a script that does `local c = camera.create(); c:attach(o)`
|
|
|
|
|
|
|
|
// and lets `c` fall out of scope hands the handle to Lua's *incremental*
|
|
|
|
|
|
|
|
// GC - which runs mid-frame, not just at shutdown - while the container is
|
|
|
|
|
|
|
|
// still actively rendering it every frame. Set on create, released on
|
|
|
|
|
|
|
|
// :destroy(); lua_close's shutdown sweep collects it (self-pin or not)
|
|
|
|
|
|
|
|
// regardless, which is what lets __gc below double as the cascade for a
|
|
|
|
|
|
|
|
// script that never explicitly destroys it.
|
|
|
|
|
|
|
|
int self_ref;
|
|
|
|
|
|
|
|
} CameraLuaHandle;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static CameraLuaHandle* check_camera(lua_State* L, int idx) {
|
|
|
|
|
|
|
|
CameraLuaHandle* h = luaL_checkudata(L, idx, CAMERA_LUA_META);
|
|
|
|
|
|
|
|
if(h->camera == NULL) {
|
|
|
|
|
|
|
|
luaL_error(L, "camera handle was destroyed");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return h;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// camera.set_position(x, y, z)
|
|
|
|
static CameraLuaHandle* push_camera_handle(lua_State* L, Camera* camera, bool owned) {
|
|
|
|
static int lua_camera_set_position(lua_State* L) {
|
|
|
|
CameraLuaHandle* h = lua_newuserdata(L, sizeof(CameraLuaHandle));
|
|
|
|
|
|
|
|
h->camera = camera;
|
|
|
|
|
|
|
|
h->owned = owned;
|
|
|
|
|
|
|
|
h->self_ref = LUA_NOREF;
|
|
|
|
|
|
|
|
luaL_setmetatable(L, CAMERA_LUA_META);
|
|
|
|
|
|
|
|
return h;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Recomputes the camera's view and pushes it to the GPU. Not gated behind
|
|
|
|
|
|
|
|
// any per-frame check - a script-driven camera move with no held input must
|
|
|
|
|
|
|
|
// be visible immediately. The picking-ray inverse only tracks the one
|
|
|
|
|
|
|
|
// interactive camera (see cursor_to_world_ray's full-window assumption in
|
|
|
|
|
|
|
|
// hex.c), so it's only refreshed when this is that specific camera.
|
|
|
|
|
|
|
|
static void refresh_camera(ClientContext* context, Camera* camera) {
|
|
|
|
|
|
|
|
camera_update_view(camera);
|
|
|
|
|
|
|
|
camera_sync_gpu(camera, &context->render);
|
|
|
|
|
|
|
|
if(camera == &context->camera) {
|
|
|
|
|
|
|
|
update_hex_picking_inverse(camera, &context->hex);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// camera.main() -> handle for the engine's one interactive camera
|
|
|
|
|
|
|
|
static int lua_camera_main(lua_State* L) {
|
|
|
|
|
|
|
|
push_camera_handle(L, ¤t_context(L)->camera, false);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// camera.create() -> handle for a new, script-owned camera (no render
|
|
|
|
|
|
|
|
// target yet - attach it to an overlay with h:attach, or give it one with
|
|
|
|
|
|
|
|
// nothing at all to leave it inert)
|
|
|
|
|
|
|
|
static int lua_camera_create(lua_State* L) {
|
|
|
|
ClientContext* context = current_context(L);
|
|
|
|
ClientContext* context = current_context(L);
|
|
|
|
context->camera.position[0] = luaL_checknumber(L, 1);
|
|
|
|
|
|
|
|
context->camera.position[1] = luaL_checknumber(L, 2);
|
|
|
|
Camera* camera = malloc(sizeof(Camera));
|
|
|
|
context->camera.position[2] = luaL_checknumber(L, 3);
|
|
|
|
memset(camera, 0, sizeof(Camera));
|
|
|
|
refresh_camera(context);
|
|
|
|
if(create_camera(&context->render, camera) != VK_SUCCESS) {
|
|
|
|
|
|
|
|
free(camera);
|
|
|
|
|
|
|
|
return luaL_error(L, "camera.create: failed to allocate camera buffers");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CameraLuaHandle* h = push_camera_handle(L, camera, true);
|
|
|
|
|
|
|
|
lua_pushvalue(L, -1);
|
|
|
|
|
|
|
|
h->self_ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// h:destroy() - only valid for camera.create()'d handles
|
|
|
|
|
|
|
|
static int lua_camera_destroy(lua_State* L) {
|
|
|
|
|
|
|
|
CameraLuaHandle* h = check_camera(L, 1);
|
|
|
|
|
|
|
|
if(!h->owned) {
|
|
|
|
|
|
|
|
return luaL_error(L, "camera:destroy: the main camera is engine-owned, not destroyable");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
ClientContext* context = current_context(L);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Don't leave any container pointing at memory this is about to free.
|
|
|
|
|
|
|
|
for(uint32_t i = 0; i < context->ui.max_containers; i++) {
|
|
|
|
|
|
|
|
if(context->ui.containers[i].camera == h->camera) {
|
|
|
|
|
|
|
|
context->ui.containers[i].camera = NULL;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
luaL_unref(L, LUA_REGISTRYINDEX, h->self_ref);
|
|
|
|
|
|
|
|
h->self_ref = LUA_NOREF;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
destroy_camera(&context->render, h->camera);
|
|
|
|
|
|
|
|
free(h->camera);
|
|
|
|
|
|
|
|
h->camera = NULL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// __gc - catches camera.create()'d handles a script never explicitly
|
|
|
|
|
|
|
|
// :destroy()'d. Runs for every camera userdata when lua_close collects the
|
|
|
|
|
|
|
|
// state (including camera.main()'s, hence the owned check), by which point
|
|
|
|
|
|
|
|
// destroy_ui_context has already unloaded every container, so there's no
|
|
|
|
|
|
|
|
// container->camera pointer left to scrub the way lua_camera_destroy does.
|
|
|
|
|
|
|
|
static int lua_camera_gc(lua_State* L) {
|
|
|
|
|
|
|
|
CameraLuaHandle* h = luaL_checkudata(L, 1, CAMERA_LUA_META);
|
|
|
|
|
|
|
|
if(!h->owned || h->camera == NULL) {
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
destroy_camera(¤t_context(L)->render, h->camera);
|
|
|
|
|
|
|
|
free(h->camera);
|
|
|
|
|
|
|
|
h->camera = NULL;
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// camera.get_position() -> x, y, z
|
|
|
|
// h:attach(overlay) - the overlay's region/z-order becomes this camera's
|
|
|
|
|
|
|
|
// viewport and draw order (see container_set_camera, ui.h)
|
|
|
|
|
|
|
|
static int lua_camera_attach(lua_State* L) {
|
|
|
|
|
|
|
|
CameraLuaHandle* h = check_camera(L, 1);
|
|
|
|
|
|
|
|
Container* c = ui_lua_check_overlay(L, 2);
|
|
|
|
|
|
|
|
container_set_camera(c, h->camera);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// h:detach(overlay)
|
|
|
|
|
|
|
|
static int lua_camera_detach(lua_State* L) {
|
|
|
|
|
|
|
|
check_camera(L, 1);
|
|
|
|
|
|
|
|
Container* c = ui_lua_check_overlay(L, 2);
|
|
|
|
|
|
|
|
container_set_camera(c, NULL);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// h:set_position(x, y, z)
|
|
|
|
|
|
|
|
static int lua_camera_set_position(lua_State* L) {
|
|
|
|
|
|
|
|
CameraLuaHandle* h = check_camera(L, 1);
|
|
|
|
|
|
|
|
h->camera->position[0] = luaL_checknumber(L, 2);
|
|
|
|
|
|
|
|
h->camera->position[1] = luaL_checknumber(L, 3);
|
|
|
|
|
|
|
|
h->camera->position[2] = luaL_checknumber(L, 4);
|
|
|
|
|
|
|
|
refresh_camera(current_context(L), h->camera);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// h:get_position() -> x, y, z
|
|
|
|
static int lua_camera_get_position(lua_State* L) {
|
|
|
|
static int lua_camera_get_position(lua_State* L) {
|
|
|
|
Camera* camera = ¤t_context(L)->camera;
|
|
|
|
CameraLuaHandle* h = check_camera(L, 1);
|
|
|
|
lua_pushnumber(L, camera->position[0]);
|
|
|
|
lua_pushnumber(L, h->camera->position[0]);
|
|
|
|
lua_pushnumber(L, camera->position[1]);
|
|
|
|
lua_pushnumber(L, h->camera->position[1]);
|
|
|
|
lua_pushnumber(L, camera->position[2]);
|
|
|
|
lua_pushnumber(L, h->camera->position[2]);
|
|
|
|
return 3;
|
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// camera.set_rotation(yaw, pitch)
|
|
|
|
// h:set_rotation(yaw, pitch)
|
|
|
|
static int lua_camera_set_rotation(lua_State* L) {
|
|
|
|
static int lua_camera_set_rotation(lua_State* L) {
|
|
|
|
ClientContext* context = current_context(L);
|
|
|
|
CameraLuaHandle* h = check_camera(L, 1);
|
|
|
|
context->camera.rotation[0] = luaL_checknumber(L, 1);
|
|
|
|
h->camera->rotation[0] = luaL_checknumber(L, 2);
|
|
|
|
context->camera.rotation[1] = luaL_checknumber(L, 2);
|
|
|
|
h->camera->rotation[1] = luaL_checknumber(L, 3);
|
|
|
|
refresh_camera(context);
|
|
|
|
refresh_camera(current_context(L), h->camera);
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// camera.get_rotation() -> yaw, pitch
|
|
|
|
// h:get_rotation() -> yaw, pitch
|
|
|
|
static int lua_camera_get_rotation(lua_State* L) {
|
|
|
|
static int lua_camera_get_rotation(lua_State* L) {
|
|
|
|
Camera* camera = ¤t_context(L)->camera;
|
|
|
|
CameraLuaHandle* h = check_camera(L, 1);
|
|
|
|
lua_pushnumber(L, camera->rotation[0]);
|
|
|
|
lua_pushnumber(L, h->camera->rotation[0]);
|
|
|
|
lua_pushnumber(L, camera->rotation[1]);
|
|
|
|
lua_pushnumber(L, h->camera->rotation[1]);
|
|
|
|
return 2;
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// camera.set_distance(d)
|
|
|
|
// h:set_distance(d)
|
|
|
|
static int lua_camera_set_distance(lua_State* L) {
|
|
|
|
static int lua_camera_set_distance(lua_State* L) {
|
|
|
|
ClientContext* context = current_context(L);
|
|
|
|
CameraLuaHandle* h = check_camera(L, 1);
|
|
|
|
context->camera.distance = luaL_checknumber(L, 1);
|
|
|
|
h->camera->distance = luaL_checknumber(L, 2);
|
|
|
|
refresh_camera(context);
|
|
|
|
refresh_camera(current_context(L), h->camera);
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// camera.get_distance() -> d
|
|
|
|
// h:get_distance() -> d
|
|
|
|
static int lua_camera_get_distance(lua_State* L) {
|
|
|
|
static int lua_camera_get_distance(lua_State* L) {
|
|
|
|
lua_pushnumber(L, current_context(L)->camera.distance);
|
|
|
|
CameraLuaHandle* h = check_camera(L, 1);
|
|
|
|
|
|
|
|
lua_pushnumber(L, h->camera->distance);
|
|
|
|
return 1;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// h:set_aspect(width, height) - projection is derived from an explicit
|
|
|
|
|
|
|
|
// aspect rather than read off whatever the camera is attached to (camera.c
|
|
|
|
|
|
|
|
// stays decoupled from ui.h), so scripts set it once after attaching (and
|
|
|
|
|
|
|
|
// again on resize, for anything that tracks the window)
|
|
|
|
|
|
|
|
static int lua_camera_set_aspect(lua_State* L) {
|
|
|
|
|
|
|
|
CameraLuaHandle* h = check_camera(L, 1);
|
|
|
|
|
|
|
|
float width = luaL_checknumber(L, 2);
|
|
|
|
|
|
|
|
float height = luaL_checknumber(L, 3);
|
|
|
|
|
|
|
|
camera_update_proj(h->camera, width/height);
|
|
|
|
|
|
|
|
camera_sync_gpu(h->camera, ¤t_context(L)->render);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void editor_lua_register(lua_State* L, ClientContext* context) {
|
|
|
|
void editor_lua_register(lua_State* L, ClientContext* context) {
|
|
|
|
lua_pushlightuserdata(L, context);
|
|
|
|
lua_pushlightuserdata(L, context);
|
|
|
|
lua_setfield(L, LUA_REGISTRYINDEX, EDITOR_LUA_CONTEXT);
|
|
|
|
lua_setfield(L, LUA_REGISTRYINDEX, EDITOR_LUA_CONTEXT);
|
|
|
|
|
|
|
|
|
|
|
|
static const luaL_Reg camera_funcs[] = {
|
|
|
|
static const luaL_Reg camera_funcs[] = {
|
|
|
|
|
|
|
|
{"main", lua_camera_main},
|
|
|
|
|
|
|
|
{"create", lua_camera_create},
|
|
|
|
|
|
|
|
{NULL, NULL},
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
luaL_newlib(L, camera_funcs);
|
|
|
|
|
|
|
|
lua_setglobal(L, "camera");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static const luaL_Reg camera_methods[] = {
|
|
|
|
|
|
|
|
{"destroy", lua_camera_destroy},
|
|
|
|
|
|
|
|
{"attach", lua_camera_attach},
|
|
|
|
|
|
|
|
{"detach", lua_camera_detach},
|
|
|
|
{"set_position", lua_camera_set_position},
|
|
|
|
{"set_position", lua_camera_set_position},
|
|
|
|
{"get_position", lua_camera_get_position},
|
|
|
|
{"get_position", lua_camera_get_position},
|
|
|
|
{"set_rotation", lua_camera_set_rotation},
|
|
|
|
{"set_rotation", lua_camera_set_rotation},
|
|
|
|
{"get_rotation", lua_camera_get_rotation},
|
|
|
|
{"get_rotation", lua_camera_get_rotation},
|
|
|
|
{"set_distance", lua_camera_set_distance},
|
|
|
|
{"set_distance", lua_camera_set_distance},
|
|
|
|
{"get_distance", lua_camera_get_distance},
|
|
|
|
{"get_distance", lua_camera_get_distance},
|
|
|
|
|
|
|
|
{"set_aspect", lua_camera_set_aspect},
|
|
|
|
{NULL, NULL},
|
|
|
|
{NULL, NULL},
|
|
|
|
};
|
|
|
|
};
|
|
|
|
luaL_newlib(L, camera_funcs);
|
|
|
|
luaL_newmetatable(L, CAMERA_LUA_META);
|
|
|
|
lua_setglobal(L, "camera");
|
|
|
|
luaL_newlib(L, camera_methods);
|
|
|
|
|
|
|
|
lua_setfield(L, -2, "__index");
|
|
|
|
|
|
|
|
lua_pushcfunction(L, lua_camera_gc);
|
|
|
|
|
|
|
|
lua_setfield(L, -2, "__gc");
|
|
|
|
|
|
|
|
lua_pop(L, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|