]> git.tdb.fi Git - libs/game.git/commitdiff
Rework Landscape to support replication
authorMikko Rasa <tdb@tdb.fi>
Sat, 17 Jun 2023 22:09:30 +0000 (01:09 +0300)
committerMikko Rasa <tdb@tdb.fi>
Sat, 17 Jun 2023 22:09:30 +0000 (01:09 +0300)
source/game/heightmapterrain.cpp
source/game/heightmapterrain.h
source/game/landscape.cpp
source/game/landscape.h
source/game/messages.h
source/game/protocol.cpp

index 8e4d1a2a8ec4c53acbc71c1bd87e25582a4c2743..dafa433f7adf766188d30c7cc2cc30db573b1dbd 100644 (file)
@@ -50,4 +50,29 @@ float HeightmapTerrain::get_elevation(const LinAl::Vector<float, 2> &p) const
        return weighted_elev*setup.elevation_scale;
 }
 
+void HeightmapTerrain::send_data(vector<Messages::HeightmapPoint> &out_data) const
+{
+       out_data.resize(grid_size.x*grid_size.y);
+       for(unsigned y=0; y<grid_size.y; ++y)
+               for(unsigned x=0; x<grid_size.x; ++x)
+               {
+                       unsigned i = x+y*grid_size.x;
+                       out_data[i].elevation = elevations[i];
+                       out_data[i].material = materials[i];
+               }
+}
+
+void HeightmapTerrain::receive_data(const vector<Messages::HeightmapPoint> &in_data)
+{
+       for(unsigned y=0; y<grid_size.y; ++y)
+               for(unsigned x=0; x<grid_size.x; ++x)
+               {
+                       unsigned i = x+y*grid_size.x;
+                       elevations[i] = in_data[i].elevation;
+                       materials[i] = in_data[i].material;
+               }
+
+       ++generation;
+}
+
 } // namespace Msp::Game
index f6ada8676ab1b4ec477665ad994f969c24a91132..284c1f3e4e6357e6f46e8d1169ccc190c00f8da0 100644 (file)
@@ -4,6 +4,7 @@
 #include <msp/core/noncopyable.h>
 #include <msp/game/setups.h>
 #include "component.h"
+#include "messages.h"
 #include "mspgame_api.h"
 
 namespace Msp::Game {
@@ -35,6 +36,9 @@ public:
        unsigned get_material_index(const LinAl::Vector<unsigned, 2> &i) const { return materials[i.x+i.y*grid_size.x]; }
        const std::string &get_technique_name() const { return setup.technique_name; }
        unsigned get_generation() const { return generation; }
+
+       void send_data(std::vector<Messages::HeightmapPoint> &) const;
+       void receive_data(const std::vector<Messages::HeightmapPoint> &);
 };
 
 } // namespace Msp::Game
index c9c2814c63d1416ce311e694f6e9c5716e1f5496..2f39c6dfb4d3deaef4f7c6258710d00c46a0c7bd 100644 (file)
@@ -6,20 +6,30 @@ using namespace std;
 
 namespace Msp::Game {
 
-Landscape::Landscape(Stage &s, const HeightmapTerrainSetup &t, float b):
+Landscape::Landscape(Stage &s, Replicator *r, const HeightmapTerrainSetup &t, float b):
        System(s),
+       replicator(r),
+       data_call(replicator, [this](const auto &... a){ receive_data(a...); }),
        subroot(stage.get_root(), Entity::NO_TRANSFORM),
-       terrain_setup(t),
+       spawner(stage, replicator, subroot, *this),
+       block_setup(t),
        block_size(b)
 {
-       float grid_size = block_size/terrain_setup.grid_spacing;
+       spawner.add_spawnable_type<TerrainBlock>([this](const std::string &) -> const BlockSetup & { return block_setup; });
+
+       float grid_size = block_size/block_setup.heightmap.grid_spacing;
        grid_size = max(round(grid_size), 1.0f);
-       block_size = grid_size*terrain_setup.grid_spacing;
+       block_size = grid_size*block_setup.heightmap.grid_spacing;
+       block_setup.block_size = { block_size, block_size };
 }
 
-bool Landscape::terrain_index_compare(const Terrain &t, const LinAl::Vector<int, 2> &i)
+Landscape::Landscape(Stage &s, const HeightmapTerrainSetup &t, float b):
+       Landscape(s, nullptr, t, b)
+{ }
+
+bool Landscape::block_index_compare(const Owned<TerrainBlock> &t, const LinAl::Vector<int, 2> &i)
 {
-       return t.index.y<i.y || (t.index.y==i.y && t.index.x<i.x);
+       return t->index.y<i.y || (t->index.y==i.y && t->index.x<i.x);
 }
 
 void Landscape::set_generator(LandscapeGenerator *g)
@@ -33,44 +43,72 @@ void Landscape::generate(const Geometry::BoundingBox<float, 2> &bounds)
        const LinAl::Vector<float, 2> &max_pt = bounds.get_maximum_point();
        LinAl::Vector<int, 2> min_index(round(min_pt.x/block_size), round(min_pt.y/block_size));
        LinAl::Vector<int, 2> max_index(round(max_pt.x/block_size), round(max_pt.y/block_size));
-       LinAl::Vector<float, 2> block_size_vec(block_size, block_size);
 
        for(int y=min_index.y; y<=max_index.y; ++y)
-       {
-               auto i = lower_bound(terrains, LinAl::Vector<int, 2>(min_index.x, y), terrain_index_compare);
-
                for(int x=min_index.x; x<=max_index.x; ++x)
                {
-                       if(i==terrains.end() || i->index.x>x)
-                       {
-                               LinAl::Vector<int, 2> index(x ,y);
-                               LinAl::Vector<float, 2> center = LinAl::Vector<float, 2>(index)*block_size;
-                               Owned<Entity> entity(subroot, compose(center, 0.0f));
-                               Owned<HeightmapTerrain> terrain(entity, terrain_setup, block_size_vec);
-                               if(generator)
-                               {
-                                       LinAl::Vector<float, 2> base = center-block_size_vec/2.0f;
-                                       generator->generate_block(*this, { base, base+block_size_vec }, terrain);
-                               }
-                               i = terrains.emplace(i, index, move(entity), move(terrain));
-                       }
-                       ++i;
+                       auto i = lower_bound(blocks, LinAl::Vector<int, 2>(x, y), block_index_compare);
+                       if(i==blocks.end() || (*i)->index.y!=y || (*i)->index.x!=x)
+                               spawner.spawn<TerrainBlock>(string(), LinAl::Vector<float, 3>(x*block_size, y*block_size, 0.0f));
                }
-       }
 }
 
 LinAl::Vector<float, 3> Landscape::get_ground_position(const LinAl::Vector<float, 3> &p) const
 {
        LinAl::Vector<int, 2> index(round(p.x/block_size), round(p.y/block_size));
-       auto i = lower_bound(terrains, index, terrain_index_compare);
-       if(i==terrains.end() || i->index!=index)
+       auto i = lower_bound(blocks, index, block_index_compare);
+       if(i==blocks.end() || (*i)->index!=index)
                return compose(p.slice<2>(0), 0.0f);
 
-       LinAl::Vector<float, 3> center = i->entity->get_transform()->get_position();
-       LinAl::Vector<float, 2> corner = center.slice<2>(0) - i->terrain->get_size()/2.0f;
+       LinAl::Vector<float, 3> center = (*i)->get_transform()->get_position();
+       LinAl::Vector<float, 2> corner = center.slice<2>(0) - (*i)->terrain->get_size()/2.0f;
        LinAl::Vector<float, 2> p_xy = p.slice<2>(0);
-       float z = i->terrain->get_elevation(p_xy-corner);
+       float z = (*i)->terrain->get_elevation(p_xy-corner);
        return center+compose(p_xy, z);
 }
 
+void Landscape::spawned(Owned<Entity> entity)
+{
+       if(Owned<TerrainBlock> block = dynamic_owned_cast<TerrainBlock>(move(entity)))
+       {
+               LinAl::Vector<float, 2> center = block->get_transform()->get_position().slice<2>(0);
+               block->index = { static_cast<int>(round(center.x/block_size)), static_cast<int>(round(center.y/block_size)) };
+               if(generator)
+               {
+                       LinAl::Vector<float, 2> base = center-block_setup.block_size/2.0f;
+                       generator->generate_block({ base, base+block_setup.block_size }, block->terrain, spawner);
+               }
+               auto i = lower_bound(blocks, block->index, block_index_compare);
+               blocks.emplace(i, move(block));
+       }
+       else
+       {
+               Handle<Transform> tf = entity->get_transform();
+               tf->set_position(get_ground_position(tf->get_position()));
+               props.emplace_back(move(entity));
+       }
+}
+
+void Landscape::spawn_sent(Handle<Entity> entity, unsigned target)
+{
+       if(Handle<TerrainBlock> block = dynamic_handle_cast<TerrainBlock>(entity))
+       {
+               Messages::LandscapeData message;
+               block->terrain->send_data(message.heightmap);
+               data_call(target, block, message);
+       }
+}
+
+void Landscape::receive_data(Handle<TerrainBlock> block, const Messages::LandscapeData &message)
+{
+       block->terrain->receive_data(message.heightmap);
+}
+
+
+Landscape::TerrainBlock::TerrainBlock(Handle<Entity> p, const BlockSetup &s, const TransformValues &t):
+       Entity(p, t),
+       terrain(this, s.heightmap, s.block_size),
+       zygote(this)
+{ }
+
 } // namespace Msp::Game
index 93197183041901d4df5612a5425979a2d25871cb..aa042e5ee50b51313f9ebc6301f60e4c4f3bbe73 100644 (file)
@@ -3,34 +3,47 @@
 
 #include <msp/geometry/boundingbox.h>
 #include <msp/linal/vector.h>
+#include "entity.h"
 #include "heightmapterrain.h"
 #include "mspgame_api.h"
 #include "owned.h"
+#include "remotecall.h"
+#include "spawner.h"
 #include "system.h"
 #include "transform.h"
+#include "zygote.h"
 
 namespace Msp::Game {
 
-class Entity;
-class Landscape;
-
 class MSPGAME_API LandscapeGenerator
 {
 protected:
        LandscapeGenerator() = default;
 
 public:
-       virtual void generate_block(Landscape &, const Geometry::BoundingBox<float, 2> &, Handle<HeightmapTerrain>) = 0;
+       virtual void generate_block(const Geometry::BoundingBox<float, 2> &, Handle<HeightmapTerrain>, Spawner &) = 0;
 };
 
-class MSPGAME_API Landscape: public System
+class MSPGAME_API Landscape: public System, private SpawningHandler
 {
 private:
-       struct Terrain
+       struct BlockSetup
+       {
+               const HeightmapTerrainSetup &heightmap;
+               LinAl::Vector<float, 2> block_size;
+
+               BlockSetup(const HeightmapTerrainSetup &h): heightmap(h) { }
+       };
+
+       struct TerrainBlock: Entity
        {
+               using Setup = BlockSetup;
+
                LinAl::Vector<int, 2> index;
-               Owned<Entity> entity;
                Owned<HeightmapTerrain> terrain;
+               Owned<Zygote> zygote;
+
+               TerrainBlock(Handle<Entity>, const BlockSetup &, const TransformValues &);
        };
 
        struct Prop
@@ -38,18 +51,22 @@ private:
                Owned<Entity> entity;
        };
 
+       Replicator *replicator = nullptr;
+       ReplicatedEntityCall<Messages::LandscapeData, TerrainBlock> data_call;
        Owned<Entity> subroot;
-       const HeightmapTerrainSetup &terrain_setup;
+       Spawner spawner;
+       BlockSetup block_setup;
        float block_size = 100.0f;
        LandscapeGenerator *generator = nullptr;
-       std::vector<Terrain> terrains;
+       std::vector<Owned<TerrainBlock>> blocks;
        std::vector<Prop> props;
 
 public:
+       Landscape(Stage &, Replicator *, const HeightmapTerrainSetup &, float);
        Landscape(Stage &, const HeightmapTerrainSetup &, float);
 
 private:
-       static bool terrain_index_compare(const Terrain &, const LinAl::Vector<int, 2> &);
+       static bool block_index_compare(const Owned<TerrainBlock> &, const LinAl::Vector<int, 2> &);
 
 public:
        void set_generator(LandscapeGenerator *);
@@ -58,20 +75,16 @@ public:
 
        template<typename T>
                requires std::is_base_of_v<Entity, T>
-       void add_prop(const T::Setup &, TransformValues);
+       void add_prop_type() { spawner.add_spawnable_type<T>(); }
 
        void tick(Time::TimeDelta) override { }
-};
 
+private:
+       void spawned(Owned<Entity>) override;
+       void spawn_sent(Handle<Entity>, unsigned) override;
 
-template<typename T>
-       requires std::is_base_of_v<Entity, T>
-void Landscape::add_prop(const T::Setup &setup, TransformValues tf)
-{
-       tf.position = get_ground_position(tf.position);
-       Owned<T> prop(subroot, setup, tf);
-       props.emplace_back(std::move(prop));
-}
+       void receive_data(Handle<TerrainBlock>, const Messages::LandscapeData &);
+};
 
 } // namespace Msp::Game
 
index 860fc454f3120593b298e20d4e84db4f5064fd52..7e91919579d72d9d12335f63a1eb46801eb74eb4 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef MSP_GAME_MESSAGES_H_
 #define MSP_GAME_MESSAGES_H_
 
+#include <vector>
 #include <msp/geometry/quaternion.h>
 #include <msp/linal/vector.h>
 
@@ -51,6 +52,18 @@ struct StageActivated
        std::uint16_t name;
 };
 
+struct HeightmapPoint
+{
+       std::int16_t elevation;
+       std::uint8_t material;
+};
+
+struct LandscapeData
+{
+       std::uint32_t entity_id;
+       std::vector<HeightmapPoint> heightmap;
+};
+
 } // namespace Messages
 } // namespace Msp::Game
 
index 9c1d1f6fa10d7b01433d5ab09cf610658b9cb45b..9b81f516ae364b5326388da47839928ded266197 100644 (file)
@@ -23,8 +23,10 @@ StageProtocol::StageProtocol()
        using Quaternion = Geometry::Quaternion<float>;
        add<Vector3f, float, float, float>(&Vector3f::x, &Vector3f::y, &Vector3f::z);
        add<Quaternion>(&Quaternion::a, &Quaternion::b, &Quaternion::c, &Quaternion::d);
+       add<HeightmapPoint>(&HeightmapPoint::elevation, &HeightmapPoint::material);
 
        add<SpawnEntity>(&SpawnEntity::id, &SpawnEntity::type, &SpawnEntity::setup, &SpawnEntity::position, &SpawnEntity::rotation, &SpawnEntity::scale);
+       add<LandscapeData>(&LandscapeData::entity_id, &LandscapeData::heightmap);
 }
 
 } // namespace Msp::Game