diff options
Diffstat (limited to 'apps/openmb/scene/VoxelEditor.hpp')
| -rw-r--r-- | apps/openmb/scene/VoxelEditor.hpp | 132 |
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 |