From: Daniel Karbach Date: Mon, 16 Nov 2015 14:34:56 +0000 (+0100) Subject: per block type "gravity" X-Git-Url: http://git.localhorst.tv/?a=commitdiff_plain;h=ed3bdc028edc0ecb5835d1c0bf18dbc59b342daf;p=blank.git per block type "gravity" actually, it's just a configurable acceleration atm, but who can tell the difference? :P --- diff --git a/assets b/assets index 00b0cac..8fed550 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 00b0cac2c26af161cc436d404d033e242a00c5a6 +Subproject commit 8fed55072b5b2d5baa06e2ab045df3161f86fdb2 diff --git a/doc/todo b/doc/todo index 921221a..1b7d9b0 100644 --- a/doc/todo +++ b/doc/todo @@ -77,11 +77,8 @@ lighting 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 diff --git a/src/app/app.cpp b/src/app/app.cpp index 2b1a494..dc4869a 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -328,7 +328,7 @@ void AssetLoader::LoadBlockTypes( BlockType type; type.Read(in, snd_index, tex_index, shapes); in.Skip(Token::SEMICOLON); - reg.Add(type); + reg.Add(std::move(type)); } } diff --git a/src/client/net.cpp b/src/client/net.cpp index 75865be..bd2a43a 100644 --- a/src/client/net.cpp +++ b/src/client/net.cpp @@ -185,6 +185,7 @@ void ChunkReceiver::Commit(ChunkTransmission &trans) { } else { memcpy(dst, src, min(src_len, dst_len)); } + chunk->ScanActive(); chunk->Invalidate(); trans.Clear(); } diff --git a/src/io/WorldSave.cpp b/src/io/WorldSave.cpp index 8422623..d483c91 100644 --- a/src/io/WorldSave.cpp +++ b/src/io/WorldSave.cpp @@ -183,6 +183,7 @@ void WorldSave::Read(Chunk &chunk) const { if (gzclose(file) != Z_OK) { throw runtime_error("failed to read chunk file"); } + chunk.ScanActive(); chunk.InvalidateMesh(); chunk.ClearSave(); } diff --git a/src/world/BlockGravity.hpp b/src/world/BlockGravity.hpp new file mode 100644 index 0000000..6a17b61 --- /dev/null +++ b/src/world/BlockGravity.hpp @@ -0,0 +1,28 @@ +#ifndef BLANK_WORLD_BLOCKGRAVITY_HPP_ +#define BLANK_WORLD_BLOCKGRAVITY_HPP_ + +#include +#include + + +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 Read(TokenStreamReader &in); + +}; + +} + +#endif diff --git a/src/world/BlockType.hpp b/src/world/BlockType.hpp index 8837ca9..a63507a 100644 --- a/src/world/BlockType.hpp +++ b/src/world/BlockType.hpp @@ -2,6 +2,7 @@ #define BLANK_WORLD_BLOCKTYPE_HPP_ #include "Block.hpp" +#include "BlockGravity.hpp" #include "../graphics/BlockMesh.hpp" #include "../graphics/EntityMesh.hpp" #include "../graphics/PrimitiveMesh.hpp" @@ -27,6 +28,9 @@ struct BlockType { glm::vec3 rgb_mod; glm::vec3 outline_color; + /// gravity configuration or null if not emitting gravity + std::unique_ptr gravity; + /// a string to display to the user std::string label; diff --git a/src/world/BlockTypeRegistry.hpp b/src/world/BlockTypeRegistry.hpp index 9d9597d..6612d15 100644 --- a/src/world/BlockTypeRegistry.hpp +++ b/src/world/BlockTypeRegistry.hpp @@ -20,7 +20,7 @@ public: public: BlockTypeRegistry(); - Block::Type Add(const BlockType &); + Block::Type Add(BlockType &&); size_t size() const noexcept { return types.size(); } diff --git a/src/world/Chunk.hpp b/src/world/Chunk.hpp index d0dcf94..7eceb98 100644 --- a/src/world/Chunk.hpp +++ b/src/world/Chunk.hpp @@ -6,6 +6,7 @@ #include "../geometry/Location.hpp" #include "../geometry/primitive.hpp" +#include #include #include #include @@ -132,6 +133,9 @@ public: 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, @@ -175,6 +179,10 @@ public: 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; } @@ -192,6 +200,8 @@ private: const BlockTypeRegistry *types; Chunk *neighbor[Block::FACE_COUNT]; + std::set gravity; + Block blocks[size]; unsigned char light[size]; bool generated; diff --git a/src/world/block.cpp b/src/world/block.cpp index 04209e1..61e2ab4 100644 --- a/src/world/block.cpp +++ b/src/world/block.cpp @@ -1,4 +1,5 @@ #include "Block.hpp" +#include "BlockGravity.hpp" #include "BlockType.hpp" #include "BlockTypeRegistry.hpp" @@ -8,6 +9,7 @@ #include #include +#include #include @@ -74,6 +76,7 @@ BlockType::BlockType() noexcept , 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) @@ -132,6 +135,8 @@ void BlockType::Read( 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") { @@ -224,17 +229,103 @@ BlockTypeRegistry::BlockTypeRegistry() { 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::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(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(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 diff --git a/src/world/chunk.cpp b/src/world/chunk.cpp index 1d120bb..e93966e 100644 --- a/src/world/chunk.cpp +++ b/src/world/chunk.cpp @@ -29,6 +29,7 @@ constexpr int Chunk::size; Chunk::Chunk(const BlockTypeRegistry &types) noexcept : types(&types) , neighbor{0} +, gravity() , blocks{} , light{0} , generated(false) @@ -42,6 +43,7 @@ Chunk::Chunk(const BlockTypeRegistry &types) noexcept Chunk::Chunk(Chunk &&other) noexcept : types(other.types) +, gravity(std::move(other.gravity)) , generated(other.generated) , lighted(other.lighted) , position(other.position) @@ -57,6 +59,7 @@ Chunk::Chunk(Chunk &&other) noexcept 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; @@ -174,6 +177,12 @@ void Chunk::SetBlock(int index, const Block &block) noexcept { 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) { @@ -233,6 +242,15 @@ void Chunk::ScanLights() { 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; @@ -349,6 +367,20 @@ float Chunk::GetVertexLight(const RoughLocation::Fine &pos, const BlockMesh::Pos } +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) { diff --git a/src/world/world.cpp b/src/world/world.cpp index dfcf721..2a3d5fb 100644 --- a/src/world/world.cpp +++ b/src/world/world.cpp @@ -725,7 +725,22 @@ glm::vec3 World::Gravity( 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) { diff --git a/tst/world/ChunkTest.cpp b/tst/world/ChunkTest.cpp index 46933e9..e2c02fd 100644 --- a/tst/world/ChunkTest.cpp +++ b/tst/world/ChunkTest.cpp @@ -19,13 +19,13 @@ void ChunkTest::setUp() { 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() {