diff --git a/src/library/dll/main.c b/src/library/dll/main.c index 49af3ee..cf7e716 100644 --- a/src/library/dll/main.c +++ b/src/library/dll/main.c @@ -51,6 +51,7 @@ static void hook_glTexSubImage2D(GLenum, GLint, GLint, GLint, GLsizei, GLsizei, static void hook_glDeleteTextures(GLsizei, const GLuint*); static void hook_glClear(GLbitfield); static void hook_glViewport(GLint, GLint, GLsizei, GLsizei); +static void hook_glTexParameteri(GLenum, GLenum, GLint); static HGLRC __stdcall hook_wglCreateContextAttribsARB(HDC, HGLRC, const int*); @@ -105,7 +106,7 @@ DWORD __stdcall BOLT_STUB_ENTRYNAME(struct PluginInjectParams* data) { libgl.GenTextures = (void(*)(GLsizei, GLuint*))data->pGetProcAddress(libgl_module, "glGenTextures"); libgl.GetError = (GLenum(*)(void))data->pGetProcAddress(libgl_module, "glGetError"); libgl.ReadPixels = (void(*)(GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, void*))data->pGetProcAddress(libgl_module, "glReadPixels"); - libgl.TexParameteri = (void(*)(GLenum, GLenum, GLfloat))data->pGetProcAddress(libgl_module, "glTexParameteri"); + libgl.TexParameteri = (void(*)(GLenum, GLenum, GLint))data->pGetProcAddress(libgl_module, "glTexParameteri"); libgl.TexSubImage2D = (void(*)(GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, const void*))data->pGetProcAddress(libgl_module, "glTexSubImage2D"); libgl.Viewport = (void(*)(GLint, GLint, GLsizei, GLsizei))data->pGetProcAddress(libgl_module, "glViewport"); @@ -135,6 +136,7 @@ DWORD __stdcall BOLT_STUB_ENTRYNAME(struct PluginInjectParams* data) { FNHOOK(glDrawArrays) FNHOOK(glTexSubImage2D) FNHOOK(glDeleteTextures) + FNHOOK(glTexParameteri) FNHOOK(glClear) FNHOOK(glViewport) } @@ -388,6 +390,13 @@ static void hook_glDeleteTextures(GLsizei n, const GLuint* textures) { LOG("glDeleteTextures end\n"); } +static void hook_glTexParameteri(GLenum target, GLenum pname, GLint param) { + LOG("glTexParameteri\n"); + libgl.TexParameteri(target, pname, param); + _bolt_gl_onTexParameteri(target, pname, param); + LOG("glTexParameteri end\n"); +} + static void hook_glClear(GLbitfield mask) { LOG("glClear\n"); libgl.Clear(mask); diff --git a/src/library/doc.texi b/src/library/doc.texi index 40a24a5..3aee81c 100644 --- a/src/library/doc.texi +++ b/src/library/doc.texi @@ -566,6 +566,26 @@ end) It's @strong{not safe} to use an event object after the event handler has returned. +@node functions-onrendericon +@section onrendericon + +Sets an event handler for item icon render events. The function will +overwrite any previous event handler, or, if it's not a function, then +any previous event handler will be deleted. See +@ref{objects-renderitemicon,, Render Item Icon Event} for documentation +on this event object. + +@example lua +@verbatim +bolt.onrendericon(function (event) + -- ... +end) +@end verbatim +@end example + +It's @strong{not safe} to use an event object after the event handler +has returned. + @node functions-onminimap @section onminimap @@ -2039,15 +2059,16 @@ vertex coordinates and texture data can be queried, along with its animation data if the model is animated. Bolt has a complex set of functions for doing calculations with 3D space -data. Render3D's @ref{render3d-vertexxyz,,vertexxyz} function returns -static model data which will always be the same for two instances of the -same model even if they're placed differently, scaled or rotated -differently, doing different animations, etc. To get the model's -position in world space, use @ref{render3d-modelmatrix,,modelmatrix}: +data. Render3D's @ref{render3d-vertexpoint,,vertexpoint} function +returns static model data which will always be the same for two +instances of the same model even if they're placed differently, scaled +or rotated differently, doing different animations, etc. To get the +model's position in world space, use @ref{render3d-modelmatrix,, +modelmatrix}: @example lua @verbatim -local modelpoint = myevent:vertexxyz(1) +local modelpoint = myevent:vertexpoint(1) local worldpoint = modelpoint:transform(myevent:modelmatrix()) @end verbatim @end example @@ -2072,7 +2093,7 @@ must be applied before any of the other transforms. @example lua @verbatim -local modelpoint = myevent:vertexxyz(1) +local modelpoint = myevent:vertexpoint(1) local animatedpoint = modelpoint:transform(myevent:vertexanimation(1)) local worldpoint = animatedpoint:transform(myevent:modelmatrix()) @end verbatim @@ -2117,8 +2138,8 @@ local count = event:vertexcount() @end verbatim @end example -@node render3d-vertexxyz -@subsection vertexxyz +@node render3d-vertexpoint +@subsection vertexpoint Given a vertex number, returns a @ref{objects-point,,Point object} representing the vertex's model coordinates, according to the static @@ -2126,7 +2147,7 @@ model data. @example lua @verbatim -local modelpoint = event:vertexxyz(1) +local modelpoint = event:vertexpoint(1) @end verbatim @end example @@ -2140,7 +2161,7 @@ points from model coordinates into world coordinates. @example lua @verbatim local modelmatrix = event:modelmatrix() -local modelpoint = event:vertexxyz(1) +local modelpoint = event:vertexpoint(1) local worldpoint = modelpoint:transform(modelmatrix) @end verbatim @end example @@ -2319,6 +2340,162 @@ end @end verbatim @end example +@node objects-renderitemicon +@section Render Item Icon Event + +A render-item-icon event comes from @ref{functions-onrendericon}. It +occurs when an item icon is rendered to the game view. Item icons refer +to items appearing in the UI, such as the player's inventory, equipment +screen, or action bar slots. These appear to be 2D images but in reality +they're "snapshots" of the item's 3D models, which can make them very +difficult to work with compared to a normal 2D image. Bolt attempts to +simplify this by storing some of the details of the models rendered at +the time when the icon is created, and allows plugins to query this +stored data. However this means that some data is missing compared to +a Render3D event; most notably, the texture data is currently not saved. + +An icon may contain any number of models, and each vertex in each model +may be queried using the functions of this event. + +@example lua +@verbatim +bolt.onrendericon(function (event) + for model = 1, event:modelcount() do + for vertex = 1, event:modelvertexcount(model) do + let point = event:modelvertexpoint(model, vertex) + -- ... + end + end +end) +@end verbatim +@end example + +The view and projection matrix used when pre-rendering the item can also +be queried for each model. There's no model matrix for this process, so +the model coordinates are treated as if they were the world coordinates. +Some items, especially most weapons' off-hand variants, use the same +item model viewed from a different camera angle, which means the only +way to differentiate between them is to @ref{transform-decompose} the +view matrix. + +It's @strong{not safe} to use an event object after the event handler +has returned. + +@node renderitemicon-xywh +@subsection xywh + +Returns the x, y, width and height of where this icon is being drawn on +the screen, in pixel coordinates. The X and Y are relative to the +top-left pixel of the inner area of the window. + +@example lua +@verbatim +local x, y, width, height = event:xywh() +@end verbatim +@end example + +@node renderitemicon-modelcount +@subsection modelcount + +Returns the number of 3D models that were rendered to this icon. + +@example lua +@verbatim +for model = 1, event:modelcount() do + -- ... +end +@end verbatim +@end example + +@node renderitemicon-modelvertexcount +@subsection modelvertexcount + +Given a model index, returns the number of vertices in that model. + +@example lua +@verbatim +for model = 1, event:modelcount() do + for vertex = 1, event:modelvertexcount(model) do + -- ... + end +end +@end verbatim +@end example + +@node renderitemicon-modelvertexpoint +@subsection modelvertexpoint + +Given a model number and vertex number, returns a @ref{objects-point} +representing the vertex's model coordinates. + +@example lua +@verbatim +local point = event:modelvertexpoint(model, vertex) +@end verbatim +@end example + +@node renderitemicon-modelvertexcolour +@subsection modelvertexcolour + +Given a model number and a vertex number, returns the red, green, blue +and alpha values for that vertex, in that order. All four values will be +floating-point numbers in the range 0.0 - 1.0. + +These values are multiplied with the texture, so values less than 1.0 +will darken the texture (or make it more transparent in the case of the +alpha channel). A common use for this is to draw differently "tinted" +versions of the same model using the same texture. + +@example lua +@verbatim +local red, green, blue, alpha = event:modelvertexcolour(model, vertex) +@end verbatim +@end example + +@node renderitemicon-modelvertexcolor +@subsection modelvertexcolor + +Alias for @ref{renderitemicon-modelvertexcolour} + +@node renderitemicon-modelviewmatrix +@subsection modelviewmatrix + +Given a model number, returns a @ref{objects-transform} representing the +view matrix that was used to render it. + +@example lua +@verbatim +local viewmatrix = event:modelviewmatrix(model) +@end verbatim +@end example + +@node renderitemicon-modelprojectionmatrix +@subsection modelprojectionmatrix + +Given a model number, returns a @ref{objects-transform} representing the +projection matrix that was used to render it. + +@example lua +@verbatim +local projmatrix = event:modelprojectionmatrix(model) +@end verbatim +@end example + +@node renderitemicon-modelviewprojmatrix +@subsection modelviewprojmatrix + +Given a model number, returns a @ref{objects-transform} representing the +combined view and projection matrices, commonly called the "viewproj +matrix", that was used to render that model. These are pre-multiplied +in the shader, so using the viewproj matrix is more efficient than using +the view and projection matrices separately. + +@example lua +@verbatim +local viewprojmatrix = event:modelviewprojmatrix(model) +@end verbatim +@end example + @node objects-minimap @section Minimap Event @@ -2393,7 +2570,7 @@ has returned. Returns the new x, y, width and height that the window was repositioned to. -@example +@example lua @verbatim local newx, newy, newwidth, newheight = event:xywh() @end verbatim @@ -2405,7 +2582,7 @@ local newx, newy, newwidth, newheight = event:xywh() Returns a boolean value indicating whether the window changed size. If true, the contents of the window were cleared and need to be redrawn. -@example +@example lua @verbatim if event:didresize() then -- re-draw something to the window @@ -2433,7 +2610,7 @@ has returned. Returns the x and y for this mouse event in pixels, relative to the top-left of the window that it relates to. -@example +@example lua @verbatim local x, y = event:xy() @end verbatim @@ -2445,7 +2622,7 @@ local x, y = event:xy() Returns a boolean value indicating whether @code{ctrl} was held when this event was fired. -@example +@example lua @verbatim local ctrl = event:ctrl() @end verbatim @@ -2457,7 +2634,7 @@ local ctrl = event:ctrl() Returns a boolean value indicating whether @code{shift} was held when this event was fired. -@example +@example lua @verbatim local shift = event:shift() @end verbatim @@ -2470,7 +2647,7 @@ Returns a boolean value indicating whether the meta key (also known as super, command, or the "windows key") was held when this event was fired. -@example +@example lua @verbatim local meta = event:meta() @end verbatim @@ -2482,7 +2659,7 @@ local meta = event:meta() Returns a boolean value indicating whether @code{alt} was held when this event was fired. -@example +@example lua @verbatim local alt = event:alt() @end verbatim @@ -2494,7 +2671,7 @@ local alt = event:alt() Returns a boolean value indicating whether caps lock was on when this event was fired. -@example +@example lua @verbatim local capslock = event:capslock() @end verbatim @@ -2506,7 +2683,7 @@ local capslock = event:capslock() Returns a boolean value indicating whether numlock was on when this event was fired. -@example +@example lua @verbatim local numlock = event:numlock() @end verbatim @@ -2519,7 +2696,7 @@ Returns three boolean values indicating whether each primary mouse button was held when this event fired, in the order: left, right, middle. -@example +@example lua @verbatim local lmb, rmb, mmb = event:buttons() @end verbatim @@ -2548,7 +2725,7 @@ has returned. Returns the x and y for this mouse event in pixels, relative to the top-left of the window that it relates to. -@example +@example lua @verbatim local x, y = event:xy() @end verbatim @@ -2560,7 +2737,7 @@ local x, y = event:xy() Returns a boolean value indicating whether @code{ctrl} was held when this event was fired. -@example +@example lua @verbatim local ctrl = event:ctrl() @end verbatim @@ -2572,7 +2749,7 @@ local ctrl = event:ctrl() Returns a boolean value indicating whether @code{shift} was held when this event was fired. -@example +@example lua @verbatim local shift = event:shift() @end verbatim @@ -2585,7 +2762,7 @@ Returns a boolean value indicating whether the meta key (also known as super, command, or the "windows key") was held when this event was fired. -@example +@example lua @verbatim local meta = event:meta() @end verbatim @@ -2597,7 +2774,7 @@ local meta = event:meta() Returns a boolean value indicating whether @code{alt} was held when this event was fired. -@example +@example lua @verbatim local alt = event:alt() @end verbatim @@ -2609,7 +2786,7 @@ local alt = event:alt() Returns a boolean value indicating whether caps lock was on when this event was fired. -@example +@example lua @verbatim local capslock = event:capslock() @end verbatim @@ -2621,7 +2798,7 @@ local capslock = event:capslock() Returns a boolean value indicating whether numlock was on when this event was fired. -@example +@example lua @verbatim local numlock = event:numlock() @end verbatim @@ -2634,7 +2811,7 @@ Returns three boolean values indicating whether each primary mouse button was held when this event fired, in the order: left, right, middle. -@example +@example lua @verbatim local lmb, rmb, mmb = event:buttons() @end verbatim @@ -2664,7 +2841,7 @@ has returned. Returns the x and y for this mouse event in pixels, relative to the top-left of the window that it relates to. -@example +@example lua @verbatim local x, y = event:xy() @end verbatim @@ -2676,7 +2853,7 @@ local x, y = event:xy() Returns a boolean value indicating whether @code{ctrl} was held when this event was fired. -@example +@example lua @verbatim local ctrl = event:ctrl() @end verbatim @@ -2688,7 +2865,7 @@ local ctrl = event:ctrl() Returns a boolean value indicating whether @code{shift} was held when this event was fired. -@example +@example lua @verbatim local shift = event:shift() @end verbatim @@ -2701,7 +2878,7 @@ Returns a boolean value indicating whether the meta key (also known as super, command, or the "windows key") was held when this event was fired. -@example +@example lua @verbatim local meta = event:meta() @end verbatim @@ -2713,7 +2890,7 @@ local meta = event:meta() Returns a boolean value indicating whether @code{alt} was held when this event was fired. -@example +@example lua @verbatim local alt = event:alt() @end verbatim @@ -2725,7 +2902,7 @@ local alt = event:alt() Returns a boolean value indicating whether caps lock was on when this event was fired. -@example +@example lua @verbatim local capslock = event:capslock() @end verbatim @@ -2737,7 +2914,7 @@ local capslock = event:capslock() Returns a boolean value indicating whether numlock was on when this event was fired. -@example +@example lua @verbatim local numlock = event:numlock() @end verbatim @@ -2750,7 +2927,7 @@ Returns three boolean values indicating whether each primary mouse button was held when this event fired, in the order: left, right, middle. -@example +@example lua @verbatim local lmb, rmb, mmb = event:buttons() @end verbatim @@ -2763,7 +2940,7 @@ Returns a boolean value representing the scroll direction. False means scrolling down, toward the user, and true means scrolling up, away from the user. -@example +@example lua @verbatim local direction = event:direction() @end verbatim @@ -2785,7 +2962,7 @@ has returned. Returns the x and y for this mouse event in pixels, relative to the top-left of the window that it relates to. -@example +@example lua @verbatim local x, y = event:xy() @end verbatim @@ -2797,7 +2974,7 @@ local x, y = event:xy() Returns a boolean value indicating whether @code{ctrl} was held when this event was fired. -@example +@example lua @verbatim local ctrl = event:ctrl() @end verbatim @@ -2809,7 +2986,7 @@ local ctrl = event:ctrl() Returns a boolean value indicating whether @code{shift} was held when this event was fired. -@example +@example lua @verbatim local shift = event:shift() @end verbatim @@ -2822,7 +2999,7 @@ Returns a boolean value indicating whether the meta key (also known as super, command, or the "windows key") was held when this event was fired. -@example +@example lua @verbatim local meta = event:meta() @end verbatim @@ -2834,7 +3011,7 @@ local meta = event:meta() Returns a boolean value indicating whether @code{alt} was held when this event was fired. -@example +@example lua @verbatim local alt = event:alt() @end verbatim @@ -2846,7 +3023,7 @@ local alt = event:alt() Returns a boolean value indicating whether caps lock was on when this event was fired. -@example +@example lua @verbatim local capslock = event:capslock() @end verbatim @@ -2858,7 +3035,7 @@ local capslock = event:capslock() Returns a boolean value indicating whether numlock was on when this event was fired. -@example +@example lua @verbatim local numlock = event:numlock() @end verbatim @@ -2871,7 +3048,7 @@ Returns three boolean values indicating whether each primary mouse button was held when this event fired, in the order: left, right, middle. -@example +@example lua @verbatim local lmb, rmb, mmb = event:buttons() @end verbatim diff --git a/src/library/gl.c b/src/library/gl.c index 9418fb6..64580f7 100644 --- a/src/library/gl.c +++ b/src/library/gl.c @@ -138,6 +138,7 @@ static void _bolt_gl_plugin_game_view_rect(int* x, int* y, int* w, int* h); #define VAO_LIST_CAPACITY 256 * 256 #define CONTEXTS_CAPACITY 64 // not growable so we just have to hard-code a number and hope it's enough forever #define GAME_MINIMAP_BIG_SIZE 2048 +#define GAME_ITEM_ICON_SIZE 64 static struct GLContext contexts[CONTEXTS_CAPACITY]; // since GL contexts are bound only to the thread that binds them, we use thread-local storage (TLS) @@ -824,9 +825,11 @@ static void _bolt_glTexStorage2D(GLenum target, GLsizei levels, GLenum internalf if (target == GL_TEXTURE_2D) { struct GLTexture2D* tex = c->texture_units[c->active_texture]; free(tex->data); - tex->data = malloc(width * height * 4); + tex->data = calloc(width * height * 4, sizeof(*tex->data)); + tex->internalformat = internalformat; tex->width = width; tex->height = height; + tex->icon.model_count = 0; } LOG("glTexStorage2D end\n"); } @@ -1042,14 +1045,29 @@ static void _bolt_glCopyImageSubData( gl.CopyImageSubData(srcName, srcTarget, srcLevel, srcX, srcY, srcZ, dstName, dstTarget, dstLevel, dstX, dstY, dstZ, srcWidth, srcHeight, srcDepth); struct GLContext* c = _bolt_context(); if (srcTarget == GL_TEXTURE_2D && dstTarget == GL_TEXTURE_2D && srcLevel == 0 && dstLevel == 0) { - const struct GLTexture2D* src = _bolt_context_get_texture(c, srcName); - const struct GLTexture2D* dst = _bolt_context_get_texture(c, dstName); + struct GLTexture2D* src = _bolt_context_get_texture(c, srcName); + struct GLTexture2D* dst = _bolt_context_get_texture(c, dstName); if (!c->does_blit_3d_target && c->depth_of_field_enabled && dst->id == c->depth_of_field_sSourceTex) { if (srcX == 0 && srcY == 0 && dstX == 0 && dstY == 0 && src->width == dst->width && src->height == dst->height && src->width == srcWidth && src->height == srcHeight) { printf("copy to depth-of-field tex from tex %i\n", src->id); c->does_blit_3d_target = true; c->target_3d_tex = src->id; } + } else if (src->icon.model_count && dst->width > GAME_ITEM_ICON_SIZE && dst->height > GAME_ITEM_ICON_SIZE) { + if (!dst->icons) { + dst->icons = hashmap_new(sizeof(struct ItemIcon), 256, 0, 0, _bolt_plugin_itemicon_hash, _bolt_plugin_itemicon_compare, NULL, NULL); + } + src->icon.x = dstX; + src->icon.y = dstY; + src->icon.w = srcWidth; + src->icon.h = srcHeight; + const struct ItemIcon* old_icon = hashmap_set(dst->icons, &src->icon); + if (old_icon) { + for (size_t i = 0; i < old_icon->model_count; i += 1) { + free(old_icon->models[i].vertices); + } + } + src->icon.model_count = 0; } else if (src->id != c->target_3d_tex) { for (GLsizei i = 0; i < srcHeight; i += 1) { memcpy(dst->data + (dstY * dst->width * 4) + (dstX * 4), src->data + (srcY * src->width * 4) + (srcX * 4), srcWidth * 4); @@ -1343,6 +1361,9 @@ void _bolt_gl_onGenTextures(GLsizei n, GLuint* textures) { for (GLsizei i = 0; i < n; i += 1) { struct GLTexture2D* tex = calloc(1, sizeof(struct GLTexture2D)); tex->id = textures[i]; + tex->internalformat = -1; + tex->compare_mode = -1; + tex->icons = NULL; tex->is_minimap_tex_big = 0; tex->is_minimap_tex_small = 0; hashmap_set(c->textures->map, &tex); @@ -1434,11 +1455,11 @@ void _bolt_gl_onDrawElements(GLenum mode, GLsizei count, GLenum type, const void _bolt_plugin_handle_minimap(&render); } } - } else { + } else if (c->current_draw_framebuffer == 0) { struct GLPluginDrawElementsVertex2DUserData vertex_userdata; vertex_userdata.c = c; - vertex_userdata.indices = (unsigned short*)((uint8_t*)element_buffer->data + (uintptr_t)indices_offset); - vertex_userdata.atlas = c->texture_units[diffuse_map]; + vertex_userdata.indices = indices; + vertex_userdata.atlas = tex; vertex_userdata.position = &attributes[c->bound_program->loc_aVertexPosition2D]; vertex_userdata.atlas_min = &attributes[c->bound_program->loc_aTextureUVAtlasMin]; vertex_userdata.atlas_size = &attributes[c->bound_program->loc_aTextureUVAtlasExtents]; @@ -1465,7 +1486,49 @@ void _bolt_gl_onDrawElements(GLenum mode, GLsizei count, GLenum type, const void batch.texture_functions.size = _bolt_gl_plugin_texture_size; batch.texture_functions.compare = _bolt_gl_plugin_texture_compare; batch.texture_functions.data = _bolt_gl_plugin_texture_data; - _bolt_plugin_handle_render2d(&batch); + + if (tex->icons) { + size_t batch_start = 0; + for (size_t i = 0; i < count; i += batch.vertices_per_icon) { + float xy[2]; + float wh[2]; + _bolt_get_attr_binding(c, vertex_userdata.atlas_min, indices[i], 2, xy); + _bolt_get_attr_binding(c, vertex_userdata.atlas_size, indices[i], 2, wh); + const struct ItemIcon dummy_icon = { + .x = (uint16_t)roundf(xy[0] * tex->width), + .y = (uint16_t)roundf(xy[1] * tex->height), + .w = (uint16_t)roundf(fabs(wh[0]) * tex->width), + .h = (uint16_t)roundf(fabs(wh[1]) * tex->height), + }; + const struct ItemIcon* icon = hashmap_get(tex->icons, &dummy_icon); + if (icon) { + batch.index_count = i - batch_start; + if (batch.index_count) { + vertex_userdata.indices = indices + batch_start; + _bolt_plugin_handle_render2d(&batch); + } + int32_t xy0[2]; + int32_t xy2[2]; + _bolt_get_attr_binding_int(c, vertex_userdata.position, i, 2, xy0); + _bolt_get_attr_binding_int(c, vertex_userdata.position, i + 2, 2, xy2); + struct RenderItemIconEvent event; + event.icon = icon; + event.target_x = (int16_t)xy0[0]; + event.target_y = (int16_t)(batch.screen_height - xy0[1]); + event.target_w = xy2[0] - xy0[0]; + event.target_h = xy0[1] - xy2[1]; + _bolt_plugin_handle_rendericon(&event); + batch_start = i + batch.vertices_per_icon; + } + } + if (batch_start < count) { + batch.index_count = count - batch_start; + vertex_userdata.indices = indices + batch_start; + _bolt_plugin_handle_render2d(&batch); + } + } else { + _bolt_plugin_handle_render2d(&batch); + } } } if (type == GL_UNSIGNED_SHORT && mode == GL_TRIANGLES && c->bound_program->is_3d) { @@ -1486,7 +1549,7 @@ void _bolt_gl_onDrawElements(GLenum mode, GLsizei count, GLenum type, const void struct GLPluginDrawElementsVertex3DUserData vertex_userdata; vertex_userdata.c = c; - vertex_userdata.indices = (unsigned short*)((uint8_t*)element_buffer->data + (uintptr_t)indices_offset); + vertex_userdata.indices = indices; vertex_userdata.atlas_scale = roundf(atlas_meta[1]); vertex_userdata.atlas = tex; vertex_userdata.settings_atlas = tex_settings; @@ -1529,6 +1592,30 @@ void _bolt_gl_onDrawElements(GLenum mode, GLsizei count, GLenum type, const void render.matrix_functions.viewproj_matrix = _bolt_gl_plugin_matrix3d_viewproj; _bolt_plugin_handle_render3d(&render); + } else { + struct GLTexture2D* tex = _bolt_context_get_texture(c, draw_tex); + if (tex && tex->compare_mode == GL_NONE && tex->internalformat == GL_RGBA8 && tex->width == GAME_ITEM_ICON_SIZE && tex->height == GAME_ITEM_ICON_SIZE && tex->icon.model_count < MAX_MODELS_PER_ICON) { + struct ItemIconModel* model = &tex->icon.models[tex->icon.model_count]; + tex->icon.model_count += 1; + GLfloat model_matrix[16]; + GLint ubo_binding, ubo_view_index; + gl.GetActiveUniformBlockiv(c->bound_program->id, c->bound_program->block_index_ViewTransforms, GL_UNIFORM_BLOCK_BINDING, &ubo_binding); + gl.GetIntegeri_v(GL_UNIFORM_BUFFER_BINDING, ubo_binding, &ubo_view_index); + const uint8_t* ubo_view_buf = (uint8_t*)_bolt_context_get_buffer(c, ubo_view_index)->data; + gl.GetUniformfv(c->bound_program->id, c->bound_program->loc_uModelMatrix, model_matrix); + for (size_t i = 0; i < 16; i += 1) { + model->view_matrix.matrix[i] = (double)*(float*)(ubo_view_buf + c->bound_program->offset_uViewMatrix); + model->projection_matrix.matrix[i] = (double)*(float*)(ubo_view_buf + c->bound_program->offset_uProjectionMatrix); + model->viewproj_matrix.matrix[i] = (double)*(float*)(ubo_view_buf + c->bound_program->offset_uViewProjMatrix); + } + model->vertex_count = count; + model->vertices = malloc(sizeof(*model->vertices) * count); + for (size_t i = 0; i < count; i += 1) { + _bolt_get_attr_binding_int(c, &attributes[c->bound_program->loc_aVertexPosition_BoneLabel], indices[i], 4, model->vertices[i].point.xyzh.ints); + _bolt_get_attr_binding(c, &attributes[c->bound_program->loc_aVertexColour], indices[i], 4, model->vertices[i].rgba); + model->vertices[i].point.integer = true; + } + } } } } @@ -1659,6 +1746,20 @@ void _bolt_gl_onDeleteTextures(GLsizei n, const GLuint* textures) { const GLuint* ptr = &textures[i]; struct GLTexture2D* const* texture = hashmap_delete(c->textures->map, &ptr); free((*texture)->data); + if ((*texture)->icons) { + size_t iter = 0; + void* item; + while (hashmap_iter((*texture)->icons, &iter, &item)) { + const struct ItemIcon* icon = (struct ItemIcon*)item; + for (size_t i = 0; i < icon->model_count; i += 1) { + free(icon->models[i].vertices); + } + } + hashmap_free((*texture)->icons); + } + for (size_t i = 0; i < (*texture)->icon.model_count; i += 1) { + free((*texture)->icon.models[i].vertices); + } free(*texture); } _bolt_rwlock_unlock_write(&c->textures->rwlock); @@ -1672,6 +1773,10 @@ void _bolt_gl_onClear(GLbitfield mask) { struct GLTexture2D* tex = _bolt_context_get_texture(c, draw_tex); if (tex) { tex->is_minimap_tex_big = 0; + for (size_t i = 0; i < tex->icon.model_count; i += 1) { + free(tex->icon.models[i].vertices); + } + tex->icon.model_count = 0; } } } @@ -1684,6 +1789,14 @@ void _bolt_gl_onViewport(GLint x, GLint y, GLsizei width, GLsizei height) { c->viewport_h = height; } +void _bolt_gl_onTexParameteri(GLenum target, GLenum pname, GLint param) { + if (target == GL_TEXTURE_2D && pname == GL_TEXTURE_COMPARE_MODE) { + struct GLContext* c = _bolt_context(); + struct GLTexture2D* tex = c->texture_units[c->active_texture]; + tex->compare_mode = param; + } +} + static void _bolt_gl_plugin_drawelements_vertex2d_xy(size_t index, void* userdata, int32_t* out) { struct GLPluginDrawElementsVertex2DUserData* data = userdata; if (_bolt_get_attr_binding_int(data->c, data->position, data->indices[index], 2, out)) { @@ -1739,10 +1852,10 @@ static void _bolt_gl_plugin_drawelements_vertex3d_xyz(size_t index, void* userda if (!_bolt_get_attr_binding_int(data->c, data->xyz_bone, data->indices[index], 3, out->xyzh.ints)) { float pos[3]; _bolt_get_attr_binding(data->c, data->xyz_bone, data->indices[index], 3, pos); - out->xyzh.ints[0] = (double)pos[0]; - out->xyzh.ints[1] = (double)pos[1]; - out->xyzh.ints[2] = (double)pos[2]; - out->xyzh.ints[3] = 1.0; + out->xyzh.floats[0] = (double)pos[0]; + out->xyzh.floats[1] = (double)pos[1]; + out->xyzh.floats[2] = (double)pos[2]; + out->xyzh.floats[3] = 1.0; out->integer = false; } } diff --git a/src/library/gl.h b/src/library/gl.h index db4e194..2a28969 100644 --- a/src/library/gl.h +++ b/src/library/gl.h @@ -5,8 +5,8 @@ #include "../../modules/hashmap/hashmap.h" #include "rwlock/rwlock.h" +#include "plugin/plugin.h" struct hashmap; -struct SurfaceFunctions; /* types from gl.h */ typedef unsigned int GLenum; @@ -97,12 +97,13 @@ struct GLLibFunctions { void (*GenTextures)(GLsizei, GLuint*); GLenum (*GetError)(void); void (*ReadPixels)(GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, void*); - void (*TexParameteri)(GLenum, GLenum, GLfloat); + void (*TexParameteri)(GLenum, GLenum, GLint); void (*TexSubImage2D)(GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, const void*); void (*Viewport)(GLint, GLint, GLsizei, GLsizei); }; /* consts used from libgl */ +#define GL_NONE 0 #define GL_TEXTURE 5890 #define GL_TEXTURE_2D 3553 #define GL_RGB 6407 @@ -158,6 +159,7 @@ struct GLLibFunctions { #define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 36048 #define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 36049 #define GL_MAX_VERTEX_ATTRIBS 34921 +#define GL_TEXTURE_COMPARE_MODE 34892 /* bolt re-implementation of some gl objects, storing only the things we need */ @@ -177,6 +179,10 @@ struct GLTexture2D { GLsizei height; double minimap_center_x; double minimap_center_y; + GLenum internalformat; + GLint compare_mode; + struct ItemIcon icon; + struct hashmap* icons; uint8_t is_minimap_tex_big; uint8_t is_minimap_tex_small; }; @@ -325,11 +331,14 @@ void _bolt_gl_onClear(GLbitfield); /// Call this in response to glViewport, which needs to be hooked from libgl. void _bolt_gl_onViewport(GLint, GLint, GLsizei, GLsizei); +/// Call this in response to glTexParameteri, which needs to be hooked from libgl. +void _bolt_gl_onTexParameteri(GLenum, GLenum, GLint); + /* plugin library interop stuff */ struct GLPluginDrawElementsVertex2DUserData { struct GLContext* c; - unsigned short* indices; + const unsigned short* indices; struct GLTexture2D* atlas; struct GLAttrBinding* position; struct GLAttrBinding* atlas_min; @@ -341,7 +350,7 @@ struct GLPluginDrawElementsVertex2DUserData { struct GLPluginDrawElementsVertex3DUserData { struct GLContext* c; - unsigned short* indices; + const unsigned short* indices; int atlas_scale; struct GLTexture2D* atlas; struct GLTexture2D* settings_atlas; diff --git a/src/library/plugin/plugin.c b/src/library/plugin/plugin.c index ccf3e43..263c1ae 100644 --- a/src/library/plugin/plugin.c +++ b/src/library/plugin/plugin.c @@ -109,6 +109,20 @@ static uint64_t _bolt_plugin_map_hash(const void* item, uint64_t seed0, uint64_t return hashmap_sip(&p->id, sizeof(p->id), seed0, seed1); } +int _bolt_plugin_itemicon_compare(const void* a, const void* b, void* udata) { + const struct ItemIcon* i1 = a; + const struct ItemIcon* i2 = b; + uint64_t xywh1, xywh2; + memcpy(&xywh1, &i1->x, sizeof xywh1); + memcpy(&xywh2, &i2->x, sizeof xywh2); + return xywh2 - xywh1; +} + +uint64_t _bolt_plugin_itemicon_hash(const void* item, uint64_t seed0, uint64_t seed1) { + const struct ItemIcon* icon = item; + return hashmap_sip(&icon->x, 4 * sizeof icon->x, seed0, seed1); +} + static struct hashmap* plugins; #define DEFINE_CALLBACK(APINAME, STRUCTNAME) \ @@ -1110,6 +1124,7 @@ uint8_t _bolt_plugin_handle_mouse_event(struct MouseEvent* event, ptrdiff_t bool DEFINE_CALLBACK_STATIC(swapbuffers, SwapBuffersEvent) DEFINE_CALLBACK(render2d, RenderBatch2D) DEFINE_CALLBACK(render3d, Render3D) +DEFINE_CALLBACK(rendericon, RenderItemIconEvent) DEFINE_CALLBACK(minimap, RenderMinimapEvent) DEFINE_CALLBACK(mousemotion, MouseMotionEvent) DEFINE_CALLBACK(mousebutton, MouseButtonEvent) @@ -1203,6 +1218,7 @@ _bolt_api_push_metatable_##NAME(plugin->state); \ lua_settable(plugin->state, LUA_REGISTRYINDEX); SETMETA(render2d) SETMETA(render3d) + SETMETA(rendericon) SETMETA(minimap) SETMETA(point) SETMETA(transform) diff --git a/src/library/plugin/plugin.h b/src/library/plugin/plugin.h index 5749d8d..d6c0f4a 100644 --- a/src/library/plugin/plugin.h +++ b/src/library/plugin/plugin.h @@ -16,6 +16,8 @@ struct lua_State; #define GRAB_TYPE_START 1 #define GRAB_TYPE_STOP 2 +#define MAX_MODELS_PER_ICON 8 + // a currently-running plugin. // note "path" is not null-terminated, and must always be converted to use '/' as path-separators // and must always end with a trailing separator by the time it's received by this process. @@ -270,6 +272,39 @@ struct Render3D { struct Render3DMatrixFunctions matrix_functions; }; +struct ItemIconVertex { + struct Point3D point; + uint16_t bone_id; + float rgba[4]; +}; + +struct ItemIconModel { + uint32_t vertex_count; + struct ItemIconVertex* vertices; + struct Transform3D view_matrix; + struct Transform3D projection_matrix; + struct Transform3D viewproj_matrix; +}; + +struct ItemIcon { + uint16_t x; + uint16_t y; + uint16_t w; + uint16_t h; + uint8_t model_count; + struct ItemIconModel models[MAX_MODELS_PER_ICON]; +}; +int _bolt_plugin_itemicon_compare(const void* a, const void* b, void* udata); +uint64_t _bolt_plugin_itemicon_hash(const void* item, uint64_t seed0, uint64_t seed1); + +struct RenderItemIconEvent { + const struct ItemIcon* icon; + uint16_t target_x; + uint16_t target_y; + uint16_t target_w; + uint16_t target_h; +}; + struct RenderMinimapEvent { double angle; double scale; @@ -361,6 +396,9 @@ void _bolt_plugin_handle_render2d(struct RenderBatch2D*); /// Sends a Render3D to all plugins. void _bolt_plugin_handle_render3d(struct Render3D*); +/// Sends a RenderItemIconEvent to all plugins. +void _bolt_plugin_handle_rendericon(struct RenderItemIconEvent*); + /// Sends a RenderMinimap to all plugins. void _bolt_plugin_handle_minimap(struct RenderMinimapEvent*); diff --git a/src/library/plugin/plugin_api.c b/src/library/plugin/plugin_api.c index 021e378..535b5a7 100644 --- a/src/library/plugin/plugin_api.c +++ b/src/library/plugin/plugin_api.c @@ -174,6 +174,7 @@ static int api_buffer_set##FNAME(lua_State* state) { \ DEFINE_CALLBACK(swapbuffers) DEFINE_CALLBACK(render2d) DEFINE_CALLBACK(render3d) +DEFINE_CALLBACK(rendericon) DEFINE_CALLBACK(minimap) DEFINE_CALLBACK(mousemotion) DEFINE_CALLBACK(mousebutton) @@ -1051,8 +1052,8 @@ static int api_render3d_vertexcount(lua_State* state) { return 1; } -static int api_render3d_vertexxyz(lua_State* state) { - const struct Render3D* render = require_self_userdata(state, "vertexxyz"); +static int api_render3d_vertexpoint(lua_State* state) { + const struct Render3D* render = require_self_userdata(state, "vertexpoint"); const lua_Integer index = lua_tointeger(state, 2); struct Point3D* point = lua_newuserdata(state, sizeof(struct Point3D)); render->vertex_functions.xyz(index - 1, render->vertex_functions.userdata, point); @@ -1199,6 +1200,79 @@ static int api_render3d_animated(lua_State* state) { return 1; } +static int api_rendericon_xywh(lua_State* state) { + const struct RenderItemIconEvent* event = require_self_userdata(state, "xywh"); + lua_pushinteger(state, event->target_x); + lua_pushinteger(state, event->target_y); + lua_pushinteger(state, event->target_w); + lua_pushinteger(state, event->target_h); + return 4; +} + +static int api_rendericon_modelcount(lua_State* state) { + const struct RenderItemIconEvent* event = require_self_userdata(state, "modelcount"); + lua_pushinteger(state, event->icon->model_count); + return 1; +} + +static int api_rendericon_modelvertexcount(lua_State* state) { + const struct RenderItemIconEvent* event = require_self_userdata(state, "modelvertexcount"); + const size_t model = luaL_checkinteger(state, 2); + lua_pushinteger(state, event->icon->models[model - 1].vertex_count); + return 1; +} + +static int api_rendericon_modelvertexpoint(lua_State* state) { + const struct RenderItemIconEvent* event = require_self_userdata(state, "modelvertexpoint"); + const size_t model = luaL_checkinteger(state, 2); + const size_t vertex = luaL_checkinteger(state, 3); + struct Point3D* point = lua_newuserdata(state, sizeof(struct Point3D)); + *point = event->icon->models[model - 1].vertices[vertex - 1].point; + lua_getfield(state, LUA_REGISTRYINDEX, "pointmeta"); + lua_setmetatable(state, -2); + return 1; +} + +static int api_rendericon_modelvertexcolour(lua_State* state) { + const struct RenderItemIconEvent* event = require_self_userdata(state, "modelvertexpoint"); + const size_t model = luaL_checkinteger(state, 2); + const size_t vertex = luaL_checkinteger(state, 3); + for (size_t i = 0; i < 4; i += 1) { + lua_pushnumber(state, event->icon->models[model - 1].vertices[vertex - 1].rgba[i]); + } + return 4; +} + +static int api_rendericon_modelviewmatrix(lua_State* state) { + const struct RenderItemIconEvent* event = require_self_userdata(state, "modelviewmatrix"); + const size_t model = luaL_checkinteger(state, 2); + struct Transform3D* transform = lua_newuserdata(state, sizeof(struct Transform3D)); + *transform = event->icon->models[model - 1].view_matrix; + lua_getfield(state, LUA_REGISTRYINDEX, "transformmeta"); + lua_setmetatable(state, -2); + return 1; +} + +static int api_rendericon_modelprojectionmatrix(lua_State* state) { + const struct RenderItemIconEvent* event = require_self_userdata(state, "modelprojectionmatrix"); + const size_t model = luaL_checkinteger(state, 2); + struct Transform3D* transform = lua_newuserdata(state, sizeof(struct Transform3D)); + *transform = event->icon->models[model - 1].projection_matrix; + lua_getfield(state, LUA_REGISTRYINDEX, "transformmeta"); + lua_setmetatable(state, -2); + return 1; +} + +static int api_rendericon_modelviewprojmatrix(lua_State* state) { + const struct RenderItemIconEvent* event = require_self_userdata(state, "modelviewprojmatrix"); + const size_t model = luaL_checkinteger(state, 2); + struct Transform3D* transform = lua_newuserdata(state, sizeof(struct Transform3D)); + *transform = event->icon->models[model - 1].viewproj_matrix; + lua_getfield(state, LUA_REGISTRYINDEX, "transformmeta"); + lua_setmetatable(state, -2); + return 1; +} + static int api_repositionevent_xywh(lua_State* state) { const struct RepositionEvent* event = require_self_userdata(state, "xywh"); lua_pushinteger(state, event->x); @@ -1480,6 +1554,7 @@ static struct ApiFuncTemplate bolt_functions[] = { BOLTFUNC(onrender2d), BOLTFUNC(onrender3d), + BOLTFUNC(onrendericon), BOLTFUNC(onminimap), BOLTFUNC(onswapbuffers), BOLTFUNC(onmousemotion), @@ -1530,7 +1605,7 @@ static struct ApiFuncTemplate render2d_functions[] = { static struct ApiFuncTemplate render3d_functions[] = { BOLTFUNC(vertexcount, render3d), - BOLTFUNC(vertexxyz, render3d), + BOLTFUNC(vertexpoint, render3d), BOLTFUNC(modelmatrix, render3d), BOLTFUNC(viewmatrix, render3d), BOLTFUNC(projectionmatrix, render3d), @@ -1548,6 +1623,18 @@ static struct ApiFuncTemplate render3d_functions[] = { BOLTALIAS(vertexcolour, vertexcolor, render3d), }; +static struct ApiFuncTemplate rendericon_functions[] = { + BOLTFUNC(xywh, rendericon), + BOLTFUNC(modelcount, rendericon), + BOLTFUNC(modelvertexcount, rendericon), + BOLTFUNC(modelvertexpoint, rendericon), + BOLTFUNC(modelvertexcolour, rendericon), + BOLTFUNC(modelviewmatrix, rendericon), + BOLTFUNC(modelprojectionmatrix, rendericon), + BOLTFUNC(modelviewprojmatrix, rendericon), + BOLTALIAS(modelvertexcolour, modelvertexcolor, rendericon), +}; + static struct ApiFuncTemplate minimap_functions[] = { BOLTFUNC(angle, minimap), BOLTFUNC(scale, minimap), @@ -1712,6 +1799,7 @@ void _bolt_api_push_metatable_##NAME(lua_State* state) { \ DEFPUSHMETA(render2d) DEFPUSHMETA(render3d) +DEFPUSHMETA(rendericon) DEFPUSHMETA(minimap) DEFPUSHMETA(point) DEFPUSHMETA(transform) diff --git a/src/library/plugin/plugin_api.h b/src/library/plugin/plugin_api.h index bed49e3..cc39339 100644 --- a/src/library/plugin/plugin_api.h +++ b/src/library/plugin/plugin_api.h @@ -37,6 +37,7 @@ struct ExternalBrowser { void _bolt_api_push_bolt_table(lua_State*); void _bolt_api_push_metatable_render2d(lua_State*); void _bolt_api_push_metatable_render3d(lua_State*); +void _bolt_api_push_metatable_rendericon(lua_State*); void _bolt_api_push_metatable_minimap(lua_State*); void _bolt_api_push_metatable_point(lua_State*); void _bolt_api_push_metatable_transform(lua_State*); diff --git a/src/library/so/main.c b/src/library/so/main.c index 7d49a8c..38f53a0 100644 --- a/src/library/so/main.c +++ b/src/library/so/main.c @@ -361,6 +361,13 @@ void glViewport(GLint x, GLint y, GLsizei width, GLsizei height) { LOG("glViewport end\n"); } +void glTexParameteri(GLenum target, GLenum pname, GLint param) { + LOG("glTexParameteri\n"); + libgl.TexParameteri(target, pname, param); + _bolt_gl_onTexParameteri(target, pname, param); + LOG("glTexParameteri end\n"); +} + void* eglGetProcAddress(const char* name) { LOG("eglGetProcAddress('%s')\n", name); void* ret = _bolt_gl_GetProcAddress(name); @@ -739,6 +746,8 @@ static void* _bolt_dl_lookup(void* handle, const char* symbol) { if (strcmp(symbol, "glTexSubImage2D") == 0) return glTexSubImage2D; if (strcmp(symbol, "glDeleteTextures") == 0) return glDeleteTextures; if (strcmp(symbol, "glClear") == 0) return glClear; + if (strcmp(symbol, "glViewport") == 0) return glViewport; + if (strcmp(symbol, "glTexParameteri") == 0) return glTexParameteri; } else if (handle == libxcb_addr) { if (strcmp(symbol, "xcb_poll_for_event") == 0) return xcb_poll_for_event; if (strcmp(symbol, "xcb_poll_for_queued_event") == 0) return xcb_poll_for_queued_event;