#pragma once #include "glContainer.h" #include "pack.h" #include #include #include template class InstanceVertices : protected glContainer { using base = glContainer; public: class [[nodiscard]] InstanceProxy { public: InstanceProxy(InstanceVertices * iv, std::size_t idx) : instances {iv}, index {idx} { } InstanceProxy(InstanceProxy && other) : instances {std::exchange(other.instances, nullptr)}, index {other.index} { } NO_COPY(InstanceProxy); ~InstanceProxy() { if (instances) { instances->release(index); } } InstanceProxy & operator=(InstanceProxy && other) { if (instances) { instances->release(index); } instances = std::exchange(other.instances, nullptr); index = other.index; return *this; } template T & operator=(U && v) { return instances->lookup(index) = std::forward(v); } [[nodiscard]] operator T &() { return instances->lookup(index); } [[nodiscard]] operator const T &() const { return instances->lookup(index); } [[nodiscard]] T * get() { return &instances->lookup(index); } [[nodiscard]] const T * get() const { return &instances->lookup(index); } [[nodiscard]] T * operator->() { return get(); } [[nodiscard]] const T * operator->() const { return get(); } [[nodiscard]] T & operator*() { return instances->lookup(index); } [[nodiscard]] const T & operator*() const { return instances->lookup(index); } private: InstanceVertices * instances; std::size_t index; }; template [[nodiscard]] InstanceProxy acquire(Params &&... params) { if (!unused.empty()) { auto idx = unused.back(); unused.pop_back(); index[idx] = base::size(); base::emplace_back(std::forward(params)...); return InstanceProxy {this, idx}; } index.emplace_back(base::size()); base::emplace_back(std::forward(params)...); return InstanceProxy {this, index.size() - 1}; } using base::bufferName; [[nodiscard]] auto size() const { base::unmap(); return base::size(); } protected: friend InstanceProxy; void release(const size_t pidx) { if (base::size() - 1 != index[pidx]) { lookup(pidx) = std::move(base::back()); *std::find_if(index.begin(), index.end(), [this, old = base::size() - 1](const auto & i) { return i == old && !std::binary_search(unused.begin(), unused.end(), &i - index.data()); }) = index[pidx]; } base::pop_back(); if (pidx == index.size() - 1) { index.pop_back(); } else { // Remember p.index is free index now, keeping it sorted unused.insert(std::upper_bound(unused.begin(), unused.end(), pidx), pidx); } } [[nodiscard]] T & lookup(size_t pindex) { return base::data()[index[pindex]]; } // Index into buffer given to nth proxy std::vector index; // List of free spaces in index std::vector unused; };