Begin work separating selection code from rectangle select tool
This commit is contained in:
parent
7f905f01df
commit
ff1355234f
15 changed files with 386 additions and 639 deletions
|
|
@ -56,6 +56,7 @@ set(sources "src/main.cpp"
|
|||
"src/DrawingProgram/ScreenshotTool.cpp"
|
||||
"src/DrawingProgram/EditTool.cpp"
|
||||
"src/DrawingProgram/ImageTool.cpp"
|
||||
"src/DrawingProgram/DrawingProgramSelection.cpp"
|
||||
"src/DrawComponents/DrawEllipse.cpp"
|
||||
"src/DrawComponents/DrawRectangle.cpp"
|
||||
"src/DrawComponents/DrawImage.cpp"
|
||||
|
|
|
|||
|
|
@ -114,24 +114,21 @@ Vector2f CoordSpaceHelper::get_mouse_pos(const World& w) const {
|
|||
return to_space(w.drawData.cam.c.from_space(w.main.input.mouse.pos));
|
||||
}
|
||||
|
||||
//SkMatrix CoordSpaceHelper::get_sk_matrix(const DrawData& drawData) const {
|
||||
// //WorldTransform newTransform = drawData.cam.transform * inverseTransform;
|
||||
// //const auto& newMat = newTransform.matrix();
|
||||
// //return SkMatrix::MakeAll(newMat(0, 0), newMat(0, 1), newMat(0, 2),
|
||||
// // newMat(1, 0), newMat(1, 1), newMat(1, 2),
|
||||
// // 0.0f, 0.0f, 1.0f);
|
||||
// return SkMatrix::I();
|
||||
//}
|
||||
//
|
||||
//SkMatrix CoordSpaceHelper::get_sk_matrix_temp(const WorldTransform& tempTransform, const DrawData& drawData) const {
|
||||
// //WorldTransform newTransform = drawData.cam.transform * tempTransform * inverseTransform;
|
||||
// //const auto& newMat = newTransform.matrix();
|
||||
// //return SkMatrix::MakeAll(newMat(0, 0), newMat(0, 1), newMat(0, 2),
|
||||
// // newMat(1, 0), newMat(1, 1), newMat(1, 2),
|
||||
// // 0.0f, 0.0f, 1.0f);
|
||||
// return SkMatrix::I();
|
||||
//}
|
||||
//
|
||||
CoordSpaceHelper CoordSpaceHelper::other_coord_space_to_this_space(const CoordSpaceHelper& other) const {
|
||||
CoordSpaceHelper toRet;
|
||||
toRet.pos = (other.pos - pos) / inverseScale;
|
||||
toRet.rotation = other.rotation;
|
||||
toRet.inverseScale = other.inverseScale / inverseScale;
|
||||
return toRet;
|
||||
}
|
||||
|
||||
CoordSpaceHelper CoordSpaceHelper::other_coord_space_from_this_space(const CoordSpaceHelper& other) const {
|
||||
CoordSpaceHelper toRet;
|
||||
toRet.pos = (other.pos * inverseScale) + pos;
|
||||
toRet.rotation = other.rotation;
|
||||
toRet.inverseScale = other.inverseScale * inverseScale;
|
||||
return toRet;
|
||||
}
|
||||
|
||||
void CoordSpaceHelper::transform_sk_canvas(SkCanvas* canvas, const DrawData& drawData) const {
|
||||
Vector2f translateSpace = -to_space(drawData.cam.c.pos);
|
||||
|
|
|
|||
|
|
@ -32,6 +32,9 @@ class CoordSpaceHelper {
|
|||
Vector2f dir_to_space(const WorldVec& coord) const;
|
||||
Vector2f normalized_dir_to_space(const WorldVec& coord) const;
|
||||
|
||||
CoordSpaceHelper other_coord_space_to_this_space(const CoordSpaceHelper& other) const;
|
||||
CoordSpaceHelper other_coord_space_from_this_space(const CoordSpaceHelper& other) const;
|
||||
|
||||
template <typename S, typename T> S collider_to_world(const T& collider) const {
|
||||
return collider.template transform<WorldScalar>(
|
||||
[&coords = *this](const Vector2f& a) {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
#include "../CoordSpaceHelper.hpp"
|
||||
#include "../Server/MainServer.hpp"
|
||||
#include "../CollabList.hpp"
|
||||
#include "../DrawingProgram/DrawingProgramCache.hpp"
|
||||
|
||||
#ifndef IS_SERVER
|
||||
#include "../DrawData.hpp"
|
||||
|
|
@ -24,6 +23,10 @@
|
|||
#define DRAWCOMP_MIPMAP_LEVEL_TWO 5
|
||||
|
||||
class DrawingProgram;
|
||||
class DrawComponent;
|
||||
class DrawingProgramCacheBVHNode;
|
||||
|
||||
typedef CollabList<std::shared_ptr<DrawComponent>, ServerClientID> CollabListType;
|
||||
|
||||
class DrawComponent {
|
||||
public:
|
||||
|
|
@ -64,16 +67,14 @@ class DrawComponent {
|
|||
std::chrono::time_point<std::chrono::steady_clock> tempServerTransformTimer;
|
||||
bool serverIsTempTransform = false;
|
||||
|
||||
std::weak_ptr<CollabList<std::shared_ptr<DrawComponent>, ServerClientID>::ObjectInfo> collabListInfo;
|
||||
std::weak_ptr<DrawingProgramCache::BVHNode> parentBvhNode;
|
||||
std::weak_ptr<CollabListType::ObjectInfo> collabListInfo;
|
||||
std::weak_ptr<DrawingProgramCacheBVHNode> parentBvhNode;
|
||||
|
||||
void server_update(MainServer& server, ServerClientID id);
|
||||
|
||||
#ifndef IS_SERVER
|
||||
bool selected = false;
|
||||
|
||||
bool globalCollisionCheck = false;
|
||||
|
||||
std::chrono::time_point<std::chrono::steady_clock> lastTransformTime;
|
||||
std::chrono::time_point<std::chrono::steady_clock> lastUpdateTime;
|
||||
|
||||
|
|
@ -115,4 +116,3 @@ class DrawComponent {
|
|||
virtual void allocate_collider() = 0;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -49,7 +49,8 @@ DrawingProgram::DrawingProgram(World& initWorld):
|
|||
inkDropperTool(*this),
|
||||
screenshotTool(*this),
|
||||
editTool(*this),
|
||||
imageTool(*this)
|
||||
imageTool(*this),
|
||||
selection(*this)
|
||||
{
|
||||
components.updateCallback = [&]() {
|
||||
if(&world == world.main.world.get())
|
||||
|
|
@ -172,18 +173,6 @@ void DrawingProgram::parallel_loop_all_components(std::function<void(const std::
|
|||
parallel_loop_container(components.client_list(), func);
|
||||
}
|
||||
|
||||
void DrawingProgram::check_all_collisions_base(const SCollision::ColliderCollection<WorldScalar>& checkAgainstWorld, const SCollision::ColliderCollection<float>& checkAgainstCam) {
|
||||
compCache.traverse_bvh_collision_check(checkAgainstWorld, checkAgainstCam);
|
||||
}
|
||||
|
||||
void DrawingProgram::check_all_collisions(const SCollision::ColliderCollection<WorldScalar>& checkAgainst) {
|
||||
check_all_collisions_base(checkAgainst, world.drawData.cam.c.world_collider_to_coords<SCollision::ColliderCollection<float>>(checkAgainst));
|
||||
}
|
||||
|
||||
void DrawingProgram::check_all_collisions_transform(const SCollision::ColliderCollection<float>& checkAgainst) {
|
||||
check_all_collisions_base(world.drawData.cam.c.collider_to_world<SCollision::ColliderCollection<WorldScalar>, SCollision::ColliderCollection<float>>(checkAgainst), checkAgainst);
|
||||
}
|
||||
|
||||
std::unordered_set<ServerClientID> DrawingProgram::get_used_resources() {
|
||||
std::unordered_set<ServerClientID> toRet;
|
||||
for(auto& c : components.client_list())
|
||||
|
|
@ -339,8 +328,6 @@ void DrawingProgram::update() {
|
|||
controls.selectedTool = TOOL_SCREENSHOT;
|
||||
else if(world.main.input.key(InputManager::KEY_DRAW_TOOL_EDIT).pressed)
|
||||
controls.selectedTool = TOOL_EDIT;
|
||||
else if(world.main.input.key(InputManager::KEY_PASTE).pressed)
|
||||
rectSelectTool.paste_clipboard();
|
||||
|
||||
switch(controls.selectedTool) {
|
||||
case TOOL_BRUSH:
|
||||
|
|
@ -381,6 +368,7 @@ void DrawingProgram::update() {
|
|||
check_updateable_components();
|
||||
|
||||
compCache.update();
|
||||
selection.update();
|
||||
}
|
||||
|
||||
void DrawingProgram::drag_drop_update() {
|
||||
|
|
@ -473,7 +461,7 @@ void DrawingProgram::initialize_draw_data(cereal::PortableBinaryInputArchive& a)
|
|||
parallel_loop_all_components([&](const auto& c){
|
||||
c->obj->final_update(*this, false);
|
||||
});
|
||||
compCache.force_rebuild();
|
||||
compCache.force_rebuild(components.client_list());
|
||||
}
|
||||
|
||||
void DrawingProgram::reset_selection() {
|
||||
|
|
@ -583,9 +571,10 @@ void DrawingProgram::draw(SkCanvas* canvas, const DrawData& drawData) {
|
|||
else {
|
||||
SkCanvas* drawProgCacheCanvas = world.main.drawProgCache.surface->getCanvas();
|
||||
drawProgCacheCanvas->clear(SkColor4f{0.0f, 0.0f, 0.0f, 0.0f});
|
||||
compCache.refresh_all_draw_cache(drawProgCacheCanvas, drawData);
|
||||
draw_components_to_canvas(drawProgCacheCanvas, drawData, false);
|
||||
compCache.refresh_all_draw_cache(drawData);
|
||||
compCache.draw_components_to_canvas(drawProgCacheCanvas, drawData);
|
||||
canvas->drawImage(world.main.drawProgCache.surface->makeTemporaryImage(), 0, 0);
|
||||
selection.draw(canvas, drawData);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -622,96 +611,6 @@ void DrawingProgram::draw(SkCanvas* canvas, const DrawData& drawData) {
|
|||
}
|
||||
}
|
||||
|
||||
void DrawingProgram::draw_components_to_canvas(SkCanvas* canvas, const DrawData& drawData, bool dontUseCache, uint64_t* lastDrawnComponentPlacement) {
|
||||
uint64_t lastComponentDrawn = 0;
|
||||
|
||||
std::vector<std::shared_ptr<DrawingProgramCache::BVHNode>> cachedNodesToDraw;
|
||||
std::vector<CollabListType::ObjectInfoPtr> uncachedCompsToDraw;
|
||||
|
||||
compCache.traverse_bvh_run_function(drawData.cam.viewingAreaGenerousCollider, [&](const std::shared_ptr<DrawingProgramCache::BVHNode>& node, const std::vector<CollabListType::ObjectInfoPtr>& comps) {
|
||||
if(!dontUseCache && node && node->drawCache && node->coords.inverseScale <= drawData.cam.c.inverseScale) {
|
||||
cachedNodesToDraw.emplace_back(node);
|
||||
return false;
|
||||
}
|
||||
|
||||
for(auto& c : comps)
|
||||
c->obj->calculate_draw_transform(drawData);
|
||||
|
||||
uncachedCompsToDraw.insert(uncachedCompsToDraw.end(), comps.begin(), comps.end());
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
std::sort(uncachedCompsToDraw.begin(), uncachedCompsToDraw.end(), [](auto& a, auto& b) {
|
||||
return a->pos < b->pos;
|
||||
});
|
||||
std::sort(cachedNodesToDraw.begin(), cachedNodesToDraw.end(), [](auto& a, auto& b) {
|
||||
return a->drawCache.value().lastDrawnComponentPlacement < b->drawCache.value().lastDrawnComponentPlacement;
|
||||
});
|
||||
size_t nextCacheToRender = 0;
|
||||
for(auto& c : uncachedCompsToDraw) {
|
||||
for(;;) {
|
||||
if(nextCacheToRender >= cachedNodesToDraw.size() || c->pos < cachedNodesToDraw[nextCacheToRender]->drawCache.value().lastDrawnComponentPlacement)
|
||||
break;
|
||||
auto& bvhNode = cachedNodesToDraw[nextCacheToRender];
|
||||
auto& drawCache = bvhNode->drawCache.value();
|
||||
canvas->save();
|
||||
bvhNode->coords.transform_sk_canvas(canvas, drawData);
|
||||
drawCache.lastRenderTime = std::chrono::steady_clock::now();
|
||||
|
||||
lastComponentDrawn = std::max(lastComponentDrawn, drawCache.lastDrawnComponentPlacement);
|
||||
|
||||
SkPaint p;
|
||||
p.setBlendMode(SkBlendMode::kSrc);
|
||||
|
||||
// Note, no mipmaps here. You might need to generate mipmaps in the future (withDefaultMipmaps)
|
||||
canvas->drawImage(drawCache.surface->makeTemporaryImage(), 0, 0, {SkFilterMode::kLinear, SkMipmapMode::kLinear}, &p);
|
||||
|
||||
canvas->restore();
|
||||
nextCacheToRender++;
|
||||
}
|
||||
c->obj->draw(canvas, drawData);
|
||||
c->obj->drawSetupData.shouldDraw = false;
|
||||
lastComponentDrawn = std::max(lastComponentDrawn, c->pos);
|
||||
}
|
||||
for(;nextCacheToRender < cachedNodesToDraw.size(); nextCacheToRender++) {
|
||||
auto& bvhNode = cachedNodesToDraw[nextCacheToRender];
|
||||
auto& drawCache = bvhNode->drawCache.value();
|
||||
canvas->save();
|
||||
bvhNode->coords.transform_sk_canvas(canvas, drawData);
|
||||
drawCache.lastRenderTime = std::chrono::steady_clock::now();
|
||||
|
||||
lastComponentDrawn = std::max(lastComponentDrawn, drawCache.lastDrawnComponentPlacement);
|
||||
|
||||
SkPaint p;
|
||||
p.setBlendMode(SkBlendMode::kSrc);
|
||||
|
||||
// Note, no mipmaps here. You might need to generate mipmaps in the future (withDefaultMipmaps)
|
||||
canvas->drawImage(drawCache.surface->makeTemporaryImage(), 0, 0, {SkFilterMode::kLinear, SkMipmapMode::kLinear}, &p);
|
||||
|
||||
canvas->restore();
|
||||
}
|
||||
|
||||
if(lastDrawnComponentPlacement)
|
||||
*lastDrawnComponentPlacement = lastComponentDrawn;
|
||||
}
|
||||
|
||||
Vector4f* DrawingProgram::get_foreground_color_ptr() {
|
||||
return &controls.foregroundColor;
|
||||
}
|
||||
|
||||
void DrawingProgram::draw_cache(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr<DrawComponent>& lastComp) {
|
||||
SkCanvas* cacheCanvas = world.main.drawProgCache.canvas;
|
||||
cacheCanvas->clear(SkColor4f{0, 0, 0, 0});
|
||||
parallel_loop_all_components([&](auto& c) {
|
||||
c->obj->calculate_draw_transform(drawData);
|
||||
});
|
||||
for(auto& c : components.client_list()) {
|
||||
if(c->obj == lastComp)
|
||||
break;
|
||||
c->obj->draw(cacheCanvas, drawData);
|
||||
}
|
||||
canvas->drawImage(world.main.drawProgCache.surface->makeTemporaryImage(), 0, 0);
|
||||
world.main.drawProgCache.firstCompUpdate = lastComp;
|
||||
world.main.drawProgCache.refresh = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#include "ScreenshotTool.hpp"
|
||||
#include "EditTool.hpp"
|
||||
#include "ImageTool.hpp"
|
||||
#include "DrawingProgramSelection.hpp"
|
||||
#include "../CollabList.hpp"
|
||||
|
||||
class World;
|
||||
|
|
@ -51,8 +52,6 @@ class DrawingProgram {
|
|||
|
||||
DrawingProgramCache compCache;
|
||||
|
||||
typedef CollabList<std::shared_ptr<DrawComponent>, ServerClientID> CollabListType;
|
||||
|
||||
CollabListType components;
|
||||
|
||||
std::unordered_set<std::shared_ptr<DrawComponent>> updateableComponents;
|
||||
|
|
@ -69,13 +68,6 @@ class DrawingProgram {
|
|||
|
||||
void add_file_to_canvas_by_path_execute(const std::string& filePath, Vector2f dropPos);
|
||||
|
||||
void draw_cache(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr<DrawComponent>& lastComp);
|
||||
|
||||
void check_all_collisions_base(const SCollision::ColliderCollection<WorldScalar>& checkAgainstWorld, const SCollision::ColliderCollection<float>& checkAgainstCam);
|
||||
void check_all_collisions(const SCollision::ColliderCollection<WorldScalar>& checkAgainst);
|
||||
void check_all_collisions_transform(const SCollision::ColliderCollection<float>& checkAgainst);
|
||||
|
||||
void add_variable_width_line(SkPath& p, const DrawData& drawData, const WorldVec& p1, const WorldVec& p2, float w1, float w2);
|
||||
void reset_selection();
|
||||
void reset_tools();
|
||||
|
||||
|
|
@ -86,8 +78,6 @@ class DrawingProgram {
|
|||
void add_undo_place_component(uint64_t placement, const std::shared_ptr<DrawComponent>& comp);
|
||||
void add_undo_place_components(uint64_t placement, const std::vector<std::shared_ptr<DrawComponent>>& comps);
|
||||
|
||||
void draw_components_to_canvas(SkCanvas* canvas, const DrawData& drawData, bool dontUseCache, uint64_t* lastDrawnComponentPlacement = nullptr);
|
||||
|
||||
BrushTool brushTool;
|
||||
EraserTool eraserTool;
|
||||
RectSelectTool rectSelectTool;
|
||||
|
|
@ -99,6 +89,8 @@ class DrawingProgram {
|
|||
EditTool editTool;
|
||||
ImageTool imageTool;
|
||||
|
||||
DrawingProgramSelection selection;
|
||||
|
||||
bool temporaryEraser = false;
|
||||
|
||||
enum Tool : int {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
#define MAXIMUM_DRAW_CACHE_SURFACES 40
|
||||
|
||||
std::unordered_set<std::shared_ptr<DrawingProgramCache::BVHNode>> DrawingProgramCache::nodesWithCachedSurfaces;
|
||||
std::unordered_set<std::shared_ptr<DrawingProgramCacheBVHNode>> DrawingProgramCache::nodesWithCachedSurfaces;
|
||||
|
||||
DrawingProgramCache::DrawingProgramCache(DrawingProgram& initDrawP):
|
||||
drawP(initDrawP)
|
||||
|
|
@ -22,7 +22,7 @@ DrawingProgramCache::DrawingProgramCache(DrawingProgram& initDrawP):
|
|||
}
|
||||
|
||||
void DrawingProgramCache::build(std::vector<CollabListType::ObjectInfoPtr> components) {
|
||||
bvhRoot = std::make_shared<BVHNode>();
|
||||
bvhRoot = std::make_shared<DrawingProgramCacheBVHNode>();
|
||||
unsortedComponents.clear();
|
||||
clear_own_cached_surfaces();
|
||||
std::erase_if(components, [&](auto& c) {
|
||||
|
|
@ -37,19 +37,33 @@ void DrawingProgramCache::build(std::vector<CollabListType::ObjectInfoPtr> compo
|
|||
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& s) {
|
||||
return s->drawCache.value().attachedDrawP == &drawP;
|
||||
return s->drawCache.value().attachedCache == this;
|
||||
});
|
||||
}
|
||||
|
||||
void DrawingProgramCache::update() {
|
||||
if(!disableRefresh && unsortedComponents.size() >= 1000 && (std::chrono::steady_clock::now() - lastBvhBuildTime) >= std::chrono::seconds(5))
|
||||
force_rebuild();
|
||||
force_rebuild(drawP.components.client_list());
|
||||
}
|
||||
|
||||
void DrawingProgramCache::force_rebuild() {
|
||||
build(drawP.components.client_list());
|
||||
void DrawingProgramCache::force_rebuild(const std::vector<CollabListType::ObjectInfoPtr>& componentsToBuild) {
|
||||
build(componentsToBuild);
|
||||
}
|
||||
|
||||
const std::vector<CollabListType::ObjectInfoPtr>& 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 CollabListType::ObjectInfoPtr& c) {
|
||||
|
|
@ -71,7 +85,7 @@ void DrawingProgramCache::erase_component(const CollabListType::ObjectInfoPtr& c
|
|||
invalidate_cache_before_pos(c->pos);
|
||||
}
|
||||
|
||||
std::pair<std::shared_ptr<DrawingProgramCache::BVHNode>, std::vector<DrawingProgramCache::CollabListType::ObjectInfoPtr>::iterator> DrawingProgramCache::get_bvh_node_fully_containing_recursive(const std::shared_ptr<BVHNode>& bvhNode, DrawComponent* c) {
|
||||
std::pair<std::shared_ptr<DrawingProgramCacheBVHNode>, std::vector<CollabListType::ObjectInfoPtr>::iterator> DrawingProgramCache::get_bvh_node_fully_containing_recursive(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, DrawComponent* c) {
|
||||
SCollision::AABB<WorldScalar>& cAABB = c->worldAABB.value();
|
||||
if(!bvhNode->bounds.fully_contains_aabb(cAABB))
|
||||
return {nullptr, bvhNode->components.end()};
|
||||
|
|
@ -88,13 +102,13 @@ std::pair<std::shared_ptr<DrawingProgramCache::BVHNode>, std::vector<DrawingProg
|
|||
return {nullptr, bvhNode->components.end()};
|
||||
}
|
||||
|
||||
void DrawingProgramCache::traverse_bvh_erase_function(const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<BVHNode>& node, std::vector<CollabListType::ObjectInfoPtr>& components)> f) {
|
||||
void DrawingProgramCache::traverse_bvh_erase_function(const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node, std::vector<CollabListType::ObjectInfoPtr>& 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<BVHNode>& bvhNode, const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<BVHNode>& node, std::vector<CollabListType::ObjectInfoPtr>& components)> f) {
|
||||
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<CollabListType::ObjectInfoPtr>& components)> f) {
|
||||
if(bvhNode && SCollision::collide(aabb, bvhNode->bounds)) {
|
||||
if(f(bvhNode, bvhNode->components))
|
||||
return true;
|
||||
|
|
@ -105,47 +119,12 @@ bool DrawingProgramCache::traverse_bvh_erase_function_recursive(const std::share
|
|||
return false;
|
||||
}
|
||||
|
||||
void DrawingProgramCache::traverse_bvh_collision_check(const SCollision::ColliderCollection<WorldScalar>& checkAgainstWorld, const SCollision::ColliderCollection<float>& checkAgainstCam) {
|
||||
for(auto& c : drawP.components.client_list())
|
||||
c->obj->globalCollisionCheck = false;
|
||||
for(auto& c : unsortedComponents)
|
||||
c->obj->globalCollisionCheck = c->obj->collides_with(drawP.world.drawData.cam.c, checkAgainstWorld, checkAgainstCam, drawP.colliderAllocated);
|
||||
traverse_bvh_collision_check_recursive(bvhRoot, checkAgainstWorld, checkAgainstCam);
|
||||
}
|
||||
|
||||
void DrawingProgramCache::traverse_bvh_collision_check_recursive_tiny(const std::shared_ptr<BVHNode>& bvhNode) {
|
||||
if(bvhNode) {
|
||||
for(auto& c : bvhNode->components)
|
||||
c->obj->globalCollisionCheck = true;
|
||||
for(auto& p : bvhNode->children)
|
||||
traverse_bvh_collision_check_recursive_tiny(p);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawingProgramCache::traverse_bvh_collision_check_recursive(const std::shared_ptr<BVHNode>& bvhNode, const SCollision::ColliderCollection<WorldScalar>& checkAgainstWorld, const SCollision::ColliderCollection<float>& checkAgainstCam) {
|
||||
if(bvhNode) {
|
||||
if(SCollision::collide(checkAgainstWorld.bounds, bvhNode->bounds)) {
|
||||
auto& camCoords = drawP.world.drawData.cam.c;
|
||||
if((camCoords.inverseScale >> DRAWCOMP_COLLIDE_MIN_SHIFT_TINY) >= bvhNode->coords.inverseScale) {
|
||||
if(SCollision::collide(checkAgainstCam, camCoords.to_space(bvhNode->bounds.min)))
|
||||
traverse_bvh_collision_check_recursive_tiny(bvhNode);
|
||||
}
|
||||
else {
|
||||
for(auto& c : bvhNode->components)
|
||||
c->obj->globalCollisionCheck = c->obj->collides_with(drawP.world.drawData.cam.c, checkAgainstWorld, checkAgainstCam, drawP.colliderAllocated);
|
||||
for(auto& p : bvhNode->children)
|
||||
traverse_bvh_collision_check_recursive(p, checkAgainstWorld, checkAgainstCam);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawingProgramCache::traverse_bvh_run_function(const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<BVHNode>& node, const std::vector<CollabListType::ObjectInfoPtr>& components)> f) {
|
||||
void DrawingProgramCache::traverse_bvh_run_function(const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node, const std::vector<CollabListType::ObjectInfoPtr>& components)> f) {
|
||||
f(nullptr, unsortedComponents);
|
||||
traverse_bvh_run_function_recursive(bvhRoot, aabb, f);
|
||||
}
|
||||
|
||||
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<CollabListType::ObjectInfoPtr>& components)> 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<CollabListType::ObjectInfoPtr>& 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);
|
||||
|
|
@ -162,7 +141,7 @@ void DrawingProgramCache::preupdate_component(const CollabListType::ObjectInfoPt
|
|||
invalidate_cache_before_pos(c->pos);
|
||||
}
|
||||
|
||||
void DrawingProgramCache::build_bvh_node(const std::shared_ptr<BVHNode>& bvhNode, std::vector<CollabListType::ObjectInfoPtr> components) {
|
||||
void DrawingProgramCache::build_bvh_node(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, std::vector<CollabListType::ObjectInfoPtr> components) {
|
||||
if(components.empty())
|
||||
return;
|
||||
|
||||
|
|
@ -201,11 +180,11 @@ void DrawingProgramCache::build_bvh_node(const std::shared_ptr<BVHNode>& bvhNode
|
|||
|
||||
for(auto& p : parts) {
|
||||
if(!p.empty())
|
||||
build_bvh_node(bvhNode->children.emplace_back(std::make_shared<BVHNode>()), p);
|
||||
build_bvh_node(bvhNode->children.emplace_back(std::make_shared<DrawingProgramCacheBVHNode>()), p);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawingProgramCache::build_bvh_node_coords_and_resolution(BVHNode& node) {
|
||||
void DrawingProgramCache::build_bvh_node_coords_and_resolution(DrawingProgramCacheBVHNode& 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
|
||||
|
|
@ -255,9 +234,9 @@ void DrawingProgramCache::invalidate_cache_at_aabb_before_pos(const SCollision::
|
|||
});
|
||||
}
|
||||
|
||||
void DrawingProgramCache::refresh_all_draw_cache(SkCanvas* canvas, const DrawData& drawData) {
|
||||
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<DrawingProgramCache::BVHNode> node, const std::vector<CollabListType::ObjectInfoPtr>& comps) {
|
||||
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<CollabListType::ObjectInfoPtr>& comps) {
|
||||
if(node && node->coords.inverseScale <= drawData.cam.c.inverseScale) {
|
||||
if(!node->drawCache)
|
||||
nodeFlatList.emplace_front(node);
|
||||
|
|
@ -269,12 +248,12 @@ void DrawingProgramCache::refresh_all_draw_cache(SkCanvas* canvas, const DrawDat
|
|||
refresh_draw_cache(nodeFlatList.front(), drawData);
|
||||
}
|
||||
|
||||
void DrawingProgramCache::refresh_draw_cache(const std::shared_ptr<BVHNode>& bvhNode, const DrawData& drawData) {
|
||||
void DrawingProgramCache::refresh_draw_cache(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const DrawData& drawData) {
|
||||
if(bvhNode->children.empty() && bvhNode->components.empty())
|
||||
return;
|
||||
|
||||
bvhNode->drawCache = std::nullopt;
|
||||
BVHNode::DrawCacheData drawCache;
|
||||
DrawingProgramCacheBVHNode::DrawCacheData drawCache;
|
||||
|
||||
SkImageInfo imgInfo = SkImageInfo::MakeN32Premul(bvhNode->resolution.x(), bvhNode->resolution.y());
|
||||
#ifdef USE_SKIA_BACKEND_GRAPHITE
|
||||
|
|
@ -296,15 +275,15 @@ void DrawingProgramCache::refresh_draw_cache(const std::shared_ptr<BVHNode>& bvh
|
|||
cacheDrawData.cam.set_viewing_area(bvhNode->resolution.cast<float>());
|
||||
cacheDrawData.refresh_draw_optimizing_values();
|
||||
|
||||
drawP.draw_components_to_canvas(cacheCanvas, cacheDrawData, false, &drawCache.lastDrawnComponentPlacement);
|
||||
draw_components_to_canvas(cacheCanvas, cacheDrawData, &drawCache.lastDrawnComponentPlacement);
|
||||
drawCache.lastRenderTime = std::chrono::steady_clock::now();
|
||||
drawCache.attachedDrawP = &drawP;
|
||||
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<BVHNode> leastUsedNode = *nodesWithCachedSurfaces.begin();
|
||||
for(const std::shared_ptr<BVHNode>& n : nodesWithCachedSurfaces) {
|
||||
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;
|
||||
}
|
||||
|
|
@ -314,3 +293,81 @@ void DrawingProgramCache::refresh_draw_cache(const std::shared_ptr<BVHNode>& bvh
|
|||
|
||||
nodesWithCachedSurfaces.emplace(bvhNode);
|
||||
}
|
||||
|
||||
void DrawingProgramCache::draw_components_to_canvas(SkCanvas* canvas, const DrawData& drawData, uint64_t* lastDrawnComponentPlacement) {
|
||||
uint64_t lastComponentDrawn = 0;
|
||||
|
||||
std::vector<std::shared_ptr<DrawingProgramCacheBVHNode>> cachedNodesToDraw;
|
||||
std::vector<CollabListType::ObjectInfoPtr> uncachedCompsToDraw;
|
||||
|
||||
traverse_bvh_run_function(drawData.cam.viewingAreaGenerousCollider, [&](const std::shared_ptr<DrawingProgramCacheBVHNode>& node, const std::vector<CollabListType::ObjectInfoPtr>& comps) {
|
||||
if(node && node->drawCache && node->coords.inverseScale <= drawData.cam.c.inverseScale) {
|
||||
cachedNodesToDraw.emplace_back(node);
|
||||
return false;
|
||||
}
|
||||
|
||||
for(auto& c : comps)
|
||||
c->obj->calculate_draw_transform(drawData);
|
||||
|
||||
uncachedCompsToDraw.insert(uncachedCompsToDraw.end(), comps.begin(), comps.end());
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
std::sort(uncachedCompsToDraw.begin(), uncachedCompsToDraw.end(), [](auto& a, auto& b) {
|
||||
return a->pos < b->pos;
|
||||
});
|
||||
std::sort(cachedNodesToDraw.begin(), cachedNodesToDraw.end(), [](auto& a, auto& b) {
|
||||
return a->drawCache.value().lastDrawnComponentPlacement < b->drawCache.value().lastDrawnComponentPlacement;
|
||||
});
|
||||
size_t nextCacheToRender = 0;
|
||||
for(auto& c : uncachedCompsToDraw) {
|
||||
for(;;) {
|
||||
if(nextCacheToRender >= cachedNodesToDraw.size() || c->pos < cachedNodesToDraw[nextCacheToRender]->drawCache.value().lastDrawnComponentPlacement)
|
||||
break;
|
||||
auto& bvhNode = cachedNodesToDraw[nextCacheToRender];
|
||||
auto& drawCache = bvhNode->drawCache.value();
|
||||
canvas->save();
|
||||
bvhNode->coords.transform_sk_canvas(canvas, drawData);
|
||||
drawCache.lastRenderTime = std::chrono::steady_clock::now();
|
||||
|
||||
lastComponentDrawn = std::max(lastComponentDrawn, drawCache.lastDrawnComponentPlacement);
|
||||
|
||||
SkPaint p;
|
||||
p.setBlendMode(SkBlendMode::kSrc);
|
||||
|
||||
// Note, no mipmaps here. You might need to generate mipmaps in the future (withDefaultMipmaps)
|
||||
canvas->drawImage(drawCache.surface->makeTemporaryImage(), 0, 0, {SkFilterMode::kLinear, SkMipmapMode::kLinear}, &p);
|
||||
|
||||
canvas->restore();
|
||||
nextCacheToRender++;
|
||||
}
|
||||
c->obj->draw(canvas, drawData);
|
||||
c->obj->drawSetupData.shouldDraw = false;
|
||||
lastComponentDrawn = std::max(lastComponentDrawn, c->pos);
|
||||
}
|
||||
for(;nextCacheToRender < cachedNodesToDraw.size(); nextCacheToRender++) {
|
||||
auto& bvhNode = cachedNodesToDraw[nextCacheToRender];
|
||||
auto& drawCache = bvhNode->drawCache.value();
|
||||
canvas->save();
|
||||
bvhNode->coords.transform_sk_canvas(canvas, drawData);
|
||||
drawCache.lastRenderTime = std::chrono::steady_clock::now();
|
||||
|
||||
lastComponentDrawn = std::max(lastComponentDrawn, drawCache.lastDrawnComponentPlacement);
|
||||
|
||||
SkPaint p;
|
||||
p.setBlendMode(SkBlendMode::kSrc);
|
||||
|
||||
// Note, no mipmaps here. You might need to generate mipmaps in the future (withDefaultMipmaps)
|
||||
canvas->drawImage(drawCache.surface->makeTemporaryImage(), 0, 0, {SkFilterMode::kLinear, SkMipmapMode::kLinear}, &p);
|
||||
|
||||
canvas->restore();
|
||||
}
|
||||
|
||||
if(lastDrawnComponentPlacement)
|
||||
*lastDrawnComponentPlacement = lastComponentDrawn;
|
||||
}
|
||||
|
||||
DrawingProgramCache::~DrawingProgramCache() {
|
||||
clear_own_cached_surfaces();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,81 +1,68 @@
|
|||
#pragma once
|
||||
#include <chrono>
|
||||
#include <vector>
|
||||
#include "../CollabList.hpp"
|
||||
#include <include/core/SkSurface.h>
|
||||
#include <unordered_set>
|
||||
#include "../DrawData.hpp"
|
||||
#include "../DrawComponents/DrawComponent.hpp"
|
||||
|
||||
class DrawingProgram;
|
||||
class DrawingProgramCache;
|
||||
|
||||
class DrawComponent;
|
||||
class DrawingProgramCacheBVHNode {
|
||||
public:
|
||||
SCollision::AABB<WorldScalar> bounds;
|
||||
std::vector<CollabListType::ObjectInfoPtr> components;
|
||||
std::vector<std::shared_ptr<DrawingProgramCacheBVHNode>> children;
|
||||
|
||||
CoordSpaceHelper coords;
|
||||
Vector2i resolution;
|
||||
struct DrawCacheData {
|
||||
uint64_t lastDrawnComponentPlacement;
|
||||
uint64_t firstDrawnComponentPlacement;
|
||||
sk_sp<SkSurface> surface;
|
||||
std::chrono::steady_clock::time_point lastRenderTime;
|
||||
DrawingProgramCache* attachedCache;
|
||||
};
|
||||
|
||||
std::optional<DrawCacheData> drawCache;
|
||||
};
|
||||
|
||||
class DrawingProgramCache {
|
||||
public:
|
||||
typedef CollabList<std::shared_ptr<DrawComponent>, ServerClientID> CollabListType;
|
||||
DrawingProgramCache(DrawingProgram& initDrawP);
|
||||
~DrawingProgramCache();
|
||||
|
||||
static std::unordered_set<std::shared_ptr<DrawingProgramCacheBVHNode>> nodesWithCachedSurfaces;
|
||||
bool disableRefresh = false;
|
||||
|
||||
void update();
|
||||
void erase_component(const CollabListType::ObjectInfoPtr& c);
|
||||
void add_component(const CollabListType::ObjectInfoPtr& c);
|
||||
void refresh_all_draw_cache(SkCanvas* canvas, const DrawData& drawData);
|
||||
|
||||
struct BVHNode {
|
||||
SCollision::AABB<WorldScalar> bounds;
|
||||
std::vector<CollabListType::ObjectInfoPtr> components;
|
||||
std::vector<std::shared_ptr<BVHNode>> children;
|
||||
|
||||
CoordSpaceHelper coords;
|
||||
Vector2i resolution;
|
||||
struct DrawCacheData {
|
||||
uint64_t lastDrawnComponentPlacement;
|
||||
uint64_t firstDrawnComponentPlacement;
|
||||
sk_sp<SkSurface> surface;
|
||||
std::chrono::steady_clock::time_point lastRenderTime;
|
||||
DrawingProgram* attachedDrawP;
|
||||
};
|
||||
|
||||
std::optional<DrawCacheData> drawCache;
|
||||
};
|
||||
|
||||
static std::unordered_set<std::shared_ptr<BVHNode>> nodesWithCachedSurfaces;
|
||||
|
||||
bool disableRefresh = false;
|
||||
|
||||
void traverse_bvh_run_function(const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<BVHNode>& node, const std::vector<CollabListType::ObjectInfoPtr>& components)> f);
|
||||
void traverse_bvh_collision_check(const SCollision::ColliderCollection<WorldScalar>& checkAgainstWorld, const SCollision::ColliderCollection<float>& checkAgainstCam);
|
||||
|
||||
void traverse_bvh_erase_function(const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<BVHNode>& node, std::vector<CollabListType::ObjectInfoPtr>& components)> f);
|
||||
|
||||
void force_rebuild();
|
||||
|
||||
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<CollabListType::ObjectInfoPtr>& components)> f);
|
||||
void traverse_bvh_erase_function(const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node, std::vector<CollabListType::ObjectInfoPtr>& components)> f);
|
||||
void force_rebuild(const std::vector<CollabListType::ObjectInfoPtr>& componentsToBuild);
|
||||
// Run before actually updating the component
|
||||
void preupdate_component(const CollabListType::ObjectInfoPtr& c);
|
||||
|
||||
void invalidate_cache_before_pos(uint64_t placementToInvalidateAt);
|
||||
void invalidate_cache_at_aabb_before_pos(const SCollision::AABB<WorldScalar>& aabb, uint64_t placementToInvalidateAt);
|
||||
private:
|
||||
void clear();
|
||||
void draw_components_to_canvas(SkCanvas* canvas, const DrawData& drawData, uint64_t* lastDrawnComponentPlacement = nullptr);
|
||||
const std::vector<CollabListType::ObjectInfoPtr>& get_unsorted_component_list() const;
|
||||
const std::chrono::steady_clock::time_point& get_last_bvh_build_time() const;
|
||||
void clear_own_cached_surfaces();
|
||||
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<CollabListType::ObjectInfoPtr>& components)> f);
|
||||
|
||||
void traverse_bvh_collision_check_recursive(const std::shared_ptr<BVHNode>& bvhNode, const SCollision::ColliderCollection<WorldScalar>& checkAgainstWorld, const SCollision::ColliderCollection<float>& checkAgainstCam);
|
||||
void traverse_bvh_collision_check_recursive_tiny(const std::shared_ptr<BVHNode>& bvhNode);
|
||||
|
||||
void build_bvh_node_coords_and_resolution(BVHNode& node);
|
||||
void refresh_draw_cache(const std::shared_ptr<BVHNode>& bvhNode, const DrawData& drawData);
|
||||
|
||||
private:
|
||||
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<CollabListType::ObjectInfoPtr>& 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<CollabListType::ObjectInfoPtr> 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<CollabListType::ObjectInfoPtr>& components)> f);
|
||||
std::pair<std::shared_ptr<DrawingProgramCacheBVHNode>, std::vector<CollabListType::ObjectInfoPtr>::iterator> get_bvh_node_fully_containing_recursive(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, DrawComponent* c);
|
||||
void build_bvh_node(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, std::vector<CollabListType::ObjectInfoPtr> components);
|
||||
|
||||
std::chrono::steady_clock::time_point lastBvhBuildTime;
|
||||
|
||||
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<CollabListType::ObjectInfoPtr>& components)> f);
|
||||
std::pair<std::shared_ptr<DrawingProgramCache::BVHNode>, std::vector<DrawingProgramCache::CollabListType::ObjectInfoPtr>::iterator> get_bvh_node_fully_containing_recursive(const std::shared_ptr<BVHNode>& bvhNode, DrawComponent* c);
|
||||
|
||||
void build_bvh_node(const std::shared_ptr<BVHNode>& bvhNode, std::vector<CollabListType::ObjectInfoPtr> components);
|
||||
|
||||
std::shared_ptr<BVHNode> bvhRoot;
|
||||
std::shared_ptr<DrawingProgramCacheBVHNode> bvhRoot;
|
||||
std::vector<CollabListType::ObjectInfoPtr> unsortedComponents;
|
||||
|
||||
std::unordered_set<CollabListType::ObjectInfoPtr> componentsSetToErase;
|
||||
|
||||
DrawingProgram& drawP;
|
||||
};
|
||||
|
|
|
|||
119
src/DrawingProgram/DrawingProgramSelection.cpp
Normal file
119
src/DrawingProgram/DrawingProgramSelection.cpp
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
#include "DrawingProgramSelection.hpp"
|
||||
#include "DrawingProgram.hpp"
|
||||
#include "../World.hpp"
|
||||
#include "../MainProgram.hpp"
|
||||
|
||||
DrawingProgramSelection::DrawingProgramSelection(DrawingProgram& initDrawP):
|
||||
cache(initDrawP),
|
||||
drawP(initDrawP)
|
||||
{}
|
||||
|
||||
void DrawingProgramSelection::add_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection<float>& cC) {
|
||||
auto cCWorld = drawP.world.drawData.cam.c.collider_to_world<SCollision::ColliderCollection<WorldScalar>, SCollision::ColliderCollection<float>>(cC);
|
||||
|
||||
drawP.compCache.disableRefresh = true;
|
||||
|
||||
std::unordered_set<CollabListType::ObjectInfoPtr> selectedComponents;
|
||||
|
||||
std::function<void(const std::shared_ptr<DrawingProgramCacheBVHNode>&)> fullyCollidedFunc = [&](const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode) {
|
||||
if(bvhNode) {
|
||||
for(auto& c : bvhNode->components)
|
||||
selectedComponents.emplace(c);
|
||||
for(auto& p : bvhNode->children)
|
||||
fullyCollidedFunc(p);
|
||||
}
|
||||
};
|
||||
drawP.compCache.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()))) {
|
||||
fullyCollidedFunc(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, drawP.colliderAllocated)) {
|
||||
selectedComponents.emplace(c);
|
||||
drawP.compCache.invalidate_cache_at_aabb_before_pos(c->obj->worldAABB.value(), c->obj->collabListInfo.lock()->pos);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
selectedSet = selectedComponents;
|
||||
for(auto& obj : selectedSet)
|
||||
cache.add_component(obj);
|
||||
calculate_aabb();
|
||||
}
|
||||
|
||||
bool DrawingProgramSelection::is_something_selected() {
|
||||
return !selectedSet.empty();
|
||||
}
|
||||
|
||||
void DrawingProgramSelection::calculate_aabb() {
|
||||
if(is_something_selected()) {
|
||||
selectionAABB = (*selectedSet.begin())->obj->worldAABB.value();
|
||||
for(auto& c : selectedSet)
|
||||
selectionAABB.include_aabb_in_bounds(c->obj->worldAABB.value());
|
||||
}
|
||||
}
|
||||
|
||||
void DrawingProgramSelection::deselect_all() {
|
||||
for(auto& obj : selectedSet) {
|
||||
obj->obj->coords = selectionTransformCoords.other_coord_space_from_this_space(obj->obj->coords);
|
||||
obj->obj->final_update(drawP);
|
||||
}
|
||||
selectedSet.clear();
|
||||
cache.clear();
|
||||
selectionTransformCoords = CoordSpaceHelper();
|
||||
drawP.compCache.force_rebuild(drawP.components.client_list());
|
||||
}
|
||||
|
||||
void DrawingProgramSelection::update() {
|
||||
if(is_something_selected()) {
|
||||
if(cache.get_unsorted_component_list().size() >= 1000 && (std::chrono::steady_clock::now() - cache.get_last_bvh_build_time()) >= std::chrono::seconds(5))
|
||||
cache.force_rebuild(std::vector<CollabListType::ObjectInfoPtr>(selectedSet.begin(), selectedSet.end()));
|
||||
if(drawP.world.main.input.key(InputManager::KEY_DRAW_UNSELECT).pressed)
|
||||
deselect_all();
|
||||
if(drawP.world.main.input.key(InputManager::KEY_TEXT_UP).held)
|
||||
selectionTransformCoords.translate(WorldVec{0, -drawP.world.drawData.cam.c.inverseScale});
|
||||
if(drawP.world.main.input.key(InputManager::KEY_TEXT_DOWN).held)
|
||||
selectionTransformCoords.translate(WorldVec{0, drawP.world.drawData.cam.c.inverseScale});
|
||||
if(drawP.world.main.input.key(InputManager::KEY_TEXT_LEFT).held)
|
||||
selectionTransformCoords.translate(WorldVec{-drawP.world.drawData.cam.c.inverseScale, 0});
|
||||
if(drawP.world.main.input.key(InputManager::KEY_TEXT_RIGHT).held)
|
||||
selectionTransformCoords.translate(WorldVec{drawP.world.drawData.cam.c.inverseScale, 0});
|
||||
if(drawP.world.main.input.key(InputManager::KEY_TEXT_CTRL).held)
|
||||
selectionTransformCoords.scale_about_double(selectionTransformCoords.pos, 1.01);
|
||||
if(drawP.world.main.input.key(InputManager::KEY_TEXT_SHIFT).held)
|
||||
selectionTransformCoords.scale_about_double(selectionTransformCoords.pos, 0.99);
|
||||
}
|
||||
}
|
||||
|
||||
bool DrawingProgramSelection::mouse_collided_with_selection_aabb() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void DrawingProgramSelection::draw(SkCanvas* canvas, const DrawData& drawData) {
|
||||
if(is_something_selected()) {
|
||||
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);
|
||||
cache.draw_components_to_canvas(canvas, selectionDrawData);
|
||||
|
||||
canvas->save();
|
||||
selectionTransformCoords.transform_sk_canvas(canvas, selectionDrawData);
|
||||
Vector2f minRectPos = selectionTransformCoords.to_space(selectionAABB.min);
|
||||
Vector2f maxRectPos = selectionTransformCoords.to_space(selectionAABB.max);
|
||||
SkPaint p{SkColor4f{0.0f, 0.0f, 1.0f, 0.3f}};
|
||||
canvas->drawRect(SkRect::MakeLTRB(minRectPos.x(), minRectPos.y(), maxRectPos.x(), maxRectPos.y()), p);
|
||||
canvas->restore();
|
||||
}
|
||||
}
|
||||
23
src/DrawingProgram/DrawingProgramSelection.hpp
Normal file
23
src/DrawingProgram/DrawingProgramSelection.hpp
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
#include "DrawingProgramCache.hpp"
|
||||
|
||||
class DrawingProgram;
|
||||
|
||||
class DrawingProgramSelection {
|
||||
public:
|
||||
DrawingProgramSelection(DrawingProgram& initDrawP);
|
||||
void add_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection<float>& cC);
|
||||
void deselect_all();
|
||||
void update();
|
||||
void draw(SkCanvas* canvas, const DrawData& drawData);
|
||||
bool is_something_selected();
|
||||
private:
|
||||
void calculate_aabb();
|
||||
bool mouse_collided_with_selection_aabb();
|
||||
|
||||
SCollision::AABB<WorldScalar> selectionAABB;
|
||||
CoordSpaceHelper selectionTransformCoords;
|
||||
std::unordered_set<CollabListType::ObjectInfoPtr> selectedSet;
|
||||
DrawingProgramCache cache;
|
||||
DrawingProgram& drawP;
|
||||
};
|
||||
|
|
@ -123,36 +123,49 @@ void EditTool::edit_start(const std::shared_ptr<DrawComponent>& comp) {
|
|||
}
|
||||
|
||||
void EditTool::tool_update() {
|
||||
using namespace SCollision;
|
||||
ColliderCollection<float> ci;
|
||||
ci.circle.emplace_back(Circle<float>{drawP.world.main.input.mouse.pos, 1.0f});
|
||||
ci.recalculate_bounds();
|
||||
|
||||
if(!controls.isEditing) {
|
||||
SCollision::AABB<WorldScalar> mouseAABB{drawP.world.get_mouse_world_pos() - WorldVec{0.5f, 0.5f}, drawP.world.get_mouse_world_pos() + WorldVec{0.5f, 0.5f}};
|
||||
SCollision::ColliderCollection<WorldScalar> cMouseAABB;
|
||||
cMouseAABB.aabb.emplace_back(mouseAABB);
|
||||
cMouseAABB.recalculate_bounds();
|
||||
|
||||
if(drawP.controls.leftClick) {
|
||||
drawP.check_all_collisions_transform(ci);
|
||||
for(auto& comp : drawP.components.client_list() | std::views::reverse) {
|
||||
if(comp->obj->globalCollisionCheck) {
|
||||
edit_start(comp->obj);
|
||||
break;
|
||||
CollabListType::ObjectInfoPtr lastSelectedObj;
|
||||
uint64_t lastPos = 0;
|
||||
|
||||
drawP.compCache.traverse_bvh_run_function(mouseAABB, [&](const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const std::vector<CollabListType::ObjectInfoPtr>& components) {
|
||||
for(auto& c : components) {
|
||||
if(c->pos > lastPos && c->obj->collides_with_world_coords(drawP.world.drawData.cam.c, cMouseAABB, drawP.colliderAllocated)) {
|
||||
lastSelectedObj = c;
|
||||
lastPos = c->pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if(lastSelectedObj)
|
||||
edit_start(lastSelectedObj->obj);
|
||||
}
|
||||
}
|
||||
else {
|
||||
SCollision::Circle<float> mouseCircle{drawP.world.main.input.mouse.pos, 1.0f};
|
||||
SCollision::ColliderCollection<float> cMouseCircle;
|
||||
cMouseCircle.circle.emplace_back(mouseCircle);
|
||||
cMouseCircle.recalculate_bounds();
|
||||
|
||||
std::shared_ptr<DrawComponent> a = controls.compToEdit.lock();
|
||||
if(a) {
|
||||
bool isMovingPoint = false;
|
||||
bool clickedAway = false;
|
||||
if(!controls.pointDragging && drawP.controls.leftClick) {
|
||||
for(HandleData& h : controls.pointHandles) {
|
||||
if(SCollision::collide(ci, Circle<float>(drawP.world.drawData.cam.c.to_space(a->coords.from_space(*h.p)), DRAG_POINT_RADIUS))) {
|
||||
if(SCollision::collide(mouseCircle, SCollision::Circle<float>(drawP.world.drawData.cam.c.to_space(a->coords.from_space(*h.p)), DRAG_POINT_RADIUS))) {
|
||||
controls.pointDragging = &h;
|
||||
isMovingPoint = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!isMovingPoint && !a->collides_with_cam_coords(drawP.world.drawData.cam.c, ci, drawP.colliderAllocated))
|
||||
if(!isMovingPoint && !a->collides_with_cam_coords(drawP.world.drawData.cam.c, cMouseCircle, drawP.colliderAllocated))
|
||||
clickedAway = true;
|
||||
}
|
||||
else if(controls.pointDragging) {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ void EraserTool::reset_tool() {
|
|||
|
||||
drawP.world.undo.push(UndoManager::UndoRedoPair{
|
||||
[&, erasedCompVal = erasedComponents]() {
|
||||
std::vector<DrawingProgram::CollabListType::ObjectInfoPtr> sortedObjects(erasedCompVal.begin(), erasedCompVal.end());
|
||||
std::vector<CollabListType::ObjectInfoPtr> sortedObjects(erasedCompVal.begin(), erasedCompVal.end());
|
||||
std::sort(sortedObjects.begin(), sortedObjects.end(), [](auto& a, auto& b) {
|
||||
return a->pos < b->pos;
|
||||
});
|
||||
|
|
@ -61,7 +61,7 @@ void EraserTool::reset_tool() {
|
|||
|
||||
drawP.reset_tools();
|
||||
|
||||
drawP.compCache.force_rebuild();
|
||||
drawP.compCache.force_rebuild(drawP.components.client_list());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -97,10 +97,7 @@ void EraserTool::tool_update() {
|
|||
std::erase_if(comps, [&](auto& c) {
|
||||
if(c->obj->collides_with(drawP.world.drawData.cam.c, cCWorld, cC, drawP.colliderAllocated)) {
|
||||
erasedComponents.emplace(c);
|
||||
if(c->obj->worldAABB)
|
||||
drawP.compCache.invalidate_cache_at_aabb_before_pos(c->obj->worldAABB.value(), c->obj->collabListInfo.lock()->pos);
|
||||
else
|
||||
drawP.compCache.invalidate_cache_before_pos(c->obj->collabListInfo.lock()->pos);
|
||||
drawP.compCache.invalidate_cache_at_aabb_before_pos(c->obj->worldAABB.value(), c->obj->collabListInfo.lock()->pos);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -112,7 +109,7 @@ void EraserTool::tool_update() {
|
|||
reset_tool();
|
||||
}
|
||||
|
||||
void EraserTool::move_erased_components_from_bvh_nodes_recursive(const std::shared_ptr<DrawingProgramCache::BVHNode>& bvhNode) {
|
||||
void EraserTool::move_erased_components_from_bvh_nodes_recursive(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode) {
|
||||
for(auto& c : bvhNode->components)
|
||||
erasedComponents.emplace(c);
|
||||
for(auto& p : bvhNode->children)
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ class EraserTool {
|
|||
void reset_tool();
|
||||
void draw(SkCanvas* canvas, const DrawData& drawData);
|
||||
private:
|
||||
std::unordered_set<CollabList<std::shared_ptr<DrawComponent>, ServerClientID>::ObjectInfoPtr> erasedComponents;
|
||||
std::unordered_set<std::shared_ptr<DrawingProgramCache::BVHNode>> erasedBVHNodes;
|
||||
std::unordered_set<CollabListType::ObjectInfoPtr> erasedComponents;
|
||||
std::unordered_set<std::shared_ptr<DrawingProgramCacheBVHNode>> erasedBVHNodes;
|
||||
|
||||
void move_erased_components_from_bvh_nodes_recursive(const std::shared_ptr<DrawingProgramCache::BVHNode>& bvhNode);
|
||||
void move_erased_components_from_bvh_nodes_recursive(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode);
|
||||
|
||||
DrawingProgram& drawP;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -13,6 +13,13 @@
|
|||
#include <algorithm>
|
||||
#include <ranges>
|
||||
|
||||
#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
|
||||
|
||||
#define ROTATION_POINT_RADIUS_MULTIPLIER 0.7
|
||||
#define ROTATION_POINTS_DISTANCE 20.0
|
||||
|
||||
|
|
@ -26,19 +33,17 @@ void RectSelectTool::gui_toolbox() {
|
|||
}
|
||||
|
||||
void RectSelectTool::reset_tool() {
|
||||
if(controls.selectionMode == 3)
|
||||
commit_transformation();
|
||||
controls = RectSelectControls();
|
||||
}
|
||||
|
||||
void RectSelectTool::tool_update() {
|
||||
switch(controls.selectionMode) {
|
||||
case 0: {
|
||||
drawP.compCache.disableRefresh = false;
|
||||
if(drawP.controls.leftClick) {
|
||||
reset_tool();
|
||||
controls.coords = drawP.world.drawData.cam.c;
|
||||
controls.selectStartAt = controls.coords.get_mouse_pos(drawP.world);
|
||||
controls.rotationPointAngle = 0.0;
|
||||
controls.selectionMode = 1;
|
||||
}
|
||||
break;
|
||||
|
|
@ -50,349 +55,31 @@ void RectSelectTool::tool_update() {
|
|||
Vector2f newP2 = cwise_vec_max(controls.selectStartAt, mPos);
|
||||
controls.newT = triangle_from_rect_points(newP1, newP2);
|
||||
if(!drawP.controls.leftClickHeld) {
|
||||
ColliderCollection<float> selectionCollection;
|
||||
selectionCollection.triangle.emplace_back(controls.newT[0], controls.newT[1], controls.newT[2]);
|
||||
selectionCollection.triangle.emplace_back(controls.newT[2], controls.newT[3], controls.newT[0]);
|
||||
drawP.check_all_collisions_transform(selectionCollection);
|
||||
for(auto& c : drawP.components.client_list())
|
||||
c->obj->selected = c->obj->globalCollisionCheck;
|
||||
controls.selectionMode = create_selection_aabb() ? 2 : 0;
|
||||
controls.rotationCenter = controls.finalSelectionAABB.center();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
Vector2f mPos = drawP.world.main.input.mouse.pos;
|
||||
bool copyHit = drawP.world.main.input.key(InputManager::KEY_COPY).pressed;
|
||||
bool cutHit = drawP.world.main.input.key(InputManager::KEY_CUT).pressed;
|
||||
bool deleteHit = drawP.world.main.input.key(InputManager::KEY_DRAW_DELETE).pressed;
|
||||
bool unselectHit = drawP.world.main.input.key(InputManager::KEY_DRAW_UNSELECT).pressed;
|
||||
ColliderCollection<float> cC;
|
||||
cC.triangle.emplace_back(controls.newT[0], controls.newT[1], controls.newT[2]);
|
||||
cC.triangle.emplace_back(controls.newT[2], controls.newT[3], controls.newT[0]);
|
||||
|
||||
if(drawP.controls.leftClick) {
|
||||
if(SCollision::collide(mPos, controls.scaleSelectionCircle)) {
|
||||
controls.scaleStartAt = controls.finalSelectionAABB.max;
|
||||
controls.scaleCenterPoint = controls.finalSelectionAABB.center();
|
||||
set_selected_items();
|
||||
controls.selectionMode = 4;
|
||||
}
|
||||
else if(SCollision::collide(mPos, controls.rotationCenterCircle)) {
|
||||
controls.moveStartAt = controls.rotationCenter;
|
||||
controls.selectionMode = 5;
|
||||
}
|
||||
else if(SCollision::collide(mPos, controls.rotationPointCircle)) {
|
||||
set_selected_items();
|
||||
controls.selectionMode = 6;
|
||||
}
|
||||
else if(SCollision::collide(drawP.controls.currentMouseWorldPos, controls.finalSelectionAABB)) {
|
||||
controls.moveStartAt = drawP.controls.currentMouseWorldPos;
|
||||
set_selected_items();
|
||||
controls.selectionMode = 3;
|
||||
}
|
||||
else {
|
||||
controls.selectionMode = 0;
|
||||
drawP.reset_selection();
|
||||
}
|
||||
}
|
||||
else if(deleteHit || cutHit) {
|
||||
selection_to_clipboard();
|
||||
std::vector<std::pair<uint64_t, std::shared_ptr<DrawComponent>>> erasedCompVal; // <placement order, object>
|
||||
drawP.components.client_erase_if([&](uint64_t oldPlacement, const auto& c) {
|
||||
if(c->obj->selected) {
|
||||
erasedCompVal.emplace_back(std::pair<uint64_t, std::shared_ptr<DrawComponent>>{oldPlacement, c->obj});
|
||||
c->obj->client_send_erase(drawP);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
drawP.world.undo.push(UndoManager::UndoRedoPair{
|
||||
[&, erasedCompVal]() {
|
||||
for(auto& comp : erasedCompVal)
|
||||
if(drawP.components.get_id(comp.second) != ServerClientID{0, 0})
|
||||
return false;
|
||||
drawP.selection.add_from_cam_coord_collider_to_selection(cC);
|
||||
|
||||
for(auto& comp : erasedCompVal | std::views::reverse) {
|
||||
drawP.components.client_insert(comp.first, comp.second);
|
||||
comp.second->client_send_place(drawP);
|
||||
}
|
||||
drawP.reset_tools();
|
||||
return true;
|
||||
},
|
||||
[&, erasedCompVal]() {
|
||||
for(auto& comp : erasedCompVal)
|
||||
if(drawP.components.get_id(comp.second) == ServerClientID{0, 0})
|
||||
return false;
|
||||
|
||||
for(auto& comp : erasedCompVal) {
|
||||
ServerClientID compID;
|
||||
comp.second->client_send_erase(drawP);
|
||||
drawP.components.client_erase(comp.second, compID);
|
||||
}
|
||||
drawP.reset_tools();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
controls.selectionMode = 0;
|
||||
}
|
||||
else if(copyHit)
|
||||
selection_to_clipboard();
|
||||
else if(unselectHit) {
|
||||
drawP.reset_selection();
|
||||
controls.selectionMode = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
WorldVec translationVec = drawP.controls.currentMouseWorldPos - controls.moveStartAt;
|
||||
if(drawP.controls.leftClickHeld) {
|
||||
for(auto& item : controls.selectedItems) {
|
||||
auto newCoords = item.oldCoords;
|
||||
newCoords.translate(translationVec);
|
||||
item.comp->coords = newCoords;
|
||||
item.comp->transform_temp_update(drawP);
|
||||
item.comp->client_send_transform_temp(drawP);
|
||||
item.comp->lastTransformTime = std::chrono::steady_clock::now();
|
||||
}
|
||||
}
|
||||
else {
|
||||
commit_transformation();
|
||||
controls.selectionMode = create_selection_aabb() ? 2 : 0;
|
||||
controls.rotationCenter = controls.finalSelectionAABB.center();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
WorldVec centerToScaleStart = controls.scaleStartAt - controls.scaleCenterPoint;
|
||||
Vector2f centerToScaleStartInCamSpace = drawP.world.drawData.cam.c.normalized_dir_to_space(centerToScaleStart.normalized());
|
||||
Vector2f scaleCenterPointInCamSpace = drawP.world.drawData.cam.c.to_space(controls.scaleCenterPoint);
|
||||
controls.scaleCurrentPoint = drawP.world.drawData.cam.c.from_space(project_point_on_line(drawP.world.main.input.mouse.pos, scaleCenterPointInCamSpace, (scaleCenterPointInCamSpace + centerToScaleStartInCamSpace).eval()));
|
||||
|
||||
WorldVec centerToScaleCurrent = controls.scaleCurrentPoint - controls.scaleCenterPoint;
|
||||
|
||||
WorldScalar centerToScaleStartNorm = centerToScaleStart.norm();
|
||||
WorldScalar centerToScaleCurrentNorm = centerToScaleCurrent.norm();
|
||||
bool isAnyNumberZero = centerToScaleCurrentNorm == WorldScalar(0) || centerToScaleStartNorm == WorldScalar(0);
|
||||
if(!isAnyNumberZero) {
|
||||
bool flipScale = centerToScaleStartNorm < centerToScaleCurrentNorm;
|
||||
WorldScalar scaleAmount;
|
||||
if(flipScale)
|
||||
scaleAmount = centerToScaleCurrentNorm / centerToScaleStartNorm;
|
||||
else
|
||||
scaleAmount = centerToScaleStartNorm / centerToScaleCurrentNorm;
|
||||
|
||||
if(drawP.controls.leftClickHeld) {
|
||||
for(auto& item : controls.selectedItems) {
|
||||
auto newCoords = item.oldCoords;
|
||||
newCoords.scale_about(controls.scaleCenterPoint, scaleAmount, flipScale);
|
||||
item.comp->coords = newCoords;
|
||||
item.comp->client_send_transform_temp(drawP);
|
||||
item.comp->transform_temp_update(drawP);
|
||||
item.comp->lastTransformTime = std::chrono::steady_clock::now();
|
||||
}
|
||||
}
|
||||
else {
|
||||
commit_transformation();
|
||||
controls.selectionMode = create_selection_aabb() ? 2 : 0;
|
||||
controls.rotationCenter = controls.finalSelectionAABB.center();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
controls.rotationCenter = drawP.controls.currentMouseWorldPos;
|
||||
if(!drawP.controls.leftClickHeld) {
|
||||
controls.selectionMode = 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
Vector2f rotationPointDiff = drawP.world.main.input.mouse.pos - drawP.world.drawData.cam.c.to_space(controls.rotationCenter);
|
||||
controls.rotationPointAngle = std::atan2(rotationPointDiff.y(), rotationPointDiff.x());
|
||||
if(drawP.controls.leftClickHeld) {
|
||||
for(auto& item : controls.selectedItems) {
|
||||
auto newCoords = item.oldCoords;
|
||||
newCoords.rotate_about(controls.rotationCenter, controls.rotationPointAngle);
|
||||
item.comp->coords = newCoords;
|
||||
item.comp->transform_temp_update(drawP);
|
||||
item.comp->client_send_transform_temp(drawP);
|
||||
item.comp->lastTransformTime = std::chrono::steady_clock::now();
|
||||
}
|
||||
}
|
||||
else {
|
||||
commit_transformation();
|
||||
controls.rotationPointAngle = 0.0;
|
||||
controls.selectionMode = create_selection_aabb() ? 2 : 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
update_drag_circles();
|
||||
}
|
||||
|
||||
void RectSelectTool::paste_clipboard() {
|
||||
auto& clipboard = drawP.world.main.clipboard;
|
||||
if(!clipboard.components.empty()) {
|
||||
drawP.reset_tools();
|
||||
drawP.controls.selectedTool = DrawingProgram::TOOL_RECTSELECT;
|
||||
drawP.controls.previousSelected = DrawingProgram::TOOL_RECTSELECT;
|
||||
uint64_t allPlacement = drawP.components.client_list().size();
|
||||
std::vector<std::shared_ptr<DrawComponent>> placedComponents;
|
||||
WorldVec mousePos = drawP.world.get_mouse_world_pos();
|
||||
WorldVec moveVec = clipboard.pos - mousePos;
|
||||
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) {
|
||||
uint64_t placement = drawP.components.client_list().size();
|
||||
std::shared_ptr<DrawComponent> newC = c->copy();
|
||||
newC->remap_resource_ids(resourceRemapIDs);
|
||||
newC->coords.translate(-moveVec);
|
||||
newC->selected = true;
|
||||
|
||||
drawP.components.client_insert(placement, newC);
|
||||
newC->final_update(drawP);
|
||||
newC->client_send_place(drawP);
|
||||
placedComponents.emplace_back(newC);
|
||||
}
|
||||
set_selected_items();
|
||||
create_selection_aabb();
|
||||
controls.rotationCenter = controls.finalSelectionAABB.center();
|
||||
controls.selectionMode = 2;
|
||||
drawP.add_undo_place_components(allPlacement, placedComponents);
|
||||
}
|
||||
}
|
||||
|
||||
void RectSelectTool::selection_to_clipboard() {
|
||||
auto& clipboard = drawP.world.main.clipboard;
|
||||
clipboard.components.clear();
|
||||
std::unordered_set<ServerClientID> resourceSet;
|
||||
for(auto& c : drawP.components.client_list()) {
|
||||
if(c->obj->selected) {
|
||||
c->obj->get_used_resources(resourceSet);
|
||||
clipboard.components.emplace_back(c->obj->copy());
|
||||
}
|
||||
}
|
||||
drawP.world.rMan.copy_resource_set_to_map(resourceSet, clipboard.resources);
|
||||
clipboard.pos = controls.finalSelectionAABB.center();
|
||||
}
|
||||
|
||||
Vector2f RectSelectTool::get_rotation_point_pos_from_angle(double angle) {
|
||||
return Vector2f{drawP.world.drawData.cam.c.to_space(controls.rotationCenter) + ROTATION_POINTS_DISTANCE * Vector2f{std::cos(angle), std::sin(angle)}};
|
||||
}
|
||||
|
||||
void RectSelectTool::update_drag_circles() {
|
||||
controls.scaleSelectionCircle = SCollision::Circle<float>(drawP.world.drawData.cam.c.to_space(controls.scaleCurrentPoint), DRAG_POINT_RADIUS);
|
||||
controls.rotationCenterCircle = SCollision::Circle<float>(drawP.world.drawData.cam.c.to_space(controls.rotationCenter), DRAG_POINT_RADIUS);
|
||||
controls.rotationPoint = get_rotation_point_pos_from_angle(controls.rotationPointAngle);
|
||||
controls.rotationPointCircle = SCollision::Circle<float>(controls.rotationPoint, ROTATION_POINT_RADIUS_MULTIPLIER * DRAG_POINT_RADIUS);
|
||||
}
|
||||
|
||||
bool RectSelectTool::create_selection_aabb() {
|
||||
controls.coords = drawP.world.drawData.cam.c;
|
||||
|
||||
controls.finalSelectionAABB = SCollision::AABB<WorldScalar>();
|
||||
bool first = true;
|
||||
for(auto& c : drawP.components.client_list()) {
|
||||
if(c->obj->selected && c->obj->get_world_bounds()) {
|
||||
if(first) {
|
||||
controls.finalSelectionAABB = *c->obj->get_world_bounds();
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
controls.finalSelectionAABB.include_aabb_in_bounds(*c->obj->get_world_bounds());
|
||||
}
|
||||
}
|
||||
|
||||
controls.scaleCurrentPoint = controls.finalSelectionAABB.max;
|
||||
|
||||
return !first;
|
||||
}
|
||||
|
||||
void RectSelectTool::set_selected_items() {
|
||||
controls.selectedItems.clear();
|
||||
for(auto& c : drawP.components.client_list()) {
|
||||
if(c->obj->selected) {
|
||||
controls.selectedItems.emplace_back(c->obj, c->id, c->obj->coords);
|
||||
c->obj->lastTransformTime = std::chrono::steady_clock::now();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RectSelectTool::commit_transformation() {
|
||||
std::vector<CoordSpaceHelper> finalCoords;
|
||||
for(auto& item : controls.selectedItems) {
|
||||
item.comp->client_send_transform_final(drawP);
|
||||
item.comp->finalize_update(drawP);
|
||||
finalCoords.emplace_back(item.comp->coords);
|
||||
}
|
||||
|
||||
auto sItems = controls.selectedItems;
|
||||
|
||||
drawP.world.undo.push(UndoManager::UndoRedoPair{
|
||||
[&, sItems, finalCoords]() {
|
||||
for(auto& item : sItems) {
|
||||
item.comp->coords = item.oldCoords;
|
||||
item.comp->client_send_transform_final(drawP);
|
||||
item.comp->finalize_update(drawP);
|
||||
}
|
||||
drawP.reset_tools();
|
||||
return true;
|
||||
},
|
||||
[&, sItems, finalCoords]() {
|
||||
for(auto [item, finalCoord] : std::views::zip(sItems, finalCoords)) {
|
||||
item.comp->coords = finalCoord;
|
||||
item.comp->client_send_transform_final(drawP);
|
||||
item.comp->finalize_update(drawP);
|
||||
}
|
||||
drawP.reset_tools();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void RectSelectTool::draw(SkCanvas* canvas, const DrawData& drawData) {
|
||||
if(controls.selectionMode != 0) {
|
||||
if(controls.selectionMode == 1) {
|
||||
canvas->save();
|
||||
controls.coords.transform_sk_canvas(canvas, drawData);
|
||||
|
||||
SkPaint selectionPaint;
|
||||
selectionPaint.setColor4f({0.3f, 0.6f, 0.9f, 0.4f});
|
||||
|
||||
if(controls.selectionMode == 1) {
|
||||
SkRect r = SkRect::MakeLTRB(controls.newT[0].x(), controls.newT[0].y(), controls.newT[2].x(), controls.newT[2].y());
|
||||
canvas->drawRect(r, selectionPaint);
|
||||
canvas->restore();
|
||||
}
|
||||
if(controls.selectionMode == 2 || controls.selectionMode == 5) {
|
||||
auto triArray = triangle_from_rect_points(controls.finalSelectionAABB.min, controls.finalSelectionAABB.max);
|
||||
std::array<Vector2f, 4> triArray2;
|
||||
triArray2[0] = controls.coords.to_space(controls.finalSelectionAABB.min);
|
||||
triArray2[1] = controls.coords.to_space(controls.finalSelectionAABB.top_right());
|
||||
triArray2[2] = controls.coords.to_space(controls.finalSelectionAABB.bottom_left());
|
||||
triArray2[3] = controls.coords.to_space(controls.finalSelectionAABB.max);
|
||||
SkPath a;
|
||||
a.moveTo(triArray2[0].x(), triArray2[0].y());
|
||||
a.lineTo(triArray2[1].x(), triArray2[1].y());
|
||||
a.lineTo(triArray2[2].x(), triArray2[2].y());
|
||||
a.close();
|
||||
a.moveTo(triArray2[1].x(), triArray2[1].y());
|
||||
a.lineTo(triArray2[2].x(), triArray2[2].y());
|
||||
a.lineTo(triArray2[3].x(), triArray2[3].y());
|
||||
a.close();
|
||||
canvas->drawPath(a, selectionPaint);
|
||||
// SkPaint rotateCirclePaint(SkColor4f{0.9f, 0.5f, 0.1f, 1.0f});
|
||||
}
|
||||
|
||||
SkRect r = SkRect::MakeLTRB(controls.newT[0].x(), controls.newT[0].y(), controls.newT[2].x(), controls.newT[2].y());
|
||||
canvas->drawRect(r, selectionPaint);
|
||||
canvas->restore();
|
||||
|
||||
if(controls.selectionMode == 6 || controls.selectionMode == 5 || controls.selectionMode == 2) {
|
||||
drawP.draw_drag_circle(canvas, controls.rotationPoint, {0.9f, 0.5f, 0.1f, 1.0f}, drawData, ROTATION_POINT_RADIUS_MULTIPLIER);
|
||||
drawP.draw_drag_circle(canvas, drawData.cam.c.to_space(controls.rotationCenter), {0.9f, 0.5f, 0.1f, 1.0f}, drawData);
|
||||
}
|
||||
if(controls.selectionMode == 4 || controls.selectionMode == 2) {
|
||||
drawP.draw_drag_circle(canvas, drawData.cam.c.to_space(controls.scaleCurrentPoint), {0.1f, 0.9f, 0.9f, 1.0f}, drawData);
|
||||
if(controls.selectionMode == 4)
|
||||
drawP.draw_drag_circle(canvas, drawData.cam.c.to_space(controls.scaleCenterPoint), {0.0f, 0.0f, 0.0f, 0.0f}, drawData);
|
||||
}
|
||||
canvas->restore();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,45 +14,17 @@ class RectSelectTool {
|
|||
void tool_update();
|
||||
void draw(SkCanvas* canvas, const DrawData& drawData);
|
||||
void reset_tool();
|
||||
void paste_clipboard();
|
||||
private:
|
||||
struct RectSelectControls {
|
||||
int selectionMode = 0;
|
||||
Vector2f selectStartAt;
|
||||
|
||||
|
||||
CoordSpaceHelper coords;
|
||||
|
||||
WorldVec moveStartAt;
|
||||
SCollision::AABB<WorldScalar> finalSelectionAABB;
|
||||
Vector2f selectStartAt;
|
||||
|
||||
std::array<Vector2f, 4> newT;
|
||||
|
||||
WorldVec scaleStartAt;
|
||||
WorldVec scaleCenterPoint;
|
||||
WorldVec scaleCurrentPoint;
|
||||
|
||||
WorldVec rotationCenter;
|
||||
|
||||
// Temporary, per frame colliders for handles
|
||||
Vector2f rotationPoint;
|
||||
SCollision::Circle<float> scaleSelectionCircle;
|
||||
SCollision::Circle<float> rotationCenterCircle;
|
||||
SCollision::Circle<float> rotationPointCircle;
|
||||
|
||||
double rotationPointAngle;
|
||||
|
||||
struct SelectedItemInfo {
|
||||
std::shared_ptr<DrawComponent> comp;
|
||||
ServerClientID id;
|
||||
CoordSpaceHelper oldCoords;
|
||||
};
|
||||
std::vector<SelectedItemInfo> selectedItems;
|
||||
} controls;
|
||||
|
||||
void selection_to_clipboard();
|
||||
Vector2f get_rotation_point_pos_from_angle(double angle);
|
||||
void update_drag_circles();
|
||||
void set_selected_items();
|
||||
bool create_selection_aabb();
|
||||
void commit_transformation();
|
||||
|
||||
DrawingProgram& drawP;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue