Replaced LibNoise in MapGen with FastNoise2 for S U P E R S P E E D
parent
c2831a84bc
commit
10edb1eb60
|
@ -48,6 +48,7 @@ include_directories(
|
||||||
${ENET_HEADERS}
|
${ENET_HEADERS}
|
||||||
${NOISE_HEADERS}
|
${NOISE_HEADERS}
|
||||||
${PTHREAD_HEADERS}
|
${PTHREAD_HEADERS}
|
||||||
|
lib/fastnoise2/include
|
||||||
lib/catch2/include
|
lib/catch2/include
|
||||||
lib/gzip/include
|
lib/gzip/include
|
||||||
lib/cute_files/include
|
lib/cute_files/include
|
||||||
|
@ -106,6 +107,11 @@ else()
|
||||||
target_link_libraries(${MAIN_EXEC_NAME} assimp)
|
target_link_libraries(${MAIN_EXEC_NAME} assimp)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Build and Link FastNoise2
|
||||||
|
|
||||||
|
add_subdirectory(lib/fastnoise2)
|
||||||
|
target_link_libraries(${MAIN_EXEC_NAME} FastNoise)
|
||||||
|
|
||||||
# Link Lua 5.3.5
|
# Link Lua 5.3.5
|
||||||
find_library(LUA_LIB NAMES lua lua5.3)
|
find_library(LUA_LIB NAMES lua lua5.3)
|
||||||
target_link_libraries(${MAIN_EXEC_NAME} ${LUA_LIB})
|
target_link_libraries(${MAIN_EXEC_NAME} ${LUA_LIB})
|
||||||
|
|
|
@ -26,4 +26,4 @@
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
return StartGame(argc, argv);
|
return StartGame(argc, argv);
|
||||||
}
|
}
|
|
@ -57,15 +57,15 @@ ChunkMeshGenerator::ChunkMeshGenerator(MeshChunkDetails* meshDetails, LocalDefin
|
||||||
default: break;
|
default: break;
|
||||||
|
|
||||||
case MeshMod::OFFSET_X:
|
case MeshMod::OFFSET_X:
|
||||||
vis.x += blockOffsets[0].get(vec3(off) / 16.f) * mod.second;
|
// vis.x += blockOffsets[0][vec3(off) / 16.f] * mod.second;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MeshMod::OFFSET_Y:
|
case MeshMod::OFFSET_Y:
|
||||||
vis.y += blockOffsets[1].get(vec3(off) / 16.f) * mod.second;
|
// vis.y += blockOffsets[1][vec3(off) / 16.f] * mod.second;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MeshMod::OFFSET_Z:
|
case MeshMod::OFFSET_Z:
|
||||||
vis.z += blockOffsets[2].get(vec3(off) / 16.f) * mod.second;
|
// vis.z += blockOffsets[2][vec3(off) / 16.f] * mod.second;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,12 +97,13 @@ u16 ChunkMeshGenerator::getBlockAt(const ivec3& pos) {
|
||||||
}
|
}
|
||||||
|
|
||||||
u8vec4 ChunkMeshGenerator::getLightAt(const ivec3& pos) {
|
u8vec4 ChunkMeshGenerator::getLightAt(const ivec3& pos) {
|
||||||
auto dir = glm::floor(vec3(pos) / 16.f);
|
// auto dir = glm::floor(vec3(pos) / 16.f);
|
||||||
if (dir.x != 0 || dir.y != 0 || dir.z != 0) {
|
// if (dir.x != 0 || dir.y != 0 || dir.z != 0) {
|
||||||
u8 ind = static_cast<u8>(Vec::TO_ENUM.at(dir));
|
// u8 ind = static_cast<u8>(Vec::TO_ENUM.at(dir));
|
||||||
return adjacent[ind]->getLight(Space::Block::index(pos));
|
// return adjacent[ind]->getLight(Space::Block::index(pos));
|
||||||
}
|
// }
|
||||||
return chunk->getLight(Space::Block::index(pos));
|
// return chunk->getLight(Space::Block::index(pos));
|
||||||
|
return { 0, 0, 0, 15 };
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChunkMeshGenerator::addFaces(const vec3& offset,
|
void ChunkMeshGenerator::addFaces(const vec3& offset,
|
||||||
|
|
|
@ -57,13 +57,17 @@ std::shared_ptr<GuiComponent> GuiBuilder::createComponent(LuaGuiElement& elem, g
|
||||||
c = body;
|
c = body;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Util::hash("Rect"):c = GuiRect::fromSerialized(elem, textures, bounds);
|
case Util::hash("Rect"):
|
||||||
|
c = GuiRect::fromSerialized(elem, textures, bounds);
|
||||||
break;
|
break;
|
||||||
case Util::hash("Button"):c = GuiImageButton::fromSerialized(elem, textures, bounds);
|
case Util::hash("Button"):
|
||||||
|
c = GuiImageButton::fromSerialized(elem, textures, bounds);
|
||||||
break;
|
break;
|
||||||
case Util::hash("Text"):c = GuiText::fromSerialized(elem, textures, bounds);
|
case Util::hash("Text"):
|
||||||
|
c = GuiText::fromSerialized(elem, textures, bounds);
|
||||||
break;
|
break;
|
||||||
case Util::hash("Model"):c = GuiModel::fromSerialized(elem, textures, models, bounds);
|
case Util::hash("Model"):
|
||||||
|
c = GuiModel::fromSerialized(elem, textures, models, bounds);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,10 +11,8 @@
|
||||||
#include "world/dim/chunk/Chunk.h"
|
#include "world/dim/chunk/Chunk.h"
|
||||||
#include "world/dim/LocalDimension.h"
|
#include "world/dim/LocalDimension.h"
|
||||||
|
|
||||||
MeshGenStream::MeshGenStream(SubgamePtr game, LocalDimension& dimension) :
|
MeshGenStream::MeshGenStream(SubgamePtr game, LocalDimension& dimension): dimension(dimension),
|
||||||
dimension(dimension),
|
noiseSampler({ NoiseSample(u16vec3(16), 4), NoiseSample(u16vec3(16), 4), NoiseSample(u16vec3(16), 4) }) {
|
||||||
noiseSampler({ NoiseSample{ 16 }, NoiseSample{ 16 }, NoiseSample{ 16 }}) {
|
|
||||||
|
|
||||||
noise::module::Perlin offsetBaseNoise;
|
noise::module::Perlin offsetBaseNoise;
|
||||||
offsetBaseNoise.SetFrequency(8);
|
offsetBaseNoise.SetFrequency(8);
|
||||||
offsetBaseNoise.SetOctaveCount(3);
|
offsetBaseNoise.SetOctaveCount(3);
|
||||||
|
@ -24,10 +22,12 @@ MeshGenStream::MeshGenStream(SubgamePtr game, LocalDimension& dimension) :
|
||||||
offsetTurbulence.SetFrequency(4.0);
|
offsetTurbulence.SetFrequency(4.0);
|
||||||
offsetTurbulence.SetPower(0.125);
|
offsetTurbulence.SetPower(0.125);
|
||||||
|
|
||||||
|
generator = FastNoise::New<FastNoise::Constant>();
|
||||||
|
|
||||||
// 8 is just a random value to offset results
|
// 8 is just a random value to offset results
|
||||||
noiseSampler[0].populate([&](glm::ivec3 pos) { return offsetTurbulence.GetValue(pos.x + 8, pos.y, pos.z); });
|
// noiseSampler[0].generate(u16vec3(8, 0, 0), generator);
|
||||||
noiseSampler[1].populate([&](glm::ivec3 pos) { return offsetTurbulence.GetValue(pos.x, pos.y + 8, pos.z); });
|
// noiseSampler[1].generate(u16vec3(0, 8, 0), generator);
|
||||||
noiseSampler[2].populate([&](glm::ivec3 pos) { return offsetTurbulence.GetValue(pos.x, pos.y, pos.z + 8); });
|
// noiseSampler[2].generate(u16vec3(0, 0, 8), generator);
|
||||||
|
|
||||||
threads.reserve(THREADS);
|
threads.reserve(THREADS);
|
||||||
for (int i = 0; i < THREADS; i++) threads.emplace_back(*game.l(), noiseSampler);
|
for (int i = 0; i < THREADS; i++) threads.emplace_back(*game.l(), noiseSampler);
|
||||||
|
@ -93,7 +93,7 @@ void MeshGenStream::Thread::exec() {
|
||||||
assert(u.thisChunk);
|
assert(u.thisChunk);
|
||||||
for (int i = 0; i < u.adjacentChunks.size(); i++) assert(u.adjacentChunks[i]);
|
for (int i = 0; i < u.adjacentChunks.size(); i++) assert(u.adjacentChunks[i]);
|
||||||
ChunkMeshGenerator m(u.meshDetails, game.getDefs(), game.getBiomes(),
|
ChunkMeshGenerator m(u.meshDetails, game.getDefs(), game.getBiomes(),
|
||||||
std::move(u.thisChunk), std::move(u.adjacentChunks), offsetSamplers, u.detail);
|
std::move(u.thisChunk), std::move(u.adjacentChunks), offsetSamplers, ChunkMeshGenerator::Detail::HIGH);
|
||||||
empty = false;
|
empty = false;
|
||||||
u.busy = false;
|
u.busy = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,5 +70,7 @@ private:
|
||||||
std::unordered_set<glm::vec3, Vec::vec3> queuedMap;
|
std::unordered_set<glm::vec3, Vec::vec3> queuedMap;
|
||||||
|
|
||||||
constexpr const static u8 HIGH_DETAIL_RANGE = 4;
|
constexpr const static u8 HIGH_DETAIL_RANGE = 4;
|
||||||
|
|
||||||
|
FastNoise::SmartNode<> generator;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ ServerSubgame::ServerSubgame(const string& subgame, usize seed) :
|
||||||
lua(make_unique<ServerLuaParser>(*this)) {
|
lua(make_unique<ServerLuaParser>(*this)) {
|
||||||
|
|
||||||
if (subgame.empty()) throw std::runtime_error("No subgame specified.");
|
if (subgame.empty()) throw std::runtime_error("No subgame specified.");
|
||||||
else if (!cf_file_exists(subgamePath.data())) throw std::runtime_error("Subgame does not exist.");
|
if (!cf_file_exists(subgamePath.data())) throw std::runtime_error("Subgame does not exist.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerSubgame::init(WorldPtr world) {
|
void ServerSubgame::init(WorldPtr world) {
|
||||||
|
|
|
@ -6,31 +6,27 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <FastNoise/FastNoise.h>
|
||||||
#include <vector>
|
|
||||||
#include <memory>
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
#include <libnoise/module/modulebase.h>
|
|
||||||
|
|
||||||
class Structure;
|
class Structure;
|
||||||
|
|
||||||
struct BiomeDef {
|
struct BiomeDef {
|
||||||
std::string identifier = "";
|
u32 index = 0;
|
||||||
unsigned int index = 0;
|
string identifier = "";
|
||||||
std::unordered_map<std::string, unsigned short> tags{};
|
std::unordered_map<string, u16> tags {};
|
||||||
|
|
||||||
float temperature = 0;
|
f32 temperature = 0;
|
||||||
float humidity = 0;
|
f32 humidity = 0;
|
||||||
float roughness = 0;
|
f32 roughness = 0;
|
||||||
|
|
||||||
unsigned int topBlock = 0;
|
u16 topBlock = 0;
|
||||||
unsigned int soilBlock = 0;
|
u16 soilBlock = 0;
|
||||||
unsigned int rockBlock = 0;
|
u16 rockBlock = 0;
|
||||||
|
|
||||||
std::vector<noise::module::Module*> heightmap;
|
FastNoise::SmartNode<> heightmap;
|
||||||
std::vector<noise::module::Module*> volume;
|
FastNoise::SmartNode<> volume;
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Structure>> schematics;
|
vec<sptr<Structure>> schematics;
|
||||||
|
|
||||||
glm::vec3 tint {};
|
vec3 tint {};
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,251 +2,349 @@
|
||||||
|
|
||||||
#include "NoiseFromLua.h"
|
#include "NoiseFromLua.h"
|
||||||
|
|
||||||
std::vector<noise::module::Module*> NoiseFromLua::build(sol::table noise) {
|
FastNoise::SmartNode<> NoiseFromLua::parse(sol::table table) {
|
||||||
std::vector<noise::module::Module*> modules;
|
using namespace FastNoise;
|
||||||
parseNoise(modules, noise);
|
|
||||||
return std::move(modules);
|
|
||||||
}
|
|
||||||
|
|
||||||
noise::module::Module* NoiseFromLua::parseNoise(std::vector<noise::module::Module*>& modules, sol::table noise) {
|
|
||||||
std::string type = noise["module"];
|
|
||||||
|
|
||||||
// Modifer Modules
|
let type = table.get<string>("module");
|
||||||
if (type == "abs") {
|
// std::cout << type << std::endl;
|
||||||
auto module = new noise::module::Abs();
|
|
||||||
|
/**
|
||||||
modules.push_back(module);
|
* Produces a constant value, regardless of the position.
|
||||||
return module;
|
*
|
||||||
}
|
* @param value - The value to produce, default 0.
|
||||||
else if (type == "clamp") {
|
*/
|
||||||
auto module = new noise::module::Clamp();
|
|
||||||
module->SetBounds(noise.get_or<float>("low", noise::module::DEFAULT_CLAMP_LOWER_BOUND),
|
if (type == "const") {
|
||||||
noise.get_or<float>("high", noise::module::DEFAULT_CLAMP_UPPER_BOUND));
|
let module = New<Constant>();
|
||||||
|
module->SetValue(table.get_or<f32>("value", 0));
|
||||||
modules.push_back(module);
|
|
||||||
return module;
|
|
||||||
}
|
|
||||||
else if (type == "curve") {
|
|
||||||
auto module = new noise::module::Exponent();
|
|
||||||
|
|
||||||
module->SetExponent(noise.get_or<float>("exponent", noise::module::DEFAULT_EXPONENT));
|
|
||||||
|
|
||||||
modules.push_back(module);
|
|
||||||
return module;
|
|
||||||
}
|
|
||||||
else if (type == "invert") {
|
|
||||||
auto module = new noise::module::Invert();
|
|
||||||
|
|
||||||
modules.push_back(module);
|
|
||||||
return module;
|
|
||||||
}
|
|
||||||
else if (type == "scale_bias") {
|
|
||||||
auto module = new noise::module::ScaleBias();
|
|
||||||
sol::table source = noise["source"];
|
|
||||||
|
|
||||||
auto mod = parseNoise(modules, source);
|
|
||||||
module->SetSourceModule(0, *mod);
|
|
||||||
|
|
||||||
module->SetScale(noise.get_or<float>("scale", noise::module::DEFAULT_SCALE));
|
|
||||||
module->SetBias(noise.get_or<float>("bias", noise::module::DEFAULT_BIAS));
|
|
||||||
|
|
||||||
modules.push_back(module);
|
|
||||||
return module;
|
|
||||||
}
|
|
||||||
// Combiner Modules
|
|
||||||
else if (type == "add") {
|
|
||||||
auto module = new noise::module::Add();
|
|
||||||
sol::table sources = noise["sources"];
|
|
||||||
|
|
||||||
auto mod0 = parseNoise(modules, sources[1]);
|
|
||||||
auto mod1 = parseNoise(modules, sources[2]);
|
|
||||||
module->SetSourceModule(0, *mod0);
|
|
||||||
module->SetSourceModule(1, *mod1);
|
|
||||||
|
|
||||||
modules.push_back(module);
|
|
||||||
return module;
|
|
||||||
}
|
|
||||||
else if (type == "max") {
|
|
||||||
auto module = new noise::module::Max();
|
|
||||||
sol::table sources = noise["sources"];
|
|
||||||
|
|
||||||
auto mod0 = parseNoise(modules, sources[1]);
|
|
||||||
auto mod1 = parseNoise(modules, sources[2]);
|
|
||||||
module->SetSourceModule(0, *mod0);
|
|
||||||
module->SetSourceModule(1, *mod1);
|
|
||||||
|
|
||||||
modules.push_back(module);
|
|
||||||
return module;
|
|
||||||
}
|
|
||||||
else if (type == "min") {
|
|
||||||
auto module = new noise::module::Min();
|
|
||||||
sol::table sources = noise["sources"];
|
|
||||||
|
|
||||||
auto mod0 = parseNoise(modules, sources[1]);
|
|
||||||
auto mod1 = parseNoise(modules, sources[2]);
|
|
||||||
module->SetSourceModule(0, *mod0);
|
|
||||||
module->SetSourceModule(1, *mod1);
|
|
||||||
|
|
||||||
modules.push_back(module);
|
|
||||||
return module;
|
|
||||||
}
|
|
||||||
else if (type == "multiply") {
|
|
||||||
auto module = new noise::module::Multiply();
|
|
||||||
sol::table sources = noise["sources"];
|
|
||||||
|
|
||||||
auto mod0 = parseNoise(modules, sources[1]);
|
|
||||||
auto mod1 = parseNoise(modules, sources[2]);
|
|
||||||
module->SetSourceModule(0, *mod0);
|
|
||||||
module->SetSourceModule(1, *mod1);
|
|
||||||
|
|
||||||
modules.push_back(module);
|
|
||||||
return module;
|
|
||||||
}
|
|
||||||
else if (type == "power") {
|
|
||||||
auto module = new noise::module::Power();
|
|
||||||
sol::table sources = noise["sources"];
|
|
||||||
|
|
||||||
auto mod0 = parseNoise(modules, sources[1]);
|
|
||||||
auto mod1 = parseNoise(modules, sources[2]);
|
|
||||||
module->SetSourceModule(0, *mod0);
|
|
||||||
module->SetSourceModule(1, *mod1);
|
|
||||||
|
|
||||||
modules.push_back(module);
|
|
||||||
return module;
|
|
||||||
}
|
|
||||||
// Generator modules
|
|
||||||
else if (type == "billow") {
|
|
||||||
auto module = new noise::module::Billow();
|
|
||||||
|
|
||||||
module->SetSeed(noise.get_or<float>("seed", 0));
|
|
||||||
module->SetOctaveCount(noise.get_or<float>("octaves", noise::module::DEFAULT_BILLOW_OCTAVE_COUNT));
|
|
||||||
module->SetFrequency(noise.get_or<float>("frequency", noise::module::DEFAULT_BILLOW_FREQUENCY));
|
|
||||||
module->SetLacunarity(noise.get_or<float>("lacunarity", noise::module::DEFAULT_BILLOW_LACUNARITY));
|
|
||||||
module->SetPersistence(noise.get_or<float>("persistence", noise::module::DEFAULT_BILLOW_PERSISTENCE));
|
|
||||||
|
|
||||||
modules.push_back(module);
|
|
||||||
return module;
|
|
||||||
}
|
|
||||||
else if (type == "checkerboard") {
|
|
||||||
auto module = new noise::module::Checkerboard();
|
|
||||||
|
|
||||||
modules.push_back(module);
|
|
||||||
return module;
|
|
||||||
}
|
|
||||||
else if (type == "const") {
|
|
||||||
auto module = new noise::module::Const();
|
|
||||||
|
|
||||||
module->SetConstValue(noise.get_or<float>("value", noise::module::DEFAULT_CONST_VALUE));
|
|
||||||
|
|
||||||
modules.push_back(module);
|
|
||||||
return module;
|
|
||||||
}
|
|
||||||
else if (type == "cylinders") {
|
|
||||||
auto module = new noise::module::Cylinders();
|
|
||||||
|
|
||||||
module->SetFrequency(noise.get_or<float>("frequency", noise::module::DEFAULT_CYLINDERS_FREQUENCY));
|
|
||||||
|
|
||||||
modules.push_back(module);
|
|
||||||
return module;
|
|
||||||
}
|
|
||||||
else if (type == "ridged_multi") {
|
|
||||||
auto module = new noise::module::RidgedMulti();
|
|
||||||
|
|
||||||
module->SetSeed(noise.get_or<float>("seed", 0));
|
|
||||||
module->SetOctaveCount(noise.get_or<float>("octaves", noise::module::DEFAULT_RIDGED_OCTAVE_COUNT));
|
|
||||||
module->SetFrequency(noise.get_or<float>("frequency", noise::module::DEFAULT_RIDGED_FREQUENCY));
|
|
||||||
module->SetLacunarity(noise.get_or<float>("lacunarity", noise::module::DEFAULT_RIDGED_LACUNARITY));
|
|
||||||
|
|
||||||
modules.push_back(module);
|
|
||||||
return module;
|
|
||||||
}
|
|
||||||
else if (type == "spheres") {
|
|
||||||
auto module = new noise::module::Spheres();
|
|
||||||
|
|
||||||
module->SetFrequency(noise.get_or<float>("frequency", noise::module::DEFAULT_SPHERES_FREQUENCY));
|
|
||||||
|
|
||||||
modules.push_back(module);
|
|
||||||
return module;
|
|
||||||
}
|
|
||||||
else if (type == "perlin") {
|
|
||||||
auto module = new noise::module::Perlin();
|
|
||||||
|
|
||||||
module->SetSeed(noise.get_or<float>("seed", 0));
|
|
||||||
module->SetOctaveCount(noise.get_or<float>("octaves", noise::module::DEFAULT_PERLIN_OCTAVE_COUNT));
|
|
||||||
module->SetFrequency(noise.get_or<float>("frequency", noise::module::DEFAULT_PERLIN_FREQUENCY));
|
|
||||||
module->SetLacunarity(noise.get_or<float>("lacunarity", noise::module::DEFAULT_PERLIN_LACUNARITY));
|
|
||||||
module->SetPersistence(noise.get_or<float>("persistence", noise::module::DEFAULT_PERLIN_PERSISTENCE));
|
|
||||||
|
|
||||||
modules.push_back(module);
|
|
||||||
return module;
|
|
||||||
}
|
|
||||||
else if (type == "voronoi") {
|
|
||||||
auto module = new noise::module::Voronoi();
|
|
||||||
|
|
||||||
module->SetSeed(noise.get_or<float>("seed", 0));
|
|
||||||
// module->EnableDistance(noise.get_or<u32>("distance", false));
|
|
||||||
module->SetDisplacement(noise.get_or<float>("displacement", 0));
|
|
||||||
module->SetFrequency(noise.get_or<float>("frequency", 0));
|
|
||||||
|
|
||||||
modules.push_back(module);
|
|
||||||
return module;
|
|
||||||
}
|
|
||||||
// Selector Modules
|
|
||||||
else if (type == "blend") {
|
|
||||||
auto module = new noise::module::Blend();
|
|
||||||
sol::table sources = noise["sources"];
|
|
||||||
|
|
||||||
auto mod0 = parseNoise(modules, sources[1]);
|
|
||||||
auto mod1 = parseNoise(modules, sources[2]);
|
|
||||||
auto control = parseNoise(modules, noise["control"]);
|
|
||||||
module->SetSourceModule(0, *mod0);
|
|
||||||
module->SetSourceModule(1, *mod1);
|
|
||||||
module->SetSourceModule(1, *control);
|
|
||||||
|
|
||||||
modules.push_back(module);
|
|
||||||
return module;
|
|
||||||
}
|
|
||||||
else if (type == "select") {
|
|
||||||
auto module = new noise::module::Select();
|
|
||||||
sol::table sources = noise["sources"];
|
|
||||||
|
|
||||||
auto mod0 = parseNoise(modules, sources[1]);
|
|
||||||
auto mod1 = parseNoise(modules, sources[2]);
|
|
||||||
auto control = parseNoise(modules, noise["control"]);
|
|
||||||
module->SetSourceModule(0, *mod0);
|
|
||||||
module->SetSourceModule(1, *mod1);
|
|
||||||
module->SetSourceModule(1, *control);
|
|
||||||
|
|
||||||
modules.push_back(module);
|
|
||||||
return module;
|
|
||||||
}
|
|
||||||
// Transformer Modules
|
|
||||||
else if (type == "turbulence") {
|
|
||||||
auto module = new noise::module::Turbulence();
|
|
||||||
sol::table source = noise["source"];
|
|
||||||
|
|
||||||
auto mod0 = parseNoise(modules, source);
|
|
||||||
module->SetSourceModule(0, *mod0);
|
|
||||||
module->SetPower(noise.get_or<float>("power", noise::module::DEFAULT_TURBULENCE_POWER));
|
|
||||||
module->SetFrequency(noise.get_or<float>("frequency", noise::module::DEFAULT_TURBULENCE_FREQUENCY));
|
|
||||||
module->SetRoughness(noise.get_or<float>("roughness", noise::module::DEFAULT_TURBULENCE_ROUGHNESS));
|
|
||||||
|
|
||||||
modules.push_back(module);
|
|
||||||
return module;
|
return module;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (type == "scale_point") {
|
/**
|
||||||
auto module = new noise::module::ScalePoint();
|
* Applies the input position to the output using the factors and offsets provided.
|
||||||
sol::table source = noise["source"];
|
* All values default to zero.
|
||||||
|
*
|
||||||
|
* @param x_factor - The factor that the x value should influence the output.
|
||||||
|
* @param y_factor - The factor that the y value should influence the output.
|
||||||
|
* @param z_factor - The factor that the z value should influence the output.
|
||||||
|
* @param x_offset - The offset applied to the x value.
|
||||||
|
* @param y_offset - The offset applied to the y value.
|
||||||
|
* @param z_offset - The offset applied to the z value.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (type == "position_output") {
|
||||||
|
let module = New<PositionOutput>();
|
||||||
|
|
||||||
auto mod0 = parseNoise(modules, source);
|
module->Set<Dim::X>(table.get_or("x_factor", 0), table.get_or("x_offset", 0));
|
||||||
module->SetSourceModule(0, *mod0);
|
module->Set<Dim::Y>(table.get_or("y_factor", 0), table.get_or("y_offset", 0));
|
||||||
module->SetXScale(noise.get_or<float>("x_scale", 1));
|
module->Set<Dim::Z>(table.get_or("z_factor", 0), table.get_or("z_offset", 0));
|
||||||
module->SetYScale(noise.get_or<float>("y_scale", 1));
|
|
||||||
module->SetZScale(noise.get_or<float>("z_scale", 1));
|
|
||||||
|
|
||||||
modules.push_back(module);
|
|
||||||
return module;
|
return module;
|
||||||
}
|
}
|
||||||
throw std::runtime_error("Invalid noise module specified.");
|
|
||||||
|
/**
|
||||||
|
* Adds two sources, or a source and a scalar.
|
||||||
|
*
|
||||||
|
* @param sources - Two sources to add. Supply this or `source` and `scalar`.
|
||||||
|
* @param source - A single source, if supplied, also specify `scalar`.
|
||||||
|
* @param scalar - A scalar value to add to `source`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (type == "add") {
|
||||||
|
let module = New<Add>();
|
||||||
|
|
||||||
|
if (table.get<sol::optional<sol::table>>("sources")) {
|
||||||
|
module->SetLHS(parse(table["sources"][1]));
|
||||||
|
module->SetRHS(parse(table["sources"][2]));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
module->SetLHS(parse(table["source"]));
|
||||||
|
module->SetRHS(table.get_or<f32>("scalar", 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subtracts two sources, or a source and a scalar.
|
||||||
|
*
|
||||||
|
* @param sources - Two sources to subtract. Supply this or `source` and `scalar`.
|
||||||
|
* @param source - A single source, if supplied, also specify `scalar`.
|
||||||
|
* @param scalar - A scalar value to add to `source`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (type == "subtract") {
|
||||||
|
let module = New<Subtract>();
|
||||||
|
|
||||||
|
if (table.get<sol::optional<sol::table>>("sources")) {
|
||||||
|
module->SetLHS(parse(table["sources"][1]));
|
||||||
|
module->SetRHS(parse(table["sources"][2]));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
module->SetLHS(parse(table["source"]));
|
||||||
|
module->SetRHS(table.get_or<f32>("scalar", 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multiplies two sources, or a source and a scalar.
|
||||||
|
*
|
||||||
|
* @param sources - Two sources to multiply. Supply this or `source` and `scalar`.
|
||||||
|
* @param source - A single source, if supplied, also specify `scalar`.
|
||||||
|
* @param scalar - A scalar value to multiply `source` by.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (type == "multiply") {
|
||||||
|
let module = New<Multiply>();
|
||||||
|
|
||||||
|
if (table.get<sol::optional<sol::table>>("sources")) {
|
||||||
|
module->SetLHS(parse(table["sources"][1]));
|
||||||
|
module->SetRHS(parse(table["sources"][2]));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
module->SetLHS(parse(table["source"]));
|
||||||
|
module->SetRHS(table.get_or<f32>("scalar", 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Divides two sources, or a source and a scalar.
|
||||||
|
*
|
||||||
|
* @param sources - Two sources to divide. Supply this or `source` and `scalar`.
|
||||||
|
* @param source - A single source, if supplied, also specify `scalar`.
|
||||||
|
* @param scalar - A scalar value to divide `source` by.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (type == "divide") {
|
||||||
|
let module = New<Divide>();
|
||||||
|
|
||||||
|
if (table.get<sol::optional<sol::table>>("sources")) {
|
||||||
|
module->SetLHS(parse(table["sources"][1]));
|
||||||
|
module->SetRHS(parse(table["sources"][2]));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
module->SetLHS(parse(table["source"]));
|
||||||
|
module->SetRHS(table.get_or<f32>("scalar", 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blends between two sources, weighted by the factor provided.
|
||||||
|
*
|
||||||
|
* @param sources - The two sources to blend.
|
||||||
|
* @param factor - The factor to blend by, default 0.5.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (type == "fade") {
|
||||||
|
let module = New<Fade>();
|
||||||
|
|
||||||
|
module->SetA(parse(table["sources"][1]));
|
||||||
|
module->SetB(parse(table["sources"][2]));
|
||||||
|
module->SetFade(table.get_or<f32>("factor", 0.5));
|
||||||
|
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects the minimum between two sources, or a source and a scalar.
|
||||||
|
* The two can also be smoothed between.
|
||||||
|
*
|
||||||
|
* @param sources - Two sources to choose the minimum of. Supply this or `source` and `scalar`.
|
||||||
|
* @param source - A single source, if supplied, also specify `scalar`.
|
||||||
|
* @param scalar - A scalar value as the minimum.
|
||||||
|
* @param smoothness - The smoothness of the transition, defaults to 0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (type == "min") {
|
||||||
|
let smoothness = table.get_or("smoothness", 0);
|
||||||
|
|
||||||
|
if (smoothness > 0) {
|
||||||
|
let module = New<MinSmooth>();
|
||||||
|
let sources = table["sources"];
|
||||||
|
|
||||||
|
module->SetSmoothness(smoothness);
|
||||||
|
if (sources.valid()) {
|
||||||
|
module->SetLHS(parse(sources[1]));
|
||||||
|
module->SetRHS(parse(sources[2]));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
module->SetLHS(parse(table["source"]));
|
||||||
|
module->SetRHS(table.get_or<f32>("scalar", 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let module = New<Min>();
|
||||||
|
let sources = table["sources"];
|
||||||
|
|
||||||
|
if (sources.valid()) {
|
||||||
|
module->SetLHS(parse(sources[1]));
|
||||||
|
module->SetRHS(parse(sources[2]));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
module->SetLHS(parse(table["source"]));
|
||||||
|
module->SetRHS(table.get_or<f32>("scalar", 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects the maximum between two sources, or a source and a scalar.
|
||||||
|
* The two can also be smoothed between.
|
||||||
|
*
|
||||||
|
* @param sources - Two sources to choose the maximum of. Supply this or `source` and `scalar`.
|
||||||
|
* @param source - A single source, if supplied, also specify `scalar`.
|
||||||
|
* @param scalar - A scalar value as the maximum.
|
||||||
|
* @param smoothness - The smoothness of the transition, defaults to 0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (type == "max") {
|
||||||
|
let smoothness = table.get_or("smoothness", 0);
|
||||||
|
|
||||||
|
if (smoothness > 0) {
|
||||||
|
let module = New<MaxSmooth>();
|
||||||
|
let sources = table["sources"];
|
||||||
|
|
||||||
|
module->SetSmoothness(smoothness);
|
||||||
|
if (sources.valid()) {
|
||||||
|
module->SetLHS(parse(sources[1]));
|
||||||
|
module->SetRHS(parse(sources[2]));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
module->SetLHS(parse(table["source"]));
|
||||||
|
module->SetRHS(table.get_or<f32>("scalar", 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let module = New<Max>();
|
||||||
|
let sources = table["sources"];
|
||||||
|
|
||||||
|
if (sources.valid()) {
|
||||||
|
module->SetLHS(parse(sources[1]));
|
||||||
|
module->SetRHS(parse(sources[2]));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
module->SetLHS(parse(table["source"]));
|
||||||
|
module->SetRHS(table.get_or<f32>("scalar", 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clamps the source between two scalar values. Can also be smoothed.
|
||||||
|
*
|
||||||
|
* @param source - The source to clamp.
|
||||||
|
* @param min - The minimum value, defaults to 0.
|
||||||
|
* @param max - The maximum value, defaults to 1.
|
||||||
|
* @param smoothness - The smoothness of the transition, defaults to 0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (type == "max") {
|
||||||
|
let source = parse(table["source"]);
|
||||||
|
let smoothness = table.get_or<f32>("smoothness", 0);
|
||||||
|
|
||||||
|
if (smoothness > 0) {
|
||||||
|
let minModule = New<MinSmooth>();
|
||||||
|
minModule->SetLHS(source);
|
||||||
|
minModule->SetRHS(table.get_or<f32>("min", 0));
|
||||||
|
minModule->SetSmoothness(smoothness);
|
||||||
|
|
||||||
|
let maxModule = New<MaxSmooth>();
|
||||||
|
maxModule->SetLHS(minModule);
|
||||||
|
maxModule->SetRHS(table.get_or<f32>("max", 1));
|
||||||
|
maxModule->SetSmoothness(smoothness);
|
||||||
|
|
||||||
|
return maxModule;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let minModule = New<Min>();
|
||||||
|
minModule->SetLHS(source);
|
||||||
|
minModule->SetRHS(table.get_or<f32>("min", 0));
|
||||||
|
|
||||||
|
let maxModule = New<Max>();
|
||||||
|
maxModule->SetLHS(minModule);
|
||||||
|
maxModule->SetRHS(table.get_or<f32>("max", 1));
|
||||||
|
|
||||||
|
return maxModule;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates Simplex Noise.
|
||||||
|
*
|
||||||
|
* @param frequency - The frequency of the noise, default 1.
|
||||||
|
* @param octaves - The amount of octaves the noise will have, default 3.
|
||||||
|
* @param lacunarity - The lacunarity of the octaves, default 0.5.
|
||||||
|
* @param seed - The seed **offset** to use, relative to the world seed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (type == "simplex") {
|
||||||
|
SmartNode<> module = New<Simplex>();
|
||||||
|
|
||||||
|
let seed = table.get_or<u32>("seed", 0);
|
||||||
|
let frequency = table.get_or<f32>("frequency", 1);
|
||||||
|
let octaves = table.get_or<f32>("octaves", 3);
|
||||||
|
let persistence = table.get_or<f32>("persistence", 0.5);
|
||||||
|
let lacunarity = table.get_or<f32>("lacunarity", 0.5);
|
||||||
|
|
||||||
|
if (frequency != 1) {
|
||||||
|
let scaleModule = New<DomainScale>();
|
||||||
|
scaleModule->SetSource(module);
|
||||||
|
scaleModule->SetScale(frequency);
|
||||||
|
module = scaleModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (octaves > 1) {
|
||||||
|
let fractalModule = New<FractalFBm>();
|
||||||
|
fractalModule->SetSource(module);
|
||||||
|
fractalModule->SetOctaveCount(octaves);
|
||||||
|
fractalModule->SetLacunarity(lacunarity);
|
||||||
|
fractalModule->SetGain(persistence);
|
||||||
|
module = fractalModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (seed != 0) {
|
||||||
|
let seedOffsetModule = New<SeedOffset>();
|
||||||
|
seedOffsetModule->SetSource(module);
|
||||||
|
seedOffsetModule->SetOffset(seed);
|
||||||
|
module = seedOffsetModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scales the domain by axis.
|
||||||
|
*
|
||||||
|
* @param source - The source to scale.
|
||||||
|
* @param x_scale - The scaling to apply to the x axis.
|
||||||
|
* @param y_scale - The scaling to apply to the y axis.
|
||||||
|
* @param z_scale - The scaling to apply to the z axis.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (type == "scale") {
|
||||||
|
let module = New<DomainAxisScale>();
|
||||||
|
|
||||||
|
module->SetSource(parse(table["source"]));
|
||||||
|
module->SetScale<Dim::X>(table.get_or<f32>("x_scale", 1));
|
||||||
|
module->SetScale<Dim::Y>(table.get_or<f32>("y_scale", 1));
|
||||||
|
module->SetScale<Dim::Z>(table.get_or<f32>("z_scale", 1));
|
||||||
|
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::runtime_error("Invalid module name: " + type);
|
||||||
}
|
}
|
|
@ -1,35 +1,13 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <vector>
|
#include "util/Types.h"
|
||||||
|
|
||||||
#include <sol/forward.hpp>
|
#include <sol/forward.hpp>
|
||||||
#include <libnoise/module/add.h>
|
#include <FastNoise/FastNoise.h>
|
||||||
#include <libnoise/module/module.h>
|
|
||||||
#include <libnoise/module/modulebase.h>
|
|
||||||
|
|
||||||
namespace NoiseFromLua {
|
namespace NoiseFromLua {
|
||||||
|
/** Builds a FastNoise node tree from a Lua table. */
|
||||||
/**
|
FastNoise::SmartNode<> parse(sol::table table);
|
||||||
* Builds a noise module vector from a Lua noise table.
|
|
||||||
* The top level module will always be the last element in the vector.
|
|
||||||
*
|
|
||||||
* @param noise - The lua noise definition to parse.
|
|
||||||
* @returns a vector containing all of the noise modules built.
|
|
||||||
*/
|
|
||||||
|
|
||||||
std::vector<noise::module::Module*> build(sol::table noise);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Will initialize Noise::Module instances from a lua noise module definition, and recursively initialize it's
|
|
||||||
* child modules as well. All modules will be added in reverse-discovered-order to the modules vector reference
|
|
||||||
* passed in. The top level module will always be the last element in the vector.
|
|
||||||
*
|
|
||||||
* @param modules - The vector reference to insert generated noise modules into.
|
|
||||||
* @param noise - The lua noise definition to parse.
|
|
||||||
* @returns the noise module that was parsed.
|
|
||||||
*/
|
|
||||||
|
|
||||||
noise::module::Module* parseNoise(std::vector<noise::module::Module*>& modules, sol::table noise);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -66,21 +66,18 @@ namespace RegisterBiome {
|
||||||
if (!biomeTint) throw identifier + "biome definitions require a biome_tint";
|
if (!biomeTint) throw identifier + "biome definitions require a biome_tint";
|
||||||
|
|
||||||
// Get noise parameters
|
// Get noise parameters
|
||||||
|
let constGenerator = FastNoise::New<FastNoise::Constant>();
|
||||||
|
constGenerator->SetValue(0);
|
||||||
|
FastNoise::SmartNode<> heightmap = constGenerator, volume = constGenerator;
|
||||||
|
|
||||||
auto noiseList = biomeTable.get<sol::optional<sol::table>>("noise");
|
auto noiseList = biomeTable.get<sol::optional<sol::table>>("noise");
|
||||||
std::vector<noise::module::Module*> volumeModules, heightmapModules;
|
|
||||||
|
|
||||||
if (noiseList) {
|
if (noiseList) {
|
||||||
if (noiseList->get<sol::optional<sol::table>>("heightmap"))
|
let heightTable = noiseList->get<sol::optional<sol::table>>("heightmap");
|
||||||
NoiseFromLua::parseNoise(heightmapModules, noiseList->get<sol::table>("heightmap"));
|
if (heightTable) heightmap = NoiseFromLua::parse(*heightTable);
|
||||||
else heightmapModules.push_back(new noise::module::Const);
|
|
||||||
|
|
||||||
if (noiseList->get<sol::optional<sol::table>>("volume"))
|
let volumeTable = noiseList->get<sol::optional<sol::table>>("volume");
|
||||||
NoiseFromLua::parseNoise(volumeModules, noiseList->get<sol::table>("volume"));
|
if (volumeTable) volume = NoiseFromLua::parse(*volumeTable);
|
||||||
else volumeModules.push_back(new noise::module::Const);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
volumeModules.push_back(new noise::module::Const);
|
|
||||||
heightmapModules.push_back(new noise::module::Const);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Structure>> schematics {};
|
std::vector<std::shared_ptr<Structure>> schematics {};
|
||||||
|
@ -103,8 +100,8 @@ namespace RegisterBiome {
|
||||||
biomeDef->soilBlock = defs.blockFromStr(*bSoil).index;
|
biomeDef->soilBlock = defs.blockFromStr(*bSoil).index;
|
||||||
biomeDef->rockBlock = defs.blockFromStr(*bRock).index;
|
biomeDef->rockBlock = defs.blockFromStr(*bRock).index;
|
||||||
|
|
||||||
biomeDef->heightmap = heightmapModules;
|
biomeDef->heightmap = heightmap;
|
||||||
biomeDef->volume = volumeModules;
|
biomeDef->volume = volume;
|
||||||
|
|
||||||
biomeDef->schematics = schematics;
|
biomeDef->schematics = schematics;
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,8 @@ class ServerSubgame;
|
||||||
|
|
||||||
class ServerGenStream {
|
class ServerGenStream {
|
||||||
public:
|
public:
|
||||||
static const usize THREADS = 6;
|
static const usize THREADS = 4;
|
||||||
static const usize THREAD_QUEUE_SIZE = 6;
|
static const usize THREAD_QUEUE_SIZE = 16;
|
||||||
|
|
||||||
struct FinishedJob {
|
struct FinishedJob {
|
||||||
FinishedJob(u16 dim, ivec3 pos, uptr<MapGen::ChunkMap> created) :
|
FinishedJob(u16 dim, ivec3 pos, uptr<MapGen::ChunkMap> created) :
|
||||||
|
|
|
@ -21,6 +21,21 @@ namespace Space {
|
||||||
const static i16f MAPBLOCK_CHUNK_LENGTH = MAPBLOCK_SIZE;
|
const static i16f MAPBLOCK_CHUNK_LENGTH = MAPBLOCK_SIZE;
|
||||||
const static i16f REGION_CHUNK_LENGTH = MAPBLOCK_CHUNK_LENGTH * REGION_SIZE;
|
const static i16f REGION_CHUNK_LENGTH = MAPBLOCK_CHUNK_LENGTH * REGION_SIZE;
|
||||||
|
|
||||||
|
// Get the index of a position vector in a cube.
|
||||||
|
static inline u32 posToIndex(const ivec3& pos, const u32 size) {
|
||||||
|
return pos.x + size * (pos.z + size * pos.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a position vector from an index of a cube.
|
||||||
|
static inline ivec3 indexToPos(u32 ind, const u32 size) {
|
||||||
|
ivec3 vec {};
|
||||||
|
vec.y = ind / pow(size, 2);
|
||||||
|
ind -= static_cast<u32>(vec.y) * pow(size, 2);
|
||||||
|
vec.z = ind / size;
|
||||||
|
vec.x = ind % size;
|
||||||
|
return vec;
|
||||||
|
}
|
||||||
|
|
||||||
// Private helper methods
|
// Private helper methods
|
||||||
namespace {
|
namespace {
|
||||||
inline u16vec3 localFromGlobal(const ivec3& pos, i16 size) {
|
inline u16vec3 localFromGlobal(const ivec3& pos, i16 size) {
|
||||||
|
@ -86,8 +101,7 @@ namespace Space {
|
||||||
|
|
||||||
// Get the index of a MapBlock within its Region from its local or world position.
|
// Get the index of a MapBlock within its Region from its local or world position.
|
||||||
static inline u16 index(const ivec3& vec) {
|
static inline u16 index(const ivec3& vec) {
|
||||||
u8vec3 local = MapBlock::relative::toRegion(vec);
|
return Space::posToIndex(MapBlock::relative::toRegion(vec), REGION_SIZE);
|
||||||
return local.x + REGION_SIZE * (local.y + REGION_SIZE * local.z);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,20 +137,12 @@ namespace Space {
|
||||||
|
|
||||||
// Get the index of a Chunk within its MapBlock from its local or world position.
|
// Get the index of a Chunk within its MapBlock from its local or world position.
|
||||||
static inline u16 index(const glm::ivec3& vec) {
|
static inline u16 index(const glm::ivec3& vec) {
|
||||||
u8vec3 local = Chunk::relative::toMapBlock(vec);
|
return Space::posToIndex(Chunk::relative::toMapBlock(vec), MAPBLOCK_SIZE);
|
||||||
return local.x + MAPBLOCK_SIZE * (local.z + MAPBLOCK_SIZE * local.y);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a local vector of an chunk within its mapblock.
|
// Return a local vector of an chunk within its mapblock.
|
||||||
static inline ivec3 fromIndex(u16 ind) {
|
static inline ivec3 fromIndex(u16 ind) {
|
||||||
u8vec3 vec {};
|
return Space::indexToPos(ind, MAPBLOCK_SIZE);
|
||||||
|
|
||||||
vec.y = ind / (MAPBLOCK_SIZE * MAPBLOCK_SIZE);
|
|
||||||
ind -= (static_cast<int>(vec.y) * MAPBLOCK_SIZE * MAPBLOCK_SIZE);
|
|
||||||
vec.z = ind / MAPBLOCK_SIZE;
|
|
||||||
vec.x = ind % MAPBLOCK_SIZE;
|
|
||||||
|
|
||||||
return vec;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,20 +183,12 @@ namespace Space {
|
||||||
|
|
||||||
// Get the index of a Block within its Chunk from its local or world position.
|
// Get the index of a Block within its Chunk from its local or world position.
|
||||||
static inline u16 index(const ivec3& vec) {
|
static inline u16 index(const ivec3& vec) {
|
||||||
u8vec3 local = Block::relative::toChunk(vec);
|
return Space::posToIndex(Block::relative::toChunk(vec), CHUNK_SIZE);
|
||||||
return local.x + CHUNK_SIZE * (local.z + CHUNK_SIZE * local.y);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a local vector of an block within its chunk.
|
// Return a local vector of an block within its chunk.
|
||||||
static inline u8vec3 fromIndex(u16 ind) {
|
static inline u8vec3 fromIndex(u16 ind) {
|
||||||
u8vec3 vec {};
|
return Space::indexToPos(ind, CHUNK_SIZE);
|
||||||
|
|
||||||
vec.y = ind / (CHUNK_SIZE * CHUNK_SIZE);
|
|
||||||
ind -= (static_cast<int>(vec.y) * CHUNK_SIZE * CHUNK_SIZE);
|
|
||||||
vec.z = ind / CHUNK_SIZE;
|
|
||||||
vec.x = ind % CHUNK_SIZE;
|
|
||||||
|
|
||||||
return vec;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ using i8 = int8_t;
|
||||||
using i16 = int16_t;
|
using i16 = int16_t;
|
||||||
using i32 = int32_t;
|
using i32 = int32_t;
|
||||||
using i64 = int64_t;
|
using i64 = int64_t;
|
||||||
|
using isize = intmax_t;
|
||||||
|
|
||||||
using i8f = int_fast8_t;
|
using i8f = int_fast8_t;
|
||||||
using i16f = int_fast16_t;
|
using i16f = int_fast16_t;
|
||||||
|
|
|
@ -26,6 +26,18 @@ MapGen::MapGen(Subgame& game, World& world, u32 seed, std::unordered_set<string>
|
||||||
}
|
}
|
||||||
|
|
||||||
generateVoronoi(biomeIndices);
|
generateVoronoi(biomeIndices);
|
||||||
|
|
||||||
|
let biomePerlin = FastNoise::New<FastNoise::Simplex>();
|
||||||
|
|
||||||
|
let biomeScale = FastNoise::New<FastNoise::DomainScale>();
|
||||||
|
biomeScale->SetSource(biomePerlin);
|
||||||
|
biomeScale->SetScale(1/1000.f);
|
||||||
|
|
||||||
|
let biomeFractal = FastNoise::New<FastNoise::FractalFBm>();
|
||||||
|
biomeFractal->SetSource(biomeScale);
|
||||||
|
biomeFractal->SetOctaveCount(4);
|
||||||
|
|
||||||
|
biomeGenerator = biomeFractal;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[maybe_unused]] uptr<MapGen::ChunkMap> MapGen::generateChunk(u16 dim, ivec3 pos) {
|
[[maybe_unused]] uptr<MapGen::ChunkMap> MapGen::generateChunk(u16 dim, ivec3 pos) {
|
||||||
|
@ -39,50 +51,29 @@ uptr<MapGen::ChunkMap> MapGen::generateMapBlock(u16 dim, ivec3 pos) {
|
||||||
std::unique_ptr<MapGen::ChunkMap> MapGen::generateArea(u16 dim, ivec3 origin, u16 size) {
|
std::unique_ptr<MapGen::ChunkMap> MapGen::generateArea(u16 dim, ivec3 origin, u16 size) {
|
||||||
Job job(origin, size);
|
Job job(origin, size);
|
||||||
|
|
||||||
// Build Biome Prop Maps
|
job.temperature.generate({ job.pos.x * 16, 0, job.pos.z * 16 }, biomeGenerator);
|
||||||
|
job.roughness.generate({ job.pos.x * 16, 0, job.pos.z * 16 }, biomeGenerator);
|
||||||
const auto fill = [&](const noise::module::Module& s) {
|
job.humidity.generate({ job.pos.x * 16, 0, job.pos.z * 16 }, biomeGenerator);
|
||||||
return [&](vec3 pos) {
|
|
||||||
vec3 worldPos = vec3(job.pos) + pos * static_cast<f32>(job.size);
|
let biomeMap = vec<u16>(pow(job.size * 16 + 1, 2));
|
||||||
return s.GetValue(worldPos.x, 0, worldPos.z);
|
u16vec3 bPos {};
|
||||||
};
|
for (bPos.x = 0; bPos.x < job.size * 16 + 1; bPos.x++) {
|
||||||
};
|
for (bPos.z = 0; bPos.z < job.size * 16 + 1; bPos.z++) {
|
||||||
|
biomeMap[bPos.z * (job.size * 16 + 1 ) + bPos.x] =
|
||||||
job.temperature.populate(fill(props.temperature));
|
getBiomeAt(job.temperature[bPos], job.humidity[bPos], job.roughness[bPos]);
|
||||||
job.roughness.populate(fill(props.roughness));
|
}
|
||||||
job.humidity.populate(fill(props.humidity));
|
|
||||||
|
|
||||||
// Generate Biome Topmap
|
|
||||||
|
|
||||||
vec<u16> biomeMap {};
|
|
||||||
biomeMap.resize((job.size * 16 + 1) * (job.size * 16 + 1));
|
|
||||||
|
|
||||||
for (usize i = 0; i < biomeMap.size(); i++) {
|
|
||||||
vec3 indPos = { i / (job.size * 16 + 1), 0, i % (job.size * 16 + 1) };
|
|
||||||
vec3 queryPos = indPos / 16.f / static_cast<f32>(job.size);
|
|
||||||
|
|
||||||
biomeMap[i] = getBiomeAt(job.temperature.get(queryPos),
|
|
||||||
job.humidity.get(queryPos), job.roughness.get(queryPos));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate Heightmap and Volume
|
job.heightmap.fill([&](ivec3 pos) {
|
||||||
|
return game.getBiomes().biomeFromId(biomeMap[pos.z * (job.size * 16 + 1) + pos.x])
|
||||||
job.heightmap.populate([&](vec3 pos) {
|
.heightmap->GenSingle2D(job.pos.x * 16 + pos.x, job.pos.z * 16 + pos.z, 1337);
|
||||||
ivec3 blockPos = ivec3(pos * 16.f * static_cast<f32>(job.size));
|
|
||||||
auto& biome = game.getBiomes().biomeFromId(biomeMap.at(blockPos.x * (job.size * 16 + 1) + blockPos.z));
|
|
||||||
vec3 worldPos = vec3(job.pos) + pos * static_cast<f32>(job.size);
|
|
||||||
return biome.heightmap[biome.heightmap.size() - 1]->GetValue(worldPos.x, 0, worldPos.z);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
job.volume.populate([&](vec3 pos) {
|
job.volume.fill([&](ivec3 pos) {
|
||||||
ivec3 blockPos = ivec3(pos * 16.f * static_cast<f32>(job.size));
|
return game.getBiomes().biomeFromId(biomeMap[pos.z * (job.size * 16 + 1) + pos.x])
|
||||||
auto& biome = game.getBiomes().biomeFromId(biomeMap.at(blockPos.x * (job.size * 16 + 1) + blockPos.z));
|
.volume->GenSingle3D(job.pos.x * 16 + pos.x, job.pos.y * 16 + pos.y, job.pos.z * 16 + pos.z, 1337);
|
||||||
vec3 worldPos = vec3(job.pos) + pos * static_cast<f32>(job.size);
|
|
||||||
return biome.volume[biome.volume.size() - 1]->GetValue(worldPos.x, worldPos.y, worldPos.z);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Generate Chunks
|
|
||||||
|
|
||||||
i16vec3 pos {};
|
i16vec3 pos {};
|
||||||
for (pos.x = 0; pos.x < job.size; pos.x++) {
|
for (pos.x = 0; pos.x < job.size; pos.x++) {
|
||||||
for (pos.z = 0; pos.z < job.size; pos.z++) {
|
for (pos.z = 0; pos.z < job.size; pos.z++) {
|
||||||
|
@ -104,7 +95,7 @@ std::unique_ptr<MapGen::ChunkMap> MapGen::generateArea(u16 dim, ivec3 origin, u1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
propogateSunlightNodes(job);
|
// propogateSunlightNodes(job);
|
||||||
|
|
||||||
for (let& chunk : *job.chunks) {
|
for (let& chunk : *job.chunks) {
|
||||||
chunk.second->compress();
|
chunk.second->compress();
|
||||||
|
@ -138,12 +129,12 @@ u16 MapGen::getBiomeAt(f32 temperature, f32 humidity, f32 roughness) {
|
||||||
|
|
||||||
uptr<MapGen::ChunkData> MapGen::populateChunkDensity(MapGen::Job& job, ivec3 localPos) {
|
uptr<MapGen::ChunkData> MapGen::populateChunkDensity(MapGen::Job& job, ivec3 localPos) {
|
||||||
auto data = make_unique<ChunkData>();
|
auto data = make_unique<ChunkData>();
|
||||||
|
|
||||||
for (u16 i = 0; i < 4096; i++) {
|
for (u16 i = 0; i < 4096; i++) {
|
||||||
ivec3 indPos = Space::Block::fromIndex(i);
|
ivec3 indPos = Space::Block::fromIndex(i);
|
||||||
vec3 queryPos = (vec3(localPos) + vec3(indPos) / 16.f) / static_cast<f32>(job.size);
|
let ind3d = localPos * 16 + indPos;
|
||||||
(*data)[i] = (job.volume.get(queryPos) + job.heightmap.get({ queryPos.x, 0, queryPos.z }))
|
let ind2d = (localPos.z * 16 + indPos.z) * (job.size * 16) + (localPos.x * 16 + indPos.x);
|
||||||
- ((job.pos.y + localPos.y) * 16 + indPos.y);
|
(*data)[i] = (job.heightmap[ind2d]) + (job.volume[ind3d]) - ((job.pos.y + localPos.y) * 16 + indPos.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
@ -191,7 +182,7 @@ void MapGen::generateChunkBlocks(Job& job, ivec3 localPos, vec<u16> biomeMap, Ch
|
||||||
for (u16 i = 0; i < 4096; i++) {
|
for (u16 i = 0; i < 4096; i++) {
|
||||||
ivec3 indPos = Space::Block::fromIndex(i);
|
ivec3 indPos = Space::Block::fromIndex(i);
|
||||||
|
|
||||||
u16 biomeId = biomeMap[(localPos.x * 16 + indPos.x) * (job.size * 16 + 1) + (localPos.z * 16 + indPos.z)];
|
u16 biomeId = biomeMap[(localPos.z * 16 + indPos.z) * (job.size * 16 + 1) + (localPos.x * 16 + indPos.x)];
|
||||||
auto& biome = game.getBiomes().biomeFromId(biomeId);
|
auto& biome = game.getBiomes().biomeFromId(biomeId);
|
||||||
chunk.d->biomes[i] = biomeId;
|
chunk.d->biomes[i] = biomeId;
|
||||||
|
|
||||||
|
@ -225,11 +216,10 @@ void MapGen::generateChunkDecorAndLight(Job& job, ivec3 localPos, vec<u16> biome
|
||||||
job.chunks->count(abovePos) ? job.chunks->at(abovePos) : nullptr : nullptr;
|
job.chunks->count(abovePos) ? job.chunks->at(abovePos) : nullptr : nullptr;
|
||||||
|
|
||||||
for (u16 i = 0; i < 256; i++) {
|
for (u16 i = 0; i < 256; i++) {
|
||||||
ivec3 indPos = { i / 16, 15, i % 16 };
|
ivec3 indPos { i / 16, 15, i % 16 };
|
||||||
|
|
||||||
u16 biomeID = biomeMap[(localPos.x * 16 + indPos.x)
|
u16 biomeId = biomeMap[(localPos.z * 16 + indPos.z) * (job.size * 16 + 1) + (localPos.x * 16 + indPos.x)];
|
||||||
* (job.size * 16 + 1) + (localPos.z * 16 + indPos.z)];
|
auto& biome = game.getBiomes().biomeFromId(biomeId);
|
||||||
auto& biome = game.getBiomes().biomeFromId(biomeID);
|
|
||||||
|
|
||||||
i16 schemID = -1;
|
i16 schemID = -1;
|
||||||
for (u16 j = 0; j < biome.schematics.size(); j++) {
|
for (u16 j = 0; j < biome.schematics.size(); j++) {
|
||||||
|
|
|
@ -21,10 +21,10 @@ class DefinitionAtlas;
|
||||||
class MapGen {
|
class MapGen {
|
||||||
public:
|
public:
|
||||||
/** The precision of the Biome map, as a divisor of the chunk size. */
|
/** The precision of the Biome map, as a divisor of the chunk size. */
|
||||||
constexpr static u8 BIOP = 4;
|
// constexpr static u8 BIOP = 4;
|
||||||
|
|
||||||
/** The precision of the Terrain maps, as a divisor of the chunk size. */
|
/** The precision of the Terrain maps, as a divisor of the chunk size. */
|
||||||
constexpr static u8 TERP = 4;
|
// constexpr static u8 TERP = 4;
|
||||||
|
|
||||||
/** A type alias for the type the map of Chunks stored in the Job. */
|
/** A type alias for the type the map of Chunks stored in the Job. */
|
||||||
typedef std::unordered_map<ivec3, sptr<Chunk>, Vec::ivec3> ChunkMap;
|
typedef std::unordered_map<ivec3, sptr<Chunk>, Vec::ivec3> ChunkMap;
|
||||||
|
@ -55,16 +55,20 @@ public:
|
||||||
|
|
||||||
Job(ivec3 pos, u16 size) :
|
Job(ivec3 pos, u16 size) :
|
||||||
pos(pos), size(size),
|
pos(pos), size(size),
|
||||||
volume {{ size * TERP, (size + 1) * TERP }, { 1, 1.25 }}, heightmap {{ size * TERP, 0 }},
|
volume(u16vec3(size * 16), 4),
|
||||||
temperature {{ size * BIOP, 0 }}, roughness {{ size * BIOP, 0 }}, humidity {{ size * BIOP, 0 }} {}
|
heightmap(u16vec2(size * 16), 4),
|
||||||
|
|
||||||
|
temperature(u16vec2(size * 16), 4),
|
||||||
|
humidity(u16vec2(size * 16), 4),
|
||||||
|
roughness(u16vec2(size * 16), 4) {}
|
||||||
|
|
||||||
ivec3 pos {};
|
ivec3 pos {};
|
||||||
u16 size {};
|
u16 size {};
|
||||||
|
|
||||||
uptr<ChunkMap> chunks = make_unique<ChunkMap>();
|
uptr<ChunkMap> chunks = make_unique<ChunkMap>();
|
||||||
std::queue<SunlightNode> sunlightQueue {};
|
std::queue<SunlightNode> sunlightQueue {};
|
||||||
|
|
||||||
NoiseSample volume, heightmap;
|
NoiseSample heightmap, volume;
|
||||||
NoiseSample temperature, humidity, roughness;
|
NoiseSample temperature, humidity, roughness;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -218,6 +222,8 @@ private:
|
||||||
|
|
||||||
constexpr const static u16 voronoiSize = 64;
|
constexpr const static u16 voronoiSize = 64;
|
||||||
Voronoi3D voronoi { voronoiSize };
|
Voronoi3D voronoi { voronoiSize };
|
||||||
|
|
||||||
|
FastNoise::SmartNode<> biomeGenerator;
|
||||||
|
|
||||||
Subgame& game;
|
Subgame& game;
|
||||||
World& world;
|
World& world;
|
||||||
|
|
|
@ -1,21 +1,57 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include <util/Util.h>
|
||||||
|
|
||||||
#include "NoiseSample.h"
|
#include "NoiseSample.h"
|
||||||
|
|
||||||
NoiseSample::NoiseSample(u16 precision, f32 scaleBy) :
|
|
||||||
NoiseSample({ precision, precision }, { scaleBy, scaleBy }) {}
|
|
||||||
|
|
||||||
NoiseSample::NoiseSample(u16vec2 precision, glm::vec2 scaleBy) :
|
NoiseSample::NoiseSample(u16vec2 size, u16 precision) : NoiseSample({ size.x, 0, size.y }, precision) {}
|
||||||
precision(precision.x, precision.y, precision.x),
|
|
||||||
scaleBy(scaleBy.x, scaleBy.y, scaleBy.x) {
|
NoiseSample::NoiseSample(u16vec3 size, u16 precision):
|
||||||
data.resize((this->precision.x + 1) * (this->precision.y + 1) * (this->precision.z + 1));
|
size(size), precision(precision), data((size.x + 1) * (size.y + 1) * (size.z + 1), 0) {
|
||||||
|
if (size.x % precision != 0 || (size.y != 0 && size.y % precision) != 0 || size.z % precision != 0)
|
||||||
|
throw std::runtime_error("NoiseSample precision must be a divisor of size." +
|
||||||
|
Util::toString(size) + " : " + Util::toString(precision));
|
||||||
}
|
}
|
||||||
|
|
||||||
void NoiseSample::populate(const NoiseSample::fill_function& fn) {
|
void NoiseSample::generate(ivec3 pos, const FastNoise::SmartNode<>& generator) {
|
||||||
vec3 pos {}, precisionFloat = vec3(precision);
|
for (u16 x = 0; x < size.x + 1; x += precision)
|
||||||
for (pos.x = 0; pos.x <= precisionFloat.x; pos.x++)
|
for (u16 y = 0; y < size.y + 1; y += precision)
|
||||||
for (pos.y = 0; pos.y <= precisionFloat.y; pos.y++)
|
for (u16 z = 0; z < size.z + 1; z += precision)
|
||||||
for (pos.z = 0; pos.z <= precisionFloat.z; pos.z++) {
|
data[index(x, y, z)] = generator->GenSingle3D(
|
||||||
vec3 queryPos = pos / precisionFloat * scaleBy;
|
pos.x + x, pos.y + y, pos.z + z, 1337);
|
||||||
if (queryPos.y == NAN) queryPos.y = 0;
|
|
||||||
data[index(pos.x, pos.y, pos.z)] = fn(queryPos);
|
interpolateData();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NoiseSample::fill(const NoiseSample::fill_function& fill) {
|
||||||
|
u16vec3 pos {};
|
||||||
|
for (pos.x = 0; pos.x < size.x + 1; pos.x += precision)
|
||||||
|
for (pos.y = 0; pos.y < size.y + 1; pos.y += precision)
|
||||||
|
for (pos.z = 0; pos.z < size.z + 1; pos.z += precision)
|
||||||
|
data[index(pos)] = fill(pos);
|
||||||
|
|
||||||
|
interpolateData();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NoiseSample::interpolateData() {
|
||||||
|
u16vec3 pos = {};
|
||||||
|
for (pos.x = 0; pos.x < size.x + 1; pos.x += 1) {
|
||||||
|
for (pos.y = 0; pos.y < size.y + 1; pos.y += 1) {
|
||||||
|
for (pos.z = 0; pos.z < size.z + 1; pos.z += 1) {
|
||||||
|
if (pos.x % precision == 0 && pos.y % precision == 0 && pos.z % precision == 0) continue;
|
||||||
|
|
||||||
|
vec3 frac = vec3(pos) / static_cast<f32>(precision);
|
||||||
|
u16vec3 a = u16vec3(glm::floor(frac)) * static_cast<u16>(precision);
|
||||||
|
u16vec3 b = u16vec3(glm::ceil(frac)) * static_cast<u16>(precision);
|
||||||
|
vec3 factor = frac - glm::floor(frac);
|
||||||
|
|
||||||
|
data[index(pos)] = Interp::trilerp(
|
||||||
|
data[index(a.x, a.y, a.z)], data[index(b.x, a.y, a.z)],
|
||||||
|
data[index(a.x, a.y, b.z)], data[index(b.x, a.y, b.z)],
|
||||||
|
data[index(a.x, b.y, a.z)], data[index(b.x, b.y, a.z)],
|
||||||
|
data[index(a.x, b.y, b.z)], data[index(b.x, b.y, b.z)],
|
||||||
|
factor.x, factor.z, factor.y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,54 +1,87 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <FastNoise/FastNoise.h>
|
||||||
|
|
||||||
#include "util/Types.h"
|
#include "util/Types.h"
|
||||||
#include "util/Interp.h"
|
#include "util/Interp.h"
|
||||||
|
|
||||||
class NoiseSample {
|
class NoiseSample {
|
||||||
public:
|
public:
|
||||||
typedef std::function<f32(vec3 pos)> fill_function;
|
typedef std::function<f32(ivec3 pos)> fill_function;
|
||||||
|
|
||||||
NoiseSample(u16 precision, f32 scaleBy = 1);
|
NoiseSample(u16vec2 size, u16 precision);
|
||||||
|
NoiseSample(u16vec3 size, u16 precision);
|
||||||
|
|
||||||
NoiseSample(u16vec2 precision, vec2 scaleBy = vec2(1));
|
void generate(ivec3 pos, const FastNoise::SmartNode<>& generator);
|
||||||
|
|
||||||
void populate(const fill_function& fn);
|
void fill(const fill_function& fill);
|
||||||
|
|
||||||
inline f32 get(vec3 pos) {
|
inline f32 operator[](u16vec3 pos) {
|
||||||
vec3 scaled = pos * vec3(precision) / scaleBy;
|
return data[index(pos)];
|
||||||
|
|
||||||
ivec3 a = { scaled.x, scaled.y, scaled.z };
|
|
||||||
vec3 factor = { scaled.x - a.x, scaled.y - a.y, scaled.z - a.z };
|
|
||||||
ivec3 b = {
|
|
||||||
(std::min)(static_cast<i32>(std::ceil(scaled.x)), precision.x),
|
|
||||||
(std::min)(static_cast<i32>(std::ceil(scaled.y)), precision.y),
|
|
||||||
(std::min)(static_cast<i32>(std::ceil(scaled.z)), precision.z) };
|
|
||||||
|
|
||||||
assert(index(b.x, b.y, b.z) < data.size());
|
|
||||||
|
|
||||||
// No vertical interpolation
|
|
||||||
if (precision.y == 0)
|
|
||||||
return Interp::bilerp(
|
|
||||||
data[index(a.x, a.y, a.z)], data[index(b.x, a.y, a.z)],
|
|
||||||
data[index(a.x, a.y, b.z)], data[index(b.x, a.y, b.z)],
|
|
||||||
factor.x, factor.z);
|
|
||||||
|
|
||||||
return Interp::trilerp(
|
|
||||||
data[index(a.x, a.y, a.z)], data[index(b.x, a.y, a.z)],
|
|
||||||
data[index(a.x, a.y, b.z)], data[index(b.x, a.y, b.z)],
|
|
||||||
data[index(a.x, b.y, a.z)], data[index(b.x, b.y, a.z)],
|
|
||||||
data[index(a.x, b.y, b.z)], data[index(b.x, b.y, b.z)],
|
|
||||||
factor.x, factor.z, factor.y);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline f32 operator[](u32 ind) {
|
||||||
|
return data[shiftIndexByOne(ind)];
|
||||||
|
}
|
||||||
|
|
||||||
|
// inline f32 get(vec3 pos) {
|
||||||
|
// vec3 scaled = pos * vec3(precision) / scaleBy;
|
||||||
|
//
|
||||||
|
// ivec3 a = { scaled.x, scaled.y, scaled.z };
|
||||||
|
// vec3 factor = { scaled.x - a.x, scaled.y - a.y, scaled.z - a.z };
|
||||||
|
// ivec3 b = {
|
||||||
|
// (std::min)(static_cast<i32>(std::ceil(scaled.x)), precision.x),
|
||||||
|
// (std::min)(static_cast<i32>(std::ceil(scaled.y)), precision.y),
|
||||||
|
// (std::min)(static_cast<i32>(std::ceil(scaled.z)), precision.z) };
|
||||||
|
//
|
||||||
|
// assert(index(b.x, b.y, b.z) < data.size());
|
||||||
|
//
|
||||||
|
// // No vertical interpolation
|
||||||
|
// if (precision.y == 0)
|
||||||
|
// return Interp::bilerp(
|
||||||
|
// data[index(a.x, a.y, a.z)], data[index(b.x, a.y, a.z)],
|
||||||
|
// data[index(a.x, a.y, b.z)], data[index(b.x, a.y, b.z)],
|
||||||
|
// factor.x, factor.z);
|
||||||
|
//
|
||||||
|
// return Interp::trilerp(
|
||||||
|
// data[index(a.x, a.y, a.z)], data[index(b.x, a.y, a.z)],
|
||||||
|
// data[index(a.x, a.y, b.z)], data[index(b.x, a.y, b.z)],
|
||||||
|
// data[index(a.x, b.y, a.z)], data[index(b.x, b.y, a.z)],
|
||||||
|
// data[index(a.x, b.y, b.z)], data[index(b.x, b.y, b.z)],
|
||||||
|
// factor.x, factor.z, factor.y);
|
||||||
|
// }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
inline u16 index(u16 x, u16 y, u16 z) {
|
void interpolateData();
|
||||||
return x * (precision.x + 1) * (precision.y + 1) + y * (precision.x + 1) + z;
|
|
||||||
|
// inline u32 innerIndex(u16 x, u16 y, u16 z) {
|
||||||
|
// return static_cast<u32>(x) * size.x * size.y + y * size.x + z;
|
||||||
|
// };
|
||||||
|
|
||||||
|
inline u32 index(u16vec3 pos) {
|
||||||
|
return index(pos.x, pos.y, pos.z);
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<f32> data {};
|
inline u32 index(u16 x, u16 y, u16 z) {
|
||||||
ivec3 precision {};
|
return static_cast<u32>(x) * (size.x + 1) * (size.y + 1) + y * (size.x + 1) + z;
|
||||||
vec3 scaleBy;
|
};
|
||||||
|
|
||||||
|
inline u32 shiftIndexByOne(u32 ind) {
|
||||||
|
u16vec3 vec = {};
|
||||||
|
|
||||||
|
let divY = size.y == 0 ? 1 : size.y;
|
||||||
|
|
||||||
|
vec.z = ind / (size.x * divY);
|
||||||
|
ind -= (vec.z * size.x * divY);
|
||||||
|
vec.y = ind / size.x;
|
||||||
|
vec.x = ind % size.x;
|
||||||
|
|
||||||
|
return index(vec);
|
||||||
|
};
|
||||||
|
|
||||||
|
u16vec3 size;
|
||||||
|
u16 precision;
|
||||||
|
vec<f32> data {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
#include "world/LocalWorld.h"
|
#include "world/LocalWorld.h"
|
||||||
#include "game/def/BlockDef.h"
|
#include "game/def/BlockDef.h"
|
||||||
#include "util/net/NetField.h"
|
#include "util/net/NetField.h"
|
||||||
#include "lua/usertype/Player.h"
|
|
||||||
#include "lua/usertype/Target.h"
|
#include "lua/usertype/Target.h"
|
||||||
#include "client/graph/Renderer.h"
|
#include "client/graph/Renderer.h"
|
||||||
#include "world/dim/chunk/Chunk.h"
|
#include "world/dim/chunk/Chunk.h"
|
||||||
|
|
|
@ -9,8 +9,8 @@ local menu = zepha.build_gui(function()
|
||||||
Gui.Rect {
|
Gui.Rect {
|
||||||
key = "inventory",
|
key = "inventory",
|
||||||
position = { pc(50), pc(50) },
|
position = { pc(50), pc(50) },
|
||||||
position_anchor = { pc(50), pc(55) },
|
position_anchor = { pc(50), pc(50) },
|
||||||
size = { 237, 187 },
|
size = { 342, 187 },
|
||||||
|
|
||||||
Gui.Rect {
|
Gui.Rect {
|
||||||
key = "backpack",
|
key = "backpack",
|
||||||
|
@ -63,10 +63,26 @@ local menu = zepha.build_gui(function()
|
||||||
list = "hot_wheel_6",
|
list = "hot_wheel_6",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Gui.Rect {
|
||||||
|
key = "player_frame",
|
||||||
|
position = { 105, 0 },
|
||||||
|
size = { 106, 187 },
|
||||||
|
background = "zeus:inventory:player_frame",
|
||||||
|
|
||||||
|
Gui.Model {
|
||||||
|
position = { 52, 150 },
|
||||||
|
scale = { 64, 64 },
|
||||||
|
|
||||||
|
type = "model",
|
||||||
|
source = "zeus:default:player",
|
||||||
|
texture = "zeus:default:player",
|
||||||
|
anim_range = { 0, 100 }
|
||||||
|
}
|
||||||
|
},
|
||||||
Gui.Rect {
|
Gui.Rect {
|
||||||
key = "equipment",
|
key = "equipment",
|
||||||
|
|
||||||
position = { 106, 1 },
|
position = { 209, 1 },
|
||||||
size = { 132, 80 },
|
size = { 132, 80 },
|
||||||
padding = { 18, 8, 8, 8 },
|
padding = { 18, 8, 8, 8 },
|
||||||
background = "zeus:inventory:equipment",
|
background = "zeus:inventory:equipment",
|
||||||
|
@ -76,23 +92,13 @@ local menu = zepha.build_gui(function()
|
||||||
|
|
||||||
position = { 41, 1 },
|
position = { 41, 1 },
|
||||||
size = { 34, 52 },
|
size = { 34, 52 },
|
||||||
overflow = "hidden",
|
overflow = "hidden"
|
||||||
|
|
||||||
Gui.Model {
|
|
||||||
position = { 15, 52 },
|
|
||||||
scale = { 28, 28 },
|
|
||||||
|
|
||||||
type = "model",
|
|
||||||
source = "zeus:default:player",
|
|
||||||
texture = "zeus:default:player",
|
|
||||||
anim_range = { 0, 100 }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Gui.Rect {
|
Gui.Rect {
|
||||||
key = "dynamic",
|
key = "dynamic",
|
||||||
|
|
||||||
position = { 106, 80 },
|
position = { 209, 80 },
|
||||||
size = { 132, 107 },
|
size = { 132, 107 },
|
||||||
padding = { 8, 8, 8, 8 },
|
padding = { 8, 8, 8, 8 },
|
||||||
background = "zeus:inventory:dynamic",
|
background = "zeus:inventory:dynamic",
|
||||||
|
|
|
@ -65,15 +65,20 @@ local noise = {
|
||||||
-- }}
|
-- }}
|
||||||
-- }
|
-- }
|
||||||
volume = {
|
volume = {
|
||||||
module = "scale_bias",
|
module = "scale",
|
||||||
scale = 3000,
|
y_scale = 2,
|
||||||
bias = -3500,
|
|
||||||
source = {
|
source = {
|
||||||
module = "scale_point",
|
module = "add",
|
||||||
y_scale = 2,
|
scalar = -2200,
|
||||||
source = {
|
source = {
|
||||||
module = "perlin",
|
module = "multiply",
|
||||||
frequency = 0.1
|
scalar = 3000,
|
||||||
|
source = {
|
||||||
|
module = "simplex",
|
||||||
|
frequency = 0.0025,
|
||||||
|
octaves = 6,
|
||||||
|
lacunarity = 2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,178 +1,178 @@
|
||||||
local identifier = "zeus:world:forest"
|
-- local identifier = "zeus:world:forest"
|
||||||
|
--
|
||||||
local wood = "zeus:default:wood"
|
-- local wood = "zeus:default:wood"
|
||||||
local leaf = "zeus:default:leaves"
|
-- local leaf = "zeus:default:leaves"
|
||||||
local none = "invalid"
|
-- local none = "invalid"
|
||||||
|
--
|
||||||
local structures = {}
|
-- local structures = {}
|
||||||
|
-- --
|
||||||
|
-- -- table.insert(structures, zepha.create_structure({
|
||||||
|
-- -- -- noise = {
|
||||||
|
-- -- -- module = "perlin",
|
||||||
|
-- -- -- frequency = 0.002,
|
||||||
|
-- -- -- octaves = 8
|
||||||
|
-- -- -- },
|
||||||
|
-- -- -- region_size = 4,
|
||||||
|
-- -- probability = 0.1,
|
||||||
|
-- -- -- origin = V{1, 1, 1},
|
||||||
|
-- -- origin = V(),
|
||||||
|
-- -- layout = {{{ "zeus:flowers:flower_geranium" }}}
|
||||||
|
-- -- }))
|
||||||
|
--
|
||||||
|
-- for i = 1, 5 do
|
||||||
|
-- table.insert(structures, zepha.create_structure({
|
||||||
|
-- origin = V(),
|
||||||
|
-- probability = 0.025,
|
||||||
|
-- layout = {{{ "zeus:default:tall_grass_" .. tostring(i) }}}
|
||||||
|
-- }))
|
||||||
|
-- end
|
||||||
|
-- --
|
||||||
|
-- table.insert(structures, zepha.create_structure({
|
||||||
|
-- origin = V(),
|
||||||
|
-- probability = 0.05,
|
||||||
|
-- layout = {{{ "zeus:flowers:flower_red_mushroom" }}}
|
||||||
|
-- }))
|
||||||
--
|
--
|
||||||
-- table.insert(structures, zepha.create_structure({
|
-- table.insert(structures, zepha.create_structure({
|
||||||
-- -- noise = {
|
-- origin = V(),
|
||||||
-- -- module = "perlin",
|
-- probability = 0.05,
|
||||||
-- -- frequency = 0.002,
|
-- layout = {{{ "zeus:flowers:flower_brown_mushroom" }}}
|
||||||
-- -- octaves = 8
|
|
||||||
-- -- },
|
|
||||||
-- -- region_size = 4,
|
|
||||||
-- probability = 0.1,
|
|
||||||
-- -- origin = V{1, 1, 1},
|
|
||||||
-- origin = V(),
|
|
||||||
-- layout = {{{ "zeus:flowers:flower_geranium" }}}
|
|
||||||
-- }))
|
-- }))
|
||||||
|
|
||||||
for i = 1, 5 do
|
|
||||||
table.insert(structures, zepha.create_structure({
|
|
||||||
origin = V(),
|
|
||||||
probability = 0.025,
|
|
||||||
layout = {{{ "zeus:default:tall_grass_" .. tostring(i) }}}
|
|
||||||
}))
|
|
||||||
end
|
|
||||||
--
|
--
|
||||||
table.insert(structures, zepha.create_structure({
|
-- table.insert(structures, zepha.create_structure({
|
||||||
origin = V(),
|
-- origin = V(1),
|
||||||
probability = 0.05,
|
-- probability = 0.1,
|
||||||
layout = {{{ "zeus:flowers:flower_red_mushroom" }}}
|
-- layout = {{
|
||||||
}))
|
-- { none, none, none },
|
||||||
|
-- { none, wood, none },
|
||||||
table.insert(structures, zepha.create_structure({
|
-- { none, none, none }
|
||||||
origin = V(),
|
-- }, {
|
||||||
probability = 0.05,
|
-- { none, leaf, none },
|
||||||
layout = {{{ "zeus:flowers:flower_brown_mushroom" }}}
|
-- { leaf, wood, leaf },
|
||||||
}))
|
-- { none, leaf, none }
|
||||||
|
-- }, {
|
||||||
table.insert(structures, zepha.create_structure({
|
-- { none, none, none },
|
||||||
origin = V(1),
|
-- { none, leaf, none },
|
||||||
probability = 0.1,
|
-- { none, none, none }
|
||||||
layout = {{
|
-- }}
|
||||||
{ none, none, none },
|
-- }))
|
||||||
{ none, wood, none },
|
--
|
||||||
{ none, none, none }
|
-- local woo = "zeus:default:wood"
|
||||||
}, {
|
-- local lea = "zeus:default:leaves"
|
||||||
{ none, leaf, none },
|
-- local inv = "invalid"
|
||||||
{ leaf, wood, leaf },
|
--
|
||||||
{ none, leaf, none }
|
-- local trunk_layer_0 = {
|
||||||
}, {
|
-- { inv, inv, inv, inv, inv },
|
||||||
{ none, none, none },
|
-- { inv, woo, woo, woo, inv },
|
||||||
{ none, leaf, none },
|
-- { inv, woo, woo, woo, inv },
|
||||||
{ none, none, none }
|
-- { inv, woo, woo, woo, inv },
|
||||||
}}
|
-- { inv, inv, inv, inv, inv }
|
||||||
}))
|
-- }
|
||||||
|
--
|
||||||
local woo = "zeus:default:wood"
|
-- local trunk_layer_1 = {
|
||||||
local lea = "zeus:default:leaves"
|
-- { inv, inv, inv, inv, inv },
|
||||||
local inv = "invalid"
|
-- { inv, inv, woo, inv, inv },
|
||||||
|
-- { inv, woo, woo, woo, inv },
|
||||||
local trunk_layer_0 = {
|
-- { inv, inv, woo, inv, inv },
|
||||||
{ inv, inv, inv, inv, inv },
|
-- { inv, inv, inv, inv, inv }
|
||||||
{ inv, woo, woo, woo, inv },
|
-- }
|
||||||
{ inv, woo, woo, woo, inv },
|
--
|
||||||
{ inv, woo, woo, woo, inv },
|
-- local trunk_layer_2 = {
|
||||||
{ inv, inv, inv, inv, inv }
|
-- { inv, inv, inv, inv, inv },
|
||||||
}
|
-- { inv, inv, inv, inv, inv },
|
||||||
|
-- { inv, inv, woo, inv, inv },
|
||||||
local trunk_layer_1 = {
|
-- { inv, inv, inv, inv, inv },
|
||||||
{ inv, inv, inv, inv, inv },
|
-- { inv, inv, inv, inv, inv }
|
||||||
{ inv, inv, woo, inv, inv },
|
-- }
|
||||||
{ inv, woo, woo, woo, inv },
|
--
|
||||||
{ inv, inv, woo, inv, inv },
|
-- local leaf_layer_1 = {
|
||||||
{ inv, inv, inv, inv, inv }
|
-- { inv, lea, lea, lea, inv },
|
||||||
}
|
-- { lea, lea, lea, lea, lea },
|
||||||
|
-- { lea, lea, woo, lea, lea },
|
||||||
local trunk_layer_2 = {
|
-- { lea, lea, lea, lea, lea },
|
||||||
{ inv, inv, inv, inv, inv },
|
-- { inv, lea, lea, lea, inv }
|
||||||
{ inv, inv, inv, inv, inv },
|
-- }
|
||||||
{ inv, inv, woo, inv, inv },
|
--
|
||||||
{ inv, inv, inv, inv, inv },
|
-- local leaf_layer_2 = {
|
||||||
{ inv, inv, inv, inv, inv }
|
-- { inv, inv, inv, inv, inv },
|
||||||
}
|
-- { inv, lea, lea, lea, inv },
|
||||||
|
-- { inv, lea, woo, lea, inv },
|
||||||
local leaf_layer_1 = {
|
-- { inv, lea, lea, lea, inv },
|
||||||
{ inv, lea, lea, lea, inv },
|
-- { inv, inv, inv, inv, inv }
|
||||||
{ lea, lea, lea, lea, lea },
|
-- }
|
||||||
{ lea, lea, woo, lea, lea },
|
--
|
||||||
{ lea, lea, lea, lea, lea },
|
-- local leaf_layer_3 = {
|
||||||
{ inv, lea, lea, lea, inv }
|
-- { inv, inv, inv, inv, inv },
|
||||||
}
|
-- { inv, lea, lea, inv, inv },
|
||||||
|
-- { inv, lea, lea, lea, inv },
|
||||||
local leaf_layer_2 = {
|
-- { inv, inv, lea, lea, inv },
|
||||||
{ inv, inv, inv, inv, inv },
|
-- { inv, inv, inv, inv, inv }
|
||||||
{ inv, lea, lea, lea, inv },
|
-- }
|
||||||
{ inv, lea, woo, lea, inv },
|
--
|
||||||
{ inv, lea, lea, lea, inv },
|
-- table.insert(structures, zepha.create_structure({
|
||||||
{ inv, inv, inv, inv, inv }
|
-- origin = V(2, 2, 2),
|
||||||
}
|
-- probability = 0.05,
|
||||||
|
-- layout = {
|
||||||
local leaf_layer_3 = {
|
-- trunk_layer_0,
|
||||||
{ inv, inv, inv, inv, inv },
|
-- trunk_layer_0,
|
||||||
{ inv, lea, lea, inv, inv },
|
-- trunk_layer_0,
|
||||||
{ inv, lea, lea, lea, inv },
|
-- trunk_layer_0,
|
||||||
{ inv, inv, lea, lea, inv },
|
-- trunk_layer_1,
|
||||||
{ inv, inv, inv, inv, inv }
|
-- trunk_layer_1,
|
||||||
}
|
-- trunk_layer_1,
|
||||||
|
-- trunk_layer_2,
|
||||||
table.insert(structures, zepha.create_structure({
|
-- trunk_layer_2,
|
||||||
origin = V(2, 2, 2),
|
-- trunk_layer_2,
|
||||||
probability = 0.05,
|
-- trunk_layer_2,
|
||||||
layout = {
|
-- trunk_layer_2,
|
||||||
trunk_layer_0,
|
-- trunk_layer_2,
|
||||||
trunk_layer_0,
|
-- trunk_layer_2,
|
||||||
trunk_layer_0,
|
-- trunk_layer_2,
|
||||||
trunk_layer_0,
|
-- trunk_layer_2,
|
||||||
trunk_layer_1,
|
-- trunk_layer_2,
|
||||||
trunk_layer_1,
|
-- trunk_layer_2,
|
||||||
trunk_layer_1,
|
-- leaf_layer_2,
|
||||||
trunk_layer_2,
|
-- leaf_layer_1,
|
||||||
trunk_layer_2,
|
-- leaf_layer_1,
|
||||||
trunk_layer_2,
|
-- leaf_layer_1,
|
||||||
trunk_layer_2,
|
-- leaf_layer_1,
|
||||||
trunk_layer_2,
|
-- leaf_layer_2,
|
||||||
trunk_layer_2,
|
-- leaf_layer_3
|
||||||
trunk_layer_2,
|
-- }
|
||||||
trunk_layer_2,
|
-- }))
|
||||||
trunk_layer_2,
|
--
|
||||||
trunk_layer_2,
|
-- local noise = {
|
||||||
trunk_layer_2,
|
-- -- heightmap = runfile(_PATH .. 'world_noise'),
|
||||||
leaf_layer_2,
|
-- volume = {
|
||||||
leaf_layer_1,
|
-- module = "scale_bias",
|
||||||
leaf_layer_1,
|
-- scale = 3000,
|
||||||
leaf_layer_1,
|
-- bias = -3500,
|
||||||
leaf_layer_1,
|
-- source = {
|
||||||
leaf_layer_2,
|
-- module = "scale_point",
|
||||||
leaf_layer_3
|
-- y_scale = 2,
|
||||||
}
|
-- source = {
|
||||||
}))
|
-- module = "perlin",
|
||||||
|
-- frequency = 0.1
|
||||||
local noise = {
|
-- }
|
||||||
-- heightmap = runfile(_PATH .. 'world_noise'),
|
-- }
|
||||||
volume = {
|
-- }
|
||||||
module = "scale_bias",
|
-- }
|
||||||
scale = 3000,
|
--
|
||||||
bias = -3500,
|
-- zepha.register_biome(identifier, {
|
||||||
source = {
|
-- environment = {
|
||||||
module = "scale_point",
|
-- temperature = 25/100,
|
||||||
y_scale = 2,
|
-- humidity = 70/100,
|
||||||
source = {
|
-- roughness = 20/100,
|
||||||
module = "perlin",
|
-- },
|
||||||
frequency = 0.1
|
-- blocks = {
|
||||||
}
|
-- top = "zeus:default:grass",
|
||||||
}
|
-- soil = "zeus:default:dirt",
|
||||||
}
|
-- rock = "zeus:default:stone"
|
||||||
}
|
-- },
|
||||||
|
-- tags = { natural = 1, default = 1 },
|
||||||
zepha.register_biome(identifier, {
|
-- structures = structures,
|
||||||
environment = {
|
-- biome_tint = "#aaed45",
|
||||||
temperature = 25/100,
|
-- noise = noise
|
||||||
humidity = 70/100,
|
-- })
|
||||||
roughness = 20/100,
|
--
|
||||||
},
|
-- return identifier
|
||||||
blocks = {
|
|
||||||
top = "zeus:default:grass",
|
|
||||||
soil = "zeus:default:dirt",
|
|
||||||
rock = "zeus:default:stone"
|
|
||||||
},
|
|
||||||
tags = { natural = 1, default = 1 },
|
|
||||||
structures = structures,
|
|
||||||
biome_tint = "#aaed45",
|
|
||||||
noise = noise
|
|
||||||
})
|
|
||||||
|
|
||||||
return identifier
|
|
|
@ -144,15 +144,20 @@ table.insert(structures, zepha.create_structure({
|
||||||
local noise = {
|
local noise = {
|
||||||
-- heightmap = runfile(_PATH .. 'world_noise'),
|
-- heightmap = runfile(_PATH .. 'world_noise'),
|
||||||
volume = {
|
volume = {
|
||||||
module = "scale_bias",
|
module = "scale",
|
||||||
scale = 3000,
|
y_scale = 2,
|
||||||
bias = -3500,
|
|
||||||
source = {
|
source = {
|
||||||
module = "scale_point",
|
module = "add",
|
||||||
y_scale = 2,
|
scalar = -2200,
|
||||||
source = {
|
source = {
|
||||||
module = "perlin",
|
module = "multiply",
|
||||||
frequency = 0.1
|
scalar = 3000,
|
||||||
|
source = {
|
||||||
|
module = "simplex",
|
||||||
|
frequency = 0.0025,
|
||||||
|
octaves = 6,
|
||||||
|
lacunarity = 2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
return {
|
return {
|
||||||
runfile(_PATH.."biomes/plains"),
|
runfile(_PATH .. "biomes/plains"),
|
||||||
runfile(_PATH.."biomes/highlands"),
|
runfile(_PATH .. "biomes/highlands"),
|
||||||
runfile(_PATH.."biomes/desert"),
|
runfile(_PATH .. "biomes/desert"),
|
||||||
runfile(_PATH.."biomes/forest")
|
runfile(_PATH .. "biomes/forest")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue