aboutsummaryrefslogtreecommitdiff
path: root/src/client/clientmap.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/clientmap.cpp')
-rw-r--r--src/client/clientmap.cpp383
1 files changed, 334 insertions, 49 deletions
diff --git a/src/client/clientmap.cpp b/src/client/clientmap.cpp
index 8e02fea63..1a024e464 100644
--- a/src/client/clientmap.cpp
+++ b/src/client/clientmap.cpp
@@ -64,15 +64,24 @@ void MeshBufListList::add(scene::IMeshBuffer *buf, v3s16 position, u8 layer)
ClientMap::ClientMap(
Client *client,
+ RenderingEngine *rendering_engine,
MapDrawControl &control,
s32 id
):
Map(client),
- scene::ISceneNode(RenderingEngine::get_scene_manager()->getRootSceneNode(),
- RenderingEngine::get_scene_manager(), id),
+ scene::ISceneNode(rendering_engine->get_scene_manager()->getRootSceneNode(),
+ rendering_engine->get_scene_manager(), id),
m_client(client),
- m_control(control)
+ m_rendering_engine(rendering_engine),
+ m_control(control),
+ m_drawlist(MapBlockComparer(v3s16(0,0,0)))
{
+
+ /*
+ * @Liso: Sadly C++ doesn't have introspection, so the only way we have to know
+ * 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);
@@ -114,12 +123,21 @@ void ClientMap::OnRegisterSceneNode()
}
ISceneNode::OnRegisterSceneNode();
+
+ if (!m_added_to_shadow_renderer) {
+ m_added_to_shadow_renderer = true;
+ if (auto shadows = m_rendering_engine->get_shadow_renderer())
+ shadows->addNodeToShadowList(this);
+ }
}
void ClientMap::getBlocksInViewRange(v3s16 cam_pos_nodes,
- v3s16 *p_blocks_min, v3s16 *p_blocks_max)
+ v3s16 *p_blocks_min, v3s16 *p_blocks_max, float range)
{
- v3s16 box_nodes_d = m_control.wanted_range * v3s16(1, 1, 1);
+ if (range <= 0.0f)
+ range = m_control.wanted_range;
+
+ v3s16 box_nodes_d = range * v3s16(1, 1, 1);
// Define p_nodes_min/max as v3s32 because 'cam_pos_nodes -/+ box_nodes_d'
// can exceed the range of v3s16 when a large view range is used near the
// world edges.
@@ -147,6 +165,8 @@ void ClientMap::updateDrawList()
{
ScopeProfiler sp(g_profiler, "CM::updateDrawList()", SPT_AVG);
+ m_needs_update_drawlist = false;
+
for (auto &i : m_drawlist) {
MapBlock *block = i.second;
block->refDrop();
@@ -161,10 +181,14 @@ void ClientMap::updateDrawList()
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);
+ // Read the vision range, unless unlimited range is enabled.
+ float range = m_control.range_all ? 1e7 : m_control.wanted_range;
+
// Number of blocks currently loaded by the client
u32 blocks_loaded = 0;
// Number of blocks with mesh in rendering range
@@ -182,6 +206,9 @@ void ClientMap::updateDrawList()
occlusion_culling_enabled = false;
}
+ v3s16 camera_block = getContainerPos(cam_pos_nodes, MAP_BLOCKSIZE);
+ m_drawlist = std::map<v3s16, MapBlock*, MapBlockComparer>(MapBlockComparer(camera_block));
+
// 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)
@@ -218,32 +245,34 @@ void ClientMap::updateDrawList()
continue;
}
- float range = 100000 * BS;
- if (!m_control.range_all)
- range = m_control.wanted_range * BS;
+ v3s16 block_coord = block->getPos();
+ v3s16 block_position = block->getPosRelative() + MAP_BLOCKSIZE / 2;
- float d = 0.0;
- if (!isBlockInSight(block->getPos(), camera_position,
- camera_direction, camera_fov, range, &d))
- continue;
+ // First, perform a simple distance check, with a padding of one extra block.
+ if (!m_control.range_all &&
+ block_position.getDistanceFrom(cam_pos_nodes) > range + MAP_BLOCKSIZE)
+ continue; // Out of range, skip.
+ // Keep the block alive as long as it is in range.
+ block->resetUsageTimer();
blocks_in_range_with_mesh++;
- /*
- Occlusion culling
- */
+ // Frustum culling
+ float d = 0.0;
+ if (!isBlockInSight(block_coord, camera_position,
+ camera_direction, camera_fov, range * BS, &d))
+ continue;
+
+ // Occlusion culling
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();
-
// Add to set
block->refGrab();
- m_drawlist[block->getPos()] = block;
+ m_drawlist[block_coord] = block;
sector_blocks_drawn++;
} // foreach sectorblocks
@@ -282,8 +311,6 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
const u32 daynight_ratio = m_client->getEnv().getDayNightRatio();
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
@@ -300,7 +327,20 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
Draw the selected MapBlocks
*/
- MeshBufListList drawbufs;
+ MeshBufListList grouped_buffers;
+
+ struct DrawDescriptor {
+ v3s16 m_pos;
+ scene::IMeshBuffer *m_buffer;
+ bool m_reuse_material;
+
+ DrawDescriptor(const v3s16 &pos, scene::IMeshBuffer *buffer, bool reuse_material) :
+ m_pos(pos), m_buffer(buffer), m_reuse_material(reuse_material)
+ {}
+ };
+
+ std::vector<DrawDescriptor> draw_order;
+ video::SMaterial previous_material;
for (auto &i : m_drawlist) {
v3s16 block_pos = i.first;
@@ -310,14 +350,12 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
if (!block->mesh)
continue;
- float d = 0.0;
- if (!isBlockInSight(block->getPos(), camera_position,
- camera_direction, camera_fov, 100000 * BS, &d))
- continue;
+ v3f block_pos_r = intToFloat(block->getPosRelative() + MAP_BLOCKSIZE / 2, BS);
+ float d = camera_position.getDistanceFrom(block_pos_r);
+ d = MYMAX(0,d - BLOCK_MAX_RADIUS);
// Mesh animation
if (pass == scene::ESNRP_SOLID) {
- //MutexAutoLock lock(block->mesh_mutex);
MapBlockMesh *mapBlockMesh = block->mesh;
assert(mapBlockMesh);
// Pretty random but this should work somewhat nicely
@@ -338,8 +376,6 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
Get the meshbuffers of the block
*/
{
- //MutexAutoLock lock(block->mesh_mutex);
-
MapBlockMesh *mapBlockMesh = block->mesh;
assert(mapBlockMesh);
@@ -369,42 +405,79 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
material.setFlag(video::EMF_WIREFRAME,
m_control.show_wireframe);
- drawbufs.add(buf, block_pos, layer);
+ if (is_transparent_pass) {
+ // Same comparison as in MeshBufListList
+ bool new_material = material.getTexture(0) != previous_material.getTexture(0) ||
+ material != previous_material;
+
+ draw_order.emplace_back(block_pos, buf, !new_material);
+
+ if (new_material)
+ previous_material = material;
+ }
+ else {
+ grouped_buffers.add(buf, block_pos, layer);
+ }
}
}
}
}
}
+ // Capture draw order for all solid meshes
+ for (auto &lists : grouped_buffers.lists) {
+ for (MeshBufList &list : lists) {
+ // iterate in reverse to draw closest blocks first
+ for (auto it = list.bufs.rbegin(); it != list.bufs.rend(); ++it) {
+ draw_order.emplace_back(it->first, it->second, it != list.bufs.rbegin());
+ }
+ }
+ }
+
TimeTaker draw("Drawing mesh buffers");
core::matrix4 m; // Model matrix
v3f offset = intToFloat(m_camera_offset, BS);
+ u32 material_swaps = 0;
- // Render all layers in order
- for (auto &lists : drawbufs.lists) {
- for (MeshBufList &list : lists) {
- // 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);
-
- drawcall_count += list.bufs.size();
- for (auto &pair : list.bufs) {
- scene::IMeshBuffer *buf = pair.second;
+ // Render all mesh buffers in order
+ drawcall_count += draw_order.size();
+ for (auto &descriptor : draw_order) {
+ scene::IMeshBuffer *buf = descriptor.m_buffer;
- v3f block_wpos = intToFloat(pair.first * MAP_BLOCKSIZE, BS);
- m.setTranslation(block_wpos - offset);
+ // 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->setTransform(video::ETS_WORLD, m);
- driver->drawMeshBuffer(buf);
- vertex_count += buf->getVertexCount();
+ if (!descriptor.m_reuse_material) {
+ auto &material = buf->getMaterial();
+ // pass the shadow map texture to the buffer texture
+ ShadowRenderer *shadow = m_rendering_engine->get_shadow_renderer();
+ if (shadow && shadow->is_active()) {
+ auto &layer = material.TextureLayer[3];
+ layer.Texture = shadow->get_texture();
+ layer.TextureWrapU = video::E_TEXTURE_CLAMP::ETC_CLAMP_TO_EDGE;
+ layer.TextureWrapV = video::E_TEXTURE_CLAMP::ETC_CLAMP_TO_EDGE;
+ // Do not enable filter on shadow texture to avoid visual artifacts
+ // with colored shadows.
+ // Filtering is done in shader code anyway
+ layer.TrilinearFilter = false;
}
+ driver->setMaterial(material);
+ ++material_swaps;
}
+
+ v3f block_wpos = intToFloat(descriptor.m_pos * MAP_BLOCKSIZE, BS);
+ m.setTranslation(block_wpos - offset);
+
+ driver->setTransform(video::ETS_WORLD, m);
+ driver->drawMeshBuffer(buf);
+ vertex_count += buf->getVertexCount();
}
+
g_profiler->avg(prefix + "draw meshes [ms]", draw.stop(true));
// Log only on solid pass because values are the same
@@ -412,8 +485,13 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
g_profiler->avg("renderMap(): animated meshes [#]", mesh_animate_count);
}
+ if (pass == scene::ESNRP_TRANSPARENT) {
+ g_profiler->avg("renderMap(): transparent buffers [#]", draw_order.size());
+ }
+
g_profiler->avg(prefix + "vertices drawn [#]", vertex_count);
g_profiler->avg(prefix + "drawcalls [#]", drawcall_count);
+ g_profiler->avg(prefix + "material swaps [#]", material_swaps);
}
static bool getVisibleBrightness(Map *map, const v3f &p0, v3f dir, float step,
@@ -606,3 +684,210 @@ void ClientMap::PrintInfo(std::ostream &out)
{
out<<"ClientMap: ";
}
+
+void ClientMap::renderMapShadows(video::IVideoDriver *driver,
+ const video::SMaterial &material, s32 pass, int frame, int total_frames)
+{
+ bool is_transparent_pass = pass != scene::ESNRP_SOLID;
+ std::string prefix;
+ if (is_transparent_pass)
+ prefix = "renderMap(SHADOW TRANS): ";
+ else
+ prefix = "renderMap(SHADOW SOLID): ";
+
+ u32 drawcall_count = 0;
+ u32 vertex_count = 0;
+
+ MeshBufListList drawbufs;
+
+ int count = 0;
+ int low_bound = is_transparent_pass ? 0 : m_drawlist_shadow.size() / total_frames * frame;
+ int high_bound = is_transparent_pass ? m_drawlist_shadow.size() : m_drawlist_shadow.size() / total_frames * (frame + 1);
+
+ // transparent pass should be rendered in one go
+ if (is_transparent_pass && frame != total_frames - 1) {
+ return;
+ }
+
+ for (auto &i : m_drawlist_shadow) {
+ // only process specific part of the list & break early
+ ++count;
+ if (count <= low_bound)
+ continue;
+ if (count > high_bound)
+ break;
+
+ v3s16 block_pos = i.first;
+ MapBlock *block = i.second;
+
+ // If the mesh of the block happened to get deleted, ignore it
+ if (!block->mesh)
+ continue;
+
+ /*
+ Get the meshbuffers of the block
+ */
+ {
+ 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 == is_transparent_pass)
+ drawbufs.add(buf, block_pos, layer);
+ }
+ }
+ }
+ }
+
+ TimeTaker draw("Drawing shadow mesh buffers");
+
+ core::matrix4 m; // Model matrix
+ v3f offset = intToFloat(m_camera_offset, BS);
+
+ // Render all layers in order
+ for (auto &lists : drawbufs.lists) {
+ for (MeshBufList &list : lists) {
+ // Check and abort if the machine is swapping a lot
+ if (draw.getTimerTime() > 1000) {
+ infostream << "ClientMap::renderMapShadows(): Rendering "
+ "took >1s, returning." << std::endl;
+ break;
+ }
+ for (auto &pair : list.bufs) {
+ scene::IMeshBuffer *buf = pair.second;
+
+ // override some material properties
+ video::SMaterial local_material = buf->getMaterial();
+ local_material.MaterialType = material.MaterialType;
+ local_material.BackfaceCulling = material.BackfaceCulling;
+ local_material.FrontfaceCulling = material.FrontfaceCulling;
+ local_material.BlendOperation = material.BlendOperation;
+ local_material.Lighting = false;
+ driver->setMaterial(local_material);
+
+ 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();
+ }
+
+ drawcall_count += list.bufs.size();
+ }
+ }
+
+ // restore the driver material state
+ video::SMaterial clean;
+ clean.BlendOperation = video::EBO_ADD;
+ driver->setMaterial(clean); // reset material to defaults
+ 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);
+}
+
+/*
+ Custom update draw list for the pov of shadow light.
+*/
+void ClientMap::updateDrawListShadow(const v3f &shadow_light_pos, const v3f &shadow_light_dir, float shadow_range)
+{
+ ScopeProfiler sp(g_profiler, "CM::updateDrawListShadow()", SPT_AVG);
+
+ const v3f camera_position = shadow_light_pos;
+ const v3f camera_direction = shadow_light_dir;
+ // I "fake" fov just to avoid creating a new function to handle orthographic
+ // projection.
+ const f32 camera_fov = m_camera_fov * 1.9f;
+
+ 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, shadow_range);
+
+ std::vector<v2s16> blocks_in_range;
+
+ for (auto &i : m_drawlist_shadow) {
+ MapBlock *block = i.second;
+ block->refDrop();
+ }
+ m_drawlist_shadow.clear();
+
+ // We need to append the blocks from the camera POV because sometimes
+ // they are not inside the light frustum and it creates glitches.
+ // FIXME: This could be removed if we figure out why they are missing
+ // from the light frustum.
+ for (auto &i : m_drawlist) {
+ i.second->refGrab();
+ m_drawlist_shadow[i.first] = i.second;
+ }
+
+ // 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;
+
+ for (auto &sector_it : m_sectors) {
+ MapSector *sector = sector_it.second;
+ if (!sector)
+ continue;
+ blocks_loaded += sector->size();
+
+ MapBlockVect sectorblocks;
+ sector->getBlocks(sectorblocks);
+
+ /*
+ Loop through blocks in sector
+ */
+ for (MapBlock *block : sectorblocks) {
+ if (!block->mesh) {
+ // Ignore if mesh doesn't exist
+ continue;
+ }
+
+ float range = shadow_range;
+
+ float d = 0.0;
+ if (!isBlockInSight(block->getPos(), camera_position,
+ camera_direction, camera_fov, range, &d))
+ continue;
+
+ blocks_in_range_with_mesh++;
+
+ /*
+ Occlusion culling
+ */
+ if (isBlockOccluded(block, cam_pos_nodes)) {
+ blocks_occlusion_culled++;
+ continue;
+ }
+
+ // This block is in range. Reset usage timer.
+ block->resetUsageTimer();
+
+ // Add to set
+ if (m_drawlist_shadow.find(block->getPos()) == m_drawlist_shadow.end()) {
+ block->refGrab();
+ m_drawlist_shadow[block->getPos()] = block;
+ }
+ }
+ }
+
+ g_profiler->avg("SHADOW MapBlock meshes in range [#]", blocks_in_range_with_mesh);
+ g_profiler->avg("SHADOW MapBlocks occlusion culled [#]", blocks_occlusion_culled);
+ g_profiler->avg("SHADOW MapBlocks drawn [#]", m_drawlist_shadow.size());
+ g_profiler->avg("SHADOW MapBlocks loaded [#]", blocks_loaded);
+}