1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-08-01 17:38:41 +00:00

Merge remote-tracking branch 'upstream/master' into Visuals-Vol-2

This commit is contained in:
Gefüllte Taubenbrust 2025-01-04 11:19:11 +01:00
commit 78de371f2d
123 changed files with 1506 additions and 1956 deletions

View file

@ -101,7 +101,7 @@ std::vector<DistanceSortedActiveObject> ActiveObjectMgr::getActiveSelectableObje
if (!obj)
continue;
aabb3f selection_box;
aabb3f selection_box{{0.0f, 0.0f, 0.0f}};
if (!obj->getSelectionBox(&selection_box))
continue;

View file

@ -430,7 +430,7 @@ void ClientEnvironment::getSelectedActiveObjects(
for (const auto &allObject : allObjects) {
ClientActiveObject *obj = allObject.obj;
aabb3f selection_box;
aabb3f selection_box{{0.0f, 0.0f, 0.0f}};
if (!obj->getSelectionBox(&selection_box))
continue;

View file

@ -301,24 +301,29 @@ void ClientLauncher::init_input()
else
input = new RealInputHandler(receiver);
if (g_settings->getBool("enable_joysticks")) {
irr::core::array<irr::SJoystickInfo> infos;
std::vector<irr::SJoystickInfo> joystick_infos;
if (g_settings->getBool("enable_joysticks"))
init_joysticks();
}
// Make sure this is called maximum once per
// irrlicht device, otherwise it will give you
// multiple events for the same joystick.
if (m_rendering_engine->get_raw_device()->activateJoysticks(infos)) {
infostream << "Joystick support enabled" << std::endl;
joystick_infos.reserve(infos.size());
for (u32 i = 0; i < infos.size(); i++) {
joystick_infos.push_back(infos[i]);
}
input->joystick.onJoystickConnect(joystick_infos);
} else {
errorstream << "Could not activate joystick support." << std::endl;
}
void ClientLauncher::init_joysticks()
{
irr::core::array<irr::SJoystickInfo> infos;
std::vector<irr::SJoystickInfo> joystick_infos;
// Make sure this is called maximum once per
// irrlicht device, otherwise it will give you
// multiple events for the same joystick.
if (!m_rendering_engine->get_raw_device()->activateJoysticks(infos)) {
errorstream << "Could not activate joystick support." << std::endl;
return;
}
infostream << "Joystick support enabled" << std::endl;
joystick_infos.reserve(infos.size());
for (u32 i = 0; i < infos.size(); i++) {
joystick_infos.push_back(infos[i]);
}
input->joystick.onJoystickConnect(joystick_infos);
}
void ClientLauncher::setting_changed_callback(const std::string &name, void *data)

View file

@ -26,6 +26,7 @@ private:
void init_args(GameStartData &start_data, const Settings &cmd_args);
bool init_engine();
void init_input();
void init_joysticks();
static void setting_changed_callback(const std::string &name, void *data);
void config_guienv();

View file

@ -22,16 +22,27 @@
#include <queue>
namespace {
// A helper struct
// data structure that groups block meshes by material
struct MeshBufListMaps
{
using MeshBufListMap = std::unordered_map<
video::SMaterial,
std::vector<std::pair<v3s16, scene::IMeshBuffer *>>
>;
// first = block pos
using MeshBuf = std::pair<v3s16, scene::IMeshBuffer*>;
using MeshBufList = std::vector<MeshBuf>;
using MeshBufListMap = std::unordered_map<video::SMaterial, MeshBufList>;
std::array<MeshBufListMap, MAX_TILE_LAYERS> maps;
bool empty() const
{
for (auto &map : maps) {
if (!map.empty())
return false;
}
return true;
}
void clear()
{
for (auto &map : maps)
@ -48,7 +59,67 @@ namespace {
auto &bufs = map[m]; // default constructs if non-existent
bufs.emplace_back(position, buf);
}
void addFromBlock(v3s16 block_pos, MapBlockMesh *block_mesh,
video::IVideoDriver *driver);
};
// reference to a mesh buffer used when rendering the map.
struct DrawDescriptor {
v3f m_pos; // world translation
bool m_reuse_material:1;
bool m_use_partial_buffer:1;
union {
scene::IMeshBuffer *m_buffer;
const PartialMeshBuffer *m_partial_buffer;
};
DrawDescriptor(v3f pos, scene::IMeshBuffer *buffer, bool reuse_material = true) :
m_pos(pos), m_reuse_material(reuse_material), m_use_partial_buffer(false),
m_buffer(buffer)
{}
DrawDescriptor(v3f pos, const PartialMeshBuffer *buffer) :
m_pos(pos), m_reuse_material(false), m_use_partial_buffer(true),
m_partial_buffer(buffer)
{}
video::SMaterial &getMaterial();
/// @return number of vertices drawn
u32 draw(video::IVideoDriver* driver);
};
using DrawDescriptorList = std::vector<DrawDescriptor>;
/// @brief Append vertices to a mesh buffer
/// @note does not update bounding box!
void appendToMeshBuffer(scene::SMeshBuffer *dst, const scene::IMeshBuffer *src, v3f translate)
{
const size_t vcount = dst->Vertices->Data.size();
const size_t icount = dst->Indices->Data.size();
assert(src->getVertexType() == video::EVT_STANDARD);
const auto vptr = static_cast<const video::S3DVertex*>(src->getVertices());
dst->Vertices->Data.insert(dst->Vertices->Data.end(),
vptr, vptr + src->getVertexCount());
// apply translation
for (size_t j = vcount; j < dst->Vertices->Data.size(); j++)
dst->Vertices->Data[j].Pos += translate;
const auto iptr = src->getIndices();
dst->Indices->Data.insert(dst->Indices->Data.end(),
iptr, iptr + src->getIndexCount());
// fixup indices
if (vcount != 0) {
for (size_t j = icount; j < dst->Indices->Data.size(); j++)
dst->Indices->Data[j] += vcount;
}
}
template <typename T>
inline T subtract_or_zero(T a, T b) {
return b >= a ? T(0) : (a - b);
}
}
/*
@ -90,8 +161,7 @@ ClientMap::ClientMap(
* the class is whith a name ;) Name property cames from ISceneNode base class.
*/
Name = "ClientMap";
m_box = aabb3f(-BS*1000000,-BS*1000000,-BS*1000000,
BS*1000000,BS*1000000,BS*1000000);
setAutomaticCulling(scene::EAC_OFF);
for (const auto &name : ClientMap_settings)
g_settings->registerChangedCallback(name, on_settings_changed, this);
@ -299,7 +369,7 @@ void ClientMap::updateDrawList()
}
const v3s16 camera_block = getContainerPos(cam_pos_nodes, MAP_BLOCKSIZE);
m_drawlist = std::map<v3s16, MapBlock*, MapBlockComparer>(MapBlockComparer(camera_block));
m_drawlist = decltype(m_drawlist)(MapBlockComparer(camera_block));
auto is_frustum_culled = m_client->getCamera()->getFrustumCuller();
@ -692,23 +762,132 @@ void ClientMap::touchMapBlocks()
g_profiler->avg("MapBlocks loaded [#]", blocks_loaded);
}
void MeshBufListMaps::addFromBlock(v3s16 block_pos, MapBlockMesh *block_mesh,
video::IVideoDriver *driver)
{
for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
scene::IMesh *mesh = block_mesh->getMesh(layer);
assert(mesh);
u32 c = mesh->getMeshBufferCount();
for (u32 i = 0; i < c; i++) {
scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
auto &material = buf->getMaterial();
auto *rnd = driver->getMaterialRenderer(material.MaterialType);
bool transparent = rnd && rnd->isTransparent();
if (!transparent)
add(buf, block_pos, layer);
}
}
}
/**
* Copy a list of mesh buffers into the draw order, while potentially
* merging some.
* @param src buffer list
* @param dst draw order
* @param get_world_pos returns translation for a buffer
* @param buffer_trash output container for temporary mesh buffers
* @return number of buffers that were merged
*/
template <typename F, typename C>
static u32 transformBuffersToDrawOrder(
const MeshBufListMaps::MeshBufList &src, DrawDescriptorList &draw_order,
F get_world_pos, C &buffer_trash)
{
/**
* This is a tradeoff between time spent merging buffers and time spent
* due to excess drawcalls.
* Testing has shown that the ideal value is in the low hundreds, as extra
* CPU work quickly eats up the benefits.
* In MTG landscape scenes this was found to save around 20-40% of drawcalls.
*
* NOTE: if you attempt to test this with quicktune, it won't give you valid
* results since HW buffers stick around and Irrlicht handles large amounts
* inefficiently.
*
* TODO: as a next step we should cache merged meshes, so they do not need
* to be re-built *and* can be kept in GPU memory.
*/
const u32 target_min_vertices = g_settings->getU32("mesh_buffer_min_vertices");
const auto draw_order_pre = draw_order.size();
auto *driver = RenderingEngine::get_video_driver();
// check if we can even merge anything
u32 can_merge = 0;
u32 total_vtx = 0, total_idx = 0;
for (auto &pair : src) {
if (pair.second->getVertexCount() < target_min_vertices) {
can_merge++;
total_vtx += pair.second->getVertexCount();
total_idx += pair.second->getIndexCount();
}
}
scene::SMeshBuffer *tmp = nullptr;
const auto &finish_buf = [&] () {
if (tmp) {
draw_order.emplace_back(v3f(0), tmp);
total_vtx = subtract_or_zero(total_vtx, tmp->getVertexCount());
total_idx = subtract_or_zero(total_idx, tmp->getIndexCount());
// Upload buffer here explicitly to give the driver some
// extra time to get it ready before drawing.
tmp->setHardwareMappingHint(scene::EHM_STREAM);
driver->updateHardwareBuffer(tmp->getVertexBuffer());
driver->updateHardwareBuffer(tmp->getIndexBuffer());
}
tmp = nullptr;
};
// iterate in reverse to get closest blocks first
for (auto it = src.rbegin(); it != src.rend(); ++it) {
v3f translate = get_world_pos(it->first);
auto *buf = it->second;
if (can_merge < 2 || buf->getVertexCount() >= target_min_vertices) {
draw_order.emplace_back(translate, buf);
continue;
}
bool new_buffer = false;
if (!tmp)
new_buffer = true;
else if (tmp->getVertexCount() + buf->getVertexCount() > U16_MAX)
new_buffer = true;
if (new_buffer) {
finish_buf();
tmp = new scene::SMeshBuffer();
buffer_trash.push_back(tmp);
assert(tmp->getPrimitiveType() == buf->getPrimitiveType());
tmp->Material = buf->getMaterial();
// preallocate
tmp->Vertices->Data.reserve(total_vtx);
tmp->Indices->Data.reserve(total_idx);
}
appendToMeshBuffer(tmp, buf, translate);
}
finish_buf();
// first call needs to set the material
if (draw_order.size() > draw_order_pre)
draw_order[draw_order_pre].m_reuse_material = false;
return can_merge < 2 ? 0 : can_merge;
}
void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
{
ZoneScoped;
bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
const bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
std::string prefix;
if (pass == scene::ESNRP_SOLID)
prefix = "renderMap(SOLID): ";
else
prefix = "renderMap(TRANSPARENT): ";
/*
This is called two times per frame, reset on the non-transparent one
*/
if (pass == scene::ESNRP_SOLID)
m_last_drawn_sectors.clear();
prefix = "renderMap(TRANS): ";
/*
Get animation parameters
@ -719,16 +898,16 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
const v3f camera_position = m_camera_position;
/*
Get all blocks and draw all visible ones
*/
const auto mesh_grid = m_client->getMeshGrid();
// Gets world position from block map position
const auto get_block_wpos = [&] (v3s16 pos) -> v3f {
return intToFloat(mesh_grid.getMeshPos(pos) * MAP_BLOCKSIZE - m_camera_offset, BS);
};
u32 vertex_count = 0;
u32 drawcall_count = 0;
u32 merged_count = 0;
// For limiting number of mesh animations per frame
u32 mesh_animate_count = 0;
//u32 mesh_animate_count_far = 0;
/*
Update transparent meshes
@ -737,18 +916,18 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
updateTransparentMeshBuffers();
/*
Draw the selected MapBlocks
Collect everything we need to draw
*/
TimeTaker tt_collect("");
MeshBufListMaps grouped_buffers;
std::vector<DrawDescriptor> draw_order;
video::SMaterial previous_material;
std::vector<scene::IMeshBuffer*> buffer_trash;
DrawDescriptorList draw_order;
auto is_frustum_culled = m_client->getCamera()->getFrustumCuller();
const MeshGrid mesh_grid = m_client->getMeshGrid();
for (auto &i : m_drawlist) {
v3s16 block_pos = i.first;
const v3s16 block_pos = i.first;
MapBlock *block = i.second;
MapBlockMesh *block_mesh = block->mesh;
@ -789,49 +968,28 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
// In transparent pass, the mesh will give us
// the partial buffers in the correct order
for (auto &buffer : block_mesh->getTransparentBuffers())
draw_order.emplace_back(block_pos, &buffer);
}
else {
// otherwise, group buffers across meshes
// using MeshBufListMaps
for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
scene::IMesh *mesh = block_mesh->getMesh(layer);
assert(mesh);
u32 c = mesh->getMeshBufferCount();
for (u32 i = 0; i < c; i++) {
scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
video::SMaterial& material = buf->getMaterial();
video::IMaterialRenderer* rnd =
driver->getMaterialRenderer(material.MaterialType);
bool transparent = (rnd && rnd->isTransparent());
if (!transparent) {
if (buf->getVertexCount() == 0)
errorstream << "Block [" << analyze_block(block)
<< "] contains an empty meshbuf" << std::endl;
grouped_buffers.add(buf, block_pos, layer);
}
}
}
draw_order.emplace_back(get_block_wpos(block_pos), &buffer);
} else {
// Otherwise, group them
grouped_buffers.addFromBlock(block_pos, block_mesh, driver);
}
}
// Capture draw order for all solid meshes
assert(!is_transparent_pass || grouped_buffers.empty());
for (auto &map : grouped_buffers.maps) {
for (auto &list : map) {
// iterate in reverse to draw closest blocks first
for (auto it = list.second.rbegin(); it != list.second.rend(); ++it) {
draw_order.emplace_back(it->first, it->second, it != list.second.rbegin());
}
merged_count += transformBuffersToDrawOrder(
list.second, draw_order, get_block_wpos, buffer_trash);
}
}
TimeTaker draw("Drawing mesh buffers");
g_profiler->avg(prefix + "collecting [ms]", tt_collect.stop(true));
TimeTaker tt_draw("");
core::matrix4 m; // Model matrix
v3f offset = intToFloat(m_camera_offset, BS);
u32 vertex_count = 0;
u32 drawcall_count = 0;
u32 material_swaps = 0;
// Render all mesh buffers in order
@ -867,18 +1025,17 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
material.TextureLayers[ShadowRenderer::TEXTURE_LAYER_SHADOW].Texture = nullptr;
}
v3f block_wpos = intToFloat(mesh_grid.getMeshPos(descriptor.m_pos) * MAP_BLOCKSIZE, BS);
m.setTranslation(block_wpos - offset);
m.setTranslation(descriptor.m_pos);
driver->setTransform(video::ETS_WORLD, m);
vertex_count += descriptor.draw(driver);
}
g_profiler->avg(prefix + "draw meshes [ms]", draw.stop(true));
g_profiler->avg(prefix + "draw meshes [ms]", tt_draw.stop(true));
// Log only on solid pass because values are the same
if (pass == scene::ESNRP_SOLID) {
g_profiler->avg("renderMap(): animated meshes [#]", mesh_animate_count);
g_profiler->avg(prefix + "merged buffers [#]", merged_count);
}
if (pass == scene::ESNRP_TRANSPARENT) {
@ -888,6 +1045,9 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
g_profiler->avg(prefix + "vertices drawn [#]", vertex_count);
g_profiler->avg(prefix + "drawcalls [#]", drawcall_count);
g_profiler->avg(prefix + "material swaps [#]", material_swaps);
for (auto &x : buffer_trash)
x->drop();
}
static bool getVisibleBrightness(Map *map, const v3f &p0, v3f dir, float step,
@ -1096,12 +1256,15 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
else
prefix = "renderMap(SHADOW SOLID): ";
u32 drawcall_count = 0;
u32 vertex_count = 0;
const auto mesh_grid = m_client->getMeshGrid();
// Gets world position from block map position
const auto get_block_wpos = [&] (v3s16 pos) -> v3f {
return intToFloat(mesh_grid.getMeshPos(pos) * MAP_BLOCKSIZE - m_camera_offset, BS);
};
MeshBufListMaps grouped_buffers;
std::vector<DrawDescriptor> draw_order;
std::vector<scene::IMeshBuffer*> buffer_trash;
DrawDescriptorList draw_order;
std::size_t count = 0;
std::size_t meshes_per_frame = m_drawlist_shadow.size() / total_frames + 1;
@ -1113,7 +1276,6 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
return;
}
const MeshGrid mesh_grid = m_client->getMeshGrid();
for (const auto &i : m_drawlist_shadow) {
// only process specific part of the list & break early
++count;
@ -1136,52 +1298,25 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
// In transparent pass, the mesh will give us
// the partial buffers in the correct order
for (auto &buffer : block->mesh->getTransparentBuffers())
draw_order.emplace_back(block_pos, &buffer);
}
else {
// otherwise, group buffers across meshes
// using MeshBufListMaps
MapBlockMesh *mapBlockMesh = block->mesh;
assert(mapBlockMesh);
for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
scene::IMesh *mesh = mapBlockMesh->getMesh(layer);
assert(mesh);
u32 c = mesh->getMeshBufferCount();
for (u32 i = 0; i < c; i++) {
scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
video::SMaterial &mat = buf->getMaterial();
auto rnd = driver->getMaterialRenderer(mat.MaterialType);
bool transparent = rnd && rnd->isTransparent();
if (!transparent)
grouped_buffers.add(buf, block_pos, layer);
}
}
draw_order.emplace_back(get_block_wpos(block_pos), &buffer);
} else {
// Otherwise, group them
grouped_buffers.addFromBlock(block_pos, block->mesh, driver);
}
}
u32 buffer_count = 0;
for (auto &map : grouped_buffers.maps)
for (auto &list : map)
buffer_count += list.second.size();
draw_order.reserve(draw_order.size() + buffer_count);
// Capture draw order for all solid meshes
for (auto &map : grouped_buffers.maps) {
for (auto &list : map) {
// iterate in reverse to draw closest blocks first
for (auto it = list.second.rbegin(); it != list.second.rend(); ++it)
draw_order.emplace_back(it->first, it->second, it != list.second.rbegin());
transformBuffersToDrawOrder(
list.second, draw_order, get_block_wpos, buffer_trash);
}
}
TimeTaker draw("Drawing shadow mesh buffers");
TimeTaker draw("");
core::matrix4 m; // Model matrix
v3f offset = intToFloat(m_camera_offset, BS);
u32 drawcall_count = 0;
u32 vertex_count = 0;
u32 material_swaps = 0;
// Render all mesh buffers in order
@ -1221,8 +1356,7 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
++material_swaps;
}
v3f block_wpos = intToFloat(mesh_grid.getMeshPos(descriptor.m_pos) * MAP_BLOCKSIZE, BS);
m.setTranslation(block_wpos - offset);
m.setTranslation(descriptor.m_pos);
driver->setTransform(video::ETS_WORLD, m);
vertex_count += descriptor.draw(driver);
@ -1232,12 +1366,16 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
video::SMaterial clean;
clean.BlendOperation = video::EBO_ADD;
driver->setMaterial(clean); // reset material to defaults
// FIXME: why is this here?
driver->draw3DLine(v3f(), v3f(), video::SColor(0));
g_profiler->avg(prefix + "draw meshes [ms]", draw.stop(true));
g_profiler->avg(prefix + "vertices drawn [#]", vertex_count);
g_profiler->avg(prefix + "drawcalls [#]", drawcall_count);
g_profiler->avg(prefix + "material swaps [#]", material_swaps);
for (auto &x : buffer_trash)
x->drop();
}
/*
@ -1347,12 +1485,12 @@ void ClientMap::updateTransparentMeshBuffers()
m_needs_update_transparent_meshes = false;
}
video::SMaterial &ClientMap::DrawDescriptor::getMaterial()
video::SMaterial &DrawDescriptor::getMaterial()
{
return (m_use_partial_buffer ? m_partial_buffer->getBuffer() : m_buffer)->getMaterial();
}
u32 ClientMap::DrawDescriptor::draw(video::IVideoDriver* driver)
u32 DrawDescriptor::draw(video::IVideoDriver* driver)
{
if (m_use_partial_buffer) {
m_partial_buffer->draw(driver);

View file

@ -115,7 +115,6 @@ private:
// update the vertex order in transparent mesh buffers
void updateTransparentMeshBuffers();
// Orders blocks by distance to the camera
class MapBlockComparer
{
@ -133,30 +132,6 @@ private:
v3s16 m_camera_block;
};
// reference to a mesh buffer used when rendering the map.
struct DrawDescriptor {
v3s16 m_pos;
union {
scene::IMeshBuffer *m_buffer;
const PartialMeshBuffer *m_partial_buffer;
};
bool m_reuse_material:1;
bool m_use_partial_buffer:1;
DrawDescriptor(v3s16 pos, scene::IMeshBuffer *buffer, bool reuse_material) :
m_pos(pos), m_buffer(buffer), m_reuse_material(reuse_material), m_use_partial_buffer(false)
{}
DrawDescriptor(v3s16 pos, const PartialMeshBuffer *buffer) :
m_pos(pos), m_partial_buffer(buffer), m_reuse_material(false), m_use_partial_buffer(true)
{}
video::SMaterial &getMaterial();
/// @return index count
u32 draw(video::IVideoDriver* driver);
};
Client *m_client;
RenderingEngine *m_rendering_engine;
@ -177,8 +152,6 @@ private:
std::map<v3s16, MapBlock*> m_drawlist_shadow;
bool m_needs_update_drawlist;
std::set<v2s16> m_last_drawn_sectors;
bool m_cache_trilinear_filter;
bool m_cache_bilinear_filter;
bool m_cache_anistropic_filter;

View file

@ -160,7 +160,7 @@ private:
// Was the mesh ever generated?
bool m_mesh_valid = false;
aabb3f m_box;
aabb3f m_box{{0.0f, 0.0f, 0.0f}};
v2f m_origin;
u16 m_cloud_radius_i;
u32 m_seed;

View file

@ -13,7 +13,6 @@
#include "nodedef.h"
#include "client/tile.h"
#include "mesh.h"
#include <IMeshManipulator.h>
#include "client/meshgen/collector.h"
#include "client/renderingengine.h"
#include "client.h"
@ -60,12 +59,10 @@ static const auto &quad_indices = quad_indices_02;
const std::string MapblockMeshGenerator::raillike_groupname = "connect_to_raillike";
MapblockMeshGenerator::MapblockMeshGenerator(MeshMakeData *input, MeshCollector *output,
scene::IMeshManipulator *mm):
MapblockMeshGenerator::MapblockMeshGenerator(MeshMakeData *input, MeshCollector *output):
data(input),
collector(output),
nodedef(data->nodedef),
meshmanip(mm),
blockpos_nodes(data->m_blockpos * MAP_BLOCKSIZE),
smooth_liquids(g_settings->getBool("enable_water_reflections"))
{

View file

@ -5,7 +5,6 @@
#pragma once
#include "nodedef.h"
#include <IMeshManipulator.h>
struct MeshMakeData;
struct MeshCollector;
@ -46,8 +45,7 @@ struct LightFrame {
class MapblockMeshGenerator
{
public:
MapblockMeshGenerator(MeshMakeData *input, MeshCollector *output,
scene::IMeshManipulator *mm);
MapblockMeshGenerator(MeshMakeData *input, MeshCollector *output);
void generate();
void renderSingle(content_t node, u8 param2 = 0x00);
@ -56,7 +54,6 @@ private:
MeshCollector *const collector;
const NodeDefManager *const nodedef;
scene::IMeshManipulator *const meshmanip;
const v3s16 blockpos_nodes;

View file

@ -140,7 +140,7 @@ unsigned int FontEngine::getLineHeight(const FontSpec &spec)
gui::IGUIFont *font = getFont(spec);
return font->getDimension(L"Some unimportant example String").Height
+ font->getKerningHeight();
+ font->getKerning(L'S').Y;
}
/******************************************************************************/

View file

@ -1752,8 +1752,8 @@ void Game::updateProfilers(const RunStats &stats, const FpsControl &draw_times,
if (stats2.Drawcalls > 0)
g_profiler->avg("Irr: primitives per drawcall",
stats2.PrimitivesDrawn / float(stats2.Drawcalls));
g_profiler->avg("Irr: buffers uploaded", stats2.HWBuffersUploaded);
g_profiler->avg("Irr: buffers uploaded (bytes)", stats2.HWBuffersUploadedSize);
g_profiler->avg("Irr: HW buffers uploaded", stats2.HWBuffersUploaded);
g_profiler->avg("Irr: HW buffers active", stats2.HWBuffersActive);
}
void Game::updateStats(RunStats *stats, const FpsControl &draw_times,
@ -3246,7 +3246,7 @@ PointedThing Game::updatePointedThing(
hud->pointing_at_object = true;
runData.selected_object = client->getEnv().getActiveObject(result.object_id);
aabb3f selection_box;
aabb3f selection_box{{0.0f, 0.0f, 0.0f}};
if (show_entity_selectionbox && runData.selected_object->doShowSelectionBox() &&
runData.selected_object->getSelectionBox(&selection_box)) {
v3f pos = runData.selected_object->getPosition();

View file

@ -104,11 +104,11 @@ void GameUI::update(const RunStats &stats, Client *client, MapDrawControl *draw_
os << std::fixed
<< PROJECT_NAME_C " " << g_version_hash
<< " | FPS: " << fps
<< std::setprecision(0)
<< std::setprecision(fps >= 100 ? 1 : 0)
<< " | drawtime: " << m_drawtime_avg << "ms"
<< std::setprecision(1)
<< " | dtime jitter: "
<< (stats.dtime_jitter.max_fraction * 100.0) << "%"
<< (stats.dtime_jitter.max_fraction * 100.0f) << "%"
<< std::setprecision(1)
<< " | view range: "
<< (draw_control->range_all ? "All" : itos(draw_control->wanted_range))

View file

@ -874,7 +874,6 @@ void Hud::drawSelectionMesh()
{
if (m_mode == HIGHLIGHT_NONE || (m_mode == HIGHLIGHT_HALO && !m_selection_mesh))
return;
const video::SMaterial oldmaterial = driver->getMaterial2D();
driver->setMaterial(m_selection_material);
const core::matrix4 oldtransform = driver->getTransform(video::ETS_WORLD);
@ -910,7 +909,6 @@ void Hud::drawSelectionMesh()
driver->drawMeshBuffer(buf);
}
}
driver->setMaterial(oldmaterial);
driver->setTransform(video::ETS_WORLD, oldtransform);
}
@ -935,17 +933,11 @@ void Hud::drawBlockBounds()
return;
}
video::SMaterial old_material = driver->getMaterial2D();
driver->setMaterial(m_block_bounds_material);
u16 mesh_chunk_size = std::max<u16>(1, g_settings->getU16("client_mesh_chunk"));
v3s16 pos = player->getStandingNodePos();
v3s16 block_pos(
floorf((float) pos.X / MAP_BLOCKSIZE),
floorf((float) pos.Y / MAP_BLOCKSIZE),
floorf((float) pos.Z / MAP_BLOCKSIZE)
);
v3s16 block_pos = getContainerPos(player->getStandingNodePos(), MAP_BLOCKSIZE);
v3f cam_offset = intToFloat(client->getCamera()->getOffset(), BS);
@ -988,8 +980,6 @@ void Hud::drawBlockBounds()
choose_color(block_pos.Y, block_pos.Z)
);
}
driver->setMaterial(old_material);
}
void Hud::updateSelectionMesh(const v3s16 &camera_offset)

View file

@ -148,17 +148,6 @@ static void imageCleanTransparentWithInlining(video::IImage *src, u32 threshold)
}
}
/* Fill in RGB values for transparent pixels, to correct for odd colors
* appearing at borders when blending. This is because many PNG optimizers
* like to discard RGB values of transparent pixels, but when blending then
* with non-transparent neighbors, their RGB values will show up nonetheless.
*
* This function modifies the original image in-place.
*
* Parameter "threshold" is the alpha level below which pixels are considered
* transparent. Should be 127 when the texture is used with ALPHA_CHANNEL_REF,
* 0 when alpha blending is used.
*/
void imageCleanTransparent(video::IImage *src, u32 threshold)
{
if (src->getColorFormat() == video::ECF_A8R8G8B8)
@ -167,13 +156,109 @@ void imageCleanTransparent(video::IImage *src, u32 threshold)
imageCleanTransparentWithInlining<false>(src, threshold);
}
/* Scale a region of an image into another image, using nearest-neighbor with
* anti-aliasing; treat pixels as crisp rectangles, but blend them at boundaries
* to prevent non-integer scaling ratio artifacts. Note that this may cause
* some blending at the edges where pixels don't line up perfectly, but this
* filter is designed to produce the most accurate results for both upscaling
* and downscaling.
*/
/**********************************/
namespace {
// For more colorspace transformations, see for example
// <https://github.com/tobspr/GLSL-Color-Spaces/blob/master/ColorSpaces.inc.glsl>
inline float linear_to_srgb_component(float v)
{
if (v > 0.0031308f)
return 1.055f * powf(v, 1.0f / 2.4f) - 0.055f;
return 12.92f * v;
}
inline float srgb_to_linear_component(float v)
{
if (v > 0.04045f)
return powf((v + 0.055f) / 1.055f, 2.4f);
return v / 12.92f;
}
template <float (*F)(float)>
struct LUT8 {
std::array<float, 256> t;
LUT8() {
for (size_t i = 0; i < t.size(); i++)
t[i] = F(i / 255.0f);
}
};
LUT8<srgb_to_linear_component> srgb_to_linear_lut;
v3f srgb_to_linear(const video::SColor col_srgb)
{
v3f col(srgb_to_linear_lut.t[col_srgb.getRed()],
srgb_to_linear_lut.t[col_srgb.getGreen()],
srgb_to_linear_lut.t[col_srgb.getBlue()]);
return col;
}
video::SColor linear_to_srgb(const v3f col_linear)
{
v3f col;
// we can't LUT this without losing precision, but thankfully we call
// it just once :)
col.X = linear_to_srgb_component(col_linear.X);
col.Y = linear_to_srgb_component(col_linear.Y);
col.Z = linear_to_srgb_component(col_linear.Z);
col *= 255.0f;
col.X = core::clamp<float>(col.X, 0.0f, 255.0f);
col.Y = core::clamp<float>(col.Y, 0.0f, 255.0f);
col.Z = core::clamp<float>(col.Z, 0.0f, 255.0f);
return video::SColor(0xff, myround(col.X), myround(col.Y),
myround(col.Z));
}
}
template <bool IS_A8R8G8B8>
static video::SColor imageAverageColorInline(const video::IImage *src)
{
void *const src_data = src->getData();
const core::dimension2du dim = src->getDimension();
auto get_pixel = [=](u32 x, u32 y) -> video::SColor {
if constexpr (IS_A8R8G8B8) {
return reinterpret_cast<u32 *>(src_data)[y*dim.Width + x];
} else {
return src->getPixel(x, y);
}
};
u32 total = 0;
v3f col_acc;
// limit runtime cost
const u32 stepx = std::max(1U, dim.Width / 16),
stepy = std::max(1U, dim.Height / 16);
for (u32 x = 0; x < dim.Width; x += stepx) {
for (u32 y = 0; y < dim.Height; y += stepy) {
video::SColor c = get_pixel(x, y);
if (c.getAlpha() > 0) {
total++;
col_acc += srgb_to_linear(c);
}
}
}
video::SColor ret(0, 0, 0, 0);
if (total > 0) {
col_acc /= total;
ret = linear_to_srgb(col_acc);
}
ret.setAlpha(255);
return ret;
}
video::SColor imageAverageColor(const video::IImage *img)
{
if (img->getColorFormat() == video::ECF_A8R8G8B8)
return imageAverageColorInline<true>(img);
else
return imageAverageColorInline<false>(img);
}
/**********************************/
void imageScaleNNAA(video::IImage *src, const core::rect<s32> &srcrect, video::IImage *dest)
{
double sx, sy, minsx, maxsx, minsy, maxsy, area, ra, ga, ba, aa, pw, ph, pa;

View file

@ -6,6 +6,7 @@
#include "irrlichttypes.h"
#include <rect.h>
#include <SColor.h>
namespace irr::video
{
@ -26,6 +27,10 @@ namespace irr::video
*/
void imageCleanTransparent(video::IImage *src, u32 threshold);
/* Returns the gamma-correct average color of the image, with transparent pixels
* ignored. */
video::SColor imageAverageColor(const video::IImage *img);
/* Scale a region of an image into another image, using nearest-neighbor with
* anti-aliasing; treat pixels as crisp rectangles, but blend them at boundaries
* to prevent non-integer scaling ratio artifacts. Note that this may cause

View file

@ -31,8 +31,7 @@ void SourceImageCache::insert(const std::string &name, video::IImage *img, bool
{
assert(img); // Pre-condition
// Remove old image
std::map<std::string, video::IImage*>::iterator n;
n = m_images.find(name);
auto n = m_images.find(name);
if (n != m_images.end()){
if (n->second)
n->second->drop();
@ -63,8 +62,7 @@ void SourceImageCache::insert(const std::string &name, video::IImage *img, bool
video::IImage* SourceImageCache::get(const std::string &name)
{
std::map<std::string, video::IImage*>::iterator n;
n = m_images.find(name);
auto n = m_images.find(name);
if (n != m_images.end())
return n->second;
return nullptr;
@ -73,8 +71,7 @@ video::IImage* SourceImageCache::get(const std::string &name)
// Primarily fetches from cache, secondarily tries to read from filesystem
video::IImage* SourceImageCache::getOrLoad(const std::string &name)
{
std::map<std::string, video::IImage*>::iterator n;
n = m_images.find(name);
auto n = m_images.find(name);
if (n != m_images.end()){
n->second->grab(); // Grab for caller
return n->second;
@ -166,13 +163,13 @@ static void draw_crack(video::IImage *crack, video::IImage *dst,
video::IVideoDriver *driver, u8 tiles = 1);
// Brighten image
void brighten(video::IImage *image);
static void brighten(video::IImage *image);
// Parse a transform name
u32 parseImageTransform(std::string_view s);
static u32 parseImageTransform(std::string_view s);
// Apply transform to image dimension
core::dimension2d<u32> imageTransformDimension(u32 transform, core::dimension2d<u32> dim);
static core::dimension2du imageTransformDimension(u32 transform, core::dimension2du dim);
// Apply transform to image data
void imageTransform(u32 transform, video::IImage *src, video::IImage *dst);
static void imageTransform(u32 transform, video::IImage *src, video::IImage *dst);
inline static void applyShadeFactor(video::SColor &color, u32 factor)
{
@ -289,7 +286,7 @@ static video::IImage *createInventoryCubeImage(
return result;
}
static std::string unescape_string(const std::string &str, const char esc = '\\')
static std::string unescape_string(std::string_view str, const char esc = '\\')
{
std::string out;
size_t pos = 0, cpos;
@ -300,7 +297,8 @@ static std::string unescape_string(const std::string &str, const char esc = '\\'
out += str.substr(pos);
break;
}
out += str.substr(pos, cpos - pos) + str[cpos + 1];
out += str.substr(pos, cpos - pos);
out += str[cpos + 1];
pos = cpos + 2;
}
return out;
@ -312,7 +310,7 @@ static std::string unescape_string(const std::string &str, const char esc = '\\'
Ensure no other references to these images are being held, as one may
get dropped and switched with a new image.
*/
void upscaleImagesToMatchLargest(video::IImage *& img1,
static void upscaleImagesToMatchLargest(video::IImage *& img1,
video::IImage *& img2)
{
core::dimension2d<u32> dim1 = img1->getDimension();
@ -340,7 +338,7 @@ void upscaleImagesToMatchLargest(video::IImage *& img1,
}
}
void blitBaseImage(video::IImage* &src, video::IImage* &dst)
static void blitBaseImage(video::IImage* &src, video::IImage* &dst)
{
//infostream<<"Blitting "<<part_of_name<<" on base"<<std::endl;
upscaleImagesToMatchLargest(dst, src);
@ -411,9 +409,10 @@ void blit_pixel(video::SColor src_col, video::SColor &dst_col)
dst_col.set(dst_a, dst.r, dst.g, dst.b);
}
} // namespace
} // namespace (anonymous)
template<bool overlay>
void blit_with_alpha(video::IImage *src, video::IImage *dst, v2s32 dst_pos,
static void blit_with_alpha(video::IImage *src, video::IImage *dst, v2s32 dst_pos,
v2u32 size)
{
if (dst->getColorFormat() != video::ECF_A8R8G8B8)
@ -427,13 +426,12 @@ void blit_with_alpha(video::IImage *src, video::IImage *dst, v2s32 dst_pos,
video::IVideoDriver *driver = RenderingEngine::get_video_driver();
video::IImage *src_converted = driver->createImage(video::ECF_A8R8G8B8,
src_dim);
if (!src_converted)
throw BaseException("blit_with_alpha() failed to convert the "
"source image to ECF_A8R8G8B8.");
sanity_check(src_converted != nullptr);
src->copyTo(src_converted);
src = src_converted;
drop_src = true;
}
video::SColor *pixels_src =
reinterpret_cast<video::SColor *>(src->getData());
video::SColor *pixels_dst =
@ -453,6 +451,7 @@ void blit_with_alpha(video::IImage *src, video::IImage *dst, v2s32 dst_pos,
blit_pixel<overlay>(pixels_src[i_src++], pixels_dst[i_dst++]);
}
}
if (drop_src)
src->drop();
}
@ -726,7 +725,7 @@ static void apply_mask(video::IImage *mask, video::IImage *dst,
}
}
video::IImage *create_crack_image(video::IImage *crack, s32 frame_index,
static video::IImage *create_crack_image(video::IImage *crack, s32 frame_index,
core::dimension2d<u32> size, u8 tiles, video::IVideoDriver *driver)
{
core::dimension2d<u32> strip_size = crack->getDimension();
@ -804,7 +803,7 @@ static void draw_crack(video::IImage *crack, video::IImage *dst,
crack_scaled->drop();
}
void brighten(video::IImage *image)
static void brighten(video::IImage *image)
{
if (image == NULL)
return;
@ -822,7 +821,7 @@ void brighten(video::IImage *image)
}
}
u32 parseImageTransform(std::string_view s)
static u32 parseImageTransform(std::string_view s)
{
int total_transform = 0;
@ -872,15 +871,15 @@ u32 parseImageTransform(std::string_view s)
return total_transform;
}
core::dimension2d<u32> imageTransformDimension(u32 transform, core::dimension2d<u32> dim)
static core::dimension2du imageTransformDimension(u32 transform, core::dimension2du dim)
{
if (transform % 2 == 0)
return dim;
return core::dimension2d<u32>(dim.Height, dim.Width);
return core::dimension2du(dim.Height, dim.Width);
}
void imageTransform(u32 transform, video::IImage *src, video::IImage *dst)
static void imageTransform(u32 transform, video::IImage *src, video::IImage *dst)
{
if (src == NULL || dst == NULL)
return;
@ -925,48 +924,6 @@ void imageTransform(u32 transform, video::IImage *src, video::IImage *dst)
}
}
namespace {
// For more colorspace transformations, see for example
// https://github.com/tobspr/GLSL-Color-Spaces/blob/master/ColorSpaces.inc.glsl
inline float linear_to_srgb_component(float v)
{
if (v > 0.0031308f)
return 1.055f * powf(v, 1.0f / 2.4f) - 0.055f;
return 12.92f * v;
}
inline float srgb_to_linear_component(float v)
{
if (v > 0.04045f)
return powf((v + 0.055f) / 1.055f, 2.4f);
return v / 12.92f;
}
v3f srgb_to_linear(const video::SColor col_srgb)
{
v3f col(col_srgb.getRed(), col_srgb.getGreen(), col_srgb.getBlue());
col /= 255.0f;
col.X = srgb_to_linear_component(col.X);
col.Y = srgb_to_linear_component(col.Y);
col.Z = srgb_to_linear_component(col.Z);
return col;
}
video::SColor linear_to_srgb(const v3f col_linear)
{
v3f col;
col.X = linear_to_srgb_component(col_linear.X);
col.Y = linear_to_srgb_component(col_linear.Y);
col.Z = linear_to_srgb_component(col_linear.Z);
col *= 255.0f;
col.X = core::clamp<float>(col.X, 0.0f, 255.0f);
col.Y = core::clamp<float>(col.Y, 0.0f, 255.0f);
col.Z = core::clamp<float>(col.Z, 0.0f, 255.0f);
return video::SColor(0xff, myround(col.X), myround(col.Y),
myround(col.Z));
}
}
///////////////////////////
// ImageSource Functions //
@ -1017,18 +974,12 @@ bool ImageSource::generateImagePart(std::string_view part_of_name,
std::string part_s(part_of_name);
source_image_names.insert(part_s);
video::IImage *image = m_sourcecache.getOrLoad(part_s);
if (!image) {
// Do not create the dummy texture
if (part_of_name.empty())
return true;
// Do not create normalmap dummies
if (str_ends_with(part_of_name, "_normal.png")) {
warningstream << "generateImagePart(): Could not load normal map \""
<< part_of_name << "\"" << std::endl;
return true;
}
errorstream << "generateImagePart(): Could not load image \""
<< part_of_name << "\" while building texture; "
"Creating a dummy image" << std::endl;
@ -1040,16 +991,15 @@ bool ImageSource::generateImagePart(std::string_view part_of_name,
myrand()%256,myrand()%256));
}
// If base image is NULL, load as base.
if (baseimg == NULL)
// load as base or blit
if (!baseimg)
{
/*
Copy it this way to get an alpha channel.
Otherwise images with alpha cannot be blitted on
images that don't have alpha in the original file.
*/
core::dimension2d<u32> dim = image->getDimension();
baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
baseimg = driver->createImage(video::ECF_A8R8G8B8, image->getDimension());
image->copyTo(baseimg);
}
// Else blit on base.
@ -1705,14 +1655,17 @@ bool ImageSource::generateImagePart(std::string_view part_of_name,
return false;
}
// blit or use as base
if (baseimg) {
blitBaseImage(pngimg, baseimg);
} else {
core::dimension2d<u32> dim = pngimg->getDimension();
baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
pngimg->drop();
} else if (pngimg->getColorFormat() != video::ECF_A8R8G8B8) {
baseimg = driver->createImage(video::ECF_A8R8G8B8, pngimg->getDimension());
pngimg->copyTo(baseimg);
pngimg->drop();
} else {
baseimg = pngimg;
}
pngimg->drop();
}
/*
[hsl:hue:saturation:lightness
@ -1945,32 +1898,7 @@ video::IImage* ImageSource::generateImage(std::string_view name,
return baseimg;
}
video::SColor ImageSource::getImageAverageColor(const video::IImage &image)
void ImageSource::insertSourceImage(const std::string &name, video::IImage *img, bool prefer_local)
{
video::SColor c(0, 0, 0, 0);
u32 total = 0;
v3f col_acc(0, 0, 0);
core::dimension2d<u32> dim = image.getDimension();
u16 step = 1;
if (dim.Width > 16)
step = dim.Width / 16;
for (u16 x = 0; x < dim.Width; x += step) {
for (u16 y = 0; y < dim.Width; y += step) {
c = image.getPixel(x,y);
if (c.getAlpha() > 0) {
total++;
col_acc += srgb_to_linear(c);
}
}
}
if (total > 0) {
col_acc /= total;
c = linear_to_srgb(col_acc);
}
c.setAlpha(255);
return c;
}
void ImageSource::insertSourceImage(const std::string &name, video::IImage *img, bool prefer_local) {
m_sourcecache.insert(name, img, prefer_local);
}

View file

@ -5,7 +5,7 @@
#pragma once
#include <IImage.h>
#include <map>
#include <unordered_map>
#include <set>
#include <string>
@ -28,7 +28,7 @@ public:
// Primarily fetches from cache, secondarily tries to read from filesystem.
video::IImage *getOrLoad(const std::string &name);
private:
std::map<std::string, video::IImage*> m_images;
std::unordered_map<std::string, video::IImage*> m_images;
};
// Generates images using texture modifiers, and caches source images.
@ -45,9 +45,6 @@ struct ImageSource {
// Insert a source image into the cache without touching the filesystem.
void insertSourceImage(const std::string &name, video::IImage *img, bool prefer_local);
// TODO should probably be moved elsewhere
static video::SColor getImageAverageColor(const video::IImage &image);
private:
// Generate image based on a string like "stone.png" or "[crack:1:0".

View file

@ -75,10 +75,8 @@ static aabb3f getNodeBoundingBox(const std::vector<aabb3f> &nodeboxes)
if (nodeboxes.empty())
return aabb3f(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
aabb3f b_max;
std::vector<aabb3f>::const_iterator it = nodeboxes.begin();
b_max = aabb3f(it->MinEdge, it->MaxEdge);
auto it = nodeboxes.begin();
aabb3f b_max(it->MinEdge, it->MaxEdge);
++it;
for (; it != nodeboxes.end(); ++it)

View file

@ -633,8 +633,7 @@ MapBlockMesh::MapBlockMesh(Client *client, MeshMakeData *data, v3s16 camera_offs
*/
{
MapblockMeshGenerator(data, &collector,
client->getSceneManager()->getMeshManipulator()).generate();
MapblockMeshGenerator(data, &collector).generate();
}
/*

View file

@ -105,8 +105,7 @@ void scaleMesh(scene::IMesh *mesh, v3f scale)
if (mesh == NULL)
return;
aabb3f bbox;
bbox.reset(0, 0, 0);
aabb3f bbox{{0.0f, 0.0f, 0.0f}};
u32 mc = mesh->getMeshBufferCount();
for (u32 j = 0; j < mc; j++) {
@ -134,8 +133,7 @@ void translateMesh(scene::IMesh *mesh, v3f vec)
if (mesh == NULL)
return;
aabb3f bbox;
bbox.reset(0, 0, 0);
aabb3f bbox{{0.0f, 0.0f, 0.0f}};
u32 mc = mesh->getMeshBufferCount();
for (u32 j = 0; j < mc; j++) {
@ -296,8 +294,7 @@ void rotateMeshBy6dFacedir(scene::IMesh *mesh, u8 facedir)
void recalculateBoundingBox(scene::IMesh *src_mesh)
{
aabb3f bbox;
bbox.reset(0,0,0);
aabb3f bbox{{0.0f, 0.0f, 0.0f}};
for (u16 j = 0; j < src_mesh->getMeshBufferCount(); j++) {
scene::IMeshBuffer *buf = src_mesh->getMeshBuffer(j);
buf->recalculateBoundingBox();

View file

@ -619,15 +619,22 @@ const core::aabbox3df &ParticleBuffer::getBoundingBox() const
if (!m_bounding_box_dirty)
return m_mesh_buffer->BoundingBox;
core::aabbox3df box;
core::aabbox3df box{{0, 0, 0}};
bool first = true;
for (u16 i = 0; i < m_count; i++) {
// check if this index is used
static_assert(quad_indices[1] != 0);
if (m_mesh_buffer->getIndices()[6 * i + 1] == 0)
continue;
for (u16 j = 0; j < 4; j++)
box.addInternalPoint(m_mesh_buffer->getPosition(i * 4 + j));
for (u16 j = 0; j < 4; j++) {
const auto pos = m_mesh_buffer->getPosition(i * 4 + j);
if (first)
box.reset(pos);
else
box.addInternalPoint(pos);
first = false;
}
}
m_mesh_buffer->BoundingBox = box;

View file

@ -135,7 +135,7 @@ static irr::IrrlichtDevice *createDevice(SIrrlichtCreationParameters params, std
{
if (requested_driver) {
params.DriverType = *requested_driver;
verbosestream << "Trying video driver " << getVideoDriverName(params.DriverType) << std::endl;
infostream << "Trying video driver " << getVideoDriverName(params.DriverType) << std::endl;
if (auto *device = createDeviceEx(params))
return device;
errorstream << "Failed to initialize the " << getVideoDriverName(params.DriverType) << " video driver" << std::endl;
@ -147,7 +147,7 @@ static irr::IrrlichtDevice *createDevice(SIrrlichtCreationParameters params, std
if (fallback_driver == video::EDT_NULL || fallback_driver == requested_driver)
continue;
params.DriverType = fallback_driver;
verbosestream << "Trying video driver " << getVideoDriverName(params.DriverType) << std::endl;
infostream << "Trying video driver " << getVideoDriverName(params.DriverType) << std::endl;
if (auto *device = createDeviceEx(params))
return device;
}
@ -374,16 +374,16 @@ void RenderingEngine::draw_load_screen(const std::wstring &text,
std::vector<video::E_DRIVER_TYPE> RenderingEngine::getSupportedVideoDrivers()
{
// Only check these drivers. We do not support software and D3D in any capacity.
// Order by preference (best first)
// ordered by preference (best first)
static const video::E_DRIVER_TYPE glDrivers[] = {
video::EDT_OPENGL,
video::EDT_OPENGL3,
video::EDT_OPENGL,
video::EDT_OGLES2,
video::EDT_NULL,
};
std::vector<video::E_DRIVER_TYPE> drivers;
for (video::E_DRIVER_TYPE driver: glDrivers) {
for (auto driver : glDrivers) {
if (IrrlichtDevice::isDriverSupported(driver))
drivers.push_back(driver);
}

View file

@ -19,7 +19,6 @@
#include <IMaterialRendererServices.h>
#include <IShaderConstantSetCallBack.h>
#include "client/renderingengine.h"
#include <EShaderTypes.h>
#include "gettext.h"
#include "log.h"
#include "gamedef.h"
@ -615,10 +614,16 @@ ShaderInfo ShaderSource::generateShader(const std::string &name,
#define textureFlags texture2
)";
/// Unique name of this shader, for debug/logging
std::string log_name = name;
/* Define constants for node and object shaders */
const bool node_shader = drawtype != NodeDrawType_END;
if (node_shader) {
log_name.append(" mat=").append(itos(material_type))
.append(" draw=").append(itos(drawtype));
bool use_discard = fully_programmable;
if (!use_discard) {
// workaround for a certain OpenGL implementation lacking GL_ALPHA_TEST
@ -775,18 +780,15 @@ ShaderInfo ShaderSource::generateShader(const std::string &name,
geometry_shader_ptr = geometry_shader.c_str();
}
irr_ptr<ShaderCallback> cb{new ShaderCallback(m_setter_factories)};
infostream<<"Compiling high level shaders for "<<name<<std::endl;
auto cb = make_irr<ShaderCallback>(m_setter_factories);
infostream << "Compiling high level shaders for " << log_name << std::endl;
s32 shadermat = gpu->addHighLevelShaderMaterial(
vertex_shader.c_str(), nullptr, video::EVST_VS_1_1,
fragment_shader.c_str(), nullptr, video::EPST_PS_1_1,
geometry_shader_ptr, nullptr, video::EGST_GS_4_0, scene::EPT_TRIANGLES, scene::EPT_TRIANGLES, 0,
cb.get(), shaderinfo.base_material, 1);
vertex_shader.c_str(), fragment_shader.c_str(), geometry_shader_ptr,
log_name.c_str(), scene::EPT_TRIANGLES, scene::EPT_TRIANGLES, 0,
cb.get(), shaderinfo.base_material);
if (shadermat == -1) {
errorstream<<"generate_shader(): "
"failed to generate \""<<name<<"\", "
"addHighLevelShaderMaterial failed."
<<std::endl;
errorstream << "generateShader(): failed to generate shaders for "
<< log_name << ", addHighLevelShaderMaterial failed." << std::endl;
dumpShaderProgram(warningstream, "Vertex", vertex_shader);
dumpShaderProgram(warningstream, "Fragment", fragment_shader);
dumpShaderProgram(warningstream, "Geometry", geometry_shader);

View file

@ -14,10 +14,9 @@
#include "client/client.h"
#include "client/clientmap.h"
#include "profiler.h"
#include "EShaderTypes.h"
#include "IGPUProgrammingServices.h"
#include "IMaterialRenderer.h"
#include <IVideoDriver.h>
#include "IVideoDriver.h"
ShadowRenderer::ShadowRenderer(IrrlichtDevice *device, Client *client) :
m_smgr(device->getSceneManager()), m_driver(device->getVideoDriver()),
@ -539,10 +538,9 @@ void ShadowRenderer::createShaders()
m_shadow_depth_cb = new ShadowDepthShaderCB();
depth_shader = gpu->addHighLevelShaderMaterial(
readShaderFile(depth_shader_vs).c_str(), "vertexMain",
video::EVST_VS_1_1,
readShaderFile(depth_shader_fs).c_str(), "pixelMain",
video::EPST_PS_1_2, m_shadow_depth_cb, video::EMT_ONETEXTURE_BLEND);
readShaderFile(depth_shader_vs).c_str(),
readShaderFile(depth_shader_fs).c_str(), nullptr,
m_shadow_depth_cb, video::EMT_ONETEXTURE_BLEND);
if (depth_shader == -1) {
// upsi, something went wrong loading shader.
@ -578,10 +576,9 @@ void ShadowRenderer::createShaders()
m_shadow_depth_entity_cb = new ShadowDepthShaderCB();
depth_shader_entities = gpu->addHighLevelShaderMaterial(
readShaderFile(depth_shader_vs).c_str(), "vertexMain",
video::EVST_VS_1_1,
readShaderFile(depth_shader_fs).c_str(), "pixelMain",
video::EPST_PS_1_2, m_shadow_depth_entity_cb);
readShaderFile(depth_shader_vs).c_str(),
readShaderFile(depth_shader_fs).c_str(), nullptr,
m_shadow_depth_entity_cb);
if (depth_shader_entities == -1) {
// upsi, something went wrong loading shader.
@ -616,10 +613,9 @@ void ShadowRenderer::createShaders()
m_shadow_mix_cb = new shadowScreenQuadCB();
m_screen_quad = new shadowScreenQuad();
mixcsm_shader = gpu->addHighLevelShaderMaterial(
readShaderFile(depth_shader_vs).c_str(), "vertexMain",
video::EVST_VS_1_1,
readShaderFile(depth_shader_fs).c_str(), "pixelMain",
video::EPST_PS_1_2, m_shadow_mix_cb);
readShaderFile(depth_shader_vs).c_str(),
readShaderFile(depth_shader_fs).c_str(), nullptr,
m_shadow_mix_cb);
m_screen_quad->getMaterial().MaterialType =
(video::E_MATERIAL_TYPE)mixcsm_shader;
@ -655,10 +651,9 @@ void ShadowRenderer::createShaders()
m_shadow_depth_trans_cb = new ShadowDepthShaderCB();
depth_shader_trans = gpu->addHighLevelShaderMaterial(
readShaderFile(depth_shader_vs).c_str(), "vertexMain",
video::EVST_VS_1_1,
readShaderFile(depth_shader_fs).c_str(), "pixelMain",
video::EPST_PS_1_2, m_shadow_depth_trans_cb);
readShaderFile(depth_shader_vs).c_str(),
readShaderFile(depth_shader_fs).c_str(), nullptr,
m_shadow_depth_trans_cb);
if (depth_shader_trans == -1) {
// upsi, something went wrong loading shader.

View file

@ -50,8 +50,6 @@ Sky::Sky(s32 id, RenderingEngine *rendering_engine, ITextureSource *tsrc, IShade
m_seed = (u64)myrand() << 32 | myrand();
setAutomaticCulling(scene::EAC_OFF);
m_box.MaxEdge.set(0, 0, 0);
m_box.MinEdge.set(0, 0, 0);
m_sky_params = SkyboxDefaults::getSkyDefaults();
m_sun_params = SkyboxDefaults::getSunDefaults();

View file

@ -122,7 +122,7 @@ public:
}
private:
aabb3f m_box;
aabb3f m_box{{0.0f, 0.0f, 0.0f}};
video::SMaterial m_materials[SKY_MATERIAL_COUNT];
// How much sun & moon transition should affect horizon color
float m_horizon_blend()

View file

@ -121,7 +121,6 @@ public:
// Shall be called from the main thread.
void rebuildImagesAndTextures();
video::ITexture* getNormalTexture(const std::string &name);
video::SColor getTextureAverageColor(const std::string &name);
private:
@ -488,40 +487,20 @@ void TextureSource::rebuildTexture(video::IVideoDriver *driver, TextureInfo &ti)
m_texture_trash.push_back(t_old);
}
video::ITexture* TextureSource::getNormalTexture(const std::string &name)
{
if (isKnownSourceImage("override_normal.png"))
return getTexture("override_normal.png");
std::string fname_base = name;
static const char *normal_ext = "_normal.png";
static const u32 normal_ext_size = strlen(normal_ext);
size_t pos = fname_base.find('.');
std::string fname_normal = fname_base.substr(0, pos) + normal_ext;
if (isKnownSourceImage(fname_normal)) {
// look for image extension and replace it
size_t i = 0;
while ((i = fname_base.find('.', i)) != std::string::npos) {
fname_base.replace(i, 4, normal_ext);
i += normal_ext_size;
}
return getTexture(fname_base);
}
return nullptr;
}
video::SColor TextureSource::getTextureAverageColor(const std::string &name)
{
video::IVideoDriver *driver = RenderingEngine::get_video_driver();
video::ITexture *texture = getTexture(name);
if (!texture)
return {0, 0, 0, 0};
// Note: this downloads the texture back from the GPU, which is pointless
video::IImage *image = driver->createImage(texture,
core::position2d<s32>(0, 0),
texture->getOriginalSize());
if (!image)
return {0, 0, 0, 0};
video::SColor c = ImageSource::getImageAverageColor(*image);
video::SColor c = imageAverageColor(image);
image->drop();
return c;

View file

@ -54,7 +54,6 @@ public:
*/
virtual Palette* getPalette(const std::string &name) = 0;
virtual bool isKnownSourceImage(const std::string &name)=0;
virtual video::ITexture* getNormalTexture(const std::string &name)=0;
virtual video::SColor getTextureAverageColor(const std::string &name)=0;
};
@ -75,7 +74,6 @@ public:
virtual void processQueue()=0;
virtual void insertSourceImage(const std::string &name, video::IImage *img)=0;
virtual void rebuildImagesAndTextures()=0;
virtual video::ITexture* getNormalTexture(const std::string &name)=0;
virtual video::SColor getTextureAverageColor(const std::string &name)=0;
};

View file

@ -83,10 +83,13 @@ struct TileLayer
void applyMaterialOptionsWithShaders(video::SMaterial &material) const;
/// @return is this layer semi-transparent?
bool isTransparent() const
{
// see also: the mapping in ShaderSource::generateShader()
switch (material_type) {
case TILE_MATERIAL_ALPHA:
case TILE_MATERIAL_PLAIN_ALPHA:
case TILE_MATERIAL_LIQUID_TRANSPARENT:
case TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT:
return true;

View file

@ -195,8 +195,7 @@ WieldMeshSceneNode::WieldMeshSceneNode(scene::ISceneManager *mgr, s32 id):
else
g_extrusion_mesh_cache->grab();
// Disable bounding box culling for this scene node
// since we won't calculate the bounding box.
// This class doesn't render anything, so disable culling.
setAutomaticCulling(scene::EAC_OFF);
// Create the child scene node
@ -299,8 +298,7 @@ static scene::SMesh *createSpecialNodeMesh(Client *client, MapNode n,
MeshMakeData mesh_make_data(client->ndef(), 1);
MeshCollector collector(v3f(0.0f * BS), v3f());
mesh_make_data.setSmoothLighting(false);
MapblockMeshGenerator gen(&mesh_make_data, &collector,
client->getSceneManager()->getMeshManipulator());
MapblockMeshGenerator gen(&mesh_make_data, &collector);
if (n.getParam2()) {
// keep it

View file

@ -134,7 +134,7 @@ private:
// Bounding box culling is disabled for this type of scene node,
// so this variable is just required so we can implement
// getBoundingBox() and is set to an empty box.
aabb3f m_bounding_box;
aabb3f m_bounding_box{{0, 0, 0}};
ShadowRenderer *m_shadow;
};

View file

@ -262,7 +262,7 @@ static void add_object_boxes(Environment *env,
{
auto process_object = [&cinfo] (ActiveObject *object) {
if (object && object->collideWithObjects()) {
aabb3f box;
aabb3f box{{0.0f, 0.0f, 0.0f}};
if (object->getCollisionBox(&box))
cinfo.emplace_back(object, 0, box);
}

View file

@ -104,6 +104,7 @@ void set_default_settings()
settings->setDefault("sound_extensions_blacklist", "");
settings->setDefault("mesh_generation_interval", "0");
settings->setDefault("mesh_generation_threads", "0");
settings->setDefault("mesh_buffer_min_vertices", "100");
settings->setDefault("free_move", "false");
settings->setDefault("pitch_move", "false");
settings->setDefault("fast_move", "false");
@ -311,7 +312,7 @@ void set_default_settings()
// Effects
settings->setDefault("enable_post_processing", "true");
settings->setDefault("post_processing_texture_bits", "16");
settings->setDefault("post_processing_texture_bits", "10");
settings->setDefault("directional_colored_fog", "true");
settings->setDefault("inventory_items_animations", "false");
settings->setDefault("mip_map", "false");

View file

@ -194,9 +194,9 @@ void GUIEditBoxWithScrollBar::draw()
mbegin = font->getDimension(s.c_str()).Width;
// deal with kerning
mbegin += font->getKerningWidth(
&((*txt_line)[realmbgn - start_pos]),
realmbgn - start_pos > 0 ? &((*txt_line)[realmbgn - start_pos - 1]) : 0);
mbegin += font->getKerning(
(*txt_line)[realmbgn - start_pos],
realmbgn - start_pos > 0 ? (*txt_line)[realmbgn - start_pos - 1] : 0).X;
lineStartPos = realmbgn - start_pos;
}
@ -242,7 +242,8 @@ void GUIEditBoxWithScrollBar::draw()
}
s = txt_line->subString(0, m_cursor_pos - start_pos);
charcursorpos = font->getDimension(s.c_str()).Width +
font->getKerningWidth(L"_", m_cursor_pos - start_pos > 0 ? &((*txt_line)[m_cursor_pos - start_pos - 1]) : 0);
font->getKerning(L'_',
m_cursor_pos - start_pos > 0 ? (*txt_line)[m_cursor_pos - start_pos - 1] : 0).X;
if (focus && (porting::getTimeMs() - m_blink_start_time) % 700 < 350) {
setTextRect(cursor_line);
@ -431,7 +432,7 @@ void GUIEditBoxWithScrollBar::setTextRect(s32 line)
d = font->getDimension(Text.c_str());
d.Height = AbsoluteRect.getHeight();
}
d.Height += font->getKerningHeight();
d.Height += font->getKerning(L'A').Y;
// justification
switch (m_halign) {
@ -536,7 +537,7 @@ void GUIEditBoxWithScrollBar::calculateScrollPos()
// calculate vertical scrolling
if (has_broken_text) {
irr::u32 line_height = font->getDimension(L"A").Height + font->getKerningHeight();
irr::u32 line_height = font->getDimension(L"A").Height + font->getKerning(L'A').Y;
// only up to 1 line fits?
if (line_height >= (irr::u32)m_frame_rect.getHeight()) {
m_vscroll_pos = 0;

View file

@ -79,7 +79,7 @@
*/
static unsigned int font_line_height(gui::IGUIFont *font)
{
return font->getDimension(L"Ay").Height + font->getKerningHeight();
return font->getDimension(L"Ay").Height + font->getKerning(L'A').Y;
}
inline u32 clamp_u8(s32 value)

View file

@ -85,8 +85,8 @@ void GUIInventoryList::draw()
v2s32 p((i % m_geom.X) * m_slot_spacing.X,
(i / m_geom.X) * m_slot_spacing.Y);
core::rect<s32> rect = imgrect + base_pos + p;
ItemStack item = ilist->getItem(item_i);
ItemStack orig_item = item;
const ItemStack &orig_item = ilist->getItem(item_i);
ItemStack item = orig_item;
bool selected = selected_item
&& m_invmgr->getInventory(selected_item->inventoryloc) == inv
@ -137,12 +137,26 @@ void GUIInventoryList::draw()
client, rotation_kind);
}
// Add hovering tooltip
// Add hovering tooltip. The tooltip disappears if any item is selected,
// including the currently hovered one.
bool show_tooltip = !item.empty() && hovering && !selected_item;
// Make it possible to see item tooltips on touchscreens
if (RenderingEngine::getLastPointerType() == PointerType::Touch) {
// Touchscreen users cannot hover over an item without selecting it.
// To allow touchscreen users to see item tooltips, we also show the
// tooltip if the item is selected and the finger is still on the
// source slot.
// The selected amount may be 0 in rare cases during "left-dragging"
// (used to distribute items evenly).
// In this case, the user doesn't see an item being dragged,
// so we don't show the tooltip.
// Note: `m_fs_menu->getSelectedAmount() != 0` below refers to the
// part of the selected item the user is dragging.
// `!item.empty()` would refer to the part of the selected item
// remaining in the source slot.
show_tooltip |= hovering && selected && m_fs_menu->getSelectedAmount() != 0;
}
if (show_tooltip) {
std::string tooltip = orig_item.getDescription(client->idef());
if (m_fs_menu->doTooltipAppendItemname())

View file

@ -29,52 +29,47 @@
TouchControls *g_touchcontrols;
static void load_button_texture(IGUIImage *gui_button, const std::string &path,
const recti &button_rect, ISimpleTextureSource *tsrc, video::IVideoDriver *driver)
void TouchControls::emitKeyboardEvent(EKEY_CODE keycode, bool pressed)
{
video::ITexture *texture = guiScalingImageButton(driver,
tsrc->getTexture(path), button_rect.getWidth(),
button_rect.getHeight());
SEvent e{};
e.EventType = EET_KEY_INPUT_EVENT;
e.KeyInput.Key = keycode;
e.KeyInput.Control = false;
e.KeyInput.Shift = false;
e.KeyInput.Char = 0;
e.KeyInput.PressedDown = pressed;
m_receiver->OnEvent(e);
}
void TouchControls::loadButtonTexture(IGUIImage *gui_button, const std::string &path)
{
auto rect = gui_button->getRelativePosition();
video::ITexture *texture = guiScalingImageButton(m_device->getVideoDriver(),
m_texturesource->getTexture(path), rect.getWidth(), rect.getHeight());
gui_button->setImage(texture);
gui_button->setScaleImage(true);
}
void button_info::emitAction(bool action, video::IVideoDriver *driver,
IEventReceiver *receiver, ISimpleTextureSource *tsrc)
void TouchControls::buttonEmitAction(button_info &btn, bool action)
{
if (keycode == KEY_UNKNOWN)
if (btn.keycode == KEY_UNKNOWN)
return;
SEvent translated{};
translated.EventType = EET_KEY_INPUT_EVENT;
translated.KeyInput.Key = keycode;
translated.KeyInput.Control = false;
translated.KeyInput.Shift = false;
translated.KeyInput.Char = 0;
emitKeyboardEvent(btn.keycode, action);
if (action) {
translated.KeyInput.PressedDown = true;
receiver->OnEvent(translated);
if (btn.toggleable == button_info::FIRST_TEXTURE) {
btn.toggleable = button_info::SECOND_TEXTURE;
loadButtonTexture(btn.gui_button.get(), btn.toggle_textures[1]);
if (toggleable == button_info::FIRST_TEXTURE) {
toggleable = button_info::SECOND_TEXTURE;
load_button_texture(gui_button.get(), toggle_textures[1],
gui_button->getRelativePosition(),
tsrc, driver);
} else if (toggleable == button_info::SECOND_TEXTURE) {
toggleable = button_info::FIRST_TEXTURE;
load_button_texture(gui_button.get(), toggle_textures[0],
gui_button->getRelativePosition(),
tsrc, driver);
} else if (btn.toggleable == button_info::SECOND_TEXTURE) {
btn.toggleable = button_info::FIRST_TEXTURE;
loadButtonTexture(btn.gui_button.get(), btn.toggle_textures[0]);
}
} else {
translated.KeyInput.PressedDown = false;
receiver->OnEvent(translated);
}
}
static bool buttons_handlePress(std::vector<button_info> &buttons, size_t pointer_id, IGUIElement *element,
video::IVideoDriver *driver, IEventReceiver *receiver, ISimpleTextureSource *tsrc)
bool TouchControls::buttonsHandlePress(std::vector<button_info> &buttons, size_t pointer_id, IGUIElement *element)
{
if (!element)
return false;
@ -87,7 +82,7 @@ static bool buttons_handlePress(std::vector<button_info> &buttons, size_t pointe
if (btn.pointer_ids.size() > 1)
return true;
btn.emitAction(true, driver, receiver, tsrc);
buttonEmitAction(btn, true);
btn.repeat_counter = -BUTTON_REPEAT_DELAY;
return true;
}
@ -97,8 +92,7 @@ static bool buttons_handlePress(std::vector<button_info> &buttons, size_t pointe
}
static bool buttons_handleRelease(std::vector<button_info> &buttons, size_t pointer_id,
video::IVideoDriver *driver, IEventReceiver *receiver, ISimpleTextureSource *tsrc)
bool TouchControls::buttonsHandleRelease(std::vector<button_info> &buttons, size_t pointer_id)
{
for (button_info &btn : buttons) {
auto it = std::find(btn.pointer_ids.begin(), btn.pointer_ids.end(), pointer_id);
@ -108,7 +102,7 @@ static bool buttons_handleRelease(std::vector<button_info> &buttons, size_t poin
if (!btn.pointer_ids.empty())
return true;
btn.emitAction(false, driver, receiver, tsrc);
buttonEmitAction(btn, false);
return true;
}
}
@ -116,8 +110,7 @@ static bool buttons_handleRelease(std::vector<button_info> &buttons, size_t poin
return false;
}
static bool buttons_step(std::vector<button_info> &buttons, float dtime,
video::IVideoDriver *driver, IEventReceiver *receiver, ISimpleTextureSource *tsrc)
bool TouchControls::buttonsStep(std::vector<button_info> &buttons, float dtime)
{
bool has_pointers = false;
@ -130,8 +123,8 @@ static bool buttons_step(std::vector<button_info> &buttons, float dtime,
if (btn.repeat_counter < BUTTON_REPEAT_INTERVAL)
continue;
btn.emitAction(false, driver, receiver, tsrc);
btn.emitAction(true, driver, receiver, tsrc);
buttonEmitAction(btn, false);
buttonEmitAction(btn, true);
btn.repeat_counter = 0.0f;
}
@ -340,8 +333,7 @@ void TouchControls::addButton(std::vector<button_info> &buttons, touch_gui_butto
{
IGUIImage *btn_gui_button = m_guienv->addImage(rect, nullptr, id);
btn_gui_button->setVisible(visible);
load_button_texture(btn_gui_button, image, rect,
m_texturesource, m_device->getVideoDriver());
loadButtonTexture(btn_gui_button, image);
button_info &btn = buttons.emplace_back();
btn.keycode = id_to_keycode(id);
@ -363,8 +355,7 @@ IGUIImage *TouchControls::makeButtonDirect(touch_gui_button_id id,
{
IGUIImage *btn_gui_button = m_guienv->addImage(rect, nullptr, id);
btn_gui_button->setVisible(visible);
load_button_texture(btn_gui_button, button_image_names[id], rect,
m_texturesource, m_device->getVideoDriver());
loadButtonTexture(btn_gui_button, button_image_names[id]);
return btn_gui_button;
}
@ -399,11 +390,9 @@ void TouchControls::handleReleaseEvent(size_t pointer_id)
m_pointer_pos.erase(pointer_id);
// handle buttons
if (buttons_handleRelease(m_buttons, pointer_id, m_device->getVideoDriver(),
m_receiver, m_texturesource))
if (buttonsHandleRelease(m_buttons, pointer_id))
return;
if (buttons_handleRelease(m_overflow_buttons, pointer_id, m_device->getVideoDriver(),
m_receiver, m_texturesource))
if (buttonsHandleRelease(m_overflow_buttons, pointer_id))
return;
if (m_has_move_id && pointer_id == m_move_id) {
@ -481,8 +470,7 @@ void TouchControls::translateEvent(const SEvent &event)
}
}
if (buttons_handlePress(m_overflow_buttons, pointer_id, element,
m_device->getVideoDriver(), m_receiver, m_texturesource))
if (buttonsHandlePress(m_overflow_buttons, pointer_id, element))
return;
toggleOverflowMenu();
@ -494,8 +482,7 @@ void TouchControls::translateEvent(const SEvent &event)
}
// handle buttons
if (buttons_handlePress(m_buttons, pointer_id, element,
m_device->getVideoDriver(), m_receiver, m_texturesource))
if (buttonsHandlePress(m_buttons, pointer_id, element))
return;
// handle hotbar
@ -614,16 +601,10 @@ void TouchControls::translateEvent(const SEvent &event)
void TouchControls::applyJoystickStatus()
{
if (m_joystick_triggers_aux1) {
SEvent translated{};
translated.EventType = EET_KEY_INPUT_EVENT;
translated.KeyInput.Key = id_to_keycode(aux1_id);
translated.KeyInput.PressedDown = false;
m_receiver->OnEvent(translated);
if (m_joystick_status_aux1) {
translated.KeyInput.PressedDown = true;
m_receiver->OnEvent(translated);
}
auto key = id_to_keycode(aux1_id);
emitKeyboardEvent(key, false);
if (m_joystick_status_aux1)
emitKeyboardEvent(key, true);
}
}
@ -639,8 +620,8 @@ void TouchControls::step(float dtime)
}
// simulate keyboard repeats
buttons_step(m_buttons, dtime, m_device->getVideoDriver(), m_receiver, m_texturesource);
buttons_step(m_overflow_buttons, dtime, m_device->getVideoDriver(), m_receiver, m_texturesource);
buttonsStep(m_buttons, dtime);
buttonsStep(m_overflow_buttons, dtime);
// joystick
applyJoystickStatus();

View file

@ -67,9 +67,6 @@ struct button_info
SECOND_TEXTURE
} toggleable = NOT_TOGGLEABLE;
std::string toggle_textures[2];
void emitAction(bool action, video::IVideoDriver *driver,
IEventReceiver *receiver, ISimpleTextureSource *tsrc);
};
@ -186,6 +183,19 @@ private:
std::shared_ptr<IGUIStaticText> m_status_text;
// Note: TouchControls intentionally uses IGUIImage instead of IGUIButton
// for its buttons. We only want static image display, not interactivity,
// from Irrlicht.
void emitKeyboardEvent(EKEY_CODE keycode, bool pressed);
void loadButtonTexture(IGUIImage *gui_button, const std::string &path);
void buttonEmitAction(button_info &btn, bool action);
bool buttonsHandlePress(std::vector<button_info> &buttons, size_t pointer_id, IGUIElement *element);
bool buttonsHandleRelease(std::vector<button_info> &buttons, size_t pointer_id);
bool buttonsStep(std::vector<button_info> &buttons, float dtime);
void toggleOverflowMenu();
void updateVisibility();
void releaseAll();

View file

@ -1,6 +1,7 @@
/*
CGUITTFont FreeType class for Irrlicht
Copyright (c) 2009-2010 John Norman
with changes from Luanti contributors:
Copyright (c) 2016 Nathanaëlle Courant
Copyright (c) 2023 Caleb Butler
@ -31,14 +32,13 @@
*/
#include <iostream>
#include "log.h"
#include "filesys.h"
#include "debug.h"
#include "CGUITTFont.h"
#include "CMeshBuffer.h"
#include "IFileSystem.h"
#include "IGUIEnvironment.h"
#include "IMeshManipulator.h"
#include "IMeshSceneNode.h"
#include "ISceneManager.h"
#include "ISceneNode.h"
namespace irr
{
@ -46,9 +46,9 @@ namespace gui
{
// Manages the FT_Face cache.
struct SGUITTFace : public virtual irr::IReferenceCounted
struct SGUITTFace : public irr::IReferenceCounted
{
SGUITTFace() : face_buffer(0), face_buffer_size(0)
SGUITTFace()
{
memset((void*)&face, 0, sizeof(FT_Face));
}
@ -56,46 +56,29 @@ struct SGUITTFace : public virtual irr::IReferenceCounted
~SGUITTFace()
{
FT_Done_Face(face);
delete[] face_buffer;
}
FT_Face face;
FT_Byte* face_buffer;
FT_Long face_buffer_size;
std::string face_buffer;
};
// Static variables.
FT_Library CGUITTFont::c_library;
std::map<io::path, SGUITTFace*> CGUITTFont::c_faces;
bool CGUITTFont::c_libraryLoaded = false;
scene::IMesh* CGUITTFont::shared_plane_ptr_ = 0;
scene::SMesh CGUITTFont::shared_plane_;
//
/** Checks that no dimension of the FT_BitMap object is negative. If either is
* negative, abort execution.
*/
inline void checkFontBitmapSize(const FT_Bitmap &bits)
{
if ((s32)bits.rows < 0 || (s32)bits.width < 0) {
std::cout << "Insane font glyph size. File: "
<< __FILE__ << " Line " << __LINE__
<< std::endl;
abort();
}
}
video::IImage* SGUITTGlyph::createGlyphImage(const FT_Bitmap& bits, video::IVideoDriver* driver) const
{
// Make sure our casts to s32 in the loops below will not cause problems
checkFontBitmapSize(bits);
if ((s32)bits.rows < 0 || (s32)bits.width < 0)
FATAL_ERROR("Insane font glyph size");
// Determine what our texture size should be.
// Add 1 because textures are inclusive-exclusive.
core::dimension2du d(bits.width + 1, bits.rows + 1);
core::dimension2du texture_size;
//core::dimension2du texture_size(bits.width + 1, bits.rows + 1);
// Create and load our image now.
video::IImage* image = 0;
@ -147,36 +130,36 @@ video::IImage* SGUITTGlyph::createGlyphImage(const FT_Bitmap& bits, video::IVide
for (s32 x = 0; x < (s32)bits.width; ++x)
{
image_data[y * image_pitch + x] |= static_cast<u32>(255.0f * (static_cast<float>(*row++) / gray_count)) << 24;
//data[y * image_pitch + x] |= ((u32)(*bitsdata++) << 24);
}
glyph_data += bits.pitch;
}
break;
}
default:
// TODO: error message?
errorstream << "CGUITTFont: unknown pixel mode " << (int)bits.pixel_mode << std::endl;
return 0;
}
return image;
}
void SGUITTGlyph::preload(u32 char_index, FT_Face face, video::IVideoDriver* driver, u32 font_size, const FT_Int32 loadFlags)
void SGUITTGlyph::preload(u32 char_index, FT_Face face, CGUITTFont *parent, u32 font_size, const FT_Int32 loadFlags)
{
if (isLoaded) return;
// Set the size of the glyph.
FT_Set_Pixel_Sizes(face, 0, font_size);
// Attempt to load the glyph.
if (FT_Load_Glyph(face, char_index, loadFlags) != FT_Err_Ok)
// TODO: error message?
auto err = FT_Load_Glyph(face, char_index, loadFlags);
if (err != FT_Err_Ok) {
warningstream << "SGUITTGlyph: failed to load glyph " << char_index
<< " with error: " << (int)err << std::endl;
return;
}
FT_GlyphSlot glyph = face->glyph;
FT_Bitmap bits = glyph->bitmap;
const FT_Bitmap &bits = glyph->bitmap;
// Setup the glyph information here:
advance = glyph->advance;
advance = core::vector2di(glyph->advance.x, glyph->advance.y);
offset = core::vector2di(glyph->bitmap_left, glyph->bitmap_top);
// Try to get the last page with available slots.
@ -187,7 +170,6 @@ void SGUITTGlyph::preload(u32 char_index, FT_Face face, video::IVideoDriver* dri
{
page = parent->createGlyphPage(bits.pixel_mode);
if (!page)
// TODO: add error message?
return;
}
@ -205,10 +187,7 @@ void SGUITTGlyph::preload(u32 char_index, FT_Face face, video::IVideoDriver* dri
--page->available_slots;
// We grab the glyph bitmap here so the data won't be removed when the next glyph is loaded.
surface = createGlyphImage(bits, driver);
// Set our glyph as loaded.
isLoaded = true;
surface = createGlyphImage(bits, parent->getDriver());
}
void SGUITTGlyph::unload()
@ -218,7 +197,8 @@ void SGUITTGlyph::unload()
surface->drop();
surface = 0;
}
isLoaded = false;
// reset isLoaded to false
source_rect = core::recti();
}
//////////////////////
@ -251,14 +231,13 @@ CGUITTFont* CGUITTFont::createTTFont(IGUIEnvironment *env, const io::path& filen
//! Constructor.
CGUITTFont::CGUITTFont(IGUIEnvironment *env)
: use_monochrome(false), use_transparency(true), use_hinting(true), use_auto_hinting(true),
batch_load_size(1), Device(0), Environment(env), Driver(0), GlobalKerningWidth(0), GlobalKerningHeight(0),
batch_load_size(1), Driver(0), GlobalKerningWidth(0), GlobalKerningHeight(0),
shadow_offset(0), shadow_alpha(0), fallback(0)
{
if (Environment)
{
if (env) {
// don't grab environment, to avoid circular references
Driver = Environment->getVideoDriver();
Driver = env->getVideoDriver();
}
if (Driver)
@ -270,13 +249,10 @@ shadow_offset(0), shadow_alpha(0), fallback(0)
bool CGUITTFont::load(const io::path& filename, const u32 size, const bool antialias, const bool transparency)
{
// Some sanity checks.
if (Environment == 0 || Driver == 0) return false;
if (!Driver) return false;
if (size == 0) return false;
if (filename.size() == 0) return false;
if (filename.empty()) return false;
io::IFileSystem* filesystem = Environment->getFileSystem();
irr::ILogger* logger = (Device != 0 ? Device->getLogger() : 0);
// FIXME: this is always null ^
this->size = size;
this->filename = filename;
@ -285,62 +261,33 @@ bool CGUITTFont::load(const io::path& filename, const u32 size, const bool antia
this->use_transparency = transparency;
update_load_flags();
// Log.
if (logger)
logger->log("CGUITTFont", (core::stringc(L"Creating new font: ") + filename + " " + core::stringc(size) + "pt " + (antialias ? "+antialias " : "-antialias ") + (transparency ? "+transparency" : "-transparency")).c_str(), irr::ELL_INFORMATION);
infostream << "CGUITTFont: Creating new font: " << filename.c_str() << " "
<< size << "pt " << (antialias ? "+antialias " : "-antialias ")
<< (transparency ? "+transparency" : "-transparency") << std::endl;
// Grab the face.
SGUITTFace* face = 0;
SGUITTFace* face = nullptr;
auto node = c_faces.find(filename);
if (node == c_faces.end())
{
if (node == c_faces.end()) {
face = new SGUITTFace();
if (!fs::ReadFile(filename.c_str(), face->face_buffer, true)) {
delete face;
return false;
}
// Create the face.
if (FT_New_Memory_Face(c_library,
reinterpret_cast<const FT_Byte*>(face->face_buffer.data()),
face->face_buffer.size(), 0, &face->face))
{
errorstream << "CGUITTFont: FT_New_Memory_Face failed." << std::endl;
delete face;
return false;
}
c_faces.emplace(filename, face);
if (filesystem)
{
// Read in the file data.
io::IReadFile* file = filesystem->createAndOpenFile(filename);
if (file == 0)
{
if (logger) logger->log("CGUITTFont", "Failed to open the file.", irr::ELL_INFORMATION);
c_faces.erase(filename);
delete face;
face = 0;
return false;
}
face->face_buffer = new FT_Byte[file->getSize()];
file->read(face->face_buffer, file->getSize());
face->face_buffer_size = file->getSize();
file->drop();
// Create the face.
if (FT_New_Memory_Face(c_library, face->face_buffer, face->face_buffer_size, 0, &face->face))
{
if (logger) logger->log("CGUITTFont", "FT_New_Memory_Face failed.", irr::ELL_INFORMATION);
c_faces.erase(filename);
delete face;
face = 0;
return false;
}
}
else
{
if (FT_New_Face(c_library, reinterpret_cast<const char*>(filename.c_str()), 0, &face->face))
{
if (logger) logger->log("CGUITTFont", "FT_New_Face failed.", irr::ELL_INFORMATION);
c_faces.erase(filename);
delete face;
face = 0;
return false;
}
}
}
else
{
} else {
// Using another instance of this face.
face = node->second;
face->grab();
@ -353,20 +300,12 @@ bool CGUITTFont::load(const io::path& filename, const u32 size, const bool antia
FT_Set_Pixel_Sizes(tt_face, size, 0);
font_metrics = tt_face->size->metrics;
verbosestream << tt_face->num_glyphs << " glyphs, ascender=" << font_metrics.ascender
<< " height=" << font_metrics.height << std::endl;
// Allocate our glyphs.
Glyphs.clear();
Glyphs.reallocate(tt_face->num_glyphs);
Glyphs.set_used(tt_face->num_glyphs);
for (FT_Long i = 0; i < tt_face->num_glyphs; ++i)
{
Glyphs[i].isLoaded = false;
Glyphs[i].glyph_page = 0;
Glyphs[i].source_rect = core::recti();
Glyphs[i].offset = core::vector2di();
Glyphs[i].advance = FT_Vector();
Glyphs[i].surface = 0;
Glyphs[i].parent = this;
}
// Cache the first 127 ascii characters.
u32 old_size = batch_load_size;
@ -444,7 +383,7 @@ CGUITTGlyphPage* CGUITTFont::getLastGlyphPage() const
return page;
}
CGUITTGlyphPage* CGUITTFont::createGlyphPage(const u8& pixel_mode)
CGUITTGlyphPage* CGUITTFont::createGlyphPage(const u8 pixel_mode)
{
CGUITTGlyphPage* page = 0;
@ -481,7 +420,8 @@ CGUITTGlyphPage* CGUITTFont::createGlyphPage(const u8& pixel_mode)
page_texture_size = max_texture_size;
if (!page->createPageTexture(pixel_mode, page_texture_size)) {
// TODO: add error message?
errorstream << "CGUITTGlyphPage: failed to create texture ("
<< page_texture_size.Width << "x" << page_texture_size.Height << ")" << std::endl;
delete page;
return 0;
}
@ -622,13 +562,12 @@ void CGUITTFont::draw(const EnrichedString &text, const core::rect<s32>& positio
else if (fallback != 0)
{
// Let the fallback font draw it, this isn't super efficient but hopefully that doesn't matter
wchar_t l1[] = { (wchar_t) currentChar, 0 }, l2 = (wchar_t) previousChar;
wchar_t l1[] = { (wchar_t) currentChar, 0 };
if (visible)
{
// Apply kerning.
offset.X += fallback->getKerningWidth(l1, &l2);
offset.Y += fallback->getKerningHeight();
offset += fallback->getKerning(*l1, (wchar_t) previousChar);
const u32 current_color = iter - utext.begin();
fallback->draw(core::stringw(l1),
@ -688,11 +627,6 @@ void CGUITTFont::draw(const EnrichedString &text, const core::rect<s32>& positio
}
}
core::dimension2d<u32> CGUITTFont::getCharDimension(const wchar_t ch) const
{
return core::dimension2d<u32>(getWidthFromCharacter(ch), getHeightFromCharacter(ch));
}
core::dimension2d<u32> CGUITTFont::getDimension(const wchar_t* text) const
{
return getDimension(convertWCharToU32String(text));
@ -759,21 +693,12 @@ core::dimension2d<u32> CGUITTFont::getDimension(const std::u32string& text) cons
return text_dimension;
}
inline u32 CGUITTFont::getWidthFromCharacter(wchar_t c) const
{
return getWidthFromCharacter((char32_t)c);
}
inline u32 CGUITTFont::getWidthFromCharacter(char32_t c) const
{
// Set the size of the face.
// This is because we cache faces and the face may have been set to a different size.
//FT_Set_Pixel_Sizes(tt_face, 0, size);
u32 n = getGlyphIndexByChar(c);
if (n > 0)
{
int w = Glyphs[n-1].advance.x / 64;
int w = Glyphs[n-1].advance.X / 64;
return w;
}
if (fallback != 0)
@ -787,17 +712,8 @@ inline u32 CGUITTFont::getWidthFromCharacter(char32_t c) const
else return (font_metrics.ascender / 64) / 2;
}
inline u32 CGUITTFont::getHeightFromCharacter(wchar_t c) const
{
return getHeightFromCharacter((char32_t)c);
}
inline u32 CGUITTFont::getHeightFromCharacter(char32_t c) const
{
// Set the size of the face.
// This is because we cache faces and the face may have been set to a different size.
//FT_Set_Pixel_Sizes(tt_face, 0, size);
u32 n = getGlyphIndexByChar(c);
if (n > 0)
{
@ -816,11 +732,6 @@ inline u32 CGUITTFont::getHeightFromCharacter(char32_t c) const
else return (font_metrics.ascender / 64) / 2;
}
u32 CGUITTFont::getGlyphIndexByChar(wchar_t c) const
{
return getGlyphIndexByChar((char32_t)c);
}
u32 CGUITTFont::getGlyphIndexByChar(char32_t c) const
{
// Get the glyph.
@ -831,7 +742,7 @@ u32 CGUITTFont::getGlyphIndexByChar(char32_t c) const
return 0;
// If our glyph is already loaded, don't bother doing any batch loading code.
if (glyph != 0 && Glyphs[glyph - 1].isLoaded)
if (glyph != 0 && Glyphs[glyph - 1].isLoaded())
return glyph;
// Determine our batch loading positions.
@ -850,9 +761,10 @@ u32 CGUITTFont::getGlyphIndexByChar(char32_t c) const
if (char_index)
{
SGUITTGlyph& glyph = Glyphs[char_index - 1];
if (!glyph.isLoaded)
if (!glyph.isLoaded())
{
glyph.preload(char_index, tt_face, Driver, size, load_flags);
auto *this2 = const_cast<CGUITTFont*>(this); // oh well
glyph.preload(char_index, tt_face, this2, size, load_flags);
Glyph_Pages[glyph.glyph_page]->pushGlyphToBePaged(&glyph);
}
}
@ -871,11 +783,10 @@ s32 CGUITTFont::getCharacterFromPos(const wchar_t* text, s32 pixel_x) const
s32 CGUITTFont::getCharacterFromPos(const std::u32string& text, s32 pixel_x) const
{
s32 x = 0;
//s32 idx = 0;
u32 character = 0;
char32_t previousChar = 0;
std::u32string::const_iterator iter = text.begin();
auto iter = text.begin();
while (iter != text.end())
{
char32_t c = *iter;
@ -906,28 +817,6 @@ void CGUITTFont::setKerningHeight(s32 kerning)
GlobalKerningHeight = kerning;
}
s32 CGUITTFont::getKerningWidth(const wchar_t* thisLetter, const wchar_t* previousLetter) const
{
if (tt_face == 0)
return GlobalKerningWidth;
if (thisLetter == 0 || previousLetter == 0)
return 0;
return getKerningWidth((char32_t)*thisLetter, (char32_t)*previousLetter);
}
s32 CGUITTFont::getKerningWidth(const char32_t thisLetter, const char32_t previousLetter) const
{
// Return only the kerning width.
return getKerning(thisLetter, previousLetter).X;
}
s32 CGUITTFont::getKerningHeight() const
{
// FreeType 2 currently doesn't return any height kerning information.
return GlobalKerningHeight;
}
core::vector2di CGUITTFont::getKerning(const wchar_t thisLetter, const wchar_t previousLetter) const
{
return getKerning((char32_t)thisLetter, (char32_t)previousLetter);
@ -949,11 +838,8 @@ core::vector2di CGUITTFont::getKerning(const char32_t thisLetter, const char32_t
// If we don't have this glyph, ask fallback font
if (n == 0)
{
if (fallback != 0) {
wchar_t l1 = (wchar_t) thisLetter, l2 = (wchar_t) previousLetter;
ret.X = fallback->getKerningWidth(&l1, &l2);
ret.Y = fallback->getKerningHeight();
}
if (fallback)
ret = fallback->getKerning((wchar_t) thisLetter, (wchar_t) previousLetter);
return ret;
}
@ -1029,196 +915,6 @@ video::ITexture* CGUITTFont::getPageTextureByIndex(const u32& page_index) const
return 0;
}
void CGUITTFont::createSharedPlane()
{
/*
2___3
| /|
| / | <-- plane mesh is like this, point 2 is (0,0), point 0 is (0, -1)
|/ | <-- the texture coords of point 2 is (0,0, point 0 is (0, 1)
0---1
*/
using namespace core;
using namespace video;
using namespace scene;
S3DVertex vertices[4];
u16 indices[6] = {0,2,3,3,1,0};
vertices[0] = S3DVertex(vector3df(0,-1,0), vector3df(0,0,-1), SColor(255,255,255,255), vector2df(0,1));
vertices[1] = S3DVertex(vector3df(1,-1,0), vector3df(0,0,-1), SColor(255,255,255,255), vector2df(1,1));
vertices[2] = S3DVertex(vector3df(0, 0,0), vector3df(0,0,-1), SColor(255,255,255,255), vector2df(0,0));
vertices[3] = S3DVertex(vector3df(1, 0,0), vector3df(0,0,-1), SColor(255,255,255,255), vector2df(1,0));
SMeshBuffer* buf = new SMeshBuffer();
buf->append(vertices, 4, indices, 6);
shared_plane_.addMeshBuffer( buf );
shared_plane_.setHardwareMappingHint(EHM_STATIC);
shared_plane_ptr_ = &shared_plane_;
buf->drop(); //the addMeshBuffer method will grab it, so we can drop this ptr.
}
core::dimension2d<u32> CGUITTFont::getDimensionUntilEndOfLine(const wchar_t* p) const
{
core::stringw s;
for (const wchar_t* temp = p; temp && *temp != '\0' && *temp != L'\r' && *temp != L'\n'; ++temp )
s.append(*temp);
return getDimension(s.c_str());
}
core::array<scene::ISceneNode*> CGUITTFont::addTextSceneNode(const wchar_t* text, scene::ISceneManager* smgr, scene::ISceneNode* parent, const video::SColor& color, bool center)
{
using namespace core;
using namespace video;
using namespace scene;
array<scene::ISceneNode*> container;
if (!Driver || !smgr) return container;
if (!parent)
parent = smgr->addEmptySceneNode(smgr->getRootSceneNode(), -1);
// if you don't specify parent, then we add an empty node attached to the root node
// this is generally undesirable.
if (!shared_plane_ptr_) //this points to a static mesh that contains the plane
createSharedPlane(); //if it's not initialized, we create one.
dimension2d<s32> text_size(getDimension(text)); //convert from unsigned to signed.
vector3df start_point(0, 0, 0), offset;
/** NOTICE:
Because we are considering adding texts into 3D world, all Y axis vectors are inverted.
**/
// There's currently no "vertical center" concept when you apply text scene node to the 3D world.
if (center)
{
offset.X = start_point.X = -text_size.Width / 2.f;
offset.Y = start_point.Y = +text_size.Height/ 2.f;
offset.X += (text_size.Width - getDimensionUntilEndOfLine(text).Width) >> 1;
}
// the default font material
SMaterial mat;
mat.ZWriteEnable = video::EZW_OFF;
mat.MaterialType = use_transparency ? video::EMT_TRANSPARENT_ALPHA_CHANNEL : video::EMT_SOLID;
mat.MaterialTypeParam = 0.01f;
wchar_t current_char = 0, previous_char = 0;
u32 n = 0;
array<u32> glyph_indices;
while (*text)
{
current_char = *text;
bool line_break=false;
if (current_char == L'\r') // Mac or Windows breaks
{
line_break = true;
if (*(text + 1) == L'\n') // Windows line breaks.
current_char = *(++text);
}
else if (current_char == L'\n') // Unix breaks
{
line_break = true;
}
if (line_break)
{
previous_char = 0;
offset.Y -= tt_face->size->metrics.ascender / 64;
offset.X = start_point.X;
if (center)
offset.X += (text_size.Width - getDimensionUntilEndOfLine(text+1).Width) >> 1;
++text;
}
else
{
n = getGlyphIndexByChar(current_char);
if (n > 0)
{
glyph_indices.push_back( n );
// Store glyph size and offset informations.
SGUITTGlyph const& glyph = Glyphs[n-1];
u32 texw = glyph.source_rect.getWidth();
u32 texh = glyph.source_rect.getHeight();
s32 offx = glyph.offset.X;
s32 offy = (font_metrics.ascender / 64) - glyph.offset.Y;
// Apply kerning.
vector2di k = getKerning(current_char, previous_char);
offset.X += k.X;
offset.Y += k.Y;
vector3df current_pos(offset.X + offx, offset.Y - offy, 0);
dimension2d<u32> letter_size = dimension2d<u32>(texw, texh);
// Now we copy planes corresponding to the letter size.
IMeshManipulator* mani = smgr->getMeshManipulator();
IMesh* meshcopy = mani->createMeshCopy(shared_plane_ptr_);
mani->scale(meshcopy, vector3df((f32)letter_size.Width, (f32)letter_size.Height, 1));
ISceneNode* current_node = smgr->addMeshSceneNode(meshcopy, parent, -1, current_pos);
meshcopy->drop();
current_node->getMaterial(0) = mat;
current_node->setAutomaticCulling(EAC_OFF);
current_node->setIsDebugObject(true); //so the picking won't have any effect on individual letter
//current_node->setDebugDataVisible(EDS_BBOX); //de-comment this when debugging
container.push_back(current_node);
}
offset.X += getWidthFromCharacter(current_char);
// Note that fallback font handling is missing here (Minetest never uses this)
previous_char = current_char;
++text;
}
}
update_glyph_pages();
//only after we update the textures can we use the glyph page textures.
for (u32 i = 0; i < glyph_indices.size(); ++i)
{
u32 n = glyph_indices[i];
SGUITTGlyph const& glyph = Glyphs[n-1];
ITexture* current_tex = Glyph_Pages[glyph.glyph_page]->texture;
f32 page_texture_size = (f32)current_tex->getSize().Width;
//Now we calculate the UV position according to the texture size and the source rect.
//
// 2___3
// | /|
// | / | <-- plane mesh is like this, point 2 is (0,0), point 0 is (0, -1)
// |/ | <-- the texture coords of point 2 is (0,0, point 0 is (0, 1)
// 0---1
//
f32 u1 = glyph.source_rect.UpperLeftCorner.X / page_texture_size;
f32 u2 = u1 + (glyph.source_rect.getWidth() / page_texture_size);
f32 v1 = glyph.source_rect.UpperLeftCorner.Y / page_texture_size;
f32 v2 = v1 + (glyph.source_rect.getHeight() / page_texture_size);
//we can be quite sure that this is IMeshSceneNode, because we just added them in the above loop.
IMeshSceneNode* node = static_cast<IMeshSceneNode*>(container[i]);
S3DVertex* pv = static_cast<S3DVertex*>(node->getMesh()->getMeshBuffer(0)->getVertices());
//pv[0].TCoords.Y = pv[1].TCoords.Y = (letter_size.Height - 1) / static_cast<f32>(letter_size.Height);
//pv[1].TCoords.X = pv[3].TCoords.X = (letter_size.Width - 1) / static_cast<f32>(letter_size.Width);
pv[0].TCoords = vector2df(u1, v2);
pv[1].TCoords = vector2df(u2, v2);
pv[2].TCoords = vector2df(u1, v1);
pv[3].TCoords = vector2df(u2, v1);
container[i]->getMaterial(0).setTexture(0, current_tex);
}
return container;
}
std::u32string CGUITTFont::convertWCharToU32String(const wchar_t* const charArray) const
{
static_assert(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4, "unexpected wchar size");

View file

@ -32,17 +32,17 @@
#pragma once
#include <ft2build.h>
#include <map>
#include <ft2build.h>
#include FT_FREETYPE_H
#include "IGUIEnvironment.h"
#include "IGUIFont.h"
#include "ISceneManager.h"
#include "IVideoDriver.h"
#include "IrrlichtDevice.h"
#include "SMesh.h"
#include "util/enriched_string.h"
#include "util/basic_macros.h"
#include FT_FREETYPE_H
namespace irr
{
@ -56,26 +56,22 @@ namespace gui
{
//! Constructor.
SGUITTGlyph() :
isLoaded(false),
glyph_page(0),
source_rect(),
offset(),
advance(),
surface(0),
parent(0)
surface(0)
{}
DISABLE_CLASS_COPY(SGUITTGlyph);
//! This class would be trivially copyable except for the reference count on `surface`.
SGUITTGlyph(SGUITTGlyph &&other) noexcept :
isLoaded(other.isLoaded),
glyph_page(other.glyph_page),
source_rect(other.source_rect),
offset(other.offset),
advance(other.advance),
surface(other.surface),
parent(other.parent)
surface(other.surface)
{
other.surface = 0;
}
@ -83,12 +79,17 @@ namespace gui
//! Destructor.
~SGUITTGlyph() { unload(); }
//! If true, the glyph has been loaded.
inline bool isLoaded() const {
return source_rect != core::recti();
}
//! Preload the glyph.
//! The preload process occurs when the program tries to cache the glyph from FT_Library.
//! However, it simply defines the SGUITTGlyph's properties and will only create the page
//! textures if necessary. The actual creation of the textures should only occur right
//! before the batch draw call.
void preload(u32 char_index, FT_Face face, video::IVideoDriver* driver, u32 font_size, const FT_Int32 loadFlags);
void preload(u32 char_index, FT_Face face, CGUITTFont *parent, u32 font_size, const FT_Int32 loadFlags);
//! Unloads the glyph.
void unload();
@ -96,9 +97,6 @@ namespace gui
//! Creates the IImage object from the FT_Bitmap.
video::IImage* createGlyphImage(const FT_Bitmap& bits, video::IVideoDriver* driver) const;
//! If true, the glyph has been loaded.
bool isLoaded;
//! The page the glyph is on.
u32 glyph_page;
@ -109,14 +107,11 @@ namespace gui
core::vector2di offset;
//! Glyph advance information.
FT_Vector advance;
core::vector2di advance;
//! This is just the temporary image holder. After this glyph is paged,
//! it will be dropped.
mutable video::IImage* surface;
//! The pointer pointing to the parent (CGUITTFont)
CGUITTFont* parent;
};
//! Holds a sheet of glyphs.
@ -130,7 +125,8 @@ namespace gui
{
if (driver)
driver->removeTexture(texture);
else texture->drop();
else
texture->drop();
}
}
@ -184,19 +180,11 @@ namespace gui
for (u32 i = 0; i < glyph_to_be_paged.size(); ++i)
{
const SGUITTGlyph* glyph = glyph_to_be_paged[i];
if (glyph && glyph->isLoaded)
if (glyph && glyph->surface)
{
if (glyph->surface)
{
glyph->surface->copyTo(pageholder, glyph->source_rect.UpperLeftCorner);
glyph->surface->drop();
glyph->surface = 0;
}
else
{
; // TODO: add error message?
//currently, if we failed to create the image, just ignore this operation.
}
glyph->surface->copyTo(pageholder, glyph->source_rect.UpperLeftCorner);
glyph->surface->drop();
glyph->surface = 0;
}
}
@ -238,77 +226,70 @@ namespace gui
virtual ~CGUITTFont();
//! Sets the amount of glyphs to batch load.
virtual void setBatchLoadSize(u32 batch_size) { batch_load_size = batch_size; }
void setBatchLoadSize(u32 batch_size) { batch_load_size = batch_size; }
//! Sets the maximum texture size for a page of glyphs.
virtual void setMaxPageTextureSize(const core::dimension2du& texture_size) { max_page_texture_size = texture_size; }
void setMaxPageTextureSize(const core::dimension2du& texture_size) { max_page_texture_size = texture_size; }
//! Get the font size.
virtual u32 getFontSize() const { return size; }
u32 getFontSize() const { return size; }
//! Check the font's transparency.
virtual bool isTransparent() const { return use_transparency; }
bool isTransparent() const { return use_transparency; }
//! Check if the font auto-hinting is enabled.
//! Auto-hinting is FreeType's built-in font hinting engine.
virtual bool useAutoHinting() const { return use_auto_hinting; }
bool useAutoHinting() const { return use_auto_hinting; }
//! Check if the font hinting is enabled.
virtual bool useHinting() const { return use_hinting; }
bool useHinting() const { return use_hinting; }
//! Check if the font is being loaded as a monochrome font.
//! The font can either be a 256 color grayscale font, or a 2 color monochrome font.
virtual bool useMonochrome() const { return use_monochrome; }
bool useMonochrome() const { return use_monochrome; }
//! Tells the font to allow transparency when rendering.
//! Default: true.
//! \param flag If true, the font draws using transparency.
virtual void setTransparency(const bool flag);
void setTransparency(const bool flag);
//! Tells the font to use monochrome rendering.
//! Default: false.
//! \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);
void setMonochrome(const bool flag);
//! 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.
//! \param enable_auto_hinting If true, FreeType uses its own auto-hinting algorithm. If false, it tries to use the algorithm specified by the font.
virtual void setFontHinting(const bool enable, const bool enable_auto_hinting = true);
void setFontHinting(const bool enable, const bool enable_auto_hinting = true);
//! Draws some text and clips it to the specified rectangle if wanted.
virtual void draw(const core::stringw& text, const core::rect<s32>& position,
video::SColor color, bool hcenter=false, bool vcenter=false,
const core::rect<s32>* clip=0);
const core::rect<s32>* clip=0) override;
void draw(const EnrichedString& text, const core::rect<s32>& position,
bool hcenter=false, bool vcenter=false,
const core::rect<s32>* clip=0);
//! Returns the dimension of a character produced by this font.
virtual core::dimension2d<u32> getCharDimension(const wchar_t ch) const;
//! Returns the dimension of a text string.
virtual core::dimension2d<u32> getDimension(const wchar_t* text) const;
virtual core::dimension2du getDimension(const wchar_t* text) const override;
//! Calculates the index of the character in the text which is on a specific position.
virtual s32 getCharacterFromPos(const wchar_t* text, s32 pixel_x) const;
virtual s32 getCharacterFromPos(const wchar_t* text, s32 pixel_x) const override;
//! Sets global kerning width for the font.
virtual void setKerningWidth(s32 kerning);
virtual void setKerningWidth(s32 kerning) override;
//! Sets global kerning height for the font.
virtual void setKerningHeight(s32 kerning);
//! Gets kerning values (distance between letters) for the font. If no parameters are provided,
virtual s32 getKerningWidth(const wchar_t* thisLetter=0, const wchar_t* previousLetter=0) const;
virtual s32 getKerningWidth(const char32_t thisLetter=0, const char32_t previousLetter=0) const;
virtual void setKerningHeight(s32 kerning) override;
//! Returns the distance between letters
virtual s32 getKerningHeight() const;
virtual core::vector2di getKerning(const wchar_t thisLetter, const wchar_t previousLetter) const override;
//! Define which characters should not be drawn by the font.
virtual void setInvisibleCharacters(const wchar_t *s);
virtual void setInvisibleCharacters(const wchar_t *s) override;
//! Get the last glyph page if there's still available slots.
//! If not, it will return zero.
@ -317,7 +298,7 @@ namespace gui
//! Create a new glyph page texture.
//! \param pixel_mode the pixel mode defined by FT_Pixel_Mode
//should be better typed. fix later.
CGUITTGlyphPage* createGlyphPage(const u8& pixel_mode);
CGUITTGlyphPage* createGlyphPage(const u8 pixel_mode);
//! Get the last glyph page's index.
u32 getLastGlyphPageIndex() const { return Glyph_Pages.size() - 1; }
@ -328,16 +309,13 @@ namespace gui
//! 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
virtual video::IImage* createTextureFromChar(const char32_t& ch);
video::IImage* createTextureFromChar(const char32_t& ch);
//! This function is for debugging mostly. If the page doesn't exist it returns zero.
//! \param page_index Simply return the texture handle of a given page index.
virtual video::ITexture* getPageTextureByIndex(const u32& page_index) const;
video::ITexture* getPageTextureByIndex(const u32& page_index) const;
//! Add a list of scene nodes generated by putting font textures on the 3D planes.
virtual core::array<scene::ISceneNode*> addTextSceneNode
(const wchar_t* text, scene::ISceneManager* smgr, scene::ISceneNode* parent = 0,
const video::SColor& color = video::SColor(255, 0, 0, 0), bool center = false );
inline video::IVideoDriver *getDriver() const { return Driver; }
inline s32 getAscender() const { return font_metrics.ascender; }
@ -355,8 +333,6 @@ namespace gui
static FT_Library c_library;
static std::map<io::path, SGUITTFace*> c_faces;
static bool c_libraryLoaded;
static scene::IMesh* shared_plane_ptr_;
static scene::SMesh shared_plane_;
// Helper functions for the same-named public member functions above
// (Since std::u32string is nicer to work with than wchar_t *)
@ -379,20 +355,11 @@ namespace gui
if (useMonochrome()) load_flags |= FT_LOAD_MONOCHROME | FT_LOAD_TARGET_MONO;
else load_flags |= FT_LOAD_TARGET_NORMAL;
}
u32 getWidthFromCharacter(wchar_t c) const;
u32 getWidthFromCharacter(char32_t c) const;
u32 getHeightFromCharacter(wchar_t c) const;
u32 getHeightFromCharacter(char32_t c) const;
u32 getGlyphIndexByChar(wchar_t c) const;
u32 getGlyphIndexByChar(char32_t c) const;
core::vector2di getKerning(const wchar_t thisLetter, const wchar_t previousLetter) const;
core::vector2di getKerning(const char32_t thisLetter, const char32_t previousLetter) const;
core::dimension2d<u32> getDimensionUntilEndOfLine(const wchar_t* p) const;
void createSharedPlane();
irr::IrrlichtDevice* Device;
gui::IGUIEnvironment* Environment;
video::IVideoDriver* Driver;
io::path filename;
FT_Face tt_face;

View file

@ -74,7 +74,7 @@ void StaticText::draw()
updateText();
core::rect<s32> r = frameRect;
s32 height_line = font->getDimension(L"A").Height + font->getKerningHeight();
s32 height_line = font->getDimension(L"A").Height + font->getKerning(L'A').Y;
s32 height_total = height_line * BrokenText.size();
if (VAlign == EGUIA_CENTER && WordWrap)
{
@ -546,7 +546,7 @@ s32 StaticText::getTextHeight() const
return 0;
if (WordWrap) {
s32 height = font->getDimension(L"A").Height + font->getKerningHeight();
s32 height = font->getDimension(L"A").Height + font->getKerning(L'A').Y;
return height * BrokenText.size();
}
// There may be intentional new lines without WordWrap

View file

@ -88,12 +88,11 @@ void ItemStackMetadata::deSerialize(std::istream &is)
void ItemStackMetadata::updateToolCapabilities()
{
if (contains(TOOLCAP_KEY)) {
toolcaps_overridden = true;
toolcaps_override = ToolCapabilities();
std::istringstream is(getString(TOOLCAP_KEY));
toolcaps_override.deserializeJson(is);
toolcaps_override->deserializeJson(is);
} else {
toolcaps_overridden = false;
toolcaps_override = std::nullopt;
}
}

View file

@ -15,8 +15,7 @@ class IItemDefManager;
class ItemStackMetadata : public SimpleMetadata
{
public:
ItemStackMetadata():
toolcaps_overridden(false)
ItemStackMetadata()
{}
// Overrides
@ -29,7 +28,7 @@ public:
const ToolCapabilities &getToolCapabilities(
const ToolCapabilities &default_caps) const
{
return toolcaps_overridden ? toolcaps_override : default_caps;
return toolcaps_override.has_value() ? *toolcaps_override : default_caps;
}
void setToolCapabilities(const ToolCapabilities &caps);
@ -40,7 +39,6 @@ public:
return wear_bar_override;
}
void setWearBarParams(const WearBarParams &params);
void clearWearBarParams();
@ -48,7 +46,6 @@ private:
void updateToolCapabilities();
void updateWearBarParams();
bool toolcaps_overridden;
ToolCapabilities toolcaps_override;
std::optional<ToolCapabilities> toolcaps_override;
std::optional<WearBarParams> wear_bar_override;
};

View file

@ -105,7 +105,7 @@ void AndroidLogOutput::logRaw(LogLevel lev, std::string_view line)
static_assert(ARRLEN(g_level_to_android) == LL_MAX,
"mismatch between android and internal loglevels");
__android_log_print(g_level_to_android[lev], PROJECT_NAME_C, "%.*s",
line.size(), line.data());
static_cast<int>(line.size()), line.data());
}
#endif

View file

@ -41,10 +41,9 @@ bool MapSettingsManager::getMapSetting(
}
bool MapSettingsManager::getMapSettingNoiseParams(
bool MapSettingsManager::getNoiseParams(
const std::string &name, NoiseParams *value_out) const
{
// TODO: Rename to "getNoiseParams"
return m_map_settings->getNoiseParams(name, *value_out);
}

View file

@ -37,7 +37,7 @@ public:
bool getMapSetting(const std::string &name, std::string *value_out) const;
bool getMapSettingNoiseParams(const std::string &name,
bool getNoiseParams(const std::string &name,
NoiseParams *value_out) const;
// Note: Map config becomes read-only after makeMapgenParams() gets called

View file

@ -121,7 +121,7 @@ void NodeBox::deSerialize(std::istream &is)
case NODEBOX_LEVELED: {
u16 fixed_count = readU16(is);
while(fixed_count--) {
aabb3f box;
aabb3f box{{0.0f, 0.0f, 0.0f}};
box.MinEdge = readV3F32(is);
box.MaxEdge = readV3F32(is);
fixed.push_back(box);
@ -742,7 +742,7 @@ static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer,
}
}
bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype)
static bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype)
{
if (style == ALIGN_STYLE_WORLD)
return true;

View file

@ -115,9 +115,9 @@ struct NodeBox
// NODEBOX_FIXED
std::vector<aabb3f> fixed;
// NODEBOX_WALLMOUNTED
aabb3f wall_top;
aabb3f wall_bottom;
aabb3f wall_side; // being at the -X side
aabb3f wall_top = dummybox;
aabb3f wall_bottom = dummybox;
aabb3f wall_side = dummybox; // being at the -X side
// NODEBOX_CONNECTED
// (kept externally to not bloat the structure)
std::shared_ptr<NodeBoxConnected> connected;
@ -139,6 +139,10 @@ struct NodeBox
void reset();
void serialize(std::ostream &os, u16 protocol_version) const;
void deSerialize(std::istream &is);
private:
/// @note the actual defaults are in reset(), see nodedef.cpp
static constexpr aabb3f dummybox = aabb3f({0, 0, 0});
};
struct MapNode;
@ -810,14 +814,14 @@ private:
* The union of all nodes' selection boxes.
* Might be larger if big nodes are removed from the manager.
*/
aabb3f m_selection_box_union;
aabb3f m_selection_box_union{{0.0f, 0.0f, 0.0f}};
/*!
* The smallest box in integer node coordinates that
* contains all nodes' selection boxes.
* Might be larger if big nodes are removed from the manager.
*/
core::aabbox3d<s16> m_selection_box_int_union;
core::aabbox3d<s16> m_selection_box_int_union{{0, 0, 0}};
/*!
* NodeResolver instances to notify once node registration has finished.

View file

@ -302,7 +302,7 @@ private:
v3s16 m_start; /**< source position */
v3s16 m_destination; /**< destination position */
core::aabbox3d<s16> m_limits; /**< position limits in real map coordinates */
core::aabbox3d<s16> m_limits{{0, 0, 0}}; /**< position limits in real map coordinates */
/** contains all map data already collected and analyzed.
Access it via the getIndexElement/getIdxElem methods. */

View file

@ -121,7 +121,7 @@ int Profiler::print(std::ostream &o, u32 page, u32 pagecount)
{
GraphValues values;
getPage(values, page, pagecount);
char buffer[50];
char buffer[128];
for (const auto &i : values) {
o << " " << i.first << " ";
@ -132,7 +132,7 @@ int Profiler::print(std::ostream &o, u32 page, u32 pagecount)
{
// Padding
s32 space = std::max(0, 44 - (s32)i.first.size());
s32 space = std::max(0, 46 - (s32)i.first.size());
memset(buffer, '_', space);
buffer[space] = '\0';
o << buffer;

View file

@ -1594,7 +1594,7 @@ ToolCapabilities read_tool_capabilities(
// key at index -2 and value at index -1
int rating = luaL_checkinteger(L, -2);
float time = luaL_checknumber(L, -1);
groupcap.times[rating] = time;
groupcap.times.emplace_back(rating, time);
// removes value, keeps key for next iteration
lua_pop(L, 1);
}

View file

@ -326,7 +326,8 @@ bool is_color_table(lua_State *L, int index)
aabb3f read_aabb3f(lua_State *L, int index, f32 scale)
{
aabb3f box;
// default value for accidental/historical reasons
aabb3f box{-1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f};
if(lua_istable(L, index)){
lua_rawgeti(L, index, 1);
box.MinEdge.X = lua_tonumber(L, -1) * scale;

View file

@ -85,7 +85,19 @@ bool read_color (lua_State *L, int index,
video::SColor *color);
bool is_color_table (lua_State *L, int index);
/**
* Read a floating-point axis-aligned box from Lua.
*
* @param L the Lua state
* @param index the index of the Lua variable to read the box from. The
* variable must contain a table of the form
* {minx, miny, minz, maxx, maxy, maxz}.
* @param scale factor to scale the bounding box by
*
* @return the box corresponding to lua table
*/
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);
size_t read_stringlist (lua_State *L, int index,

View file

@ -892,7 +892,7 @@ int ModApiMapgen::l_get_mapgen_setting_noiseparams(lua_State *L)
getEmergeManager(L)->map_settings_mgr;
const char *name = luaL_checkstring(L, 1);
if (!settingsmgr->getMapSettingNoiseParams(name, &np))
if (!settingsmgr->getNoiseParams(name, &np))
return 0;
push_noiseparams(L, &np);

View file

@ -1168,24 +1168,25 @@ void Server::yieldToOtherThreads(float dtime)
g_profiler->avg("Server::yieldTo...() progress [#]", qs_initial - qs);
}
PlayerSAO* Server::StageTwoClientInit(session_t peer_id)
PlayerSAO *Server::StageTwoClientInit(session_t peer_id)
{
std::string playername;
PlayerSAO *playersao = NULL;
std::unique_ptr<PlayerSAO> sao;
{
ClientInterface::AutoLock clientlock(m_clients);
RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
if (client) {
playername = client->getName();
playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
sao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
}
}
RemotePlayer *player = m_env->getPlayer(playername.c_str(), true);
RemotePlayer *player = sao ? sao->getPlayer() : nullptr;
// If failed, cancel
if (!playersao || !player) {
if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
if (!player) {
RemotePlayer *joined = m_env->getPlayer(playername.c_str());
if (joined && joined->getPeerId() != PEER_ID_INEXISTENT) {
actionstream << "Server: Failed to emerge player \"" << playername
<< "\" (player allocated to another client)" << std::endl;
DenyAccess(peer_id, SERVER_ACCESSDENIED_ALREADY_CONNECTED);
@ -1197,6 +1198,19 @@ PlayerSAO* Server::StageTwoClientInit(session_t peer_id)
return nullptr;
}
// Add player to environment
m_env->addPlayer(player);
/* Clean up old HUD elements from previous sessions */
player->clearHud();
/* Add object to environment */
PlayerSAO *playersao = sao.get();
m_env->addActiveObject(std::move(sao));
if (playersao->isNewPlayer())
m_script->on_newplayer(playersao);
/*
Send complete position information
*/
@ -3239,9 +3253,7 @@ void Server::reportPrivsModified(const std::string &name)
PlayerSAO *sao = player->getPlayerSAO();
if(!sao)
return;
sao->updatePrivileges(
getPlayerEffectivePrivs(name),
isSingleplayer());
sao->updatePrivileges(getPlayerEffectivePrivs(name));
}
}
@ -3975,7 +3987,8 @@ void Server::requestShutdown(const std::string &msg, bool reconnect, float delay
m_shutdown_state.trigger(delay, msg, reconnect);
}
PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
std::unique_ptr<PlayerSAO> Server::emergePlayer(const char *name, session_t peer_id,
u16 proto_version)
{
/*
Try to get an existing player
@ -4018,20 +4031,13 @@ PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_v
player = new RemotePlayer(name, idef());
}
bool newplayer = false;
// Load player
PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
auto playersao = m_env->loadPlayer(player, peer_id);
// Complete init with server parts
playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
player->protocol_version = proto_version;
/* Run scripts */
if (newplayer) {
m_script->on_newplayer(playersao);
}
return playersao;
}

View file

@ -194,7 +194,9 @@ public:
void Receive(float min_time);
void yieldToOtherThreads(float dtime);
PlayerSAO* StageTwoClientInit(session_t peer_id);
// Full player initialization after they processed all static media
// This is a helper function for TOSERVER_CLIENT_READY
PlayerSAO *StageTwoClientInit(session_t peer_id);
/*
* Command Handlers
@ -626,7 +628,8 @@ private:
Call with env and con locked.
*/
PlayerSAO *emergePlayer(const char *name, session_t peer_id, u16 proto_version);
std::unique_ptr<PlayerSAO> emergePlayer(const char *name, session_t peer_id,
u16 proto_version);
/*
Variables

View file

@ -156,12 +156,14 @@ public:
// Other
void updatePrivileges(const std::set<std::string> &privs, bool is_singleplayer)
void updatePrivileges(const std::set<std::string> &privs)
{
m_privs = privs;
m_is_singleplayer = is_singleplayer;
}
inline void setNewPlayer() { m_is_new_player = true; }
inline bool isNewPlayer() { return m_is_new_player; }
bool getCollisionBox(aabb3f *toset) const override;
bool getSelectionBox(aabb3f *toset) const override;
bool collideWithObjects() const override { return true; }
@ -202,7 +204,8 @@ private:
// Cached privileges for enforcement
std::set<std::string> m_privs;
bool m_is_singleplayer;
const bool m_is_singleplayer;
bool m_is_new_player = false;
u16 m_breath = PLAYER_MAX_BREATH_DEFAULT;
f32 m_pitch = 0.0f;

View file

@ -667,13 +667,13 @@ void ServerEnvironment::savePlayer(RemotePlayer *player)
}
}
PlayerSAO *ServerEnvironment::loadPlayer(RemotePlayer *player, bool *new_player,
session_t peer_id, bool is_singleplayer)
std::unique_ptr<PlayerSAO> ServerEnvironment::loadPlayer(RemotePlayer *player, session_t peer_id)
{
auto playersao = std::make_unique<PlayerSAO>(this, player, peer_id, is_singleplayer);
auto playersao = std::make_unique<PlayerSAO>(this, player, peer_id, m_server->isSingleplayer());
// Create player if it doesn't exist
if (!m_player_database->loadPlayer(player, playersao.get())) {
*new_player = true;
playersao->setNewPlayer();
// Set player position
infostream << "Server: Finding spawn place for player \""
<< player->getName() << "\"" << std::endl;
@ -692,20 +692,10 @@ PlayerSAO *ServerEnvironment::loadPlayer(RemotePlayer *player, bool *new_player,
}
}
// Add player to environment
addPlayer(player);
/* Clean up old HUD elements from previous sessions */
player->clearHud();
/* Add object to environment */
PlayerSAO *ret = playersao.get();
addActiveObject(std::move(playersao));
// Update active blocks quickly for a bit so objects in those blocks appear on the client
m_fast_active_block_divider = 10;
return ret;
return playersao;
}
void ServerEnvironment::saveMeta()
@ -1857,7 +1847,7 @@ void ServerEnvironment::getSelectedActiveObjects(
auto process = [&] (ServerActiveObject *obj) -> bool {
if (obj->isGone())
return false;
aabb3f selection_box;
aabb3f selection_box{{0.0f, 0.0f, 0.0f}};
if (!obj->getSelectionBox(&selection_box))
return false;

View file

@ -240,8 +240,7 @@ public:
// Save players
void saveLoadedPlayers(bool force = false);
void savePlayer(RemotePlayer *player);
PlayerSAO *loadPlayer(RemotePlayer *player, bool *new_player, session_t peer_id,
bool is_singleplayer);
std::unique_ptr<PlayerSAO> loadPlayer(RemotePlayer *player, session_t peer_id);
void addPlayer(RemotePlayer *player);
void removePlayer(RemotePlayer *player);
bool removePlayerFromDatabase(const std::string &name);

View file

@ -47,7 +47,7 @@ void ToolGroupCap::fromJson(const Json::Value &json)
for (Json::ArrayIndex i = 0; i < size; ++i) {
if (times_object[i].isDouble())
times[i] = times_object[i].asFloat();
times.emplace_back(i, times_object[i].asFloat());
}
}
@ -106,7 +106,7 @@ void ToolCapabilities::deSerialize(std::istream &is)
for(u32 i = 0; i < times_size; i++) {
int level = readS16(is);
float time = readF32(is);
cap.times[level] = time;
cap.times.emplace_back(level, time);
}
groupcaps[name] = cap;
}
@ -272,21 +272,11 @@ std::optional<WearBarParams> WearBarParams::deserializeJson(std::istream &is)
return WearBarParams(colorStops, blend);
}
video::SColor WearBarParams::getWearBarColor(f32 durabilityPercent) {
video::SColor WearBarParams::getWearBarColor(f32 durabilityPercent)
{
if (colorStops.empty())
return video::SColor();
/*
* Strategy:
* Find upper bound of durabilityPercent
*
* if it == stops.end() -> return last color in the map
* if it == stops.begin() -> return first color in the map
*
* else:
* lower_bound = it - 1
* interpolate/do constant
*/
auto upper = colorStops.upper_bound(durabilityPercent);
if (upper == colorStops.end()) // durability is >= the highest defined color stop
@ -295,6 +285,7 @@ video::SColor WearBarParams::getWearBarColor(f32 durabilityPercent) {
if (upper == colorStops.begin()) // durability is <= the lowest defined color stop
return upper->second;
// between two values, interpolate
auto lower = std::prev(upper);
f32 lower_bound = lower->first;
video::SColor lower_color = lower->second;

View file

@ -12,26 +12,33 @@
#include <string>
#include <iostream>
#include <vector>
#include <map>
#include <unordered_map>
#include <optional>
struct ItemDefinition;
class IItemDefManager;
/*
* NOTE: these structs intentionally use vector<pair<>> or map<> over unordered_map<>
* to avoid blowing up the structure sizes. Also because the linear "dumb" approach
* works better if you have just a handful of items.
*/
struct ToolGroupCap
{
std::unordered_map<int, float> times;
std::vector<std::pair<int, float>> times;
int maxlevel = 1;
int uses = 20;
ToolGroupCap() = default;
std::optional<float> getTime(int rating) const {
auto i = times.find(rating);
if (i == times.end())
return std::nullopt;
return i->second;
for (auto &it : times) {
if (it.first == rating)
return it.second;
}
return std::nullopt;
}
void toJson(Json::Value &object) const;
@ -39,16 +46,16 @@ struct ToolGroupCap
};
typedef std::unordered_map<std::string, struct ToolGroupCap> ToolGCMap;
typedef std::unordered_map<std::string, s16> DamageGroup;
typedef std::map<std::string, ToolGroupCap> ToolGCMap;
typedef std::map<std::string, s16> DamageGroup;
struct ToolCapabilities
{
float full_punch_interval;
int max_drop_level;
int punch_attack_uses;
ToolGCMap groupcaps;
DamageGroup damageGroups;
int punch_attack_uses;
ToolCapabilities(
float full_punch_interval_ = 1.4f,
@ -59,9 +66,9 @@ struct ToolCapabilities
):
full_punch_interval(full_punch_interval_),
max_drop_level(max_drop_level_),
punch_attack_uses(punch_attack_uses_),
groupcaps(groupcaps_),
damageGroups(damageGroups_),
punch_attack_uses(punch_attack_uses_)
damageGroups(damageGroups_)
{}
void serialize(std::ostream &os, u16 version) const;
@ -76,17 +83,18 @@ private:
struct WearBarParams
{
std::map<f32, video::SColor> colorStops;
enum BlendMode : u8 {
BLEND_MODE_CONSTANT,
BLEND_MODE_LINEAR,
BlendMode_END // Dummy for validity check
};
constexpr const static EnumString es_BlendMode[3] = {
{WearBarParams::BLEND_MODE_CONSTANT, "constant"},
{WearBarParams::BLEND_MODE_LINEAR, "linear"},
{BLEND_MODE_CONSTANT, "constant"},
{BLEND_MODE_LINEAR, "linear"},
{0, nullptr}
};
std::map<f32, video::SColor> colorStops;
BlendMode blend;
WearBarParams(const std::map<f32, video::SColor> &colorStops, BlendMode blend):
@ -102,6 +110,7 @@ struct WearBarParams
static WearBarParams deserialize(std::istream &is);
void serializeJson(std::ostream &os) const;
static std::optional<WearBarParams> deserializeJson(std::istream &is);
video::SColor getWearBarColor(f32 durabilityPercent);
};

View file

@ -175,7 +175,7 @@ void TestMapblockMeshGenerator::testSimpleNode()
data.m_vmanip.setNode({0, 0, 0}, {stone, 0, 0});
MeshCollector col{{}};
MapblockMeshGenerator mg{&data, &col, nullptr};
MapblockMeshGenerator mg{&data, &col};
mg.generate();
UASSERTEQ(std::size_t, col.prebuffers[0].size(), 1);
UASSERTEQ(std::size_t, col.prebuffers[1].size(), 0);
@ -197,7 +197,7 @@ void TestMapblockMeshGenerator::testSurroundedNode()
data.m_vmanip.setNode({1, 0, 0}, {wood, 0, 0});
MeshCollector col{{}};
MapblockMeshGenerator mg{&data, &col, nullptr};
MapblockMeshGenerator mg{&data, &col};
mg.generate();
UASSERTEQ(std::size_t, col.prebuffers[0].size(), 1);
UASSERTEQ(std::size_t, col.prebuffers[1].size(), 0);
@ -218,7 +218,7 @@ void TestMapblockMeshGenerator::testInterliquidSame()
data.m_vmanip.setNode({1, 0, 0}, {water, 0, 0});
MeshCollector col{{}};
MapblockMeshGenerator mg{&data, &col, nullptr};
MapblockMeshGenerator mg{&data, &col};
mg.generate();
UASSERTEQ(std::size_t, col.prebuffers[0].size(), 1);
UASSERTEQ(std::size_t, col.prebuffers[1].size(), 0);
@ -240,7 +240,7 @@ void TestMapblockMeshGenerator::testInterliquidDifferent()
data.m_vmanip.setNode({0, 0, 1}, {lava, 0, 0});
MeshCollector col{{}};
MapblockMeshGenerator mg{&data, &col, nullptr};
MapblockMeshGenerator mg{&data, &col};
mg.generate();
UASSERTEQ(std::size_t, col.prebuffers[0].size(), 1);
UASSERTEQ(std::size_t, col.prebuffers[1].size(), 0);

View file

@ -129,7 +129,7 @@ void TestMapSettingsManager::testMapSettingsManager()
{
NoiseParams dummy;
mgr.getMapSettingNoiseParams("mgv5_np_factor", &dummy);
mgr.getNoiseParams("mgv5_np_factor", &dummy);
check_noise_params(&dummy, &script_np_factor);
}

View file

@ -105,8 +105,20 @@ void TestMapgen::testBiomeGen(IGameDef *gamedef)
);
s16 next_y = biomegen->getNextTransitionY(expected.check_y);
UASSERTEQ(auto, biome->name, expected.name);
UASSERTEQ(auto, next_y, expected.next_y);
//UASSERTEQ(auto, biome->name, expected.name);
//UASSERTEQ(auto, next_y, expected.next_y);
if (biome->name != expected.name) {
errorstream << "FIXME " << FUNCTION_NAME << " " << biome->name
<< " != " << expected.name << "\nThe test would have failed."
<< std::endl;
return;
}
if (next_y != expected.next_y) {
errorstream << "FIXME " << FUNCTION_NAME << " " << next_y
<< " != " << expected.next_y << "\nThe test would have failed."
<< std::endl;
return;
}
}
}
}

View file

@ -13,6 +13,8 @@ std::string QuicktuneValue::getString()
return "(none)";
case QVT_FLOAT:
return ftos(value_QVT_FLOAT.current);
case QVT_INT:
return itos(value_QVT_INT.current);
}
return "<invalid type>";
}
@ -22,14 +24,21 @@ void QuicktuneValue::relativeAdd(float amount)
switch(type){
case QVT_NONE:
break;
case QVT_FLOAT:
value_QVT_FLOAT.current += amount * (value_QVT_FLOAT.max - value_QVT_FLOAT.min);
if(value_QVT_FLOAT.current > value_QVT_FLOAT.max)
value_QVT_FLOAT.current = value_QVT_FLOAT.max;
if(value_QVT_FLOAT.current < value_QVT_FLOAT.min)
value_QVT_FLOAT.current = value_QVT_FLOAT.min;
case QVT_FLOAT: {
float &v = value_QVT_FLOAT.current;
v += amount * (value_QVT_FLOAT.max - value_QVT_FLOAT.min);
v = core::clamp(v, value_QVT_FLOAT.min, value_QVT_FLOAT.max);
break;
}
case QVT_INT: {
int &v = value_QVT_INT.current;
int diff = std::floor(amount * (value_QVT_INT.max - value_QVT_INT.min));
if (!diff)
diff = amount < 0 ? -1 : 1;
v = core::clamp(v + diff, value_QVT_INT.min, value_QVT_INT.max);
break;
}
}
}
static std::map<std::string, QuicktuneValue> g_values;
@ -44,12 +53,9 @@ const std::vector<std::string> &getQuicktuneNames()
QuicktuneValue getQuicktuneValue(const std::string &name)
{
MutexAutoLock lock(g_mutex);
std::map<std::string, QuicktuneValue>::iterator i = g_values.find(name);
if(i == g_values.end()){
QuicktuneValue val;
val.type = QVT_NONE;
return val;
}
auto i = g_values.find(name);
if (i == g_values.end())
return QuicktuneValue();
return i->second;
}
@ -64,15 +70,15 @@ void updateQuicktuneValue(const std::string &name, QuicktuneValue &val)
{
MutexAutoLock lock(g_mutex);
auto i = g_values.find(name);
if(i == g_values.end()){
if (i == g_values.end()) {
g_values[name] = val;
g_names.push_back(name);
return;
}
QuicktuneValue &ref = i->second;
if(ref.modified)
if (ref.modified) {
val = ref;
else{
} else {
ref = val;
ref.modified = false;
}

View file

@ -37,19 +37,21 @@
#include <map>
#include <vector>
enum QuicktuneValueType{
enum QuicktuneValueType {
QVT_NONE,
QVT_FLOAT
QVT_FLOAT,
QVT_INT
};
struct QuicktuneValue
{
QuicktuneValueType type = QVT_NONE;
union{
struct{
float current;
float min;
float max;
union {
struct {
float current, min, max;
} value_QVT_FLOAT;
struct {
int current, min, max;
} value_QVT_INT;
};
bool modified = false;
@ -65,19 +67,15 @@ void setQuicktuneValue(const std::string &name, const QuicktuneValue &val);
void updateQuicktuneValue(const std::string &name, QuicktuneValue &val);
#ifndef NDEBUG
#define QUICKTUNE(type_, var, min_, max_, name){\
QuicktuneValue qv;\
qv.type = type_;\
qv.value_##type_.current = var;\
qv.value_##type_.min = min_;\
qv.value_##type_.max = max_;\
updateQuicktuneValue(name, qv);\
var = qv.value_##type_.current;\
}
#else // NDEBUG
#define QUICKTUNE(type, var, min_, max_, name){}
#endif
#define QUICKTUNE(type_, var, min_, max_, name) do { \
QuicktuneValue qv; \
qv.type = type_; \
qv.value_##type_.current = var; \
qv.value_##type_.min = min_; \
qv.value_##type_.max = max_; \
updateQuicktuneValue(name, qv); \
var = qv.value_##type_.current; \
} while (0)
#define QUICKTUNE_AUTONAME(type_, var, min_, max_)\
QUICKTUNE(type_, var, min_, max_, #var)