]> git.localhorst.tv Git - blank.git/commitdiff
per block type "gravity"
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Mon, 16 Nov 2015 14:34:56 +0000 (15:34 +0100)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Tue, 17 Nov 2015 10:36:06 +0000 (11:36 +0100)
actually, it's just a configurable acceleration atm, but
who can tell the difference? :P

13 files changed:
assets
doc/todo
src/app/app.cpp
src/client/net.cpp
src/io/WorldSave.cpp
src/world/BlockGravity.hpp [new file with mode: 0644]
src/world/BlockType.hpp
src/world/BlockTypeRegistry.hpp
src/world/Chunk.hpp
src/world/block.cpp
src/world/chunk.cpp
src/world/world.cpp
tst/world/ChunkTest.cpp

diff --git a/assets b/assets
index 00b0cac2c26af161cc436d404d033e242a00c5a6..8fed55072b5b2d5baa06e2ab045df3161f86fdb2 160000 (submodule)
--- a/assets
+++ b/assets
@@ -1 +1 @@
-Subproject commit 00b0cac2c26af161cc436d404d033e242a00c5a6
+Subproject commit 8fed55072b5b2d5baa06e2ab045df3161f86fdb2
index 921221aa14f9681672023c4ed7480a35eaa3604d..1b7d9b01802bd504521b68e7086cf10f43b4a07a 100644 (file)
--- 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
 
index 2b1a494efadbd20424a22886b57e93c6c2828478..dc4869a46191417735b008b9faf95a66a79a09d0 100644 (file)
@@ -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));
        }
 }
 
index 75865be577dfac866df3ac302ac85f0a491e440e..bd2a43abd002a20224e8019ff36f203c08989061 100644 (file)
@@ -185,6 +185,7 @@ void ChunkReceiver::Commit(ChunkTransmission &trans) {
        } else {
                memcpy(dst, src, min(src_len, dst_len));
        }
+       chunk->ScanActive();
        chunk->Invalidate();
        trans.Clear();
 }
index 8422623f36746262f30e10f0d78942a44f635439..d483c915939407426fbab0d4a7c70387a9a11bce 100644 (file)
@@ -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 (file)
index 0000000..6a17b61
--- /dev/null
@@ -0,0 +1,28 @@
+#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
index 8837ca9fd4bd8e1567accde47c5a429c6681cbde..a63507aec28ce568f991866a2abece597d2ac845 100644 (file)
@@ -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<BlockGravity> gravity;
+
        /// a string to display to the user
        std::string label;
 
index 9d9597dde0716348245f8a3e4d0622b008b50d8c..6612d1516e72c893298f21ed193830a15fdd5671 100644 (file)
@@ -20,7 +20,7 @@ public:
 public:
        BlockTypeRegistry();
 
-       Block::Type Add(const BlockType &);
+       Block::Type Add(BlockType &&);
 
        size_t size() const noexcept { return types.size(); }
 
index d0dcf9404219ba063f91fb9b2e5dced933df40e9..7eceb980cb62d6c66918ae9b3eb7fa587416c2c3 100644 (file)
@@ -6,6 +6,7 @@
 #include "../geometry/Location.hpp"
 #include "../geometry/primitive.hpp"
 
+#include <set>
 #include <vector>
 #include <glm/glm.hpp>
 #include <glm/gtx/transform.hpp>
@@ -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<int> gravity;
+
        Block blocks[size];
        unsigned char light[size];
        bool generated;
index 04209e1ac0ecb5bdf8b09baf537f56f88419f7f4..61e2ab4e9801fc920b4885353a7bc03858620808 100644 (file)
@@ -1,4 +1,5 @@
 #include "Block.hpp"
+#include "BlockGravity.hpp"
 #include "BlockType.hpp"
 #include "BlockTypeRegistry.hpp"
 
@@ -8,6 +9,7 @@
 
 #include <iostream>
 #include <glm/gtx/euler_angles.hpp>
+#include <glm/gtx/norm.hpp>
 #include <glm/gtx/transform.hpp>
 
 
@@ -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> 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
index 1d120bb2ebe8d2475a4be2ac1bc4799755b8d25d..e93966e959ce13d4678dfaa3f5870ffcade50e17 100644 (file)
@@ -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) {
index dfcf7217f99372d1ea4ada491d55ffa409ec2336..2a3d5fbc12d7c4dd447acd5c71a4d5f4b47044ca 100644 (file)
@@ -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) {
index 46933e96e6d724774ddca1c8baa70c9d9ff39715..e2c02fd4862ee36d4a2112ea33fc799a6be778e8 100644 (file)
@@ -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() {