mirror of
https://github.com/luanti-org/luanti.git
synced 2025-08-16 18:01:40 +00:00
Initial files
This commit is contained in:
commit
4e249fb3fb
71 changed files with 22664 additions and 0 deletions
556
src/heightmap.h
Normal file
556
src/heightmap.h
Normal file
|
@ -0,0 +1,556 @@
|
|||
/*
|
||||
(c) 2010 Perttu Ahola <celeron55@gmail.com>
|
||||
*/
|
||||
|
||||
#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;
|
||||
|
||||
ValueGenerator *m_randmax_generator;
|
||||
ValueGenerator *m_randfactor_generator;
|
||||
ValueGenerator *m_base_generator;
|
||||
|
||||
public:
|
||||
|
||||
UnlimitedHeightmap(
|
||||
s16 blocksize,
|
||||
ValueGenerator *randmax_generator,
|
||||
ValueGenerator *randfactor_generator,
|
||||
ValueGenerator *base_generator
|
||||
):
|
||||
m_blocksize(blocksize),
|
||||
m_randmax_generator(randmax_generator),
|
||||
m_randfactor_generator(randfactor_generator),
|
||||
m_base_generator(base_generator)
|
||||
{
|
||||
assert(m_randmax_generator != NULL);
|
||||
assert(m_randfactor_generator != NULL);
|
||||
assert(m_base_generator != NULL);
|
||||
}
|
||||
|
||||
~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 setParams(f32 randmax, f32 randfactor)
|
||||
{
|
||||
m_randmax = randmax;
|
||||
m_randfactor = randfactor;
|
||||
}*/
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue