From: Chong K. X. <des...@us...> - 2005-04-14 08:31:25
|
Update of /cvsroot/copter/tools/terraingen In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv12993 Modified Files: Makefile Added Files: main.cpp concept.hpp fault.hpp functor.hpp map.hpp midpoint.hpp random.hpp Log Message: * Makefile, main.cpp, concept.hpp, fault.hpp, midpoint.hpp, map.hpp, function.hpp, random.hpp: Port of terraingen to C++. --- NEW FILE: random.hpp --- #ifndef TERRAIN_RANDOM_HPP #define TERRAIN_RANDOM_HPP #include <cstdlib> #include <algorithm> namespace Random { inline int range(int max) { return std::rand() % max; } inline int range(int min, int max) { if (min > max) std::swap(min, max); return min + std::rand() % (max - min + 1); } template <typename T> inline T norm() { return T(rand()) / RAND_MAX; } } #endif --- NEW FILE: midpoint.hpp --- #ifndef TERRAIN_MIDPOINT_HPP #define TERRAIN_MIDPOINT_HPP #include <functional> #include <boost/concept_check.hpp> #include "random.hpp" #include "map.hpp" namespace Terrain { template <typename T> T clamp(T x, T min, T max) { return std::min(max, std::max(min, x)); } template <typename T> class MidpointDisplace : public std::unary_function<Map<T>&, void> { public: MidpointDisplace() {} void operator () (Map<T>& map) { map.clear(-1); /* Generate corners */ set_random_height(map, 0, 0); set_random_height(map, map.get_width()-1, 0); set_random_height(map, 0, map.get_height()-1); set_random_height(map, map.get_width()-1, map.get_height()-1); generate_region(map, 0, 0, map.get_width()-1, map.get_height()-1); } void generate_region(Map<T>& map, int x0, int y0, int x1, int y1) { int x, y, dx, dy; T z0, z1, z2, z3; dx = x1 - x0; dy = y1 - y0; x = (x0 + x1) / 2; y = (y0 + y1) / 2; /* End when we're already at pixel size */ if (dx <= 1 && dy <= 1) return; /* Generate midpoint of each edge */ z0 = displace_x_edge_center(map, x0, x1, x, y0); z1 = displace_x_edge_center(map, x0, x1, x, y1); z2 = displace_y_edge_center(map, y0, y1, x0, y); z3 = displace_y_edge_center(map, y0, y1, x1, y); /* Generate midpoint of region by averaging edge midpoints */ if (map(x, y) < 0) map(x, y) = (z0 + z1 + z2 + z3) / 4; /* Subdivide into 4 subregions and recurse */ generate_region(map, x0, y0, x, y); generate_region(map, x, y0, x1, y); generate_region(map, x0, y, x, y1); generate_region(map, x, y, x1, y1); } T displace_x_edge_center(Map<T>& map, int x0, int x1, int x, int y) { T z, dz; if ((z = map(x, y)) < 0) { dz = ((x1 - x0) * (Random::norm<T>() - 0.5)) / map.get_width(); z = clamp<T>((map(x0, y) + map(x1, y)) / 2 + dz, 0, 1); map(x, y) = z; } return z; } T displace_y_edge_center(Map<T>& map, int y0, int y1, int x, int y) { T z, dz; if ((z = map(x, y)) < 0) { dz = ((y1 - y0) * (Random::norm<T>() - 0.5)) / map.get_height(); z = clamp<T>((map(x, y0) + map(x, y1)) / 2 + dz, 0, 1); map(x, y) = z; } } return z; } private: void set_random_height(Map<T>& map, int x, int y) { if (map(x, y) < 0) map(x, y) = Random::norm<T>(); } }; // class MidpointDisplace } // namespace Terrain #endif --- NEW FILE: functor.hpp --- #ifndef TERRAIN_FUNCTORS_HPP #define TERRAIN_FUNCTORS_HPP #include <algorithm> #include <functional> #include <boost/concept_check.hpp> #include "concept.hpp" namespace Functor { template <typename T> class Range : public std::unary_function<T, void> { BOOST_CLASS_REQUIRE(T, boost, LessThanComparableConcept); public: Range() : m_min(0), m_max(0), m_first(true) {} void operator () (T x) { if (m_first) { m_min = m_max = x; m_first = false; return; } m_min = std::min(m_min, x); m_max = std::max(m_max, x); } T min() const { return m_min; } T max() const { return m_max; } private: T m_min; T m_max; bool m_first; }; template <typename T> class ScaleFit : public std::unary_function<T, T> { BOOST_CLASS_REQUIRE(T, Concept, Arithmetic); public: // WARNING: this doesn't work for integers because the 'scale' // will be computed using an integer division. Easily solved // using specialization ScaleFit(T min1, T max1, T min2, T max2) : m_min1(min1), m_min2(min2) { scale = (max2 - min2) / (max1 - min1); } T operator () (T x) { return m_min2 + (x - m_min1) * scale; } private: T m_min1, m_min2; T scale; }; } #endif --- NEW FILE: concept.hpp --- #ifndef TERRAIN_CONCEPT_HPP #define TERRAIN_CONCEPT_HPP namespace Concept { template <typename T> struct Arithmetic { void constraints() { T x, y; x = y; x = -y; x += y; x = x + y; x -= y; x = y - y; x *= y; x = x * y; x /= y; x = x / y; } T x; }; } // namespace Concept #endif --- NEW FILE: map.hpp --- #ifndef TERRAIN_MAP_HPP #define TERRAIN_MAP_HPP #include <vector> #include <algorithm> #include <boost/concept_check.hpp> #include "functor.hpp" #include "concept.hpp" namespace Terrain { template <typename ValueT> class Map { BOOST_CLASS_REQUIRE(ValueT, Concept, Arithmetic); public: typedef ValueT *iterator; typedef const iterator const_iterator; typedef ValueT &reference; typedef const reference const_reference; Map() : m_width(0), m_height(0), m_table(0) {} Map(int width, int height) : m_width(width), m_height(height) { m_table = allocate(width, height); } Map(const Map& map) : m_width(map.m_width), m_height(map.m_height) { // It would be more efficient to implement a variation of copy // that resorts to memcpy when the ValueT is primitive m_table = allocate(m_width, m_height); std::copy(map.begin(), map.end(), begin()); } ~Map() {} void clear(const ValueT& value) { std::fill(begin(), end(), value); } void resize(int width, int height, bool preserve = true) { ValueTable table = allocate(width, height); if (m_table) { if (preserve) { for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) table[y][x] = m_table[y][x]; } destroy(m_table); } m_table = table; } void scale_fit(ValueT min, ValueT max) { using namespace Functor; Range<ValueT> range = std::for_each(begin(), end(), Range<ValueT>()); std::transform(begin(), end(), begin(), ScaleFit<ValueT>(range.min(), range.max(), min, max)); } void normalize() { scale_fit(0, 1); } inline int get_width() const { return m_width; } inline int get_height() const { return m_height; } inline iterator begin() { return m_table[0]; } inline iterator end() { return begin() + m_width * m_height; } inline const_iterator begin() const { return m_table[0]; } inline const_iterator end() const { return begin() + m_width * m_height; } inline reference operator () (int x, int y) { return m_table[y][x]; } inline const_reference operator () (int x, int y) const { return m_table[y][x]; } private: typedef iterator *ValueTable; int m_width; int m_height; ValueTable m_table; ValueTable allocate(int width, int height) { ValueTable table; table = new iterator[height]; table[0] = new ValueT[width * height]; for (int y = 1; y < height; y++) table[y] = table[y-1] + width; return table; } void destroy(ValueTable table) { if (m_table) { delete []m_table[0]; delete []m_table; } } }; } // namespace Terrain #endif Index: Makefile =================================================================== RCS file: /cvsroot/copter/tools/terraingen/Makefile,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** Makefile 19 May 2000 00:03:32 -0000 1.2 --- Makefile 14 Apr 2005 08:31:15 -0000 1.3 *************** *** 1,28 **** - OUTPUT = terraingen ! CC = gcc ! CFLAGS = -Wall -ansi -pedantic -g -O3 ! PATHS = ! LIBS = ! ! CFILES = terraingen.c ! ! OBJFILES = $(CFILES:%.c=%.o) ! ! all : $(OBJFILES) ! $(CC) $(CFLAGS) $(PATHS) $(LIBS) $(OBJFILES) -o $(OUTPUT) ! .c.o : ! $(CC) $(CFLAGS) -c $< -o $@ ! .PHONY : clean ! clean : ! rm -f $(OBJFILES) $(OUTPUT) core *~ --- 1,35 ---- OUTPUT = terraingen ! CXX = g++ ! CTAGS = etags ! MAKEDEPEND = g++ -M + CXXFLAGS = -Wall -ansi -pedantic + LDFLAGS = -lm -lboost_program_options ! SRCFILES = $(wildcard *.hpp) $(wildcard *.cpp) ! CXXFILES = $(wildcard *.cpp) ! OBJFILES = $(CXXFILES:.cpp=.o) + $(OUTPUT): TAGS $(OBJFILES) + $(CXX) $(LDFLAGS) $(OBJFILES) -o $(OUTPUT) ! .PHONY: clean-all ! .PHONY: clean + clean-all: clean + rm -f $(OUTPUT) + clean: + rm -f $(OBJFILES) + rm -f *~ + rm -f core* ! TAGS: $(SRCFILES) ! $(CTAGS) $(SRCFILES) ! .PHONY: depend ! depend: $(SRCFILES) ! $(MAKEDEPEND) $(SRCFILES) > Dependencies + -include Dependencies --- NEW FILE: main.cpp --- #include <iostream> #include <fstream> #include <algorithm> #include <iterator> #include <string> #include <map> #include <boost/format.hpp> #include <boost/program_options.hpp> #include <cstdlib> #include <ctime> #include "map.hpp" #include "fault.hpp" #include "midpoint.hpp" void unexpected_exception_handler() { std::cerr << "Exception specifications violated" << std::endl; throw; } void terminate_handler() { std::cerr << "Uncaught exception encountered, aborting." << std::endl; std::abort(); } void parse_options(int argc, char **argv) { namespace po = boost::program_options; po::options_description generic_desc("Allowed options"); generic_desc.add_options() ("help,h" , "show this help message") ("version,v", "print version") ("output,o" , po::value<std::string>(), "output filename") ("width,x" , po::value<int>()->default_value(256), "width in grid units") ("height,y" , po::value<int>(), "height in grid units") ("min,z" , po::value<float>()->default_value(0), "min Z") ("max,Z" , po::value<float>()->default_value(1), "max Z") ; po::options_description fault_desc("Faulting options"); fault_desc.add_options() ("faults,f", po::value<int>()->default_value(1000), "fault count") ; po::options_description desc; desc.add(generic_desc).add(fault_desc); po::variables_map var_map; try { po::store(po::parse_command_line(argc, argv, desc), var_map); po::notify(var_map); } catch (po::error& error) { std::cerr << error.what() << std::endl; } if (var_map.count("help")) { std::cout << desc << std::endl; std::exit(EXIT_SUCCESS); } } template <typename T> void save(std::ostream& stream, const Terrain::Map<T>& map) { stream << boost::format("%d\n%d\n") % map.get_width() % map.get_height(); std::copy(map.begin(), map.end(), std::ostream_iterator<T>(stream, "\n")); } template <typename T> void save_to_file(const char *filename, const Terrain::Map<T>& map) { std::fstream file(filename, std::ios::out); save(file, map); } template <typename T> void fault_generate(Terrain::Map<T>& map, int n_faults, T depth) { Terrain::Fault<T>(n_faults, depth)(map); } template <typename T> void midpoint_displace_generate(Terrain::Map<T>& map) { Terrain::MidpointDisplace<T>()(map); } int main(int argc, char *argv[]) { std::set_unexpected(unexpected_exception_handler); std::set_terminate(terminate_handler); parse_options(argc, argv); std::srand(std::time(NULL)); Terrain::Map<float> map(256, 256); midpoint_displace_generate(map); //fault_generate(map, 1000, 1.0f); map.scale_fit(-1, 1); save_to_file("terrain.dat", map); return 0; } --- NEW FILE: fault.hpp --- #ifndef TERRAIN_FAULT_HPP #define TERRAIN_FAULT_HPP #include <functional> #include <boost/concept_check.hpp> #include <cmath> #include "fault.hpp" #include "map.hpp" #include "concept.hpp" #include "random.hpp" namespace Terrain { template <typename T> int random_edge_point(const Map<T>& map, int& x, int& y) { int edge = rand() & 3; // decide which edge the point is to be placed on switch (edge) { case 0: // left edge x = 0; y = Random::range(map.get_height()); break; case 1: // right edge x = map.get_width() - 1; y = Random::range(map.get_height()); break; case 2: // top edge x = Random::range(map.get_width()); y = 0; break; case 3: // bottom edge x = Random::range(map.get_width()); y = map.get_height() - 1; break; } return edge; } template <typename T> void raise_row(Map<T>& map, int xt, int yt, int side, T amount) { if (side == 0) { // left side for (int x = 0; x < xt; x++) map(x, yt) += amount; } else { // right side int width = map.get_width(); for (int x = xt + 1; x < width; x++) map(x, yt) += amount; } } template <typename T> void raise_column(Map<T>& map, int xt, int yt, int side, T amount) { if (side == 0) { // upper for (int y = 0; y < yt; y++) map(xt, y) += amount; } else { // lower int height = map.get_height(); for (int y = yt + 1; y < height; y++) map(xt, y) += amount; } } template <typename T> void fault(Map<T>& map, int x0, int y0, int x1, int y1, int side, T depth) { // NOTE: the fault line is traced using DDA. Using Bresaham may speed // it up slightly. int map_width = map.get_width(); int map_height = map.get_height(); int dx = x1 - x0; int dy = y1 - y0; if (std::abs(dx) > std::abs(dy)) { // X-axis is the major axis if (dx < 0) { std::swap(x0, x1); dx = -dx; std::swap(y0, y1); dy = -dy; } float step = static_cast<float>(dy) / dx; float yt = y0; for (int xt = 0; xt < map_width; xt++) { if (xt == x0) { // trace fault line for (; xt <= x1; xt++) { raise_column(map, xt, static_cast<int>(yt), side, depth); yt += step; } if (xt >= map_width) break; yt = y1; } raise_column(map, xt, static_cast<int>(yt), side, depth); } } else { // Y-axis is the major axis if (dy < 0) { std::swap(x0, x1); dx = -dx; std::swap(y0, y1); dy = -dy; } float step = static_cast<float>(dx) / dy; float xt = x0; for (int yt = y0; yt < map_height; yt++) { if (yt == y0) { // trace fault line for (; yt <= y1; yt++) { raise_row(map, static_cast<int>(xt), yt, side, depth); xt += step; } if (yt >= map_height) break; xt = x1; } raise_row(map, static_cast<int>(xt), yt, side, depth); } } } template <typename T> class Fault : public std::unary_function<Map<T>&, void> { BOOST_CLASS_REQUIRE(T, Concept, Arithmetic); public: Fault(int n_faults, T fault_depth) : m_n_faults(n_faults), m_fault_depth(fault_depth) {} void operator () (Map<T>& map) { int x0, y0, x1, y1; int edge, side; T depth; map.clear(0); for (int i = 0; i < m_n_faults; i++) { // generate fault endpoints edge = random_edge_point<T>(map, x0, y0); while (random_edge_point<T>(map, x1, y1) == edge); // determine which side to raise or drop side = rand() & 1; depth = rand() & 1 ? m_fault_depth : -m_fault_depth; fault(map, x0, y0, x1, y1, side, depth); } // Normalize height range to [0, 1.0] map.normalize(); } private: int m_n_faults; T m_fault_depth; }; } // namespace Terrain #endif |