mapgen stuff

This commit is contained in:
Perttu Ahola 2011-02-05 14:55:16 +02:00
parent 7f2aa30bf2
commit ea6740e900
17 changed files with 239 additions and 3396 deletions

@ -58,7 +58,6 @@ set(common_SRCS
socket.cpp socket.cpp
mapblock.cpp mapblock.cpp
mapsector.cpp mapsector.cpp
heightmap.cpp
map.cpp map.cpp
player.cpp player.cpp
utility.cpp utility.cpp

@ -97,12 +97,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
/* /*
This is good to be a bit different than 0 so that water level This is good to be a bit different than 0 so that water level
is not between to MapBlocks is not between two MapBlocks
*/ */
#define WATER_LEVEL 3 #define WATER_LEVEL 1
// 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
// Some stuff needed by old code moved to here from heightmap.h
#define GROUNDHEIGHT_NOTFOUND_SETVALUE (-10e6)
#define GROUNDHEIGHT_VALID_MINVALUE ( -9e6)
#endif #endif

@ -115,6 +115,7 @@ void GUIPauseMenu::regenerateGui(v2u32 screensize)
L"- Mouse left: dig blocks\n" L"- Mouse left: dig blocks\n"
L"- Mouse right: place blocks\n" L"- Mouse right: place blocks\n"
L"- Mouse wheel: select item\n" L"- Mouse wheel: select item\n"
L"- 0...9: select item\n"
L"- R: Toggle viewing all loaded chunks\n" L"- R: Toggle viewing all loaded chunks\n"
L"- I: Inventory menu\n" L"- I: Inventory menu\n"
L"- ESC: This menu\n" L"- ESC: This menu\n"

File diff suppressed because it is too large Load Diff

@ -1,572 +0,0 @@
/*
Minetest-c55
Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef HEIGHTMAP_HEADER
#define HEIGHTMAP_HEADER
#include <iostream>
#include <time.h>
#include <sstream>
#include "debug.h"
#include "common_irrlicht.h"
#include "exceptions.h"
#include "utility.h"
#include "serialization.h"
#define GROUNDHEIGHT_NOTFOUND_SETVALUE (-10e6)
#define GROUNDHEIGHT_VALID_MINVALUE ( -9e6)
class Heightmappish
{
public:
virtual f32 getGroundHeight(v2s16 p, bool generate=true) = 0;
virtual void setGroundHeight(v2s16 p, f32 y, bool generate=true) = 0;
v2f32 getSlope(v2s16 p)
{
f32 y0 = getGroundHeight(p, false);
v2s16 dirs[] = {
v2s16(1,0),
v2s16(0,1),
};
v2f32 fdirs[] = {
v2f32(1,0),
v2f32(0,1),
};
v2f32 slopevector(0.0, 0.0);
for(u16 i=0; i<2; i++){
f32 y1 = 0.0;
f32 y2 = 0.0;
f32 count = 0.0;
v2s16 p1 = p - dirs[i];
y1 = getGroundHeight(p1, false);
if(y1 > GROUNDHEIGHT_VALID_MINVALUE){
y1 -= y0;
count += 1.0;
}
else
y1 = 0;
v2s16 p2 = p + dirs[i];
y2 = getGroundHeight(p2, false);
if(y2 > GROUNDHEIGHT_VALID_MINVALUE){
y2 -= y0;
count += 1.0;
}
else
y2 = 0;
if(count < 0.001)
return v2f32(0.0, 0.0);
/*
If y2 is higher than y1, slope is positive
*/
f32 slope = (y2 - y1)/count;
slopevector += fdirs[i] * slope;
}
return slopevector;
}
};
// TODO: Get rid of this dummy wrapper
class Heightmap : public Heightmappish /*, public ReferenceCounted*/
{
};
class WrapperHeightmap : public Heightmap
{
Heightmappish *m_target;
public:
WrapperHeightmap(Heightmappish *target):
m_target(target)
{
if(target == NULL)
throw NullPointerException();
}
f32 getGroundHeight(v2s16 p, bool generate=true)
{
return m_target->getGroundHeight(p, generate);
}
void setGroundHeight(v2s16 p, f32 y, bool generate=true)
{
m_target->setGroundHeight(p, y, generate);
}
};
/*
Base class that defines a generator that gives out values at
positions in 2-dimensional space.
Can be given to UnlimitedHeightmap to feed stuff.
These are always serialized as readable text ending in "\n"
*/
class ValueGenerator
{
public:
ValueGenerator(){}
virtual ~ValueGenerator(){}
static ValueGenerator* deSerialize(std::string line);
static ValueGenerator* deSerialize(std::istream &is)
{
std::string line;
std::getline(is, line, '\n');
return deSerialize(line);
}
void serializeBase(std::ostream &os)
{
os<<getName()<<" ";
}
// Virtual methods
virtual const char * getName() const = 0;
virtual f32 getValue(v2s16 p) = 0;
virtual void serialize(std::ostream &os) = 0;
};
class ConstantGenerator : public ValueGenerator
{
public:
f32 m_value;
ConstantGenerator(f32 value)
{
m_value = value;
}
const char * getName() const
{
return "constant";
}
f32 getValue(v2s16 p)
{
return m_value;
}
void serialize(std::ostream &os)
{
serializeBase(os);
std::ostringstream ss;
//ss.imbue(std::locale("C"));
ss<<m_value<<"\n";
os<<ss.str();
}
};
class LinearGenerator : public ValueGenerator
{
public:
f32 m_height;
v2f m_slope;
LinearGenerator(f32 height, v2f slope)
{
m_height = height;
m_slope = slope;
}
const char * getName() const
{
return "linear";
}
f32 getValue(v2s16 p)
{
return m_height + m_slope.X * p.X + m_slope.Y * p.Y;
}
void serialize(std::ostream &os)
{
serializeBase(os);
std::ostringstream ss;
//ss.imbue(std::locale("C"));
ss<<m_height<<" "<<m_slope.X<<" "<<m_slope.Y<<"\n";
os<<ss.str();
}
};
class PowerGenerator : public ValueGenerator
{
public:
f32 m_height;
v2f m_slope;
f32 m_power;
PowerGenerator(f32 height, v2f slope, f32 power)
{
m_height = height;
m_slope = slope;
m_power = power;
}
const char * getName() const
{
return "power";
}
f32 getValue(v2s16 p)
{
return m_height
+ m_slope.X * pow((f32)p.X, m_power)
+ m_slope.Y * pow((f32)p.Y, m_power);
}
void serialize(std::ostream &os)
{
serializeBase(os);
std::ostringstream ss;
//ss.imbue(std::locale("C"));
ss<<m_height<<" "
<<m_slope.X<<" "
<<m_slope.Y<<" "
<<m_power<<"\n";
os<<ss.str();
}
};
class FixedHeightmap : public Heightmap
{
// A meta-heightmap on which this heightmap is located
// (at m_pos_on_master * m_blocksize)
Heightmap * m_master;
// Position on master heightmap (in blocks)
v2s16 m_pos_on_master;
s32 m_blocksize; // This is (W-1) = (H-1)
// These are the actual size of the data
s32 W;
s32 H;
f32 *m_data;
public:
FixedHeightmap(Heightmap * master,
v2s16 pos_on_master, s32 blocksize):
m_master(master),
m_pos_on_master(pos_on_master),
m_blocksize(blocksize)
{
W = m_blocksize+1;
H = m_blocksize+1;
m_data = NULL;
m_data = new f32[(blocksize+1)*(blocksize+1)];
for(s32 i=0; i<(blocksize+1)*(blocksize+1); i++){
m_data[i] = GROUNDHEIGHT_NOTFOUND_SETVALUE;
}
}
~FixedHeightmap()
{
if(m_data)
delete[] m_data;
}
v2s16 getPosOnMaster()
{
return m_pos_on_master;
}
/*
TODO: BorderWrapper class or something to allow defining
borders that wrap to an another heightmap. The algorithm
should be allowed to edit stuff over the border and on
the border in that case, too.
This will allow non-square heightmaps, too. (probably)
*/
void print()
{
printf("FixedHeightmap::print(): size is %ix%i\n", W, H);
for(s32 y=0; y<H; y++){
for(s32 x=0; x<W; x++){
/*if(getSeeded(v2s16(x,y)))
printf("S");*/
f32 n = getGroundHeight(v2s16(x,y));
if(n < GROUNDHEIGHT_VALID_MINVALUE)
printf(" - ");
else
printf("% -5.1f ", getGroundHeight(v2s16(x,y)));
}
printf("\n");
}
}
bool overborder(v2s16 p)
{
return (p.X < 0 || p.X >= W || p.Y < 0 || p.Y >= H);
}
bool atborder(v2s16 p)
{
if(overborder(p))
return false;
return (p.X == 0 || p.X == W-1 || p.Y == 0 || p.Y == H-1);
}
void setGroundHeight(v2s16 p, f32 y, bool generate=false)
{
/*dstream<<"FixedHeightmap::setGroundHeight(("
<<p.X<<","<<p.Y
<<"), "<<y<<")"<<std::endl;*/
if(overborder(p))
throw InvalidPositionException();
m_data[p.Y*W + p.X] = y;
}
// Returns true on success, false on railure.
bool setGroundHeightParent(v2s16 p, f32 y, bool generate=false)
{
/*// Position on master
v2s16 blockpos_nodes = m_pos_on_master * m_blocksize;
v2s16 nodepos_master = blockpos_nodes + p;
dstream<<"FixedHeightmap::setGroundHeightParent(("
<<p.X<<","<<p.Y
<<"), "<<y<<"): nodepos_master=("
<<nodepos_master.X<<","
<<nodepos_master.Y<<")"<<std::endl;
m_master->setGroundHeight(nodepos_master, y, false);*/
// Try to set on master
bool master_got_it = false;
if(overborder(p) || atborder(p))
{
try{
// Position on master
v2s16 blockpos_nodes = m_pos_on_master * m_blocksize;
v2s16 nodepos_master = blockpos_nodes + p;
m_master->setGroundHeight(nodepos_master, y, false);
master_got_it = true;
}
catch(InvalidPositionException &e)
{
}
}
if(overborder(p))
return master_got_it;
setGroundHeight(p, y);
return true;
}
f32 getGroundHeight(v2s16 p, bool generate=false)
{
if(overborder(p))
return GROUNDHEIGHT_NOTFOUND_SETVALUE;
return m_data[p.Y*W + p.X];
}
f32 getGroundHeightParent(v2s16 p)
{
/*v2s16 blockpos_nodes = m_pos_on_master * m_blocksize;
return m_master->getGroundHeight(blockpos_nodes + p, false);*/
if(overborder(p) == false){
f32 h = getGroundHeight(p);
if(h > GROUNDHEIGHT_VALID_MINVALUE)
return h;
}
// Position on master
v2s16 blockpos_nodes = m_pos_on_master * m_blocksize;
f32 h = m_master->getGroundHeight(blockpos_nodes + p, false);
return h;
}
f32 avgNeighbours(v2s16 p, s16 d);
f32 avgDiagNeighbours(v2s16 p, s16 d);
void makeDiamond(
v2s16 center,
s16 a,
f32 randmax,
core::map<v2s16, bool> &next_squares);
void makeSquare(
v2s16 center,
s16 a,
f32 randmax,
core::map<v2s16, bool> &next_diamonds);
void DiamondSquare(f32 randmax, f32 randfactor);
/*
corners: [i]=XY: [0]=00, [1]=10, [2]=11, [3]=10
*/
void generateContinued(f32 randmax, f32 randfactor, f32 *corners);
static u32 serializedLength(u8 version, u16 blocksize);
u32 serializedLength(u8 version);
void serialize(u8 *dest, u8 version);
void deSerialize(u8 *source, u8 version);
/*static FixedHeightmap * deSerialize(u8 *source, u32 size,
u32 &usedsize, Heightmap *master, u8 version);*/
};
class OneChildHeightmap : public Heightmap
{
s16 m_blocksize;
public:
FixedHeightmap m_child;
OneChildHeightmap(s16 blocksize):
m_blocksize(blocksize),
m_child(this, v2s16(0,0), blocksize)
{
}
f32 getGroundHeight(v2s16 p, bool generate=true)
{
if(p.X < 0 || p.X > m_blocksize
|| p.Y < 0 || p.Y > m_blocksize)
return GROUNDHEIGHT_NOTFOUND_SETVALUE;
return m_child.getGroundHeight(p);
}
void setGroundHeight(v2s16 p, f32 y, bool generate=true)
{
//dstream<<"OneChildHeightmap::setGroundHeight()"<<std::endl;
if(p.X < 0 || p.X > m_blocksize
|| p.Y < 0 || p.Y > m_blocksize)
throw InvalidPositionException();
m_child.setGroundHeight(p, y);
}
};
/*
This is a dynamic container of an arbitrary number of heightmaps
at arbitrary positions.
It is able to redirect queries to the corresponding heightmaps and
it generates new heightmaps on-the-fly according to the relevant
parameters.
It doesn't have a master heightmap because it is meant to be used
as such itself.
Child heightmaps are spaced at m_blocksize distances, and are of
size (m_blocksize+1)*(m_blocksize+1)
This is used as the master heightmap of a Map object.
*/
class UnlimitedHeightmap: public Heightmap
{
private:
core::map<v2s16, FixedHeightmap*> m_heightmaps;
s16 m_blocksize;
// TODO: Remove ValueGenerators
/*ValueGenerator *m_randmax_generator;
ValueGenerator *m_randfactor_generator;
ValueGenerator *m_base_generator;*/
PointAttributeDatabase *m_padb;
public:
UnlimitedHeightmap(
s16 blocksize,
/*ValueGenerator *randmax_generator,
ValueGenerator *randfactor_generator,
ValueGenerator *base_generator,*/
PointAttributeDatabase *padb
):
m_blocksize(blocksize),
/*m_randmax_generator(randmax_generator),
m_randfactor_generator(randfactor_generator),
m_base_generator(base_generator),*/
m_padb(padb)
{
/*assert(m_randmax_generator != NULL);
assert(m_randfactor_generator != NULL);
assert(m_base_generator != NULL);*/
assert(m_padb);
}
~UnlimitedHeightmap()
{
core::map<v2s16, FixedHeightmap*>::Iterator i;
i = m_heightmaps.getIterator();
for(; i.atEnd() == false; i++)
{
delete i.getNode()->getValue();
}
/*delete m_randmax_generator;
delete m_randfactor_generator;
delete m_base_generator;*/
}
void print();
v2s16 getNodeHeightmapPos(v2s16 p)
{
return v2s16(
(p.X>=0 ? p.X : p.X-m_blocksize+1) / m_blocksize,
(p.Y>=0 ? p.Y : p.Y-m_blocksize+1) / m_blocksize);
}
// Can throw an InvalidPositionException
FixedHeightmap * getHeightmap(v2s16 p, bool generate=true);
f32 getGroundHeight(v2s16 p, bool generate=true);
void setGroundHeight(v2s16 p, f32 y, bool generate=true);
/*static UnlimitedHeightmap * deSerialize(u8 *source, u32 maxsize,
u32 &usedsize, u8 version);*/
//SharedBuffer<u8> serialize(u8 version);
void serialize(std::ostream &os, u8 version);
static UnlimitedHeightmap * deSerialize(std::istream &istr,
PointAttributeDatabase *padb);
};
#endif

@ -320,40 +320,42 @@ Doing now (most important at the top):
# maybe done # maybe done
* not done * not done
* Perlin noise stuff sucks in heightmap generation, fix it === Stuff to do before release
* Create perlin noise functions and use them to get natural randomness * Save map seed to a metafile (with version information)
in everything. No need for attributes or fractal terrain. - map/meta.txt, which should contain only plain text, something like this:
* Do something about AttributeDatabase/List being too slow seed = O7+BZT9Vk/iVYiBlZ2dsb6zemp4xdGVysJqYmNt2X+MQ+Kg1
- Remove it chunksize = 8
- map/chunks/
-
- Compressed bunch of data... um, actually no.
- Make a directory for every chunk instead, which contains
sectors and blocks
* Save chunk metadata on disk * Save chunk metadata on disk
* Remove all kinds of systems that are made redundant by the new map
generator
- Sector heightmaps? At least they should be made redundant.
- Sector objects
* Fix the strange mineral occurences
- Do they appear anymore?
* Make server find the spawning place from the real map data, not from * Make server find the spawning place from the real map data, not from
the heightmap the heightmap
- But the changing borders of chunk have to be avoided, because - But the changing borders of chunk have to be avoided, because
there is time to generate only one chunk. there is time to generate only one chunk.
* only_from_disk might not work anymore - check and fix it.
* Make the generator to run in background and not blocking block * Make the generator to run in background and not blocking block
placement and transfer placement and transfer
* only_from_disk might not work anymore - check and fix it.
=== Stuff to do after release
* Add some kind of erosion and other stuff that now is possible * Add some kind of erosion and other stuff that now is possible
* Make client to fetch stuff asynchronously * Make client to fetch stuff asynchronously
- Needs method SyncProcessData - Needs method SyncProcessData
* What is the problem with the server constantly saving one or a few * Fix the problem with the server constantly saving one or a few
blocks? List the first saved block, maybe it explains. blocks? List the first saved block, maybe it explains.
- Does it still do this? - It is probably caused by oscillating water
* Water doesn't start flowing after map generation like it should * Water doesn't start flowing after map generation like it should
- Are there still problems? - Are there still problems?
* Better water generation (spread it to underwater caverns) * Better water generation (spread it to underwater caverns but don't
fill dungeons that don't touch outside air)
* When generating a chunk and the neighboring chunk doesn't have mud * When generating a chunk and the neighboring chunk doesn't have mud
and stuff yet and the ground is fairly flat, the mud will flow to and stuff yet and the ground is fairly flat, the mud will flow to
the other chunk making nasty straight walls when the other chunk the other chunk making nasty straight walls when the other chunk
is generated. Fix it. is generated. Fix it.
* Save map seed to a metafile (with version information) * Make a small history check to transformLiquids to detect and log
- Remove master heightmap continuous oscillations, in such detail that they can be fixed.
====================================================================== ======================================================================
@ -666,7 +668,7 @@ public:
} }
// Material selection // Material selection
if(event.KeyInput.Key == irr::KEY_KEY_F) /*if(event.KeyInput.Key == irr::KEY_KEY_F)
{ {
if(g_selected_item < PLAYER_INVENTORY_SIZE-1) if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
g_selected_item++; g_selected_item++;
@ -674,6 +676,18 @@ public:
g_selected_item = 0; g_selected_item = 0;
dstream<<DTIME<<"Selected item: " dstream<<DTIME<<"Selected item: "
<<g_selected_item<<std::endl; <<g_selected_item<<std::endl;
}*/
if(event.KeyInput.Key >= irr::KEY_KEY_0
&& event.KeyInput.Key <= irr::KEY_KEY_9)
{
u16 s1 = event.KeyInput.Key - irr::KEY_KEY_0;
if(event.KeyInput.Key == irr::KEY_KEY_0)
s1 = 10;
if(s1 < PLAYER_INVENTORY_SIZE)
g_selected_item = s1-1;
dstream<<DTIME<<"Selected item: "
<<g_selected_item<<std::endl;
} }
// Viewing range selection // Viewing range selection
@ -1009,7 +1023,7 @@ public:
if(counter1 < 0.0) if(counter1 < 0.0)
{ {
counter1 = 0.1*Rand(1, 40); counter1 = 0.1*Rand(1, 40);
keydown[irr::KEY_KEY_2] = !keydown[irr::KEY_KEY_2]; keydown[irr::KEY_KEY_E] = !keydown[irr::KEY_KEY_E];
} }
} }
{ {
@ -1595,18 +1609,6 @@ int main(int argc, char *argv[])
run_tests(); run_tests();
} }
// Read map parameters from settings
HMParams hm_params;
/*hm_params.blocksize = g_settings.getU16("heightmap_blocksize");
hm_params.randmax = g_settings.get("height_randmax");
hm_params.randfactor = g_settings.get("height_randfactor");
hm_params.base = g_settings.get("height_base");*/
MapParams map_params;
map_params.plants_amount = g_settings.getFloat("plants_amount");
map_params.ravines_amount = g_settings.getFloat("ravines_amount");
/* /*
Some parameters Some parameters
*/ */
@ -1631,7 +1633,7 @@ int main(int argc, char *argv[])
DSTACK("Dedicated server branch"); DSTACK("Dedicated server branch");
// Create server // Create server
Server server(map_dir.c_str(), hm_params, map_params); Server server(map_dir.c_str());
server.start(port); server.start(port);
// Run server // Run server
@ -1946,7 +1948,7 @@ int main(int argc, char *argv[])
*/ */
SharedPtr<Server> server; SharedPtr<Server> server;
if(address == ""){ if(address == ""){
server = new Server(map_dir, hm_params, map_params); server = new Server(map_dir);
server->start(port); server->start(port);
} }
@ -2266,7 +2268,7 @@ int main(int argc, char *argv[])
g_input->isKeyDown(irr::KEY_KEY_A), g_input->isKeyDown(irr::KEY_KEY_A),
g_input->isKeyDown(irr::KEY_KEY_D), g_input->isKeyDown(irr::KEY_KEY_D),
g_input->isKeyDown(irr::KEY_SPACE), g_input->isKeyDown(irr::KEY_SPACE),
g_input->isKeyDown(irr::KEY_KEY_2), g_input->isKeyDown(irr::KEY_KEY_E),
camera_pitch, camera_pitch,
camera_yaw camera_yaw
); );

File diff suppressed because it is too large Load Diff

@ -34,7 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#endif #endif
#include "common_irrlicht.h" #include "common_irrlicht.h"
#include "heightmap.h" //#include "heightmap.h"
#include "mapnode.h" #include "mapnode.h"
#include "mapblock.h" #include "mapblock.h"
#include "mapsector.h" #include "mapsector.h"
@ -46,7 +46,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define MAPTYPE_SERVER 1 #define MAPTYPE_SERVER 1
#define MAPTYPE_CLIENT 2 #define MAPTYPE_CLIENT 2
class Map : public NodeContainer, public Heightmappish class Map : public NodeContainer
{ {
public: public:
@ -273,42 +273,12 @@ protected:
MapSector *m_sector_cache; MapSector *m_sector_cache;
v2s16 m_sector_cache_p; v2s16 m_sector_cache_p;
WrapperHeightmap m_hwrapper; //WrapperHeightmap m_hwrapper;
// Queued transforming water nodes // Queued transforming water nodes
UniqueQueue<v3s16> m_transforming_liquid; UniqueQueue<v3s16> m_transforming_liquid;
}; };
// Master heightmap parameters
struct HMParams
{
HMParams()
{
blocksize = 64;
randmax = "constant 70.0";
randfactor = "constant 0.6";
base = "linear 0 80 0";
}
s16 blocksize;
std::string randmax;
std::string randfactor;
std::string base;
};
// Map parameters
struct MapParams
{
MapParams()
{
plants_amount = 1.0;
ravines_amount = 1.0;
//max_objects_in_block = 30;
}
float plants_amount;
float ravines_amount;
//u16 max_objects_in_block;
};
/* /*
ServerMap ServerMap
@ -321,7 +291,7 @@ public:
/* /*
savedir: directory to which map data should be saved savedir: directory to which map data should be saved
*/ */
ServerMap(std::string savedir, HMParams hmp, MapParams mp); ServerMap(std::string savedir);
~ServerMap(); ~ServerMap();
s32 mapType() const s32 mapType() const
@ -505,6 +475,15 @@ public:
void save(bool only_changed); void save(bool only_changed);
void loadAll(); void loadAll();
// TODO
void saveMapMeta();
void loadMapMeta();
// TODO
void saveChunkMeta();
void loadChunkMeta();
// DEPRECATED
void saveMasterHeightmap(); void saveMasterHeightmap();
void loadMasterHeightmap(); void loadMasterHeightmap();
@ -512,6 +491,7 @@ public:
// This only saves sector-specific data such as the heightmap // This only saves sector-specific data such as the heightmap
// (no MapBlocks) // (no MapBlocks)
// DEPRECATED? Sectors have no metadata anymore.
void saveSectorMeta(ServerMapSector *sector); void saveSectorMeta(ServerMapSector *sector);
MapSector* loadSectorMeta(std::string dirname); MapSector* loadSectorMeta(std::string dirname);
@ -527,17 +507,13 @@ public:
void loadBlock(std::string sectordir, std::string blockfile, MapSector *sector); void loadBlock(std::string sectordir, std::string blockfile, MapSector *sector);
// Gets from master heightmap // Gets from master heightmap
// DEPRECATED?
void getSectorCorners(v2s16 p2d, s16 *corners); void getSectorCorners(v2s16 p2d, s16 *corners);
// For debug printing // For debug printing
virtual void PrintInfo(std::ostream &out); virtual void PrintInfo(std::ostream &out);
private: private:
// Generator parameters
UnlimitedHeightmap *m_heightmap;
MapParams m_params;
PointAttributeDatabase m_padb;
// Seed used for all kinds of randomness // Seed used for all kinds of randomness
u64 m_seed; u64 m_seed;
@ -664,8 +640,8 @@ private:
core::aabbox3d<f32> m_box; core::aabbox3d<f32> m_box;
// This is the master heightmap mesh // This is the master heightmap mesh
scene::SMesh *mesh; //scene::SMesh *mesh;
JMutex mesh_mutex; //JMutex mesh_mutex;
MapDrawControl &m_control; MapDrawControl &m_control;
}; };

@ -612,7 +612,7 @@ void MapBlock::updateMesh(u32 daynight_ratio)
bool new_style_water = g_settings.getBool("new_style_water"); bool new_style_water = g_settings.getBool("new_style_water");
float node_water_level = 1.0; float node_water_level = 1.0;
if(new_style_water) if(new_style_water)
node_water_level = 0.8; node_water_level = 0.9;
/* /*
We are including the faces of the trailing edges of the block. We are including the faces of the trailing edges of the block.

@ -176,168 +176,22 @@ void MapSector::getBlocks(core::list<MapBlock*> &dest)
ServerMapSector ServerMapSector
*/ */
ServerMapSector::ServerMapSector(NodeContainer *parent, v2s16 pos, u16 hm_split): ServerMapSector::ServerMapSector(NodeContainer *parent, v2s16 pos):
MapSector(parent, pos), MapSector(parent, pos)
m_hm_split(hm_split),
m_objects(NULL)
{ {
// hm_split has to be 1 or 2^x
assert(hm_split == 0 || hm_split == 1 || (hm_split & (hm_split-1)) == 0);
assert(hm_split * hm_split <= MAPSECTOR_FIXEDHEIGHTMAPS_MAXCOUNT);
for(u16 i=0; i<hm_split*hm_split; i++)
m_heightmaps[i] = NULL;
} }
ServerMapSector::~ServerMapSector() ServerMapSector::~ServerMapSector()
{ {
u16 hm_count = m_hm_split * m_hm_split;
// Write heightmaps
for(u16 i=0; i<hm_count; i++)
{
if(m_heightmaps[i])
delete m_heightmaps[i];
}
if(m_objects)
delete m_objects;
}
void ServerMapSector::setHeightmap(v2s16 hm_p, FixedHeightmap *hm)
{
assert(isInArea(hm_p, m_hm_split));
s16 i = hm_p.Y * m_hm_split + hm_p.X;
// Don't allow setting already set heightmaps as of now
assert(m_heightmaps[i] == NULL);
/*std::cout<<"MapSector::setHeightmap for sector "
<<"("<<m_pos.X<<","<<m_pos.Y<<"): "
<<"Setting heightmap "
<<"("<<hm_p.X<<","<<hm_p.Y<<")"
<<" which is i="<<i
<<" to pointer "<<(long long)hm
<<std::endl;*/
m_heightmaps[i] = hm;
differs_from_disk = true;
}
FixedHeightmap * ServerMapSector::getHeightmap(v2s16 hm_p)
{
assert(isInArea(hm_p, m_hm_split));
s16 i = hm_p.Y * m_hm_split + hm_p.X;
return m_heightmaps[i];
} }
f32 ServerMapSector::getGroundHeight(v2s16 p, bool generate) f32 ServerMapSector::getGroundHeight(v2s16 p, bool generate)
{ {
// If no heightmaps
if(m_hm_split == 0)
{
/*std::cout<<"Sector has no heightmap"
<<" while trying to get height at ("<<p.X<<","<<p.Y<<")"
<<" for sector ("<<m_pos.X<<","<<m_pos.Y<<")"
<<std::endl;*/
return GROUNDHEIGHT_NOTFOUND_SETVALUE; return GROUNDHEIGHT_NOTFOUND_SETVALUE;
}
// Side length of heightmap
s16 hm_d = MAP_BLOCKSIZE / m_hm_split;
// Position of selected heightmap
v2s16 hm_p = getContainerPos(p, hm_d);
if(isInArea(hm_p, m_hm_split) == false)
{
/*std::cout<<"Sector has no heightmap ("<<hm_p.X<<","<<hm_p.Y<<")"
<<" while trying to get height at ("<<p.X<<","<<p.Y<<")"
<<" for sector ("<<m_pos.X<<","<<m_pos.Y<<")"
<<std::endl;*/
return GROUNDHEIGHT_NOTFOUND_SETVALUE;
}
// Selected heightmap
FixedHeightmap *hm = m_heightmaps[hm_p.Y * m_hm_split + hm_p.X];
if(hm == NULL)
{
/*std::cout<<"Sector heightmap ("<<hm_p.X<<","<<hm_p.Y<<")"
" is NULL"
<<" while trying to get height at ("<<p.X<<","<<p.Y<<")"
<<" for sector ("<<m_pos.X<<","<<m_pos.Y<<")"
<<std::endl;*/
return GROUNDHEIGHT_NOTFOUND_SETVALUE;
}
// Position in selected heighmap
v2s16 p_in_hm = p - hm_p * hm_d;
if(isInArea(p_in_hm, hm_d+1) == false)
{
/*std::cout<<"Position ("<<p_in_hm.X<<","<<p_in_hm.Y<<")"
" not in sector heightmap area"
<<" while trying to get height at ("<<p.X<<","<<p.Y<<")"
<<" for sector ("<<m_pos.X<<","<<m_pos.Y<<")"
<<std::endl;*/
return GROUNDHEIGHT_NOTFOUND_SETVALUE;
}
f32 h = hm->getGroundHeight(p_in_hm);
/*if(h < GROUNDHEIGHT_VALID_MINVALUE)
{
std::cout<<"Sector heightmap ("<<hm_p.X<<","<<hm_p.Y<<")"
" returned invalid value"
<<" while trying to get height at ("<<p.X<<","<<p.Y<<")"
<<" which is ("<<p_in_hm.X<<","<<p_in_hm.Y<<") in heightmap"
<<" for sector ("<<m_pos.X<<","<<m_pos.Y<<")"
<<std::endl;
}*/
return h;
} }
void ServerMapSector::setGroundHeight(v2s16 p, f32 y, bool generate) void ServerMapSector::setGroundHeight(v2s16 p, f32 y, bool generate)
{ {
/*
NOTE:
This causes glitches because the sector cannot be actually
modified according to heightmap changes.
This is useful when generating continued sub-heightmaps
inside the sector.
*/
// If no heightmaps
if(m_hm_split == 0)
return;
// Side length of heightmap
s16 hm_d = MAP_BLOCKSIZE / m_hm_split;
// Position of selected heightmap
v2s16 hm_p = getContainerPos(p, hm_d);
if(isInArea(hm_p, m_hm_split) == false)
return;
// Selected heightmap
FixedHeightmap *hm = m_heightmaps[hm_p.Y * m_hm_split + hm_p.X];
if(hm == NULL)
return;
// Position in selected heighmap
v2s16 p_in_hm = p - hm_p * hm_d;
if(isInArea(p_in_hm, hm_d) == false)
return;
hm->setGroundHeight(p_in_hm, y);
differs_from_disk = true;
} }
void ServerMapSector::serialize(std::ostream &os, u8 version) void ServerMapSector::serialize(std::ostream &os, u8 version)
@ -351,118 +205,21 @@ void ServerMapSector::serialize(std::ostream &os, u8 version)
*/ */
// Server has both of these, no need to support not having them. // Server has both of these, no need to support not having them.
assert(m_objects != NULL); //assert(m_objects != NULL);
// Write version // Write version
os.write((char*)&version, 1); os.write((char*)&version, 1);
/* /*
Serialize heightmap(s) Add stuff here, if needed
*/ */
// Version with single heightmap
if(version <= 7)
{
u32 heightmap_size =
FixedHeightmap::serializedLength(version, MAP_BLOCKSIZE);
SharedBuffer<u8> data(heightmap_size);
m_heightmaps[0]->serialize(*data, version);
os.write((const char*)*data, heightmap_size);
if(version >= 5)
{
/*
Write objects
*/
u16 object_count;
if(m_objects->size() > 65535)
object_count = 65535;
else
object_count = m_objects->size();
u8 b[2];
writeU16(b, object_count);
os.write((char*)b, 2);
core::map<v3s16, u8>::Iterator i;
i = m_objects->getIterator();
for(; i.atEnd() == false; i++)
{
v3s16 p = i.getNode()->getKey();
u8 d = i.getNode()->getValue();
u8 b[7];
writeV3S16(&b[0], p);
b[6] = d;
os.write((char*)b, 7);
}
}
}
// Version with multiple heightmaps
else
{
u8 buf[2];
if(m_hm_split > 255)
throw SerializationError("Sector has too many heightmaps");
// Write heightmap split ratio
writeU8(buf, m_hm_split);
os.write((char*)buf, 1);
// If there are heightmaps, write them
if(m_hm_split != 0)
{
u16 hm_d = MAP_BLOCKSIZE / m_hm_split;
u32 hm_size = FixedHeightmap::serializedLength(version, hm_d);
SharedBuffer<u8> data(hm_size);
u16 hm_count = m_hm_split * m_hm_split;
// Write heightmaps
for(u16 i=0; i<hm_count; i++)
{
m_heightmaps[i]->serialize(*data, version);
os.write((const char*)*data, hm_size);
}
}
/*
Write objects
*/
u16 object_count;
if(m_objects->size() > 65535)
object_count = 65535;
else
object_count = m_objects->size();
u8 b[2];
writeU16(b, object_count);
os.write((char*)b, 2);
core::map<v3s16, u8>::Iterator i;
i = m_objects->getIterator();
for(; i.atEnd() == false; i++)
{
v3s16 p = i.getNode()->getKey();
u8 d = i.getNode()->getValue();
u8 b[7];
writeV3S16(&b[0], p);
b[6] = d;
os.write((char*)b, 7);
}
}
} }
ServerMapSector* ServerMapSector::deSerialize( ServerMapSector* ServerMapSector::deSerialize(
std::istream &is, std::istream &is,
NodeContainer *parent, NodeContainer *parent,
v2s16 p2d, v2s16 p2d,
Heightmap *master_hm,
core::map<v2s16, MapSector*> & sectors core::map<v2s16, MapSector*> & sectors
) )
{ {
@ -483,83 +240,9 @@ ServerMapSector* ServerMapSector::deSerialize(
throw VersionMismatchException("ERROR: MapSector format not supported"); throw VersionMismatchException("ERROR: MapSector format not supported");
/* /*
Read heightmap(s) Add necessary reading stuff here
*/ */
FixedHeightmap *hms[MAPSECTOR_FIXEDHEIGHTMAPS_MAXCOUNT];
u16 hm_split = 0;
// Version with a single heightmap
if(version <= 7)
{
hm_split = 1;
u32 hm_size =
FixedHeightmap::serializedLength(version, MAP_BLOCKSIZE);
SharedBuffer<u8> data(hm_size);
is.read((char*)*data, hm_size);
hms[0] = new FixedHeightmap(master_hm, p2d, MAP_BLOCKSIZE);
hms[0]->deSerialize(*data, version);
}
// Version with multiple heightmaps
else
{
u8 buf[2];
// Read split ratio
is.read((char*)buf, 1);
hm_split = readU8(buf);
// If there are heightmaps, read them
if(hm_split != 0)
{
u16 hm_count = hm_split * hm_split;
if(hm_count > MAPSECTOR_FIXEDHEIGHTMAPS_MAXCOUNT)
throw SerializationError("Sector has too many heightmaps");
u16 hm_d = MAP_BLOCKSIZE / hm_split;
u32 hm_size = FixedHeightmap::serializedLength(version, hm_d);
u16 i=0;
for(s16 y=0; y<hm_split; y++)
for(s16 x=0; x<hm_split; x++)
{
SharedBuffer<u8> data(hm_size);
is.read((char*)*data, hm_size);
hms[i] = new FixedHeightmap(master_hm, p2d+v2s16(x,y), hm_d);
hms[i]->deSerialize(*data, version);
i++;
}
}
}
/*
Read objects
*/
core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
if(version >= 5)
{
u8 b[2];
is.read((char*)b, 2);
u16 object_count = readU16(b);
for(u16 i=0; i<object_count; i++)
{
u8 b[7];
is.read((char*)b, 7);
v3s16 p = readV3S16(&b[0]);
u8 d = b[6];
objects->insert(p, d);
}
}
/* /*
Get or create sector Get or create sector
*/ */
@ -574,18 +257,13 @@ ServerMapSector* ServerMapSector::deSerialize(
"at the moment, because code hasn't been tested." "at the moment, because code hasn't been tested."
<<std::endl; <<std::endl;
//assert(0);
MapSector *sector = n->getValue(); MapSector *sector = n->getValue();
assert(sector->getId() == MAPSECTOR_SERVER); assert(sector->getId() == MAPSECTOR_SERVER);
return (ServerMapSector*)sector; return (ServerMapSector*)sector;
// NOTE: At least hm_split mismatch would have to be checked
//sector = n->getValue();
} }
else else
{ {
sector = new ServerMapSector(parent, p2d, hm_split); sector = new ServerMapSector(parent, p2d);
sectors.insert(p2d, sector); sectors.insert(p2d, sector);
} }
@ -593,26 +271,7 @@ ServerMapSector* ServerMapSector::deSerialize(
Set stuff in sector Set stuff in sector
*/ */
// Set heightmaps // Nothing here
sector->m_hm_split = hm_split;
u16 hm_count = hm_split * hm_split;
for(u16 i=0; i<hm_count; i++)
{
// Set (or change) heightmap
FixedHeightmap *oldhm = sector->m_heightmaps[i];
sector->m_heightmaps[i] = hms[i];
if(oldhm != NULL)
delete oldhm;
}
// Set (or change) objects
core::map<v3s16, u8> *oldfo = sector->m_objects;
sector->m_objects = objects;
if(oldfo)
delete oldfo;
return sector; return sector;
} }
@ -654,29 +313,21 @@ void ClientMapSector::deSerialize(std::istream &is)
if(!ser_ver_supported(version)) if(!ser_ver_supported(version))
throw VersionMismatchException("ERROR: MapSector format not supported"); throw VersionMismatchException("ERROR: MapSector format not supported");
if(version <= 7)
throw VersionMismatchException("ERROR: MapSector format not supported");
u8 buf[2]; u8 buf[2];
// Read corners // Dummy read corners
is.read((char*)buf, 2); is.read((char*)buf, 2);
s16 c0 = readU16(buf);
is.read((char*)buf, 2); is.read((char*)buf, 2);
s16 c1 = readU16(buf);
is.read((char*)buf, 2); is.read((char*)buf, 2);
s16 c2 = readU16(buf);
is.read((char*)buf, 2); is.read((char*)buf, 2);
s16 c3 = readU16(buf);
/* /*
Set stuff in sector Set stuff in sector
*/ */
m_corners[0] = c0; // Nothing here
m_corners[1] = c1;
m_corners[2] = c2;
m_corners[3] = c3;
} }
#endif // !SERVER #endif // !SERVER

@ -27,26 +27,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <jmutex.h> #include <jmutex.h>
#include "common_irrlicht.h" #include "common_irrlicht.h"
#include "mapblock.h" #include "mapblock.h"
#include "heightmap.h" //#include "heightmap.h"
#include "exceptions.h" #include "exceptions.h"
/* /*
This is an Y-wise stack of MapBlocks. This is an Y-wise stack of MapBlocks.
*/ */
#define SECTOR_OBJECT_TEST 0
#define SECTOR_OBJECT_TREE_1 1
#define SECTOR_OBJECT_BUSH_1 2
#define SECTOR_OBJECT_RAVINE 3
//#define MAPSECTOR_FIXEDHEIGHTMAPS_MAXCOUNT 4
#define MAPSECTOR_FIXEDHEIGHTMAPS_MAXCOUNT \
(SECTOR_HEIGHTMAP_SPLIT * SECTOR_HEIGHTMAP_SPLIT)
#define MAPSECTOR_SERVER 0 #define MAPSECTOR_SERVER 0
#define MAPSECTOR_CLIENT 1 #define MAPSECTOR_CLIENT 1
class MapSector: public NodeContainer, public Heightmappish class MapSector: public NodeContainer
{ {
public: public:
@ -198,6 +189,7 @@ public:
blockref->setNode(relpos, n); blockref->setNode(relpos, n);
} }
// DEPRECATED?
virtual f32 getGroundHeight(v2s16 p, bool generate=false) virtual f32 getGroundHeight(v2s16 p, bool generate=false)
{ {
return GROUNDHEIGHT_NOTFOUND_SETVALUE; return GROUNDHEIGHT_NOTFOUND_SETVALUE;
@ -245,7 +237,7 @@ protected:
class ServerMapSector : public MapSector class ServerMapSector : public MapSector
{ {
public: public:
ServerMapSector(NodeContainer *parent, v2s16 pos, u16 hm_split); ServerMapSector(NodeContainer *parent, v2s16 pos);
~ServerMapSector(); ~ServerMapSector();
u32 getId() const u32 getId() const
@ -253,36 +245,7 @@ public:
return MAPSECTOR_SERVER; return MAPSECTOR_SERVER;
} }
void setHeightmap(v2s16 hm_p, FixedHeightmap *hm); // DEPRECATED?
FixedHeightmap * getHeightmap(v2s16 hm_p);
void printHeightmaps()
{
for(s16 y=0; y<m_hm_split; y++)
for(s16 x=0; x<m_hm_split; x++)
{
std::cout<<"Sector "
<<"("<<m_pos.X<<","<<m_pos.Y<<")"
" heightmap "
"("<<x<<","<<y<<"):"
<<std::endl;
FixedHeightmap *hm = getHeightmap(v2s16(x,y));
hm->print();
}
}
void setObjects(core::map<v3s16, u8> *objects)
{
m_objects = objects;
differs_from_disk = true;
}
core::map<v3s16, u8> * getObjects()
{
differs_from_disk = true;
return m_objects;
}
f32 getGroundHeight(v2s16 p, bool generate=false); f32 getGroundHeight(v2s16 p, bool generate=false);
void setGroundHeight(v2s16 p, f32 y, bool generate=false); void setGroundHeight(v2s16 p, f32 y, bool generate=false);
@ -296,20 +259,10 @@ public:
std::istream &is, std::istream &is,
NodeContainer *parent, NodeContainer *parent,
v2s16 p2d, v2s16 p2d,
Heightmap *master_hm,
core::map<v2s16, MapSector*> & sectors core::map<v2s16, MapSector*> & sectors
); );
private: private:
// Heightmap(s) for the sector
FixedHeightmap *m_heightmaps[MAPSECTOR_FIXEDHEIGHTMAPS_MAXCOUNT];
// Sector is split in m_hm_split^2 heightmaps.
// Value of 0 means there is no heightmap.
u16 m_hm_split;
// These are removed when they are drawn to blocks.
// - Each is drawn when generating blocks; When the last one of
// the needed blocks is being generated.
core::map<v3s16, u8> *m_objects;
}; };
#ifndef SERVER #ifndef SERVER
@ -326,14 +279,14 @@ public:
void deSerialize(std::istream &is); void deSerialize(std::istream &is);
s16 getCorner(u16 i) /*s16 getCorner(u16 i)
{ {
return m_corners[i]; return m_corners[i];
} }*/
private: private:
// The ground height of the corners is stored in here // The ground height of the corners is stored in here
s16 m_corners[4]; //s16 m_corners[4];
}; };
#endif #endif

@ -1022,11 +1022,9 @@ u32 PIChecksum(core::list<PlayerInfo> &l)
*/ */
Server::Server( Server::Server(
std::string mapsavedir, std::string mapsavedir
HMParams hm_params,
MapParams map_params
): ):
m_env(new ServerMap(mapsavedir, hm_params, map_params), dout_server), m_env(new ServerMap(mapsavedir), dout_server),
m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this), m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
m_thread(this), m_thread(this),
m_emergethread(this), m_emergethread(this),
@ -1406,8 +1404,10 @@ void Server::AsyncRunStep()
} }
} }
// Trigger emergethread (it gets somehow gets to a /*
// non-triggered but bysy state sometimes) Trigger emergethread (it somehow gets to a non-triggered but
bysy state sometimes)
*/
{ {
float &counter = m_emergethread_trigger_timer; float &counter = m_emergethread_trigger_timer;
counter += dtime; counter += dtime;

@ -377,9 +377,7 @@ public:
NOTE: Every public method should be thread-safe NOTE: Every public method should be thread-safe
*/ */
Server( Server(
std::string mapsavedir, std::string mapsavedir
HMParams hm_params,
MapParams map_params
); );
~Server(); ~Server();
void start(unsigned short port); void start(unsigned short port);

@ -263,18 +263,6 @@ int main(int argc, char *argv[])
init_mapnode(&irrlicht); init_mapnode(&irrlicht);
init_mineral(&irrlicht); init_mineral(&irrlicht);
// Read map parameters from settings
HMParams hm_params;
/*hm_params.blocksize = g_settings.getU16("heightmap_blocksize");
hm_params.randmax = g_settings.get("height_randmax");
hm_params.randfactor = g_settings.get("height_randfactor");
hm_params.base = g_settings.get("height_base");*/
MapParams map_params;
map_params.plants_amount = g_settings.getFloat("plants_amount");
map_params.ravines_amount = g_settings.getFloat("ravines_amount");
/* /*
Check parameters Check parameters
*/ */
@ -316,7 +304,7 @@ int main(int argc, char *argv[])
map_dir = g_settings.get("map-dir"); map_dir = g_settings.get("map-dir");
// Create server // Create server
Server server(map_dir.c_str(), hm_params, map_params); Server server(map_dir.c_str());
server.start(port); server.start(port);
// Run server // Run server

@ -23,7 +23,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "map.h" #include "map.h"
#include "player.h" #include "player.h"
#include "main.h" #include "main.h"
#include "heightmap.h"
#include "socket.h" #include "socket.h"
#include "connection.h" #include "connection.h"
#include "utility.h" #include "utility.h"
@ -628,9 +627,7 @@ struct TestMapSector
parent.position_valid = false; parent.position_valid = false;
// Create one with no heightmaps // Create one with no heightmaps
ServerMapSector sector(&parent, v2s16(1,1), 0); ServerMapSector sector(&parent, v2s16(1,1));
//ConstantGenerator *dummyheightmap = new ConstantGenerator();
//sector->setHeightmap(dummyheightmap);
EXCEPTION_CHECK(InvalidPositionException, sector.getBlockNoCreate(0)); EXCEPTION_CHECK(InvalidPositionException, sector.getBlockNoCreate(0));
EXCEPTION_CHECK(InvalidPositionException, sector.getBlockNoCreate(1)); EXCEPTION_CHECK(InvalidPositionException, sector.getBlockNoCreate(1));
@ -654,134 +651,6 @@ struct TestMapSector
} }
}; };
struct TestHeightmap
{
void TestSingleFixed()
{
const s16 BS1 = 4;
OneChildHeightmap hm1(BS1);
// Test that it is filled with < GROUNDHEIGHT_VALID_MINVALUE
for(s16 y=0; y<=BS1; y++){
for(s16 x=0; x<=BS1; x++){
v2s16 p(x,y);
assert(hm1.m_child.getGroundHeight(p)
< GROUNDHEIGHT_VALID_MINVALUE);
}
}
hm1.m_child.setGroundHeight(v2s16(1,0), 2.0);
//hm1.m_child.print();
assert(fabs(hm1.getGroundHeight(v2s16(1,0))-2.0)<0.001);
hm1.setGroundHeight(v2s16(0,1), 3.0);
assert(fabs(hm1.m_child.getGroundHeight(v2s16(0,1))-3.0)<0.001);
// Fill with -1.0
for(s16 y=0; y<=BS1; y++){
for(s16 x=0; x<=BS1; x++){
v2s16 p(x,y);
hm1.m_child.setGroundHeight(p, -1.0);
}
}
f32 corners[] = {0.0, 0.0, 1.0, 1.0};
hm1.m_child.generateContinued(0.0, 0.0, corners);
hm1.m_child.print();
assert(fabs(hm1.m_child.getGroundHeight(v2s16(1,0))-0.2)<0.05);
assert(fabs(hm1.m_child.getGroundHeight(v2s16(4,3))-0.7)<0.05);
assert(fabs(hm1.m_child.getGroundHeight(v2s16(4,4))-1.0)<0.05);
}
void TestUnlimited()
{
//g_heightmap_debugprint = true;
const s16 BS1 = 4;
/*UnlimitedHeightmap hm1(BS1,
new ConstantGenerator(0.0),
new ConstantGenerator(0.0),
new ConstantGenerator(5.0));*/
PointAttributeDatabase padb;
UnlimitedHeightmap hm1(BS1, &padb);
// Go through it so it generates itself
for(s16 y=0; y<=BS1; y++){
for(s16 x=0; x<=BS1; x++){
v2s16 p(x,y);
hm1.getGroundHeight(p);
}
}
// Print it
dstream<<"UnlimitedHeightmap hm1:"<<std::endl;
hm1.print();
dstream<<"testing UnlimitedHeightmap set/get"<<std::endl;
v2s16 p1(0,3);
f32 v1(234.01);
// Get first heightmap and try setGroundHeight
FixedHeightmap * href = hm1.getHeightmap(v2s16(0,0));
href->setGroundHeight(p1, v1);
// Read from UnlimitedHeightmap
assert(fabs(hm1.getGroundHeight(p1)-v1)<0.001);
}
void Random()
{
dstream<<"Running random code (get a human to check this)"<<std::endl;
dstream<<"myrand() values: ";
for(u16 i=0; i<5; i++)
dstream<<(u16)myrand()<<" ";
dstream<<std::endl;
const s16 BS1 = 8;
/*UnlimitedHeightmap hm1(BS1,
new ConstantGenerator(10.0),
new ConstantGenerator(0.3),
new ConstantGenerator(0.0));*/
PointAttributeDatabase padb;
padb.getList("hm_baseheight")->addPoint(v2s16(-BS1,0), Attribute(0));
padb.getList("hm_randmax")->addPoint(v2s16(-BS1,0), Attribute(0));
padb.getList("hm_randfactor")->addPoint(v2s16(-BS1,0), Attribute(0.0));
padb.getList("hm_baseheight")->addPoint(v2s16(0,0), Attribute(-20));
padb.getList("hm_randmax")->addPoint(v2s16(0,0), Attribute(0));
padb.getList("hm_randfactor")->addPoint(v2s16(0,0), Attribute(0.5));
padb.getList("hm_baseheight")->addPoint(v2s16(BS1*2,BS1), Attribute(0));
padb.getList("hm_randmax")->addPoint(v2s16(BS1*2,BS1), Attribute(30));
padb.getList("hm_randfactor")->addPoint(v2s16(BS1*2,BS1), Attribute(0.63));
UnlimitedHeightmap hm1(BS1, &padb);
// Force hm1 to generate a some heightmap
hm1.getGroundHeight(v2s16(0,0));
hm1.getGroundHeight(v2s16(0,BS1));
/*hm1.getGroundHeight(v2s16(BS1,-1));
hm1.getGroundHeight(v2s16(BS1-1,-1));*/
hm1.print();
// Get the (0,0) and (1,0) heightmaps
/*FixedHeightmap * hr00 = hm1.getHeightmap(v2s16(0,0));
FixedHeightmap * hr01 = hm1.getHeightmap(v2s16(1,0));
f32 corners[] = {1.0, 1.0, 1.0, 1.0};
hr00->generateContinued(0.0, 0.0, corners);
hm1.print();*/
//assert(0);
}
void Run()
{
//srand(7); // Get constant random
srand(time(0)); // Get better random
TestSingleFixed();
TestUnlimited();
Random();
}
};
struct TestSocket struct TestSocket
{ {
void Run() void Run()
@ -1149,7 +1018,6 @@ void run_tests()
TEST(TestVoxelManipulator); TEST(TestVoxelManipulator);
TEST(TestMapBlock); TEST(TestMapBlock);
TEST(TestMapSector); TEST(TestMapSector);
TEST(TestHeightmap);
if(INTERNET_SIMULATOR == false){ if(INTERNET_SIMULATOR == false){
TEST(TestSocket); TEST(TestSocket);
dout_con<<"=== BEGIN RUNNING UNIT TESTS FOR CONNECTION ==="<<std::endl; dout_con<<"=== BEGIN RUNNING UNIT TESTS FOR CONNECTION ==="<<std::endl;

@ -144,236 +144,6 @@ void mysrand(unsigned seed)
next = seed; next = seed;
} }
/*
PointAttributeList
*/
// Float with distance
struct DFloat
{
float v;
u32 d;
};
float PointAttributeList::getInterpolatedFloat(v2s16 p)
{
const u32 near_wanted_count = 5;
// Last is nearest, first is farthest
core::list<DFloat> near_list;
for(core::list<PointWithAttr>::Iterator
i = m_points.begin();
i != m_points.end(); i++)
{
PointWithAttr &pwa = *i;
u32 d = pwa.p.getDistanceFrom(p);
DFloat df;
df.v = pwa.attr.getFloat();
df.d = d;
// If near list is empty, add directly and continue
if(near_list.size() == 0)
{
near_list.push_back(df);
continue;
}
// Get distance of farthest in near list
u32 near_d = 100000;
if(near_list.size() > 0)
{
core::list<DFloat>::Iterator i = near_list.begin();
near_d = i->d;
}
/*
If point is closer than the farthest in the near list or
there are not yet enough points on the list
*/
if(d < near_d || near_list.size() < near_wanted_count)
{
// Find the right place in the near list and put it there
// Go from farthest to near in the near list
core::list<DFloat>::Iterator i = near_list.begin();
for(; i != near_list.end(); i++)
{
// Stop when i is at the first nearer node
if(i->d < d)
break;
}
// Add df to before i
if(i == near_list.end())
near_list.push_back(df);
else
near_list.insert_before(i, df);
// Keep near list at right size
if(near_list.size() > near_wanted_count)
{
core::list<DFloat>::Iterator j = near_list.begin();
near_list.erase(j);
}
}
}
// Return if no values found
if(near_list.size() == 0)
return 0.0;
/*
20:58:29 < tejeez> joka pisteelle a += arvo / etäisyys^6; b += 1 / etäisyys^6; ja
lopuks sit otetaan a/b
*/
float a = 0;
float b = 0;
for(core::list<DFloat>::Iterator i = near_list.begin();
i != near_list.end(); i++)
{
if(i->d == 0)
return i->v;
//float dd = pow((float)i->d, 6);
float dd = pow((float)i->d, 5);
float v = i->v;
//dstream<<"dd="<<dd<<", v="<<v<<std::endl;
a += v / dd;
b += 1 / dd;
}
return a / b;
}
#if 0
float PointAttributeList::getInterpolatedFloat(v3s16 p)
{
const u32 near_wanted_count = 2;
const u32 nearest_wanted_count = 2;
// Last is near
core::list<DFloat> near;
for(core::list<PointWithAttr>::Iterator
i = m_points.begin();
i != m_points.end(); i++)
{
PointWithAttr &pwa = *i;
u32 d = pwa.p.getDistanceFrom(p);
DFloat df;
df.v = pwa.attr.getFloat();
df.d = d;
// If near list is empty, add directly and continue
if(near_list.size() == 0)
{
near_list.push_back(df);
continue;
}
// Get distance of farthest in near list
u32 near_d = 100000;
if(near_list.size() > 0)
{
core::list<DFloat>::Iterator i = near_list.begin();
near_d = i->d;
}
/*
If point is closer than the farthest in the near list or
there are not yet enough points on the list
*/
if(d < near_d || near_list.size() < near_wanted_count)
{
// Find the right place in the near list and put it there
// Go from farthest to near in the near list
core::list<DFloat>::Iterator i = near_list.begin();
for(; i != near_list.end(); i++)
{
// Stop when i is at the first nearer node
if(i->d < d)
break;
}
// Add df to before i
if(i == near_list.end())
near_list.push_back(df);
else
near_list.insert_before(i, df);
// Keep near list at right size
if(near_list.size() > near_wanted_count)
{
core::list<DFloat>::Iterator j = near_list.begin();
near_list.erase(j);
}
}
}
// Return if no values found
if(near_list.size() == 0)
return 0.0;
/*
Get nearest ones
*/
u32 nearest_count = nearest_wanted_count;
if(nearest_count > near_list.size())
nearest_count = near_list.size();
core::list<DFloat> nearest;
{
core::list<DFloat>::Iterator i = near_list.getLast();
for(u32 j=0; j<nearest_count; j++)
{
nearest.push_front(*i);
i--;
}
}
/*
TODO: Try this:
20:58:29 < tejeez> joka pisteelle a += arvo / etäisyys^6; b += 1 / etäisyys^6; ja
lopuks sit otetaan a/b
*/
/*
Get total distance to nearest points
*/
float nearest_d_sum = 0;
for(core::list<DFloat>::Iterator i = nearest.begin();
i != nearest.end(); i++)
{
nearest_d_sum += (float)i->d;
}
/*
Interpolate a value between the first ones
*/
dstream<<"nearest.size()="<<nearest.size()<<std::endl;
float interpolated = 0;
for(core::list<DFloat>::Iterator i = nearest.begin();
i != nearest.end(); i++)
{
float weight;
if(nearest_d_sum > 0.001)
weight = (float)i->d / nearest_d_sum;
else
weight = 1. / nearest.size();
/*dstream<<"i->d="<<i->d<<" nearest_d_sum="<<nearest_d_sum
<<" weight="<<weight<<std::endl;*/
interpolated += weight * i->v;
}
return interpolated;
}
#endif
/* /*
blockpos: position of block in block coordinates blockpos: position of block in block coordinates
camera_pos: position of camera in nodes camera_pos: position of camera in nodes

@ -1482,183 +1482,6 @@ inline int myrand_range(int min, int max)
return (myrand()%(max-min+1))+min; return (myrand()%(max-min+1))+min;
} }
/*
Some kind of a thing that stores attributes related to
coordinate points
*/
struct Attribute
{
Attribute()
{
}
Attribute(const std::string &value):
m_value(value)
{
}
Attribute(float value)
{
m_value = ftos(value);
}
void set(const std::string &value)
{
m_value = value;
}
std::string get()
{
return m_value;
}
bool getBool()
{
return is_yes(get());
}
float getFloat()
{
float f;
std::istringstream vis(get());
vis>>f;
return f;
}
u16 getU16()
{
return stoi(get(), 0, 65535);
}
s16 getS16()
{
return stoi(get(), -32768, 32767);
}
s32 getS32()
{
return stoi(get());
}
std::string m_value;
};
class PointAttributeList
{
struct PointWithAttr
{
v2s16 p;
Attribute attr;
};
public:
~PointAttributeList()
{
}
Attribute getNearAttr(v2s16 p)
{
core::list<PointWithAttr>::Iterator
nearest_i = m_points.end();
s16 nearest_d = 32767;
for(core::list<PointWithAttr>::Iterator
i = m_points.begin();
i != m_points.end(); i++)
{
PointWithAttr &pwa = *i;
s16 d = pwa.p.getDistanceFrom(p);
if(d < nearest_d)
{
nearest_i = i;
nearest_d = d;
}
}
if(nearest_i == m_points.end())
Attribute();
return nearest_i->attr;
}
Attribute getNearAttr(v3s16 p)
{
return getNearAttr(v2s16(p.X, p.Z));
}
bool empty()
{
return (m_points.size() == 0);
}
/*
Take all points in range, or at least the nearest point,
and interpolate the values as floats
*/
float getInterpolatedFloat(v2s16 p);
float getInterpolatedFloat(v3s16 p)
{
return getInterpolatedFloat(v2s16(p.X, p.Z));
}
void addPoint(v2s16 p, const Attribute &attr)
{
PointWithAttr pattr;
pattr.p = p;
pattr.attr = attr;
m_points.push_back(pattr);
}
void addPoint(v3s16 p, const Attribute &attr)
{
addPoint(v2s16(p.X, p.Z), attr);
}
private:
core::list<PointWithAttr> m_points;
};
/*
Basically just a wrapper to core::map<PointAttributeList*>
*/
class PointAttributeDatabase
{
public:
~PointAttributeDatabase()
{
for(core::map<std::string, PointAttributeList*>::Iterator
i = m_lists.getIterator();
i.atEnd() == false; i++)
{
delete i.getNode()->getValue();
}
}
PointAttributeList *getList(const std::string &name)
{
PointAttributeList *list = NULL;
core::map<std::string, PointAttributeList*>::Node *n;
n = m_lists.find(name);
if(n == NULL)
{
list = new PointAttributeList();
m_lists.insert(name, list);
}
else
{
list = n->getValue();
}
return list;
}
private:
core::map<std::string, PointAttributeList*> m_lists;
};
/* /*
Miscellaneous functions Miscellaneous functions
*/ */