aboutsummaryrefslogtreecommitdiff
path: root/apps/openmb/scene/VoxelEditor.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'apps/openmb/scene/VoxelEditor.hpp')
-rw-r--r--apps/openmb/scene/VoxelEditor.hpp132
1 files changed, 132 insertions, 0 deletions
diff --git a/apps/openmb/scene/VoxelEditor.hpp b/apps/openmb/scene/VoxelEditor.hpp
new file mode 100644
index 0000000..a77f547
--- /dev/null
+++ b/apps/openmb/scene/VoxelEditor.hpp
@@ -0,0 +1,132 @@
+#ifndef OPENMB_APPS_OPENMB_SCENE_VOXELEDITOR_H
+#define OPENMB_APPS_OPENMB_SCENE_VOXELEDITOR_H
+
+#include <glm/glm.hpp>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+namespace scene {
+class GridSystem;
+
+struct IVec3Hash {
+ size_t operator()( const glm::ivec3& v ) const noexcept {
+ uint32_t x = static_cast<uint32_t>( v.x );
+ uint32_t y = static_cast<uint32_t>( v.y );
+ uint32_t z = static_cast<uint32_t>( v.z );
+ return (size_t)( ( x * 73856093u ) ^ ( y * 19349663u ) ^ ( z * 83492791u ) );
+ }
+};
+
+struct IVec3Eq {
+ bool operator()( const glm::ivec3& a, const glm::ivec3& b ) const noexcept {
+ return a.x == b.x && a.y == b.y && a.z == b.z;
+ }
+};
+
+class VoxelEditor {
+ public:
+ VoxelEditor( const GridSystem& gridSystem );
+ ~VoxelEditor() = default;
+
+ void processInput( const glm::vec3& rayOrigin, const glm::vec3& rayDir, bool leftMouseDown, bool rightMouseDown,
+ bool shiftPressed, const std::vector<std::pair<glm::vec3, glm::vec3>>& baseWorldBoxes,
+ int currentTextureId, bool placeCollidable );
+
+ void applyCircularBrush( const glm::vec3& centerWorld, float radiusWorld, float heightWorld, int textureId,
+ bool placeCollidable );
+
+ void undo();
+
+ void redo();
+
+ const std::vector<glm::ivec3>& getPlacedCells() const;
+
+ const std::vector<glm::ivec3>& getPreviewCells() const;
+
+ size_t getUndoStackSize() const;
+
+ size_t getRedoStackSize() const;
+
+ glm::ivec3 worldPosToCell( const glm::vec3& pos ) const;
+
+ glm::vec3 cellToWorldCenter( const glm::ivec3& cell ) const;
+
+ std::pair<glm::vec3, glm::vec3> cellToAABB( const glm::ivec3& cell ) const;
+
+ std::vector<std::pair<glm::vec3, glm::vec3>>
+ getAllCollisionBoxes( const std::vector<std::pair<glm::vec3, glm::vec3>>& baseWorldBoxes ) const;
+
+ int getTextureIdForCell( const glm::ivec3& cell ) const;
+
+ bool saveToFile( const std::string& filepath ) const;
+
+ bool loadFromFile( const std::string& filepath );
+
+ void clear();
+
+ struct ModelInstance {
+ int modelIndex;
+ glm::vec3 pos;
+ float yaw;
+ float scale;
+ bool mCollidable;
+ };
+
+ void addModelInstance( const ModelInstance& mi );
+ const std::vector<ModelInstance>& getPlacedModels() const;
+
+ private:
+ struct VoxelData {
+ glm::ivec3 mCell;
+ int mTextureId;
+ bool mCollidable;
+ };
+
+ struct Action {
+ std::vector<VoxelData> mAddedCells;
+ std::vector<VoxelData> mRemovedCells;
+ struct ModelInstance {
+ int modelIndex;
+ glm::vec3 pos;
+ float yaw;
+ float scale;
+ bool mCollidable;
+ };
+ std::vector<ModelInstance> mAddedModels;
+ std::vector<ModelInstance> mRemovedModels;
+ };
+
+ bool rayAABBIntersect( const glm::vec3& ro, const glm::vec3& rd, const glm::vec3& bmin, const glm::vec3& bmax,
+ float& outT ) const;
+
+ std::vector<glm::ivec3> rasterizeGridBox( const glm::ivec3& a, const glm::ivec3& b ) const;
+
+ std::vector<glm::ivec3> rasterizeCircle( const glm::ivec3& centerCell, int radiusCells,
+ int heightLayers ) const;
+
+ const GridSystem& mGridSystem;
+
+ std::unordered_set<glm::ivec3, IVec3Hash, IVec3Eq> mPlacedSet;
+ std::vector<glm::ivec3> mPlacedList;
+ std::unordered_map<glm::ivec3, int, IVec3Hash, IVec3Eq> mCellTextureIds;
+ std::unordered_map<glm::ivec3, bool, IVec3Hash, IVec3Eq> mCellCollidable;
+
+ std::vector<Action> mUndoStack;
+ std::vector<Action> mRedoStack;
+ static constexpr int mMaxUndoSteps = 100;
+
+ std::vector<ModelInstance> mPlacedModels;
+
+ bool mDragging;
+ int mDragButton;
+ glm::ivec3 mDragStartCell;
+ std::vector<glm::ivec3> mPreviewCells;
+
+ bool mPrevLeftDown;
+ bool mPrevRightDown;
+};
+} // namespace scene
+
+#endif