mirror of
https://github.com/Adamcake/Bolt.git
synced 2026-04-20 00:46:53 -04:00
library: animation and bone APIs
This commit is contained in:
@@ -86,6 +86,8 @@ static size_t _bolt_gl_plugin_drawelements_vertex3d_atlas_meta(size_t index, voi
|
||||
static void _bolt_gl_plugin_drawelements_vertex3d_meta_xywh(size_t meta, void* userdata, int32_t* out);
|
||||
static void _bolt_gl_plugin_drawelements_vertex3d_uv(size_t index, void* userdata, double* out);
|
||||
static void _bolt_gl_plugin_drawelements_vertex3d_colour(size_t index, void* userdata, double* out);
|
||||
static uint8_t _bolt_gl_plugin_drawelements_vertex3d_boneid(size_t index, void* userdata);
|
||||
static void _bolt_gl_plugin_bone_transform(uint8_t bone_id, void* userdata, double* out);
|
||||
static void _bolt_gl_plugin_matrix3d_toworldspace(int x, int y, int z, void* userdata, double* out);
|
||||
static void _bolt_gl_plugin_matrix3d_toscreenspace(int x, int y, int z, void* userdata, double* out);
|
||||
static void _bolt_gl_plugin_matrix3d_worldpos(void* userdata, double* out);
|
||||
@@ -606,6 +608,7 @@ static GLuint _bolt_glCreateProgram() {
|
||||
program->loc_uTextureAtlasSettings = -1;
|
||||
program->loc_uAtlasMeta = -1;
|
||||
program->loc_uModelMatrix = -1;
|
||||
program->loc_uBoneTransforms = -1;
|
||||
program->loc_uGridSize = -1;
|
||||
program->loc_uVertexScale = -1;
|
||||
program->loc_sSceneHDRTex = -1;
|
||||
@@ -666,6 +669,7 @@ static void _bolt_glLinkProgram(GLuint program) {
|
||||
const GLint loc_uVertexScale = gl.GetUniformLocation(program, "uVertexScale");
|
||||
p->loc_sSceneHDRTex = gl.GetUniformLocation(program, "sSceneHDRTex");
|
||||
p->loc_sSourceTex = gl.GetUniformLocation(program, "sSourceTex");
|
||||
p->loc_uBoneTransforms = gl.GetUniformLocation(program, "uBoneTransforms");
|
||||
|
||||
const GLchar* view_var_names[] = {"uCameraPosition", "uViewProjMatrix"};
|
||||
const GLuint block_index_ViewTransforms = gl.GetUniformBlockIndex(program, "ViewTransforms");
|
||||
@@ -1375,12 +1379,15 @@ void _bolt_gl_onDrawElements(GLenum mode, GLsizei count, GLenum type, const void
|
||||
|
||||
struct Render3D render;
|
||||
render.vertex_count = count;
|
||||
render.is_animated = c->bound_program->loc_uBoneTransforms != -1;
|
||||
render.vertex_functions.userdata = &vertex_userdata;
|
||||
render.vertex_functions.xyz = _bolt_gl_plugin_drawelements_vertex3d_xyz;
|
||||
render.vertex_functions.atlas_meta = _bolt_gl_plugin_drawelements_vertex3d_atlas_meta;
|
||||
render.vertex_functions.atlas_xywh = _bolt_gl_plugin_drawelements_vertex3d_meta_xywh;
|
||||
render.vertex_functions.uv = _bolt_gl_plugin_drawelements_vertex3d_uv;
|
||||
render.vertex_functions.colour = _bolt_gl_plugin_drawelements_vertex3d_colour;
|
||||
render.vertex_functions.bone_id = _bolt_gl_plugin_drawelements_vertex3d_boneid;
|
||||
render.vertex_functions.bone_transform = _bolt_gl_plugin_bone_transform;
|
||||
render.texture_functions.userdata = &tex_userdata;
|
||||
render.texture_functions.id = _bolt_gl_plugin_texture_id;
|
||||
render.texture_functions.size = _bolt_gl_plugin_texture_size;
|
||||
@@ -1588,6 +1595,42 @@ static void _bolt_gl_plugin_drawelements_vertex3d_colour(size_t index, void* use
|
||||
out[3] = (double)colour[0];
|
||||
}
|
||||
|
||||
static uint8_t _bolt_gl_plugin_drawelements_vertex3d_boneid(size_t index, void* userdata) {
|
||||
struct GLPluginDrawElementsVertex3DUserData* data = userdata;
|
||||
uint32_t ret[4];
|
||||
if (!_bolt_get_attr_binding_int(data->c, data->xyz_bone, data->indices[index], 4, (int32_t*)&ret)) {
|
||||
float retf[4];
|
||||
_bolt_get_attr_binding(data->c, data->xyz_bone, data->indices[index], 4, retf);
|
||||
return (uint8_t)(uint32_t)(int32_t)roundf(retf[3]);
|
||||
}
|
||||
return (uint8_t)(ret[3]);
|
||||
}
|
||||
|
||||
static void _bolt_gl_plugin_bone_transform(uint8_t bone_id, void* userdata, double* out) {
|
||||
struct GLContext* c = _bolt_context();
|
||||
const GLint uniform_loc = c->bound_program->loc_uBoneTransforms + (bone_id * 3);
|
||||
GLfloat f[4];
|
||||
gl.GetUniformfv(c->bound_program->id, uniform_loc, f);
|
||||
out[0] = (double)f[0];
|
||||
out[1] = (double)f[1];
|
||||
out[2] = (double)f[2];
|
||||
out[3] = 0.0;
|
||||
out[4] = (double)f[3];
|
||||
gl.GetUniformfv(c->bound_program->id, uniform_loc + 1, f);
|
||||
out[5] = (double)f[0];
|
||||
out[6] = (double)f[1];
|
||||
out[7] = 0.0;
|
||||
out[8] = (double)f[2];
|
||||
out[9] = (double)f[3];
|
||||
gl.GetUniformfv(c->bound_program->id, uniform_loc + 2, f);
|
||||
out[10] = (double)f[0];
|
||||
out[11] = 0.0;
|
||||
out[12] = (double)f[1];
|
||||
out[13] = (double)f[2];
|
||||
out[14] = (double)f[3];
|
||||
out[15] = 1.0;
|
||||
}
|
||||
|
||||
static void _bolt_gl_plugin_matrix3d_toworldspace(int x, int y, int z, void* userdata, double* out) {
|
||||
const struct GLPlugin3DMatrixUserData* data = userdata;
|
||||
const double dx = (double)x;
|
||||
|
||||
@@ -193,6 +193,7 @@ struct GLProgram {
|
||||
GLint loc_uTextureAtlasSettings;
|
||||
GLint loc_uAtlasMeta;
|
||||
GLint loc_uModelMatrix;
|
||||
GLint loc_uBoneTransforms;
|
||||
GLint loc_uGridSize;
|
||||
GLint loc_uVertexScale;
|
||||
GLint loc_sSceneHDRTex;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
#include <lualib.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
@@ -628,7 +629,7 @@ uint8_t _bolt_plugin_add(const char* path, struct Plugin* plugin) {
|
||||
PUSHSTRING(plugin->state, RENDER3D_META_REGISTRYNAME);
|
||||
lua_newtable(plugin->state);
|
||||
PUSHSTRING(plugin->state, "__index");
|
||||
lua_createtable(plugin->state, 0, 14);
|
||||
lua_createtable(plugin->state, 0, 17);
|
||||
API_ADD_SUB(plugin->state, vertexcount, render3d)
|
||||
API_ADD_SUB(plugin->state, vertexxyz, render3d)
|
||||
API_ADD_SUB(plugin->state, vertexmeta, render3d)
|
||||
@@ -642,6 +643,9 @@ uint8_t _bolt_plugin_add(const char* path, struct Plugin* plugin) {
|
||||
API_ADD_SUB(plugin->state, toworldspace, render3d)
|
||||
API_ADD_SUB(plugin->state, toscreenspace, render3d)
|
||||
API_ADD_SUB(plugin->state, worldposition, render3d)
|
||||
API_ADD_SUB(plugin->state, vertexbone, render3d)
|
||||
API_ADD_SUB(plugin->state, bonetransforms, render3d)
|
||||
API_ADD_SUB(plugin->state, animated, render3d)
|
||||
API_ADD_SUB_ALIAS(plugin->state, vertexcolour, vertexcolor, render3d)
|
||||
lua_settable(plugin->state, -3);
|
||||
lua_settable(plugin->state, LUA_REGISTRYINDEX);
|
||||
@@ -1554,6 +1558,69 @@ static int api_render3d_worldposition(lua_State* state) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
static int api_render3d_vertexbone(lua_State* state) {
|
||||
_bolt_check_argc(state, 2, "render3d_vertexbone");
|
||||
struct Render3D* render = lua_touserdata(state, 1);
|
||||
const int index = lua_tointeger(state, 2);
|
||||
uint8_t ret = render->vertex_functions.bone_id(index, render->vertex_functions.userdata);
|
||||
lua_pushinteger(state, ret);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int api_render3d_bonetransforms(lua_State* state) {
|
||||
_bolt_check_argc(state, 2, "render3d_bonetransforms");
|
||||
struct Render3D* render = lua_touserdata(state, 1);
|
||||
const int bone_id = lua_tointeger(state, 2);
|
||||
|
||||
if (!render->is_animated) {
|
||||
PUSHSTRING(state, "render3d_bonetransforms: cannot get bone transforms for non-animated model");
|
||||
lua_error(state);
|
||||
}
|
||||
|
||||
// not a mistake - game's shader supports values from 0 to 128, inclusive
|
||||
if (bone_id < 0 || bone_id > 128) {
|
||||
PUSHSTRING(state, "render3d_bonetransforms: invalid bone ID");
|
||||
lua_error(state);
|
||||
}
|
||||
|
||||
#define LENGTH(N1, N2, N3) sqrt((N1 * N1) + (N2 * N2) + (N3 * N3))
|
||||
double transform[16];
|
||||
render->vertex_functions.bone_transform((uint8_t)bone_id, render->vertex_functions.userdata, transform);
|
||||
lua_pushnumber(state, transform[12]);
|
||||
lua_pushnumber(state, transform[13]);
|
||||
lua_pushnumber(state, transform[14]);
|
||||
const double scale_x = LENGTH(transform[0], transform[1], transform[2]);
|
||||
const double scale_y = LENGTH(transform[4], transform[5], transform[6]);
|
||||
const double scale_z = LENGTH(transform[8], transform[9], transform[10]);
|
||||
lua_pushnumber(state, scale_x);
|
||||
lua_pushnumber(state, scale_y);
|
||||
lua_pushnumber(state, scale_z);
|
||||
if (scale_x == 0.0 || scale_y == 0.0 || scale_z == 0.0) {
|
||||
// scale=0 means we can't calculate angles because there would be a division by zero,
|
||||
// and angle is meaningless for a zero-scale model anyway, so just put 0 for all the angles
|
||||
lua_pushnumber(state, 0.0);
|
||||
lua_pushnumber(state, 0.0);
|
||||
lua_pushnumber(state, 0.0);
|
||||
} else {
|
||||
// see https://stackoverflow.com/questions/39128589/decomposing-rotation-matrix-x-y-z-cartesian-angles
|
||||
const double yaw = atan2(-(transform[8] / scale_z), sqrt((transform[0] * transform[0] / (scale_x * scale_x)) + (transform[4] * transform[4] / (scale_y * scale_y))));
|
||||
const double pitch = atan2((transform[9] / scale_z) / cos(yaw), (transform[10] / scale_z) / cos(yaw));
|
||||
const double roll = atan2((transform[4] / scale_y) / cos(yaw), (transform[0] / scale_x) / cos(yaw));
|
||||
lua_pushnumber(state, yaw);
|
||||
lua_pushnumber(state, pitch);
|
||||
lua_pushnumber(state, roll);
|
||||
}
|
||||
return 9;
|
||||
#undef LENGTH
|
||||
}
|
||||
|
||||
static int api_render3d_animated(lua_State* state) {
|
||||
_bolt_check_argc(state, 1, "render3d_animated");
|
||||
struct Render3D* render = lua_touserdata(state, 1);
|
||||
lua_pushboolean(state, render->is_animated);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int api_resizeevent_size(lua_State* state) {
|
||||
_bolt_check_argc(state, 1, "resizeevent_size");
|
||||
struct ResizeEvent* event = lua_touserdata(state, 1);
|
||||
|
||||
@@ -58,6 +58,12 @@ struct Vertex3DFunctions {
|
||||
|
||||
/// Returns the RGBA colour of this vertex, each one normalised from 0.0 to 1.0.
|
||||
void (*colour)(size_t index, void* userdata, double* out);
|
||||
|
||||
/// Returns the ID of the bone this vertex belongs to.
|
||||
uint8_t (*bone_id)(size_t index, void* userdata);
|
||||
|
||||
/// Returns the transform matrix for the given bone, as 16 doubles.
|
||||
void (*bone_transform)(uint8_t bone_id, void* userdata, double* out);
|
||||
};
|
||||
|
||||
/// Struct containing "vtable" callback information for textures.
|
||||
@@ -199,6 +205,7 @@ struct RenderBatch2D {
|
||||
|
||||
struct Render3D {
|
||||
uint32_t vertex_count;
|
||||
uint8_t is_animated;
|
||||
struct Vertex3DFunctions vertex_functions;
|
||||
struct TextureFunctions texture_functions;
|
||||
struct Render3DMatrixFunctions matrix_functions;
|
||||
|
||||
@@ -484,7 +484,9 @@ static int api_window_onscroll(lua_State*);
|
||||
static int api_render3d_vertexcount(lua_State*);
|
||||
|
||||
/// [-2, +3, -]
|
||||
/// Given an index of a vertex in a model, returns its X Y and Z in model coordinates.
|
||||
/// Given an index of a vertex in a model, returns its X Y and Z in model coordinates. More
|
||||
/// specifically, this is the default position of this vertex in the model - it is not affected by
|
||||
/// any kind of scaling, rotation, movement, or animation that may be happening to the model.
|
||||
static int api_render3d_vertexxyz(lua_State*);
|
||||
|
||||
/// [-2, +1, -]
|
||||
@@ -571,6 +573,39 @@ static int api_render3d_toscreenspace(lua_State*);
|
||||
/// Equivalent to `render:toworldspace(0, 0, 0)`
|
||||
static int api_render3d_worldposition(lua_State*);
|
||||
|
||||
/// [-1, +1, -]
|
||||
/// Returns the bone ID of this vertex. Animated models have multiple bones which can move
|
||||
/// independently of each other, and this function can be used to find out which bone a vertex
|
||||
/// belongs to. The returned value may be any integer from 0 to 255, although the game engine
|
||||
/// actually seems to be unable to handle indices higher than 128. (128 itself is valid.)
|
||||
///
|
||||
/// All vertices have bone IDs, even in non-animated models, so plugins may call this function
|
||||
/// regardless of whether the model is animated or not. For a non-animated model the bone ID seems
|
||||
/// to be meaningless and is usually 0. To check if the model is animated, use `animated()`.
|
||||
static int api_render3d_vertexbone(lua_State*);
|
||||
|
||||
/// [-1, +9, -]
|
||||
/// Given a bone ID, returns the following nine floating-point values in this order: translation X,
|
||||
/// Y and Z, in model coordinates; scale factor X, Y and Z; yaw, pitch, and roll, in radians. These
|
||||
/// values represent the animation state of this bone during this render.
|
||||
///
|
||||
/// It is a fatal error to call this function on a render event for a non-animated model, since
|
||||
/// non-animated models have no bone transforms that could be queried. To check if the model is
|
||||
/// animated, use `animated()`.
|
||||
///
|
||||
/// The results of this function are imperfect for two reasons. Firstly, the engine doesn't use
|
||||
/// static animation frames; instead it interpolates between multiple frames, so the exact state of
|
||||
/// animation will depend on the user's FPS. Secondly, these values get pre-multiplied into one big
|
||||
/// matrix by the time Bolt can access them. Bolt decomposes the matrix back to its original values
|
||||
/// but there will be some loss of precision.
|
||||
static int api_render3d_bonetransforms(lua_State*);
|
||||
|
||||
/// [-1, +1, -]
|
||||
/// Returns a boolean value indicating whether this model is animated. Animated models can have
|
||||
/// multiple bones which can move independently of each other. For more information on bones, see
|
||||
/// `vertexbone()` and `bonetransforms()`.
|
||||
static int api_render3d_animated(lua_State*);
|
||||
|
||||
/// [-1, +2, -]
|
||||
/// Returns the new width and height that the window was resized to.
|
||||
static int api_resizeevent_size(lua_State*);
|
||||
|
||||
Reference in New Issue
Block a user