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)
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
#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
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 *);
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