-Subproject commit 00b0cac2c26af161cc436d404d033e242a00c5a6
+Subproject commit 8fed55072b5b2d5baa06e2ab045df3161f86fdb2
gravity
- maybe like light levels? should also store a direction with it in
- that case. also, global gravity may be a world option.
- no, per-block gravity vector is most probably too expensive.
- better have the chunks store a few point masses (maybe blocks that
- emit gravitation?) and calculate from that
+ now implemented as optional gravity emitter per block type
+ let's see how that pans out
block attributes
BlockType type;
type.Read(in, snd_index, tex_index, shapes);
in.Skip(Token::SEMICOLON);
- reg.Add(type);
+ reg.Add(std::move(type));
}
}
} else {
memcpy(dst, src, min(src_len, dst_len));
}
+ chunk->ScanActive();
chunk->Invalidate();
trans.Clear();
}
if (gzclose(file) != Z_OK) {
throw runtime_error("failed to read chunk file");
}
+ chunk.ScanActive();
chunk.InvalidateMesh();
chunk.ClearSave();
}
--- /dev/null
+#ifndef BLANK_WORLD_BLOCKGRAVITY_HPP_
+#define BLANK_WORLD_BLOCKGRAVITY_HPP_
+
+#include <memory>
+#include <glm/glm.hpp>
+
+
+namespace blank {
+
+class TokenStreamReader;
+
+struct BlockGravity {
+
+ virtual ~BlockGravity();
+
+ /// get gravitational force for a unit mass at relative position diff
+ /// diff is target - block, i.e. pointing from block towards the target
+ /// orientation of the block in question is given by M
+ /// return value should be world absolute
+ virtual glm::vec3 GetGravity(const glm::vec3 &diff, const glm::mat4 &M) const noexcept = 0;
+
+ static std::unique_ptr<BlockGravity> Read(TokenStreamReader &in);
+
+};
+
+}
+
+#endif
#define BLANK_WORLD_BLOCKTYPE_HPP_
#include "Block.hpp"
+#include "BlockGravity.hpp"
#include "../graphics/BlockMesh.hpp"
#include "../graphics/EntityMesh.hpp"
#include "../graphics/PrimitiveMesh.hpp"
glm::vec3 rgb_mod;
glm::vec3 outline_color;
+ /// gravity configuration or null if not emitting gravity
+ std::unique_ptr<BlockGravity> gravity;
+
/// a string to display to the user
std::string label;
public:
BlockTypeRegistry();
- Block::Type Add(const BlockType &);
+ Block::Type Add(BlockType &&);
size_t size() const noexcept { return types.size(); }
#include "../geometry/Location.hpp"
#include "../geometry/primitive.hpp"
+#include <set>
#include <vector>
#include <glm/glm.hpp>
#include <glm/gtx/transform.hpp>
float GetVertexLight(const RoughLocation::Fine &, const BlockMesh::Position &, const EntityMesh::Normal &) const noexcept;
+ /// get gravity for one unit mass at given point
+ glm::vec3 GravityAt(const ExactLocation &) const noexcept;
+
bool Intersection(
const Ray &ray,
const ExactLocation::Coarse &reference,
bool Lighted() const noexcept { return lighted; }
void ScanLights();
+ /// check for active blocks, should be called after
+ /// block data was modified by means other than SetBlock()
+ void ScanActive();
+
void Ref() noexcept { ++ref_count; }
void UnRef() noexcept { --ref_count; }
bool Referenced() const noexcept { return ref_count > 0; }
const BlockTypeRegistry *types;
Chunk *neighbor[Block::FACE_COUNT];
+ std::set<int> gravity;
+
Block blocks[size];
unsigned char light[size];
bool generated;
#include "Block.hpp"
+#include "BlockGravity.hpp"
#include "BlockType.hpp"
#include "BlockTypeRegistry.hpp"
#include <iostream>
#include <glm/gtx/euler_angles.hpp>
+#include <glm/gtx/norm.hpp>
#include <glm/gtx/transform.hpp>
, hsl_mod(0.0f, 1.0f, 1.0f)
, rgb_mod(1.0f, 1.0f, 1.0f)
, outline_color(-1, -1, -1)
+, gravity()
, label("some block")
, place_sound(-1)
, remove_sound(-1)
in.ReadVec(hsl_mod);
} else if (name == "outline") {
in.ReadVec(outline_color);
+ } else if (name == "gravity") {
+ gravity = BlockGravity::Read(in);
} else if (name == "label") {
in.ReadString(label);
} else if (name == "place_sound") {
air.block_light = false;
air.collision = false;
air.collide_block = false;
- Add(air);
+ Add(std::move(air));
}
-Block::Type BlockTypeRegistry::Add(const BlockType &t) {
+Block::Type BlockTypeRegistry::Add(BlockType &&t) {
int id = types.size();
- types.push_back(t);
+ types.push_back(std::move(t));
types.back().id = id;
return id;
}
+namespace {
+
+/// the "usual" type of gravity
+/// direction is towards the block's center, strength is inverse
+/// proportional to distance squared
+/// note that the effect can get clipped at distances > 16 units
+struct RadialGravity
+: public BlockGravity {
+
+ explicit RadialGravity(float strength)
+ : strength(strength) { }
+
+ glm::vec3 GetGravity(const glm::vec3 &diff, const glm::mat4 &) const noexcept override {
+ float dist2 = length2(diff);
+ glm::vec3 dir = -normalize(diff);
+ return dir * (strength / dist2);
+ }
+
+ float strength;
+
+};
+
+/// a "force field" variant of artificial gravity
+/// strength and direction is constant throughout the cuboid
+/// extent shouldn't exceed 16 units as gravity is only calculated for
+/// chunks surrounding and entity (and sometimes not even those if they're
+/// unavailable, but they will be for players)
+struct CuboidFieldGravity
+: public BlockGravity {
+
+ explicit CuboidFieldGravity(const glm::vec3 &strength, const AABB &extent)
+ : strength(strength), extent(extent) { }
+
+ glm::vec3 GetGravity(const glm::vec3 &diff, const glm::mat4 &M) const noexcept override {
+ /// rotate AABB endpoints accordingly, ignore translation
+ glm::vec3 min(M * glm::vec4(extent.min, 0.0f));
+ glm::vec3 max(M * glm::vec4(extent.max, 0.0f));
+ if (diff.x < min.x || diff.y < min.y || diff.z < min.z ||
+ diff.x > max.x || diff.y > max.y || diff.z > max.z) {
+ /// if point is outside, force is zero
+ return glm::vec3(0.0f);
+ } else {
+ /// otherwise it's out constant strength in block orientation
+ return glm::vec3(M * glm::vec4(strength, 0.0f));
+ }
+ }
+
+ glm::vec3 strength;
+ AABB extent;
+
+};
+
+
+}
+
+BlockGravity::~BlockGravity() {
+
+}
+
+std::unique_ptr<BlockGravity> BlockGravity::Read(TokenStreamReader &in) {
+ std::string type;
+ in.ReadIdentifier(type);
+ if (type == "Radial") {
+ float strength;
+ in.Skip(Token::PARENTHESIS_OPEN);
+ in.ReadNumber(strength);
+ in.Skip(Token::PARENTHESIS_CLOSE);
+ return std::unique_ptr<BlockGravity>(new RadialGravity(strength));
+ } else if (type == "CuboidField") {
+ glm::vec3 strength;
+ AABB extent;
+ in.Skip(Token::PARENTHESIS_OPEN);
+ in.ReadVec(strength);
+ in.Skip(Token::COMMA);
+ in.ReadVec(extent.min);
+ in.Skip(Token::COMMA);
+ in.ReadVec(extent.max);
+ in.Skip(Token::PARENTHESIS_CLOSE);
+ extent.Adjust();
+ return std::unique_ptr<BlockGravity>(new CuboidFieldGravity(strength, extent));
+ } else {
+ throw std::runtime_error("unknown gravity type: " + type);
+ }
+}
+
+
const glm::mat4 Block::orient2transform[ORIENT_COUNT] = {
{ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, }, // face: up, turn: none
{ 0, 0, -1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, }, // face: up, turn: left
Chunk::Chunk(const BlockTypeRegistry &types) noexcept
: types(&types)
, neighbor{0}
+, gravity()
, blocks{}
, light{0}
, generated(false)
Chunk::Chunk(Chunk &&other) noexcept
: types(other.types)
+, gravity(std::move(other.gravity))
, generated(other.generated)
, lighted(other.lighted)
, position(other.position)
Chunk &Chunk::operator =(Chunk &&other) noexcept {
types = other.types;
std::copy(other.neighbor, other.neighbor + sizeof(neighbor), neighbor);
+ gravity = std::move(other.gravity);
std::copy(other.blocks, other.blocks + sizeof(blocks), blocks);
std::copy(other.light, other.light + sizeof(light), light);
generated = other.generated;
blocks[index] = block;
Invalidate();
+ if (old_type.gravity && !new_type.gravity) {
+ gravity.erase(index);
+ } else if (new_type.gravity && !old_type.gravity) {
+ gravity.insert(index);
+ }
+
if (!lighted || &old_type == &new_type) return;
if (new_type.luminosity > old_type.luminosity) {
lighted = true;
}
+void Chunk::ScanActive() {
+ gravity.clear();
+ for (int index = 0; index < size; ++index) {
+ if (Type(index).gravity) {
+ gravity.insert(gravity.end(), index);
+ }
+ }
+}
+
void Chunk::SetNeighbor(Block::Face face, Chunk &other) noexcept {
neighbor[face] = &other;
other.neighbor[Block::Opposite(face)] = this;
}
+glm::vec3 Chunk::GravityAt(const ExactLocation &coords) const noexcept {
+ glm::vec3 grav;
+ for (int index : gravity) {
+ RoughLocation::Fine block_pos(ToPos(index));
+ ExactLocation block_coords(position, ToCoords(block_pos));
+ // trust that block type hasn't changed
+ grav += Type(index).gravity->GetGravity(
+ coords.Difference(block_coords).Absolute(),
+ ToTransform(block_pos, index));
+ }
+ return grav;
+}
+
+
bool Chunk::IsSurface(const RoughLocation::Fine &pos) const noexcept {
const Block &block = BlockAt(pos);
if (!Type(block).visible) {
const Entity &entity,
const EntityState &state
) {
- return glm::vec3(0.0f);
+ glm::vec3 force(0.0f);
+ ExactLocation::Coarse begin(state.pos.chunk - 1);
+ ExactLocation::Coarse end(state.pos.chunk + 2);
+
+ for (ExactLocation::Coarse pos(begin); pos.z < end.z; ++pos.z) {
+ for (pos.y = begin.y; pos.y < end.y; ++pos.y) {
+ for (pos.x = begin.x; pos.x < end.x; ++pos.x) {
+ Chunk *chunk = chunks.Get(pos);
+ if (chunk) {
+ force += chunk->GravityAt(state.pos);
+ }
+ }
+ }
+ }
+
+ return force;
}
World::EntityHandle World::RemoveEntity(EntityHandle &eh) {
BlockType obstacle;
obstacle.visible = true;
obstacle.block_light = true;
- types.Add(obstacle);
+ types.Add(std::move(obstacle));
BlockType source;
source.visible = true;
source.luminosity = 5;
source.block_light = true;
- types.Add(source);
+ types.Add(std::move(source));
}
void ChunkTest::tearDown() {