Commit for 2020.07.17.7z
This commit is contained in:
parent
830566ab5f
commit
e3e28a8910
@ -10,7 +10,7 @@ layout (location = 0) out vec4 outFragColor;
|
|||||||
|
|
||||||
void fog( inout vec3 i ) {
|
void fog( inout vec3 i ) {
|
||||||
vec3 color = vec3( 0, 0, 0 );
|
vec3 color = vec3( 0, 0, 0 );
|
||||||
float inner = 8, outer = 64;
|
float inner = 8, outer = 32;
|
||||||
float distance = length(-inPositionEye);
|
float distance = length(-inPositionEye);
|
||||||
float factor = (distance - inner) / (outer - inner);
|
float factor = (distance - inner) / (outer - inner);
|
||||||
factor = clamp( factor, 0.0, 1.0 );
|
factor = clamp( factor, 0.0, 1.0 );
|
||||||
@ -46,7 +46,7 @@ void phong( inout vec3 i ) {
|
|||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
outFragColor = texture(samplerColor, inUv);
|
outFragColor = texture(samplerColor, inUv);
|
||||||
// phong(outFragColor.rgb);
|
phong(outFragColor.rgb);
|
||||||
fog(outFragColor.rgb);
|
fog(outFragColor.rgb);
|
||||||
// outFragColor.rgb = mix( outFragColor.rgb, inColor.rgb, inColor.a );
|
// outFragColor.rgb = mix( outFragColor.rgb, inColor.rgb, inColor.a );
|
||||||
outFragColor.rgb = outFragColor.rgb * inColor.rgb;
|
outFragColor.rgb = outFragColor.rgb * inColor.rgb;
|
||||||
|
|||||||
@ -84,7 +84,7 @@ void ext::TerrainGenerator::generate( ext::Region& region ){
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
std::string base = region.getParent().getComponent<uf::Serializer>()["_config"]["hash"].asString();
|
std::string base = region.getParent().getComponent<uf::Serializer>()["_config"]["hash"].asString();
|
||||||
std::string filename = "./data/save/regions/" + base + "/" + std::to_string(location.x) + "." + std::to_string(location.y) + "." + std::to_string(location.z) + ".json";
|
std::string filename = "./data/save/" + base + "/regions/" + std::to_string(location.x) + "." + std::to_string(location.y) + "." + std::to_string(location.z) + ".json";
|
||||||
// old region, load save
|
// old region, load save
|
||||||
if ( uf::string::exists( filename ) ) {
|
if ( uf::string::exists( filename ) ) {
|
||||||
uf::Serializer save; save.readFromFile(filename);
|
uf::Serializer save; save.readFromFile(filename);
|
||||||
@ -123,6 +123,22 @@ void ext::TerrainGenerator::generate( ext::Region& region ){
|
|||||||
}
|
}
|
||||||
// load failed / new region
|
// load failed / new region
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// maze
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
ext::Maze& maze = this->m_terrain->getComponent<ext::Maze>();
|
||||||
|
int ROW = this->m_location.z + (maze.WIDTH / 2) - 1;
|
||||||
|
int COL = this->m_location.x + (maze.LENGTH / 2) - 1;
|
||||||
|
auto value = maze( ROW, COL, 0 );
|
||||||
|
bool east = value & ext::Maze::EAST; // ROW + 1 < maze.WIDTH ? maze( ROW + 1, COL, 1 ) : 0;
|
||||||
|
bool west = value & ext::Maze::WEST; // ROW - 1 > 0 ? maze( ROW - 1, COL, 1 ) : 0;
|
||||||
|
bool north = value & ext::Maze::NORTH; // COL + 1 < maze.LENGTH ? maze( ROW, COL + 1, 1 ) : 0;
|
||||||
|
bool south = value & ext::Maze::SOUTH; // COL - 1 > 0 ? maze( ROW, COL - 1, 1 ) : 0;
|
||||||
|
std::cout << ROW << ", " << COL << " = " << value << "\t" << east << " " << west << " " << north << " " << south << std::endl;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
if ( this->m_voxels.id.rle.empty() ) {
|
if ( this->m_voxels.id.rle.empty() ) {
|
||||||
double maxValue = 0.0;
|
double maxValue = 0.0;
|
||||||
double base = 0;
|
double base = 0;
|
||||||
@ -131,9 +147,9 @@ void ext::TerrainGenerator::generate( ext::Region& region ){
|
|||||||
ext::TerrainVoxel::light_t raw_lighting[this->m_size.x][this->m_size.y][this->m_size.z];
|
ext::TerrainVoxel::light_t raw_lighting[this->m_size.x][this->m_size.y][this->m_size.z];
|
||||||
double raw_noise[this->m_size.x][this->m_size.y][this->m_size.z];
|
double raw_noise[this->m_size.x][this->m_size.y][this->m_size.z];
|
||||||
|
|
||||||
for ( int y = 0; y < this->m_size.y; ++y )
|
for ( uint y = 0; y < this->m_size.y; ++y )
|
||||||
for ( int z = 0; z < this->m_size.z; ++z )
|
for ( uint z = 0; z < this->m_size.z; ++z )
|
||||||
for ( int x = 0; x < this->m_size.x; ++x ) {
|
for ( uint x = 0; x < this->m_size.x; ++x ) {
|
||||||
pod::Vector3d position = {
|
pod::Vector3d position = {
|
||||||
transform.position.x - half_x, transform.position.y - half_y, transform.position.z - half_z
|
transform.position.x - half_x, transform.position.y - half_y, transform.position.z - half_z
|
||||||
};
|
};
|
||||||
@ -145,14 +161,13 @@ void ext::TerrainGenerator::generate( ext::Region& region ){
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
for ( int y = 0; y < this->m_size.y; ++y )
|
for ( uint y = 0; y < this->m_size.y; ++y )
|
||||||
for ( int z = 0; z < this->m_size.z; ++z )
|
for ( uint z = 0; z < this->m_size.z; ++z )
|
||||||
for ( int x = 0; x < this->m_size.x; ++x )
|
for ( uint x = 0; x < this->m_size.x; ++x )
|
||||||
base += raw_noise[x][y][z] / maxValue;
|
base += raw_noise[x][y][z] / maxValue;
|
||||||
|
|
||||||
base /= this->m_size.x * this->m_size.y * this->m_size.z;
|
base /= this->m_size.x * this->m_size.y * this->m_size.z;
|
||||||
|
|
||||||
|
|
||||||
uint partitions = this->m_modulus;
|
uint partitions = this->m_modulus;
|
||||||
struct {
|
struct {
|
||||||
ext::TerrainVoxel::uid_t floor = ext::TerrainVoxelFloor().uid();
|
ext::TerrainVoxel::uid_t floor = ext::TerrainVoxelFloor().uid();
|
||||||
@ -166,17 +181,31 @@ void ext::TerrainGenerator::generate( ext::Region& region ){
|
|||||||
this->m_voxels.id.raw.reserve( this->m_size.x * this->m_size.y * this->m_size.z );
|
this->m_voxels.id.raw.reserve( this->m_size.x * this->m_size.y * this->m_size.z );
|
||||||
this->m_voxels.lighting.raw.reserve( this->m_size.x * this->m_size.y * this->m_size.z );
|
this->m_voxels.lighting.raw.reserve( this->m_size.x * this->m_size.y * this->m_size.z );
|
||||||
|
|
||||||
std::size_t i = 0;
|
ext::Maze& maze = this->m_terrain->getComponent<ext::Maze>();
|
||||||
|
int LABYRINTH = ext::Maze::FLOOR | ext::Maze::CEIL;
|
||||||
|
{
|
||||||
|
int ROW = this->m_location.z + (maze.WIDTH / 2) - 1;
|
||||||
|
int COL = this->m_location.x + (maze.LENGTH / 2) - 1;
|
||||||
|
if ( ROW >= 0 && ROW < maze.WIDTH && COL >= 0 && COL < maze.LENGTH )
|
||||||
|
LABYRINTH = maze( ROW, COL, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t i = 0;
|
||||||
for ( uint y = 0; y < this->m_size.y; ++y ) {
|
for ( uint y = 0; y < this->m_size.y; ++y ) {
|
||||||
for ( uint z = 0; z < this->m_size.z; ++z ) {
|
for ( uint z = 0; z < this->m_size.z; ++z ) {
|
||||||
for ( uint x = 0; x < this->m_size.x; ++x ) {
|
for ( uint x = 0; x < this->m_size.x; ++x ) {
|
||||||
ext::TerrainVoxel::uid_t voxel = 0;
|
ext::TerrainVoxel::uid_t voxel = 0;
|
||||||
/*
|
|
||||||
if ( y <= 4 ) voxel = atlas.floor;
|
|
||||||
*/
|
|
||||||
|
|
||||||
double e = raw_noise[x][y][z] / maxValue;
|
double e = raw_noise[x][y][z] / maxValue;
|
||||||
|
// generate walls if wall exists in maze
|
||||||
|
if ( LABYRINTH & ext::Maze::EAST ) if ( x == 0 ) voxel = atlas.wall;
|
||||||
|
if ( LABYRINTH & ext::Maze::WEST ) if ( x == this->m_size.x - 1 ) voxel = atlas.wall;
|
||||||
|
if ( LABYRINTH & ext::Maze::NORTH ) if ( z == 0 ) voxel = atlas.wall;
|
||||||
|
if ( LABYRINTH & ext::Maze::SOUTH ) if ( z == this->m_size.z - 1 ) voxel = atlas.wall;
|
||||||
|
if ( LABYRINTH & ext::Maze::FLOOR ) if ( y == 0 ) voxel = atlas.floor;
|
||||||
|
if ( LABYRINTH & ext::Maze::CEIL ) if ( y == this->m_size.y - 1 ) voxel = atlas.ceiling;
|
||||||
|
|
||||||
|
/*
|
||||||
// if ( (x) % (this->m_size.x / partitions) == 0 ) voxel = atlas.wall;
|
// if ( (x) % (this->m_size.x / partitions) == 0 ) voxel = atlas.wall;
|
||||||
// if ( (z) % (this->m_size.z / partitions) == 0 ) voxel = atlas.wall;
|
// if ( (z) % (this->m_size.z / partitions) == 0 ) voxel = atlas.wall;
|
||||||
if ( (x + 1) % (this->m_size.x / partitions) == 0 ) voxel = atlas.wall;
|
if ( (x + 1) % (this->m_size.x / partitions) == 0 ) voxel = atlas.wall;
|
||||||
@ -195,7 +224,7 @@ void ext::TerrainGenerator::generate( ext::Region& region ){
|
|||||||
} else {
|
} else {
|
||||||
// if ( x == half_x && y == 4 && z == half_z ) voxel = atlas.lava;
|
// if ( x == half_x && y == 4 && z == half_z ) voxel = atlas.lava;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
auto light = ext::TerrainVoxel::atlas(voxel).light();
|
auto light = ext::TerrainVoxel::atlas(voxel).light();
|
||||||
if ( light < 0x1111 ) light = 0x1111;
|
if ( light < 0x1111 ) light = 0x1111;
|
||||||
|
|
||||||
@ -226,7 +255,7 @@ void ext::TerrainGenerator::writeToFile() {
|
|||||||
|
|
||||||
uf::Serializer serializer;
|
uf::Serializer serializer;
|
||||||
std::string base = this->m_terrain ? this->m_terrain->getComponent<uf::Serializer>()["_config"]["hash"].asString() : "UNKNOWN";
|
std::string base = this->m_terrain ? this->m_terrain->getComponent<uf::Serializer>()["_config"]["hash"].asString() : "UNKNOWN";
|
||||||
std::string filename = "./data/save/regions/" + base + "/" + std::to_string(this->m_location.x) + "." + std::to_string(this->m_location.y) + "." + std::to_string(this->m_location.z) + ".json";
|
std::string filename = "./data/save/" + base + "/regions/" + std::to_string(this->m_location.x) + "." + std::to_string(this->m_location.y) + "." + std::to_string(this->m_location.z) + ".json";
|
||||||
{
|
{
|
||||||
// encode as base64, json safe
|
// encode as base64, json safe
|
||||||
serializer["voxels"]["id"]["base64"] = uf::base64::encode( this->m_voxels.id.rle );
|
serializer["voxels"]["id"]["base64"] = uf::base64::encode( this->m_voxels.id.rle );
|
||||||
@ -347,7 +376,7 @@ ext::TerrainVoxel::light_t ext::TerrainGenerator::getLight( int x, int y, int z
|
|||||||
ext::Region* region = terrain.at( location );
|
ext::Region* region = terrain.at( location );
|
||||||
if ( !region ) {
|
if ( !region ) {
|
||||||
// std::cout << "Out of bounds Region("<< location.x << ", " << location.y << ", " << location.z <<") @ getLight( " << x << ", " << y << ", " << z << " ) " << std::endl;
|
// std::cout << "Out of bounds Region("<< location.x << ", " << location.y << ", " << location.z <<") @ getLight( " << x << ", " << y << ", " << z << " ) " << std::endl;
|
||||||
return 0x0000;
|
return 0xFFFF;
|
||||||
}
|
}
|
||||||
return region->getComponent<ext::TerrainGenerator>().getLight( x, y, z );
|
return region->getComponent<ext::TerrainGenerator>().getLight( x, y, z );
|
||||||
}
|
}
|
||||||
|
|||||||
436
ext/world/terrain/maze.cpp
Normal file
436
ext/world/terrain/maze.cpp
Normal file
@ -0,0 +1,436 @@
|
|||||||
|
#include "Maze.h"
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <set>
|
||||||
|
#include <tuple>
|
||||||
|
#include <map>
|
||||||
|
#include <random>
|
||||||
|
#include <time.h>
|
||||||
|
#include <numeric>
|
||||||
|
|
||||||
|
void ext::Maze::initialize(int columns, int rows, int floors, double horizontal_bias, double vertical_bias) {
|
||||||
|
/* The Maze class contructor
|
||||||
|
* Input:
|
||||||
|
* int columns - Set the number of columns in the maze. Correspond to the X axis (length).
|
||||||
|
* int rows - Set the number of rows in the maze. Correspond to the Y axis (width).
|
||||||
|
* int floors - Set the number of floors in the maze. Correspond to the Z axis (height).
|
||||||
|
*
|
||||||
|
* All three must be larger than 1.
|
||||||
|
*
|
||||||
|
* double horizontal_bias - Sets the likelihood of a passage between rooms on the same row.
|
||||||
|
* double vertical_bias - Sets the likelihood of a passage between rooms in the same column.
|
||||||
|
*
|
||||||
|
* Both must be between 0 and 1 exclusive.
|
||||||
|
* Higher number gives higher likelihood of a passage.
|
||||||
|
*
|
||||||
|
* Output:
|
||||||
|
* Maze object
|
||||||
|
*/
|
||||||
|
if (rows < 1 || columns < 1 || floors < 1){
|
||||||
|
std::cout << "A maze must have dimensions greater than zero.\n";
|
||||||
|
throw std::invalid_argument("A maze must have dimensions greater than zero.\n");
|
||||||
|
}
|
||||||
|
LENGTH = columns;
|
||||||
|
WIDTH = rows;
|
||||||
|
HEIGHT = floors;
|
||||||
|
|
||||||
|
if (horizontal_bias <= 0 || horizontal_bias >= 1 || vertical_bias <= 0 || vertical_bias >= 1){
|
||||||
|
std::cout << "Biases must be between 0 and 1 exclusive.\n";
|
||||||
|
throw std::invalid_argument("Biases must be between 0 and 1 exclusive.\n");
|
||||||
|
}
|
||||||
|
EAST_WALL_THRESHOLD = horizontal_bias;
|
||||||
|
SOUTH_WALL_THRESHOLD = vertical_bias;
|
||||||
|
|
||||||
|
//Creating a vector of all rooms.
|
||||||
|
cells.assign(LENGTH*WIDTH*HEIGHT, -1);
|
||||||
|
|
||||||
|
// seed
|
||||||
|
seed = time(NULL);
|
||||||
|
|
||||||
|
// Give each room a different set number (ascending).
|
||||||
|
// (Which is used during maze generation)
|
||||||
|
std::iota(std::begin(cells), std::end(cells), 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
int ext::Maze::get(int i, int col, int floor){
|
||||||
|
// Gets the value stored at row i, column j, level k, in cells;
|
||||||
|
return cells.at(col + LENGTH*i + LENGTH*WIDTH*floor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ext::Maze::set(int row, int col, int floor, int val){
|
||||||
|
// Set the value of cell at (row, col, floor) to val
|
||||||
|
cells[col + LENGTH*row + LENGTH*WIDTH*floor] = val;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ext::Maze::build(){
|
||||||
|
/* This function generates a maze.
|
||||||
|
*
|
||||||
|
* It uses sets to keep track of areas created by removing walls.
|
||||||
|
* Initially each room has it's own set, known by it's room value.
|
||||||
|
* The room sets grows as walls are removed and sets joined.
|
||||||
|
* To keep track, each room value is mapped to itself, or the room value it has joined.
|
||||||
|
* The mapping and sets are reset at each floor.
|
||||||
|
*
|
||||||
|
* Summary:
|
||||||
|
* It goes through each cell, removing the walls randomly.
|
||||||
|
* (First WEST to EAST, then NORTH to SOUTH, then DOWN to UP.
|
||||||
|
* Just as one write in English, putting each new page on top of the other)
|
||||||
|
* If a wall is between two rooms in the same set, it is not removed.
|
||||||
|
*
|
||||||
|
* Each room value denote which set it belongs to
|
||||||
|
*
|
||||||
|
* Each wall removed is stored in as a passage between the two rooms.
|
||||||
|
* (The rooms position is stored in ext::Maze::passages)
|
||||||
|
*
|
||||||
|
* If the current room value is not mapped, then map it to itself.
|
||||||
|
* Set current room value to it's mapped value.
|
||||||
|
*
|
||||||
|
* Add the room to the set given by room value
|
||||||
|
*
|
||||||
|
* If a EASTERN wall is removed, then:
|
||||||
|
* - The set of the connected room is added to the set of this room.
|
||||||
|
* - The set of the connected room is deleted.
|
||||||
|
* - Map the connected room value to the current room value.
|
||||||
|
* - Change the connected room value to the current room value.
|
||||||
|
* - Add connected room to set.
|
||||||
|
*
|
||||||
|
* If a SOUTHERN wall is removed, then:
|
||||||
|
* - Change the connected room value to the current room value.
|
||||||
|
* - Add the connected room to set.
|
||||||
|
*
|
||||||
|
* After all the rooms of the current floor has been visited:
|
||||||
|
* - Go through each set, pick a random room, and make a passage up.
|
||||||
|
* - Clear the sets, and mapping.
|
||||||
|
*
|
||||||
|
* On the last floor, each set on each row must have a passage south.
|
||||||
|
*
|
||||||
|
* On the last row on the last floor, passages between unconnected sets are removed.
|
||||||
|
*
|
||||||
|
* Finally each room value is set according to which walls remain. (Including floor and ceiling)
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// The room sets
|
||||||
|
std::map<std::size_t, std::vector<pos>> room_set;
|
||||||
|
|
||||||
|
// The mapping of connected rooms
|
||||||
|
std::map<int, int> merged_room_sets;
|
||||||
|
|
||||||
|
|
||||||
|
int room_value;
|
||||||
|
int east_room_value;
|
||||||
|
int south_room_value;
|
||||||
|
|
||||||
|
// To get different mazes each time
|
||||||
|
srand(seed);
|
||||||
|
|
||||||
|
for (int floor = 0; floor < HEIGHT - 1; floor++){ // Go through each floor but the last.
|
||||||
|
|
||||||
|
for (int row = 0; row < WIDTH; row++){ // Go through each row
|
||||||
|
|
||||||
|
for(int col = 0; col < LENGTH; col++){ // Go through each element in row (Each column)
|
||||||
|
|
||||||
|
room_value = get(row, col, floor);
|
||||||
|
|
||||||
|
// If this room value is mapped to another, get that value: else map value to itself
|
||||||
|
if(merged_room_sets.find(room_value) != merged_room_sets.end()){
|
||||||
|
room_value = merged_room_sets[room_value];
|
||||||
|
} else {
|
||||||
|
merged_room_sets[room_value] = room_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not the last cell in row, store eastern room value
|
||||||
|
if (col<LENGTH-1){
|
||||||
|
//Is the eastern room value mapped?
|
||||||
|
east_room_value = (merged_room_sets.find(get(row, col+1, floor))!=merged_room_sets.end()) ?
|
||||||
|
// Yes: Use mapped value
|
||||||
|
merged_room_sets[get(row, col+1, floor)] :
|
||||||
|
// No: Use stored value
|
||||||
|
get(row, col+1, floor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not the last row, store southern room value
|
||||||
|
if (row < WIDTH-1){
|
||||||
|
//Is the southern room value mapped?
|
||||||
|
south_room_value = (merged_room_sets.find(get(row + 1, col, floor))!=merged_room_sets.end()) ?
|
||||||
|
|
||||||
|
// Yes: Use mapped value
|
||||||
|
merged_room_sets[get(row + 1, col, floor)] :
|
||||||
|
|
||||||
|
// No: Use stored value
|
||||||
|
get(row + 1, col, floor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add this room to the set of room value
|
||||||
|
room_set[room_value].push_back(std::make_tuple(row, col, floor));
|
||||||
|
|
||||||
|
//If last room in row, skip (don't remove eastern boundary wall)
|
||||||
|
if(col == LENGTH - 1){}
|
||||||
|
|
||||||
|
// If this and eastern room not in same set, maybe remove wall
|
||||||
|
else if((rand() < EAST_WALL_THRESHOLD * RAND_MAX) && (room_value != east_room_value)){
|
||||||
|
// EASTERN WALL REMOVED
|
||||||
|
|
||||||
|
// Get set of eastern room
|
||||||
|
std::vector<pos> next_room_position_set = room_set[get(row, col+1, floor)];
|
||||||
|
|
||||||
|
// Add set of eastern room to this set.
|
||||||
|
room_set[room_value].insert(room_set[room_value].end(), next_room_position_set.begin(), next_room_position_set.end() );
|
||||||
|
|
||||||
|
// Remove eastern room set
|
||||||
|
room_set.erase(get(row, col+1, floor));
|
||||||
|
|
||||||
|
// Map eastern room value to this room value.
|
||||||
|
merged_room_sets[get(row,col+1,floor)] = room_value;
|
||||||
|
|
||||||
|
// Set eastern room value to this
|
||||||
|
set(row, col+1, floor, room_value);
|
||||||
|
|
||||||
|
// Add the new horizontal passage created.
|
||||||
|
passages.push_back(std::make_tuple(row, col, floor, row, col+1, floor));
|
||||||
|
|
||||||
|
} // Else the eastern wall remain
|
||||||
|
|
||||||
|
// Don't remove southern boundary wall.
|
||||||
|
if(row == WIDTH - 1){}
|
||||||
|
|
||||||
|
// If current and southern room is not in same set, then maybe remove wall
|
||||||
|
else if(rand() < SOUTH_WALL_THRESHOLD * RAND_MAX && room_value != south_room_value){
|
||||||
|
|
||||||
|
// Set southern room value to this one
|
||||||
|
set(row+1, col, floor, room_value);
|
||||||
|
|
||||||
|
// Add the newly created vertical passage.
|
||||||
|
passages.push_back(std::make_tuple(row, col, floor, row+1, col, floor));
|
||||||
|
} //Else wall down remain
|
||||||
|
|
||||||
|
} // All rooms in this row has been visited
|
||||||
|
|
||||||
|
} // All rows on this floor has been visited
|
||||||
|
|
||||||
|
// Clear room set mapping
|
||||||
|
merged_room_sets.clear();
|
||||||
|
|
||||||
|
// Go through all sets this floor
|
||||||
|
for(auto entry : room_set)
|
||||||
|
{
|
||||||
|
auto group = entry.second;
|
||||||
|
int x, y, z;
|
||||||
|
|
||||||
|
// Picking out a random room in each group
|
||||||
|
// Room position saved in matrix notation (i, j, k = y, x, z)
|
||||||
|
std::tie(y, x, z) = group.at(rand() % group.size());
|
||||||
|
|
||||||
|
// Only one passage up for each group. To avoid graph cycles
|
||||||
|
passages.push_back(std::make_tuple(y, x, z, y, x, z+1));
|
||||||
|
}
|
||||||
|
// Passages up have been set. Clearing room groups for next floor.
|
||||||
|
room_set.clear();
|
||||||
|
|
||||||
|
}// Finished with all but last floor
|
||||||
|
|
||||||
|
|
||||||
|
int floor = HEIGHT - 1;
|
||||||
|
|
||||||
|
std::set<int> can_go_south;
|
||||||
|
|
||||||
|
//deal with last floor, but leave the last row
|
||||||
|
for(int row = 0; row < WIDTH - 1; row++){
|
||||||
|
|
||||||
|
for(int col = 0; col<LENGTH; col++){
|
||||||
|
room_value = get(row, col, floor);
|
||||||
|
|
||||||
|
// If this room value is mapped to another, get that value: else map value to itself
|
||||||
|
if(merged_room_sets.find(room_value) != merged_room_sets.end()){
|
||||||
|
room_value = merged_room_sets[room_value];
|
||||||
|
} else {
|
||||||
|
merged_room_sets[room_value] = room_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not the last cell in row, get eastern room value
|
||||||
|
if (col<LENGTH-1){
|
||||||
|
east_room_value = (merged_room_sets.find(get(row, col+1, floor)) != merged_room_sets.end()) ?
|
||||||
|
merged_room_sets[get(row, col+1, floor)] : get(row, col+1, floor);
|
||||||
|
}
|
||||||
|
// Get southern room value
|
||||||
|
south_room_value = (merged_room_sets.find(get(row + 1, col, floor)) != merged_room_sets.end()) ?
|
||||||
|
merged_room_sets[get(row + 1, col, floor)] : get(row + 1, col, floor);
|
||||||
|
|
||||||
|
// Try and make passage east
|
||||||
|
if(col == LENGTH -1){}
|
||||||
|
else if((rand() < EAST_WALL_THRESHOLD * RAND_MAX) && (room_value != east_room_value)){
|
||||||
|
|
||||||
|
passages.push_back(std::make_tuple(row, col, floor, row, col+1, floor));
|
||||||
|
merged_room_sets[east_room_value] = room_value;
|
||||||
|
set(row, col+1, floor, room_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try and make passage south
|
||||||
|
if( ( (rand() < SOUTH_WALL_THRESHOLD * RAND_MAX)
|
||||||
|
|| (can_go_south.find(room_value) == can_go_south.end()) ) // Make sure all sets this row have a passage down
|
||||||
|
&& (room_value != south_room_value))
|
||||||
|
{
|
||||||
|
passages.push_back(std::make_tuple(row, col, floor, row+1, col, floor));
|
||||||
|
merged_room_sets[south_room_value] = room_value;
|
||||||
|
set(row+1, col, floor, room_value);
|
||||||
|
can_go_south.insert(room_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
can_go_south.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go through the last row on the last floor
|
||||||
|
int row = WIDTH - 1;
|
||||||
|
for (int col = 0; col < LENGTH - 1; col++)
|
||||||
|
{
|
||||||
|
// Get room values
|
||||||
|
room_value = get(row, col, floor);
|
||||||
|
|
||||||
|
if(merged_room_sets.find(room_value) != merged_room_sets.end()){
|
||||||
|
room_value = merged_room_sets[room_value];
|
||||||
|
} else {
|
||||||
|
merged_room_sets[room_value] = room_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get eastern room value
|
||||||
|
east_room_value = (merged_room_sets.find(get(row, col+1, floor))!=merged_room_sets.end()) ?
|
||||||
|
merged_room_sets[get(row, col+1, floor)] : get(row, col+1, floor);
|
||||||
|
|
||||||
|
// If this and eastern room is in different sets
|
||||||
|
if(room_value != east_room_value)
|
||||||
|
{
|
||||||
|
// Make passage and merge sets
|
||||||
|
passages.push_back(std::make_tuple(row, col, floor, row, col+1, floor));
|
||||||
|
merged_room_sets[east_room_value] = room_value;
|
||||||
|
set(row, col+1, floor, room_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the room values based on remaining walls (and floors & ceilings)
|
||||||
|
ext::Maze::calculate();
|
||||||
|
};
|
||||||
|
|
||||||
|
void ext::Maze::calculate(){
|
||||||
|
/* This function set each room value according to the remaining walls in that room.
|
||||||
|
* The walls are represented as bits of a 8-bit int.
|
||||||
|
* Floor = 0000 0001
|
||||||
|
* Eastern wall = 0000 0010
|
||||||
|
* Northern wall = 0000 0100
|
||||||
|
* Western wall = 0000 1000
|
||||||
|
* Southern wall = 0001 0000
|
||||||
|
* Ceiling = 0010 0000
|
||||||
|
*/
|
||||||
|
|
||||||
|
cells.clear();
|
||||||
|
|
||||||
|
// 63 = 0011 1111 = all walls, floor, and ceiling
|
||||||
|
cells.assign(LENGTH*WIDTH*HEIGHT, 63);
|
||||||
|
|
||||||
|
int row_from, row_to,
|
||||||
|
col_from, col_to,
|
||||||
|
lvl_from, lvl_to;
|
||||||
|
|
||||||
|
uint8_t cell_num_to;
|
||||||
|
uint8_t cell_num_from;
|
||||||
|
|
||||||
|
// Go through all passages
|
||||||
|
for(auto pass : passages){
|
||||||
|
std::tie(row_from, col_from, lvl_from,
|
||||||
|
row_to, col_to, lvl_to ) = pass;
|
||||||
|
|
||||||
|
cell_num_from = get(row_from, col_from, lvl_from);
|
||||||
|
cell_num_to = get(row_to, col_to, lvl_to);
|
||||||
|
|
||||||
|
// North to South passage
|
||||||
|
if(row_from != row_to){
|
||||||
|
set(row_from, col_from, lvl_from, cell_num_from & ~(SOUTH));
|
||||||
|
set(row_to, col_to, lvl_to, cell_num_to & ~(NORTH));
|
||||||
|
|
||||||
|
// West to East passage
|
||||||
|
} else if (col_from != col_to){
|
||||||
|
set(row_from, col_from, lvl_from, cell_num_from & ~(EAST));
|
||||||
|
set(row_to, col_to, lvl_to, cell_num_to & ~(WEST));
|
||||||
|
|
||||||
|
// Passage Up
|
||||||
|
} else if(lvl_from != lvl_to){
|
||||||
|
set(row_from, col_from, lvl_from, cell_num_from & ~(CEIL));
|
||||||
|
set(row_to, col_to, lvl_to, cell_num_to & ~(FLOOR));
|
||||||
|
}
|
||||||
|
// Else the passage is in several directions and in this case discarded
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ext::Maze::print(){
|
||||||
|
/* Print the maze to std::cout
|
||||||
|
*
|
||||||
|
* Prints floor in ascending order (top floor last)
|
||||||
|
* | - represent vertical and horizontal walls respectively
|
||||||
|
* + is the room corners
|
||||||
|
* U D B represent stairs up, down, and both, respectively
|
||||||
|
*
|
||||||
|
* ext::Maze::passages s used, so remember to run ext::Maze::build first.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Maze map canvas
|
||||||
|
std::string map_drawing[2 * WIDTH + 1][HEIGHT];
|
||||||
|
|
||||||
|
std::string wall_row = "+";
|
||||||
|
std::string room_row = "|";
|
||||||
|
|
||||||
|
// Make a row of walls and rooms
|
||||||
|
for (int col = 0; col < LENGTH; col++){
|
||||||
|
wall_row.append("-+");
|
||||||
|
room_row.append(" |");
|
||||||
|
}
|
||||||
|
// Drawing initial map
|
||||||
|
for (int floor = 0; floor < HEIGHT; floor++){
|
||||||
|
map_drawing[2*WIDTH][floor] = wall_row;
|
||||||
|
|
||||||
|
for (int row = 0; row < WIDTH; row++){
|
||||||
|
map_drawing[2*row][floor] = wall_row;
|
||||||
|
map_drawing[2*row + 1][floor] = room_row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Making passages
|
||||||
|
for ( auto pass : passages){
|
||||||
|
int row_from, row_to,
|
||||||
|
col_from, col_to,
|
||||||
|
lvl_from, lvl_to;
|
||||||
|
|
||||||
|
std::tie(row_from, col_from, lvl_from,
|
||||||
|
row_to, col_to, lvl_to) = pass;
|
||||||
|
|
||||||
|
// North - South passage
|
||||||
|
if(row_from != row_to){
|
||||||
|
map_drawing[2 * row_from + 2][lvl_from].replace(2 * col_from + 1, 1, " ");
|
||||||
|
|
||||||
|
// West - East passage
|
||||||
|
} else if (col_from != col_to){
|
||||||
|
map_drawing[2*row_from + 1][lvl_from].replace(2 * col_from + 2, 1, " ");
|
||||||
|
|
||||||
|
// Up - Down passage
|
||||||
|
} else if (lvl_from != lvl_to){
|
||||||
|
|
||||||
|
// If this room have a passage down, you can go up and down from it
|
||||||
|
if (map_drawing[2 * row_from + 1][lvl_from].at(2 * col_from + 1) == 'D'){
|
||||||
|
map_drawing[2 * row_from + 1][lvl_from].replace(2 * col_from + 1, 1, "B");
|
||||||
|
} else {
|
||||||
|
map_drawing[2 * row_from + 1][lvl_from].replace(2 * col_from + 1, 1, "U");
|
||||||
|
}
|
||||||
|
|
||||||
|
// The passages should come in order, so floor above have no passages up
|
||||||
|
map_drawing[2 * row_to + 1][lvl_to].replace(2 * col_to + 1, 1, "D");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Printing the maze, one row at a time.
|
||||||
|
for (int floor = 0; floor < HEIGHT; floor++){
|
||||||
|
for (int row = 0; row < 2*WIDTH+1; row++){
|
||||||
|
std::cout << map_drawing[row][floor] << std::endl;
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
38
ext/world/terrain/maze.h
Normal file
38
ext/world/terrain/maze.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
#include<vector>
|
||||||
|
#include<iostream>
|
||||||
|
|
||||||
|
namespace ext {
|
||||||
|
class Maze {
|
||||||
|
protected:
|
||||||
|
double EAST_WALL_THRESHOLD;
|
||||||
|
double SOUTH_WALL_THRESHOLD;
|
||||||
|
std::vector< std::tuple<int, int, int, int, int, int> > passages;
|
||||||
|
typedef std::tuple<int, int, int> pos;
|
||||||
|
|
||||||
|
int get(int, int, int);
|
||||||
|
void set(int, int, int, int);
|
||||||
|
void calculate();
|
||||||
|
public:
|
||||||
|
int LENGTH;
|
||||||
|
int WIDTH;
|
||||||
|
int HEIGHT;
|
||||||
|
size_t seed;
|
||||||
|
|
||||||
|
static const uint8_t FLOOR = 1; // 00 00 00 01;
|
||||||
|
static const uint8_t EAST = 2; // 00 00 00 10;
|
||||||
|
static const uint8_t NORTH = 4; // 00 00 01 00;
|
||||||
|
static const uint8_t WEST = 8; // 00 00 10 00;
|
||||||
|
static const uint8_t SOUTH = 16; // 00 01 00 00;
|
||||||
|
static const uint8_t CEIL = 32; // 00 10 00 00;
|
||||||
|
|
||||||
|
std::vector<int> cells;
|
||||||
|
|
||||||
|
int& operator()(int row, int col, int floor){
|
||||||
|
return cells.at(col + LENGTH*row + LENGTH*WIDTH*floor);
|
||||||
|
}
|
||||||
|
void initialize(int, int, int, double, double);
|
||||||
|
void build();
|
||||||
|
void print();
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -52,28 +52,17 @@ void ext::Terrain::initialize() {
|
|||||||
ext::Object::initialize();
|
ext::Object::initialize();
|
||||||
uf::Serializer& metadata = this->getComponent<uf::Serializer>();
|
uf::Serializer& metadata = this->getComponent<uf::Serializer>();
|
||||||
|
|
||||||
|
std::size_t seed;
|
||||||
|
if ( metadata["terrain"]["seed"].isUInt64() ) {
|
||||||
|
seed = metadata["terrain"]["seed"].asUInt64();
|
||||||
|
} else if ( metadata["terrain"]["seed"].isString() ) {
|
||||||
|
seed = std::hash<std::string>{}( metadata["terrain"]["seed"].asString() );
|
||||||
|
}
|
||||||
{
|
{
|
||||||
std::size_t seed;
|
|
||||||
if ( metadata["terrain"]["seed"].isUInt64() ) {
|
|
||||||
seed = metadata["terrain"]["seed"].asUInt64();
|
|
||||||
} else if ( metadata["terrain"]["seed"].isString() ) {
|
|
||||||
seed = std::hash<std::string>{}( metadata["terrain"]["seed"].asString() );
|
|
||||||
}
|
|
||||||
std::cout << "Seed: " << seed << std::endl;
|
std::cout << "Seed: " << seed << std::endl;
|
||||||
ext::TerrainGenerator::noise.seed( seed );
|
ext::TerrainGenerator::noise.seed( seed );
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Test */
|
|
||||||
/*
|
|
||||||
{
|
|
||||||
std::string seed = "170271159441424384 170271159441424384";
|
|
||||||
std::size_t hash = std::hash<std::string>{}(seed);
|
|
||||||
uf::PerlinNoise& noise = ext::TerrainGenerator::noise;
|
|
||||||
noise.seed(hash);
|
|
||||||
double y = noise.sample(pod::Vector3d{ 0.01, 0.01, 0.01 });
|
|
||||||
std::cout << seed << ": " << y << std::endl;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
if ( metadata["terrain"]["raytrace"].asBool() ) {
|
if ( metadata["terrain"]["raytrace"].asBool() ) {
|
||||||
this->addComponent<ext::vulkan::RTGraphic>();
|
this->addComponent<ext::vulkan::RTGraphic>();
|
||||||
@ -90,11 +79,26 @@ void ext::Terrain::initialize() {
|
|||||||
metadata["_config"]["hash"] = uf::string::sha256( input );
|
metadata["_config"]["hash"] = uf::string::sha256( input );
|
||||||
|
|
||||||
try {
|
try {
|
||||||
std::string save = "./data/save/regions/" + metadata["_config"]["hash"].asString();
|
std::string save = "./data/save/" + metadata["_config"]["hash"].asString() + "/";
|
||||||
int status = mkdir(save.c_str());
|
int status = mkdir(save.c_str());
|
||||||
} catch ( ... ) {
|
} catch ( ... ) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
std::string save = "./data/save/" + metadata["_config"]["hash"].asString() + "/regions/";
|
||||||
|
int status = mkdir(save.c_str());
|
||||||
|
} catch ( ... ) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup maze
|
||||||
|
{
|
||||||
|
ext::Maze& maze = this->getComponent<ext::Maze>();
|
||||||
|
maze.initialize(16, 16, 1, 0.5, 0.5);
|
||||||
|
maze.seed = seed;
|
||||||
|
maze.build();
|
||||||
|
maze.print();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( this->hasComponent<ext::vulkan::RTGraphic>() ) {
|
if ( this->hasComponent<ext::vulkan::RTGraphic>() ) {
|
||||||
@ -325,7 +329,7 @@ void ext::Terrain::tick() {
|
|||||||
}
|
}
|
||||||
/* Sort by closest to farthest */ {
|
/* Sort by closest to farthest */ {
|
||||||
const ext::World& world = this->getRootParent<ext::World>();
|
const ext::World& world = this->getRootParent<ext::World>();
|
||||||
const ext::Player& player = world.getController<ext::Player>();
|
const ext::Object& player = world.getController<ext::Object>();
|
||||||
const pod::Vector3& position = player.getComponent<pod::Transform<>>().position;
|
const pod::Vector3& position = player.getComponent<pod::Transform<>>().position;
|
||||||
std::sort( regions.begin(), regions.end(), [&]( const uf::Entity* l, const uf::Entity* r ){
|
std::sort( regions.begin(), regions.end(), [&]( const uf::Entity* l, const uf::Entity* r ){
|
||||||
if ( !l ) return false; if ( !r ) return true;
|
if ( !l ) return false; if ( !r ) return true;
|
||||||
@ -359,7 +363,7 @@ void ext::Terrain::tick() {
|
|||||||
}
|
}
|
||||||
/* Sort by closest to farthest */ {
|
/* Sort by closest to farthest */ {
|
||||||
const ext::World& world = this->getRootParent<ext::World>();
|
const ext::World& world = this->getRootParent<ext::World>();
|
||||||
const ext::Player& player = world.getController<ext::Player>();
|
const ext::Object& player = world.getController<ext::Object>();
|
||||||
const pod::Vector3& position = player.getComponent<pod::Transform<>>().position;
|
const pod::Vector3& position = player.getComponent<pod::Transform<>>().position;
|
||||||
std::sort( regions.begin(), regions.end(), [&]( const uf::Entity* l, const uf::Entity* r ){
|
std::sort( regions.begin(), regions.end(), [&]( const uf::Entity* l, const uf::Entity* r ){
|
||||||
if ( !l ) return false; if ( !r ) return true;
|
if ( !l ) return false; if ( !r ) return true;
|
||||||
@ -447,7 +451,7 @@ void ext::Terrain::tick() {
|
|||||||
|
|
||||||
pod::Thread& mainThread = uf::thread::has("Main") ? uf::thread::get("Main") : uf::thread::create( "Main", false, true );
|
pod::Thread& mainThread = uf::thread::has("Main") ? uf::thread::get("Main") : uf::thread::create( "Main", false, true );
|
||||||
if ( metadata["terrain"]["state"] == "open" &&
|
if ( metadata["terrain"]["state"] == "open" &&
|
||||||
( (generationTimer.running() && generationTimer.elapsed().asDouble() > 3) || !generationTimer.running() )
|
( (generationTimer.running() && generationTimer.elapsed().asDouble() > 1) || !generationTimer.running() )
|
||||||
) { metadata["terrain"]["state"] = "resolving:" + metadata["terrain"]["state"].asString();
|
) { metadata["terrain"]["state"] = "resolving:" + metadata["terrain"]["state"].asString();
|
||||||
if ( !generationTimer.running() ) generationTimer.start();
|
if ( !generationTimer.running() ) generationTimer.start();
|
||||||
else generationTimer.reset();
|
else generationTimer.reset();
|
||||||
@ -489,6 +493,7 @@ void ext::Terrain::tick() {
|
|||||||
|
|
||||||
bool resolved = !metadata["terrain"]["modified"].asBool();
|
bool resolved = !metadata["terrain"]["modified"].asBool();
|
||||||
if ( !locations.empty() ) {
|
if ( !locations.empty() ) {
|
||||||
|
std::cout << "GENERATING" << std::endl;
|
||||||
this->generate();
|
this->generate();
|
||||||
for ( pod::Vector3i& location : locations ) this->degenerate(location);
|
for ( pod::Vector3i& location : locations ) this->degenerate(location);
|
||||||
resolved = false;
|
resolved = false;
|
||||||
|
|||||||
@ -8,7 +8,9 @@
|
|||||||
#include <uf/utils/math/physics.h>
|
#include <uf/utils/math/physics.h>
|
||||||
|
|
||||||
#include "../../object/object.h"
|
#include "../../object/object.h"
|
||||||
|
|
||||||
#include "region.h"
|
#include "region.h"
|
||||||
|
#include "maze.h"
|
||||||
|
|
||||||
namespace ext {
|
namespace ext {
|
||||||
class EXT_API Terrain : public ext::Object {
|
class EXT_API Terrain : public ext::Object {
|
||||||
|
|||||||
@ -476,7 +476,7 @@ ext::TerrainVoxelWall::TerrainVoxelWall() : ext::TerrainVoxel( 2, true, true, {1
|
|||||||
ext::TerrainVoxelCeiling::TerrainVoxelCeiling() : ext::TerrainVoxel( 3, true, true, {2, 1}, 0x0000 ) {}
|
ext::TerrainVoxelCeiling::TerrainVoxelCeiling() : ext::TerrainVoxel( 3, true, true, {2, 1}, 0x0000 ) {}
|
||||||
ext::TerrainVoxelPillar::TerrainVoxelPillar() : ext::TerrainVoxel( 4, true, true, {3, 0}, 0x0000 ) {}
|
ext::TerrainVoxelPillar::TerrainVoxelPillar() : ext::TerrainVoxel( 4, true, true, {3, 0}, 0x0000 ) {}
|
||||||
ext::TerrainVoxelStair::TerrainVoxelStair() : ext::TerrainVoxel( 5, true, true, {2, 0}, 0x0000 ) {}
|
ext::TerrainVoxelStair::TerrainVoxelStair() : ext::TerrainVoxel( 5, true, true, {2, 0}, 0x0000 ) {}
|
||||||
ext::TerrainVoxelLava::TerrainVoxelLava() : ext::TerrainVoxel( 6, true, true, {0, 2}, 0x66FF ) {}
|
ext::TerrainVoxelLava::TerrainVoxelLava() : ext::TerrainVoxel( 6, true, true, {0, 2}, 0xFFFF ) {}
|
||||||
|
|
||||||
//
|
//
|
||||||
const std::vector<ext::TerrainVoxel>& ext::TerrainVoxel::atlas() {
|
const std::vector<ext::TerrainVoxel>& ext::TerrainVoxel::atlas() {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user