Files
panopainter/src/app_core/grid_ui.h

210 lines
6.9 KiB
C++

#pragma once
#include "foundation/result.h"
#include <string>
#include <string_view>
#include <utility>
namespace pp::app {
enum class GridUiOperation {
request_heightmap_pick,
load_heightmap,
clear_heightmap,
reload_heightmap,
render_lightmap,
commit_heightmap,
};
struct GridUiPlan {
GridUiOperation operation = GridUiOperation::request_heightmap_pick;
std::string path;
int texture_resolution = 0;
int sample_count = 0;
bool opens_picker = false;
bool loads_heightmap = false;
bool clears_heightmap = false;
bool renders_lightmap = false;
bool commits_heightmap = false;
bool updates_preview = false;
bool updates_ground_opacity = false;
bool updates_shading_mode = false;
bool shows_unsupported_message = false;
bool shows_progress = false;
bool mutates_grid_state = false;
};
class GridUiServices {
public:
virtual ~GridUiServices() = default;
virtual void request_heightmap_pick() = 0;
virtual pp::foundation::Status load_heightmap(std::string_view path, bool raise_ground_opacity) = 0;
virtual void clear_heightmap(bool updates_preview) = 0;
virtual void render_lightmap(bool shows_unsupported_message, bool renders_lightmap) = 0;
virtual void commit_heightmap(bool updates_ground_opacity) = 0;
};
[[nodiscard]] inline pp::foundation::Status validate_grid_texture_resolution(int texture_resolution) noexcept
{
if (texture_resolution <= 0 || texture_resolution > 16384) {
return pp::foundation::Status::out_of_range("grid texture resolution must be within 1..16384");
}
return pp::foundation::Status::success();
}
[[nodiscard]] inline pp::foundation::Status validate_grid_lightmap_samples(int sample_count) noexcept
{
if (sample_count <= 0 || sample_count > 4096) {
return pp::foundation::Status::out_of_range("grid lightmap samples must be within 1..4096");
}
return pp::foundation::Status::success();
}
[[nodiscard]] inline constexpr GridUiPlan plan_grid_heightmap_pick() noexcept
{
GridUiPlan plan;
plan.operation = GridUiOperation::request_heightmap_pick;
plan.opens_picker = true;
return plan;
}
[[nodiscard]] inline pp::foundation::Result<GridUiPlan> plan_grid_heightmap_load(std::string_view path)
{
if (path.empty()) {
return pp::foundation::Result<GridUiPlan>::failure(
pp::foundation::Status::invalid_argument("heightmap path must not be empty"));
}
GridUiPlan plan;
plan.operation = GridUiOperation::load_heightmap;
plan.path = std::string(path);
plan.loads_heightmap = true;
plan.updates_preview = true;
plan.updates_ground_opacity = true;
plan.mutates_grid_state = true;
return pp::foundation::Result<GridUiPlan>::success(std::move(plan));
}
[[nodiscard]] inline constexpr GridUiPlan plan_grid_heightmap_clear(bool has_heightmap) noexcept
{
GridUiPlan plan;
plan.operation = GridUiOperation::clear_heightmap;
plan.clears_heightmap = true;
plan.updates_preview = has_heightmap;
plan.mutates_grid_state = has_heightmap;
return plan;
}
[[nodiscard]] inline pp::foundation::Result<GridUiPlan> plan_grid_heightmap_reload(std::string_view path)
{
auto plan = plan_grid_heightmap_load(path);
if (!plan) {
return pp::foundation::Result<GridUiPlan>::failure(plan.status());
}
plan.value().operation = GridUiOperation::reload_heightmap;
plan.value().updates_ground_opacity = false;
return plan;
}
[[nodiscard]] inline pp::foundation::Result<GridUiPlan> plan_grid_lightmap_render(
bool has_heightmap,
bool supports_float32,
bool supports_float16,
int texture_resolution,
int sample_count)
{
const auto texture_status = validate_grid_texture_resolution(texture_resolution);
if (!texture_status.ok()) {
return pp::foundation::Result<GridUiPlan>::failure(texture_status);
}
const auto sample_status = validate_grid_lightmap_samples(sample_count);
if (!sample_status.ok()) {
return pp::foundation::Result<GridUiPlan>::failure(sample_status);
}
GridUiPlan plan;
plan.operation = GridUiOperation::render_lightmap;
plan.texture_resolution = texture_resolution;
plan.sample_count = sample_count;
if (!supports_float32 && !supports_float16) {
plan.shows_unsupported_message = true;
return pp::foundation::Result<GridUiPlan>::success(plan);
}
plan.renders_lightmap = has_heightmap;
plan.shows_progress = has_heightmap;
plan.updates_shading_mode = has_heightmap;
plan.mutates_grid_state = has_heightmap;
return pp::foundation::Result<GridUiPlan>::success(plan);
}
[[nodiscard]] inline constexpr GridUiPlan plan_grid_heightmap_commit(bool has_canvas) noexcept
{
GridUiPlan plan;
plan.operation = GridUiOperation::commit_heightmap;
plan.commits_heightmap = has_canvas;
plan.updates_ground_opacity = has_canvas;
plan.mutates_grid_state = has_canvas;
return plan;
}
[[nodiscard]] inline pp::foundation::Status execute_grid_ui_plan(
const GridUiPlan& plan,
GridUiServices& services)
{
switch (plan.operation) {
case GridUiOperation::request_heightmap_pick:
if (!plan.opens_picker) {
return pp::foundation::Status::invalid_argument("grid heightmap pick plan must open a picker");
}
services.request_heightmap_pick();
return pp::foundation::Status::success();
case GridUiOperation::load_heightmap:
case GridUiOperation::reload_heightmap:
if (!plan.loads_heightmap || plan.path.empty()) {
return pp::foundation::Status::invalid_argument("grid heightmap load plan must provide a path");
}
return services.load_heightmap(plan.path, plan.updates_ground_opacity);
case GridUiOperation::clear_heightmap:
if (!plan.clears_heightmap) {
return pp::foundation::Status::invalid_argument("grid heightmap clear plan must clear heightmap state");
}
services.clear_heightmap(plan.updates_preview);
return pp::foundation::Status::success();
case GridUiOperation::render_lightmap:
{
const auto texture_status = validate_grid_texture_resolution(plan.texture_resolution);
if (!texture_status.ok()) {
return texture_status;
}
const auto sample_status = validate_grid_lightmap_samples(plan.sample_count);
if (!sample_status.ok()) {
return sample_status;
}
if (!plan.shows_unsupported_message && !plan.renders_lightmap) {
return pp::foundation::Status::success();
}
services.render_lightmap(plan.shows_unsupported_message, plan.renders_lightmap);
return pp::foundation::Status::success();
}
case GridUiOperation::commit_heightmap:
if (plan.commits_heightmap) {
services.commit_heightmap(plan.updates_ground_opacity);
}
return pp::foundation::Status::success();
}
return pp::foundation::Status::invalid_argument("unknown grid UI operation");
}
} // namespace pp::app