Skip to content

Commit

Permalink
Merge pull request #52 from hotstreams/ref/instanced
Browse files Browse the repository at this point in the history
Ref/instanced
  • Loading branch information
hotstreams authored Dec 16, 2023
2 parents ccf0ce6 + b3f4218 commit 1846c09
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 109 deletions.
5 changes: 3 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ project(limitless-engine)
OPTION(BUILD_SAMPLES "Builds samples" OFF)
OPTION(BUILD_TESTS "Builds tests" ON)

OPTION(OPENGL_DEBUG "Enables debug mode for OpenGL" ON)
OPTION(OPENGL_NO_EXTENSIONS "Disables all extensions" ON)
OPTION(OPENGL_DEBUG "Enables debug mode for OpenGL" OFF)
OPTION(OPENGL_NO_EXTENSIONS "Disables all extensions" OFF)
OPTION(OPENGL_SHADER_OUTPUT "Outputs all source shaders for GLSLANG testing" OFF)

#########################################
Expand Down Expand Up @@ -75,6 +75,7 @@ set(ENGINE_INSTANCES
src/limitless/instances/socket_attachment.cpp
src/limitless/instances/instance_builder.cpp
src/limitless/instances/decal_instance.cpp
src/limitless/instances/instanced_instance.cpp
)

set(ENGINE_LIGHTING
Expand Down
2 changes: 1 addition & 1 deletion include/limitless/core/texture/texture.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ namespace Limitless {
// allocates mutable storage
void image(const void* data = nullptr);
void image(const std::array<void*, 6>& data);
void image(const std::vector<const void*>& layers);
void image(uint32_t level, glm::uvec2 size, const void* data = nullptr);
void image(uint32_t level, glm::uvec3 size, const void* data = nullptr);

Expand All @@ -191,7 +192,6 @@ namespace Limitless {
void compressedImage(uint32_t level, glm::uvec2 size, const void* data, std::size_t byte_count);
void compressedImage(uint32_t level, glm::uvec3 size, const void* data, std::size_t byte_count);


/* UPLOADING FUNCTIONS */

// uploads data
Expand Down
2 changes: 2 additions & 0 deletions include/limitless/core/texture/texture_builder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ namespace Limitless {
std::unique_ptr<Texture> texture;
std::array<void*, 6> cube_data {};
const void* data_ {};
std::vector<const void*> layers_data;
std::size_t byte_count {};
public:
Builder();
Expand All @@ -30,6 +31,7 @@ namespace Limitless {
Builder& format(Texture::Format format);
Builder& target(Texture::Type target);
Builder& data(const void* data);
Builder& layer_data(const void* data);
Builder& data(const std::array<void*, 6>& data);
Builder& compressed_data(const void* data, std::size_t bytes);
Builder& levels(uint32_t levels);
Expand Down
114 changes: 11 additions & 103 deletions include/limitless/instances/instanced_instance.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,123 +5,31 @@
#include <limitless/core/context.hpp>

namespace Limitless {
template<typename Instance, typename = void>
class InstancedInstance : public Instance {
static_assert(std::is_same_v<Instance, Instance>, "InstancedInstance for this typename is unimplemented!");
};

template<>
class InstancedInstance<ModelInstance> : public Instance {
protected:
// contains instanced models
std::vector<std::unique_ptr<ModelInstance>> instances;
std::vector<std::shared_ptr<ModelInstance>> instances;

// contains model matrices for each ModelInstance
std::shared_ptr<Buffer> buffer;

void initializeBuffer(uint32_t count) {
buffer = Buffer::builder()
.target(Buffer::Type::ShaderStorage)
.usage(Buffer::Usage::DynamicDraw)
.access(Buffer::MutableAccess::WriteOrphaning)
.data(nullptr)
.size(sizeof(glm::mat4) * count)
.build("model_buffer", *ContextState::getState(glfwGetCurrentContext()));
}

virtual void checkSize() {
if (buffer->getSize() < sizeof(glm::mat4) * instances.size()) {
buffer->resize(sizeof(glm::mat4) * instances.size());
}
}

void updateBoundingBox() noexcept override {
assert("RIP");
}

void updateBuffer(Context& context, const Camera& camera) {
checkSize();

std::vector<glm::mat4> data;
data.reserve(instances.size());

for (const auto& instance : instances) {
//TODO
// if (instance->isHidden()) {
// continue;
// }

instance->update(context, camera);

data.emplace_back(instance->getModelMatrix());
}

buffer->mapData(data.data(), data.size() * sizeof(glm::mat4));
}
std::vector<glm::mat4> current_data;

explicit InstancedInstance(InstanceType shader, const glm::vec3& position, uint32_t count)
: Instance(shader, position) {
initializeBuffer(count);
}
void updateBoundingBox() noexcept override;
void updateBuffer();
public:
explicit InstancedInstance(const glm::vec3& position, uint32_t count = 4)
: Instance(InstanceType::Instanced, position) {
initializeBuffer(count);
}

InstancedInstance();
~InstancedInstance() override = default;

InstancedInstance(const InstancedInstance& rhs)
: Instance(rhs.shader_type, rhs.position) {
initializeBuffer(rhs.instances.size());
for (const auto& instance : rhs.instances) {
instances.emplace_back((ModelInstance*)instance->clone().release());
}
}
InstancedInstance(const InstancedInstance& rhs);
InstancedInstance(InstancedInstance&&) noexcept = default;

std::unique_ptr<Instance> clone() noexcept override {
return std::make_unique<InstancedInstance>(*this);
}

void addInstance(std::unique_ptr<ModelInstance> instance) {
instances.emplace_back(std::move(instance));
}

void removeInstance(size_t index) {
instances.erase(instances.begin() + index);
}

auto& getInstances() noexcept { return instances; }
[[nodiscard]] const auto& getInstances() const noexcept { return instances; }

ModelInstance& operator[](size_t index) { return *instances[index]; }
const ModelInstance& operator[](size_t index) const { return *instances[index]; }

ModelInstance& at(size_t index) { return *instances.at(index); }
[[nodiscard]] const ModelInstance& at(size_t index) const { return *instances.at(index); }

void update(Context& context, const Camera& camera) override {
if (instances.empty()) {
return;
}

Instance::update(context, camera);

updateBuffer(context, camera);
}

void draw(Context& ctx, const Assets& assets, ShaderType pass, ms::Blending blending, const UniformSetter& uniform_set) override {
if (hidden || instances.empty()) {
return;
}
std::unique_ptr<Instance> clone() noexcept override;

buffer->bindBase(ctx.getIndexedBuffers().getBindingPoint(IndexedBuffer::Type::ShaderStorage, "model_buffer"));
void add(const std::shared_ptr<ModelInstance>& instance);
void remove(uint64_t id);

// iterates over all meshes
for (auto& [name, mesh] : instances[0]->getMeshes()) {
mesh.draw_instanced(ctx, assets, pass, shader_type, model_matrix, blending, uniform_set, instances.size());
}
}
void update(Context& context, const Camera& camera) override;
void draw(Context& ctx, const Assets& assets, ShaderType pass, ms::Blending blending, const UniformSetter& uniform_set) override;
};
}
14 changes: 13 additions & 1 deletion src/limitless/core/texture/texture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,18 @@ void Texture::image(const std::array<void*, 6>& data) {
}
}

void Texture::image(const std::vector<const void*>& layers) {
setParameters();

for (uint32_t i = 0; i < layers.size(); ++i) {
texture->texImage3D(static_cast<GLenum>(target), 0, static_cast<GLenum>(internal_format), static_cast<GLenum>(format), static_cast<GLenum>(data_type), {size.x, size.y, i}, false, layers.at(i));
}

if (mipmap) {
generateMipMap();
}
}

void Texture::image(uint32_t level, glm::uvec2 _size, const void* data) {
setParameters();
texture->texImage2D(static_cast<GLenum>(target), level, static_cast<GLenum>(internal_format), static_cast<GLenum>(format), static_cast<GLenum>(data_type), _size, false, data);
Expand Down Expand Up @@ -214,4 +226,4 @@ bool Texture::isImmutable() const noexcept {

Texture::Builder Texture::builder() {
return {};
}
}
11 changes: 10 additions & 1 deletion src/limitless/core/texture/texture_builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ Texture::Builder& Texture::Builder::data(const void* _data) {
return *this;
}

Texture::Builder & Texture::Builder::layer_data(const void *data) {
layers_data.emplace_back(data);
return *this;
}

Texture::Builder& Texture::Builder::data(const std::array<void *, 6>& _data) {
cube_data = _data;
return *this;
Expand Down Expand Up @@ -121,7 +126,11 @@ std::shared_ptr<Texture> Texture::Builder::buildMutable() {
if (isCompressed()) {
texture->compressedImage(data_, byte_count);
} else {
isCubeMap() ? texture->image(cube_data) : texture->image(data_);
if (!layers_data.empty()) {
texture->image(layers_data);
} else {
isCubeMap() ? texture->image(cube_data) : texture->image(data_);
}
}

auto tex = std::move(texture);
Expand Down
97 changes: 97 additions & 0 deletions src/limitless/instances/instanced_instance.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#include <limitless/instances/instanced_instance.hpp>

using namespace Limitless;

InstancedInstance::InstancedInstance()
: Instance {InstanceType::Instanced, glm::vec3{0.0f}}
, buffer {Buffer::builder()
.target(Buffer::Type::ShaderStorage)
.usage(Buffer::Usage::DynamicDraw)
.access(Buffer::MutableAccess::WriteOrphaning)
.data(nullptr)
.size(sizeof(glm::mat4) * 1)
.build("model_buffer", *ContextState::getState(glfwGetCurrentContext()))} {
}

InstancedInstance::InstancedInstance(const InstancedInstance& rhs)
: Instance(rhs)
, buffer {Buffer::builder()
.target(Buffer::Type::ShaderStorage)
.usage(Buffer::Usage::DynamicDraw)
.access(Buffer::MutableAccess::WriteOrphaning)
.data(nullptr)
.size(sizeof(glm::mat4) * 1)
.build("model_buffer", *ContextState::getState(glfwGetCurrentContext()))} {
for (const auto& instance : rhs.instances) {
instances.emplace_back((ModelInstance*)instance->clone().release());
}
}

void InstancedInstance::updateBoundingBox() noexcept {

}

std::unique_ptr<Instance> InstancedInstance::clone() noexcept {
return std::make_unique<InstancedInstance>(*this);
}

void InstancedInstance::add(const std::shared_ptr<ModelInstance>& instance) {
instances.emplace_back(instance);
}

void InstancedInstance::remove(uint64_t id){
std::remove_if(instances.begin(), instances.end(), [&] (auto& i) { return i->getId() == id; });
}

void InstancedInstance::updateBuffer() {
std::vector<glm::mat4> new_data;
new_data.reserve(instances.size());

for (const auto& instance : instances) {
if (instance->isHidden()) {
continue;
}

new_data.emplace_back(instance->getFinalMatrix());
}

// if update is needed
if (new_data != current_data) {
// ensure buffer size
auto size = sizeof(glm::mat4) * new_data.size();
if (buffer->getSize() < size) {
buffer->resize(size);
}

buffer->mapData(new_data.data(), size);

current_data = new_data;
}
}

void InstancedInstance::update(Context& context, const Camera& camera) {
if (instances.empty()) {
return;
}

Instance::update(context, camera);

for (const auto& instance : instances) {
instance->update(context, camera);
}

updateBuffer();
}

void InstancedInstance::draw(Context& ctx, const Assets& assets, ShaderType pass, ms::Blending blending, const UniformSetter& uniform_set) {
if (hidden || instances.empty()) {
return;
}

buffer->bindBase(ctx.getIndexedBuffers().getBindingPoint(IndexedBuffer::Type::ShaderStorage, "model_buffer"));

// iterates over all meshes
for (auto& [name, mesh] : instances[0]->getMeshes()) {
mesh.draw_instanced(ctx, assets, pass, shader_type, model_matrix, blending, uniform_set, instances.size());
}
}
1 change: 0 additions & 1 deletion src/limitless/pipeline/deferred/depth_pass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ void DepthPass::draw([[maybe_unused]] Instances& instances, Context& ctx, [[mayb
ctx.setStencilFunc(StencilFunc::Always, 1, 0xFF);

auto& fb = pipeline.get<DeferredFramebufferPass>().getFramebuffer();

fb.bind();

for (auto& instance : instances) {
Expand Down

0 comments on commit 1846c09

Please sign in to comment.