#ifndef OPENMB_APPS_OPENMB_SCENE_VOXELEDITOR_H #define OPENMB_APPS_OPENMB_SCENE_VOXELEDITOR_H #include #include #include #include #include namespace scene { class GridSystem; struct IVec3Hash { size_t operator()( const glm::ivec3& v ) const noexcept { uint32_t x = static_cast( v.x ); uint32_t y = static_cast( v.y ); uint32_t z = static_cast( 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>& 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& getPlacedCells() const; const std::vector& 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 cellToAABB( const glm::ivec3& cell ) const; std::vector> getAllCollisionBoxes( const std::vector>& 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& getPlacedModels() const; private: struct VoxelData { glm::ivec3 mCell; int mTextureId; bool mCollidable; }; struct Action { std::vector mAddedCells; std::vector mRemovedCells; struct ModelInstance { int modelIndex; glm::vec3 pos; float yaw; float scale; bool mCollidable; }; std::vector mAddedModels; std::vector mRemovedModels; }; bool rayAABBIntersect( const glm::vec3& ro, const glm::vec3& rd, const glm::vec3& bmin, const glm::vec3& bmax, float& outT ) const; std::vector rasterizeGridBox( const glm::ivec3& a, const glm::ivec3& b ) const; std::vector rasterizeCircle( const glm::ivec3& centerCell, int radiusCells, int heightLayers ) const; const GridSystem& mGridSystem; std::unordered_set mPlacedSet; std::vector mPlacedList; std::unordered_map mCellTextureIds; std::unordered_map mCellCollidable; std::vector mUndoStack; std::vector mRedoStack; static constexpr int mMaxUndoSteps = 100; std::vector mPlacedModels; bool mDragging; int mDragButton; glm::ivec3 mDragStartCell; std::vector mPreviewCells; bool mPrevLeftDown; bool mPrevRightDown; }; } // namespace scene #endif