Commit 310efea6 authored by Shu's avatar Shu 🍠
Browse files

Basic entities

parent ff994269
......@@ -15,7 +15,13 @@
- [x] High memory: Save multiple
- [x] Unload unused
- [x] Edition
- [ ] Entity
- [~] Entity
- [x] Basic
- [ ] Instanced
- https://learnopengl.com/Advanced-OpenGL/Instancing
- [ ] Inheritance
- [ ] ECS
- [ ] Relative to area
- [x] Area
- [x] Offset
- [ ] Rotation
......
......@@ -8,7 +8,7 @@
enum class Input {
Forward, Backward, Left, Right, Up, Down,
Mouse, Debug
Mouse, Debug, Throw
};
enum class Mouse {
Left = GLFW_MOUSE_BUTTON_LEFT,
......@@ -46,9 +46,10 @@ private:
{Input::Down, GLFW_KEY_LEFT_SHIFT},
{Input::Mouse, GLFW_KEY_E},
{Input::Debug, GLFW_KEY_F3},
{Input::Throw, GLFW_KEY_B}
};
const std::vector<Input> Toggles = {
Input::Mouse, Input::Debug
Input::Mouse, Input::Debug, Input::Throw
};
std::unordered_map<Input, bool> Previous;
......
......@@ -111,7 +111,7 @@ namespace data::generational {
freed.pop_back();
auto &entry = entries[idx];
assert(!entry.value.has_value());
entry.value = std::make_optional<T>(std::forward<_Args>(__args)...);
entry.value.emplace(std::forward<_Args>(__args)...);
return id(idx, ++entries[idx].generation);
}
}
......@@ -125,6 +125,10 @@ namespace data::generational {
entries[idx.index].value = in;
return true;
}
T& at(id idx) {
assert(contains(idx));
return entries[idx.index].value.value();
}
bool contains(id idx) const {
return idx.index < entries.size() &&
......@@ -151,6 +155,16 @@ namespace data::generational {
}
}
template<typename apply>
void for_each(apply fn) {
for (size_t i = 0; i < entries.size(); i++) {
auto &entry = entries[i];
if(entry.value.has_value()) {
fn(id(i, entry.generation), entry.value.value());
}
}
}
template<typename extractor>
void extract(extractor fn) {
for (size_t i = 0; i < entries.size(); i++) {
......@@ -162,6 +176,19 @@ namespace data::generational {
}
}
template<typename extractor>
void remove(extractor fn) {
for (size_t i = 0; i < entries.size(); i++) {
auto &entry = entries[i];
if(entry.value.has_value()) {
if(fn(id(i, entry.generation), entry.value.value())) {
entry.value = std::nullopt;
freed.push_back(i);
}
}
}
}
size_t size() const {
return std::count_if(entries.begin(), entries.end(),
[](const entry &e) { return e.value.has_value(); });
......@@ -172,6 +199,10 @@ namespace data::generational {
entry(): value(std::nullopt), generation(0) { }
entry(const T &in, size_t gen = 0):
value(in), generation(gen) { }
template <typename... _Args>
entry(_Args &&... __args): generation(0) {
value.emplace(std::forward<_Args>(__args)...);
}
std::optional<T> value;
size_t generation;
};
......
......@@ -33,6 +33,11 @@ namespace glm {
glm::llvec3 raw_as_long() const;
glm::dvec3 as_double() const;
inline const ifvec3 &operator+=(const offset_t &v) {
offset += v;
center();
return *this;
}
inline ifvec3 operator+(const offset_t& v) const { return ifvec3(raw, offset + v); }
inline ifvec3 operator/(int i) const { return ifvec3(raw / i, offset / (i * 1.f), false); }
inline ifvec3 operator*(int i) const { return ifvec3(raw * i, offset * (i * 1.f)); }
......
......@@ -100,11 +100,16 @@ int main(int /*unused*/, char */*unused*/[]){
state.position = camera.getPosition();
state.look_at = world.raycast(camera.getRay() * options.voxel_density);
if (state.capture_mouse && state.look_at.has_value()) {
if (inputs.isPressing(Mouse::Left))
world.setCube(state.look_at.value().pos, world::Voxel(0), options.tool.radius);
else if (inputs.isPressing(Mouse::Right))
world.setCube(state.look_at.value().pos, world::Voxel(options.tool.material, world::Voxel::DENSITY_MAX), options.tool.radius);
if (state.capture_mouse) {
if (state.look_at.has_value()) {
if (inputs.isPressing(Mouse::Left))
world.setCube(state.look_at.value().pos, world::Voxel(0), options.tool.radius);
else if (inputs.isPressing(Mouse::Right))
world.setCube(state.look_at.value().pos, world::Voxel(options.tool.material, world::Voxel::DENSITY_MAX), options.tool.radius);
}
if (inputs.isDown(Input::Throw)) {
world.addEntity(entity_id(0), {state.position * options.voxel_density, glm::vec3(10, 0, 0)});
}
}
world.update((state.position * options.voxel_density).as_voxel(), deltaTime, reports.world);
inputs.saveKeys();
......@@ -157,25 +162,40 @@ int main(int /*unused*/, char */*unused*/[]){
auto pass = renderer->getPass();
pass.start();
std::vector<std::pair<glm::mat4, buffer::Abstract *const>> models;
reports.main.models_count = 0;
reports.main.tris_count = 0;
std::optional<geometry::Frustum> frustum;
if(options.culling) {
frustum = {camera.getFrustum()};
}
const auto offset = state.position.raw_as_long();
world.getContouring()->getModels(models, frustum, offset, options.voxel_density);
reports.main.models_count = 0;
reports.main.tris_count = 0;
rmt_ScopedOpenGLSample(Render);
for (auto [model, buffer] : models) {
reports.main.models_count++;
reports.main.tris_count += buffer->draw(pass.setup(model));
{ // Chunks
std::vector<std::pair<glm::mat4, buffer::Abstract *const>> models;
world.getContouring()->getModels(models, frustum, offset, options.voxel_density);
rmt_ScopedOpenGLSample(Render);
for (auto [model, buffer] : models) {
reports.main.models_count++;
reports.main.tris_count += buffer->draw(pass.setup(model));
}
}
{ // Entities
std::vector<std::pair<std::vector<glm::mat4>, buffer::Abstract *const>> models;
world.getEntitiesModels(models, frustum, offset, options.voxel_density);
lookProgram->useIt();
lookProgram->start(renderer);
for (auto [mats, buffer]: models) {
for(auto model: mats) {
reports.main.models_count++;
reports.main.tris_count += lookBuffer.draw(lookProgram->setup(renderer, model));
}
}
}
if(state.look_at.has_value()) {
if(state.look_at.has_value()) { // Indicator
lookProgram->useIt();
lookProgram->start(renderer);
const auto model = glm::scale(glm::translate(glm::scale(glm::mat4(1), 1.f / glm::vec3(options.voxel_density)), glm::vec3(state.look_at.value().pos.second + state.look_at.value().offset - offset * glm::llvec3(options.voxel_density)) - glm::vec3(.5 + options.tool.radius)), glm::vec3(1 + options.tool.radius * 2));
lookBuffer.draw(lookProgram->setup(renderer, model));
reports.main.models_count++;
reports.main.tris_count += lookBuffer.draw(lookProgram->setup(renderer, model));
}
renderer->postProcess();
......
......@@ -107,6 +107,7 @@ UI::Actions UI::draw(options &options, state &state, const reports &reports, GLu
ImGui::PlotHistogram("Loading", reports.world.chunk_load.buffer.get(), reports.world.chunk_load.size, 0, std::to_string(reports.world.chunk_load.current()).c_str(), 0);
ImGui::PlotHistogram("Saving", reports.world.chunk_unload.buffer.get(), reports.world.chunk_unload.size, 0, std::to_string(reports.world.chunk_unload.current()).c_str(), 0);
ImGui::PlotHistogram("Regions", reports.world.region_count.buffer.get(), reports.world.region_count.size, 0, std::to_string(reports.world.region_count.current()).c_str(), 0);
ImGui::PlotHistogram("Entities", reports.world.entity_count.buffer.get(), reports.world.entity_count.size, 0, std::to_string(reports.world.entity_count.current()).c_str(), 0);
ImGui::Separator();
ImGui::Text("Path: %s", options.world.folderPath.c_str());
if (ImGui::SliderInt("Load distance", &options.world.loadDistance, 1, options.world.keepDistance) |
......
......@@ -8,6 +8,7 @@ namespace buffer {
struct params {
/// Bind only vertices positions
bool vertexOnly;
size_t instances = 1;
};
/// Abstract OpenGL Buffer
......
......@@ -39,13 +39,13 @@ namespace buffer {
ShortIndexed(GLenum shape, const typename ShortIndexed::Data &data);
virtual ~ShortIndexed();
uint draw(params params) override;
private:
void enableAllAttribs();
void disableAllAttribs();
void enableIndex();
uint draw(params params) override;
private:
GLuint IndexBufferID;
GLushort IndexSize = 0;
......
......@@ -53,6 +53,8 @@ Universe::Universe(const Universe::options &options): dicts("content/zstd.dict"
index.close();
}
entities.emplace(nullptr, glm::vec3(1), glm::vec3(2));
// Load workers
for (size_t i = 0; i < 4; i++) {
loadWorkers.emplace_back([&] {
......@@ -185,7 +187,7 @@ void Universe::update(const voxel_pos& pos, float deltaTime, Universe::report& r
});
}
{ // Update alive areas
rmt_ScopedCPUSample(Update, 0);
rmt_ScopedCPUSample(World, 0);
size_t chunk_count = 0;
size_t region_count = 0;
const bool queuesEmpty = loadQueue.empty() && saveQueue.empty();
......@@ -271,6 +273,18 @@ void Universe::update(const voxel_pos& pos, float deltaTime, Universe::report& r
contouring->update(pos, areas);
//MAYBE: if(chunkChange) contouring->notify(chunks);
}
{ // Update entities
rmt_ScopedCPUSample(Entities, 0);
size_t entity_count = 0;
entities.for_each([&](entity_id, Entity &val) {
val.instances.remove([&](entity_id, Entity::Instance &inst) {
entity_count++;
inst.pos += inst.velocity * deltaTime;
return glm::length2(glm::divide(pos - inst.pos.as_voxel())) > glm::pow2(keepDistance);
});
});
rep.entity_count.push(entity_count);
}
{ // Store loaded chunks
rmt_ScopedCPUSample(Load, 0);
......@@ -357,4 +371,20 @@ ItemList Universe::setCube(const area_<voxel_pos>& pos, const Voxel& val, int ra
}}}
}
return list;
}
entity_instance_id Universe::addEntity(entity_id type, const Entity::Instance &instance) {
return std::make_pair(type, entities.at(type).instances.push(instance));
}
void Universe::getEntitiesModels(std::vector<std::pair<std::vector<glm::mat4>, buffer::Abstract *const>> &buffers, const std::optional<geometry::Frustum> &frustum, const glm::llvec3 &offset, int density) {
entities.iter([&](entity_id, const Entity &entity) {
std::vector<glm::mat4> mats;
entity.instances.iter([&](entity_id, const Entity::Instance &inst) {
const glm::vec3 fPos = (glm::vec3(inst.pos.raw_as_long() - offset * glm::llvec3(density)) + inst.pos.offset) / glm::vec3(density);
if (!frustum.has_value() || frustum.value().contains(geometry::Box::fromMin(fPos, entity.size)))
mats.emplace_back(glm::scale(glm::translate(glm::mat4(1), fPos * (float)density), entity.scale));
});
if(!mats.empty())
buffers.emplace_back(mats, entity.buffer);
});
}
\ No newline at end of file
......@@ -7,6 +7,7 @@
#include "../data/safe_priority_queue.hpp"
#include "../data/circular_buffer.hpp"
#include "../data/geometry/Ray.hpp"
#include "../data/geometry/Frustum.hpp"
#include "forward.h"
#include "Area.hpp"
#include "Voxel.hpp"
......@@ -16,6 +17,9 @@
namespace contouring {
class Abstract;
};
namespace buffer {
class Abstract;
}
using namespace data;
/// Universe data
......@@ -42,6 +46,8 @@ namespace world {
report_buffer chunk_unload;
/// Regions in memory
report_buffer region_count;
/// Entity instances
report_buffer entity_count;
};
Universe(const options &);
......@@ -66,6 +72,23 @@ namespace world {
/// MAYBE: allow set multi area
ItemList setCube(const area_<voxel_pos> &pos, const Voxel &val, int radius);
/// Entities commun properties
struct Entity {
Entity(buffer::Abstract* buffer, const glm::vec3& size = glm::vec3(1), const glm::vec3& scale = glm::vec3(1)):
buffer(buffer), size(size), scale(scale) { };
buffer::Abstract* buffer;
glm::vec3 size;
glm::vec3 scale;
struct Instance {
glm::ifvec3 pos;
glm::vec3 velocity;
};
data::generational::vector<Instance> instances;
};
/// Instante entity
entity_instance_id addEntity(entity_id type, const Entity::Instance &instance);
void getEntitiesModels(std::vector<std::pair<std::vector<glm::mat4>, buffer::Abstract *const>> &buffers, const std::optional<geometry::Frustum> &frustum, const glm::llvec3 &offset, int density);
/// Change contouring worker
void setContouring(const std::shared_ptr<contouring::Abstract>& ct);
/// Get current contouring worker
......@@ -83,6 +106,8 @@ namespace world {
data::generational::vector<Area::params> far_areas;
void saveAreas() const;
data::generational::vector<Entity> entities;
bool running = true;
std::vector<std::thread> loadWorkers;
safe_priority_queue_map<area_<chunk_pos>, std::shared_ptr<Area>, int, area_hash> loadQueue; //NOTE: consider Area const (getRegion uses mutex)
......
......@@ -47,4 +47,7 @@ struct area_hash {
};
using area_pos = glm::ifvec3;
using entity_id = data::generational::id;
using entity_instance_id = std::pair<entity_id, data::generational::id>;
using camera_pos = glm::ifvec3;
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment