271 changes: 271 additions & 0 deletions doc/client_lua_api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,83 @@ For the following functions `x` can be either a vector or a number:
* `vector.multiply(v, x)`: returns a scaled vector or Schur product
* `vector.divide(v, x)`: returns a scaled vector or Schur quotient

Relative Positions and Rectangles
---------------------------------

A relative position/rectangle is a position/rectangle that is relative to a parent rectangle.
It is a table, but the internal representation of this table can vary depending on what part
of the parent rectangle the position/rectangle should be relative to.

Relative positions/rectangles are based on zero, i.e. `{x=0, y=0}` is the top left position,
not `{x=1, y=1}`, as might be assumed with Lua.

The internal representation of a relative position table:
* For the X axis, either:
* `x`: Relative to the left side of the parent rectangle.
* `nx`: Relative to the right side of the parent rectangle.
* For the Y axis, either:
* `y`: Relative to the top side of the parent rectangle.
* `ny`: Relative to the bottom side of the parent rectangle.

A position that uses the components `x` and `y` is called a "standard position".

Examples: Positions relative to a rectangle with a width of 15 and a height of 6:
* `{x=5, y=3}`:
```
|
3
|
--5--X---------
|
|
```
* `{nx=7, y=1}`:
```
1
-------X---7---
|
|
|
|
```

The internal representation of a relative rectangle table:
* For the X axis, any two of:
* `w`: Defines the width of the rectangle.
* `x`: Defines the left side of the rectangle as relative to the left side of the parent
rectangle.
* `nx`: Defines the right side of the rectangle as relative to the right side of the
parent rectangle.
* For the Y axis, any two of:
* `h`: Defines the height of the rectangle.
* `y`: Defines the top side of the rectangle as relative to the top side of the parent
rectangle.
* `ny`: Defines the bottom side of the rectangle as relative to the bottom side of the
parent rectangle.

A rectangle that uses the components `x`, `y`, `w`, and `h` is called a "standard rectangle".
A rectangle that uses the components `x`, `y`, `nx`, and `ny` is called an "offset rectangle".

Examples: Rectangles relative to another rectangle with a width of 15 and a height of 6:
* `{x=3, y=1, w=4, h=3}`:
```
1
-3-XXXX--------
3 X
XX4X
|
|
```
* `{x=4, nx=4, y=1, ny=0}`:
```
1
--4-XXXXXXX-4--
X X
X X
X X
XXXXXXX
```

Helper functions
----------------
* `dump2(obj, name="_", dumped={})`
Expand Down Expand Up @@ -821,6 +898,67 @@ Call these functions only at load time!
* `minetest.localplayer`
* Reference to the LocalPlayer object. See [`LocalPlayer`](#localplayer) class reference for methods.

### Rendering

Functions for texture drawing and rendering related functions.

* `minetest.register_on_predraw(function(dtime))`
* Called every frame right before the engine has started drawing. Code used here must be
fast to ensure the framerate doesn't drop.
* Mainly useful for updating or binding `Texture`s before the engine has started drawing.
* `dtime`: Time in seconds since the last frame.
* `minetest.register_on_draw(function(dtime))`
* Called every frame after the engine is finished drawing for custom drawing to the screen
Code used here must be fast to ensure the framerate doesn't drop.
* `dtime`: Time in seconds since the last frame.
* `minetest.on_next_predraw(function(dtime))`
* Called right before the next time `minetest.register_on_predraw` has started running.
* Calling this function inside itself will be run in the same predraw.
* Useful if something needs to be done inside a drawing callback once, e.g. drawing to a
`Texture`.
* `dtime`: Time in seconds since the previous frame.
* `minetest.on_next_draw(function(dtime))`
* Called right before the next time `minetest.register_on_draw` has started running.
* Calling this function inside itself will be run in the same draw.
* Useful if something needs to be done inside a drawing callback once, e.g. drawing to a
`Texture`.
* `dtime`: Time in seconds since the previous frame.
* `minetest.get_driver_info()` -> info: Returns a table containing information about and the
limitations of the current video driver.
* On lower end graphics cards, e.g. Android, some features may not be available. OpenGL,
the recommended driver for desktop computers, supports all these features.
* `info`: A table containing the information. Contains the following keys:
* `name`: Name of the current video driver. This string is the current `video_driver`
setting value. Possible values are "null", "software", "burningsvideo", "direct3d8",
"direct3d9", "opengl", "ogles1", or "ogles2".
* `friendly_name`: A more human readable name of the current driver.
* `render_textures`: True if the driver supports true hardware rendering to textures,
i.e. all the drawing and modification functions of `Texture`.
* `npot_textures`: True if the driver supports textures with dimensions that are not
powers of two, i.e. not just 16, 32, 64, 128, etc.
* `non_square_textures`: True if the driver supports non-square textures, i.e.
rectangular textures.
* `max_texture_size`: A 2D vector denoting the maximum texture size that can be used. If
`x` or `y` is `math.huge`, there is no limit.
* `minetest.get_optimal_texture_size(size[, pot][, square][, max_size][, smaller])` -> optimal_size:
Gets the best possible texture size given the specified driver limitations for textures.
* Unless otherwise specified, all optional arguments, if not provided or nil, default to
the current driver limitations found in `minetest.get_driver_info()`.
* `size`: A 2D vector that is the preferred texture size that will be modified.
* `pot` (optional): Whether the texture dimensions must be powers of two.
* `square` (optional): Whether the texture must be a square.
* `max_size` (optional): A 2D vector defining the maximum size that this texture may be. If
either dimension is `math.huge`, there is no limit.
* `smaller` (optional): If true and any texture dimensions do have to be changed, the texture
dimensions will be reduced instead of enlarged. Defaults to false. However, if the size
provided is larger than the maximum size, it will be reduced regardless of this setting.
* `optimal_value`: The optimal texture value given the provided constraints. If either
resulting dimension is zero, it will default to one.
* `minetest.window`: A special `Texture` that points to the Minetest window.
* The window is write-only and cannot contain alpha pixels.
* The screen supports all functions in `Texture` regardless of any driver limitations in
`minetest.get_driver_info` unless documented as otherwise.

### Privileges
* `minetest.get_privilege_list()`
* Returns a list of privileges the current player has in the format `{priv1=true,...}`
Expand Down Expand Up @@ -1152,6 +1290,139 @@ Can be obtained via `minetest.get_meta(pos)`.
* `fields`: key-value storage
* `inventory`: `{list1 = {}, ...}}`

`Texture`
---------

A Lua interface for interacting with hardware GPU textures.

All functions that draw to or create textures must be in a draw or predraw callback and (with
the exception of `minetest.window`) require the video driver to support render textures. See
`minetest.get_driver_info` for more information on the limitations of textures.

Unless otherwise specified, all positions and rectangles are relative to the `Texture` being
drawn to. See [Relative Positions and Rectangles].

### Constructors

* `Texture.new(size)` -> texture: Creates a blank `Texture`.
* `size`: 2D vector defining the size to make the `Texture`.
* Does not work if the driver doesn't support render textures. See
`minetest.get_driver_info`.
* Returns nil if the texture could not be created for some reason.
* `Texture.copy(texture)` -> texture: Returns a copy of another texture.
* `texture`: Either an in-game texture string or a `Texture` to copy. Must be readable.
* Returns nil if the texture could not be created for some reason.
* `Texture.ref(texture)` -> texture: Returns a reference to a texture.
* `texture`: Either an in-game texture string or a `Texture`.
* The resulting `Texture` object will point to the texture. Since it does not hold full
access to the texture, it is read-only.
* This is useful for getting attributes about any texture, e.g. the size, without making a full
copy, which is time consuming.

### Methods

* `is_readable()` -> bool: Returns whether the texture can be read from.
* `minetest.window` can not be read from.
* `is_writable()` -> bool: Returns whether the texture can be written to.
* Texture references cannot be written to.
* `get_size()` -> size: Returns a 2D vector of the size of the texture.
* This may not be the same as the provided size to `Texture.new` if the driver doesn't
support the specified texture size.
* `fill(color)`: Fills the entire texture with a certain color.
* Unlike `draw_rect`, this replaces the pixels completely instead of adding the alpha.
* `color`: The color to fill the texture with.
* The texture being filled must be writable.
* `draw_pixel(pos, color[, clip_rect])`: Draws a pixel.
* `pos`: A position to draw the pixel at. Where the pixel will be positioned from depends
on the relative position provided:
* `x`: Horizontal is positioned from left edge of pixel
* `nx`: Horizontal is positioned from right edge of pixel
* `y`: Vertical is positioned from top edge of pixel
* `ny`: Vertical is positioned from bottom edge of pixel
* `color`: Color of the pixel to draw.
* `clip_rect` (optional): If specified, the pixel will only be drawn if it is inside this
rectangle.
* The texture being drawn to must be writable.
* `draw_rect(rect, color[, clip_rect])`: Draws a filled rectangle.
* `rect`: The rectangle to draw.
* `color`: Can be one of two things:
* A single color: The whole rectangle will be filled with this color
* An array of four colors: The rectangle will be drawn with a gradient. Each corner is
assigned one of the colors. Index 1 is top-left, 2 is top-right, 3 is bottom-left,
and 4 is bottom-right.
* `clip_rect` (optional): If specified, any part of the filled rectangle that extends out
of this rectangle will not be drawn.
* The texture being drawn to must be writable.
* `draw_texture(rect, texture[, clip_rect][, source_rect][, recolor])`: Draws a 2D texture.
* `rect`: The rectangle to draw the texture in. The texture will be stretched to fit.
* `texture`: Either the name of the texture with optional texture modifiers or the
`Texture` object to draw. Must be readable. A texture may not draw itself to itself.
* `clip_rect` (optional): If specified, any part of the texture that extends out of this
rectangle will not be drawn.
* `source_rect` (optional): Defines a rectangular sub-texture of the texture which will be
drawn instead of the entire texture. If nil, the whole texture is drawn. This rectangle
is relative to the rectangle of the texture being drawn to.
* `recolor` (optional): The color to recolor the texture to when drawing. This uses
hardware coloring, so it uses color multiplication and is fast.
* The texture being drawn to must be writable.
* `draw_text(pos, text[, clip_rect][, font][, color])`: Draws text.
* Font shadows are not drawn for versatility's sake and must be drawn manually if desired.
* `pos`: The position of the top left of the text.
* `text`: The text to draw. Newlines and `'\0'` are converted to spaces. Color escape codes
are stripped.
* `clip_rect` (optional): If specified, any part of the text that extends out of this
rectangle will not be drawn.
* `font` (optional): The `Font` to draw with. Defaults to the default font.
* `color` (optional): The color to make the text. Default white.
* The texture being drawn to must be writable.

`Font`
------

A Lua representation of a font. This only includes data relevant to the font itself, and any
other effects, such as color, underlining, and font shadows, must be handled elsewhere.

### Constructor

* `Font([from])` -> font: Creates a font from another `Font` or a font table.
* `from`: Either another `Font`, a font table, or nothing.
* A font table has the following keys:
* `face`: The font face to use.
* Can be one of the following:
* "normal" (default): The default user-set font.
* "mono": A monospace font.
* If a client's locale requires the fallback font, the fallback font will be used
regardless of the font specified here.
* In general, the fallback font should only be explicitly specified if it is known
that characters not supported in many fonts will be used.
* `size`: What size the font should be. The default is user-set.
* It is highly recommended to make the font size relative to the user-set font size
to respect preferences and accessibility concerns.
* `bold`: Whether the text should be bold. Default false.
* Some fonts may not support bolded styles, in which case it falls back to not
bolded.
* `italic`: Whether the text should be italicized. Default false.
* Some fonts may not support italicized styles, in which case it falls back to not
italicized.

### Methods

* `get_metrics()` -> metrics: Get global font metrics in pixels.
* `metrics`: A table containing font metrics. It has the following keys:
* `height`: The height of text in this font.
* `line_height`: The distance from one baseline to the baseline on the next line.
* `baseline`: The distance to the baseline from the top of the text.
* `line_thickness`: The thickness of underlines and strikethroughs.
* `underline_pos`: The distance to the top of the underline from the top of the text.
* `strike_pos`: The distance to the top of the strikethrough from the top of the text.
* `get_text_width(text)` -> width: Gets the width of the specified text in pixels.
* `text`: The text to get the width of. Newlines and `'\0'` are converted to spaces. Color
escape codes are stripped.
* `to_table()` -> font_table: Returns this font as a font table.
* See the `Font` constructor for the keys in a font table.
* To get default user-set values for a font, do `Font():to_table()`, optionally specifying
the font face in the constructor to get the default values for that face.

v-rob marked this conversation as resolved.
Show resolved Hide resolved
### `Raycast`

A raycast on the map. It works with selection boxes.
Expand Down
13 changes: 10 additions & 3 deletions src/client/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -737,7 +737,7 @@ class Game {
const v3f &player_position, bool show_debug);
void handleDigging(const PointedThing &pointed, const v3s16 &nodepos,
const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime);
void updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
void updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, f32 drawing_dtime,
const CameraOrientation &cam);

// Misc
Expand Down Expand Up @@ -1115,13 +1115,14 @@ void Game::run()
cam_view.camera_pitch += (cam_view_target.camera_pitch -
cam_view.camera_pitch) * m_cache_cam_smoothing;
updatePlayerControl(cam_view);
f32 drawing_dtime = dtime; // Preserve for later before potential set to zero
step(&dtime);
processClientEvents(&cam_view_target);
updateCamera(draw_times.busy_time, dtime);
updateSound(dtime);
processPlayerInteraction(dtime, m_game_ui->m_flags.show_hud,
m_game_ui->m_flags.show_debug);
updateFrame(&graph, &stats, dtime, cam_view);
updateFrame(&graph, &stats, dtime, drawing_dtime, cam_view);
updateProfilerGraphs(&graph);

// Update if minimap has been disabled by the server
Expand Down Expand Up @@ -3678,7 +3679,7 @@ void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos,
camera->setDigging(0); // Dig animation
}

void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, f32 drawing_dtime,
const CameraOrientation &cam)
{
TimeTaker tt_update("Game::updateFrame()");
Expand Down Expand Up @@ -3874,6 +3875,9 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
TimeTaker tt_draw("Draw scene");
driver->beginScene(true, true, skycolor);

if (client->modsLoaded())
client->getScript()->on_predraw(drawing_dtime);

bool draw_wield_tool = (m_game_ui->m_flags.show_hud &&
(player->hud_flags & HUD_FLAG_WIELDITEM_VISIBLE) &&
(camera->getCameraMode() == CAMERA_MODE_FIRST));
Expand All @@ -3889,6 +3893,9 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
RenderingEngine::draw_scene(skycolor, m_game_ui->m_flags.show_hud,
m_game_ui->m_flags.show_minimap, draw_wield_tool, draw_crosshair);

if (client->modsLoaded())
client->getScript()->on_draw(drawing_dtime);

/*
Profiler graph
*/
Expand Down
53 changes: 52 additions & 1 deletion src/irrlicht_changes/CGUITTFont.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,7 @@ CGUITTGlyphPage* CGUITTFont::getLastGlyphPage() const
CGUITTGlyphPage* CGUITTFont::createGlyphPage(const u8& pixel_mode)
{
CGUITTGlyphPage* page = 0;

// Name of our page.
io::path name("TTFontGlyphPage_");
name += tt_face->family_name;
Expand Down Expand Up @@ -1246,5 +1246,56 @@ core::array<scene::ISceneNode*> CGUITTFont::addTextSceneNode(const wchar_t* text
return container;
}

#define FT_FLOOR(X) (((X) & -64) / 64)
#define FT_CEIL(X) ((((X) + 63) & -64) / 64)

CGUITTFont::Metrics CGUITTFont::getMetrics() const
{
// Oddly, FT_Face seems to act somewhat like an opaque pointer that is shared by all font
// sizes for that face. This function seems to change the metrics of the face for the
// specified font size.
FT_Set_Pixel_Sizes(tt_face, 0, size);

// The following code adapted from SDL_ttf
Metrics metrics;

s32 underline_middle;

if (FT_IS_SCALABLE(tt_face)) {
FT_Fixed scale = tt_face->size->metrics.y_scale;
metrics.height = FT_CEIL(FT_MulFix(tt_face->ascender - tt_face->descender, scale));
metrics.line_height = FT_CEIL(FT_MulFix(tt_face->height, scale));
metrics.baseline = FT_CEIL(FT_MulFix(tt_face->ascender, scale));
underline_middle = metrics.baseline -
FT_FLOOR(FT_MulFix(tt_face->underline_position, scale));
metrics.line_thickness = FT_FLOOR(FT_MulFix(tt_face->underline_thickness, scale));

if (metrics.line_thickness < 1)
metrics.line_thickness = 1;
} else {
metrics.height = FT_CEIL(tt_face->size->metrics.height);
metrics.line_height = metrics.height;
metrics.baseline = FT_CEIL(tt_face->size->metrics.ascender);
// FT_Face::underline_position and FT_Face::underline_height only have values for
// scalable fonts, so use values that are as good as possible.
underline_middle = metrics.baseline - FT_CEIL(tt_face->size->metrics.descender) / 2;
metrics.line_thickness = 1;
}

metrics.underline_pos = underline_middle - metrics.line_thickness / 2;
if (metrics.underline_pos < 0)
metrics.underline_pos = 0;

// There is no consensus on strikethrough position, but this calculation, which
// approximates a line through the center line of an "e", seems to work pretty well for
// the fonts bundled with Minetest.
metrics.strike_pos = metrics.baseline * 13 / 20;

return metrics;
}

#undef FT_FLOOR
#undef FT_CEIL

} // end namespace gui
} // end namespace irr
28 changes: 28 additions & 0 deletions src/irrlicht_changes/CGUITTFont.h
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,9 @@ namespace gui
//! The font can either be a 256 color grayscale font, or a 2 color monochrome font.
virtual bool useMonochrome() const { return use_monochrome; }

//! Get the font shadow offset
u32 getShadowOffset() const { return shadow_offset; }

//! Tells the font to allow transparency when rendering.
//! Default: true.
//! \param flag If true, the font draws using transparency.
Expand All @@ -259,6 +262,9 @@ namespace gui
//! \param flag If true, the font draws using a monochrome image. If false, the font uses a grayscale image.
virtual void setMonochrome(const bool flag);

//! Set the font shadow offset
void setShadowOffset(u32 val) { shadow_offset = val; }

//! Enables or disables font hinting.
//! Default: Hinting and auto-hinting true.
//! \param enable If false, font hinting is turned off. If true, font hinting is turned on.
Expand Down Expand Up @@ -317,6 +323,9 @@ namespace gui
//! Set font that should be used for glyphs not present in ours
void setFallback(gui::IGUIFont* font) { fallback = font; }

//! Get font that should be used for glyphs not present in ours
gui::IGUIFont *getFallback() { return fallback; }

//! Create corresponding character's software image copy from the font,
//! so you can use this data just like any ordinary video::IImage.
//! \param ch The character you need
Expand All @@ -331,6 +340,25 @@ namespace gui
(const wchar_t* text, scene::ISceneManager* smgr, scene::ISceneNode* parent = 0,
const video::SColor& color = video::SColor(255, 0, 0, 0), bool center = false );

//! Font metrics in pixels independent of, and better than, `getDimension(L"").Height`.
struct Metrics {
//! The height of the font.
s32 height;
//! The distance from one baseline to the baseline on the next line.
s32 line_height;
//! The baseline of the font relative to the top of the text.
s32 baseline;
//! The thickness of the underline/strikethrough.
s32 line_thickness;
//! The top pixel of the underline relative to the top of the text.
s32 underline_pos;
//! The top pixel of the strikethrough relative to the top of the text.
s32 strike_pos;
};

//! Gets Freetype's information on font metrics in pixels.
Metrics getMetrics() const;

inline s32 getAscender() const { return font_metrics.ascender; }

protected:
Expand Down
2 changes: 2 additions & 0 deletions src/irrlichttypes_bloated.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irr_v2d.h"
#include "irr_v3d.h"
#include "irr_aabb3d.h"
#include <rect.h>
#include <dimension2d.h>

#include <SColor.h>
70 changes: 70 additions & 0 deletions src/script/common/c_converter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,76 @@ bool is_color_table(lua_State *L, int index)
return is_color_table;
}

core::recti read_rel_rect(lua_State *L, int index, const core::vector2di &parent_size)
{
CHECK_TYPE(index, "rectangle", LUA_TTABLE);
core::recti r;

lua_getfield(L, index, "w");
lua_getfield(L, index, "x");
lua_getfield(L, index, "nx");
if (lua_isnumber(L, -3)) {
s32 width = lua_tonumber(L, -3);
if (lua_isnumber(L, -2)) {
r.UpperLeftCorner.X = lua_tonumber(L, -2);
r.LowerRightCorner.X = r.UpperLeftCorner.X + width;
} else {
r.LowerRightCorner.X = parent_size.X - lua_tonumber(L, -1);
r.UpperLeftCorner.X = r.LowerRightCorner.X - width;
}
} else {
r.UpperLeftCorner.X = lua_tonumber(L, -2);
r.LowerRightCorner.X = parent_size.X - lua_tonumber(L, -1);
}
lua_pop(L, 3);

lua_getfield(L, index, "h");
lua_getfield(L, index, "y");
lua_getfield(L, index, "ny");
if (lua_isnumber(L, -3)) {
s32 height = lua_tonumber(L, -3);
if (lua_isnumber(L, -2)) {
r.UpperLeftCorner.Y = lua_tonumber(L, -2);
r.LowerRightCorner.Y = r.UpperLeftCorner.Y + height;
} else {
r.LowerRightCorner.Y = parent_size.Y - lua_tonumber(L, -1);
r.UpperLeftCorner.Y = r.LowerRightCorner.Y - height;
}
} else {
r.UpperLeftCorner.Y = lua_tonumber(L, -2);
r.LowerRightCorner.Y = parent_size.Y - lua_tonumber(L, -1);
}
lua_pop(L, 3);

return r;
}

core::vector2di read_rel_pos(lua_State *L, int index, const core::vector2di &parent_size, bool flip)
{
CHECK_TYPE(index, "position", LUA_TTABLE);
core::vector2di p;

lua_getfield(L, index, "x");
lua_getfield(L, index, "nx");
if (lua_isnumber(L, -1)) {
p.X = parent_size.X - lua_tonumber(L, -1) - (s32)flip;
} else {
p.X = lua_tonumber(L, -2);
}
lua_pop(L, 2);

lua_getfield(L, index, "y");
lua_getfield(L, index, "ny");
if (lua_isnumber(L, -1)) {
p.Y = parent_size.Y - lua_tonumber(L, -1) - (s32)flip;
} else {
p.Y = lua_tonumber(L, -2);
}
lua_pop(L, 2);

return p;
}

aabb3f read_aabb3f(lua_State *L, int index, f32 scale)
{
aabb3f box;
Expand Down
3 changes: 3 additions & 0 deletions src/script/common/c_converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ bool read_color (lua_State *L, int index,
video::SColor *color);
bool is_color_table (lua_State *L, int index);

core::recti read_rel_rect(lua_State *L, int index, const core::vector2di &parent_size);
core::vector2di read_rel_pos(lua_State *L, int index, const core::vector2di &parent_size, bool flip = false);

aabb3f read_aabb3f (lua_State *L, int index, f32 scale);
v3s16 read_v3s16 (lua_State *L, int index);
std::vector<aabb3f> read_aabb3f_vector (lua_State *L, int index, f32 scale);
Expand Down
39 changes: 39 additions & 0 deletions src/script/cpp_api/s_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "common/c_converter.h"
#include "common/c_content.h"
#include "s_item.h"
#include "script/lua_api/l_renderer.h"

void ScriptApiClient::on_mods_loaded()
{
Expand Down Expand Up @@ -125,6 +126,44 @@ void ScriptApiClient::environment_step(float dtime)
}
}

void ScriptApiClient::on_draw(float dtime)
{
SCRIPTAPI_PRECHECKHEADER

lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_draw");

lua_pushnumber(L, dtime);

ModApiRenderer::start_callback();
try {
runCallbacks(1, RUN_CALLBACKS_MODE_FIRST);
} catch (LuaError &e) {
getClient()->setFatalError(std::string("Client on_draw: ") + e.what() + "\n" +
script_get_backtrace(L));
}
ModApiRenderer::end_callback();
}

void ScriptApiClient::on_predraw(float dtime)
{
SCRIPTAPI_PRECHECKHEADER

lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_predraw");

lua_pushnumber(L, dtime);

ModApiRenderer::start_callback();
try {
runCallbacks(1, RUN_CALLBACKS_MODE_FIRST);
} catch (LuaError &e) {
getClient()->setFatalError(std::string("Client on_predraw: ") + e.what() + "\n" +
script_get_backtrace(L));
}
ModApiRenderer::end_callback();
}

void ScriptApiClient::on_formspec_input(const std::string &formname,
const StringMap &fields)
{
Expand Down
2 changes: 2 additions & 0 deletions src/script/cpp_api/s_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ class ScriptApiClient : virtual public ScriptApiBase
void on_hp_modification(int32_t newhp);
void on_death();
void environment_step(float dtime);
void on_draw(float dtime);
void on_predraw(float dtime);
void on_formspec_input(const std::string &formname, const StringMap &fields);

bool on_dignode(v3s16 p, MapNode node);
Expand Down
1 change: 1 addition & 0 deletions src/script/lua_api/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ set(client_SCRIPT_LUA_API_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/l_mainmenu.cpp
${CMAKE_CURRENT_SOURCE_DIR}/l_minimap.cpp
${CMAKE_CURRENT_SOURCE_DIR}/l_particles_local.cpp
${CMAKE_CURRENT_SOURCE_DIR}/l_renderer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/l_sound.cpp
${CMAKE_CURRENT_SOURCE_DIR}/l_storage.cpp
PARENT_SCOPE)
837 changes: 837 additions & 0 deletions src/script/lua_api/l_renderer.cpp

Large diffs are not rendered by default.

138 changes: 138 additions & 0 deletions src/script/lua_api/l_renderer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*
Minetest
Copyright (C) 2021 Vincent Robinson (v-rob) <robinsonvincent89@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#pragma once

#include "client/client.h"
#include "client/fontengine.h"
#include "client/tile.h"
#include "irrlichttypes_extrabloated.h"
#include "lua_api/l_base.h"

class LuaTexture;

class ModApiRenderer : public ModApiBase
{
private:
friend class LuaTexture;

bool in_callback = false;
video::ITexture *current_target = nullptr;

static int l_get_driver_info(lua_State *L);
static int l_get_optimal_texture_size(lua_State *L);

public:
//! Sets the render target and associated information. A texture of `nullptr` sets
//! the screen as the target. Should be used unless the render target will be set and
//! reset in the same function, in which case setRenderTarget is better.
static bool set_render_target(video::IVideoDriver *driver, video::ITexture *texture,
bool clear = false, video::SColor color = 0x0);

//! Functions for starting and ending when it is allowed to draw to textures
//! or the window.
static void start_callback();
static void end_callback();

static void Initialize(lua_State *L, int top);
};

class LuaTexture : public ModApiBase
{
private:
static const char class_name[];
static const luaL_Reg methods[];

video::ITexture *texture;
bool is_ref;
bool is_target = false;

//! Checks if the texture is writable and sets it as target if it is.
static bool set_as_target(LuaTexture *t, video::IVideoDriver *driver,
bool clear = false, video::SColor color = 0x0);

static int l_is_readable(lua_State *L);
static int l_is_writable(lua_State *L);
static int l_get_size(lua_State *L);

static int l_fill(lua_State *L);
static int l_draw_pixel(lua_State *L);
static int l_draw_rect(lua_State *L);
static int l_draw_texture(lua_State *L);
static int l_draw_text(lua_State *L);

static int l_new(lua_State *L);
static int l_copy(lua_State *L);
static int l_ref(lua_State *L);

static int gc_object(lua_State *L);

public:
//! Create `minetest.window` and leave it at the top of the stack
static int create_window(lua_State *L);

//! Read a LuaTexture from the Lua stack
static LuaTexture *read_object(lua_State *L, int index, bool allow_write_only);
//! Read a LuaTexture that can be drawn to from the Lua stack
static LuaTexture *read_writable_object(lua_State *L, int index, bool allow_write_only);
//! Read an in-game texture string from the Lua stack
static video::ITexture *read_in_game_texture(lua_State *L, int index);
//! Read any type of texture from the Lua stack
static video::ITexture *read_texture(lua_State *L, int index, bool allow_write_only);

//! Returns the dimensions of this texture
core::vector2di getSize();
//! Returns the stored texture itself
video::ITexture *getTexture() { return texture; };

static void Register(lua_State *L);
};

class LuaFont : public ModApiBase
{
private:
static const char class_name[];
static const luaL_Reg methods[];

gui::IGUIFont *font;

int size;
FontMode face;
bool bold;
bool italic;

static int l_get_metrics(lua_State *L);
static int l_get_text_width(lua_State *L);
static int l_to_table(lua_State *L);

static int l_create(lua_State *L);

static int gc_object(lua_State *L);

public:
//! Read a Font from the Lua stack
static LuaFont *read_object(lua_State *L, int index);
//! Read a font from the Lua stack, returning the default font if nil.
static gui::IGUIFont *read_font(lua_State *L, int index);

//! Returns the stored font itself
gui::IGUIFont *getFont() { return font; };

static void Register(lua_State *L);
};
4 changes: 4 additions & 0 deletions src/script/scripting_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "cpp_api/s_internal.h"
#include "lua_api/l_client.h"
#include "lua_api/l_env.h"
#include "lua_api/l_renderer.h"
#include "lua_api/l_item.h"
#include "lua_api/l_itemstackmeta.h"
#include "lua_api/l_minimap.h"
Expand Down Expand Up @@ -73,13 +74,16 @@ void ClientScripting::InitializeModApi(lua_State *L, int top)
LuaLocalPlayer::Register(L);
LuaCamera::Register(L);
ModChannelRef::Register(L);
LuaTexture::Register(L);
LuaFont::Register(L);

ModApiUtil::InitializeClient(L, top);
ModApiClient::Initialize(L, top);
ModApiStorage::Initialize(L, top);
ModApiEnvMod::InitializeClient(L, top);
ModApiChannels::Initialize(L, top);
ModApiParticlesLocal::Initialize(L, top);
ModApiRenderer::Initialize(L, top);
}

void ClientScripting::on_client_ready(LocalPlayer *localplayer)
Expand Down