3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 (c) 2010 Perttu Ahola <celeron55@gmail.com>
24 #ifndef HEIGHTMAP_HEADER
25 #define HEIGHTMAP_HEADER
32 #include "common_irrlicht.h"
33 #include "exceptions.h"
35 #include "serialization.h"
37 #define GROUNDHEIGHT_NOTFOUND_SETVALUE (-10e6)
38 #define GROUNDHEIGHT_VALID_MINVALUE ( -9e6)
43 virtual f32 getGroundHeight(v2s16 p, bool generate=true) = 0;
44 virtual void setGroundHeight(v2s16 p, f32 y, bool generate=true) = 0;
46 v2f32 getSlope(v2s16 p)
48 f32 y0 = getGroundHeight(p, false);
60 v2f32 slopevector(0.0, 0.0);
62 for(u16 i=0; i<2; i++){
67 v2s16 p1 = p - dirs[i];
68 y1 = getGroundHeight(p1, false);
69 if(y1 > GROUNDHEIGHT_VALID_MINVALUE){
76 v2s16 p2 = p + dirs[i];
77 y2 = getGroundHeight(p2, false);
78 if(y2 > GROUNDHEIGHT_VALID_MINVALUE){
86 return v2f32(0.0, 0.0);
89 If y2 is higher than y1, slope is positive
91 f32 slope = (y2 - y1)/count;
93 slopevector += fdirs[i] * slope;
101 // TODO: Get rid of this dummy wrapper
102 class Heightmap : public Heightmappish /*, public ReferenceCounted*/
106 class WrapperHeightmap : public Heightmap
108 Heightmappish *m_target;
111 WrapperHeightmap(Heightmappish *target):
115 throw NullPointerException();
118 f32 getGroundHeight(v2s16 p, bool generate=true)
120 return m_target->getGroundHeight(p, generate);
122 void setGroundHeight(v2s16 p, f32 y, bool generate=true)
124 m_target->setGroundHeight(p, y, generate);
129 Base class that defines a generator that gives out values at
130 positions in 2-dimensional space.
131 Can be given to UnlimitedHeightmap to feed stuff.
133 These are always serialized as readable text ending in "\n"
139 virtual ~ValueGenerator(){}
141 static ValueGenerator* deSerialize(std::string line);
143 static ValueGenerator* deSerialize(std::istream &is)
146 std::getline(is, line, '\n');
147 return deSerialize(line);
150 void serializeBase(std::ostream &os)
156 virtual const char * getName() const = 0;
157 virtual f32 getValue(v2s16 p) = 0;
158 virtual void serialize(std::ostream &os) = 0;
161 class ConstantGenerator : public ValueGenerator
166 ConstantGenerator(f32 value)
171 const char * getName() const
176 f32 getValue(v2s16 p)
181 void serialize(std::ostream &os)
185 std::ostringstream ss;
186 //ss.imbue(std::locale("C"));
194 class LinearGenerator : public ValueGenerator
200 LinearGenerator(f32 height, v2f slope)
206 const char * getName() const
211 f32 getValue(v2s16 p)
213 return m_height + m_slope.X * p.X + m_slope.Y * p.Y;
216 void serialize(std::ostream &os)
220 std::ostringstream ss;
221 //ss.imbue(std::locale("C"));
223 ss<<m_height<<" "<<m_slope.X<<" "<<m_slope.Y<<"\n";
229 class PowerGenerator : public ValueGenerator
236 PowerGenerator(f32 height, v2f slope, f32 power)
243 const char * getName() const
248 f32 getValue(v2s16 p)
251 + m_slope.X * pow((f32)p.X, m_power)
252 + m_slope.Y * pow((f32)p.Y, m_power);
255 void serialize(std::ostream &os)
259 std::ostringstream ss;
260 //ss.imbue(std::locale("C"));
271 class FixedHeightmap : public Heightmap
273 // A meta-heightmap on which this heightmap is located
274 // (at m_pos_on_master * m_blocksize)
275 Heightmap * m_master;
276 // Position on master heightmap (in blocks)
277 v2s16 m_pos_on_master;
278 s32 m_blocksize; // This is (W-1) = (H-1)
279 // These are the actual size of the data
286 FixedHeightmap(Heightmap * master,
287 v2s16 pos_on_master, s32 blocksize):
289 m_pos_on_master(pos_on_master),
290 m_blocksize(blocksize)
295 m_data = new f32[(blocksize+1)*(blocksize+1)];
297 for(s32 i=0; i<(blocksize+1)*(blocksize+1); i++){
298 m_data[i] = GROUNDHEIGHT_NOTFOUND_SETVALUE;
308 v2s16 getPosOnMaster()
310 return m_pos_on_master;
314 TODO: BorderWrapper class or something to allow defining
315 borders that wrap to an another heightmap. The algorithm
316 should be allowed to edit stuff over the border and on
317 the border in that case, too.
318 This will allow non-square heightmaps, too. (probably)
323 printf("FixedHeightmap::print(): size is %ix%i\n", W, H);
324 for(s32 y=0; y<H; y++){
325 for(s32 x=0; x<W; x++){
326 /*if(getSeeded(v2s16(x,y)))
328 f32 n = getGroundHeight(v2s16(x,y));
329 if(n < GROUNDHEIGHT_VALID_MINVALUE)
332 printf("% -5.1f ", getGroundHeight(v2s16(x,y)));
338 bool overborder(v2s16 p)
340 return (p.X < 0 || p.X >= W || p.Y < 0 || p.Y >= H);
343 bool atborder(v2s16 p)
347 return (p.X == 0 || p.X == W-1 || p.Y == 0 || p.Y == H-1);
350 void setGroundHeight(v2s16 p, f32 y, bool generate=false)
352 /*dstream<<"FixedHeightmap::setGroundHeight(("
354 <<"), "<<y<<")"<<std::endl;*/
356 throw InvalidPositionException();
357 m_data[p.Y*W + p.X] = y;
360 // Returns true on success, false on railure.
361 bool setGroundHeightParent(v2s16 p, f32 y, bool generate=false)
363 /*// Position on master
364 v2s16 blockpos_nodes = m_pos_on_master * m_blocksize;
365 v2s16 nodepos_master = blockpos_nodes + p;
366 dstream<<"FixedHeightmap::setGroundHeightParent(("
368 <<"), "<<y<<"): nodepos_master=("
369 <<nodepos_master.X<<","
370 <<nodepos_master.Y<<")"<<std::endl;
371 m_master->setGroundHeight(nodepos_master, y, false);*/
373 // Try to set on master
374 bool master_got_it = false;
375 if(overborder(p) || atborder(p))
378 // Position on master
379 v2s16 blockpos_nodes = m_pos_on_master * m_blocksize;
380 v2s16 nodepos_master = blockpos_nodes + p;
381 m_master->setGroundHeight(nodepos_master, y, false);
383 master_got_it = true;
385 catch(InvalidPositionException &e)
391 return master_got_it;
393 setGroundHeight(p, y);
398 f32 getGroundHeight(v2s16 p, bool generate=false)
401 return GROUNDHEIGHT_NOTFOUND_SETVALUE;
402 return m_data[p.Y*W + p.X];
405 f32 getGroundHeightParent(v2s16 p)
407 /*v2s16 blockpos_nodes = m_pos_on_master * m_blocksize;
408 return m_master->getGroundHeight(blockpos_nodes + p, false);*/
410 if(overborder(p) == false){
411 f32 h = getGroundHeight(p);
412 if(h > GROUNDHEIGHT_VALID_MINVALUE)
416 // Position on master
417 v2s16 blockpos_nodes = m_pos_on_master * m_blocksize;
418 f32 h = m_master->getGroundHeight(blockpos_nodes + p, false);
422 f32 avgNeighbours(v2s16 p, s16 d);
424 f32 avgDiagNeighbours(v2s16 p, s16 d);
430 core::map<v2s16, bool> &next_squares);
436 core::map<v2s16, bool> &next_diamonds);
438 void DiamondSquare(f32 randmax, f32 randfactor);
441 corners: [i]=XY: [0]=00, [1]=10, [2]=11, [3]=10
443 void generateContinued(f32 randmax, f32 randfactor, f32 *corners);
446 static u32 serializedLength(u8 version, u16 blocksize);
447 u32 serializedLength(u8 version);
448 void serialize(u8 *dest, u8 version);
449 void deSerialize(u8 *source, u8 version);
450 /*static FixedHeightmap * deSerialize(u8 *source, u32 size,
451 u32 &usedsize, Heightmap *master, u8 version);*/
454 class OneChildHeightmap : public Heightmap
460 FixedHeightmap m_child;
462 OneChildHeightmap(s16 blocksize):
463 m_blocksize(blocksize),
464 m_child(this, v2s16(0,0), blocksize)
468 f32 getGroundHeight(v2s16 p, bool generate=true)
470 if(p.X < 0 || p.X > m_blocksize
471 || p.Y < 0 || p.Y > m_blocksize)
472 return GROUNDHEIGHT_NOTFOUND_SETVALUE;
473 return m_child.getGroundHeight(p);
475 void setGroundHeight(v2s16 p, f32 y, bool generate=true)
477 //dstream<<"OneChildHeightmap::setGroundHeight()"<<std::endl;
478 if(p.X < 0 || p.X > m_blocksize
479 || p.Y < 0 || p.Y > m_blocksize)
480 throw InvalidPositionException();
481 m_child.setGroundHeight(p, y);
487 This is a dynamic container of an arbitrary number of heightmaps
488 at arbitrary positions.
490 It is able to redirect queries to the corresponding heightmaps and
491 it generates new heightmaps on-the-fly according to the relevant
494 It doesn't have a master heightmap because it is meant to be used
497 Child heightmaps are spaced at m_blocksize distances, and are of
498 size (m_blocksize+1)*(m_blocksize+1)
500 This is used as the master heightmap of a Map object.
502 class UnlimitedHeightmap: public Heightmap
506 core::map<v2s16, FixedHeightmap*> m_heightmaps;
509 ValueGenerator *m_randmax_generator;
510 ValueGenerator *m_randfactor_generator;
511 ValueGenerator *m_base_generator;
517 ValueGenerator *randmax_generator,
518 ValueGenerator *randfactor_generator,
519 ValueGenerator *base_generator
521 m_blocksize(blocksize),
522 m_randmax_generator(randmax_generator),
523 m_randfactor_generator(randfactor_generator),
524 m_base_generator(base_generator)
526 assert(m_randmax_generator != NULL);
527 assert(m_randfactor_generator != NULL);
528 assert(m_base_generator != NULL);
531 ~UnlimitedHeightmap()
533 core::map<v2s16, FixedHeightmap*>::Iterator i;
534 i = m_heightmaps.getIterator();
535 for(; i.atEnd() == false; i++)
537 delete i.getNode()->getValue();
540 delete m_randmax_generator;
541 delete m_randfactor_generator;
542 delete m_base_generator;
545 /*void setParams(f32 randmax, f32 randfactor)
548 m_randfactor = randfactor;
553 v2s16 getNodeHeightmapPos(v2s16 p)
556 (p.X>=0 ? p.X : p.X-m_blocksize+1) / m_blocksize,
557 (p.Y>=0 ? p.Y : p.Y-m_blocksize+1) / m_blocksize);
560 // Can throw an InvalidPositionException
561 FixedHeightmap * getHeightmap(v2s16 p, bool generate=true);
563 f32 getGroundHeight(v2s16 p, bool generate=true);
564 void setGroundHeight(v2s16 p, f32 y, bool generate=true);
566 /*static UnlimitedHeightmap * deSerialize(u8 *source, u32 maxsize,
567 u32 &usedsize, u8 version);*/
569 //SharedBuffer<u8> serialize(u8 version);
570 void serialize(std::ostream &os, u8 version);
571 static UnlimitedHeightmap * deSerialize(std::istream &istr);