#include "editor/VolumeBrushTool.h" #include "editor/BrushKernel.h" #include "editor/EditorPresentation.h" #include "editor/EditorSession.h" #include "editor/EditorVolumeTarget.h" #include #include #include namespace eve::editor { namespace { float falloffWeight(const IBrushFalloff* falloff, float normalizedDistance) { return falloff ? falloff->evaluate(normalizedDistance) : (normalizedDistance <= 1.f ? 1.f : 0.f); } } // namespace void SphereVolumeBrushKernel::sample(float x, float y, float z, float radius, std::vector& out) const { out.clear(); radius = std::max(0.5f, radius); const int minX = static_cast(std::floor(x - radius)); const int maxX = static_cast(std::ceil(x + radius)); const int minY = static_cast(std::floor(y - radius)); const int maxY = static_cast(std::ceil(y + radius)); const int minZ = static_cast(std::floor(z - radius)); const int maxZ = static_cast(std::ceil(z + radius)); for (int iz = minZ; iz <= maxZ; ++iz) for (int iy = minY; iy <= maxY; ++iy) for (int ix = minX; ix <= maxX; ++ix) { const float dx = static_cast(ix) - x; const float dy = static_cast(iy) - y; const float dz = static_cast(iz) - z; const float distance = std::sqrt(dx * dx + dy * dy + dz * dz); if (distance <= radius) out.push_back({ix, iy, iz, falloffWeight(falloff_, distance / radius)}); } } void BoxVolumeBrushKernel::sample(float x, float y, float z, float radius, std::vector& out) const { out.clear(); radius = std::max(0.5f, radius); const int minX = static_cast(std::ceil(x - radius)); const int maxX = static_cast(std::floor(x + radius)); const int minY = static_cast(std::ceil(y - radius)); const int maxY = static_cast(std::floor(y + radius)); const int minZ = static_cast(std::ceil(z - radius)); const int maxZ = static_cast(std::floor(z + radius)); for (int iz = minZ; iz <= maxZ; ++iz) for (int iy = minY; iy <= maxY; ++iy) for (int ix = minX; ix <= maxX; ++ix) { const float distance = std::max({std::fabs(static_cast(ix) - x), std::fabs(static_cast(iy) - y), std::fabs(static_cast(iz) - z)}); out.push_back({ix, iy, iz, falloffWeight(falloff_, distance / radius)}); } } PaintIntVolumeOperation::PaintIntVolumeOperation(int value) { setValue(value); } void PaintIntVolumeOperation::setValue(int value) { value_ = std::clamp(value, 0, 255); } std::unique_ptr PaintIntVolumeOperation::createCommand( IEditableTarget* target, const std::vector& points) const { if (!target || !target->query()) return nullptr; auto command = std::make_unique("paint integer volume", target); for (const auto& point : points) { if (point.weight > 0.f) command->record(point.x, point.y, point.z, value_); } return command; } VolumeBrushTool::VolumeBrushTool(std::string id, std::string label) : descriptor_{std::move(id), std::move(label), {}} {} void VolumeBrushTool::setRadius(float radius) { radius_ = std::max(0.5f, radius); } bool VolumeBrushTool::stamp(EditorContext& context, float x, float y, float z) { if (!kernel_ || !operation_ || !context.target()) return false; kernel_->sample(x, y, z, radius_, points_); auto command = operation_->createCommand(context.target(), points_); return command && context.execute(std::move(command)); } ToolResponse VolumeBrushTool::pointerEvent(EditorContext& context, const EditorPointerEvent& event) { if (event.phase == EditorPointerEvent::Phase::Down) { if (!kernel_ || !operation_ || !context.target() || !context.transactions().begin(descriptor_.label)) return ToolResponse::ignored(); stroking_ = true; stamp(context, event.x, event.y, event.z); return ToolResponse::capture(); } if (!stroking_) return ToolResponse::ignored(); if (event.phase == EditorPointerEvent::Phase::Move) { stamp(context, event.x, event.y, event.z); return ToolResponse::consumed(); } if (event.phase == EditorPointerEvent::Phase::Up) { context.transactions().commit(); stroking_ = false; return ToolResponse::release(); } cancel(context); return ToolResponse::release(); } void VolumeBrushTool::cancel(EditorContext& context) { if (stroking_) context.transactions().rollback(); stroking_ = false; } void VolumeBrushTool::inspect(EditorContext&, IEditorInspector& inspector) { inspector.beginGroup(descriptor_.id, descriptor_.label); inspector.scalar("radius", "Radius", radius_, 0.5f, 256.f); inspector.endGroup(); setRadius(radius_); } } // namespace eve::editor