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:
commit
78de371f2d
123 changed files with 1506 additions and 1956 deletions
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"))
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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".
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -633,8 +633,7 @@ MapBlockMesh::MapBlockMesh(Client *client, MeshMakeData *data, v3s16 camera_offs
|
|||
*/
|
||||
|
||||
{
|
||||
MapblockMeshGenerator(data, &collector,
|
||||
client->getSceneManager()->getMeshManipulator()).generate();
|
||||
MapblockMeshGenerator(data, &collector).generate();
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 ¶ms);
|
||||
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;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
19
src/tool.cpp
19
src/tool.cpp
|
@ -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;
|
||||
|
|
37
src/tool.h
37
src/tool.h
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue