#include <algorithm>
#include "client/renderingengine.h"
+// struct MeshBufListList
+void MeshBufListList::clear()
+{
+ for (auto &list : lists)
+ list.clear();
+}
+
+void MeshBufListList::add(scene::IMeshBuffer *buf, v3s16 position, u8 layer)
+{
+ // Append to the correct layer
+ std::vector<MeshBufList> &list = lists[layer];
+ const video::SMaterial &m = buf->getMaterial();
+ for (MeshBufList &l : list) {
+ // comparing a full material is quite expensive so we don't do it if
+ // not even first texture is equal
+ if (l.m.TextureLayer[0].Texture != m.TextureLayer[0].Texture)
+ continue;
+
+ if (l.m == m) {
+ l.bufs.emplace_back(position, buf);
+ return;
+ }
+ }
+ MeshBufList l;
+ l.m = m;
+ l.bufs.emplace_back(position, buf);
+ list.emplace_back(l);
+}
+
+// ClientMap
+
ClientMap::ClientMap(
Client *client,
MapDrawControl &control,
s32 id
):
- Map(dout_client, client),
+ Map(client),
scene::ISceneNode(RenderingEngine::get_scene_manager()->getRootSceneNode(),
RenderingEngine::get_scene_manager(), id),
m_client(client),
MapSector * ClientMap::emergeSector(v2s16 p2d)
{
// Check that it doesn't exist already
- try {
- return getSectorNoGenerate(p2d);
- } catch(InvalidPositionException &e) {
- }
+ MapSector *sector = getSectorNoGenerate(p2d);
- // Create a sector
- MapSector *sector = new MapSector(this, p2d, m_gamedef);
- m_sectors[p2d] = sector;
+ // Create it if it does not exist yet
+ if (!sector) {
+ sector = new MapSector(this, p2d, m_gamedef);
+ m_sectors[p2d] = sector;
+ }
return sector;
}
void ClientMap::updateDrawList()
{
ScopeProfiler sp(g_profiler, "CM::updateDrawList()", SPT_AVG);
- g_profiler->add("CM::updateDrawList() count", 1);
for (auto &i : m_drawlist) {
MapBlock *block = i.second;
}
m_drawlist.clear();
- v3f camera_position = m_camera_position;
- v3f camera_direction = m_camera_direction;
- f32 camera_fov = m_camera_fov;
+ const v3f camera_position = m_camera_position;
+ const v3f camera_direction = m_camera_direction;
// Use a higher fov to accomodate faster camera movements.
// Blocks are cropped better when they are drawn.
- // Or maybe they aren't? Well whatever.
- camera_fov *= 1.2;
+ const f32 camera_fov = m_camera_fov * 1.1f;
v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
v3s16 p_blocks_min;
v3s16 p_blocks_max;
getBlocksInViewRange(cam_pos_nodes, &p_blocks_min, &p_blocks_max);
- // Number of blocks in rendering range
- u32 blocks_in_range = 0;
+ // Number of blocks currently loaded by the client
+ u32 blocks_loaded = 0;
+ // Number of blocks with mesh in rendering range
+ u32 blocks_in_range_with_mesh = 0;
// Number of blocks occlusion culled
u32 blocks_occlusion_culled = 0;
- // Number of blocks in rendering range but don't have a mesh
- u32 blocks_in_range_without_mesh = 0;
- // Blocks that had mesh that would have been drawn according to
- // rendering range (if max blocks limit didn't kick in)
- u32 blocks_would_have_drawn = 0;
- // Blocks that were drawn and had a mesh
- u32 blocks_drawn = 0;
- // Blocks which had a corresponding meshbuffer for this pass
- //u32 blocks_had_pass_meshbuf = 0;
- // Blocks from which stuff was actually drawn
- //u32 blocks_without_stuff = 0;
- // Distance to farthest drawn block
- float farthest_drawn = 0;
// No occlusion culling when free_move is on and camera is
// inside ground
bool occlusion_culling_enabled = true;
- if (g_settings->getBool("free_move")) {
+ if ((g_settings->getBool("free_move") && g_settings->getBool("noclip")) || g_settings->getBool("freecam")) {
MapNode n = getNode(cam_pos_nodes);
if (n.getContent() == CONTENT_IGNORE ||
m_nodedef->get(n).solidness == 2)
occlusion_culling_enabled = false;
}
+ // Uncomment to debug occluded blocks in the wireframe mode
+ // TODO: Include this as a flag for an extended debugging setting
+ //if (occlusion_culling_enabled && m_control.show_wireframe)
+ // occlusion_culling_enabled = porting::getTimeS() & 1;
+
for (const auto §or_it : m_sectors) {
MapSector *sector = sector_it.second;
v2s16 sp = sector->getPos();
+ blocks_loaded += sector->size();
if (!m_control.range_all) {
if (sp.X < p_blocks_min.X || sp.X > p_blocks_max.X ||
sp.Y < p_blocks_min.Z || sp.Y > p_blocks_max.Z)
u32 sector_blocks_drawn = 0;
- for (auto block : sectorblocks) {
+ for (MapBlock *block : sectorblocks) {
/*
- Compare block position to camera position, skip
- if not seen on display
- */
+ Compare block position to camera position, skip
+ if not seen on display
+ */
- if (block->mesh)
- block->mesh->updateCameraOffset(m_camera_offset);
+ if (!block->mesh) {
+ // Ignore if mesh doesn't exist
+ continue;
+ }
float range = 100000 * BS;
if (!m_control.range_all)
camera_direction, camera_fov, range, &d))
continue;
- blocks_in_range++;
-
- /*
- Ignore if mesh doesn't exist
- */
- if (!block->mesh) {
- blocks_in_range_without_mesh++;
- continue;
- }
+ blocks_in_range_with_mesh++;
/*
Occlusion culling
*/
- if (occlusion_culling_enabled && isBlockOccluded(block, cam_pos_nodes)) {
+ if ((!m_control.range_all && d > m_control.wanted_range * BS) ||
+ (occlusion_culling_enabled && isBlockOccluded(block, cam_pos_nodes))) {
blocks_occlusion_culled++;
continue;
}
// This block is in range. Reset usage timer.
block->resetUsageTimer();
- // Limit block count in case of a sudden increase
- blocks_would_have_drawn++;
- if (blocks_drawn >= m_control.wanted_max_blocks &&
- !m_control.range_all &&
- d > m_control.wanted_range * BS)
- continue;
-
// Add to set
block->refGrab();
m_drawlist[block->getPos()] = block;
sector_blocks_drawn++;
- blocks_drawn++;
- if (d / BS > farthest_drawn)
- farthest_drawn = d / BS;
-
} // foreach sectorblocks
if (sector_blocks_drawn != 0)
m_last_drawn_sectors.insert(sp);
}
- g_profiler->avg("CM: blocks in range", blocks_in_range);
- g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled);
- if (blocks_in_range != 0)
- g_profiler->avg("CM: blocks in range without mesh (frac)",
- (float)blocks_in_range_without_mesh / blocks_in_range);
- g_profiler->avg("CM: blocks drawn", blocks_drawn);
- g_profiler->avg("CM: farthest drawn", farthest_drawn);
- g_profiler->avg("CM: wanted max blocks", m_control.wanted_max_blocks);
+ g_profiler->avg("MapBlock meshes in range [#]", blocks_in_range_with_mesh);
+ g_profiler->avg("MapBlocks occlusion culled [#]", blocks_occlusion_culled);
+ g_profiler->avg("MapBlocks drawn [#]", m_drawlist.size());
+ g_profiler->avg("MapBlocks loaded [#]", blocks_loaded);
}
-struct MeshBufList
-{
- video::SMaterial m;
- std::vector<scene::IMeshBuffer*> bufs;
-};
-
-struct MeshBufListList
-{
- /*!
- * Stores the mesh buffers of the world.
- * The array index is the material's layer.
- * The vector part groups vertices by material.
- */
- std::vector<MeshBufList> lists[MAX_TILE_LAYERS];
-
- void clear()
- {
- for (auto &list : lists)
- list.clear();
- }
-
- void add(scene::IMeshBuffer *buf, u8 layer)
- {
- // Append to the correct layer
- std::vector<MeshBufList> &list = lists[layer];
- const video::SMaterial &m = buf->getMaterial();
- for (MeshBufList &l : list) {
- // comparing a full material is quite expensive so we don't do it if
- // not even first texture is equal
- if (l.m.TextureLayer[0].Texture != m.TextureLayer[0].Texture)
- continue;
-
- if (l.m == m) {
- l.bufs.push_back(buf);
- return;
- }
- }
- MeshBufList l;
- l.m = m;
- l.bufs.push_back(buf);
- list.push_back(l);
- }
-};
-
void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
{
bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
std::string prefix;
if (pass == scene::ESNRP_SOLID)
- prefix = "CM: solid: ";
+ prefix = "renderMap(SOLID): ";
else
- prefix = "CM: transparent: ";
+ 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();
- /*
- Get time for measuring timeout.
-
- Measuring time is very useful for long delays when the
- machine is swapping a lot.
- */
- std::time_t time1 = time(0);
-
/*
Get animation parameters
*/
- float animation_time = m_client->getAnimationTime();
- int crack = m_client->getCrackLevel();
- u32 daynight_ratio = m_client->getEnv().getDayNightRatio();
+ const float animation_time = m_client->getAnimationTime();
+ const int crack = m_client->getCrackLevel();
+ const u32 daynight_ratio = m_client->getEnv().getDayNightRatio();
- v3f camera_position = m_camera_position;
- v3f camera_direction = m_camera_direction;
- f32 camera_fov = m_camera_fov;
+ const v3f camera_position = m_camera_position;
+ const v3f camera_direction = m_camera_direction;
+ const f32 camera_fov = m_camera_fov;
/*
Get all blocks and draw all visible ones
*/
u32 vertex_count = 0;
- u32 meshbuffer_count = 0;
+ u32 drawcall_count = 0;
// For limiting number of mesh animations per frame
u32 mesh_animate_count = 0;
- u32 mesh_animate_count_far = 0;
-
- // Blocks that were drawn and had a mesh
- u32 blocks_drawn = 0;
- // Blocks which had a corresponding meshbuffer for this pass
- u32 blocks_had_pass_meshbuf = 0;
- // Blocks from which stuff was actually drawn
- u32 blocks_without_stuff = 0;
+ //u32 mesh_animate_count_far = 0;
/*
Draw the selected MapBlocks
*/
- {
- ScopeProfiler sp(g_profiler, prefix + "drawing blocks", SPT_AVG);
-
MeshBufListList drawbufs;
for (auto &i : m_drawlist) {
+ v3s16 block_pos = i.first;
MapBlock *block = i.second;
// If the mesh of the block happened to get deleted, ignore it
assert(mapBlockMesh);
// Pretty random but this should work somewhat nicely
bool faraway = d >= BS * 50;
- //bool faraway = d >= m_control.wanted_range * BS;
if (mapBlockMesh->isAnimationForced() || !faraway ||
- mesh_animate_count_far < (m_control.range_all ? 200 : 50)) {
+ mesh_animate_count < (m_control.range_all ? 200 : 50)) {
+
bool animated = mapBlockMesh->animate(faraway, animation_time,
crack, daynight_ratio);
if (animated)
mesh_animate_count++;
- if (animated && faraway)
- mesh_animate_count_far++;
} else {
mapBlockMesh->decreaseAnimationForceTimer();
}
material.setFlag(video::EMF_WIREFRAME,
m_control.show_wireframe);
- drawbufs.add(buf, layer);
+ drawbufs.add(buf, block_pos, layer);
}
}
}
}
}
+ TimeTaker draw("Drawing mesh buffers");
+
+ core::matrix4 m; // Model matrix
+ v3f offset = intToFloat(m_camera_offset, BS);
+
// Render all layers in order
for (auto &lists : drawbufs.lists) {
- int timecheck_counter = 0;
for (MeshBufList &list : lists) {
- timecheck_counter++;
- if (timecheck_counter > 50) {
- timecheck_counter = 0;
- std::time_t time2 = time(0);
- if (time2 > time1 + 4) {
- infostream << "ClientMap::renderMap(): "
- "Rendering takes ages, returning."
- << std::endl;
- return;
- }
+ // Check and abort if the machine is swapping a lot
+ if (draw.getTimerTime() > 2000) {
+ infostream << "ClientMap::renderMap(): Rendering took >2s, " <<
+ "returning." << std::endl;
+ return;
}
-
driver->setMaterial(list.m);
- for (scene::IMeshBuffer *buf : list.bufs) {
+ drawcall_count += list.bufs.size();
+ for (auto &pair : list.bufs) {
+ scene::IMeshBuffer *buf = pair.second;
+
+ v3f block_wpos = intToFloat(pair.first * MAP_BLOCKSIZE, BS);
+ m.setTranslation(block_wpos - offset);
+
+ driver->setTransform(video::ETS_WORLD, m);
driver->drawMeshBuffer(buf);
vertex_count += buf->getVertexCount();
- meshbuffer_count++;
}
}
}
- } // ScopeProfiler
+ g_profiler->avg(prefix + "draw meshes [ms]", draw.stop(true));
// Log only on solid pass because values are the same
if (pass == scene::ESNRP_SOLID) {
- g_profiler->avg("CM: animated meshes", mesh_animate_count);
- g_profiler->avg("CM: animated meshes (far)", mesh_animate_count_far);
+ g_profiler->avg("renderMap(): animated meshes [#]", mesh_animate_count);
}
- g_profiler->avg(prefix + "vertices drawn", vertex_count);
- if (blocks_had_pass_meshbuf != 0)
- g_profiler->avg(prefix + "meshbuffers per block",
- (float)meshbuffer_count / (float)blocks_had_pass_meshbuf);
- if (blocks_drawn != 0)
- g_profiler->avg(prefix + "empty blocks (frac)",
- (float)blocks_without_stuff / blocks_drawn);
+ g_profiler->avg(prefix + "vertices drawn [#]", vertex_count);
+ g_profiler->avg(prefix + "drawcalls [#]", drawcall_count);
}
static bool getVisibleBrightness(Map *map, const v3f &p0, v3f dir, float step,
int ClientMap::getBackgroundBrightness(float max_d, u32 daylight_factor,
int oldvalue, bool *sunlight_seen_result)
{
+ ScopeProfiler sp(g_profiler, "CM::getBackgroundBrightness", SPT_AVG);
static v3f z_directions[50] = {
v3f(-100, 0, 0)
};
// - Do not if player is in third person mode
const ContentFeatures& features = m_nodedef->get(n);
video::SColor post_effect_color = features.post_effect_color;
- if(features.solidness == 2 && !(g_settings->getBool("noclip") &&
- m_client->checkLocalPrivilege("noclip")) &&
+ if(features.solidness == 2 && !((g_settings->getBool("noclip") || g_settings->getBool("freecam")) &&
+ (m_client->checkLocalPrivilege("noclip") || g_settings->getBool("freecam"))) &&
cam_mode == CAMERA_MODE_FIRST)
{
post_effect_color = video::SColor(255, 0, 0, 0);
{
out<<"ClientMap: ";
}
-
-