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.
20 #ifndef HEIGHTMAP_HEADER
21 #define HEIGHTMAP_HEADER
28 #include "common_irrlicht.h"
29 #include "exceptions.h"
31 #include "serialization.h"
33 #define GROUNDHEIGHT_NOTFOUND_SETVALUE (-10e6)
34 #define GROUNDHEIGHT_VALID_MINVALUE ( -9e6)
39 virtual f32 getGroundHeight(v2s16 p, bool generate=true) = 0;
40 virtual void setGroundHeight(v2s16 p, f32 y, bool generate=true) = 0;
42 v2f32 getSlope(v2s16 p)
44 f32 y0 = getGroundHeight(p, false);
56 v2f32 slopevector(0.0, 0.0);
58 for(u16 i=0; i<2; i++){
63 v2s16 p1 = p - dirs[i];
64 y1 = getGroundHeight(p1, false);
65 if(y1 > GROUNDHEIGHT_VALID_MINVALUE){
72 v2s16 p2 = p + dirs[i];
73 y2 = getGroundHeight(p2, false);
74 if(y2 > GROUNDHEIGHT_VALID_MINVALUE){
82 return v2f32(0.0, 0.0);
85 If y2 is higher than y1, slope is positive
87 f32 slope = (y2 - y1)/count;
89 slopevector += fdirs[i] * slope;
97 // TODO: Get rid of this dummy wrapper
98 class Heightmap : public Heightmappish /*, public ReferenceCounted*/
102 class WrapperHeightmap : public Heightmap
104 Heightmappish *m_target;
107 WrapperHeightmap(Heightmappish *target):
111 throw NullPointerException();
114 f32 getGroundHeight(v2s16 p, bool generate=true)
116 return m_target->getGroundHeight(p, generate);
118 void setGroundHeight(v2s16 p, f32 y, bool generate=true)
120 m_target->setGroundHeight(p, y, generate);
125 Base class that defines a generator that gives out values at
126 positions in 2-dimensional space.
127 Can be given to UnlimitedHeightmap to feed stuff.
129 These are always serialized as readable text ending in "\n"
135 virtual ~ValueGenerator(){}
137 static ValueGenerator* deSerialize(std::string line);
139 static ValueGenerator* deSerialize(std::istream &is)
142 std::getline(is, line, '\n');
143 return deSerialize(line);
146 void serializeBase(std::ostream &os)
152 virtual const char * getName() const = 0;
153 virtual f32 getValue(v2s16 p) = 0;
154 virtual void serialize(std::ostream &os) = 0;
157 class ConstantGenerator : public ValueGenerator
162 ConstantGenerator(f32 value)
167 const char * getName() const
172 f32 getValue(v2s16 p)
177 void serialize(std::ostream &os)
181 std::ostringstream ss;
182 //ss.imbue(std::locale("C"));
190 class LinearGenerator : public ValueGenerator
196 LinearGenerator(f32 height, v2f slope)
202 const char * getName() const
207 f32 getValue(v2s16 p)
209 return m_height + m_slope.X * p.X + m_slope.Y * p.Y;
212 void serialize(std::ostream &os)
216 std::ostringstream ss;
217 //ss.imbue(std::locale("C"));
219 ss<<m_height<<" "<<m_slope.X<<" "<<m_slope.Y<<"\n";
225 class PowerGenerator : public ValueGenerator
232 PowerGenerator(f32 height, v2f slope, f32 power)
239 const char * getName() const
244 f32 getValue(v2s16 p)
247 + m_slope.X * pow((f32)p.X, m_power)
248 + m_slope.Y * pow((f32)p.Y, m_power);
251 void serialize(std::ostream &os)
255 std::ostringstream ss;
256 //ss.imbue(std::locale("C"));
267 class FixedHeightmap : public Heightmap
269 // A meta-heightmap on which this heightmap is located
270 // (at m_pos_on_master * m_blocksize)
271 Heightmap * m_master;
272 // Position on master heightmap (in blocks)
273 v2s16 m_pos_on_master;
274 s32 m_blocksize; // This is (W-1) = (H-1)
275 // These are the actual size of the data
282 FixedHeightmap(Heightmap * master,
283 v2s16 pos_on_master, s32 blocksize):
285 m_pos_on_master(pos_on_master),
286 m_blocksize(blocksize)
291 m_data = new f32[(blocksize+1)*(blocksize+1)];
293 for(s32 i=0; i<(blocksize+1)*(blocksize+1); i++){
294 m_data[i] = GROUNDHEIGHT_NOTFOUND_SETVALUE;
304 v2s16 getPosOnMaster()
306 return m_pos_on_master;
310 TODO: BorderWrapper class or something to allow defining
311 borders that wrap to an another heightmap. The algorithm
312 should be allowed to edit stuff over the border and on
313 the border in that case, too.
314 This will allow non-square heightmaps, too. (probably)
319 printf("FixedHeightmap::print(): size is %ix%i\n", W, H);
320 for(s32 y=0; y<H; y++){
321 for(s32 x=0; x<W; x++){
322 /*if(getSeeded(v2s16(x,y)))
324 f32 n = getGroundHeight(v2s16(x,y));
325 if(n < GROUNDHEIGHT_VALID_MINVALUE)
328 printf("% -5.1f ", getGroundHeight(v2s16(x,y)));
334 bool overborder(v2s16 p)
336 return (p.X < 0 || p.X >= W || p.Y < 0 || p.Y >= H);
339 bool atborder(v2s16 p)
343 return (p.X == 0 || p.X == W-1 || p.Y == 0 || p.Y == H-1);
346 void setGroundHeight(v2s16 p, f32 y, bool generate=false)
348 /*dstream<<"FixedHeightmap::setGroundHeight(("
350 <<"), "<<y<<")"<<std::endl;*/
352 throw InvalidPositionException();
353 m_data[p.Y*W + p.X] = y;
356 // Returns true on success, false on railure.
357 bool setGroundHeightParent(v2s16 p, f32 y, bool generate=false)
359 /*// Position on master
360 v2s16 blockpos_nodes = m_pos_on_master * m_blocksize;
361 v2s16 nodepos_master = blockpos_nodes + p;
362 dstream<<"FixedHeightmap::setGroundHeightParent(("
364 <<"), "<<y<<"): nodepos_master=("
365 <<nodepos_master.X<<","
366 <<nodepos_master.Y<<")"<<std::endl;
367 m_master->setGroundHeight(nodepos_master, y, false);*/
369 // Try to set on master
370 bool master_got_it = false;
371 if(overborder(p) || atborder(p))
374 // Position on master
375 v2s16 blockpos_nodes = m_pos_on_master * m_blocksize;
376 v2s16 nodepos_master = blockpos_nodes + p;
377 m_master->setGroundHeight(nodepos_master, y, false);
379 master_got_it = true;
381 catch(InvalidPositionException &e)
387 return master_got_it;
389 setGroundHeight(p, y);
394 f32 getGroundHeight(v2s16 p, bool generate=false)
397 return GROUNDHEIGHT_NOTFOUND_SETVALUE;
398 return m_data[p.Y*W + p.X];
401 f32 getGroundHeightParent(v2s16 p)
403 /*v2s16 blockpos_nodes = m_pos_on_master * m_blocksize;
404 return m_master->getGroundHeight(blockpos_nodes + p, false);*/
406 if(overborder(p) == false){
407 f32 h = getGroundHeight(p);
408 if(h > GROUNDHEIGHT_VALID_MINVALUE)
412 // Position on master
413 v2s16 blockpos_nodes = m_pos_on_master * m_blocksize;
414 f32 h = m_master->getGroundHeight(blockpos_nodes + p, false);
418 f32 avgNeighbours(v2s16 p, s16 d);
420 f32 avgDiagNeighbours(v2s16 p, s16 d);
426 core::map<v2s16, bool> &next_squares);
432 core::map<v2s16, bool> &next_diamonds);
434 void DiamondSquare(f32 randmax, f32 randfactor);
437 corners: [i]=XY: [0]=00, [1]=10, [2]=11, [3]=10
439 void generateContinued(f32 randmax, f32 randfactor, f32 *corners);
442 static u32 serializedLength(u8 version, u16 blocksize);
443 u32 serializedLength(u8 version);
444 void serialize(u8 *dest, u8 version);
445 void deSerialize(u8 *source, u8 version);
446 /*static FixedHeightmap * deSerialize(u8 *source, u32 size,
447 u32 &usedsize, Heightmap *master, u8 version);*/
450 class OneChildHeightmap : public Heightmap
456 FixedHeightmap m_child;
458 OneChildHeightmap(s16 blocksize):
459 m_blocksize(blocksize),
460 m_child(this, v2s16(0,0), blocksize)
464 f32 getGroundHeight(v2s16 p, bool generate=true)
466 if(p.X < 0 || p.X > m_blocksize
467 || p.Y < 0 || p.Y > m_blocksize)
468 return GROUNDHEIGHT_NOTFOUND_SETVALUE;
469 return m_child.getGroundHeight(p);
471 void setGroundHeight(v2s16 p, f32 y, bool generate=true)
473 //dstream<<"OneChildHeightmap::setGroundHeight()"<<std::endl;
474 if(p.X < 0 || p.X > m_blocksize
475 || p.Y < 0 || p.Y > m_blocksize)
476 throw InvalidPositionException();
477 m_child.setGroundHeight(p, y);
483 This is a dynamic container of an arbitrary number of heightmaps
484 at arbitrary positions.
486 It is able to redirect queries to the corresponding heightmaps and
487 it generates new heightmaps on-the-fly according to the relevant
490 It doesn't have a master heightmap because it is meant to be used
493 Child heightmaps are spaced at m_blocksize distances, and are of
494 size (m_blocksize+1)*(m_blocksize+1)
496 This is used as the master heightmap of a Map object.
498 class UnlimitedHeightmap: public Heightmap
502 core::map<v2s16, FixedHeightmap*> m_heightmaps;
505 ValueGenerator *m_randmax_generator;
506 ValueGenerator *m_randfactor_generator;
507 ValueGenerator *m_base_generator;
513 ValueGenerator *randmax_generator,
514 ValueGenerator *randfactor_generator,
515 ValueGenerator *base_generator
517 m_blocksize(blocksize),
518 m_randmax_generator(randmax_generator),
519 m_randfactor_generator(randfactor_generator),
520 m_base_generator(base_generator)
522 assert(m_randmax_generator != NULL);
523 assert(m_randfactor_generator != NULL);
524 assert(m_base_generator != NULL);
527 ~UnlimitedHeightmap()
529 core::map<v2s16, FixedHeightmap*>::Iterator i;
530 i = m_heightmaps.getIterator();
531 for(; i.atEnd() == false; i++)
533 delete i.getNode()->getValue();
536 delete m_randmax_generator;
537 delete m_randfactor_generator;
538 delete m_base_generator;
541 /*void setParams(f32 randmax, f32 randfactor)
544 m_randfactor = randfactor;
549 v2s16 getNodeHeightmapPos(v2s16 p)
552 (p.X>=0 ? p.X : p.X-m_blocksize+1) / m_blocksize,
553 (p.Y>=0 ? p.Y : p.Y-m_blocksize+1) / m_blocksize);
556 // Can throw an InvalidPositionException
557 FixedHeightmap * getHeightmap(v2s16 p, bool generate=true);
559 f32 getGroundHeight(v2s16 p, bool generate=true);
560 void setGroundHeight(v2s16 p, f32 y, bool generate=true);
562 /*static UnlimitedHeightmap * deSerialize(u8 *source, u32 maxsize,
563 u32 &usedsize, u8 version);*/
565 //SharedBuffer<u8> serialize(u8 version);
566 void serialize(std::ostream &os, u8 version);
567 static UnlimitedHeightmap * deSerialize(std::istream &istr);