--- /dev/null
+I want to thank:
+
+Glenn Fiedler
+ for his excellent series on game programming in general and
+ networked physics in particular. The site is
+ http://gafferongames.com/ and I can highly recommend his articles.
+
+Stefan Gustavson
+ for his implementation of simplex noise. The one I'm using doesn't
+ resemble much of his original due to language adaptation,
+ customization, and optimization, but I started out from his and
+ it's working nicely.
+
+The libnoise Project
+ despite not using their library I have learned a lot about noise
+ and terrain generation there. The library as well as documentation
+ can be found on sourceforge: http://libnoise.sourceforge.net/
+
+The Folks at http://www.opengl-tutorial.org/
+ I made my first steps with OpenGL following this tutorial (and this
+ very project, as one can witness in earlier commits) and from there
+ acquired the rest through reading their sources and the OpenGL
+ documentation itself.
}
TextureIndex tex_index;
master.GetEnv().loader.LoadBlockTypes("default", block_types, tex_index);
- interface.SetInventorySlots(block_types.Size() - 1);
+ interface.SetInventorySlots(block_types.size() - 1);
chunk_renderer.LoadTextures(master.GetEnv().loader, tex_index);
chunk_renderer.FogDensity(master.GetWorldConf().fog_density);
skeletons.Load();
Block block;
pack.ReadIndex(index, i);
pack.ReadBlock(block, i);
- if (index < Chunk::size && block.type < block_types.Size()) {
+ if (index < Chunk::size && block.type < block_types.size()) {
manip.SetBlock(*chunk, index, block);
}
}
namespace blank {
-/// (3D only) adaptation of Stefan Gustavson's SimplexNoise java class
class SimplexNoise {
public:
int perm[512];
int perm12[512];
glm::vec3 grad[12];
+ glm::ivec3 second_ints[8];
+ glm::ivec3 third_ints[8];
+ glm::vec3 second_floats[8];
+ glm::vec3 third_floats[8];
};
{ 0.0f, -1.0f, 1.0f },
{ 0.0f, 1.0f, -1.0f },
{ 0.0f, -1.0f, -1.0f },
+})
+, second_ints({
+ // x>y x>z y>z
+ { 0, 0, 1 }, // 0 0 0 ZYX
+ { 0, 1, 0 }, // 0 0 1 YZX
+ { 0, 0, 1 }, // 0 1 0 illogical, but ZYX
+ { 0, 1, 0 }, // 0 1 1 YXZ
+ { 0, 0, 1 }, // 1 0 0 ZXY
+ { 1, 0, 0 }, // 1 0 1 illogical, but XYZ
+ { 1, 0, 0 }, // 1 1 0 XZY
+ { 1, 0, 0 }, // 1 1 1 XYZ
+})
+, third_ints({
+ // x>y x>z y>z
+ { 0, 1, 1 }, // 0 0 0 ZYX
+ { 0, 1, 1 }, // 0 0 1 YZX
+ { 0, 1, 1 }, // 0 1 0 illogical, but ZYX
+ { 1, 1, 0 }, // 0 1 1 YXZ
+ { 1, 0, 1 }, // 1 0 0 ZXY
+ { 1, 1, 0 }, // 1 0 1 illogical, but XYZ
+ { 1, 0, 1 }, // 1 1 0 XZY
+ { 1, 1, 0 }, // 1 1 1 XYZ
+})
+, second_floats({
+ // x>y x>z y>z
+ { 0.0f, 0.0f, 1.0f }, // 0 0 0 ZYX
+ { 0.0f, 1.0f, 0.0f }, // 0 0 1 YZX
+ { 0.0f, 0.0f, 1.0f }, // 0 1 0 illogical, but ZYX
+ { 0.0f, 1.0f, 0.0f }, // 0 1 1 YXZ
+ { 0.0f, 0.0f, 1.0f }, // 1 0 0 ZXY
+ { 1.0f, 0.0f, 0.0f }, // 1 0 1 illogical, but XYZ
+ { 1.0f, 0.0f, 0.0f }, // 1 1 0 XZY
+ { 1.0f, 0.0f, 0.0f }, // 1 1 1 XYZ
+})
+, third_floats({
+ // x>y x>z y>z
+ { 0.0f, 1.0f, 1.0f }, // 0 0 0 ZYX
+ { 0.0f, 1.0f, 1.0f }, // 0 0 1 YZX
+ { 0.0f, 1.0f, 1.0f }, // 0 1 0 illogical, but ZYX
+ { 1.0f, 1.0f, 0.0f }, // 0 1 1 YXZ
+ { 1.0f, 0.0f, 1.0f }, // 1 0 0 ZXY
+ { 1.0f, 1.0f, 0.0f }, // 1 0 1 illogical, but XYZ
+ { 1.0f, 0.0f, 1.0f }, // 1 1 0 XZY
+ { 1.0f, 1.0f, 0.0f }, // 1 1 1 XYZ
}) {
GaloisLFSR random(seed ^ 0x0123456789ACBDEF);
unsigned char value;
glm::vec3 unskewed(skewed - tr);
glm::vec3 relative(in - unskewed);
- glm::vec3 second, third;
-
- if (relative.x >= relative.y) {
- if (relative.y >= relative.z) {
- second = { 1, 0, 0 };
- third = { 1, 1, 0 };
- } else if (relative.x >= relative.z) {
- second = { 1, 0, 0 };
- third = { 1, 0, 1 };
- } else {
- second = { 0, 0, 1 };
- third = { 1, 0, 1 };
- }
- } else if (relative.y < relative.z) {
- second = { 0, 0, 1 };
- third = { 0, 1, 1 };
- } else if (relative.x < relative.z) {
- second = { 0, 1, 0 };
- third = { 0, 1, 1 };
- } else {
- second = { 0, 1, 0 };
- third = { 1, 1, 0 };
- }
+ bool x_ge_y = relative.x >= relative.y;
+ bool x_ge_z = relative.x >= relative.z;
+ bool y_ge_z = relative.y >= relative.z;
+ unsigned int st = (x_ge_y << 2) | (x_ge_z << 1) | y_ge_z;
+
+ glm::ivec3 second_int(second_ints[st]);
+ glm::ivec3 third_int(third_ints[st]);
+ glm::vec3 second_float(second_floats[st]);
+ glm::vec3 third_float(third_floats[st]);
glm::vec3 offset[4] = {
in - unskewed,
- relative - second + one_sixth,
- relative - third + one_third,
+ relative - second_float + one_sixth,
+ relative - third_float + one_third,
relative - 0.5f,
};
float n = 0.0f;
+ // I know 0.6 is wrong, but for some reason it looks better than 0.5
+
// 0
- float t = 0.6f - dot(offset[0], offset[0]);
- if (t > 0.0f) {
- t *= t;
- int corner = Perm12(index[0] + Perm(index[1] + Perm(index[2])));
- n += t * t * dot(Grad(corner), offset[0]);
- }
+ float t = glm::clamp(0.6f - dot(offset[0], offset[0]), 0.0f, 1.0f);
+ t *= t;
+ int corner = Perm12(index[0] + Perm(index[1] + Perm(index[2])));
+ n += t * t * dot(Grad(corner), offset[0]);
// 1
- t = 0.6f - dot(offset[1], offset[1]);
- if (t > 0.0f) {
- t *= t;
- int corner = Perm12(index[0] + int(second.x) + Perm(index[1] + int(second.y) + Perm(index[2] + int(second.z))));
- n += t * t * dot(Grad(corner), offset[1]);
- }
+ t = glm::clamp(0.6f - dot(offset[1], offset[1]), 0.0f, 1.0f);
+ t *= t;
+ corner = Perm12(index[0] + second_int.x + Perm(index[1] + second_int.y + Perm(index[2] + second_int.z)));
+ n += t * t * dot(Grad(corner), offset[1]);
// 2
- t = 0.6f - dot(offset[2], offset[2]);
- if (t > 0.0f) {
- t *= t;
- int corner = Perm12(index[0] + int(third.x) + Perm(index[1] + int(third.y) + Perm(index[2] + int(third.z))));
- n += t * t * dot(Grad(corner), offset[2]);
- }
+ t = glm::clamp(0.6f - dot(offset[2], offset[2]), 0.0f, 1.0f);
+ t *= t;
+ corner = Perm12(index[0] + third_int.x + Perm(index[1] + third_int.y + Perm(index[2] + third_int.z)));
+ n += t * t * dot(Grad(corner), offset[2]);
// 3
- t = 0.6f - dot(offset[3], offset[3]);
- if (t > 0.0f) {
- t *= t;
- int corner = Perm12(index[0] + 1 + Perm(index[1] + 1 + Perm(index[2] + 1)));
- n += t * t * dot(Grad(corner), offset[3]);
- }
+ t = glm::clamp(0.6f - dot(offset[3], offset[3]), 0.0f, 1.0f);
+ t *= t;
+ corner = Perm12(index[0] + 1 + Perm(index[1] + 1 + Perm(index[2] + 1)));
+ n += t * t * dot(Grad(corner), offset[3]);
return 32.0f * n;
}
, block_types()
, world(block_types, wc)
, spawn_index(world.Chunks().MakeIndex(wc.spawn, 3))
-, generator(gc, block_types)
+, generator(gc)
, chunk_loader(world.Chunks(), generator, ws)
, skeletons()
, spawner(world, skeletons, env.rng)
, loop_timer(16) {
TextureIndex tex_index;
env.loader.LoadBlockTypes("default", block_types, tex_index);
- generator.Scan();
+ generator.LoadTypes(block_types);
skeletons.LoadHeadless();
spawner.LimitSkeletons(1, skeletons.size());
server.SetPlayerModel(skeletons[0]);
, manip(env, player.GetEntity())
, input(world, player, manip)
, interface(config, env.keymap, input, *this)
-, generator(gc, block_types)
+, generator(gc)
, chunk_loader(world.Chunks(), generator, save)
, chunk_renderer(player.GetChunks())
, skeletons()
, unload(env, world.Chunks(), save) {
TextureIndex tex_index;
env.loader.LoadBlockTypes("default", block_types, tex_index);
- interface.SetInventorySlots(block_types.Size() - 1);
- generator.Scan();
+ interface.SetInventorySlots(block_types.size() - 1);
+ generator.LoadTypes(block_types);
chunk_renderer.LoadTextures(env.loader, tex_index);
chunk_renderer.FogDensity(wc.fog_density);
skeletons.Load();
class BlockTypeRegistry {
public:
- BlockTypeRegistry();
+ using size_type = std::vector<BlockType>::size_type;
+ using reference = std::vector<BlockType>::reference;
+ using const_reference = std::vector<BlockType>::const_reference;
+ using iterator = std::vector<BlockType>::iterator;
+ using const_iterator = std::vector<BlockType>::const_iterator;
public:
+ BlockTypeRegistry();
+
Block::Type Add(const BlockType &);
- size_t Size() const noexcept { return types.size(); }
+ size_t size() const noexcept { return types.size(); }
+
+ iterator begin() noexcept { return types.begin(); }
+ const_iterator begin() const noexcept { return types.begin(); }
+ iterator end() noexcept { return types.end(); }
+ const_iterator end() const noexcept { return types.end(); }
BlockType &operator [](Block::Type id) { return types[id]; }
const BlockType &operator [](Block::Type id) const { return types[id]; }
void *BlockData() noexcept { return &blocks[0]; }
const void *BlockData() const noexcept { return &blocks[0]; }
- static constexpr std::size_t BlockSize() noexcept { return sizeof(blocks) + sizeof(light); }
+ static constexpr std::size_t BlockSize() noexcept { return offsetof(Chunk, position) - offsetof(Chunk, blocks); }
+
+ bool Generated() const noexcept { return generated; }
+ void SetGenerated() noexcept { generated = true; }
+ bool Lighted() const noexcept { return lighted; }
+ void ScanLights();
void Ref() noexcept { ++ref_count; }
void UnRef() noexcept { --ref_count; }
private:
const BlockTypeRegistry *types;
Chunk *neighbor[Block::FACE_COUNT];
+
Block blocks[size];
unsigned char light[size];
+ bool generated;
+ bool lighted;
+
Pos position;
int ref_count;
bool dirty_model;
public:
bool InRange(const Chunk::Pos &) const noexcept;
+ bool IsBorder(const Chunk::Pos &) const noexcept;
+ int Distance(const Chunk::Pos &) const noexcept;
+
+ bool HasAllSurrounding(const Chunk::Pos &) const noexcept;
+
int IndexOf(const Chunk::Pos &) const noexcept;
Chunk::Pos PositionOf(int) const noexcept;
+
/// returns nullptr if given position is out of range or the chunk
/// is not loaded, so also works as a "has" function
Chunk *Get(const Chunk::Pos &) noexcept;
ChunkIndex &MakeIndex(const Chunk::Pos &base, int extent);
void UnregisterIndex(ChunkIndex &);
+ ChunkIndex *ClosestIndex(const Chunk::Pos &pos);
+
/// returns nullptr if given position is not loaded
Chunk *Get(const Chunk::Pos &);
/// returns nullptr if given position is not indexed
}
-Generator::Generator(const Config &config, const BlockTypeRegistry &types) noexcept
+Generator::Generator(const Config &config) noexcept
: config(config)
-, types(types)
+, types()
+, min_solidity(2.0f)
, solidity_noise(config.seed ^ config.solidity.seed_mask)
, humidity_noise(config.seed ^ config.humidity.seed_mask)
, temperature_noise(config.seed ^ config.temperature.seed_mask)
}
-void Generator::Scan() {
- size_t count = 0;
- for (size_t i = 0, end = types.Size(); i < end; ++i) {
- if (types[i].generate) ++count;
+void Generator::LoadTypes(const BlockTypeRegistry ®) {
+ types.clear();
+ min_solidity = 2.0f;
+ for (const BlockType &type : reg) {
+ if (type.generate) {
+ types.push_back(&type);
+ if (type.min_solidity < min_solidity) {
+ min_solidity = type.min_solidity;
+ }
+ }
}
- candidates.reserve(count);
+ candidates.reserve(types.size());
}
void Generator::operator ()(Chunk &chunk) const noexcept {
}
}
}
+ chunk.SetGenerated();
}
Block Generator::Generate(const glm::vec3 &pos) const noexcept {
float solidity = GetValue(solidity_noise, pos, config.solidity);
+ if (solidity < min_solidity) {
+ return Block(0);
+ }
float humidity = GetValue(humidity_noise, pos, config.humidity);
float temperature = GetValue(temperature_noise, pos, config.temperature);
float richness = GetValue(richness_noise, pos, config.richness);
candidates.clear();
float total = 0.0f;
- for (size_t i = 0, end = types.Size(); i < end; ++i) {
- const BlockType &type = types[i];
- if (!type.generate) continue;
- if (solidity < type.min_solidity || solidity > type.max_solidity) continue;
- if (humidity < type.min_humidity || humidity > type.max_humidity) continue;
- if (temperature < type.min_temperature || temperature > type.max_temperature) continue;
- if (richness < type.min_richness || richness > type.max_richness) continue;
- float solidity_match = 4.0f - ((solidity - type.mid_solidity) * (solidity - type.mid_solidity));
- float humidity_match = 4.0f - ((humidity - type.mid_humidity) * (humidity - type.mid_humidity));
- float temperature_match = 4.0f - ((temperature - type.mid_temperature) * (temperature - type.mid_temperature));
- float richness_match = 4.0f - ((richness - type.mid_richness) * (richness - type.mid_richness));
- float chance = (solidity_match + humidity_match + temperature_match + richness_match) * type.commonness;
+ for (const BlockType *type : types) {
+ if (solidity < type->min_solidity || solidity > type->max_solidity) continue;
+ if (humidity < type->min_humidity || humidity > type->max_humidity) continue;
+ if (temperature < type->min_temperature || temperature > type->max_temperature) continue;
+ if (richness < type->min_richness || richness > type->max_richness) continue;
+ float solidity_match = 4.0f - ((solidity - type->mid_solidity) * (solidity - type->mid_solidity));
+ float humidity_match = 4.0f - ((humidity - type->mid_humidity) * (humidity - type->mid_humidity));
+ float temperature_match = 4.0f - ((temperature - type->mid_temperature) * (temperature - type->mid_temperature));
+ float richness_match = 4.0f - ((richness - type->mid_richness) * (richness - type->mid_richness));
+ float chance = (solidity_match + humidity_match + temperature_match + richness_match) * type->commonness;
total += chance;
- candidates.emplace_back(&type, total);
+ candidates.emplace_back(type, total);
}
if (candidates.empty()) {
return Block(0);
#define BLANK_WORLD_GENERATOR_HPP_
#include "../rand/SimplexNoise.hpp"
-#include "../rand/WorleyNoise.hpp"
#include <cstdint>
+#include <vector>
#include <glm/glm.hpp>
namespace blank {
class Block;
+class BlockType;
class BlockTypeRegistry;
class Chunk;
float growth;
};
NoiseParam solidity = { 0xA85033F6BCBDD110, 3, 0.5f, 1.0f/64.0f, 2.0f, 2.0f };
- NoiseParam humidity = { 0x3A463FB24B04A901, 3, 0.5f, 1.0f/256.0f, 2.0f, 2.0f };
- NoiseParam temperature = { 0x2530BA6C6134A9FB, 3, 0.5f, 1.0f/512.0f, 2.0f, 2.0f };
+ NoiseParam humidity = { 0x3A463FB24B04A901, 2, 0.5f, 1.0f/512.0f, 2.0f, 2.0f };
+ NoiseParam temperature = { 0x2530BA6C6134A9FB, 2, 0.5f, 1.0f/1024.0f, 2.0f, 2.0f };
NoiseParam richness = { 0x95A179F180103446, 3, 0.5f, 1.0f/128.0f, 2.0f, 2.0f };
NoiseParam randomness = { 0x074453EEE1496390, 3, 0.5f, 1.0f/16.0f, 2.0f, 2.0f };
};
- explicit Generator(const Config &, const BlockTypeRegistry &) noexcept;
+ explicit Generator(const Config &) noexcept;
- // scan types for generation
- void Scan();
+ void LoadTypes(const BlockTypeRegistry &);
void operator ()(Chunk &) const noexcept;
private:
const Config &config;
- const BlockTypeRegistry &types;
+ std::vector<const BlockType *> types;
+ float min_solidity;
+
SimplexNoise solidity_noise;
SimplexNoise humidity_noise;
SimplexNoise temperature_noise;
, neighbor{0}
, blocks{}
, light{0}
+, generated(false)
+, lighted(false)
, position(0, 0, 0)
, ref_count(0)
, dirty_model(false)
blocks[index] = block;
Invalidate();
- if (&old_type == &new_type) return;
+ if (!lighted || &old_type == &new_type) return;
if (new_type.luminosity > old_type.luminosity) {
// light added
}
}
-namespace {
-
-// propagate light from a's edge to b
-void edge_light(
- Chunk &a, const Chunk::Pos &a_pos,
- Chunk &b, const Chunk::Pos &b_pos
-) noexcept {
- if (a.GetLight(a_pos) > 1) {
- const BlockType &b_type = b.Type(Chunk::ToIndex(b_pos));
- if (!b_type.block_light) {
- light_queue.emplace(&a, a_pos);
- }
- if (b_type.visible) {
- b.Invalidate();
+void Chunk::ScanLights() {
+ int idx = 0;
+ Pos pos(0, 0, 0);
+ for (; pos.z < depth; ++pos.z) {
+ for (pos.y = 0; pos.y < height; ++pos.y) {
+ for (pos.x = 0; pos.x < width; ++pos.x, ++idx) {
+ const BlockType &type = Type(blocks[idx]);
+ if (type.luminosity) {
+ SetLight(idx, type.luminosity);
+ light_queue.emplace(this, pos);
+ }
+ }
}
}
-}
-
+ work_light();
+ lighted = true;
}
void Chunk::SetNeighbor(Block::Face face, Chunk &other) noexcept {
neighbor[face] = &other;
other.neighbor[Block::Opposite(face)] = this;
-
- switch (face) {
-
- default:
- // error
- break;
-
- case Block::FACE_LEFT:
- for (int z = 0; z < depth; ++z) {
- for (int y = 0; y < height; ++y) {
- Pos my_pos(0, y, z);
- Pos other_pos(width - 1, y, z);
- edge_light(*this, my_pos, other, other_pos);
- edge_light(other, other_pos, *this, my_pos);
- }
- }
- break;
-
- case Block::FACE_RIGHT:
- for (int z = 0; z < depth; ++z) {
- for (int y = 0; y < height; ++y) {
- Pos my_pos(width - 1, y, z);
- Pos other_pos(0, y, z);
- edge_light(*this, my_pos, other, other_pos);
- edge_light(other, other_pos, *this, my_pos);
- }
- }
- break;
-
- case Block::FACE_DOWN:
- for (int z = 0; z < depth; ++z) {
- for (int x = 0; x < width; ++x) {
- Pos my_pos(x, 0, z);
- Pos other_pos(x, height - 1, z);
- edge_light(*this, my_pos, other, other_pos);
- edge_light(other, other_pos, *this, my_pos);
- }
- }
- break;
-
- case Block::FACE_UP:
- for (int z = 0; z < depth; ++z) {
- for (int x = 0; x < width; ++x) {
- Pos my_pos(x, height - 1, z);
- Pos other_pos(x, 0, z);
- edge_light(*this, my_pos, other, other_pos);
- edge_light(other, other_pos, *this, my_pos);
- }
- }
- break;
-
- case Block::FACE_BACK:
- for (int y = 0; y < height; ++y) {
- for (int x = 0; x < width; ++x) {
- Pos my_pos(x, y, 0);
- Pos other_pos(x, y, depth - 1);
- edge_light(*this, my_pos, other, other_pos);
- edge_light(other, other_pos, *this, my_pos);
- }
- }
- break;
-
- case Block::FACE_FRONT:
- for (int y = 0; y < height; ++y) {
- for (int x = 0; x < width; ++x) {
- Pos my_pos(x, y, depth - 1);
- Pos other_pos(x, y, 0);
- edge_light(*this, my_pos, other, other_pos);
- edge_light(other, other_pos, *this, my_pos);
- }
- }
- break;
-
- }
- work_light();
}
void Chunk::Unlink() noexcept {
return false;
}
+ bool generated = false;
if (save.Exists(pos)) {
save.Read(*chunk);
- return false;
} else {
gen(*chunk);
- return true;
+ generated = true;
}
+
+ ChunkIndex *index = store.ClosestIndex(pos);
+ if (!index) {
+ return generated;
+ }
+
+ Chunk::Pos begin(pos - Chunk::Pos(1));
+ Chunk::Pos end(pos + Chunk::Pos(2));
+ for (Chunk::Pos iter(begin); iter.z < end.z; ++iter.z) {
+ for (iter.y = begin.y; iter.y < end.y; ++iter.y) {
+ for (iter.x = begin.x; iter.x < end.x; ++iter.x) {
+ if (index->IsBorder(iter)) continue;
+ Chunk *light_chunk = index->Get(iter);
+ if (!light_chunk || light_chunk->Lighted()) continue;
+ if (index->HasAllSurrounding(iter)) {
+ light_chunk->ScanLights();
+ }
+ }
+ }
+ }
+
+ return generated;
}
void ChunkLoader::LoadN(std::size_t n) {
}
bool ChunkIndex::InRange(const Chunk::Pos &pos) const noexcept {
- return manhattan_radius(pos - base) <= extent;
+ return Distance(pos) <= extent;
+}
+
+bool ChunkIndex::IsBorder(const Chunk::Pos &pos) const noexcept {
+ return Distance(pos) == extent;
+}
+
+int ChunkIndex::Distance(const Chunk::Pos &pos) const noexcept {
+ return manhattan_radius(pos - base);
+}
+
+bool ChunkIndex::HasAllSurrounding(const Chunk::Pos &pos) const noexcept {
+ Chunk::Pos begin(pos - Chunk::Pos(1));
+ Chunk::Pos end(pos + Chunk::Pos(2));
+ for (Chunk::Pos iter(begin); iter.z < end.z; ++iter.z) {
+ for (iter.y = begin.y; iter.y < end.y; ++iter.y) {
+ for (iter.x = begin.x; iter.x < end.x; ++iter.x) {
+ if (!Get(iter)) return false;
+ }
+ }
+ }
+ return true;
}
int ChunkIndex::IndexOf(const Chunk::Pos &pos) const noexcept {
}
}
+ChunkIndex *ChunkStore::ClosestIndex(const Chunk::Pos &pos) {
+ ChunkIndex *closest_index = nullptr;
+ int closest_distance = std::numeric_limits<int>::max();
+
+ for (ChunkIndex &index : indices) {
+ int distance = index.Distance(pos);
+ if (distance < closest_distance) {
+ closest_index = &index;
+ closest_distance = distance;
+ }
+ }
+
+ return closest_index;
+}
+
Chunk *ChunkStore::Get(const Chunk::Pos &pos) {
for (ChunkIndex &index : indices) {
Chunk *chunk = index.Get(pos);
--- /dev/null
+#include "StabilityTest.hpp"
+
+#include "rand/GaloisLFSR.hpp"
+#include "rand/SimplexNoise.hpp"
+
+#include <cstdint>
+#include <string>
+#include <sstream>
+#include <glm/gtx/io.hpp>
+
+CPPUNIT_TEST_SUITE_REGISTRATION(blank::test::StabilityTest);
+
+using namespace std;
+
+
+namespace blank {
+namespace test {
+
+void StabilityTest::setUp() {
+
+}
+
+void StabilityTest::tearDown() {
+
+}
+
+
+void StabilityTest::testRNG() {
+ GaloisLFSR random(0);
+ uint16_t value;
+ random(value);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected value #1 from RNG",
+ uint16_t(0x0000), value
+ );
+ random(value);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected value #2 from RNG",
+ uint16_t(0x0000), value
+ );
+ random(value);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected value #3 from RNG",
+ uint16_t(0xB000), value
+ );
+ random(value);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected value #4 from RNG",
+ uint16_t(0x0000), value
+ );
+ random(value);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected value #5 from RNG",
+ uint16_t(0x0000), value
+ );
+ random(value);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected value #6 from RNG",
+ uint16_t(0x0000), value
+ );
+ random(value);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected value #7 from RNG",
+ uint16_t(0x4500), value
+ );
+ random(value);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected value #8 from RNG",
+ uint16_t(0x0000), value
+ );
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected value #9 from RNG",
+ uint16_t(0x0000), value
+ );
+ random(value);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected value #10 from RNG",
+ uint16_t(0x0000), value
+ );
+ random(value);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected value #11 from RNG",
+ uint16_t(0x0000), value
+ );
+ random(value);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected value #12 from RNG",
+ uint16_t(0x2E70), value
+ );
+ random(value);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected value #13 from RNG",
+ uint16_t(0x0000), value
+ );
+ random(value);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected value #14 from RNG",
+ uint16_t(0x0000), value
+ );
+ random(value);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected value #15 from RNG",
+ uint16_t(0x0000), value
+ );
+ random(value);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected value #16 from RNG",
+ uint16_t(0x1011), value
+ );
+ random(value);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected value #17 from RNG",
+ uint16_t(0x0000), value
+ );
+ random(value);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected value #18 from RNG",
+ uint16_t(0x0000), value
+ );
+ random(value);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected value #19 from RNG",
+ uint16_t(0xB000), value
+ );
+ random(value);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected value #20 from RNG",
+ uint16_t(0x0B0B), value
+ );
+ random(value);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected value #21 from RNG",
+ uint16_t(0x0000), value
+ );
+ random(value);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected value #22 from RNG",
+ uint16_t(0x0000), value
+ );
+ random(value);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected value #23 from RNG",
+ uint16_t(0x1500), value
+ );
+ random(value);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected value #24 from RNG",
+ uint16_t(0x0454), value
+ );
+ random(value);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected value #25 from RNG",
+ uint16_t(0x0000), value
+ );
+ random(value);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected value #26 from RNG",
+ uint16_t(0x0000), value
+ );
+ random(value);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected value #27 from RNG",
+ uint16_t(0xC970), value
+ );
+ random(value);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected value #28 from RNG",
+ uint16_t(0x02E5), value
+ );
+ random(value);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected value #29 from RNG",
+ uint16_t(0x0000), value
+ );
+ random(value);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected value #30 from RNG",
+ uint16_t(0x0000), value
+ );
+ random(value);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected value #31 from RNG",
+ uint16_t(0x0101), value
+ );
+ random(value);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected value #32 from RNG",
+ uint16_t(0x0100), value
+ );
+}
+
+void StabilityTest::testSimplex() {
+ SimplexNoise noise(0);
+
+ Assert(noise, glm::vec3(0.0f, 0.0f, 0.0f), 0.0f);
+ Assert(noise, glm::vec3(0.0f, 0.0f, 1.0f), 0.652221322059631f);
+ Assert(noise, glm::vec3(0.0f, 1.0f, 0.0f), 0.867977976799011f);
+ Assert(noise, glm::vec3(0.0f, 1.0f, 1.0f), -0.107878111302853f);
+ Assert(noise, glm::vec3(1.0f, 0.0f, 0.0f), -0.107878260314465f);
+ Assert(noise, glm::vec3(1.0f, 0.0f, 1.0f), -6.31356940061778e-08f);
+ Assert(noise, glm::vec3(1.0f, 1.0f, 0.0f), -0.107878245413303f);
+ Assert(noise, glm::vec3(1.0f, 1.0f, 1.0f), 0.0f);
+
+ Assert(noise, glm::vec3( 0.0f, 0.0f, -1.0f), -0.107878483831882f);
+ Assert(noise, glm::vec3( 0.0f, -1.0f, 0.0f), -0.760099768638611f);
+ Assert(noise, glm::vec3( 0.0f, -1.0f, -1.0f), 0.0f);
+ Assert(noise, glm::vec3(-1.0f, 0.0f, 0.0f), 0.760099768638611f);
+ Assert(noise, glm::vec3(-1.0f, 0.0f, -1.0f), 0.0f);
+ Assert(noise, glm::vec3(-1.0f, -1.0f, 0.0f), -0.107878118753433f);
+ Assert(noise, glm::vec3(-1.0f, -1.0f, -1.0f), 0.0f);
+}
+
+void StabilityTest::Assert(
+ const SimplexNoise &noise,
+ const glm::vec3 &position,
+ float expected
+) {
+ stringstream msg;
+ msg << "unexpected simplex noise value at " << position;
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
+ msg.str(),
+ expected, noise(position), numeric_limits<float>::epsilon()
+ );
+}
+
+}
+}
--- /dev/null
+#ifndef BLANK_TEST_RAND_STABILITYTEST_HPP
+#define BLANK_TEST_RAND_STABILITYTEST_HPP
+
+#include <glm/glm.hpp>
+#include <cppunit/extensions/HelperMacros.h>
+
+
+
+namespace blank {
+
+class SimplexNoise;
+
+namespace test {
+
+class StabilityTest
+: public CppUnit::TestFixture {
+
+CPPUNIT_TEST_SUITE(StabilityTest);
+
+CPPUNIT_TEST(testRNG);
+CPPUNIT_TEST(testSimplex);
+
+CPPUNIT_TEST_SUITE_END();
+
+public:
+ void setUp();
+ void tearDown();
+
+ void testRNG();
+ void testSimplex();
+
+ static void Assert(
+ const SimplexNoise &noise,
+ const glm::vec3 &position,
+ float expected);
+
+};
+
+}
+}
+
+#endif
void ChunkTest::testLightPropagation() {
unique_ptr<Chunk> chunk(new Chunk(types));
+ // this is required to make the chunk do lighting propagation at all
+ chunk->ScanLights();
// 0 air, 1 solid, 2 solid and emits light level of 5
chunk->SetBlock(Chunk::Pos(7, 7, 7), Block(2));