Added layer draw cache, and eraser works again

This commit is contained in:
Yousef Khadadeh 2025-12-28 17:34:33 +03:00
parent c2abccefc2
commit 7549ad23a1
29 changed files with 484 additions and 1434 deletions

View file

@ -58,10 +58,8 @@ set(sources "src/main.cpp"
"src/RichText/TextBox.cpp"
"src/RichText/TextStyleModifier.cpp"
"src/RichText/ParagraphStyleData.cpp"
"src/DrawingProgram/DrawingProgramCacheNew.cpp"
"src/DrawingProgram/DrawingProgramCache.cpp"
"src/DrawingProgram/DrawingProgram.cpp"
"src/DrawingProgram/DrawingProgramSelection.cpp"
"src/DrawingProgram/Tools/BrushTool.cpp"
"src/DrawingProgram/Tools/TextBoxTool.cpp"
"src/DrawingProgram/Tools/EraserTool.cpp"

View file

@ -87,7 +87,6 @@ namespace NetworkingObjects {
typedef std::function<void(const NetObjOrderedListObjectInfoPtr<T>& objInfo)> InsertCallback;
typedef std::function<void(const NetObjOrderedListObjectInfoPtr<T>& objInfo)> EraseCallback;
typedef std::function<void(const NetObjOrderedListObjectInfoPtr<T>& objInfo, uint32_t oldPos)> MoveCallback;
typedef std::function<void()> PostSyncCallback;
void set_insert_callback(const InsertCallback& func) {
insertCallback = func;
@ -98,9 +97,6 @@ namespace NetworkingObjects {
void set_move_callback(const MoveCallback& func) {
moveCallback = func;
}
void set_post_sync_callback(const PostSyncCallback& func) {
postSyncCallback = func;
}
virtual bool contains(const NetObjID& p) const = 0;
virtual const std::vector<NetObjOrderedListObjectInfoPtr<T>>& get_data() const = 0;
@ -142,16 +138,10 @@ namespace NetworkingObjects {
moveCallback(objInfo, oldPos);
}
void call_post_sync_callback() {
if(postSyncCallback)
postSyncCallback();
}
private:
InsertCallback insertCallback;
EraseCallback eraseCallback;
MoveCallback moveCallback;
PostSyncCallback postSyncCallback;
template <typename S> friend void register_ordered_list_class(NetObjManager& t);
static void write_constructor_func(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, cereal::PortableBinaryOutputArchive& a) {
l->write_constructor(l, a);
@ -622,27 +612,17 @@ namespace NetworkingObjects {
clientData.insert(clientData.begin() + pos, objInfoPtr);
}
// Code that tries to not call post_sync_callback, which refreshes the entire cache.
//uint32_t bumpFromServerInsert = 0;
//for(uint32_t i = 0; i < clientData.size(); i++) {
// if(clientData[i]->pos == std::numeric_limits<uint32_t>::max()) {
// clientData[i]->pos = i;
// this->call_insert_callback(clientData[i]);
// bumpFromServerInsert++;
// }
// else if(clientData[i]->pos != (i + bumpFromServerInsert)) {
// uint32_t oldPos = clientData[i]->pos;
// clientData[i]->pos = i;
// this->call_move_callback(clientData[i], oldPos);
// }
// clientData[i]->pos = i;
//}
// Code that calls post_sync_callback
set_positions_for_object_info_vector<T>(clientData);
for(auto& [id, objInfo] : newServerObjects)
this->call_insert_callback(objInfo);
this->call_post_sync_callback();
uint32_t bumpFromServerInsert = 0;
for(uint32_t i = 0; i < clientData.size(); i++) {
uint32_t oldPos = clientData[i]->pos;
clientData[i]->pos = i;
if(oldPos == std::numeric_limits<uint32_t>::max()) {
this->call_insert_callback(clientData[i]);
bumpFromServerInsert++;
}
else if(oldPos != (i + bumpFromServerInsert))
this->call_move_callback(clientData[i], oldPos);
}
}
std::vector<NetObjID> serverData;

View file

@ -65,20 +65,20 @@ void CanvasComponentContainer::set_owner_obj_info(const ObjInfoSharedPtr& ownerO
}
void CanvasComponentContainer::commit_update(DrawingProgram& drawP) {
//auto lockedObjInfo = objInfo.lock();
//if(worldAABB.has_value()) // This is the only point where worldAABB could be nullopt
// drawP.preupdate_component(lockedObjInfo);
auto lockedObjInfo = objInfo.lock();
if(worldAABB.has_value()) // This is the only point where worldAABB could be nullopt
drawP.drawCache.preupdate_component(lockedObjInfo);
get_comp().initialize_draw_data(drawP);
calculate_world_bounds();
//drawP.preupdate_component(lockedObjInfo);
drawP.drawCache.preupdate_component(lockedObjInfo);
}
void CanvasComponentContainer::commit_transform(DrawingProgram& drawP) {
//auto lockedObjInfo = objInfo.lock();
//if(worldAABB.has_value()) // This is the only point where worldAABB could be nullopt
// drawP.preupdate_component(lockedObjInfo);
auto lockedObjInfo = objInfo.lock();
if(worldAABB.has_value()) // This is the only point where worldAABB could be nullopt
drawP.drawCache.preupdate_component(lockedObjInfo);
calculate_world_bounds();
//drawP.preupdate_component(lockedObjInfo);
drawP.drawCache.preupdate_component(lockedObjInfo);
}
void CanvasComponentContainer::commit_update_dont_invalidate_cache(DrawingProgram& drawP) {

View file

@ -9,6 +9,7 @@
class DrawingProgram;
class DrawingProgramCacheBVHNode;
class DrawingProgramLayerListItem;
class CanvasComponentContainer {
public:
@ -44,7 +45,8 @@ class CanvasComponentContainer {
void set_owner_obj_info(const ObjInfoSharedPtr& ownerObjInfo);
void send_comp_update(DrawingProgram& drawP, bool finalUpdate);
std::weak_ptr<DrawingProgramCacheBVHNode> parentBvhNode;
std::weak_ptr<DrawingProgramCacheBVHNode> parentBvhNode; // Should be removed later, the new cache uses an unordered_map to map objects to their cache node
DrawingProgramLayerListItem* parentLayer = nullptr;
CoordSpaceHelper coords;
private:
friend class BrushStrokeCanvasComponent;

View file

@ -65,7 +65,7 @@ void ImageCanvasComponent::update(DrawingProgram& drawP) {
ResourceDisplay* display = drawP.world.drawData.rMan->get_display_data(d.imageID);
if(display) {
if(display->update_draw())
drawP.invalidate_cache_at_component(compContainer->objInfo.lock());
drawP.drawCache.invalidate_cache_at_aabb(compContainer->get_world_bounds());
}
}

View file

@ -45,7 +45,7 @@ void CanvasTheme::set_tool_front_color(DrawingProgram& drawP) {
else
toolFrontColor = SkColor4f{1.0f, 1.0f, 1.0f, 1.0f};
if(oldToolFrontColor != toolFrontColor)
drawP.clear_draw_cache();
drawP.drawCache.clear_own_cached_surfaces();
}
void CanvasTheme::register_class() {

View file

@ -27,48 +27,23 @@
#include <include/effects/SkDashPathEffect.h>
#include <chrono>
#include "../CanvasComponents/ImageCanvasComponent.hpp"
#include "Layers/DrawingProgramLayer.hpp"
#include "Layers/DrawingProgramLayerListItem.hpp"
#include "Tools/EraserTool.hpp"
DrawingProgram::DrawingProgram(World& initWorld):
world(initWorld),
compCache(*this),
layerMan(*this),
selection(*this)
drawCache(*this),
layerMan(*this)
{
drawTool = DrawingProgramToolBase::allocate_tool_type(*this, DrawingProgramToolType::BRUSH);
}
void DrawingProgram::init() {
if(world.netObjMan.is_server()) {
components = world.netObjMan.make_obj<NetworkingObjects::NetObjOrderedList<CanvasComponentContainer>>();
set_component_list_callbacks();
}
layerMan.init();
}
void DrawingProgram::set_component_list_callbacks() {
components->set_insert_callback([&](const CanvasComponentContainer::ObjInfoSharedPtr& c) {
c->obj->set_owner_obj_info(c);
c->obj->commit_update(*this); // Run commit update on insert so that world bounds are calculated
compCache.add_component(c);
if(c->obj->get_comp().get_type() == CanvasComponentType::IMAGE)
updateableComponents.emplace(c);
});
components->set_erase_callback([&](const CanvasComponentContainer::ObjInfoSharedPtr& c) {
compCache.erase_component(c);
selection.erase_component(c);
drawTool->erase_component(c);
std::erase_if(droppedDownloadingFiles, [&c](auto& downloadingFile) {
return downloadingFile.comp == c;
});
updateableComponents.erase(c);
});
components->set_post_sync_callback([&]() {
clear_draw_cache();
});
}
void DrawingProgram::scale_up(const WorldScalar& scaleUpAmount) {
//for(auto& c : components->get_data())
// c->obj->scale_up(scaleUpAmount);
@ -77,35 +52,13 @@ void DrawingProgram::scale_up(const WorldScalar& scaleUpAmount) {
//switch_to_tool(drawTool->get_type() == DrawingProgramToolType::GRIDMODIFY ? DrawingProgramToolType::EDIT : drawTool->get_type(), true);
}
void DrawingProgram::parallel_loop_all_components(std::function<void(const CanvasComponentContainer::ObjInfoSharedPtr&)> func) {
parallel_loop_container(components->get_data(), func);
}
void DrawingProgram::erase_component_set(const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& compsToErase) {
std::unordered_set<NetworkingObjects::NetObjID> idsToErase;
for(auto& c : compsToErase)
idsToErase.emplace(c->obj.get_net_id());
components->erase_unordered_set(components, idsToErase);
}
void DrawingProgram::clear_draw_cache() {
compCache.clear_own_cached_surfaces();
selection.clear_own_cached_surfaces();
}
void DrawingProgram::write_components_server(cereal::PortableBinaryOutputArchive& a) {
components.write_create_message(a);
layerMan.write_components_server(a);
}
void DrawingProgram::read_components_client(cereal::PortableBinaryInputArchive& a) {
components = world.netObjMan.read_create_message<NetworkingObjects::NetObjOrderedList<CanvasComponentContainer>>(a, nullptr);
parallel_loop_all_components([&](const auto& c){
c->obj->commit_update_dont_invalidate_cache(*this);
});
compCache.test_rebuild(components->get_data(), true);
set_component_list_callbacks();
layerMan.read_components_client(a);
drawCache.build({});
}
void DrawingProgram::toolbar_gui() {
@ -154,26 +107,26 @@ bool DrawingProgram::selection_action_menu(Vector2f popupPos) {
bool shouldClose = false;
t.gui.list_popup_menu("Selection popup menu", popupPos, [&]() {
t.gui.text_label_light("Selection menu");
if(t.gui.text_button_left_transparent("Paste", "Paste")) {
selection.deselect_all();
selection.paste_clipboard(popupPos * t.final_gui_scale());
shouldClose = true;
}
if(selection.is_something_selected()) {
if(t.gui.text_button_left_transparent("Copy", "Copy")) {
selection.selection_to_clipboard();
shouldClose = true;
}
if(t.gui.text_button_left_transparent("Cut", "Cut")) {
selection.selection_to_clipboard();
selection.delete_all();
shouldClose = true;
}
if(t.gui.text_button_left_transparent("Delete", "Delete")) {
selection.delete_all();
shouldClose = true;
}
}
//if(t.gui.text_button_left_transparent("Paste", "Paste")) {
// selection.deselect_all();
// selection.paste_clipboard(popupPos * t.final_gui_scale());
// shouldClose = true;
//}
//if(selection.is_something_selected()) {
// if(t.gui.text_button_left_transparent("Copy", "Copy")) {
// selection.selection_to_clipboard();
// shouldClose = true;
// }
// if(t.gui.text_button_left_transparent("Cut", "Cut")) {
// selection.selection_to_clipboard();
// selection.delete_all();
// shouldClose = true;
// }
// if(t.gui.text_button_left_transparent("Delete", "Delete")) {
// selection.delete_all();
// shouldClose = true;
// }
//}
});
return !shouldClose;
}
@ -216,7 +169,7 @@ void DrawingProgram::update() {
if(!badFrametimeTimePoint)
badFrametimeTimePoint = std::chrono::steady_clock::now();
else if(unorderedObjectsExistTimePoint && std::chrono::steady_clock::now() - badFrametimeTimePoint.value() >= std::chrono::seconds(5) && std::chrono::steady_clock::now() - unorderedObjectsExistTimePoint.value() >= std::chrono::seconds(5)) {
force_rebuild_cache();
rebuild_cache();
unorderedObjectsExistTimePoint = std::nullopt;
badFrametimeTimePoint = std::nullopt;
}
@ -224,12 +177,10 @@ void DrawingProgram::update() {
else
badFrametimeTimePoint = std::nullopt;
if(compCache.get_unsorted_component_list().empty()) {
if(!drawCache.unsorted_components_exist())
unorderedObjectsExistTimePoint = std::nullopt;
}
else if(!unorderedObjectsExistTimePoint) {
else if(!unorderedObjectsExistTimePoint)
unorderedObjectsExistTimePoint = std::chrono::steady_clock::now();
}
if(world.main.input.pen.isEraser && !temporaryEraser) {
if(drawTool->get_type() == DrawingProgramToolType::BRUSH)
@ -317,7 +268,7 @@ void DrawingProgram::update() {
else if(world.main.input.key(InputManager::KEY_DRAW_TOOL_PAN).pressed)
switch_to_tool(DrawingProgramToolType::PAN);
selection.update();
//selection.update();
drawTool->tool_update();
// Switch tools after the tool update, not before, so that we dont have to call erase_component on the toolToSwitchToAfterUpdate as well (components will not be erased in this time period)
@ -332,14 +283,8 @@ void DrawingProgram::update() {
update_downloading_dropped_files();
check_updateable_components();
if(drawTool->get_type() == DrawingProgramToolType::ERASER) {
EraserTool* eraserTool = static_cast<EraserTool*>(drawTool.get());
compCache.test_rebuild_dont_include_set_dont_include_nodes(components->get_data(), eraserTool->erasedComponents, eraserTool->erasedBVHNodes);
}
else if(is_selection_allowing_tool(drawTool->get_type()))
compCache.test_rebuild_dont_include_set(components->get_data(), selection.get_selected_set());
else
compCache.test_rebuild(components->get_data());
if(drawCache.should_rebuild())
rebuild_cache();
}
void DrawingProgram::check_updateable_components() {
@ -387,19 +332,20 @@ SkPaint DrawingProgram::select_tool_line_paint() {
return selectLinePaint;
}
void DrawingProgram::force_rebuild_cache() {
void DrawingProgram::rebuild_cache() {
if(drawTool->get_type() == DrawingProgramToolType::ERASER) {
EraserTool* eraserTool = static_cast<EraserTool*>(drawTool.get());
compCache.test_rebuild_dont_include_set_dont_include_nodes(components->get_data(), eraserTool->erasedComponents, eraserTool->erasedBVHNodes, true);
drawCache.build(eraserTool->erasedComponents);
}
else if(is_selection_allowing_tool(drawTool->get_type())) {
//compCache.test_rebuild_dont_include_set(components->get_data(), selection.get_selected_set(), true);
}
else if(is_selection_allowing_tool(drawTool->get_type()))
compCache.test_rebuild_dont_include_set(components->get_data(), selection.get_selected_set(), true);
else
compCache.test_rebuild(components->get_data(), true);
drawCache.build({});
}
void DrawingProgram::drag_drop_update() {
if(controls.cursorHoveringOverCanvas) {
if(controls.cursorHoveringOverCanvas && layerMan.is_a_layer_being_edited()) {
for(auto& droppedItem : world.main.input.droppedItems) {
if(droppedItem.dataPath.has_value() && std::filesystem::is_regular_file(droppedItem.dataPath.value())) {
#ifdef __EMSCRIPTEN_
@ -416,7 +362,7 @@ void DrawingProgram::drag_drop_update() {
img.d.p1 = droppedItem.pos - imDim;
img.d.p2 = droppedItem.pos + imDim;
img.d.imageID = {0, 0};
auto newObjInfo = components->push_back_and_send_create(components, newContainer);
auto newObjInfo = layerMan.add_component_to_layer_being_edited(newContainer);
droppedDownloadingFiles.emplace_back(newObjInfo, world.main.window.size.cast<float>(), FileDownloader::download_data_from_url(droppedItem.dataText.value()));
}
}
@ -437,7 +383,8 @@ void DrawingProgram::update_downloading_dropped_files() {
ResourceDisplay* display = world.rMan.get_display_data(imageID);
if(display->get_type() == ResourceDisplay::Type::FILE) {
Logger::get().log("WORLDFATAL", "Failed to parse image from URL");
components->erase(components, downFile.comp->obj.get_net_id());
auto& parentLayerComponents = downFile.comp->obj->parentLayer->get_layer().components;
parentLayerComponents->erase(parentLayerComponents, downFile.comp->obj.get_net_id());
}
else {
Vector2f imTrueDim = display->get_dimensions();
@ -453,10 +400,12 @@ void DrawingProgram::update_downloading_dropped_files() {
}
return true;
}
case FileDownloader::DownloadData::Status::FAILURE:
case FileDownloader::DownloadData::Status::FAILURE: {
Logger::get().log("WORLDFATAL", "Failed to download data from URL");
components->erase(components, downFile.comp->obj.get_net_id());
auto& parentLayerComponents = downFile.comp->obj->parentLayer->get_layer().components;
parentLayerComponents->erase(parentLayerComponents, downFile.comp->obj.get_net_id());
return true;
}
case FileDownloader::DownloadData::Status::IN_PROGRESS:
return false;
}
@ -488,7 +437,7 @@ void DrawingProgram::add_file_to_canvas_by_path_execute(const std::filesystem::p
img.d.p1 = dropPos - imDim;
img.d.p2 = dropPos + imDim;
img.d.imageID = imageID;
components->push_back_and_send_create(components, newContainer);
layerMan.add_component_to_layer_being_edited(newContainer);
}
}
@ -508,21 +457,7 @@ void DrawingProgram::add_file_to_canvas_by_data(const std::string& fileName, std
img.d.p1 = dropPos - imDim;
img.d.p2 = dropPos + imDim;
img.d.imageID = imageID;
components->push_back_and_send_create(components, newContainer);
}
void DrawingProgram::invalidate_cache_at_component(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck) {
if(selection.is_selected(objToCheck))
selection.invalidate_cache_at_aabb_before_pos(objToCheck->obj->get_world_bounds(), objToCheck->pos);
else
compCache.invalidate_cache_at_aabb_before_pos(objToCheck->obj->get_world_bounds(), objToCheck->pos);
}
void DrawingProgram::preupdate_component(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck) {
if(selection.is_selected(objToCheck))
selection.preupdate_component(objToCheck);
else
compCache.preupdate_component(objToCheck);
layerMan.add_component_to_layer_being_edited(newContainer);
}
float DrawingProgram::drag_point_radius() {
@ -566,36 +501,33 @@ void DrawingProgram::save_file(cereal::PortableBinaryOutputArchive& a) const {
}
void DrawingProgram::draw(SkCanvas* canvas, const DrawData& drawData) {
layerMan.draw(canvas, drawData);
//if(drawData.dontUseDrawProgCache) {
// for(auto& c : components->get_data())
// c->obj->draw(canvas, drawData);
//}
//else {
// canvas->saveLayer(nullptr, nullptr);
// canvas->clear(SkColor4f{0.0f, 0.0f, 0.0f, 0.0f});
// compCache.refresh_all_draw_cache(drawData);
// compCache.draw_components_to_canvas(canvas, drawData, {});
// canvas->saveLayer(nullptr, nullptr);
// selection.draw_components(canvas, drawData);
// canvas->restore();
// canvas->restore();
if(drawData.dontUseDrawProgCache)
layerMan.draw(canvas, drawData);
else {
canvas->saveLayer(nullptr, nullptr);
canvas->clear(SkColor4f{0.0f, 0.0f, 0.0f, 0.0f});
drawCache.refresh_all_draw_cache(drawData);
drawCache.draw_components_to_canvas(canvas, drawData, {});
//canvas->saveLayer(nullptr, nullptr);
// selection.draw_components(canvas, drawData);
//canvas->restore();
canvas->restore();
// if(!drawData.main->takingScreenshot) {
// for(auto& droppedDownFile : droppedDownloadingFiles)
// static_cast<ImageCanvasComponent&>(droppedDownFile.comp->obj->get_comp()).draw_download_progress_bar(canvas, drawData, droppedDownFile.downData->progress);
if(!drawData.main->takingScreenshot) {
for(auto& droppedDownFile : droppedDownloadingFiles)
static_cast<ImageCanvasComponent&>(droppedDownFile.comp->obj->get_comp()).draw_download_progress_bar(canvas, drawData, droppedDownFile.downData->progress);
// for(auto& c : updateableComponents) {
// if(c->obj->get_comp().get_type() == CanvasComponentType::IMAGE) {
// auto& img = static_cast<ImageCanvasComponent&>(c->obj->get_comp());
// float progress = drawData.main->world->rMan.get_resource_retrieval_progress(img.d.imageID);
// img.draw_download_progress_bar(canvas, drawData, progress);
// }
// }
// }
for(auto& c : updateableComponents) {
if(c->obj->get_comp().get_type() == CanvasComponentType::IMAGE) {
auto& img = static_cast<ImageCanvasComponent&>(c->obj->get_comp());
float progress = drawData.main->world->rMan.get_resource_retrieval_progress(img.d.imageID);
img.draw_download_progress_bar(canvas, drawData, progress);
}
}
}
// selection.draw_gui(canvas, drawData);
//}
//selection.draw_gui(canvas, drawData);
}
drawTool->draw(canvas, drawData);
}

View file

@ -12,7 +12,6 @@
#include <Helpers/SCollision.hpp>
#include <Helpers/Hashes.hpp>
#include <Helpers/Random.hpp>
#include "DrawingProgramSelection.hpp"
#include "Tools/DrawingProgramToolBase.hpp"
#include <Helpers/FileDownloader.hpp>
#include <Helpers/NetworkingObjects/NetObjOrderedList.hpp>
@ -24,7 +23,6 @@ class DrawingProgram {
public:
DrawingProgram(World& initWorld);
void init();
void set_component_list_callbacks();
void toolbar_gui();
void tool_options_gui();
bool right_click_popup_gui(Vector2f popupPos);
@ -42,21 +40,13 @@ class DrawingProgram {
World& world;
bool prevent_undo_or_redo();
void parallel_loop_all_components(std::function<void(const CanvasComponentContainer::ObjInfoSharedPtr&)> func);
// This function, unlike preupdate_component, does not take the object out of the BVH. Use for a draw update that doesn't change the object's bounding box
void invalidate_cache_at_component(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck);
void preupdate_component(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck);
DrawingProgramCache compCache;
DrawingProgramCache drawCache;
DrawingProgramLayerManager layerMan;
CanvasComponentContainer::NetListOwnerPtr components;
Vector4f* get_foreground_color_ptr();
void switch_to_tool(DrawingProgramToolType newToolType, bool force = false);
void switch_to_tool_ptr(std::unique_ptr<DrawingProgramToolBase> newTool);
void clear_draw_cache();
void modify_grid(const NetworkingObjects::NetObjWeakPtr<WorldGrid>& gridToModify);
private:
@ -69,7 +59,7 @@ class DrawingProgram {
std::optional<std::chrono::steady_clock::time_point> unorderedObjectsExistTimePoint;
bool selection_action_menu(Vector2f popupPos);
void force_rebuild_cache();
void rebuild_cache();
std::atomic<bool> addFileInNextFrame = false;
std::pair<std::filesystem::path, Vector2f> addFileInfo;
@ -79,9 +69,8 @@ class DrawingProgram {
SkPaint select_tool_line_paint();
bool is_actual_selection_tool(DrawingProgramToolType typeToCheck);
bool is_selection_allowing_tool(DrawingProgramToolType typeToCheck);
void erase_component_set(const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& compsToErase);
DrawingProgramSelection selection;
//DrawingProgramSelection selection;
std::unique_ptr<DrawingProgramToolBase> toolToSwitchToAfterUpdate;
std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr> updateableComponents;
@ -105,6 +94,8 @@ class DrawingProgram {
bool middleClickReleased = false;
bool cursorHoveringOverCanvas = false;
DrawingProgramLayerManager::LayerSelector layerSelector = DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED;
int colorEditing = 0;
} controls;
@ -140,4 +131,7 @@ class DrawingProgram {
friend class ZoomCanvasTool;
friend class PanCanvasTool;
friend class DrawCamera;
friend class DrawingProgramLayerManager;
friend class DrawingProgramLayer;
friend class DrawingProgramLayerFolder;
};

View file

@ -2,9 +2,6 @@
#include "DrawingProgram.hpp"
#include "../World.hpp"
#include "../MainProgram.hpp"
#include "Helpers/Parallel.hpp"
#include <chrono>
#include <optional>
#ifdef USE_SKIA_BACKEND_GRAPHITE
#include <include/gpu/graphite/Surface.h>
@ -13,162 +10,80 @@
#include <include/gpu/ganesh/SkSurfaceGanesh.h>
#endif
#define MAXIMUM_DRAW_CACHE_SURFACES 40
std::unordered_set<std::shared_ptr<DrawingProgramCacheBVHNode>> DrawingProgramCache::nodesWithCachedSurfaces;
std::unordered_map<std::shared_ptr<DrawingProgramCache::BVHNode>, DrawingProgramCache::NodeCache> DrawingProgramCache::nodeCacheMap;
DrawingProgramCache::DrawingProgramCache(DrawingProgram& initDrawP):
drawP(initDrawP)
{
lastBvhBuildTime = std::chrono::steady_clock::now();
}
void DrawingProgramCache::build(std::vector<CanvasComponentContainer::ObjInfoSharedPtr> components) {
bvhRoot = std::make_shared<DrawingProgramCacheBVHNode>();
unsortedComponents.clear();
clear_own_cached_surfaces();
build_bvh_node(bvhRoot, components);
lastBvhBuildTime = std::chrono::steady_clock::now();
}
void DrawingProgramCache::clear() {
bvhRoot = nullptr;
unsortedComponents.clear();
clear_own_cached_surfaces();
}
void DrawingProgramCache::clear_own_cached_surfaces() {
std::erase_if(nodesWithCachedSurfaces, [&](auto& n) {
if(n->drawCache.value().attachedCache == this) {
n->drawCache = std::nullopt;
return true;
}
return false;
});
}
bool DrawingProgramCache::check_if_rebuild_should_occur() {
return (unsortedComponents.size() >= MINIMUM_COMPONENTS_TO_START_REBUILD);
}
void DrawingProgramCache::test_rebuild(const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& comps, bool force) {
if(force || check_if_rebuild_should_occur())
force_rebuild(comps);
}
void DrawingProgramCache::test_rebuild_dont_include_set(const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& comps, const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& objsToNotInclude, bool force) {
if(force || check_if_rebuild_should_occur())
force_rebuild_dont_include_objs(comps, objsToNotInclude);
}
void DrawingProgramCache::test_rebuild_dont_include_set_dont_include_nodes(const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& comps, const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& objsToNotInclude, const std::unordered_set<std::shared_ptr<DrawingProgramCacheBVHNode>>& nodesToNotInclude, bool force) {
if(force || check_if_rebuild_should_occur()) {
auto objsToNotIncludeNew = objsToNotInclude;
move_components_from_bvh_nodes_to_set(objsToNotIncludeNew, nodesToNotInclude);
force_rebuild_dont_include_objs(comps, objsToNotIncludeNew);
}
}
void DrawingProgramCache::move_components_from_bvh_nodes_to_set(std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& s, const std::unordered_set<std::shared_ptr<DrawingProgramCacheBVHNode>>& bvhNodes) {
for(auto& b : bvhNodes)
move_components_from_bvh_node_to_set(s, b);
}
void DrawingProgramCache::move_components_from_bvh_node_to_set(std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& s, const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode) {
for(auto& c : bvhNode->components)
s.emplace(c);
for(auto& p : bvhNode->children)
move_components_from_bvh_node_to_set(s, p);
}
void DrawingProgramCache::force_rebuild_dont_include_objs(std::vector<CanvasComponentContainer::ObjInfoSharedPtr> componentsToBuild, const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& objsToNotInclude) {
std::erase_if(componentsToBuild, [&objsToNotInclude](auto& c) {
return objsToNotInclude.contains(c);
});
build(componentsToBuild);
}
void DrawingProgramCache::force_rebuild(const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& componentsToBuild) {
build(componentsToBuild);
}
const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& DrawingProgramCache::get_unsorted_component_list() const {
return unsortedComponents;
}
const std::chrono::steady_clock::time_point& DrawingProgramCache::get_last_bvh_build_time() const {
return lastBvhBuildTime;
}
{}
void DrawingProgramCache::add_component(const CanvasComponentContainer::ObjInfoSharedPtr& c) {
unsortedComponents.emplace_back(c);
invalidate_cache_at_aabb_before_pos(c->obj->get_world_bounds(), c->pos);
invalidate_cache_at_aabb(c->obj->get_world_bounds());
}
void DrawingProgramCache::erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& c) {
auto parentBvhNode = c->obj->parentBvhNode.lock();
if(parentBvhNode) {
std::erase(parentBvhNode->components, c);
c->obj->parentBvhNode.reset();
}
auto it = objToBVHNode.find(c);
if(it != objToBVHNode.end())
objToBVHNode.erase(it);
else
std::erase(unsortedComponents, c);
invalidate_cache_at_aabb_before_pos(c->obj->get_world_bounds(), c->pos);
invalidate_cache_at_aabb(c->obj->get_world_bounds());
}
void DrawingProgramCache::traverse_bvh_erase_function(const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node, std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components)> f) {
f(nullptr, unsortedComponents);
if(traverse_bvh_erase_function_recursive(bvhRoot, aabb, f))
bvhRoot = nullptr;
}
bool DrawingProgramCache::traverse_bvh_erase_function_recursive(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node, std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components)> f) {
if(bvhNode && SCollision::collide(aabb, bvhNode->bounds)) {
if(f(bvhNode, bvhNode->components))
return true;
std::erase_if(bvhNode->children, [&](auto& p) {
return traverse_bvh_erase_function_recursive(p, aabb, f);
});
}
return false;
}
void DrawingProgramCache::traverse_bvh_run_function(const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components)> f) {
f(nullptr, unsortedComponents);
traverse_bvh_run_function_recursive(bvhRoot, aabb, f);
}
void DrawingProgramCache::traverse_bvh_run_function_recursive(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components)> f) {
if(bvhNode && SCollision::collide(aabb, bvhNode->bounds) && f(bvhNode, bvhNode->components)) {
for(auto& p : bvhNode->children)
traverse_bvh_run_function_recursive(p, aabb, f);
}
}
CanvasComponentContainer::ObjInfoSharedPtr DrawingProgramCache::get_front_object_colliding_with(const SCollision::ColliderCollection<float>& cC) {
auto cCWorld = drawP.world.drawData.cam.c.collider_to_world<SCollision::ColliderCollection<WorldScalar>, SCollision::ColliderCollection<float>>(cC);
CanvasComponentContainer::ObjInfoSharedPtr p;
traverse_bvh_run_function(cCWorld.bounds, [&](const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components) {
for(auto& c : components) {
if((!p || c->pos >= p->pos) && c->obj->collides_with_world_coords(drawP.world.drawData.cam.c, cCWorld))
p = c;
void DrawingProgramCache::invalidate_cache_at_aabb(const SCollision::AABB<WorldScalar>& aabb) {
for(auto& [node, nodeCache] : nodeCacheMap) {
if(nodeCache.attachedDrawingProgramCache == this && SCollision::collide(aabb, node->bounds)) {
if(nodeCache.invalidBounds.has_value()) {
auto& iBounds = nodeCache.invalidBounds.value();
iBounds.include_aabb_in_bounds(aabb);
}
else
nodeCache.invalidBounds = aabb;
}
return true;
}
}
bool DrawingProgramCache::should_rebuild() const {
return (unsortedComponents.size() >= MINIMUM_COMPONENTS_TO_START_REBUILD);
}
bool DrawingProgramCache::unsorted_components_exist() const {
return !unsortedComponents.empty();
}
void DrawingProgramCache::build(const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& objsToExclude) {
static int i = 0;
std::cout << "rebuilding... " << i++ << std::endl;
internal_build(drawP.layerMan.get_flattened_component_list(), objsToExclude);
}
void DrawingProgramCache::internal_build(std::vector<CanvasComponentContainer::ObjInfoSharedPtr> componentsToBuild, const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& objsToNotInclude) {
std::erase_if(componentsToBuild, [&objsToNotInclude](auto& c) {
return objsToNotInclude.contains(c);
});
bvhRoot = std::make_shared<BVHNode>();
unsortedComponents.clear();
clear_own_cached_surfaces();
build_bvh_node(bvhRoot, componentsToBuild);
}
void DrawingProgramCache::clear_own_cached_surfaces() {
std::erase_if(nodeCacheMap, [&](auto& nodeCachePair) {
return nodeCachePair.second.attachedDrawingProgramCache == this;
});
return p;
}
void DrawingProgramCache::preupdate_component(const CanvasComponentContainer::ObjInfoSharedPtr& c) {
auto parentBvhNode = c->obj->parentBvhNode.lock();
if(parentBvhNode) {
// Can be called even if object isn't in cache yet. In that case, it'll just invalidate the cache at the object's AABB
auto it = objToBVHNode.find(c);
if(it != objToBVHNode.end()) {
unsortedComponents.emplace_back(c);
std::erase(parentBvhNode->components, c);
c->obj->parentBvhNode.reset();
std::erase(it->second->components, c);
}
invalidate_cache_at_aabb_before_pos(c->obj->get_world_bounds(), c->pos);
invalidate_cache_at_aabb(c->obj->get_world_bounds());
}
void DrawingProgramCache::build_bvh_node(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components) {
void DrawingProgramCache::build_bvh_node(const std::shared_ptr<BVHNode>& bvhNode, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components) {
if(components.empty())
return;
@ -176,15 +91,12 @@ void DrawingProgramCache::build_bvh_node(const std::shared_ptr<DrawingProgramCac
for(auto& c : components)
bvhNode->bounds.include_aabb_in_bounds(c->obj->get_world_bounds());
//std::cout << "component count: " << components.size() << std::endl;
//std::cout << "bvhNode->bounds " << vec_pretty(bvhNode->bounds.min) << " " << vec_pretty(bvhNode->bounds.max) << std::endl;
build_bvh_node_coords_and_resolution(*bvhNode);
if(components.size() < MAXIMUM_COMPONENTS_IN_SINGLE_NODE) {
bvhNode->components = components;
for(auto& c : bvhNode->components)
c->obj->parentBvhNode = bvhNode;
objToBVHNode.emplace(c, bvhNode);
return;
}
@ -204,31 +116,24 @@ void DrawingProgramCache::build_bvh_node(const std::shared_ptr<DrawingProgramCac
parts[3].emplace_back(c);
else {
bvhNode->components.emplace_back(c);
c->obj->parentBvhNode = bvhNode;
objToBVHNode.emplace(c, bvhNode);
}
}
for(auto& p : parts) {
if(!p.empty())
build_bvh_node(bvhNode->children.emplace_back(std::make_shared<DrawingProgramCacheBVHNode>()), p);
build_bvh_node(bvhNode->children.emplace_back(std::make_shared<BVHNode>()), p);
}
}
void DrawingProgramCache::build_bvh_node_coords_and_resolution(DrawingProgramCacheBVHNode& node) {
void DrawingProgramCache::build_bvh_node_coords_and_resolution(BVHNode& node) {
// These are part of the draw cache data, but we're putting it out here, because:
// - We dont have to recalculate it every time we refresh the cache
// - Some of the data here is needed to determine whether to generate the cache in the first place
// NOTE: We're assuming that devices using OpenGLES are mobile devices have less VRAM, so we decrease the size of the draw cache
// This isn't necessarily true, but it can improve performance on those devices without adding more settings
#if defined(USE_BACKEND_OPENGLES_3_0)
const int MAX_DIMENSION_RESOLUTION = 1024;
#else
const int MAX_DIMENSION_RESOLUTION = 2048;
#endif
constexpr int MAX_DIMENSION_RESOLUTION = 2048;
WorldVec cacheBoundDim = node.bounds.dim();
//std::cout << "cache bound: " << vec_pretty(cacheBoundDim) << std::endl;
if(cacheBoundDim.x() > cacheBoundDim.y()) {
node.resolution.x() = MAX_DIMENSION_RESOLUTION;
node.resolution.y() = MAX_DIMENSION_RESOLUTION * static_cast<double>(cacheBoundDim.y() / cacheBoundDim.x());
@ -246,43 +151,12 @@ void DrawingProgramCache::build_bvh_node_coords_and_resolution(DrawingProgramCac
node.coords.inverseScale = cacheBoundDim.y().divide_double(node.resolution.y());
}
void DrawingProgramCache::invalidate_cache_before_pos(uint32_t placementToInvalidateAt) {
// We could use worldAABB of component to invalidate specific areas. However, that comes with some issues:
// - The AABB couldve been changed from the last frame, so the current worldAABB doesnt actually represent what we should update
// - The worldAABB might not be calculated yet, or mightve been invalidated (worldAABB = nullopt)
// For now, we will ignore worldAABB and just clear cache by placement in list
std::erase_if(nodesWithCachedSurfaces, [&](auto& n) {
if(n->drawCache.value().attachedCache == this && placementToInvalidateAt <= n->drawCache.value().lastDrawnComponentPlacement) {
n->drawCache = std::nullopt;
return true;
}
return false;
});
}
void DrawingProgramCache::invalidate_cache_at_aabb_before_pos(const SCollision::AABB<WorldScalar>& aabb, uint32_t placementToInvalidateAt) {
std::for_each(nodesWithCachedSurfaces.begin(), nodesWithCachedSurfaces.end(), [&](auto& n) {
if(n->drawCache.value().attachedCache == this && SCollision::collide(aabb, n->bounds)) {
auto& dCache = n->drawCache.value();
if(dCache.invalidBounds.has_value()) {
auto& iBounds = dCache.invalidBounds.value();
iBounds.include_aabb_in_bounds(aabb);
}
else
dCache.invalidBounds = aabb;
if(placementToInvalidateAt <= n->drawCache.value().lastDrawnComponentPlacement)
dCache.isInvalid = true;
}
});
}
void DrawingProgramCache::refresh_all_draw_cache(const DrawData& drawData) {
std::deque<std::shared_ptr<DrawingProgramCacheBVHNode>> nodeFlatList; // We want to render children before parents, so that parents can make use of the cached children
traverse_bvh_run_function(drawData.cam.viewingAreaGenerousCollider, [&](std::shared_ptr<DrawingProgramCacheBVHNode> node, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& comps) {
std::deque<std::shared_ptr<BVHNode>> nodeFlatList; // We want to render children before parents, so that parents can make use of the cached children
traverse_bvh_run_function(drawData.cam.viewingAreaGenerousCollider, [&](std::shared_ptr<BVHNode> node, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& comps) {
if(node && node->coords.inverseScale <= drawData.cam.c.inverseScale) {
if(!node->drawCache || node->drawCache.value().isInvalid)
auto it = nodeCacheMap.find(node);
if((it == nodeCacheMap.end()) || it->second.invalidBounds.has_value())
nodeFlatList.emplace_front(node);
return false;
}
@ -292,14 +166,51 @@ void DrawingProgramCache::refresh_all_draw_cache(const DrawData& drawData) {
refresh_draw_cache(nodeFlatList.front(), drawData);
}
void DrawingProgramCache::refresh_draw_cache(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const DrawData& drawData) {
void DrawingProgramCache::traverse_bvh_run_function(const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<BVHNode>& node, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components)> f) {
f(nullptr, unsortedComponents);
traverse_bvh_run_function_recursive(bvhRoot, aabb, f);
}
void DrawingProgramCache::traverse_bvh_erase_function(const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<BVHNode>& node, std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components)> f) {
f(nullptr, unsortedComponents);
if(traverse_bvh_erase_function_recursive(bvhRoot, aabb, f))
bvhRoot = nullptr;
}
void DrawingProgramCache::traverse_bvh_run_function_recursive(const std::shared_ptr<BVHNode>& bvhNode, const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<BVHNode>& node, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components)> f) {
if(bvhNode && SCollision::collide(aabb, bvhNode->bounds) && f(bvhNode, bvhNode->components)) {
for(auto& p : bvhNode->children)
traverse_bvh_run_function_recursive(p, aabb, f);
}
}
bool DrawingProgramCache::traverse_bvh_erase_function_recursive(const std::shared_ptr<BVHNode>& bvhNode, const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<BVHNode>& node, std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components)> f) {
if(bvhNode && SCollision::collide(aabb, bvhNode->bounds)) {
if(f(bvhNode, bvhNode->components))
return true;
std::erase_if(bvhNode->children, [&](auto& p) {
return traverse_bvh_erase_function_recursive(p, aabb, f);
});
}
return false;
}
void DrawingProgramCache::move_components_from_bvh_node_to_set(std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& s, const std::shared_ptr<BVHNode>& bvhNode) {
for(auto& c : bvhNode->components)
s.emplace(c);
for(auto& p : bvhNode->children)
move_components_from_bvh_node_to_set(s, p);
}
void DrawingProgramCache::refresh_draw_cache(const std::shared_ptr<BVHNode>& bvhNode, const DrawData& drawData) {
if(bvhNode->children.empty() && bvhNode->components.empty())
return;
DrawingProgramCacheBVHNode::DrawCacheData drawCache;
if(bvhNode->drawCache) {
drawCache = bvhNode->drawCache.value();
bvhNode->drawCache = std::nullopt; // Ensure drawCache is nullopt, so that the draw_components_to_canvas function doesnt use it while drawing
NodeCache nodeCache;
auto bvhNodeCacheIt = nodeCacheMap.find(bvhNode);
if(bvhNodeCacheIt != nodeCacheMap.end()) {
nodeCache = bvhNodeCacheIt->second;
nodeCacheMap.erase(bvhNode); // Ensure nodeCache is erased, so that the draw_components_to_canvas function doesnt use it while drawing
}
else {
#ifdef USE_BACKEND_OPENGLES_3_0
@ -308,24 +219,24 @@ void DrawingProgramCache::refresh_draw_cache(const std::shared_ptr<DrawingProgra
SkImageInfo imgInfo = SkImageInfo::MakeN32Premul(bvhNode->resolution.x(), bvhNode->resolution.y());
#endif
#ifdef USE_SKIA_BACKEND_GRAPHITE
drawCache.surface = SkSurfaces::RenderTarget(drawP.world.main.window.recorder(), imgInfo, skgpu::Mipmapped::kNo, drawP.world.main.window.defaultMSAASurfaceProps);
nodeCache.surface = SkSurfaces::RenderTarget(drawP.world.main.window.recorder(), imgInfo, skgpu::Mipmapped::kNo, drawP.world.main.window.defaultMSAASurfaceProps);
#elif USE_SKIA_BACKEND_GANESH
drawCache.surface = SkSurfaces::RenderTarget(drawP.world.main.window.ctx.get(), skgpu::Budgeted::kNo, imgInfo, drawP.world.main.window.defaultMSAASampleCount, &drawP.world.main.window.defaultMSAASurfaceProps);
nodeCache.surface = SkSurfaces::RenderTarget(drawP.world.main.window.ctx.get(), skgpu::Budgeted::kNo, imgInfo, drawP.world.main.window.defaultMSAASampleCount, &drawP.world.main.window.defaultMSAASurfaceProps);
#endif
if(!drawCache.surface)
if(!nodeCache.surface)
throw std::runtime_error("[DrawingProgramCache::refresh_draw_cache] Could not make cache surface");
}
SkCanvas* cacheCanvas = drawCache.surface->getCanvas();
SkCanvas* cacheCanvas = nodeCache.surface->getCanvas();
DrawData cacheDrawData = drawData;
cacheDrawData.cam.c = bvhNode->coords;
cacheDrawData.cam.set_viewing_area(bvhNode->resolution.cast<float>());
cacheDrawData.refresh_draw_optimizing_values();
if(drawCache.isInvalid) {
auto iBounds = drawCache.invalidBounds.value();
if(nodeCache.invalidBounds.has_value()) {
auto iBounds = nodeCache.invalidBounds.value();
iBounds = bvhNode->bounds.get_intersection_between_aabbs(iBounds);
WorldVec bDim = bvhNode->bounds.dim();
@ -349,108 +260,103 @@ void DrawingProgramCache::refresh_draw_cache(const std::shared_ptr<DrawingProgra
cacheCanvas->save();
cacheCanvas->clipIRect(clipRect);
cacheCanvas->clear(SkColor4f{0, 0, 0, 0});
draw_components_to_canvas(cacheCanvas, cacheDrawData, {
.lastDrawnComponentPlacement = &drawCache.lastDrawnComponentPlacement,
.drawBounds = clipRectBoundAABBWorld,
});
draw_components_to_canvas(cacheCanvas, cacheDrawData, clipRectBoundAABBWorld);
cacheCanvas->restore();
drawCache.invalidBounds = std::nullopt;
drawCache.isInvalid = false;
nodeCache.invalidBounds = std::nullopt;
}
else {
cacheCanvas->clear(SkColor4f{0, 0, 0, 0});
draw_components_to_canvas(cacheCanvas, cacheDrawData, {
.lastDrawnComponentPlacement = &drawCache.lastDrawnComponentPlacement,
draw_components_to_canvas(cacheCanvas, cacheDrawData, std::nullopt);
}
nodeCache.lastRenderTime = std::chrono::steady_clock::now();
nodeCache.attachedDrawingProgramCache = this;
while(nodeCacheMap.size() >= MAXIMUM_DRAW_CACHE_SURFACES) {
auto leastUsedNodeIt = nodeCacheMap.begin();
for(auto it = nodeCacheMap.begin(); it != nodeCacheMap.end(); ++it) {
if(it->second.lastRenderTime < leastUsedNodeIt->second.lastRenderTime)
leastUsedNodeIt = it;
}
nodeCacheMap.erase(leastUsedNodeIt);
}
nodeCacheMap.emplace(bvhNode, nodeCache); // Set the nodeCache after rendering is done, so that the draw function doesnt assume we have this node cached
}
void DrawingProgramCache::draw_components_to_canvas(SkCanvas* canvas, const DrawData& drawData, const std::optional<SCollision::AABB<WorldScalar>>& drawBounds) {
if(drawP.layerMan.layer_tree_root_exists()) {
std::vector<std::shared_ptr<BVHNode>> cachedNodesToDraw;
std::vector<std::shared_ptr<BVHNode>> uncachedNodes;
traverse_bvh_run_function(drawData.cam.viewingAreaGenerousCollider, [&](const std::shared_ptr<BVHNode>& node, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& comps) {
if(node) {
auto it = nodeCacheMap.find(node);
if(it != nodeCacheMap.end()) {
auto& nodeCache = it->second;
if(!nodeCache.invalidBounds.has_value() && node->coords.inverseScale <= drawData.cam.c.inverseScale) {
cachedNodesToDraw.emplace_back(node);
return false;
}
}
}
if(node) // Not the "unsorted components" node (which is nullptr)
uncachedNodes.emplace_back(node);
return true;
});
recursive_draw_layer_item_to_canvas(drawP.layerMan.get_layer_root(), canvas, drawData, drawBounds, uncachedNodes);
for(auto& nodeCacheToDraw : cachedNodesToDraw)
draw_cache_image_to_canvas(canvas, drawData, nodeCacheToDraw);
}
}
drawCache.lastRenderTime = std::chrono::steady_clock::now();
drawCache.attachedCache = this;
bvhNode->drawCache = drawCache; // Set the drawCache after rendering is done, so that the draw function doesnt assume we have this node cached
while(nodesWithCachedSurfaces.size() >= MAXIMUM_DRAW_CACHE_SURFACES) {
std::shared_ptr<DrawingProgramCacheBVHNode> leastUsedNode = *nodesWithCachedSurfaces.begin();
for(const std::shared_ptr<DrawingProgramCacheBVHNode>& n : nodesWithCachedSurfaces) {
if(n->drawCache.value().lastRenderTime < leastUsedNode->drawCache.value().lastRenderTime)
leastUsedNode = n;
void DrawingProgramCache::recursive_draw_layer_item_to_canvas(const DrawingProgramLayerListItem& layerListItem, SkCanvas* canvas, const DrawData& drawData, const std::optional<SCollision::AABB<WorldScalar>>& drawBounds, const std::vector<std::shared_ptr<BVHNode>>& nodesToDraw) {
if(layerListItem.get_visible()) {
SkPaint layerPaint;
layerPaint.setAlphaf(layerListItem.get_alpha());
layerPaint.setBlendMode(serialized_blend_mode_to_sk_blend_mode(layerListItem.get_blend_mode()));
canvas->saveLayer(nullptr, &layerPaint);
if(layerListItem.is_folder()) {
for(auto& p : layerListItem.get_folder().folderList->get_data() | std::views::reverse)
recursive_draw_layer_item_to_canvas(*p->obj, canvas, drawData, drawBounds, nodesToDraw);
}
leastUsedNode->drawCache = std::nullopt;
nodesWithCachedSurfaces.erase(leastUsedNode);
else {
std::vector<CanvasComponentContainer::ObjInfoSharedPtr> compsToDraw;
for(auto& c : unsortedComponents) {
if(c->obj->parentLayer == &layerListItem && (!drawBounds.has_value() || SCollision::collide(drawBounds.value(), c->obj->get_world_bounds())))
compsToDraw.emplace_back(c);
}
for(auto& node : nodesToDraw) {
for(auto& c : node->components) {
if(c->obj->parentLayer == &layerListItem && (!drawBounds.has_value() || SCollision::collide(drawBounds.value(), c->obj->get_world_bounds())))
compsToDraw.emplace_back(c);
}
}
std::sort(compsToDraw.begin(), compsToDraw.end(), [](auto& a, auto& b) {
return a->pos < b->pos;
});
for(auto& c : compsToDraw)
c->obj->draw(canvas, drawData);
}
canvas->restore();
}
nodesWithCachedSurfaces.emplace(bvhNode);
}
void DrawingProgramCache::draw_components_to_canvas(SkCanvas* canvas, const DrawData& drawData, DrawComponentsToCanvasOptionalData optData) {
uint32_t lastComponentDrawn = 0;
void DrawingProgramCache::draw_cache_image_to_canvas(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr<BVHNode>& bvhNode) {
auto it = nodeCacheMap.find(bvhNode);
if(it != nodeCacheMap.end()) {
auto& nodeCache = it->second;
canvas->save();
bvhNode->coords.transform_sk_canvas(canvas, drawData);
nodeCache.lastRenderTime = std::chrono::steady_clock::now();
std::vector<std::shared_ptr<DrawingProgramCacheBVHNode>> cachedNodesToDraw;
struct UncachedObjectToDraw {
CanvasComponentContainer::ObjInfoSharedPtr comp;
bool isAnUnsortedObject;
};
std::vector<UncachedObjectToDraw> uncachedCompsToDraw;
SkPaint srcPaint;
srcPaint.setBlendMode(SkBlendMode::kSrc);
traverse_bvh_run_function(drawData.cam.viewingAreaGenerousCollider, [&](const std::shared_ptr<DrawingProgramCacheBVHNode>& node, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& comps) {
if(node && node->drawCache && !node->drawCache.value().isInvalid && node->coords.inverseScale <= drawData.cam.c.inverseScale) {
cachedNodesToDraw.emplace_back(node);
lastComponentDrawn = std::max(lastComponentDrawn, node->drawCache->lastDrawnComponentPlacement);
return false;
}
canvas->drawImage(nodeCache.surface->makeTemporaryImage(), 0, 0, {SkFilterMode::kLinear, SkMipmapMode::kLinear}, &srcPaint);
for(auto& c : comps) {
if(!optData.drawBounds.has_value() || SCollision::collide(optData.drawBounds.value(), c->obj->get_world_bounds()))
uncachedCompsToDraw.emplace_back(c, !node);
lastComponentDrawn = std::max(lastComponentDrawn, c->pos);
}
return true;
});
std::sort(uncachedCompsToDraw.begin(), uncachedCompsToDraw.end(), [](auto& a, auto& b) {
return a.comp->pos < b.comp->pos;
});
std::sort(cachedNodesToDraw.begin(), cachedNodesToDraw.end(), [](auto& a, auto& b) {
return a->drawCache.value().lastDrawnComponentPlacement < b->drawCache.value().lastDrawnComponentPlacement;
});
size_t nextCacheToRender = 0;
std::chrono::microseconds unsortedCompDrawTime(0);
for(auto& uncachedComp : uncachedCompsToDraw) {
for(;;) {
if(nextCacheToRender >= cachedNodesToDraw.size() || uncachedComp.comp->pos <= cachedNodesToDraw[nextCacheToRender]->drawCache.value().lastDrawnComponentPlacement)
break;
draw_cache_image_to_canvas(canvas, drawData, cachedNodesToDraw[nextCacheToRender]);
nextCacheToRender++;
}
std::chrono::time_point drawStartTime = std::chrono::steady_clock::now();
uncachedComp.comp->obj->draw(canvas, drawData);
if(uncachedComp.isAnUnsortedObject)
unsortedCompDrawTime += std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - drawStartTime);
canvas->restore();
}
for(;nextCacheToRender < cachedNodesToDraw.size(); nextCacheToRender++)
draw_cache_image_to_canvas(canvas, drawData, cachedNodesToDraw[nextCacheToRender]);
if(optData.lastDrawnComponentPlacement)
*optData.lastDrawnComponentPlacement = lastComponentDrawn;
}
void DrawingProgramCache::draw_cache_image_to_canvas(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode) {
auto& drawCache = bvhNode->drawCache.value();
canvas->save();
bvhNode->coords.transform_sk_canvas(canvas, drawData);
drawCache.lastRenderTime = std::chrono::steady_clock::now();
SkPaint srcPaint;
srcPaint.setBlendMode(SkBlendMode::kSrc);
canvas->drawImage(drawCache.surface->makeTemporaryImage(), 0, 0, {SkFilterMode::kLinear, SkMipmapMode::kLinear}, &srcPaint);
canvas->restore();
}
DrawingProgramCache::~DrawingProgramCache() {
clear_own_cached_surfaces();
}

View file

@ -1,91 +1,55 @@
#pragma once
#include <chrono>
#include <vector>
#include <include/core/SkSurface.h>
#include <unordered_set>
#include "../DrawData.hpp"
#include "../CanvasComponents/CanvasComponentContainer.hpp"
class DrawingProgram;
class DrawingProgramCache;
class DrawingProgramCacheBVHNode {
public:
SCollision::AABB<WorldScalar> bounds;
std::vector<CanvasComponentContainer::ObjInfoSharedPtr> components;
std::vector<std::shared_ptr<DrawingProgramCacheBVHNode>> children;
CoordSpaceHelper coords;
Vector2i resolution;
struct DrawCacheData {
uint32_t lastDrawnComponentPlacement;
sk_sp<SkSurface> surface;
std::chrono::steady_clock::time_point lastRenderTime;
DrawingProgramCache* attachedCache;
std::optional<SCollision::AABB<WorldScalar>> invalidBounds;
bool isInvalid = false;
};
std::optional<DrawCacheData> drawCache;
};
#include <unordered_map>
class DrawingProgramCache {
public:
DrawingProgramCache(DrawingProgram& initDrawP);
~DrawingProgramCache();
static constexpr size_t MINIMUM_COMPONENTS_TO_START_REBUILD = 1000;
static constexpr size_t MAXIMUM_COMPONENTS_IN_SINGLE_NODE = 50;
static constexpr size_t MAXIMUM_DRAW_CACHE_SURFACES = 40;
bool check_if_rebuild_should_occur();
void test_rebuild(const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& comps, bool force = false);
void test_rebuild_dont_include_set(const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& comps, const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& objsToNotInclude, bool force = false);
void test_rebuild_dont_include_set_dont_include_nodes(const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& comps, const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& objsToNotInclude, const std::unordered_set<std::shared_ptr<DrawingProgramCacheBVHNode>>& nodesToNotInclude, bool force = false);
static void move_components_from_bvh_nodes_to_set(std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& s, const std::unordered_set<std::shared_ptr<DrawingProgramCacheBVHNode>>& bvhNodes);
static void move_components_from_bvh_node_to_set(std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& s, const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode);
void erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& c);
void add_component(const CanvasComponentContainer::ObjInfoSharedPtr& c);
void refresh_all_draw_cache(const DrawData& drawData);
void traverse_bvh_run_function(const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components)> f);
void traverse_bvh_erase_function(const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node, std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components)> f);
// Run before actually updating the component
void preupdate_component(const CanvasComponentContainer::ObjInfoSharedPtr& c);
void invalidate_cache_before_pos(uint32_t placementToInvalidateAt);
void invalidate_cache_at_aabb_before_pos(const SCollision::AABB<WorldScalar>& aabb, uint32_t placementToInvalidateAt);
void clear();
struct DrawComponentsToCanvasOptionalData {
uint32_t* lastDrawnComponentPlacement = nullptr;
std::optional<SCollision::AABB<WorldScalar>> drawBounds;
struct BVHNode {
SCollision::AABB<WorldScalar> bounds;
std::vector<CanvasComponentContainer::ObjInfoSharedPtr> components;
std::vector<std::shared_ptr<BVHNode>> children;
CoordSpaceHelper coords;
Vector2i resolution;
};
void draw_components_to_canvas(SkCanvas* canvas, const DrawData& drawData, DrawComponentsToCanvasOptionalData optData);
const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& get_unsorted_component_list() const;
const std::chrono::steady_clock::time_point& get_last_bvh_build_time() const;
DrawingProgramCache(DrawingProgram& initDrawP);
void add_component(const CanvasComponentContainer::ObjInfoSharedPtr& c);
void erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& c);
void clear_own_cached_surfaces();
CanvasComponentContainer::ObjInfoSharedPtr get_front_object_colliding_with(const SCollision::ColliderCollection<float>& cC);
void preupdate_component(const CanvasComponentContainer::ObjInfoSharedPtr& c);
void build(const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& objsToExclude);
void traverse_bvh_run_function(const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<BVHNode>& node, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components)> f);
void traverse_bvh_erase_function(const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<BVHNode>& node, std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components)> f);
bool should_rebuild() const;
void draw_components_to_canvas(SkCanvas* canvas, const DrawData& drawData, const std::optional<SCollision::AABB<WorldScalar>>& drawBounds);
bool unsorted_components_exist() const;
void invalidate_cache_at_aabb(const SCollision::AABB<WorldScalar>& aabb);
void move_components_from_bvh_node_to_set(std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& s, const std::shared_ptr<BVHNode>& bvhNode);
void refresh_all_draw_cache(const DrawData& drawData);
private:
static std::unordered_set<std::shared_ptr<DrawingProgramCacheBVHNode>> nodesWithCachedSurfaces;
struct NodeCache {
sk_sp<SkSurface> surface;
std::chrono::steady_clock::time_point lastRenderTime;
DrawingProgramCache* attachedDrawingProgramCache;
std::optional<SCollision::AABB<WorldScalar>> invalidBounds;
};
static std::unordered_map<std::shared_ptr<BVHNode>, NodeCache> nodeCacheMap;
void draw_cache_image_to_canvas(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode);
void internal_build(std::vector<CanvasComponentContainer::ObjInfoSharedPtr> componentsToBuild, const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& objsToNotInclude);
void build_bvh_node(const std::shared_ptr<BVHNode>& bvhNode, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components);
void build_bvh_node_coords_and_resolution(BVHNode& node);
void refresh_draw_cache(const std::shared_ptr<BVHNode>& bvhNode, const DrawData& drawData);
void traverse_bvh_run_function_recursive(const std::shared_ptr<BVHNode>& bvhNode, const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<BVHNode>& node, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components)> f);
bool traverse_bvh_erase_function_recursive(const std::shared_ptr<BVHNode>& bvhNode, const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<BVHNode>& node, std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components)> f);
void draw_cache_image_to_canvas(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr<BVHNode>& bvhNode);
void recursive_draw_layer_item_to_canvas(const DrawingProgramLayerListItem& layerListItem, SkCanvas* canvas, const DrawData& drawData, const std::optional<SCollision::AABB<WorldScalar>>& drawBounds, const std::vector<std::shared_ptr<BVHNode>>& nodesToDraw);
void force_rebuild_dont_include_objs(std::vector<CanvasComponentContainer::ObjInfoSharedPtr> componentsToBuild, const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& objsToNotInclude);
void force_rebuild(const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& componentsToBuild);
bool traverse_bvh_erase_function_recursive(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node, std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components)> f);
void build_bvh_node_coords_and_resolution(DrawingProgramCacheBVHNode& node);
void refresh_draw_cache(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const DrawData& drawData);
void build(std::vector<CanvasComponentContainer::ObjInfoSharedPtr> components);
void traverse_bvh_run_function_recursive(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components)> f);
void build_bvh_node(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components);
std::chrono::steady_clock::time_point lastBvhBuildTime;
std::shared_ptr<DrawingProgramCacheBVHNode> bvhRoot;
std::unordered_map<CanvasComponentContainer::ObjInfoSharedPtr, std::shared_ptr<BVHNode>> objToBVHNode;
std::shared_ptr<BVHNode> bvhRoot;
std::vector<CanvasComponentContainer::ObjInfoSharedPtr> unsortedComponents;
DrawingProgram& drawP;
};

View file

@ -1,141 +0,0 @@
#include "DrawingProgramCacheNew.hpp"
#include "DrawingProgram.hpp"
std::unordered_set<std::shared_ptr<DrawingProgramCacheNew::BVHNode>> DrawingProgramCacheNew::nodesWithCachedSurfaces;
DrawingProgramCacheNew::DrawingProgramCacheNew(DrawingProgram& initDrawP):
drawP(initDrawP)
{}
void DrawingProgramCacheNew::add_component(const CanvasComponentContainer::ObjInfoSharedPtr& c) {
unsortedComponents.emplace_back(c);
invalidate_cache_at_aabb(c->obj->get_world_bounds());
}
void DrawingProgramCacheNew::erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& c) {
auto it = objToBVHNode.find(c);
if(it != objToBVHNode.end())
objToBVHNode.erase(it);
else
std::erase(unsortedComponents, c);
invalidate_cache_at_aabb(c->obj->get_world_bounds());
}
void DrawingProgramCacheNew::invalidate_cache_at_aabb(const SCollision::AABB<WorldScalar>& aabb) {
std::for_each(nodesWithCachedSurfaces.begin(), nodesWithCachedSurfaces.end(), [&](auto& n) {
if(n->drawCache.value().attachedCache == this && SCollision::collide(aabb, n->bounds)) {
auto& dCache = n->drawCache.value();
if(dCache.invalidBounds.has_value()) {
auto& iBounds = dCache.invalidBounds.value();
iBounds.include_aabb_in_bounds(aabb);
}
else
dCache.invalidBounds = aabb;
}
});
}
bool DrawingProgramCacheNew::should_rebuild() const {
return (unsortedComponents.size() >= MINIMUM_COMPONENTS_TO_START_REBUILD);
}
void DrawingProgramCacheNew::build(const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& objsToExclude) {
internal_build(drawP.layerMan.get_flattened_component_list(), objsToExclude);
}
void DrawingProgramCacheNew::internal_build(std::vector<CanvasComponentContainer::ObjInfoSharedPtr> componentsToBuild, const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& objsToNotInclude) {
std::erase_if(componentsToBuild, [&objsToNotInclude](auto& c) {
return objsToNotInclude.contains(c);
});
bvhRoot = std::make_shared<BVHNode>();
unsortedComponents.clear();
clear_own_cached_surfaces();
build_bvh_node(bvhRoot, componentsToBuild);
}
void DrawingProgramCacheNew::clear_own_cached_surfaces() {
std::erase_if(nodesWithCachedSurfaces, [&](auto& n) {
if(n->drawCache.value().attachedCache == this) {
n->drawCache = std::nullopt;
return true;
}
return false;
});
}
void DrawingProgramCacheNew::preupdate_component(const CanvasComponentContainer::ObjInfoSharedPtr& c) {
auto it = objToBVHNode.find(c);
if(it != objToBVHNode.end()) {
unsortedComponents.emplace_back(c);
std::erase(it->second->components, c);
}
invalidate_cache_at_aabb(c->obj->get_world_bounds());
}
void DrawingProgramCacheNew::build_bvh_node(const std::shared_ptr<BVHNode>& bvhNode, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components) {
if(components.empty())
return;
bvhNode->bounds = components.front()->obj->get_world_bounds();
for(auto& c : components)
bvhNode->bounds.include_aabb_in_bounds(c->obj->get_world_bounds());
build_bvh_node_coords_and_resolution(*bvhNode);
if(components.size() < MAXIMUM_COMPONENTS_IN_SINGLE_NODE) {
bvhNode->components = components;
for(auto& c : bvhNode->components)
objToBVHNode.emplace(c, bvhNode);
return;
}
WorldVec boundsCenter = bvhNode->bounds.center();
std::array<std::vector<CanvasComponentContainer::ObjInfoSharedPtr>, 4> parts;
for(auto& c : components) {
const auto& cAABB = c->obj->get_world_bounds();
if(cAABB.min.x() < boundsCenter.x() && cAABB.max.x() < boundsCenter.x() && cAABB.min.y() < boundsCenter.y() && cAABB.max.y() < boundsCenter.y())
parts[0].emplace_back(c);
else if(cAABB.min.x() > boundsCenter.x() && cAABB.max.x() > boundsCenter.x() && cAABB.min.y() < boundsCenter.y() && cAABB.max.y() < boundsCenter.y())
parts[1].emplace_back(c);
else if(cAABB.min.x() < boundsCenter.x() && cAABB.max.x() < boundsCenter.x() && cAABB.min.y() > boundsCenter.y() && cAABB.max.y() > boundsCenter.y())
parts[2].emplace_back(c);
else if(cAABB.min.x() > boundsCenter.x() && cAABB.max.x() > boundsCenter.x() && cAABB.min.y() > boundsCenter.y() && cAABB.max.y() > boundsCenter.y())
parts[3].emplace_back(c);
else {
bvhNode->components.emplace_back(c);
objToBVHNode.emplace(c, bvhNode);
}
}
for(auto& p : parts) {
if(!p.empty())
build_bvh_node(bvhNode->children.emplace_back(std::make_shared<BVHNode>()), p);
}
}
void DrawingProgramCacheNew::build_bvh_node_coords_and_resolution(BVHNode& node) {
// These are part of the draw cache data, but we're putting it out here, because:
// - We dont have to recalculate it every time we refresh the cache
// - Some of the data here is needed to determine whether to generate the cache in the first place
constexpr int MAX_DIMENSION_RESOLUTION = 2048;
WorldVec cacheBoundDim = node.bounds.dim();
if(cacheBoundDim.x() > cacheBoundDim.y()) {
node.resolution.x() = MAX_DIMENSION_RESOLUTION;
node.resolution.y() = MAX_DIMENSION_RESOLUTION * static_cast<double>(cacheBoundDim.y() / cacheBoundDim.x());
}
else {
node.resolution.y() = MAX_DIMENSION_RESOLUTION;
node.resolution.x() = MAX_DIMENSION_RESOLUTION * static_cast<double>(cacheBoundDim.x() / cacheBoundDim.y());
}
node.coords.rotation = 0.0;
node.coords.pos = node.bounds.min;
// Not necessary, but we choose the number that had less operations done on it for more accuracy
if(node.resolution.x() > node.resolution.y())
node.coords.inverseScale = cacheBoundDim.x().divide_double(node.resolution.x());
else
node.coords.inverseScale = cacheBoundDim.y().divide_double(node.resolution.y());
}

View file

@ -1,46 +0,0 @@
#pragma once
#include "../CanvasComponents/CanvasComponentContainer.hpp"
#include <unordered_map>
class DrawingProgramCacheNew {
public:
static constexpr size_t MINIMUM_COMPONENTS_TO_START_REBUILD = 1000;
static constexpr size_t MAXIMUM_COMPONENTS_IN_SINGLE_NODE = 50;
static constexpr size_t MAXIMUM_DRAW_CACHE_SURFACES = 40;
DrawingProgramCacheNew(DrawingProgram& initDrawP);
void add_component(const CanvasComponentContainer::ObjInfoSharedPtr& c);
void erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& c);
void clear_own_cached_surfaces();
void preupdate_component(const CanvasComponentContainer::ObjInfoSharedPtr& c);
void build(const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& objsToExclude);
bool should_rebuild() const;
private:
struct BVHNode {
SCollision::AABB<WorldScalar> bounds;
std::vector<CanvasComponentContainer::ObjInfoSharedPtr> components;
std::vector<std::shared_ptr<BVHNode>> children;
CoordSpaceHelper coords;
Vector2i resolution;
struct DrawCacheData {
sk_sp<SkSurface> surface;
std::chrono::steady_clock::time_point lastRenderTime;
DrawingProgramCacheNew* attachedCache;
std::optional<SCollision::AABB<WorldScalar>> invalidBounds;
};
std::optional<DrawCacheData> drawCache;
};
static std::unordered_set<std::shared_ptr<BVHNode>> nodesWithCachedSurfaces;
void internal_build(std::vector<CanvasComponentContainer::ObjInfoSharedPtr> componentsToBuild, const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& objsToNotInclude);
void invalidate_cache_at_aabb(const SCollision::AABB<WorldScalar>& aabb);
void build_bvh_node(const std::shared_ptr<BVHNode>& bvhNode, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components);
void build_bvh_node_coords_and_resolution(BVHNode& node);
std::unordered_map<CanvasComponentContainer::ObjInfoSharedPtr, std::shared_ptr<BVHNode>> objToBVHNode;
std::shared_ptr<BVHNode> bvhRoot;
std::vector<CanvasComponentContainer::ObjInfoSharedPtr> unsortedComponents;
DrawingProgram& drawP;
};

View file

@ -1,500 +0,0 @@
#include "DrawingProgramSelection.hpp"
#include "DrawingProgram.hpp"
#include "../World.hpp"
#include "../MainProgram.hpp"
#include "Tools/DrawingProgramToolBase.hpp"
#include "Helpers/FixedPoint.hpp"
#include <Helpers/MathExtras.hpp>
#include <Helpers/SCollision.hpp>
#include <Helpers/Logger.hpp>
#include <Helpers/Parallel.hpp>
#ifdef USE_SKIA_BACKEND_GRAPHITE
#include <include/gpu/graphite/Surface.h>
#elif USE_SKIA_BACKEND_GANESH
#include <include/gpu/ganesh/GrDirectContext.h>
#include <include/gpu/ganesh/SkSurfaceGanesh.h>
#endif
#include <include/effects/SkImageFilters.h>
#include <include/core/SkPathBuilder.h>
#define ROTATION_POINT_RADIUS_MULTIPLIER 0.7f
#define ROTATION_POINTS_DISTANCE 20.0f
DrawingProgramSelection::DrawingProgramSelection(DrawingProgram& initDrawP):
cache(initDrawP),
drawP(initDrawP)
{}
const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& DrawingProgramSelection::get_selected_set() {
return selectedSet;
}
void DrawingProgramSelection::erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck) {
if(is_selected(objToCheck)) {
cache.erase_component(objToCheck);
selectedSet.erase(objToCheck);
}
}
void DrawingProgramSelection::fully_collided_erase_select_objects_func(std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& selectedComponents, const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode) {
if(bvhNode) {
for(auto& c : bvhNode->components)
selectedComponents.emplace(c);
for(auto& p : bvhNode->children)
fully_collided_erase_select_objects_func(selectedComponents, p);
}
}
std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>&, std::vector<CanvasComponentContainer::ObjInfoSharedPtr>&)> DrawingProgramSelection::erase_select_objects_in_bvh_func(std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& selectedComponents, const SCollision::ColliderCollection<float>& cC, const SCollision::ColliderCollection<WorldScalar>& cCWorld) {
auto toRet = [&](const auto& bvhNode, auto& comps) {
if(bvhNode && (bvhNode->coords.inverseScale << 9) < drawP.world.drawData.cam.c.inverseScale &&
SCollision::collide(cC, drawP.world.drawData.cam.c.to_space(bvhNode->bounds.min)) &&
SCollision::collide(cC, drawP.world.drawData.cam.c.to_space(bvhNode->bounds.max)) &&
SCollision::collide(cC, drawP.world.drawData.cam.c.to_space(bvhNode->bounds.top_right())) &&
SCollision::collide(cC, drawP.world.drawData.cam.c.to_space(bvhNode->bounds.bottom_left()))) {
fully_collided_erase_select_objects_func(selectedComponents, bvhNode);
drawP.compCache.invalidate_cache_at_aabb_before_pos(bvhNode->bounds, 0);
return true;
}
std::erase_if(comps, [&](auto& c) {
if(c->obj->collides_with(drawP.world.drawData.cam.c, cCWorld, cC)) {
selectedComponents.emplace(c);
drawP.compCache.invalidate_cache_at_aabb_before_pos(c->obj->get_world_bounds(), c->pos);
return true;
}
return false;
});
return false;
};
return toRet;
}
void DrawingProgramSelection::add_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection<float>& cC, bool frontObjectOnly) {
std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr> selectedComponents;
if(frontObjectOnly) {
CanvasComponentContainer::ObjInfoSharedPtr a = drawP.compCache.get_front_object_colliding_with(cC);
if(a) {
selectedComponents.emplace(a);
drawP.compCache.erase_component(a);
}
}
else {
auto cCWorld = drawP.world.drawData.cam.c.collider_to_world<SCollision::ColliderCollection<WorldScalar>, SCollision::ColliderCollection<float>>(cC);
drawP.compCache.traverse_bvh_erase_function(cCWorld.bounds, erase_select_objects_in_bvh_func(selectedComponents, cC, cCWorld));
}
add_to_selection(selectedComponents);
}
void DrawingProgramSelection::remove_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection<float>& cC, bool frontObjectOnly) {
auto cCWorld = drawP.world.drawData.cam.c.collider_to_world<SCollision::ColliderCollection<WorldScalar>, SCollision::ColliderCollection<float>>(cC);
std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr> selectedComponents;
if(frontObjectOnly) {
CanvasComponentContainer::ObjInfoSharedPtr a = cache.get_front_object_colliding_with(cC);
if(a) {
selectedComponents.emplace(a);
cache.erase_component(a);
}
}
else {
cache.traverse_bvh_erase_function(cCWorld.bounds, erase_select_objects_in_bvh_func(selectedComponents, cC, cCWorld));
cache.clear_own_cached_surfaces();
}
std::erase_if(selectedSet, [&](auto& c) {
return selectedComponents.contains(c);
});
for(auto& obj : selectedComponents)
drawP.compCache.add_component(obj);
if(!is_something_selected()) {
reset_selection_data();
reset_transform_data();
}
else {
calculate_aabb();
calculate_initial_rotate_center_location();
}
}
void DrawingProgramSelection::add_to_selection(const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& newSelection) {
selectedSet.insert(newSelection.begin(), newSelection.end());
for(auto& obj : newSelection)
cache.add_component(obj);
calculate_aabb();
calculate_initial_rotate_center_location();
}
void DrawingProgramSelection::set_to_selection(const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& newSelection) {
selectedSet = newSelection;
for(auto& obj : selectedSet)
cache.add_component(obj);
calculate_aabb();
calculate_initial_rotate_center_location();
}
bool DrawingProgramSelection::is_something_selected() {
return !selectedSet.empty();
}
void DrawingProgramSelection::calculate_aabb() {
if(is_something_selected()) {
initialSelectionAABB = (*selectedSet.begin())->obj->get_world_bounds();
for(auto& c : selectedSet)
initialSelectionAABB.include_aabb_in_bounds(c->obj->get_world_bounds());
}
}
void DrawingProgramSelection::deselect_all() {
if(is_something_selected()) {
bool cacheWillRebuildAnyway = selectedSet.size() >= DrawingProgramCache::MINIMUM_COMPONENTS_TO_START_REBUILD;
if(cacheWillRebuildAnyway)
drawP.compCache.test_rebuild(drawP.components->get_data(), true);
else {
for(auto& obj : selectedSet)
drawP.compCache.add_component(obj);
}
reset_selection_data();
reset_transform_data();
}
}
void DrawingProgramSelection::commit_transform_selection() {
auto selectedSetTemp = selectedSet;
std::vector<CanvasComponentContainer::ObjInfoSharedPtr> a(selectedSet.begin(), selectedSet.end());
std::vector<std::pair<CanvasComponentContainer::ObjInfoSharedPtr, CoordSpaceHelper>> transformsFrom;
for(auto& transformedObj : a)
transformsFrom.emplace_back(transformedObj, transformedObj->obj->coords);
bool isSingleThread = a.size() < DrawingProgramCache::MINIMUM_COMPONENTS_TO_START_REBUILD;
parallel_loop_container(a, [&](auto& obj) {
obj->obj->coords = selectionTransformCoords.other_coord_space_from_this_space(obj->obj->coords);
obj->obj->commit_transform_dont_invalidate_cache(drawP);
}, isSingleThread);
std::vector<std::pair<CanvasComponentContainer::ObjInfoSharedPtr, CoordSpaceHelper>> transformsTo;
std::vector<std::pair<NetworkingObjects::NetObjID, CoordSpaceHelper>> transformsToSend;
for(auto& transformedObj : a) {
transformsToSend.emplace_back(transformedObj->obj.get_net_id(), transformedObj->obj->coords);
transformsTo.emplace_back(transformedObj, transformedObj->obj->coords);
}
//DrawComponent::client_send_transform_many(drawP, transformsToSend);
reset_selection_data();
reset_transform_data();
set_to_selection(selectedSetTemp);
}
CanvasComponentContainer::ObjInfoSharedPtr DrawingProgramSelection::get_front_object_colliding_with(const SCollision::ColliderCollection<float>& cC) {
return cache.get_front_object_colliding_with(cC);
}
void DrawingProgramSelection::invalidate_cache_at_aabb_before_pos(const SCollision::AABB<WorldScalar>& aabb, uint64_t placementToInvalidateAt) {
cache.invalidate_cache_at_aabb_before_pos(aabb, placementToInvalidateAt);
}
void DrawingProgramSelection::clear_own_cached_surfaces() {
cache.clear_own_cached_surfaces();
}
void DrawingProgramSelection::preupdate_component(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck) {
cache.preupdate_component(objToCheck);
}
bool DrawingProgramSelection::is_selected(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck) {
return selectedSet.contains(objToCheck);
}
void DrawingProgramSelection::delete_all() {
if(is_something_selected()) {
//drawP.client_erase_set(selectedSet);
reset_selection_data();
reset_transform_data();
}
}
// NOTE: Only run this if no transformations happened
void DrawingProgramSelection::selection_to_clipboard() {
//auto& clipboard = drawP.world.main.clipboard;
//std::unordered_set<ServerClientID> resourceSet;
//for(auto& c : selectedSet)
// c->obj->get_used_resources(resourceSet);
//std::vector<CanvasComponentContainer::ObjInfoSharedPtr> compVecSort(selectedSet.begin(), selectedSet.end());
//std::sort(compVecSort.begin(), compVecSort.end(), [&](auto& a, auto& b) {
// return a->pos < b->pos;
//});
//clipboard.components.clear();
//for(auto& c : compVecSort)
// clipboard.components.emplace_back(c->obj->deep_copy(drawP));
//clipboard.pos = initialSelectionAABB.center();
//clipboard.inverseScale = drawP.world.drawData.cam.c.inverseScale;
//drawP.world.rMan.copy_resource_set_to_map(resourceSet, clipboard.resources);
}
void DrawingProgramSelection::update() {
if(is_something_selected()) {
if(cache.check_if_rebuild_should_occur())
cache.test_rebuild(std::vector<CanvasComponentContainer::ObjInfoSharedPtr>(selectedSet.begin(), selectedSet.end()), true);
selectionRectPoints[0] = selectionTransformCoords.from_space_world(initialSelectionAABB.min);
selectionRectPoints[1] = selectionTransformCoords.from_space_world(initialSelectionAABB.bottom_left());
selectionRectPoints[2] = selectionTransformCoords.from_space_world(initialSelectionAABB.max);
selectionRectPoints[3] = selectionTransformCoords.from_space_world(initialSelectionAABB.top_right());
scaleData.handlePoint = drawP.world.drawData.cam.c.to_space(initialSelectionAABB.max);
rotateData.centerHandlePoint = drawP.world.drawData.cam.c.to_space(rotateData.centerPos);
rotateData.handlePoint = get_rotation_point_pos_from_angle(rotateData.rotationAngle);
rebuild_cam_space();
if(drawP.is_actual_selection_tool(drawP.drawTool->get_type())) {
switch(transformOpHappening) {
case TransformOperation::NONE:
if(drawP.controls.leftClick && !drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held && !drawP.world.main.input.key(InputManager::KEY_GENERIC_LALT).held) {
if(mouse_collided_with_scale_point()) {
scaleData.currentPos = scaleData.startPos = selectionTransformCoords.from_space_world(initialSelectionAABB.max);
scaleData.centerPos = selectionTransformCoords.from_space_world(initialSelectionAABB.center());
transformOpHappening = TransformOperation::SCALE;
}
else if(mouse_collided_with_rotate_center_handle_point())
transformOpHappening = TransformOperation::ROTATE_RELOCATE_CENTER;
else if(mouse_collided_with_rotate_handle_point()) {
rotateData.rotationAngle = 0.0;
transformOpHappening = TransformOperation::ROTATE;
}
else if(mouse_collided_with_selection_aabb()) {
translateData.startPos = drawP.world.get_mouse_world_pos();
transformOpHappening = TransformOperation::TRANSLATE;
}
}
break;
case TransformOperation::TRANSLATE:
if(drawP.controls.leftClickHeld)
selectionTransformCoords = CoordSpaceHelperTransform(drawP.world.get_mouse_world_pos() - translateData.startPos);
else {
commit_transform_selection();
return;
}
break;
case TransformOperation::SCALE:
if(drawP.controls.leftClickHeld) {
WorldVec centerToScaleStart = scaleData.startPos - scaleData.centerPos;
Vector2f centerToScaleStartInCamSpace = drawP.world.drawData.cam.c.normalized_dir_to_space(centerToScaleStart.normalized());
Vector2f scaleCenterPointInCamSpace = drawP.world.drawData.cam.c.to_space(scaleData.centerPos);
scaleData.currentPos = drawP.world.drawData.cam.c.from_space(project_point_on_line(drawP.world.main.input.mouse.pos, scaleCenterPointInCamSpace, (scaleCenterPointInCamSpace + centerToScaleStartInCamSpace).eval()));
WorldVec centerToScaleCurrent = scaleData.currentPos - scaleData.centerPos;
WorldScalar centerToScaleStartNorm = centerToScaleStart.norm();
WorldScalar centerToScaleCurrentNorm = centerToScaleCurrent.norm();
bool isAnyNumberZero = centerToScaleCurrentNorm == WorldScalar(0) || centerToScaleStartNorm == WorldScalar(0);
if(!isAnyNumberZero) {
WorldMultiplier scaleAmount = WorldMultiplier(centerToScaleStartNorm) / WorldMultiplier(centerToScaleCurrentNorm);
selectionTransformCoords = CoordSpaceHelperTransform(scaleData.centerPos, scaleAmount);
}
}
else {
commit_transform_selection();
return;
}
break;
case TransformOperation::ROTATE_RELOCATE_CENTER:
if(drawP.controls.leftClickHeld)
rotateData.centerPos = drawP.world.get_mouse_world_pos();
else
transformOpHappening = TransformOperation::NONE;
break;
case TransformOperation::ROTATE:
if(drawP.controls.leftClickHeld) {
Vector2f rotationPointDiff = drawP.world.main.input.mouse.pos - rotateData.centerHandlePoint;
rotateData.rotationAngle = std::atan2(rotationPointDiff.y(), rotationPointDiff.x());
selectionTransformCoords = CoordSpaceHelperTransform(rotateData.centerPos, rotateData.rotationAngle);
}
else {
commit_transform_selection();
return;
}
break;
}
}
else if(transformOpHappening != TransformOperation::NONE) {
commit_transform_selection();
return;
}
if(drawP.world.main.input.key(InputManager::KEY_GENERIC_ESCAPE).pressed)
deselect_all();
else if(drawP.world.main.input.key(InputManager::KEY_DRAW_DELETE).pressed)
delete_all();
else if(drawP.world.main.input.key(InputManager::KEY_COPY).pressed && !is_being_transformed())
selection_to_clipboard();
else if(drawP.world.main.input.key(InputManager::KEY_CUT).pressed && !is_being_transformed()) {
selection_to_clipboard();
delete_all();
}
else if(drawP.world.main.input.key(InputManager::KEY_PASTE).pressed) {
deselect_all();
paste_clipboard(drawP.world.main.input.mouse.pos);
}
}
else if(drawP.world.main.input.key(InputManager::KEY_PASTE).pressed && !drawP.prevent_undo_or_redo())
paste_clipboard(drawP.world.main.input.mouse.pos);
else if(drawP.world.main.input.key(InputManager::KEY_GENERIC_ESCAPE).pressed)
drawP.switch_to_tool(DrawingProgramToolType::EDIT, true);
}
bool DrawingProgramSelection::is_being_transformed() {
return transformOpHappening != TransformOperation::NONE;
}
void DrawingProgramSelection::paste_clipboard(Vector2f pasteScreenPos) {
//auto& clipboard = drawP.world.main.clipboard;
//if(!drawP.is_selection_allowing_tool(drawP.drawTool->get_type()))
// drawP.switch_to_tool(DrawingProgramToolType::EDIT);
//uint32_t allPlacement = drawP.components->size();
//std::vector<std::shared_ptr<DrawComponent>> placedComponents;
//WorldVec mousePos = drawP.world.drawData.cam.c.from_space(pasteScreenPos);
//WorldVec moveVec = drawP.world.main.clipboard.pos - mousePos;
//WorldMultiplier scaleMultiplier = WorldMultiplier(drawP.world.main.clipboard.inverseScale) / WorldMultiplier(drawP.world.drawData.cam.c.inverseScale);
//WorldScalar scaleLimit(0.001);
//for(auto& c : drawP.world.main.clipboard.components) {
// if((c->coords.inverseScale / scaleMultiplier) < scaleLimit) {
// Logger::get().log("WORLDFATAL", "Some pasted objects will be too small! Scale up the canvas first (zoom in alot)");
// return;
// }
//}
//std::unordered_map<ServerClientID, ServerClientID> resourceRemapIDs;
//for(auto& r : clipboard.resources)
// resourceRemapIDs[r.first] = drawP.world.rMan.add_resource(r.second);
//for(auto& c : clipboard.components)
// placedComponents.emplace_back(c->deep_copy(drawP));
//drawP.addToCompCacheOnInsert = false;
//auto compListInserted = drawP.components.client_insert_ordered_vector_items(allPlacement, placedComponents);
//parallel_loop_container(compListInserted, [&](auto& c) {
// c->obj->remap_resource_ids(resourceRemapIDs);
// c->obj->get_coords().translate(-moveVec);
// c->obj->get_coords().scale_about(mousePos, scaleMultiplier);
// c->obj->commit_transform(drawP, false);
//});
//DrawComponent::client_send_place_many(drawP, compListInserted);
//drawP.addToCompCacheOnInsert = true;
//std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr> compSetInserted(compListInserted.begin(), compListInserted.end());
//set_to_selection(compSetInserted);
//drawP.add_undo_place_components(compSetInserted);
}
void DrawingProgramSelection::rebuild_cam_space() {
SCollision::ColliderCollection<WorldScalar> collideRectWorld;
collideRectWorld.triangle.emplace_back(selectionRectPoints[0], selectionRectPoints[1], selectionRectPoints[2]);
collideRectWorld.triangle.emplace_back(selectionRectPoints[0], selectionRectPoints[2], selectionRectPoints[3]);
collideRectWorld.recalculate_bounds();
camSpaceSelection = drawP.world.drawData.cam.c.world_collider_to_coords<SCollision::ColliderCollection<float>>(collideRectWorld);
}
void DrawingProgramSelection::calculate_initial_rotate_center_location() {
rotateData.centerPos = initialSelectionAABB.center();
}
bool DrawingProgramSelection::mouse_collided_with_selection() {
return mouse_collided_with_selection_aabb() || mouse_collided_with_scale_point() || mouse_collided_with_rotate_center_handle_point() || mouse_collided_with_rotate_handle_point();
}
bool DrawingProgramSelection::mouse_collided_with_selection_aabb() {
return SCollision::collide(camSpaceSelection, drawP.world.main.input.mouse.pos);
}
bool DrawingProgramSelection::mouse_collided_with_scale_point() {
return SCollision::collide(SCollision::Circle(scaleData.handlePoint, drawP.drag_point_radius()), drawP.world.main.input.mouse.pos);
}
bool DrawingProgramSelection::mouse_collided_with_rotate_center_handle_point() {
return SCollision::collide(SCollision::Circle(rotateData.centerHandlePoint, drawP.drag_point_radius()), drawP.world.main.input.mouse.pos);
}
bool DrawingProgramSelection::mouse_collided_with_rotate_handle_point() {
return SCollision::collide(SCollision::Circle(rotateData.handlePoint, drawP.drag_point_radius() * ROTATION_POINT_RADIUS_MULTIPLIER), drawP.world.main.input.mouse.pos);
}
Vector2f DrawingProgramSelection::get_rotation_point_pos_from_angle(double angle) {
return Vector2f{rotateData.centerHandlePoint + ROTATION_POINTS_DISTANCE * drawP.world.main.toolbar.final_gui_scale() * Vector2f{std::cos(angle), std::sin(angle)}};
}
void DrawingProgramSelection::draw_components(SkCanvas* canvas, const DrawData& drawData) {
if(is_something_selected()) {
selectionRectPoints[0] = selectionTransformCoords.from_space_world(initialSelectionAABB.min);
selectionRectPoints[1] = selectionTransformCoords.from_space_world(initialSelectionAABB.bottom_left());
selectionRectPoints[2] = selectionTransformCoords.from_space_world(initialSelectionAABB.max);
selectionRectPoints[3] = selectionTransformCoords.from_space_world(initialSelectionAABB.top_right());
scaleData.handlePoint = drawP.world.drawData.cam.c.to_space(selectionTransformCoords.from_space_world(initialSelectionAABB.max));
rotateData.centerHandlePoint = drawP.world.drawData.cam.c.to_space(selectionTransformCoords.from_space_world(rotateData.centerPos));
rotateData.handlePoint = get_rotation_point_pos_from_angle(rotateData.rotationAngle);
rebuild_cam_space();
DrawData selectionDrawData = drawData;
selectionDrawData.cam.c = selectionTransformCoords.other_coord_space_to_this_space(selectionDrawData.cam.c);
selectionDrawData.cam.set_viewing_area(drawP.world.main.window.size.cast<float>());
selectionDrawData.refresh_draw_optimizing_values();
cache.refresh_all_draw_cache(selectionDrawData);
SkImageInfo imgInfo = canvas->imageInfo();
sk_sp<SkSurface> surface;
#ifdef USE_SKIA_BACKEND_GRAPHITE
surface = SkSurfaces::RenderTarget(drawP.world.main.window.recorder(), imgInfo, skgpu::Mipmapped::kNo, drawP.world.main.window.defaultMSAASurfaceProps);
#elif USE_SKIA_BACKEND_GANESH
surface = SkSurfaces::RenderTarget(drawP.world.main.window.ctx.get(), skgpu::Budgeted::kNo, imgInfo, drawP.world.main.window.defaultMSAASampleCount, &drawP.world.main.window.defaultMSAASurfaceProps);
#endif
if(!surface)
throw std::runtime_error("[DrawingProgramSelection::draw_components] Could not make temporary surface");
cache.draw_components_to_canvas(surface->getCanvas(), selectionDrawData, {});
SkPaint glowBlurP;
glowBlurP.setImageFilter(SkImageFilters::Blur(5, 5, nullptr));
canvas->drawImage(surface->makeTemporaryImage(), 0, 0, SkSamplingOptions{SkFilterMode::kLinear}, &glowBlurP);
canvas->drawImage(surface->makeTemporaryImage(), 0, 0, SkSamplingOptions{SkFilterMode::kLinear}, nullptr);
}
}
void DrawingProgramSelection::draw_gui(SkCanvas* canvas, const DrawData& drawData) {
if(is_something_selected() && !camSpaceSelection.triangle.empty()) {
SkPathBuilder selectionRectPath;
selectionRectPath.moveTo(convert_vec2<SkPoint>(camSpaceSelection.triangle[0].p[0]));
selectionRectPath.lineTo(convert_vec2<SkPoint>(camSpaceSelection.triangle[0].p[1]));
selectionRectPath.lineTo(convert_vec2<SkPoint>(camSpaceSelection.triangle[0].p[2]));
selectionRectPath.lineTo(convert_vec2<SkPoint>(camSpaceSelection.triangle[1].p[2]));
selectionRectPath.close();
SkPaint p{SkColor4f{0.3f, 0.6f, 0.9f, 0.4f}};
canvas->drawPath(selectionRectPath.detach(), p);
if(drawP.is_actual_selection_tool(drawP.drawTool->get_type())) {
if(transformOpHappening == TransformOperation::NONE || transformOpHappening == TransformOperation::ROTATE || transformOpHappening == TransformOperation::ROTATE_RELOCATE_CENTER)
drawP.draw_drag_circle(canvas, rotateData.centerHandlePoint, {0.9f, 0.5f, 0.1f, 1.0f}, drawData);
if(transformOpHappening == TransformOperation::NONE || transformOpHappening == TransformOperation::ROTATE)
drawP.draw_drag_circle(canvas, rotateData.handlePoint, {0.9f, 0.5f, 0.1f, 1.0f}, drawData, ROTATION_POINT_RADIUS_MULTIPLIER);
if(transformOpHappening == TransformOperation::NONE || transformOpHappening == TransformOperation::SCALE)
drawP.draw_drag_circle(canvas, scaleData.handlePoint, {0.1f, 0.9f, 0.9f, 1.0f}, drawData);
}
}
}
void DrawingProgramSelection::reset_selection_data() {
selectedSet.clear();
cache.clear();
}
void DrawingProgramSelection::reset_transform_data() {
selectionTransformCoords = CoordSpaceHelperTransform();
transformOpHappening = TransformOperation::NONE;
translateData = TranslationData();
scaleData = ScaleData();
rotateData = RotationData();
}

View file

@ -1,94 +0,0 @@
#pragma once
#include "DrawingProgramCache.hpp"
#include "../CoordSpaceHelperTransform.hpp"
class DrawingProgram;
class DrawingProgramSelection {
public:
DrawingProgramSelection(DrawingProgram& initDrawP);
void add_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection<float>& cC, bool frontObjectOnly = false);
void remove_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection<float>& cC, bool frontObjectOnly = false);
CanvasComponentContainer::ObjInfoSharedPtr get_front_object_colliding_with(const SCollision::ColliderCollection<float>& cC);
void deselect_all();
void update();
void draw_components(SkCanvas* canvas, const DrawData& drawData);
void draw_gui(SkCanvas* canvas, const DrawData& drawData);
bool is_something_selected();
bool mouse_collided_with_selection();
void erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck);
bool is_selected(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck);
const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& get_selected_set();
bool is_being_transformed();
void invalidate_cache_at_aabb_before_pos(const SCollision::AABB<WorldScalar>& aabb, uint64_t placementToInvalidateAt);
void clear_own_cached_surfaces();
void preupdate_component(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck);
void selection_to_clipboard();
void paste_clipboard(Vector2f pasteScreenPos);
void delete_all();
private:
std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>&, std::vector<CanvasComponentContainer::ObjInfoSharedPtr>&)> erase_select_objects_in_bvh_func(std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& selectedComponents, const SCollision::ColliderCollection<float>& cC, const SCollision::ColliderCollection<WorldScalar>& cCWorld);
void fully_collided_erase_select_objects_func(std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& selectedComponents, const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode);
bool mouse_collided_with_selection_aabb();
bool mouse_collided_with_scale_point();
bool mouse_collided_with_rotate_center_handle_point();
bool mouse_collided_with_rotate_handle_point();
void set_to_selection(const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& newSelection);
void add_to_selection(const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& newSelection);
void add_erased_objects_back_to_cache(const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& erasedObjects);
void commit_transform_selection();
void calculate_aabb();
void calculate_initial_rotate_center_location();
void rebuild_cam_space();
Vector2f get_rotation_point_pos_from_angle(double angle);
SCollision::AABB<WorldScalar> initialSelectionAABB;
SCollision::ColliderCollection<float> camSpaceSelection;
std::array<WorldVec, 4> selectionRectPoints;
CoordSpaceHelperTransform selectionTransformCoords;
std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr> selectedSet;
DrawingProgramCache cache;
DrawingProgram& drawP;
enum class TransformOperation {
NONE = 0,
TRANSLATE,
SCALE,
ROTATE_RELOCATE_CENTER,
ROTATE
} transformOpHappening = TransformOperation::NONE;
struct TranslationData {
WorldVec startPos;
} translateData;
struct ScaleData {
WorldVec startPos;
WorldVec centerPos;
WorldVec currentPos;
Vector2f handlePoint;
} scaleData;
struct RotationData {
WorldVec centerPos;
Vector2f centerHandlePoint;
Vector2f handlePoint;
double rotationAngle = 0.0;
} rotateData;
void reset_selection_data();
void reset_transform_data();
};

View file

@ -8,10 +8,25 @@ void DrawingProgramLayer::draw(SkCanvas* canvas, const DrawData& drawData) const
p->obj->draw(canvas, drawData);
}
void DrawingProgramLayer::set_component_list_callbacks(DrawingProgramLayerManager& layerMan) const {
void DrawingProgramLayer::set_component_list_callbacks(DrawingProgramLayerListItem& layerListItem, DrawingProgramLayerManager& layerMan) const {
components->set_insert_callback([&](const CanvasComponentContainer::ObjInfoSharedPtr& c) {
c->obj->set_owner_obj_info(c);
c->obj->parentLayer = &layerListItem;
c->obj->commit_update(layerMan.drawP); // Run commit update on insert so that world bounds are calculated
layerMan.drawP.drawCache.add_component(c);
if(c->obj->get_comp().get_type() == CanvasComponentType::IMAGE)
layerMan.drawP.updateableComponents.emplace(c);
});
components->set_erase_callback([&](const CanvasComponentContainer::ObjInfoSharedPtr& c) {
layerMan.drawP.drawCache.erase_component(c);
layerMan.drawP.drawTool->erase_component(c);
std::erase_if(layerMan.drawP.droppedDownloadingFiles, [&c](auto& downloadingFile) {
return downloadingFile.comp == c;
});
layerMan.drawP.updateableComponents.erase(c);
});
components->set_move_callback([&](const CanvasComponentContainer::ObjInfoSharedPtr& c, uint32_t oldPos) {
layerMan.drawP.drawCache.invalidate_cache_at_aabb(c->obj->get_world_bounds());
});
}

View file

@ -8,7 +8,7 @@ class DrawingProgramLayerManager;
class DrawingProgramLayer {
public:
void draw(SkCanvas* canvas, const DrawData& drawData) const;
void set_component_list_callbacks(DrawingProgramLayerManager& layerMan) const;
void set_component_list_callbacks(DrawingProgramLayerListItem& layerListItem, DrawingProgramLayerManager& layerMan) const;
void commit_update_dont_invalidate_cache(DrawingProgramLayerManager& layerMan) const;
void get_flattened_component_list(std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& objList) const;

View file

@ -36,7 +36,7 @@ void DrawingProgramLayerListItem::set_component_list_callbacks(DrawingProgramLay
if(folderData)
folderData->set_component_list_callbacks(layerMan);
else
layerData->set_component_list_callbacks(layerMan);
layerData->set_component_list_callbacks(*this, layerMan);
}
void DrawingProgramLayerListItem::commit_update_dont_invalidate_cache(DrawingProgramLayerManager& layerMan) {
@ -90,10 +90,11 @@ const std::string& DrawingProgramLayerListItem::get_name() const {
return nameData->name;
}
void DrawingProgramLayerListItem::set_alpha(NetworkingObjects::DelayUpdateSerializedClassManager& delayedNetObjMan, float newAlpha) const {
void DrawingProgramLayerListItem::set_alpha(DrawingProgramLayerManager& layerMan, float newAlpha) const {
if(displayData && displayData->alpha != newAlpha) {
displayData->alpha = newAlpha;
delayedNetObjMan.send_update_to_all<DisplayData>(displayData, false);
// MUST CLEAR CACHE
layerMan.drawP.world.delayedUpdateObjectManager.send_update_to_all<DisplayData>(displayData, false);
}
}
@ -101,10 +102,11 @@ float DrawingProgramLayerListItem::get_alpha() const {
return displayData->alpha;
}
void DrawingProgramLayerListItem::set_visible(NetworkingObjects::DelayUpdateSerializedClassManager& delayedNetObjMan, bool newVisible) const {
void DrawingProgramLayerListItem::set_visible(DrawingProgramLayerManager& layerMan, bool newVisible) const {
if(displayData && displayData->visible != newVisible) {
displayData->visible = newVisible;
delayedNetObjMan.send_update_to_all<DisplayData>(displayData, false);
// MUST CLEAR CACHE
layerMan.drawP.world.delayedUpdateObjectManager.send_update_to_all<DisplayData>(displayData, false);
}
}
@ -112,10 +114,11 @@ bool DrawingProgramLayerListItem::get_visible() const {
return displayData->visible;
}
void DrawingProgramLayerListItem::set_blend_mode(NetworkingObjects::DelayUpdateSerializedClassManager& delayedNetObjMan, SerializedBlendMode newBlendMode) const {
void DrawingProgramLayerListItem::set_blend_mode(DrawingProgramLayerManager& layerMan, SerializedBlendMode newBlendMode) const {
if(displayData && displayData->blendMode != newBlendMode) {
displayData->blendMode = newBlendMode;
delayedNetObjMan.send_update_to_all<DisplayData>(displayData, false);
// MUST CLEAR CACHE
layerMan.drawP.world.delayedUpdateObjectManager.send_update_to_all<DisplayData>(displayData, false);
}
}
@ -133,7 +136,11 @@ void DrawingProgramLayerListItem::register_class(World& w) {
.readUpdateFuncServer = nullptr,
});
w.delayedUpdateObjectManager.register_class<NameData>(w.netObjMan);
w.delayedUpdateObjectManager.register_class<DisplayData>(w.netObjMan);
w.delayedUpdateObjectManager.register_class<DisplayData>(w.netObjMan, NetworkingObjects::DelayUpdateSerializedClassManager::CustomConstructors<DisplayData>{
.postUpdateFunc = [&](DisplayData& o) {
// MUST CLEAR CACHE
}
});
}
void DrawingProgramLayerListItem::write_constructor_func(const NetworkingObjects::NetObjTemporaryPtr<DrawingProgramLayerListItem>& o, cereal::PortableBinaryOutputArchive& a) {

View file

@ -28,13 +28,13 @@ class DrawingProgramLayerListItem {
const std::string& get_name() const;
void get_flattened_component_list(std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& objList) const;
void set_alpha(NetworkingObjects::DelayUpdateSerializedClassManager& delayedNetObjMan, float newAlpha) const;
void set_alpha(DrawingProgramLayerManager& layerMan, float newAlpha) const;
float get_alpha() const;
void set_visible(NetworkingObjects::DelayUpdateSerializedClassManager& delayedNetObjMan, bool newVisible) const;
void set_visible(DrawingProgramLayerManager& layerMan, bool newVisible) const;
bool get_visible() const;
void set_blend_mode(NetworkingObjects::DelayUpdateSerializedClassManager& delayedNetObjMan, SerializedBlendMode newBlendMode) const;
void set_blend_mode(DrawingProgramLayerManager& layerMan, SerializedBlendMode newBlendMode) const;
SerializedBlendMode get_blend_mode() const;
private:
static void write_constructor_func(const NetworkingObjects::NetObjTemporaryPtr<DrawingProgramLayerListItem>& o, cereal::PortableBinaryOutputArchive& a);

View file

@ -38,3 +38,31 @@ std::vector<CanvasComponentContainer::ObjInfoSharedPtr> DrawingProgramLayerManag
layerTreeRoot->get_flattened_component_list(toRet);
return toRet;
}
bool DrawingProgramLayerManager::layer_tree_root_exists() {
return layerTreeRoot;
}
const DrawingProgramLayerListItem& DrawingProgramLayerManager::get_layer_root() {
return *layerTreeRoot;
}
void DrawingProgramLayerManager::erase_component_set(const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& compsToErase) {
std::unordered_map<DrawingProgramLayerListItem*, std::unordered_set<NetworkingObjects::NetObjID>> idsToEraseInSpecificLayers;
for(auto& c : compsToErase)
idsToEraseInSpecificLayers[c->obj->parentLayer].emplace(c->obj.get_net_id());
for(auto& [layerListItem, netObjSetToErase] : idsToEraseInSpecificLayers) {
auto& layerComponentList = layerListItem->get_layer().components;
layerComponentList->erase_unordered_set(layerComponentList, netObjSetToErase);
}
}
bool DrawingProgramLayerManager::component_passes_layer_selector(const CanvasComponentContainer::ObjInfoSharedPtr& c, LayerSelector layerSelector) {
switch(layerSelector) {
case LayerSelector::ALL_VISIBLE_LAYERS:
return c->obj->parentLayer->get_visible();
case LayerSelector::LAYER_BEING_EDITED:
return is_a_layer_being_edited() && c->obj->parentLayer == editingLayer.lock().get();
}
return false;
}

View file

@ -12,6 +12,10 @@ class DrawingProgramLayerManager {
NetworkingObjects::NetObjOwnerPtr<DrawingProgramLayerListItem> layerTreeRoot;
NetworkingObjects::NetObjWeakPtr<DrawingProgramLayerListItem> editingLayer;
public:
enum class LayerSelector {
ALL_VISIBLE_LAYERS,
LAYER_BEING_EDITED
};
DrawingProgramLayerManager(DrawingProgram& drawProg);
void init();
@ -19,6 +23,12 @@ class DrawingProgramLayerManager {
void write_components_server(cereal::PortableBinaryOutputArchive& a);
void read_components_client(cereal::PortableBinaryInputArchive& a);
bool is_a_layer_being_edited();
void erase_component_set(const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& compsToErase);
bool component_passes_layer_selector(const CanvasComponentContainer::ObjInfoSharedPtr& c, LayerSelector layerSelector);
bool layer_tree_root_exists();
const DrawingProgramLayerListItem& get_layer_root();
template <typename T> CanvasComponentContainer::ObjInfoSharedPtr add_component_to_layer_being_edited(T* newObj) {
auto editLayerPtr = editingLayer.lock();
if(!editLayerPtr)

View file

@ -85,7 +85,7 @@ void DrawingProgramLayerManagerGUI::setup_list_gui(const std::string& id, bool&
gui.text_label(tempPtr->get_name());
}
if(gui.svg_icon_button_transparent("visible button", tempPtr->get_visible() ? "data/icons/eyeopen.svg" : "data/icons/eyeclose.svg", false, GUIStuff::TreeListing::ENTRY_HEIGHT)) {
tempPtr->set_visible(world.delayedUpdateObjectManager, !tempPtr->get_visible());
tempPtr->set_visible(layerMan, !tempPtr->get_visible());
isButtonClicked = true;
}
if(gui.svg_icon_button_transparent("delete button", "data/icons/trash.svg", false, GUIStuff::TreeListing::ENTRY_HEIGHT)) {
@ -178,7 +178,7 @@ void DrawingProgramLayerManagerGUI::setup_list_gui(const std::string& id, bool&
gui.input_text_field("input edit name", "Name", &nameToEdit);
tempPtr->set_name(world.delayedUpdateObjectManager, nameToEdit);
gui.slider_scalar_field("input alpha slider", "Alpha", &alphaValToEdit, 0.0f, 1.0f, 2);
tempPtr->set_alpha(world.delayedUpdateObjectManager, alphaValToEdit);
tempPtr->set_alpha(layerMan, alphaValToEdit);
gui.left_to_right_line_layout([&]() {
gui.text_label("Blend Mode");
gui.dropdown_select("input blend mode", &blendModeValToEdit, get_blend_mode_name_list(), 200.0f, [&hoveringOverDropdown]() {
@ -186,7 +186,7 @@ void DrawingProgramLayerManagerGUI::setup_list_gui(const std::string& id, bool&
hoveringOverDropdown = true;
});
});
tempPtr->set_blend_mode(world.delayedUpdateObjectManager, static_cast<SerializedBlendMode>(blendModeValToEdit));
tempPtr->set_blend_mode(layerMan, static_cast<SerializedBlendMode>(blendModeValToEdit));
}
}

View file

@ -63,8 +63,8 @@ void EditTool::switch_tool(DrawingProgramToolType newTool) {
pointDragging = nullptr;
isEditing = false;
if(!drawP.is_selection_allowing_tool(newTool))
drawP.selection.deselect_all();
//if(!drawP.is_selection_allowing_tool(newTool))
// drawP.selection.deselect_all();
}
void EditTool::edit_start(const CanvasComponentContainer::ObjInfoSharedPtr& comp) {
@ -114,26 +114,26 @@ void EditTool::tool_update() {
camCMouseAABB.recalculate_bounds();
if(drawP.controls.leftClick) {
bool modifySelection = !drawP.selection.is_being_transformed();
if(drawP.world.main.input.mouse.leftClicks >= 2 && !drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held && !drawP.world.main.input.key(InputManager::KEY_GENERIC_LALT).held) {
CanvasComponentContainer::ObjInfoSharedPtr selectedObjectToEdit = drawP.selection.get_front_object_colliding_with(camCMouseAABB);
//bool modifySelection = !drawP.selection.is_being_transformed();
//if(drawP.world.main.input.mouse.leftClicks >= 2 && !drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held && !drawP.world.main.input.key(InputManager::KEY_GENERIC_LALT).held) {
// CanvasComponentContainer::ObjInfoSharedPtr selectedObjectToEdit = drawP.selection.get_front_object_colliding_with(camCMouseAABB);
if(selectedObjectToEdit && is_editable(selectedObjectToEdit)) {
drawP.selection.deselect_all();
edit_start(selectedObjectToEdit);
modifySelection = false;
}
}
if(modifySelection) {
if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held)
drawP.selection.add_from_cam_coord_collider_to_selection(camCMouseAABB, true);
else if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LALT).held)
drawP.selection.remove_from_cam_coord_collider_to_selection(camCMouseAABB, true);
else {
drawP.selection.deselect_all();
drawP.selection.add_from_cam_coord_collider_to_selection(camCMouseAABB, true);
}
}
// if(selectedObjectToEdit && is_editable(selectedObjectToEdit)) {
// drawP.selection.deselect_all();
// edit_start(selectedObjectToEdit);
// modifySelection = false;
// }
//}
//if(modifySelection) {
// if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held)
// drawP.selection.add_from_cam_coord_collider_to_selection(camCMouseAABB, true);
// else if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LALT).held)
// drawP.selection.remove_from_cam_coord_collider_to_selection(camCMouseAABB, true);
// else {
// drawP.selection.deselect_all();
// drawP.selection.add_from_cam_coord_collider_to_selection(camCMouseAABB, true);
// }
//}
}
}
else {

View file

@ -17,6 +17,11 @@ void EraserTool::gui_toolbox() {
t.gui.push_id("eraser tool");
t.gui.text_label_centered("Eraser");
t.gui.slider_scalar_field("relwidth", "Size", &drawP.controls.relativeWidth, 3.0f, 40.0f);
t.gui.text_label("Erase from:");
if(t.gui.radio_button_field("layer edited", "Layer being edited", drawP.controls.layerSelector == DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED))
drawP.controls.layerSelector = DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED;
if(t.gui.radio_button_field("all visible", "All visible layers", drawP.controls.layerSelector == DrawingProgramLayerManager::LayerSelector::ALL_VISIBLE_LAYERS))
drawP.controls.layerSelector = DrawingProgramLayerManager::LayerSelector::ALL_VISIBLE_LAYERS;
t.gui.pop_id();
}
@ -31,16 +36,7 @@ void EraserTool::erase_component(const CanvasComponentContainer::ObjInfoSharedPt
}
void EraserTool::switch_tool(DrawingProgramToolType newTool) {
DrawingProgramCache::move_components_from_bvh_nodes_to_set(erasedComponents, erasedBVHNodes);
if(!erasedComponents.empty()) {
drawP.erase_component_set(erasedComponents);
// NOTE: Ideally, you would just recalculate the lastDrawnPos of every cache instead of invalidating and redrawing the caches
// However, the eraser might have other visual glitches during drawing that i am unaware of, so for now, just refresh the
// cache after a draw
drawP.compCache.clear_own_cached_surfaces();
}
erasedComponents.clear();
erasedBVHNodes.clear();
drawP.layerMan.erase_component_set(erasedComponents);
}
void EraserTool::tool_update() {
@ -53,20 +49,20 @@ void EraserTool::tool_update() {
SCollision::generate_wide_line(cC, prevMousePos, drawP.world.main.input.mouse.pos, drawP.controls.relativeWidth * 2.0f, true);
auto cCWorld = drawP.world.drawData.cam.c.collider_to_world<SCollision::ColliderCollection<WorldScalar>, SCollision::ColliderCollection<float>>(cC);
drawP.compCache.traverse_bvh_erase_function(cCWorld.bounds, [&](const auto& bvhNode, auto& comps) {
drawP.drawCache.traverse_bvh_erase_function(cCWorld.bounds, [&](const auto& bvhNode, auto& comps) {
if(bvhNode &&
SCollision::collide(cC, drawP.world.drawData.cam.c.to_space(bvhNode->bounds.min)) &&
SCollision::collide(cC, drawP.world.drawData.cam.c.to_space(bvhNode->bounds.max)) &&
SCollision::collide(cC, drawP.world.drawData.cam.c.to_space(bvhNode->bounds.top_right())) &&
SCollision::collide(cC, drawP.world.drawData.cam.c.to_space(bvhNode->bounds.bottom_left()))) {
erasedBVHNodes.emplace(bvhNode);
drawP.compCache.invalidate_cache_at_aabb_before_pos(bvhNode->bounds, 0);
drawP.drawCache.invalidate_cache_at_aabb(bvhNode->bounds);
drawP.drawCache.move_components_from_bvh_node_to_set(erasedComponents, bvhNode);
return true;
}
std::erase_if(comps, [&](auto& c) {
if(c->obj->collides_with(drawP.world.drawData.cam.c, cCWorld, cC)) {
if(drawP.layerMan.component_passes_layer_selector(c, drawP.controls.layerSelector) && c->obj->collides_with(drawP.world.drawData.cam.c, cCWorld, cC)) {
erasedComponents.emplace(c);
drawP.compCache.invalidate_cache_at_aabb_before_pos(c->obj->get_world_bounds(), c->pos);
drawP.drawCache.invalidate_cache_at_aabb(c->obj->get_world_bounds());
return true;
}
return false;

View file

@ -19,5 +19,4 @@ class EraserTool : public DrawingProgramToolBase {
virtual bool prevent_undo_or_redo() override;
std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr> erasedComponents; // Pointers will be erased from this set if theyre erased in the main list (done by callback)
std::unordered_set<std::shared_ptr<DrawingProgramCacheBVHNode>> erasedBVHNodes;
};

View file

@ -50,19 +50,19 @@ void LassoSelectTool::erase_component(const CanvasComponentContainer::ObjInfoSha
}
void LassoSelectTool::switch_tool(DrawingProgramToolType newTool) {
if(!drawP.is_selection_allowing_tool(newTool))
drawP.selection.deselect_all();
//if(!drawP.is_selection_allowing_tool(newTool))
// drawP.selection.deselect_all();
}
void LassoSelectTool::tool_update() {
switch(controls.selectionMode) {
case 0: {
if(drawP.controls.leftClick && !drawP.selection.is_being_transformed()) {
controls = LassoSelectControls();
controls.coords = drawP.world.drawData.cam.c;
controls.lassoPoints.emplace_back(controls.coords.get_mouse_pos(drawP.world));
controls.selectionMode = 1;
}
//if(drawP.controls.leftClick && !drawP.selection.is_being_transformed()) {
// controls = LassoSelectControls();
// controls.coords = drawP.world.drawData.cam.c;
// controls.lassoPoints.emplace_back(controls.coords.get_mouse_pos(drawP.world));
// controls.selectionMode = 1;
//}
break;
}
case 1: {
@ -92,17 +92,18 @@ void LassoSelectTool::tool_update() {
cC.recalculate_bounds();
if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held)
drawP.selection.add_from_cam_coord_collider_to_selection(cC);
else if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LALT).held)
drawP.selection.remove_from_cam_coord_collider_to_selection(cC);
else {
drawP.selection.deselect_all();
drawP.selection.add_from_cam_coord_collider_to_selection(cC);
}
//if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held)
// drawP.selection.add_from_cam_coord_collider_to_selection(cC);
//else if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LALT).held)
// drawP.selection.remove_from_cam_coord_collider_to_selection(cC);
//else {
// drawP.selection.deselect_all();
// drawP.selection.add_from_cam_coord_collider_to_selection(cC);
//}
}
else if(!drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held && !drawP.world.main.input.key(InputManager::KEY_GENERIC_LALT).held) {
//drawP.selection.deselect_all();
}
else if(!drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held && !drawP.world.main.input.key(InputManager::KEY_GENERIC_LALT).held)
drawP.selection.deselect_all();
controls.selectionMode = 0;
}
break;
@ -111,7 +112,7 @@ void LassoSelectTool::tool_update() {
}
bool LassoSelectTool::prevent_undo_or_redo() {
return drawP.selection.is_something_selected() || controls.selectionMode == 1;
return true;//drawP.selection.is_something_selected() || controls.selectionMode == 1;
}
void LassoSelectTool::draw(SkCanvas* canvas, const DrawData& drawData) {

View file

@ -39,6 +39,6 @@ void PanCanvasTool::draw(SkCanvas* canvas, const DrawData& drawData) {
}
void PanCanvasTool::switch_tool(DrawingProgramToolType newTool) {
if(!drawP.is_selection_allowing_tool(newTool))
drawP.selection.deselect_all();
//if(!drawP.is_selection_allowing_tool(newTool))
// drawP.selection.deselect_all();
}

View file

@ -28,19 +28,19 @@ bool RectSelectTool::right_click_popup_gui(Vector2f popupPos) {
}
void RectSelectTool::switch_tool(DrawingProgramToolType newTool) {
if(!drawP.is_selection_allowing_tool(newTool))
drawP.selection.deselect_all();
//if(!drawP.is_selection_allowing_tool(newTool))
// drawP.selection.deselect_all();
}
void RectSelectTool::tool_update() {
switch(controls.selectionMode) {
case 0: {
if(drawP.controls.leftClick && !drawP.selection.is_being_transformed()) {
controls = RectSelectControls();
controls.coords = drawP.world.drawData.cam.c;
controls.selectStartAt = controls.coords.get_mouse_pos(drawP.world);
controls.selectionMode = 1;
}
//if(drawP.controls.leftClick && !drawP.selection.is_being_transformed()) {
// controls = RectSelectControls();
// controls.coords = drawP.world.drawData.cam.c;
// controls.selectStartAt = controls.coords.get_mouse_pos(drawP.world);
// controls.selectionMode = 1;
//}
break;
}
case 1: {
@ -55,14 +55,14 @@ void RectSelectTool::tool_update() {
cC.triangle.emplace_back(controls.newT[2], controls.newT[3], controls.newT[0]);
cC.recalculate_bounds();
if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held)
drawP.selection.add_from_cam_coord_collider_to_selection(cC);
else if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LALT).held)
drawP.selection.remove_from_cam_coord_collider_to_selection(cC);
else {
drawP.selection.deselect_all();
drawP.selection.add_from_cam_coord_collider_to_selection(cC);
}
//if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held)
// drawP.selection.add_from_cam_coord_collider_to_selection(cC);
//else if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LALT).held)
// drawP.selection.remove_from_cam_coord_collider_to_selection(cC);
//else {
// drawP.selection.deselect_all();
// drawP.selection.add_from_cam_coord_collider_to_selection(cC);
//}
controls.selectionMode = 0;
}
@ -72,7 +72,8 @@ void RectSelectTool::tool_update() {
}
bool RectSelectTool::prevent_undo_or_redo() {
return drawP.selection.is_something_selected() || controls.selectionMode == 1;
//return drawP.selection.is_something_selected() || controls.selectionMode == 1;
return true;
}
void RectSelectTool::draw(SkCanvas* canvas, const DrawData& drawData) {

View file

@ -39,6 +39,6 @@ void ZoomCanvasTool::draw(SkCanvas* canvas, const DrawData& drawData) {
}
void ZoomCanvasTool::switch_tool(DrawingProgramToolType newTool) {
if(!drawP.is_selection_allowing_tool(newTool))
drawP.selection.deselect_all();
//if(!drawP.is_selection_allowing_tool(newTool))
// drawP.selection.deselect_all();
}

View file

@ -1228,8 +1228,6 @@ void Toolbar::performance_metrics() {
std::stringstream a;
a << "FPS: " << std::fixed << std::setprecision(0) << (1.0 / main.deltaTime);
gui.text_label(a.str());
if(main.world->drawProg.components)
gui.text_label("Item Count: " + std::to_string(main.world->drawProg.components->size()));
std::stringstream b;
b << "Coord: " << main.world->drawData.cam.c.pos.x().display_int_str(5) << ", " << main.world->drawData.cam.c.pos.y().display_int_str(5);
gui.text_label(b.str());