minecraft-style water done (but no texture animation or sound)

This commit is contained in:
Perttu Ahola 2011-01-17 21:15:31 +02:00
parent 0fa0e0752a
commit d44abdab17
13 changed files with 770 additions and 65 deletions

@ -97,7 +97,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/ */
#define MAX_OBJECTDATA_SIZE 450 #define MAX_OBJECTDATA_SIZE 450
#define WATER_LEVEL (0) /*
This is good to be a bit different than 0 so that water level
is not between to MapBlocks
*/
#define WATER_LEVEL 3
// Length of cracking animation in count of images // Length of cracking animation in count of images
#define CRACK_ANIMATION_LENGTH 5 #define CRACK_ANIMATION_LENGTH 5

@ -62,8 +62,8 @@ void set_default_settings()
g_settings.setDefault("active_object_range", "2"); g_settings.setDefault("active_object_range", "2");
g_settings.setDefault("max_simultaneous_block_sends_per_client", "1"); g_settings.setDefault("max_simultaneous_block_sends_per_client", "1");
g_settings.setDefault("max_simultaneous_block_sends_server_total", "4"); g_settings.setDefault("max_simultaneous_block_sends_server_total", "4");
g_settings.setDefault("water_moves", "true"); //g_settings.setDefault("water_moves", "true");
g_settings.setDefault("disable_water_climb", "true"); //g_settings.setDefault("disable_water_climb", "true");
//g_settings.setDefault("endless_water", "true"); //g_settings.setDefault("endless_water", "true");
g_settings.setDefault("max_block_send_distance", "6"); g_settings.setDefault("max_block_send_distance", "6");
g_settings.setDefault("max_block_generate_distance", "6"); g_settings.setDefault("max_block_generate_distance", "6");

@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define LIGHT_HEADER #define LIGHT_HEADER
#include "common_irrlicht.h" #include "common_irrlicht.h"
#include "debug.h"
/* /*
Day/night cache: Day/night cache:
@ -78,7 +79,7 @@ inline u8 decode_light(u8 light)
return light_decode_table[LIGHT_MAX]; return light_decode_table[LIGHT_MAX];
if(light > LIGHT_MAX) if(light > LIGHT_MAX)
throw; light = LIGHT_MAX;
return light_decode_table[light]; return light_decode_table[light];
} }

@ -116,12 +116,16 @@ TODO: Startup and configuration menu
Graphics: Graphics:
TODO:
TODO: Optimize day/night mesh updating somehow TODO: Optimize day/night mesh updating somehow
- create copies of all textures for all lighting values and only - create copies of all textures for all lighting values and only
change texture for material? change texture for material?
- Umm... the collecting of the faces is the slow part - Umm... the collecting of the faces is the slow part
-> what about just changing the color values of the existing -> what about just changing the color values of the existing
meshbuffers? It should go quite fast. meshbuffers? It should go quite fast.
- This is not easy; There'd need to be a buffer somewhere
that would contain the night and day lighting values.
TODO: Draw big amounts of torches better (that is, throw them in the TODO: Draw big amounts of torches better (that is, throw them in the
same meshbuffer (can the meshcollector class be used?)) same meshbuffer (can the meshcollector class be used?))
@ -129,9 +133,13 @@ TODO: Draw big amounts of torches better (that is, throw them in the
TODO: Combine MapBlock's face caches to so big pieces that VBO TODO: Combine MapBlock's face caches to so big pieces that VBO
gets used gets used
- That is >500 vertices - That is >500 vertices
- This is not easy; all the MapBlocks close to the player would
still need to be drawn separately and combining the blocks
would have to happen in a background thread
TODO: Make fetching sector's blocks more efficient when rendering TODO: Make fetching sector's blocks more efficient when rendering
sectors that have very large amounts of blocks (on client) sectors that have very large amounts of blocks (on client)
- Is this necessary at all?
Configuration: Configuration:
@ -210,6 +218,9 @@ TODO: Map generator version 2
- There could be a certain height (to which mountains only reach) - There could be a certain height (to which mountains only reach)
where some minerals are found where some minerals are found
FIXME: The new pre-sunlight-propagation code messes up with initial
water lighting
Doing now: Doing now:
====================================================================== ======================================================================

@ -926,6 +926,37 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
MapBlock *block = i.getNode()->getValue(); MapBlock *block = i.getNode()->getValue();
block->updateDayNightDiff(); block->updateDayNightDiff();
} }
/*
Add neighboring liquid nodes and the node itself if it is
liquid (=water node was added) to transform queue.
*/
v3s16 dirs[7] = {
v3s16(0,0,0), // self
v3s16(0,0,1), // back
v3s16(0,1,0), // top
v3s16(1,0,0), // right
v3s16(0,0,-1), // front
v3s16(0,-1,0), // bottom
v3s16(-1,0,0), // left
};
for(u16 i=0; i<7; i++)
{
try
{
v3s16 p2 = p + dirs[i];
MapNode n2 = getNode(p2);
if(content_liquid(n2.d))
{
m_transforming_liquid.push_back(p2);
}
}catch(InvalidPositionException &e)
{
}
}
} }
/* /*
@ -1063,6 +1094,35 @@ void Map::removeNodeAndUpdate(v3s16 p,
MapBlock *block = i.getNode()->getValue(); MapBlock *block = i.getNode()->getValue();
block->updateDayNightDiff(); block->updateDayNightDiff();
} }
/*
Add neighboring liquid nodes to transform queue.
*/
v3s16 dirs[6] = {
v3s16(0,0,1), // back
v3s16(0,1,0), // top
v3s16(1,0,0), // right
v3s16(0,0,-1), // front
v3s16(0,-1,0), // bottom
v3s16(-1,0,0), // left
};
for(u16 i=0; i<6; i++)
{
try
{
v3s16 p2 = p + dirs[i];
MapNode n2 = getNode(p2);
if(content_liquid(n2.d))
{
m_transforming_liquid.push_back(p2);
}
}catch(InvalidPositionException &e)
{
}
}
} }
#ifndef SERVER #ifndef SERVER
@ -1303,6 +1363,276 @@ void Map::PrintInfo(std::ostream &out)
out<<"Map: "; out<<"Map: ";
} }
#define WATER_DROP_BOOST 4
void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
{
DSTACK(__FUNCTION_NAME);
TimeTaker timer("transformLiquids()");
u32 loopcount = 0;
u32 initial_size = m_transforming_liquid.size();
while(m_transforming_liquid.size() != 0)
{
v3s16 p0 = m_transforming_liquid.pop_front();
MapNode n0 = getNode(p0);
// Don't deal with non-liquids
if(content_liquid(n0.d) == false)
continue;
bool is_source = !content_flowing_liquid(n0.d);
u8 liquid_level = 8;
if(is_source == false)
liquid_level = n0.param2 & 0x0f;
// Turn possible source into non-source
u8 nonsource_c = make_liquid_flowing(n0.d);
/*
If not source, check that some node flows into this one
and what is the level of liquid in this one
*/
if(is_source == false)
{
s8 new_liquid_level_max = -1;
v3s16 dirs_from[5] = {
v3s16(0,1,0), // top
v3s16(0,0,1), // back
v3s16(1,0,0), // right
v3s16(0,0,-1), // front
v3s16(-1,0,0), // left
};
for(u16 i=0; i<5; i++)
{
try
{
bool from_top = (i==0);
v3s16 p2 = p0 + dirs_from[i];
MapNode n2 = getNode(p2);
if(content_liquid(n2.d))
{
u8 n2_nonsource_c = make_liquid_flowing(n2.d);
// Check that the liquids are the same type
if(n2_nonsource_c != nonsource_c)
{
dstream<<"WARNING: Not handling: different liquids"
" collide"<<std::endl;
continue;
}
bool n2_is_source = !content_flowing_liquid(n2.d);
s8 n2_liquid_level = 8;
if(n2_is_source == false)
n2_liquid_level = n2.param2 & 0x07;
s8 new_liquid_level = -1;
if(from_top)
{
//new_liquid_level = 7;
if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
new_liquid_level = 7;
else
new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
}
else if(n2_liquid_level > 0)
{
new_liquid_level = n2_liquid_level - 1;
}
if(new_liquid_level > new_liquid_level_max)
new_liquid_level_max = new_liquid_level;
}
}catch(InvalidPositionException &e)
{
}
} //for
/*
If liquid level should be something else, update it and
add all the neighboring water nodes to the transform queue.
*/
if(new_liquid_level_max != liquid_level)
{
if(new_liquid_level_max == -1)
{
// Remove water alltoghether
n0.d = CONTENT_AIR;
n0.param2 = 0;
setNode(p0, n0);
}
else
{
n0.param2 = new_liquid_level_max;
setNode(p0, n0);
}
// Block has been modified
{
v3s16 blockpos = getNodeBlockPos(p0);
MapBlock *block = getBlockNoCreateNoEx(blockpos);
if(block != NULL)
modified_blocks.insert(blockpos, block);
}
/*
Add neighboring non-source liquid nodes to transform queue.
*/
v3s16 dirs[6] = {
v3s16(0,0,1), // back
v3s16(0,1,0), // top
v3s16(1,0,0), // right
v3s16(0,0,-1), // front
v3s16(0,-1,0), // bottom
v3s16(-1,0,0), // left
};
for(u16 i=0; i<6; i++)
{
try
{
v3s16 p2 = p0 + dirs[i];
MapNode n2 = getNode(p2);
if(content_flowing_liquid(n2.d))
{
m_transforming_liquid.push_back(p2);
}
}catch(InvalidPositionException &e)
{
}
}
}
}
// Get a new one from queue if the node has turned into non-water
if(content_liquid(n0.d) == false)
continue;
/*
Flow water from this node
*/
v3s16 dirs_to[5] = {
v3s16(0,-1,0), // bottom
v3s16(0,0,1), // back
v3s16(1,0,0), // right
v3s16(0,0,-1), // front
v3s16(-1,0,0), // left
};
for(u16 i=0; i<5; i++)
{
try
{
bool to_bottom = (i == 0);
// If liquid is at lowest possible height, it's not going
// anywhere except down
if(liquid_level == 0 && to_bottom == false)
continue;
u8 liquid_next_level = 0;
// If going to bottom
if(to_bottom)
{
//liquid_next_level = 7;
if(liquid_level >= 7 - WATER_DROP_BOOST)
liquid_next_level = 7;
else
liquid_next_level = liquid_level + WATER_DROP_BOOST;
}
else
liquid_next_level = liquid_level - 1;
bool n2_changed = false;
bool flowed = false;
v3s16 p2 = p0 + dirs_to[i];
MapNode n2 = getNode(p2);
if(content_liquid(n2.d))
{
u8 n2_nonsource_c = make_liquid_flowing(n2.d);
// Check that the liquids are the same type
if(n2_nonsource_c != nonsource_c)
{
dstream<<"WARNING: Not handling: different liquids"
" collide"<<std::endl;
continue;
}
bool n2_is_source = !content_flowing_liquid(n2.d);
u8 n2_liquid_level = 8;
if(n2_is_source == false)
n2_liquid_level = n2.param2 & 0x07;
if(to_bottom)
{
flowed = true;
}
if(n2_is_source)
{
// Just flow into the source, nothing changes.
// n2_changed is not set because destination didn't change
flowed = true;
}
else
{
if(liquid_next_level > liquid_level)
{
n2.param2 = liquid_next_level;
setNode(p2, n2);
n2_changed = true;
flowed = true;
}
}
}
else if(n2.d == CONTENT_AIR)
{
n2.d = nonsource_c;
n2.param2 = liquid_next_level;
setNode(p2, n2);
n2_changed = true;
flowed = true;
}
if(n2_changed)
{
m_transforming_liquid.push_back(p2);
v3s16 blockpos = getNodeBlockPos(p2);
MapBlock *block = getBlockNoCreateNoEx(blockpos);
if(block != NULL)
modified_blocks.insert(blockpos, block);
}
// If n2_changed to bottom, don't flow anywhere else
if(to_bottom && flowed)
break;
}catch(InvalidPositionException &e)
{
}
}
loopcount++;
//if(loopcount >= 100000)
if(loopcount >= initial_size * 1)
break;
}
dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
}
/* /*
ServerMap ServerMap
*/ */
@ -1327,15 +1657,20 @@ ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
/* /*
NOTE: BEWARE: Too big amount of these will make map generation NOTE: BEWARE: Too big amount of these will make map generation
slow. Especially those that are read by every block emerge. slow. Especially those that are read by every block emerge.
Fetch times:
1000 points: 2-3ms
5000 points: 15ms
15000 points: 40ms
*/ */
for(u32 i=0; i<15000; i++) for(u32 i=0; i<5000; i++)
{ {
/*u32 lim = MAP_GENERATION_LIMIT; /*u32 lim = MAP_GENERATION_LIMIT;
if(i < 400) if(i < 400)
lim = 2000;*/ lim = 2000;*/
u32 lim = 1000 + MAP_GENERATION_LIMIT * i / 15000; u32 lim = 1000 + MAP_GENERATION_LIMIT * i / 5000;
v3s16 p( v3s16 p(
-lim + myrand()%(lim*2), -lim + myrand()%(lim*2),
@ -1679,17 +2014,19 @@ MapSector * ServerMap::emergeSector(v2s16 p2d)
Get local attributes Get local attributes
*/ */
//dstream<<"emergeSector(): Reading point attribute lists"<<std::endl; float local_plants_amount = 0.0;
{
// Get plant amount from attributes //dstream<<"emergeSector(): Reading point attribute lists"<<std::endl;
PointAttributeList *palist = m_padb.getList("plants_amount"); //TimeTaker attrtimer("emergeSector() attribute fetch");
assert(palist);
/*float local_plants_amount = // Get plant amount from attributes
palist->getNearAttr(nodepos2d).getFloat();*/ PointAttributeList *palist = m_padb.getList("plants_amount");
float local_plants_amount = assert(palist);
palist->getInterpolatedFloat(nodepos2d); /*local_plants_amount =
palist->getNearAttr(nodepos2d).getFloat();*/
//dstream<<"emergeSector(): done."<<std::endl; local_plants_amount =
palist->getInterpolatedFloat(nodepos2d);
}
/* /*
Generate sector heightmap Generate sector heightmap
@ -1988,6 +2325,12 @@ MapBlock * ServerMap::emergeBlock(
n.d = water_material; n.d = water_material;
n.setLight(LIGHTBANK_DAY, n.setLight(LIGHTBANK_DAY,
diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1)); diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
/*
Add to transforming liquid queue (in case it'd
start flowing)
*/
v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
m_transforming_liquid.push_back(real_pos);
} }
// else air // else air
else else
@ -2692,7 +3035,8 @@ continue_generating:
core::map<v3s16, bool> light_sources; core::map<v3s16, bool> light_sources;
bool black_air_left = false; bool black_air_left = false;
bool bottom_invalid = bool bottom_invalid =
block->propagateSunlight(light_sources, true, &black_air_left); block->propagateSunlight(light_sources, true,
&black_air_left, true);
// If sunlight didn't reach everywhere and part of block is // If sunlight didn't reach everywhere and part of block is
// above ground, lighting has to be properly updated // above ground, lighting has to be properly updated
@ -2700,6 +3044,11 @@ continue_generating:
{ {
lighting_invalidated_blocks[block->getPos()] = block; lighting_invalidated_blocks[block->getPos()] = block;
} }
if(bottom_invalid)
{
lighting_invalidated_blocks[block->getPos()] = block;
}
} }
/* /*
@ -2739,7 +3088,7 @@ continue_generating:
if(haxmode) if(haxmode)
{ {
// Don't calculate lighting at all // Don't calculate lighting at all
lighting_invalidated_blocks.clear(); //lighting_invalidated_blocks.clear();
} }
return block; return block;
@ -3523,11 +3872,12 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
// Total distance // Total distance
f32 d = blockpos_relative.getLength(); f32 d = blockpos_relative.getLength();
/*
Draw the faces of the block
*/
#if 1 #if 1
/*
Update expired mesh
*/
bool mesh_expired = false; bool mesh_expired = false;
{ {
@ -3585,6 +3935,9 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
continue; continue;
}*/ }*/
#endif #endif
/*
Draw the faces of the block
*/
{ {
JMutexAutoLock lock(block->mesh_mutex); JMutexAutoLock lock(block->mesh_mutex);

@ -236,6 +236,8 @@ public:
// For debug printing // For debug printing
virtual void PrintInfo(std::ostream &out); virtual void PrintInfo(std::ostream &out);
void transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks);
/* /*
Variables Variables

@ -146,6 +146,25 @@ u8 MapBlock::getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
v3s16 face_dir) v3s16 face_dir)
{ {
try{ try{
// DEBUG
/*{
if(n.d == CONTENT_WATER)
{
u8 l = n.param2*2;
if(l > LIGHT_MAX)
l = LIGHT_MAX;
return l;
}
if(n2.d == CONTENT_WATER)
{
u8 l = n2.param2*2;
if(l > LIGHT_MAX)
l = LIGHT_MAX;
return l;
}
}*/
u8 light; u8 light;
u8 l1 = n.getLightBlend(daynight_ratio); u8 l1 = n.getLightBlend(daynight_ratio);
u8 l2 = n2.getLightBlend(daynight_ratio); u8 l2 = n2.getLightBlend(daynight_ratio);
@ -645,10 +664,10 @@ void MapBlock::updateMesh(u32 daynight_ratio)
mesh_new = new scene::SMesh(); mesh_new = new scene::SMesh();
MeshCollector collector;
if(fastfaces_new.size() > 0) if(fastfaces_new.size() > 0)
{ {
MeshCollector collector;
for(u32 i=0; i<fastfaces_new.size(); i++) for(u32 i=0; i<fastfaces_new.size(); i++)
{ {
FastFace &f = fastfaces_new[i]; FastFace &f = fastfaces_new[i];
@ -685,16 +704,6 @@ void MapBlock::updateMesh(u32 daynight_ratio)
assert(0); assert(0);
} }
} }
collector.fillMesh(mesh_new);
// Use VBO for mesh (this just would set this for ever buffer)
// This will lead to infinite memory usage because or irrlicht.
//mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
/*std::cout<<"MapBlock has "<<fastfaces_new.size()<<" faces "
<<"and uses "<<mesh_new->getMeshBufferCount()
<<" materials (meshbuffers)"<<std::endl;*/
} }
/* /*
@ -714,8 +723,6 @@ void MapBlock::updateMesh(u32 daynight_ratio)
if(n.d == CONTENT_TORCH) if(n.d == CONTENT_TORCH)
{ {
//scene::IMeshBuffer *buf = new scene::SMeshBuffer();
scene::SMeshBuffer *buf = new scene::SMeshBuffer();
video::SColor c(255,255,255,255); video::SColor c(255,255,255,255);
video::S3DVertex vertices[4] = video::S3DVertex vertices[4] =
@ -746,36 +753,266 @@ void MapBlock::updateMesh(u32 daynight_ratio)
vertices[i].Pos += intToFloat(p + getPosRelative()); vertices[i].Pos += intToFloat(p + getPosRelative());
} }
u16 indices[] = {0,1,2,2,3,0};
buf->append(vertices, 4, indices, 6);
// Set material // Set material
buf->getMaterial().setFlag(video::EMF_LIGHTING, false); video::SMaterial material;
buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false); material.setFlag(video::EMF_LIGHTING, false);
buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false); material.setFlag(video::EMF_BACK_FACE_CULLING, false);
//buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; material.setFlag(video::EMF_BILINEAR_FILTER, false);
buf->getMaterial().MaterialType //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
material.MaterialType
= video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
if(dir == v3s16(0,-1,0)) if(dir == v3s16(0,-1,0))
buf->getMaterial().setTexture(0, material.setTexture(0,
g_irrlicht->getTexture(porting::getDataPath("torch_on_floor.png").c_str())); g_irrlicht->getTexture(porting::getDataPath("torch_on_floor.png").c_str()));
else if(dir == v3s16(0,1,0)) else if(dir == v3s16(0,1,0))
buf->getMaterial().setTexture(0, material.setTexture(0,
g_irrlicht->getTexture(porting::getDataPath("torch_on_ceiling.png").c_str())); g_irrlicht->getTexture(porting::getDataPath("torch_on_ceiling.png").c_str()));
// For backwards compatibility // For backwards compatibility
else if(dir == v3s16(0,0,0)) else if(dir == v3s16(0,0,0))
buf->getMaterial().setTexture(0, material.setTexture(0,
g_irrlicht->getTexture(porting::getDataPath("torch_on_floor.png").c_str())); g_irrlicht->getTexture(porting::getDataPath("torch_on_floor.png").c_str()));
else else
buf->getMaterial().setTexture(0, material.setTexture(0,
g_irrlicht->getTexture(porting::getDataPath("torch.png").c_str())); g_irrlicht->getTexture(porting::getDataPath("torch.png").c_str()));
// Add to mesh u16 indices[] = {0,1,2,2,3,0};
mesh_new->addMeshBuffer(buf); // Add to mesh collector
buf->drop(); collector.append(material, vertices, 4, indices, 6);
}
else if(n.d == CONTENT_WATER)
{
bool top_is_water = false;
try{
MapNode n = getNodeParent(v3s16(x,y+1,z));
if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
top_is_water = true;
}catch(InvalidPositionException &e){}
video::SColor c(128,255,255,255);
// Neighbor water levels (key = relative position)
// Includes current node
core::map<v3s16, f32> neighbor_levels;
core::map<v3s16, u8> neighbor_contents;
v3s16 neighbor_dirs[9] = {
v3s16(0,0,0),
v3s16(0,0,1),
v3s16(0,0,-1),
v3s16(1,0,0),
v3s16(-1,0,0),
v3s16(1,0,1),
v3s16(-1,0,-1),
v3s16(1,0,-1),
v3s16(-1,0,1),
};
for(u32 i=0; i<9; i++)
{
u8 content = CONTENT_AIR;
float level = -0.5 * BS;
try{
v3s16 p2 = p + neighbor_dirs[i];
MapNode n2 = getNodeParent(p2);
content = n2.d;
if(n2.d == CONTENT_WATERSOURCE)
level = 0.5 * BS;
else if(n2.d == CONTENT_WATER)
level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0) * BS;
}
catch(InvalidPositionException &e){}
neighbor_levels.insert(neighbor_dirs[i], level);
neighbor_contents.insert(neighbor_dirs[i], content);
}
//float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
//float water_level = neighbor_levels[v3s16(0,0,0)];
// Corner heights (average between four waters)
f32 corner_levels[4];
v3s16 halfdirs[4] = {
v3s16(0,0,0),
v3s16(1,0,0),
v3s16(1,0,1),
v3s16(0,0,1),
};
for(u32 i=0; i<4; i++)
{
v3s16 cornerdir = halfdirs[i];
float cornerlevel = 0;
u32 valid_count = 0;
for(u32 j=0; j<4; j++)
{
v3s16 neighbordir = cornerdir - halfdirs[j];
u8 content = neighbor_contents[neighbordir];
// Special case for source nodes
if(content == CONTENT_WATERSOURCE)
{
cornerlevel = 0.5*BS;
valid_count = 1;
break;
}
else if(content == CONTENT_WATER)
{
cornerlevel += neighbor_levels[neighbordir];
valid_count++;
}
else if(content == CONTENT_AIR)
{
cornerlevel += -0.5*BS;
valid_count++;
}
}
if(valid_count > 0)
cornerlevel /= valid_count;
corner_levels[i] = cornerlevel;
}
/*
Generate sides
*/
v3s16 side_dirs[4] = {
v3s16(1,0,0),
v3s16(-1,0,0),
v3s16(0,0,1),
v3s16(0,0,-1),
};
s16 side_corners[4][2] = {
{1, 2},
{3, 0},
{2, 3},
{0, 1},
};
for(u32 i=0; i<4; i++)
{
v3s16 dir = side_dirs[i];
//float neighbor_level = neighbor_levels[dir];
/*if(neighbor_level > -0.5*BS + 0.001)
continue;*/
/*if(neighbor_level > water_level - 0.1*BS)
continue;*/
u8 neighbor_content = neighbor_contents[dir];
if(neighbor_content != CONTENT_AIR
&& neighbor_content != CONTENT_WATER)
continue;
bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
if(neighbor_is_water == true && top_is_water == false)
continue;
video::S3DVertex vertices[4] =
{
/*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
};
if(top_is_water)
{
vertices[2].Pos.Y = 0.5*BS;
vertices[3].Pos.Y = 0.5*BS;
}
else
{
vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
}
if(neighbor_is_water)
{
vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
}
else
{
vertices[0].Pos.Y = -0.5*BS;
vertices[1].Pos.Y = -0.5*BS;
}
for(s32 j=0; j<4; j++)
{
if(dir == v3s16(0,0,1))
vertices[j].Pos.rotateXZBy(0);
if(dir == v3s16(0,0,-1))
vertices[j].Pos.rotateXZBy(180);
if(dir == v3s16(-1,0,0))
vertices[j].Pos.rotateXZBy(90);
if(dir == v3s16(1,0,-0))
vertices[j].Pos.rotateXZBy(-90);
vertices[j].Pos += intToFloat(p + getPosRelative());
}
// Set material
video::SMaterial material;
material.setFlag(video::EMF_LIGHTING, false);
material.setFlag(video::EMF_BACK_FACE_CULLING, false);
material.setFlag(video::EMF_BILINEAR_FILTER, false);
material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
material.setTexture(0,
g_irrlicht->getTexture(porting::getDataPath("water.png").c_str()));
u16 indices[] = {0,1,2,2,3,0};
// Add to mesh collector
collector.append(material, vertices, 4, indices, 6);
}
/*
Generate top side, if appropriate
*/
if(top_is_water == false)
{
video::S3DVertex vertices[4] =
{
video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
};
for(s32 i=0; i<4; i++)
{
//vertices[i].Pos.Y += water_level;
//vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
vertices[i].Pos.Y += corner_levels[i];
vertices[i].Pos += intToFloat(p + getPosRelative());
}
// Set material
video::SMaterial material;
material.setFlag(video::EMF_LIGHTING, false);
material.setFlag(video::EMF_BACK_FACE_CULLING, false);
material.setFlag(video::EMF_BILINEAR_FILTER, false);
material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
material.setTexture(0,
g_irrlicht->getTexture(porting::getDataPath("water.png").c_str()));
u16 indices[] = {0,1,2,2,3,0};
// Add to mesh collector
collector.append(material, vertices, 4, indices, 6);
}
} }
} }
/*
Add stuff from collector to mesh
*/
collector.fillMesh(mesh_new);
/* /*
Do some stuff to the mesh Do some stuff to the mesh
*/ */
@ -792,6 +1029,14 @@ void MapBlock::updateMesh(u32 daynight_ratio)
mesh_new = NULL; mesh_new = NULL;
} }
// Use VBO for mesh (this just would set this for ever buffer)
// This will lead to infinite memory usage because or irrlicht.
//mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
/*std::cout<<"MapBlock has "<<fastfaces_new.size()<<" faces "
<<"and uses "<<mesh_new->getMeshBufferCount()
<<" materials (meshbuffers)"<<std::endl;*/
/* /*
Replace the mesh Replace the mesh
*/ */
@ -872,7 +1117,8 @@ void MapBlock::updateMesh(u32 daynight_ratio)
air is left in block. air is left in block.
*/ */
bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources, bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
bool remove_light, bool *black_air_left) bool remove_light, bool *black_air_left,
bool grow_grass)
{ {
// Whether the sunlight at the top of the bottom block is valid // Whether the sunlight at the top of the bottom block is valid
bool block_below_is_valid = true; bool block_below_is_valid = true;
@ -962,10 +1208,23 @@ bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
} }
else if(n.light_propagates() == false) else if(n.light_propagates() == false)
{ {
// Turn mud into grass if(grow_grass)
if(n.d == CONTENT_MUD && current_light == LIGHT_SUN)
{ {
n.d = CONTENT_GRASS; bool upper_is_air = false;
try
{
if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
upper_is_air = true;
}
catch(InvalidPositionException &e)
{
}
// Turn mud into grass
if(upper_is_air && n.d == CONTENT_MUD
&& current_light == LIGHT_SUN)
{
n.d = CONTENT_GRASS;
}
} }
// A solid object is on the way. // A solid object is on the way.

@ -315,7 +315,8 @@ public:
// See comments in mapblock.cpp // See comments in mapblock.cpp
bool propagateSunlight(core::map<v3s16, bool> & light_sources, bool propagateSunlight(core::map<v3s16, bool> & light_sources,
bool remove_light=false, bool *black_air_left=NULL); bool remove_light=false, bool *black_air_left=NULL,
bool grow_grass=false);
// Copies data to VoxelManipulator to getPosRelative() // Copies data to VoxelManipulator to getPosRelative()
void copyTo(VoxelManipulator &dst); void copyTo(VoxelManipulator &dst);

@ -35,7 +35,8 @@ u16 g_content_tiles[USEFUL_CONTENT_COUNT][6] =
{ {
{TILE_STONE,TILE_STONE,TILE_STONE,TILE_STONE,TILE_STONE,TILE_STONE}, {TILE_STONE,TILE_STONE,TILE_STONE,TILE_STONE,TILE_STONE,TILE_STONE},
{TILE_GRASS,TILE_MUD,TILE_MUD_WITH_GRASS,TILE_MUD_WITH_GRASS,TILE_MUD_WITH_GRASS,TILE_MUD_WITH_GRASS}, {TILE_GRASS,TILE_MUD,TILE_MUD_WITH_GRASS,TILE_MUD_WITH_GRASS,TILE_MUD_WITH_GRASS,TILE_MUD_WITH_GRASS},
{TILE_WATER,TILE_WATER,TILE_WATER,TILE_WATER,TILE_WATER,TILE_WATER}, //{TILE_WATER,TILE_WATER,TILE_WATER,TILE_WATER,TILE_WATER,TILE_WATER},
{TILE_NONE,TILE_NONE,TILE_NONE,TILE_NONE,TILE_NONE,TILE_NONE},
{TILE_NONE,TILE_NONE,TILE_NONE,TILE_NONE,TILE_NONE,TILE_NONE}, {TILE_NONE,TILE_NONE,TILE_NONE,TILE_NONE,TILE_NONE,TILE_NONE},
{TILE_TREE_TOP,TILE_TREE_TOP,TILE_TREE,TILE_TREE,TILE_TREE,TILE_TREE}, {TILE_TREE_TOP,TILE_TREE_TOP,TILE_TREE,TILE_TREE,TILE_TREE,TILE_TREE},
{TILE_LEAVES,TILE_LEAVES,TILE_LEAVES,TILE_LEAVES,TILE_LEAVES,TILE_LEAVES}, {TILE_LEAVES,TILE_LEAVES,TILE_LEAVES,TILE_LEAVES,TILE_LEAVES,TILE_LEAVES},

@ -108,7 +108,7 @@ inline bool sunlight_propagates_content(u8 m)
inline u8 content_solidness(u8 m) inline u8 content_solidness(u8 m)
{ {
// As of now, every pseudo node like torches are added to this // As of now, every pseudo node like torches are added to this
if(m == CONTENT_AIR || m == CONTENT_TORCH) if(m == CONTENT_AIR || m == CONTENT_TORCH || m == CONTENT_WATER)
return 0; return 0;
if(m == CONTENT_WATER || m == CONTENT_WATERSOURCE) if(m == CONTENT_WATER || m == CONTENT_WATERSOURCE)
return 1; return 1;
@ -121,12 +121,30 @@ inline bool content_walkable(u8 m)
return (m != CONTENT_AIR && m != CONTENT_WATER && m != CONTENT_WATERSOURCE && m != CONTENT_TORCH); return (m != CONTENT_AIR && m != CONTENT_WATER && m != CONTENT_WATERSOURCE && m != CONTENT_TORCH);
} }
// A liquid resists fast movement
inline bool content_liquid(u8 m) inline bool content_liquid(u8 m)
{ {
return (m == CONTENT_WATER || m == CONTENT_WATERSOURCE); return (m == CONTENT_WATER || m == CONTENT_WATERSOURCE);
} }
inline bool content_flowing_liquid(u8 m)
{
return (m == CONTENT_WATER);
}
inline bool content_liquid_source(u8 m)
{
return (m == CONTENT_WATERSOURCE);
}
// CONTENT_WATER || CONTENT_WATERSOURCE -> CONTENT_WATER
// CONTENT_LAVA || CONTENT_LAVASOURCE -> CONTENT_LAVA
inline u8 make_liquid_flowing(u8 m)
{
if(m == CONTENT_WATER || m == CONTENT_WATERSOURCE)
return CONTENT_WATER;
assert(0);
}
// Pointable contents can be pointed to in the map // Pointable contents can be pointed to in the map
inline bool content_pointable(u8 m) inline bool content_pointable(u8 m)
{ {
@ -349,6 +367,8 @@ struct MapNode
union union
{ {
u8 param2;
/* /*
Pressure for liquids Pressure for liquids
*/ */

@ -971,7 +971,8 @@ Server::Server(
m_time_of_day_send_timer(0), m_time_of_day_send_timer(0),
m_uptime(0) m_uptime(0)
{ {
m_flowwater_timer = 0.0; //m_flowwater_timer = 0.0;
m_liquid_transform_timer = 0.0;
m_print_info_timer = 0.0; m_print_info_timer = 0.0;
m_objectdata_timer = 0.0; m_objectdata_timer = 0.0;
m_emergethread_trigger_timer = 0.0; m_emergethread_trigger_timer = 0.0;
@ -1140,9 +1141,54 @@ void Server::AsyncRunStep()
/* /*
Do background stuff Do background stuff
*/ */
/*
Transform liquids
*/
m_liquid_transform_timer += dtime;
if(m_liquid_transform_timer >= 1.00)
{ {
//m_env.getMap(). m_liquid_transform_timer -= 1.00;
JMutexAutoLock lock(m_env_mutex);
core::map<v3s16, MapBlock*> modified_blocks;
m_env.getMap().transformLiquids(modified_blocks);
#if 0
/*
Update lighting
*/
core::map<v3s16, MapBlock*> lighting_modified_blocks;
ServerMap &map = ((ServerMap&)m_env.getMap());
map.updateLighting(modified_blocks, lighting_modified_blocks);
// Add blocks modified by lighting to modified_blocks
for(core::map<v3s16, MapBlock*>::Iterator
i = lighting_modified_blocks.getIterator();
i.atEnd() == false; i++)
{
MapBlock *block = i.getNode()->getValue();
modified_blocks.insert(block->getPos(), block);
}
#endif
/*
Set the modified blocks unsent for all the clients
*/
JMutexAutoLock lock2(m_con_mutex);
for(core::map<u16, RemoteClient*>::Iterator
i = m_clients.getIterator();
i.atEnd() == false; i++)
{
RemoteClient *client = i.getNode()->getValue();
if(modified_blocks.size() > 0)
{
// Remove block from sent history
client->SetBlocksNotSent(modified_blocks);
}
}
} }
#if 0 #if 0

@ -457,7 +457,8 @@ private:
void handlePeerChange(PeerChange &c); void handlePeerChange(PeerChange &c);
void handlePeerChanges(); void handlePeerChanges();
float m_flowwater_timer; //float m_flowwater_timer;
float m_liquid_transform_timer;
float m_print_info_timer; float m_print_info_timer;
float m_objectdata_timer; float m_objectdata_timer;
float m_emergethread_trigger_timer; float m_emergethread_trigger_timer;

@ -1608,7 +1608,7 @@ public:
return true; return true;
} }
void pop_front() Value pop_front()
{ {
typename core::list<Value>::Iterator i = m_list.begin(); typename core::list<Value>::Iterator i = m_list.begin();
Value value = *i; Value value = *i;
@ -1617,6 +1617,12 @@ public:
return value; return value;
} }
u32 size()
{
assert(m_list.size() == m_map.size());
return m_list.size();
}
private: private:
core::map<Value, u8> m_map; core::map<Value, u8> m_map;
core::list<Value> m_list; core::list<Value> m_list;