#include "mapblock_mesh.h"
#include <IMaterialRenderer.h>
#include <matrix4.h>
-#include "log.h"
#include "mapsector.h"
-#include "nodedef.h"
#include "mapblock.h"
#include "profiler.h"
#include "settings.h"
#include "camera.h" // CameraModes
-#include "util/mathconstants.h"
#include "util/basic_macros.h"
#include <algorithm>
+#include "client/renderingengine.h"
ClientMap::ClientMap(
Client *client,
- IGameDef *gamedef,
MapDrawControl &control,
- scene::ISceneNode* parent,
- scene::ISceneManager* mgr,
s32 id
):
- Map(dout_client, gamedef),
- scene::ISceneNode(parent, mgr, id),
+ Map(dout_client, client),
+ scene::ISceneNode(RenderingEngine::get_scene_manager()->getRootSceneNode(),
+ RenderingEngine::get_scene_manager(), id),
m_client(client),
- m_control(control),
- m_camera_position(0,0,0),
- m_camera_direction(0,0,1),
- m_camera_fov(M_PI)
+ m_control(control)
{
m_box = aabb3f(-BS*1000000,-BS*1000000,-BS*1000000,
BS*1000000,BS*1000000,BS*1000000);
}
-ClientMap::~ClientMap()
-{
- /*MutexAutoLock lock(mesh_mutex);
-
- if(mesh != NULL)
- {
- mesh->drop();
- mesh = NULL;
- }*/
-}
-
MapSector * ClientMap::emergeSector(v2s16 p2d)
{
DSTACK(FUNCTION_NAME);
ISceneNode::OnRegisterSceneNode();
}
-static bool isOccluded(Map *map, v3s16 p0, v3s16 p1, float step, float stepfac,
- float start_off, float end_off, u32 needed_count, INodeDefManager *nodemgr)
-{
- float d0 = (float)BS * p0.getDistanceFrom(p1);
- v3s16 u0 = p1 - p0;
- v3f uf = v3f(u0.X, u0.Y, u0.Z) * BS;
- uf.normalize();
- v3f p0f = v3f(p0.X, p0.Y, p0.Z) * BS;
- u32 count = 0;
- for(float s=start_off; s<d0+end_off; s+=step){
- v3f pf = p0f + uf * s;
- v3s16 p = floatToInt(pf, BS);
- MapNode n = map->getNodeNoEx(p);
- bool is_transparent = false;
- const ContentFeatures &f = nodemgr->get(n);
- if(f.solidness == 0)
- is_transparent = (f.visual_solidness != 2);
- else
- is_transparent = (f.solidness != 2);
- if(!is_transparent){
- count++;
- if(count >= needed_count)
- return true;
- }
- step *= stepfac;
- }
- return false;
-}
-
-void ClientMap::getBlocksInViewRange(v3s16 cam_pos_nodes,
+void ClientMap::getBlocksInViewRange(v3s16 cam_pos_nodes,
v3s16 *p_blocks_min, v3s16 *p_blocks_max)
{
v3s16 box_nodes_d = m_control.wanted_range * v3s16(1, 1, 1);
p_nodes_max.Z / MAP_BLOCKSIZE + 1);
}
-void ClientMap::updateDrawList(video::IVideoDriver* driver)
+void ClientMap::updateDrawList()
{
ScopeProfiler sp(g_profiler, "CM::updateDrawList()", SPT_AVG);
g_profiler->add("CM::updateDrawList() count", 1);
- INodeDefManager *nodemgr = m_gamedef->ndef();
-
- for (std::map<v3s16, MapBlock*>::iterator i = m_drawlist.begin();
- i != m_drawlist.end(); ++i) {
- MapBlock *block = i->second;
+ for (auto &i : m_drawlist) {
+ MapBlock *block = i.second;
block->refDrop();
}
m_drawlist.clear();
if (g_settings->getBool("free_move")) {
MapNode n = getNodeNoEx(cam_pos_nodes);
if (n.getContent() == CONTENT_IGNORE ||
- nodemgr->get(n).solidness == 2)
+ m_nodedef->get(n).solidness == 2)
occlusion_culling_enabled = false;
}
- for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
- si != m_sectors.end(); ++si) {
- MapSector *sector = si->second;
+ for (const auto §or_it : m_sectors) {
+ MapSector *sector = sector_it.second;
v2s16 sp = sector->getPos();
- if (m_control.range_all == false) {
+ 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)
continue;
u32 sector_blocks_drawn = 0;
- for (MapBlockVect::iterator i = sectorblocks.begin();
- i != sectorblocks.end(); ++i) {
- MapBlock *block = *i;
-
+ for (auto 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 != NULL)
+ if (block->mesh)
block->mesh->updateCameraOffset(m_camera_offset);
float range = 100000 * BS;
- if (m_control.range_all == false)
+ if (!m_control.range_all)
range = m_control.wanted_range * BS;
float d = 0.0;
/*
Ignore if mesh doesn't exist
*/
- if (block->mesh == NULL) {
+ if (!block->mesh) {
blocks_in_range_without_mesh++;
continue;
}
/*
Occlusion culling
*/
- v3s16 cpn = block->getPos() * MAP_BLOCKSIZE;
- cpn += v3s16(MAP_BLOCKSIZE / 2, MAP_BLOCKSIZE / 2, MAP_BLOCKSIZE / 2);
- float step = BS * 1;
- float stepfac = 1.1;
- float startoff = BS * 1;
- // The occlusion search of 'isOccluded()' must stop short of the target
- // point by distance 'endoff' (end offset) to not enter the target mapblock.
- // For the 8 mapblock corners 'endoff' must therefore be the maximum diagonal
- // of a mapblock, because we must consider all view angles.
- // sqrt(1^2 + 1^2 + 1^2) = 1.732
- float endoff = -BS * MAP_BLOCKSIZE * 1.732050807569;
- v3s16 spn = cam_pos_nodes;
- s16 bs2 = MAP_BLOCKSIZE / 2 + 1;
- // to reduce the likelihood of falsely occluded blocks
- // require at least two solid blocks
- // this is a HACK, we should think of a more precise algorithm
- u32 needed_count = 2;
- if (occlusion_culling_enabled &&
- // For the central point of the mapblock 'endoff' can be halved
- isOccluded(this, spn, cpn,
- step, stepfac, startoff, endoff / 2.0f, needed_count, nodemgr) &&
- isOccluded(this, spn, cpn + v3s16(bs2,bs2,bs2),
- step, stepfac, startoff, endoff, needed_count, nodemgr) &&
- isOccluded(this, spn, cpn + v3s16(bs2,bs2,-bs2),
- step, stepfac, startoff, endoff, needed_count, nodemgr) &&
- isOccluded(this, spn, cpn + v3s16(bs2,-bs2,bs2),
- step, stepfac, startoff, endoff, needed_count, nodemgr) &&
- isOccluded(this, spn, cpn + v3s16(bs2,-bs2,-bs2),
- step, stepfac, startoff, endoff, needed_count, nodemgr) &&
- isOccluded(this, spn, cpn + v3s16(-bs2,bs2,bs2),
- step, stepfac, startoff, endoff, needed_count, nodemgr) &&
- isOccluded(this, spn, cpn + v3s16(-bs2,bs2,-bs2),
- step, stepfac, startoff, endoff, needed_count, nodemgr) &&
- isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,bs2),
- step, stepfac, startoff, endoff, needed_count, nodemgr) &&
- isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,-bs2),
- step, stepfac, startoff, endoff, needed_count, nodemgr)) {
+ if (occlusion_culling_enabled && isBlockOccluded(block, cam_pos_nodes)) {
blocks_occlusion_culled++;
continue;
}
m_last_drawn_sectors.insert(sp);
}
- m_control.blocks_would_have_drawn = blocks_would_have_drawn;
- m_control.blocks_drawn = blocks_drawn;
- m_control.farthest_drawn = farthest_drawn;
-
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)
struct MeshBufListList
{
- std::vector<MeshBufList> lists;
+ /*!
+ * 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()
{
- lists.clear();
+ for (int l = 0; l < MAX_TILE_LAYERS; l++)
+ lists[l].clear();
}
- void add(scene::IMeshBuffer *buf)
+ void add(scene::IMeshBuffer *buf, u8 layer)
{
- for(std::vector<MeshBufList>::iterator i = lists.begin();
- i != lists.end(); ++i){
- MeshBufList &l = *i;
- video::SMaterial &m = buf->getMaterial();
-
+ // 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)
}
}
MeshBufList l;
- l.m = buf->getMaterial();
+ l.m = m;
l.bufs.push_back(buf);
- lists.push_back(l);
+ list.push_back(l);
}
};
Measuring time is very useful for long delays when the
machine is swapping a lot.
*/
- int time1 = time(0);
+ std::time_t time1 = time(0);
/*
Get animation parameters
MeshBufListList drawbufs;
- for (std::map<v3s16, MapBlock*>::iterator i = m_drawlist.begin();
- i != m_drawlist.end(); ++i) {
- MapBlock *block = i->second;
+ for (auto &i : m_drawlist) {
+ MapBlock *block = i.second;
// If the mesh of the block happened to get deleted, ignore it
- if (block->mesh == NULL)
+ if (!block->mesh)
continue;
float d = 0.0;
continue;
// Mesh animation
- {
+ if (pass == scene::ESNRP_SOLID) {
//MutexAutoLock lock(block->mesh_mutex);
MapBlockMesh *mapBlockMesh = block->mesh;
assert(mapBlockMesh);
MapBlockMesh *mapBlockMesh = block->mesh;
assert(mapBlockMesh);
- scene::IMesh *mesh = mapBlockMesh->getMesh();
- assert(mesh);
-
- u32 c = mesh->getMeshBufferCount();
- for (u32 i = 0; i < c; i++)
- {
- scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
+ for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
+ scene::IMesh *mesh = mapBlockMesh->getMesh(layer);
+ assert(mesh);
- buf->getMaterial().setFlag(video::EMF_TRILINEAR_FILTER, m_cache_trilinear_filter);
- buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, m_cache_bilinear_filter);
- buf->getMaterial().setFlag(video::EMF_ANISOTROPIC_FILTER, m_cache_anistropic_filter);
- buf->getMaterial().setFlag(video::EMF_WIREFRAME, m_control.show_wireframe);
+ u32 c = mesh->getMeshBufferCount();
+ for (u32 i = 0; i < c; i++) {
+ scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
- const video::SMaterial& material = buf->getMaterial();
- video::IMaterialRenderer* rnd =
+ video::SMaterial& material = buf->getMaterial();
+ video::IMaterialRenderer* rnd =
driver->getMaterialRenderer(material.MaterialType);
- bool transparent = (rnd && rnd->isTransparent());
- if (transparent == is_transparent_pass) {
- if (buf->getVertexCount() == 0)
- errorstream << "Block [" << analyze_block(block)
- << "] contains an empty meshbuf" << std::endl;
- drawbufs.add(buf);
+ bool transparent = (rnd && rnd->isTransparent());
+ if (transparent == is_transparent_pass) {
+ if (buf->getVertexCount() == 0)
+ errorstream << "Block [" << analyze_block(block)
+ << "] contains an empty meshbuf" << std::endl;
+
+ material.setFlag(video::EMF_TRILINEAR_FILTER,
+ m_cache_trilinear_filter);
+ material.setFlag(video::EMF_BILINEAR_FILTER,
+ m_cache_bilinear_filter);
+ material.setFlag(video::EMF_ANISOTROPIC_FILTER,
+ m_cache_anistropic_filter);
+ material.setFlag(video::EMF_WIREFRAME,
+ m_control.show_wireframe);
+
+ drawbufs.add(buf, layer);
+ }
}
}
}
}
- std::vector<MeshBufList> &lists = drawbufs.lists;
-
- int timecheck_counter = 0;
- for (std::vector<MeshBufList>::iterator i = lists.begin();
- i != lists.end(); ++i) {
- timecheck_counter++;
- if (timecheck_counter > 50) {
- timecheck_counter = 0;
- int time2 = time(0);
- if (time2 > time1 + 4) {
- infostream << "ClientMap::renderMap(): "
- "Rendering takes ages, returning."
- << std::endl;
- return;
+ // Render all layers in order
+ for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
+ std::vector<MeshBufList> &lists = drawbufs.lists[layer];
+
+ 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;
+ }
}
- }
- MeshBufList &list = *i;
+ driver->setMaterial(list.m);
- driver->setMaterial(list.m);
-
- for (std::vector<scene::IMeshBuffer*>::iterator j = list.bufs.begin();
- j != list.bufs.end(); ++j) {
- scene::IMeshBuffer *buf = *j;
- driver->drawMeshBuffer(buf);
- vertex_count += buf->getVertexCount();
- meshbuffer_count++;
+ for (scene::IMeshBuffer *buf : list.bufs) {
+ driver->drawMeshBuffer(buf);
+ vertex_count += buf->getVertexCount();
+ meshbuffer_count++;
+ }
}
-
}
} // ScopeProfiler
if (blocks_drawn != 0)
g_profiler->avg(prefix + "empty blocks (frac)",
(float)blocks_without_stuff / blocks_drawn);
-
- /*infostream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
- <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
}
-static bool getVisibleBrightness(Map *map, v3f p0, v3f dir, float step,
+static bool getVisibleBrightness(Map *map, const v3f &p0, v3f dir, float step,
float step_multiplier, float start_distance, float end_distance,
INodeDefManager *ndef, u32 daylight_factor, float sunlight_min_d,
int *result, bool *sunlight_seen)
sunlight_min_d = 0;
}
}
- for(int i=0; distance < end_distance; i++){
+ for (int i=0; distance < end_distance; i++) {
pf += dir * step;
distance += step;
step *= step_multiplier;
v3s16 p = floatToInt(pf, BS);
MapNode n = map->getNodeNoEx(p);
- if(allow_allowing_non_sunlight_propagates && i == 0 &&
+ if (allow_allowing_non_sunlight_propagates && i == 0 &&
ndef->get(n).param_type == CPT_LIGHT &&
- !ndef->get(n).sunlight_propagates){
+ !ndef->get(n).sunlight_propagates) {
allow_non_sunlight_propagates = true;
}
- if(ndef->get(n).param_type != CPT_LIGHT ||
+
+ if (ndef->get(n).param_type != CPT_LIGHT ||
(!ndef->get(n).sunlight_propagates &&
!allow_non_sunlight_propagates)){
nonlight_seen = true;
break;
continue;
}
- if(distance >= sunlight_min_d && *sunlight_seen == false
- && nonlight_seen == false)
- if(n.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN)
+
+ if (distance >= sunlight_min_d && !*sunlight_seen && !nonlight_seen)
+ if (n.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN)
*sunlight_seen = true;
noncount = 0;
brightness_sum += decode_light(n.getLightBlend(daylight_factor, ndef));
int ClientMap::getBackgroundBrightness(float max_d, u32 daylight_factor,
int oldvalue, bool *sunlight_seen_result)
{
- const bool debugprint = false;
- INodeDefManager *ndef = m_gamedef->ndef();
static v3f z_directions[50] = {
v3f(-100, 0, 0)
};
static f32 z_offsets[sizeof(z_directions)/sizeof(*z_directions)] = {
-1000,
};
+
if(z_directions[0].X < -99){
for(u32 i=0; i<sizeof(z_directions)/sizeof(*z_directions); i++){
+ // Assumes FOV of 72 and 16/9 aspect ratio
z_directions[i] = v3f(
- 0.01 * myrand_range(-100, 100),
+ 0.02 * myrand_range(-100, 100),
1.0,
0.01 * myrand_range(-100, 100)
- );
+ ).normalize();
z_offsets[i] = 0.01 * myrand_range(0,100);
}
}
- if(debugprint)
- std::cerr<<"In goes "<<PP(m_camera_direction)<<", out comes ";
+
int sunlight_seen_count = 0;
float sunlight_min_d = max_d*0.8;
if(sunlight_min_d > 35*BS)
std::vector<int> values;
for(u32 i=0; i<sizeof(z_directions)/sizeof(*z_directions); i++){
v3f z_dir = z_directions[i];
- z_dir.normalize();
core::CMatrix4<f32> a;
a.buildRotateFromTo(v3f(0,1,0), z_dir);
v3f dir = m_camera_direction;
float off = step * z_offsets[i];
bool sunlight_seen_now = false;
bool ok = getVisibleBrightness(this, m_camera_position, dir,
- step, 1.0, max_d*0.6+off, max_d, ndef, daylight_factor,
+ step, 1.0, max_d*0.6+off, max_d, m_nodedef, daylight_factor,
sunlight_min_d,
&br, &sunlight_seen_now);
if(sunlight_seen_now)
else if(num_values_to_use >= 7)
num_values_to_use -= num_values_to_use/3;
u32 first_value_i = (values.size() - num_values_to_use) / 2;
- if(debugprint){
- for(u32 i=0; i < first_value_i; i++)
- std::cerr<<values[i]<<" ";
- std::cerr<<"[";
- }
- for(u32 i=first_value_i; i < first_value_i+num_values_to_use; i++){
- if(debugprint)
- std::cerr<<values[i]<<" ";
+
+ for (u32 i=first_value_i; i < first_value_i + num_values_to_use; i++) {
brightness_sum += values[i];
brightness_count++;
}
- if(debugprint){
- std::cerr<<"]";
- for(u32 i=first_value_i+num_values_to_use; i < values.size(); i++)
- std::cerr<<values[i]<<" ";
- }
+
int ret = 0;
if(brightness_count == 0){
MapNode n = getNodeNoEx(floatToInt(m_camera_position, BS));
- if(ndef->get(n).param_type == CPT_LIGHT){
- ret = decode_light(n.getLightBlend(daylight_factor, ndef));
+ if(m_nodedef->get(n).param_type == CPT_LIGHT){
+ ret = decode_light(n.getLightBlend(daylight_factor, m_nodedef));
} else {
ret = oldvalue;
}
} else {
- /*float pre = (float)brightness_sum / (float)brightness_count;
- float tmp = pre;
- const float d = 0.2;
- pre *= 1.0 + d*2;
- pre -= tmp * d;
- int preint = pre;
- ret = MYMAX(0, MYMIN(255, preint));*/
ret = brightness_sum / brightness_count;
}
- if(debugprint)
- std::cerr<<"Result: "<<ret<<" sunlight_seen_count="
- <<sunlight_seen_count<<std::endl;
+
*sunlight_seen_result = (sunlight_seen_count > 0);
return ret;
}
void ClientMap::renderPostFx(CameraMode cam_mode)
{
- INodeDefManager *nodemgr = m_gamedef->ndef();
-
// Sadly ISceneManager has no "post effects" render pass, in that case we
// could just register for that and handle it in renderMap().
// - If the player is in a solid node, make everything black.
// - If the player is in liquid, draw a semi-transparent overlay.
// - Do not if player is in third person mode
- const ContentFeatures& features = nodemgr->get(n);
+ 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_gamedef->checkLocalPrivilege("noclip")) &&
+ m_client->checkLocalPrivilege("noclip")) &&
cam_mode == CAMERA_MODE_FIRST)
{
post_effect_color = video::SColor(255, 0, 0, 0);