Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <assert.h>
- #include <math.h>
- #include <stdint.h>
- #include <array>
- #include <list>
- #include <string>
- #include <utility>
- #include <vector>
- using namespace std;
- //////// From Lobster's dev/src/lobster/tools.h ////////
- class PCG32 {
- // This is apparently better than the Mersenne Twister, and its also smaller/faster!
- // Adapted from *Really* minimal PCG32 code / (c) 2014 M.E. O'Neill / pcg-random.org
- // Licensed under Apache License 2.0 (NO WARRANTY, etc. see website).
- uint64_t state = 0xABADCAFEDEADBEEF;
- uint64_t inc = 0xDEADBABEABADD00D;
- public:
- uint32_t Random() {
- uint64_t oldstate = state;
- // Advance internal state.
- state = oldstate * 6364136223846793005ULL + (inc | 1);
- // Calculate output function (XSH RR), uses old state for max ILP.
- uint32_t xorshifted = uint32_t(((oldstate >> 18u) ^ oldstate) >> 27u);
- uint32_t rot = oldstate >> 59u;
- return (xorshifted >> rot) | (xorshifted << ((-rot) & 31));
- }
- void ReSeed(uint32_t s) {
- state = s;
- inc = 0xDEADBABEABADD00D;
- }
- };
- template<typename T> struct RandomNumberGenerator {
- T rnd;
- void seed(uint32_t s) { rnd.ReSeed(s); }
- int operator()(int max) { return rnd.Random() % max; }
- int operator()() { return rnd.Random(); }
- double rnddouble() { return rnd.Random() * (1.0 / 4294967296.0); }
- float rnd_float() { return (float)rnddouble(); } // FIXME: performance?
- float rndfloatsigned() { return (float)(rnddouble() * 2 - 1); }
- double n2 = 0.0;
- bool n2_cached = false;
- // Returns gaussian with stddev of 1 and mean of 0.
- // Box Muller method.
- double rnd_gaussian() {
- n2_cached = !n2_cached;
- if (n2_cached) {
- double x, y, r;
- do {
- x = 2.0 * rnddouble() - 1;
- y = 2.0 * rnddouble() - 1;
- r = x * x + y * y;
- } while (r == 0.0 || r > 1.0);
- double d = sqrt(-2.0 * log(r) / r);
- double n1 = x * d;
- n2 = y * d;
- return n1;
- } else {
- return n2;
- }
- }
- };
- inline int PopCount(uint32_t val) {
- #ifdef _MSC_VER
- return (int)__popcnt(val);
- #else
- return __builtin_popcount(val);
- #endif
- }
- inline int PopCount(uint64_t val) {
- #ifdef _MSC_VER
- #ifdef _WIN64
- return (int)__popcnt64(val);
- #else
- return (int)(__popcnt((uint32_t)val) + __popcnt((uint32_t)(val >> 32)));
- #endif
- #else
- return __builtin_popcountll(val);
- #endif
- }
- //////// From Lobster's dev/src/lobster/geom.h ////////
- // vec: supports 1..4 components of any numerical type.
- // Compile time unrolled loops for 1..4 components,
- #define DOVEC(F) { \
- const int i = 0; \
- F; \
- if constexpr (N > 1) { \
- const int i = 1; \
- F; \
- if constexpr (N > 2) { \
- const int i = 2; \
- F; \
- if constexpr (N > 3) { \
- const int i = 3; \
- F; \
- } \
- } \
- } \
- }
- #define DOVECR(F) { vec<T, N> _t; DOVEC(_t.c[i] = F); return _t; }
- #define DOVECRI(F) { vec<int, N> _t; DOVEC(_t.c[i] = F); return _t; }
- #define DOVECF(I, F) { T _ = I; DOVEC(_ = F); return _; }
- #define DOVECB(I, F) { bool _ = I; DOVEC(_ = F); return _; }
- union int2float { int i; float f; };
- union int2float64 { int64_t i; double f; };
- inline void default_debug_value(float &a) { int2float nan; nan.i = 0x7F800001; a = nan.f; }
- inline void default_debug_value(double &a) { int2float nan; nan.i = 0x7F800001; a = nan.f; }
- inline void default_debug_value(int64_t &a) { a = 0x1BADCAFEABADD00D; }
- inline void default_debug_value(int32_t &a) { a = 0x1BADCAFE; }
- inline void default_debug_value(uint16_t&a) { a = 0x1BAD; }
- inline void default_debug_value(uint8_t &a) { a = 0x1B; }
- template<typename T, int C, int R> class matrix;
- template<typename T, int N> struct basevec {
- };
- template<typename T> struct basevec<T, 2> {
- union {
- T c[2];
- struct { T x; T y; };
- };
- };
- template<typename T> struct basevec<T, 3> {
- union {
- T c[3];
- struct { T x; T y; T z; };
- };
- };
- template<typename T> struct basevec<T, 4> {
- union {
- T c[4];
- struct { T x; T y; T z; T w; };
- };
- };
- template<typename T, int N> struct vec : basevec<T, N> {
- enum { NUM_ELEMENTS = N };
- typedef T CTYPE;
- // Clang needs these, but VS is cool without them?
- using basevec<T, N>::c;
- using basevec<T, N>::x;
- using basevec<T, N>::y;
- vec() {
- #ifndef NDEBUG
- DOVEC(default_debug_value(c[i]));
- #endif
- }
- explicit vec(T e) { DOVEC(c[i] = e); }
- explicit vec(const T *v) { DOVEC(c[i] = v[i]); }
- template<typename U> explicit vec(const vec<U,N> &v) { DOVEC(c[i] = (T)v[i]); }
- vec(T _x, T _y, T _z, T _w) { x = _x; y = _y; assert(N == 4);
- if constexpr (N > 2) c[2] = _z; else (void)_z;
- if constexpr (N > 3) c[3] = _w; else (void)_w; }
- vec(T _x, T _y, T _z) { x = _x; y = _y; assert(N == 3);
- if constexpr (N > 2) c[2] = _z; else (void)_z; }
- vec(T _x, T _y) { x = _x; y = _y; assert(N == 2); }
- vec(const pair<T, T> &p) { x = p.first; y = p.second; assert(N == 2); }
- const T *data() const { return c; }
- const T *begin() const { return c; }
- const T *end() const { return c + N; }
- T operator[](size_t i) const { return c[i]; }
- T &operator[](size_t i) { return c[i]; }
- vec(const vec<T,3> &v, T e) { DOVEC(c[i] = i < 3 ? v[i] : e); }
- vec(const vec<T,2> &v, T e) { DOVEC(c[i] = i < 2 ? v[i] : e); }
- vec<T,3> xyz() const { assert(N == 4); return vec<T,3>(c); }
- vec<T,2> xy() const { assert(N >= 3); return vec<T,2>(c); }
- pair<T, T> to_pair() const { assert(N == 2); return { x, y }; }
- vec operator+(const vec &v) const { DOVECR(c[i] + v[i]); }
- vec operator-(const vec &v) const { DOVECR(c[i] - v[i]); }
- vec operator*(const vec &v) const { DOVECR(c[i] * v[i]); }
- vec operator/(const vec &v) const { DOVECR(c[i] / v[i]); }
- vec operator%(const vec &v) const { DOVECR(c[i] % v[i]); }
- vec operator+(T e) const { DOVECR(c[i] + e); }
- vec operator-(T e) const { DOVECR(c[i] - e); }
- vec operator*(T e) const { DOVECR(c[i] * e); }
- vec operator/(T e) const { DOVECR(c[i] / e); }
- vec operator%(T e) const { DOVECR(c[i] % e); }
- vec operator&(T e) const { DOVECR(c[i] & e); }
- vec operator|(T e) const { DOVECR(c[i] | e); }
- vec operator<<(T e) const { DOVECR(c[i] << e); }
- vec operator>>(T e) const { DOVECR(c[i] >> e); }
- vec operator-() const { DOVECR(-c[i]); }
- vec &operator+=(const vec &v) { DOVEC(c[i] += v[i]); return *this; }
- vec &operator-=(const vec &v) { DOVEC(c[i] -= v[i]); return *this; }
- vec &operator*=(const vec &v) { DOVEC(c[i] *= v[i]); return *this; }
- vec &operator/=(const vec &v) { DOVEC(c[i] /= v[i]); return *this; }
- vec &operator+=(T e) { DOVEC(c[i] += e); return *this; }
- vec &operator-=(T e) { DOVEC(c[i] -= e); return *this; }
- vec &operator*=(T e) { DOVEC(c[i] *= e); return *this; }
- vec &operator/=(T e) { DOVEC(c[i] /= e); return *this; }
- vec &operator&=(T e) { DOVEC(c[i] &= e); return *this; }
- bool operator<=(const vec &v) const {
- DOVECB(true, _ && c[i] <= v[i]);
- }
- bool operator< (const vec &v) const {
- DOVECB(true, _ && c[i] < v[i]);
- }
- bool operator>=(const vec &v) const {
- DOVECB(true, _ && c[i] >= v[i]);
- }
- bool operator> (const vec &v) const {
- DOVECB(true, _ && c[i] > v[i]);
- }
- bool operator==(const vec &v) const {
- DOVECB(true, _ && c[i] == v[i]);
- }
- bool operator!=(const vec &v) const {
- DOVECB(false, _ || c[i] != v[i]);
- }
- bool operator<=(T e) const { DOVECB(true, _ && c[i] <= e); }
- bool operator< (T e) const { DOVECB(true, _ && c[i] < e); }
- bool operator>=(T e) const { DOVECB(true, _ && c[i] >= e); }
- bool operator> (T e) const { DOVECB(true, _ && c[i] > e); }
- bool operator==(T e) const { DOVECB(true, _ && c[i] == e); }
- bool operator!=(T e) const { DOVECB(false, _ || c[i] != e); }
- vec<int, N> lte(T e) const { DOVECRI(c[i] <= e); }
- vec<int, N> lt (T e) const { DOVECRI(c[i] < e); }
- vec<int, N> gte(T e) const { DOVECRI(c[i] >= e); }
- vec<int, N> gt (T e) const { DOVECRI(c[i] > e); }
- vec<int, N> eq (T e) const { DOVECRI(c[i] == e); }
- vec<int, N> ne (T e) const { DOVECRI(c[i] != e); }
- vec iflt(T e, const vec &a, const vec &b) const { DOVECR(c[i] < e ? a[i] : b[i]); }
- std::string to_string() const {
- string s = "(";
- DOVEC(if (i) s += ", "; s += std::to_string(c[i]));
- return s + ")";
- }
- T volume() const { DOVECF(1, _ * c[i]); }
- template<typename T2, int C, int R> friend class matrix;
- };
- typedef vec<int, 2> int2;
- template<typename T, int N, typename R> inline vec<int, N> rndivec(RandomNumberGenerator<R> &r,
- const vec<int, N> &max) {
- DOVECR(r(max[i]));
- }
- ////////////////////////////////////////////////////////
- // Copyright 2018 Wouter van Oortmerssen. All rights reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- // Very simple tile based Wave Function Collapse ("Simple Tiled Model") implementation.
- // See: https://github.com/mxgmn/WaveFunctionCollapse
- // Derives adjacencies from an example rather than explicitly specified neighbors.
- // Does not do any symmetries/rotations unless they're in the example.
- // Algorithm has a lot of similarities to A* in how its implemented.
- // Uses bitmasks to store the set of possible tiles, which currently limits the number of
- // unique tiles to 64. This restriction cool be lifted by using std::bitset instead.
- // In my testing, generates a 50x50 tile map in <1 msec. 58% of such maps are conflict free.
- // At 100x100 that is 3 msec and 34%.
- // At 200x200 that is 24 msec and 13%
- // At 400x400 that is 205 msec and ~1%
- // Algorithm may need to extended to flood more than 2 neighbor levels to make it suitable
- // for really gigantic maps.
- // inmap & outmap must point to row-major 2D arrays of the given size.
- // each in tile char must be in range 0..127, of which max 64 may actually be in use (may be
- // sparse).
- // Returns false if too many unique tiles in input.
- template<typename T> bool WaveFunctionCollapse(const int2 &insize, const char **inmap,
- const int2 &outsize, char **outmap,
- RandomNumberGenerator<T> &rnd,
- int &num_contradictions) {
- num_contradictions = 0;
- typedef uint64_t bitmask_t;
- const auto nbits = sizeof(bitmask_t) * 8;
- array<int, 256> tile_lookup;
- tile_lookup.fill(-1);
- struct Tile { bitmask_t sides[4] = {}; int freq = 0; char tidx = 0; };
- vector<Tile> tiles;
- int2 neighbors[] = { { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 } };
- // Collect unique tiles and their frequency of occurrence.
- for (int iny = 0; iny < insize.y; iny++) {
- for (int inx = 0; inx < insize.x; inx++) {
- auto t = inmap[iny][inx];
- if (tile_lookup[t] < 0) {
- // We use a bitmask_t mask for valid neighbors.
- if (tiles.size() == nbits - 1) return false;
- tile_lookup[t] = (int)tiles.size();
- tiles.push_back(Tile());
- }
- auto &tile = tiles[tile_lookup[t]];
- tile.freq++;
- tile.tidx = t;
- }
- }
- // Construct valid neighbor bitmasks.
- auto to_bitmask = [](size_t idx) { return (bitmask_t)1 << idx; };
- for (int iny = 0; iny < insize.y; iny++) {
- for (int inx = 0; inx < insize.x; inx++) {
- auto t = inmap[iny][inx];
- auto &tile = tiles[tile_lookup[t]];
- int ni = 0;
- for (auto n : neighbors) {
- auto p = (n + int2(inx, iny) + insize) % insize;
- auto tn = inmap[p.y][p.x];
- assert(tile_lookup[tn] >= 0);
- tile.sides[ni] |= to_bitmask(tile_lookup[tn]);
- ni++;
- }
- }
- }
- size_t most_common_tile_id = 0;
- int most_common_tile_freq = 0;
- for (auto &tile : tiles) if (tile.freq > most_common_tile_freq) {
- most_common_tile_freq = tile.freq;
- most_common_tile_id = &tile - &tiles[0];
- }
- // Track an open list (much like A*) of next options, sorted by best candidate at the end.
- list<pair<int2, int>> open, temp;
- // Store a bitmask per output cell of remaining possible choices.
- auto max_bitmask = (1 << tiles.size()) - 1;
- enum class State : int { NEW, OPEN, CLOSED };
- struct Cell {
- bitmask_t wf;
- int popcnt = 0;
- State state = State::NEW;
- list<pair<int2, int>>::iterator it;
- Cell(bitmask_t wf, int popcnt) : wf(wf), popcnt(popcnt) {}
- };
- vector<vector<Cell>> cells(outsize.y, vector<Cell>(outsize.x, Cell(max_bitmask,
- (int)tiles.size())));
- auto start = rndivec<int, 2>(rnd, outsize);
- open.push_back({ start, 0 }); // Start.
- auto &scell = cells[start.y][start.x];
- scell.state = State::OPEN;
- scell.it = open.begin();
- // Pick tiles until no more possible.
- while (!open.empty()) {
- // Simply picking the first list item results in the same chance of conflicts as
- // random picks over equal options, but it is assumed the latter could generate more
- // interesting maps.
- int num_candidates = 1;
- auto numopts_0 = cells[open.back().first.y][open.back().first.x].popcnt;
- for (auto it = ++open.rbegin(); it != open.rend(); ++it)
- if (numopts_0 == cells[it->first.y][it->first.x].popcnt &&
- open.back().second == it->second)
- num_candidates++;
- else
- break;
- auto candidate_i = rnd(num_candidates);
- auto candidate_it = --open.end();
- for (int i = 0; i < candidate_i; i++) --candidate_it;
- auto cur = candidate_it->first;
- temp.splice(temp.end(), open, candidate_it);
- auto &cell = cells[cur.y][cur.x];
- assert(cell.state == State::OPEN);
- cell.state = State::CLOSED;
- bool contradiction = !cell.popcnt;
- if (contradiction) {
- num_contradictions++;
- // Rather than failing right here, fill in the whole map as best as possible just in
- // case a map with bad tile neighbors is still useful to the caller.
- // As a heuristic lets just use the most common tile, as that will likely have the
- // most neighbor options.
- cell.wf = to_bitmask(most_common_tile_id);
- cell.popcnt = 1;
- }
- // From our options, pick one randomly, weighted by frequency of tile occurrence.
- // First find total frequency.
- int total_freq = 0;
- for (size_t i = 0; i < tiles.size(); i++)
- if (cell.wf & to_bitmask(i))
- total_freq += tiles[i].freq;
- auto freqpick = rnd(total_freq);
- // Now pick.
- size_t picked = 0;
- for (size_t i = 0; i < tiles.size(); i++) if (cell.wf & to_bitmask(i)) {
- picked = i;
- if ((freqpick -= tiles[i].freq) <= 0) break;
- }
- assert(freqpick <= 0);
- // Modify the picked tile.
- auto &tile = tiles[picked];
- outmap[cur.y][cur.x] = tile.tidx;
- cell.wf = to_bitmask(picked); // Exactly one option remains.
- cell.popcnt = 1;
- // Now lets cycle thru neighbors, reduce their options (and maybe their neighbors options),
- // and add them to the open list for next pick.
- int ni = 0;
- for (auto n : neighbors) {
- auto p = (cur + n + outsize) % outsize;
- auto &ncell = cells[p.y][p.x];
- if (ncell.state != State::CLOSED) {
- ncell.wf &= tile.sides[ni]; // Reduce options.
- ncell.popcnt = PopCount(ncell.wf);
- int totalnnumopts = 0;
- if (!contradiction) {
- // Hardcoded second level of neighbors of neighbors, to reduce chance of
- // contradiction.
- // Only do this when our current tile isn't a contradiction, to avoid
- // artificially shrinking options.
- int nni = 0;
- for (auto nn : neighbors) {
- auto pnn = (p + nn + outsize) % outsize;
- auto &nncell = cells[pnn.y][pnn.x];
- if (nncell.state != State::CLOSED) {
- // Collect the superset of possible options. If we remove anything but
- // these, we are guaranteed the direct neigbor always has a possible
- //pick.
- bitmask_t superopts = 0;
- for (size_t i = 0; i < tiles.size(); i++)
- if (ncell.wf & to_bitmask(i))
- superopts |= tiles[i].sides[nni];
- nncell.wf &= superopts;
- nncell.popcnt = PopCount(nncell.wf);
- }
- totalnnumopts += nncell.popcnt;
- nni++;
- }
- }
- if (ncell.state == State::OPEN) {
- // Already in the open list, remove it for it to be re-added just in case
- // its location is not optimal anymore.
- totalnnumopts = min(totalnnumopts, ncell.it->second);
- temp.splice(temp.end(), open, ncell.it); // Avoid alloc.
- }
- // Insert this neighbor, sorted by lowest possibilities.
- // Use total possibilities of neighbors as a tie-breaker to avoid causing
- // contradictions by needless surrounding of tiles.
- list<pair<int2, int>>::iterator dit = open.begin();
- for (auto it = open.rbegin(); it != open.rend(); ++it) {
- auto onumopts = cells[it->first.y][it->first.x].popcnt;
- if (onumopts > ncell.popcnt ||
- (onumopts == ncell.popcnt && it->second >= totalnnumopts)) {
- dit = it.base();
- break;
- }
- }
- if (temp.empty()) temp.push_back({});
- open.splice(dit, temp, ncell.it = temp.begin());
- *ncell.it = { p, totalnnumopts };
- ncell.state = State::OPEN;
- }
- ni++;
- }
- }
- return true;
- }
- ////////////////////////////////////////////////////////
- #include <stdio.h>
- #include <stdlib.h>
- #include <map>
- #include <string>
- // Where to write the generated JSON.
- #define OUTPUT_FILE "game/dungeons/0_procgen.json"
- // Starting positions for map tiles in world space.
- #define START_X 75
- #define START_Z -1295
- // Number of tiles on each axis.
- #define MAP_WIDTH 10
- #define MAP_HEIGHT 20
- // One contradiction is considered manageable for most maps.
- #define MAX_CONTRADICTIONS 1
- int main(){
- std::map<char,std::string> tile_models;
- tile_models[0x20] = ""; // Space = empty.
- tile_models['+'] = "floor_001.gltf";
- tile_models['/'] = "beach_corner_northwest.gltf";
- tile_models['-'] = "beach_north.gltf";
- tile_models['\\'] = "beach_corner_northeast.gltf";
- tile_models[']'] = "beach_east.gltf";
- tile_models['J'] = "beach_corner_southeast.gltf";
- tile_models['_'] = "beach_south.gltf";
- tile_models['L'] = "beach_corner_southwest.gltf";
- tile_models['['] = "beach_west.gltf";
- tile_models['v'] = "slope_001_north.gltf";
- tile_models['^'] = "slope_001_south.gltf";
- tile_models['>'] = "slope_001_west.gltf";
- tile_models['<'] = "slope_001_east.gltf";
- tile_models['H'] = "bridge_010_x.gltf";
- tile_models['I'] = "bridge_010_z.gltf";
- tile_models['='] = "overpass_010_x.gltf";
- // Example data that WFC uses.
- auto insize = int2( 24, 11 );
- std::vector<char*> inmap = {
- (char*)R"(/---^---\ /----------^\ )",
- (char*)R"(<+++++++] [+++++++++++>H)",
- (char*)R"([+++++++>H<+++++++__++] )",
- (char*)R"(<+++++++] [++++++] [+>H)",
- (char*)R"(L___v___J [++++++] [+] )",
- (char*)R"( I [+++++++\ [+] )",
- (char*)R"( /--^-----++++++++] [+] )",
- (char*)R"(H<++++++++++++++++>H===H)",
- (char*)R"( [++++++++++++++++] [+] )",
- (char*)R"( L__v_____________J LvJ )",
- (char*)R"( I I )"
- };
- // Output map.
- auto outsize = int2( MAP_WIDTH, MAP_HEIGHT );
- std::vector<char*> outmap;
- for( int y = 0; y < MAP_HEIGHT; y++ ){
- outmap.push_back( (char*)calloc( MAP_WIDTH, 1 ) );
- }
- // RNG.
- RandomNumberGenerator<PCG32> r;
- r.seed( 50 );
- // Output JSON.
- std::string outjson = "{ // Generated by wfcgen\n\t\"partitions\": [\n";
- for( int i = 0; i < 10000; i++ ){ // 10000 attempts could take hours in some cases.
- // Contradiction counter.
- int num_contradictions = 0;
- if( !WaveFunctionCollapse(
- insize,
- (const char**)inmap.data(),
- outsize,
- outmap.data(),
- r,
- num_contradictions ) ){
- fprintf( stderr, "Too many unique tiles in input\n" );
- return 1;
- }
- if( num_contradictions <= MAX_CONTRADICTIONS ){
- // Got a map! Scroll vertically and try to find a near-optimal lower left edge.
- // This part is very specific to RSOD's map and should NOT be used in other games.
- int offset_y = 0;
- for( offset_y = 0; offset_y < MAP_HEIGHT; offset_y++ ){
- int test_y = (MAP_HEIGHT - 1 + offset_y) % MAP_HEIGHT;
- bool success = true;
- for( int x = 1; x <= 7; x++ ){
- if( outmap[test_y][x] != '+' ) success = false;
- }
- if( success ) break;
- }
- // One more RSOD-specific check: Make sure the map does not have sections that are blocked by empty rows.
- bool good_map = true;
- for( int y = 1; y < MAP_HEIGHT; y++ ){
- for( int x = 0; x < MAP_WIDTH; x++ ){
- if( outmap[(y + offset_y) % MAP_HEIGHT][x] != 0x20 )
- break;
- if( x == MAP_WIDTH - 1 ) good_map = false;
- }
- if( !good_map ) break;
- }
- if( good_map ){
- // Force map edges to be regular cement floors.
- // Top edge.
- for( int x = 0; x < MAP_WIDTH; x++ ){
- char &c = outmap[offset_y % MAP_HEIGHT][x];
- if( c != 0x20 ) c = '+';
- }
- // Bottom edge.
- for( int x = 0; x < MAP_WIDTH; x++ ){
- char &c = outmap[(MAP_HEIGHT - 1 + offset_y) % MAP_HEIGHT][x];
- if( c != 0x20 ) c = '+';
- }
- // Left edge.
- for( int y = 0; y < MAP_HEIGHT; y++ ){
- char &c = outmap[y][0];
- if( c != 0x20 ) c = '+';
- }
- // Right edge.
- for( int y = 0; y < MAP_HEIGHT; y++ ){
- char &c = outmap[y][MAP_WIDTH - 1];
- if( c != 0x20 ) c = '+';
- }
- // Output the map.
- for( int y = 0; y < MAP_HEIGHT; y++ ){
- for( int x = 0; x < MAP_WIDTH; x++ ){
- char c = outmap[(y + offset_y) % MAP_HEIGHT][x];
- putchar( c );
- int world_x = x * 50 + START_X, world_z = y * 50 + START_Z;
- // Select a model.
- auto it = tile_models.find( c );
- if( it != tile_models.end()
- && it->second.length() > 0 ){
- outjson += std::string( "\t\t{\n" )
- + "\t\t\t\"map\": \"" + it->second + "\",\n"
- + "\t\t\t\"translation\": [ " + std::to_string( world_x ) + ".0, 0.0, " + std::to_string( world_z ) + ".0 ],\n"
- + "\t\t\t\"restitution\": 0.1\n"
- + "\t\t},\n";
- }
- // Add buildings.
- if( c == '+' && r(11) < 4 ){
- std::string model =
- r(5) < 2 ? (r(2) ? (r(2) ? "bar" : "store") : (r(2) ? "restaurant" : "apartments")) : "apartments";
- outjson += std::string( "\t\t{\n" )
- + "\t\t\t\"map\": \"" + model + ".gltf\",\n"
- + "\t\t\t\"translation\": [ " + std::to_string( world_x ) + ".0, 1.0, " + std::to_string( world_z ) + ".0 ],\n"
- + "\t\t\t\"restitution\": 0.1\n"
- + "\t\t},\n";
- }
- }
- putchar( '\n' );
- }
- printf( "num_contradictions: %d (Attempts made: %d)\n\n", num_contradictions, i + 1 );
- outjson += "\t]\n}\n";
- FILE *file = fopen( OUTPUT_FILE, "w" );
- if( file ){
- fprintf( file, "%s", outjson.c_str() );
- fclose( file );
- printf( "File written: %s\n", OUTPUT_FILE );
- }
- return 0;
- }
- }
- // Clear the output map and try again.
- for( auto &row : outmap ){
- for( int x = 0; x < MAP_WIDTH; x++ ){
- row[x] = 0;
- }
- }
- }
- fprintf( stderr,
- "Failed to generate a satisfactory map. Consider changing example data or shrinking the output size." );
- return 2;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement