Working on moving canvas code to net objects (broken build)
This commit is contained in:
parent
d2a0bfcda9
commit
45ba26446a
42 changed files with 605 additions and 3186 deletions
|
|
@ -87,12 +87,6 @@ set(sources "src/main.cpp"
|
|||
"src/CanvasComponents/EllipseCanvasComponent.cpp"
|
||||
"src/CanvasComponents/ImageCanvasComponent.cpp"
|
||||
"src/CanvasComponents/CanvasComponent.cpp"
|
||||
"src/DrawComponents/DrawEllipse.cpp"
|
||||
"src/DrawComponents/DrawRectangle.cpp"
|
||||
"src/DrawComponents/DrawImage.cpp"
|
||||
"src/DrawComponents/DrawBrushStroke.cpp"
|
||||
"src/DrawComponents/DrawTextBox.cpp"
|
||||
"src/DrawComponents/DrawComponent.cpp"
|
||||
"src/GUIStuff/GUIManager.cpp"
|
||||
"src/GUIStuff/GUIManagerID.cpp"
|
||||
"src/GUIStuff/ClayImplement.cpp"
|
||||
|
|
|
|||
|
|
@ -7,46 +7,80 @@
|
|||
namespace NetworkingObjects {
|
||||
class DelayUpdateSerializedClassManager {
|
||||
public:
|
||||
template <typename T> void register_class(NetObjManager& objMan) {
|
||||
template <typename T> struct CustomConstructors {
|
||||
std::function<void(const T& o, cereal::PortableBinaryOutputArchive& a)> writeConstructor = [](const T& o, cereal::PortableBinaryOutputArchive& a) {
|
||||
a(o);
|
||||
};
|
||||
std::function<void(T& o, cereal::PortableBinaryOutputArchive& a)> readConstructor = [](T& o, cereal::PortableBinaryOutputArchive& a) {
|
||||
a(o);
|
||||
};
|
||||
std::function<void(const T& o, cereal::PortableBinaryOutputArchive& a)> writeUpdate = [](const T& o, cereal::PortableBinaryOutputArchive& a) {
|
||||
a(o);
|
||||
};
|
||||
std::function<void(T& o, cereal::PortableBinaryOutputArchive& a)> readUpdate = [](T& o, cereal::PortableBinaryOutputArchive& a) {
|
||||
a(o);
|
||||
};
|
||||
std::function<std::shared_ptr<T>(const T& o)> allocateCopy = [](const T& o) {
|
||||
return std::make_shared<T>();
|
||||
};
|
||||
std::function<void(T&, const T&)> assignmentFunc = [](T& o, const T& o2) {
|
||||
constexpr bool hasDefaultAssignment = requires(T& t, const T& t2) {
|
||||
t = t2;
|
||||
};
|
||||
if constexpr(hasDefaultAssignment)
|
||||
o = o2;
|
||||
};
|
||||
std::function<void(T&)> postUpdateFunc = [](T& o) {};
|
||||
};
|
||||
template <typename T> void register_class(NetObjManager& objMan, const CustomConstructors<T>& t = CustomConstructors<T>()) {
|
||||
objMan.register_class<T, T, T, T>({
|
||||
.writeConstructorFuncClient = [&](const NetObjTemporaryPtr<T>& o, cereal::PortableBinaryOutputArchive& a){write_constructor_func<T>(o, a);},
|
||||
.readConstructorFuncClient = [&](const NetObjTemporaryPtr<T>& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>&){read_constructor_func<T>(o, a);},
|
||||
.readUpdateFuncClient = [&](const NetObjTemporaryPtr<T>& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>&){client_read_update_func<T>(o, a);},
|
||||
.writeConstructorFuncServer = [&](const NetObjTemporaryPtr<T>& o, cereal::PortableBinaryOutputArchive& a){write_constructor_func<T>(o, a);},
|
||||
.readConstructorFuncServer = [&](const NetObjTemporaryPtr<T>& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>&){read_constructor_func<T>(o, a);},
|
||||
.readUpdateFuncServer = [&](const NetObjTemporaryPtr<T>& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>&){server_read_update_func<T>(o, a);}
|
||||
.writeConstructorFuncClient = t.writeConstructor,
|
||||
.readConstructorFuncClient = t.readConstructor,
|
||||
.readUpdateFuncClient = [&, readUpdate = t.readUpdate](const NetObjTemporaryPtr<T>& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>&){client_read_update_func<T>(o, a, readUpdate);},
|
||||
.writeConstructorFuncServer = t.writeConstructor,
|
||||
.readConstructorFuncServer = t.readConstructor,
|
||||
.readUpdateFuncServer = [&, readUpdate = t.readUpdate](const NetObjTemporaryPtr<T>& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>&){server_read_update_func<T>(o, a, readUpdate);}
|
||||
});
|
||||
typeIndexFuncs[std::type_index(typeid(T))] = {
|
||||
.writeUpdateFunc = [writeFunc = t.writeUpdate](const void* o, cereal::PortableBinaryOutputArchive& a) {
|
||||
writeFunc(*static_cast<const T*>(o), a);
|
||||
},
|
||||
.assignmentFunc = [assignFunc = t.assignmentFunc](void* o, const void* o2) {
|
||||
assignFunc(*static_cast<T*>(o), *static_cast<const T*>(o2));
|
||||
},
|
||||
.postUpdateFunc = [postUpdateFunc = t.postUpdateFunc](void* o) {
|
||||
postUpdateFunc(*static_cast<T*>(o));
|
||||
},
|
||||
.allocateCopyFunc = [allocateCopyFunc = t.allocateCopy](const void* o) {
|
||||
return std::static_pointer_cast<void>(allocateCopyFunc(*static_cast<const T*>(o)));
|
||||
}
|
||||
};
|
||||
}
|
||||
template <typename T> void send_update_to_all(const NetworkingObjects::NetObjTemporaryPtr<T>& o, const std::function<void(T&)>& postAssignmentFunc, bool finalUpdate) {
|
||||
template <typename T> void send_update_to_all(const NetworkingObjects::NetObjTemporaryPtr<T>& o, bool finalUpdate) {
|
||||
o.send_update_to_all(finalUpdate ? RELIABLE_COMMAND_CHANNEL : UNRELIABLE_COMMAND_CHANNEL, [&](const NetObjTemporaryPtr<T>& o, cereal::PortableBinaryOutputArchive& a) {
|
||||
if(o.get_obj_man()->is_server())
|
||||
server_write_func(o, a, postAssignmentFunc);
|
||||
server_write_func(o, a);
|
||||
else
|
||||
client_write_update_func(o, a, postAssignmentFunc, finalUpdate);
|
||||
client_write_update_func(o, a, finalUpdate);
|
||||
});
|
||||
}
|
||||
void update();
|
||||
private:
|
||||
template <typename T> void write_constructor_func(const NetObjTemporaryPtr<T>& o, cereal::PortableBinaryOutputArchive& a) {
|
||||
a(*o);
|
||||
}
|
||||
|
||||
template <typename T> void client_write_update_func(const NetObjTemporaryPtr<T>& o, cereal::PortableBinaryOutputArchive& a, const std::function<void(T&)>& postAssignmentFunc, bool finalUpdate) {
|
||||
template <typename T> void client_write_update_func(const NetObjTemporaryPtr<T>& o, cereal::PortableBinaryOutputArchive& a, bool finalUpdate) {
|
||||
a(finalUpdate);
|
||||
a(*o);
|
||||
typeIndexFuncs[std::type_index(typeid(T))].writeUpdateFunc(static_cast<const void*>(o.get()), a);
|
||||
auto it = updatingObjs.find(o.get_net_id());
|
||||
|
||||
if(it == updatingObjs.end()) {
|
||||
UpdatingObject toAdd;
|
||||
toAdd.objToUpdateCurrentData = std::static_pointer_cast<void>(std::make_shared<T>(*o));
|
||||
toAdd.objToUpdateCurrentData = typeIndexFuncs[std::type_index(typeid(T))].allocateCopyFunc(static_cast<const void*>(o.get()));
|
||||
toAdd.doLastAssignment = std::make_shared<bool>(true);
|
||||
toAdd.lastUpdateTimePoint = std::chrono::steady_clock::now();
|
||||
toAdd.setDataAfterTimeout = [postAssignmentFunc, oWeak = NetObjWeakPtr<T>(o), currentData = toAdd.objToUpdateCurrentData, doLastAssignment = toAdd.doLastAssignment]() {
|
||||
toAdd.setDataAfterTimeout = [&typeIndexFuncs = typeIndexFuncs, oWeak = NetObjWeakPtr<T>(o), currentData = toAdd.objToUpdateCurrentData, doLastAssignment = toAdd.doLastAssignment]() {
|
||||
NetObjTemporaryPtr<T> oLock = oWeak.lock();
|
||||
if(oLock && *doLastAssignment) {
|
||||
*oLock = *std::static_pointer_cast<T>(currentData);
|
||||
if(postAssignmentFunc)
|
||||
postAssignmentFunc(*oLock);
|
||||
typeIndexFuncs[std::type_index(typeid(T))].assignmentFunc(static_cast<void*>(oLock.get()), static_cast<const void*>(currentData.get()));
|
||||
typeIndexFuncs[std::type_index(typeid(T))].postUpdateFunc(static_cast<void*>(oLock.get()));
|
||||
}
|
||||
};
|
||||
updatingObjs.emplace(o.get_net_id(), toAdd);
|
||||
|
|
@ -57,38 +91,35 @@ namespace NetworkingObjects {
|
|||
}
|
||||
}
|
||||
|
||||
template <typename T> void read_constructor_func(const NetObjTemporaryPtr<T>& o, cereal::PortableBinaryInputArchive& a) {
|
||||
a(*o);
|
||||
}
|
||||
|
||||
template <typename T> void client_read_update_func(const NetObjTemporaryPtr<T>& o, cereal::PortableBinaryInputArchive& a) {
|
||||
template <typename T> void client_read_update_func(const NetObjTemporaryPtr<T>& o, cereal::PortableBinaryInputArchive& a, const std::function<void(T&, cereal::PortableBinaryOutputArchive&)>& readUpdate) {
|
||||
auto it = updatingObjs.find(o.get_net_id());
|
||||
if(it == updatingObjs.end())
|
||||
a(*o);
|
||||
if(it == updatingObjs.end()) {
|
||||
readUpdate(*o, a);
|
||||
typeIndexFuncs[std::type_index(typeid(T))].postUpdateFunc(static_cast<void*>(o.get()));
|
||||
}
|
||||
else {
|
||||
auto& [netID, updatingObjData] = *it;
|
||||
a(*std::static_pointer_cast<T>(updatingObjData.objToUpdateCurrentData));
|
||||
readUpdate(*std::static_pointer_cast<T>(updatingObjData.objToUpdateCurrentData), a);
|
||||
*updatingObjData.doLastAssignment = true;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T> void server_write_func(const NetObjTemporaryPtr<T>& o, cereal::PortableBinaryOutputArchive& a, const std::function<void(T&)>& postAssignmentFunc) {
|
||||
a(*o);
|
||||
typeIndexFuncs[std::type_index(typeid(T))].writeUpdateFunc(static_cast<const void*>(o.get()), a);
|
||||
auto it = updatingObjs.find(o.get_net_id());
|
||||
|
||||
if(it == updatingObjs.end()) {
|
||||
UpdatingObject toAdd;
|
||||
toAdd.objToUpdateCurrentData = std::static_pointer_cast<void>(std::make_shared<T>(*o));
|
||||
toAdd.objToUpdateCurrentData = typeIndexFuncs[std::type_index(typeid(T))].allocateCopyFunc(static_cast<const void*>(o.get()));
|
||||
toAdd.lastUpdateTimePoint = std::chrono::steady_clock::now();
|
||||
toAdd.doLastAssignment = std::make_shared<bool>(false);
|
||||
toAdd.setDataAfterTimeout = [postAssignmentFunc, oWeak = NetObjWeakPtr<T>(o), currentData = toAdd.objToUpdateCurrentData, doLastAssignment = toAdd.doLastAssignment]() {
|
||||
toAdd.setDataAfterTimeout = [&typeIndexFuncs = typeIndexFuncs, postAssignmentFunc, oWeak = NetObjWeakPtr<T>(o), currentData = toAdd.objToUpdateCurrentData, doLastAssignment = toAdd.doLastAssignment]() {
|
||||
NetObjTemporaryPtr<T> oLock = oWeak.lock();
|
||||
if(oLock && *doLastAssignment) {
|
||||
*oLock = *std::static_pointer_cast<T>(currentData);
|
||||
if(postAssignmentFunc)
|
||||
postAssignmentFunc(*oLock);
|
||||
oLock.send_server_update_to_all_clients(RELIABLE_COMMAND_CHANNEL, [](const NetObjTemporaryPtr<T>& o, cereal::PortableBinaryOutputArchive& a) {
|
||||
a(*o);
|
||||
typeIndexFuncs[std::type_index(typeid(T))].assignmentFunc(static_cast<void*>(oLock.get()), static_cast<const void*>(currentData.get()));
|
||||
typeIndexFuncs[std::type_index(typeid(T))].postUpdateFunc(static_cast<void*>(oLock.get()));
|
||||
oLock.send_server_update_to_all_clients(RELIABLE_COMMAND_CHANNEL, [&typeIndexFuncs = typeIndexFuncs](const NetObjTemporaryPtr<T>& o, cereal::PortableBinaryOutputArchive& a) {
|
||||
typeIndexFuncs[std::type_index(typeid(T))].writeUpdateFunc(static_cast<const void*>(o.get()), a);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -97,23 +128,26 @@ namespace NetworkingObjects {
|
|||
else {
|
||||
auto& [netID, updatingObjData] = *it;
|
||||
updatingObjData.lastUpdateTimePoint = std::chrono::steady_clock::now();
|
||||
*std::static_pointer_cast<T>(updatingObjs[o.get_net_id()].objToUpdateCurrentData) = *o;
|
||||
typeIndexFuncs[std::type_index(typeid(T))].assignmentFunc(static_cast<void*>(updatingObjs[o.get_net_id()].objToUpdateCurrentData.get()), static_cast<const void*>(o.get()));
|
||||
*updatingObjData.doLastAssignment = false;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T> void server_read_update_func(const NetObjTemporaryPtr<T>& o, cereal::PortableBinaryInputArchive& a) {
|
||||
template <typename T> void server_read_update_func(const NetObjTemporaryPtr<T>& o, cereal::PortableBinaryInputArchive& a, const std::function<void(T&, cereal::PortableBinaryOutputArchive&)>& readUpdate) {
|
||||
auto it = updatingObjs.find(o.get_net_id());
|
||||
bool finalUpdate;
|
||||
if(it == updatingObjs.end()) {
|
||||
a(finalUpdate, *o);
|
||||
o.send_server_update_to_all_clients(finalUpdate ? RELIABLE_COMMAND_CHANNEL : UNRELIABLE_COMMAND_CHANNEL, [](const NetObjTemporaryPtr<T>& o, cereal::PortableBinaryOutputArchive& a) {
|
||||
a(*o);
|
||||
a(finalUpdate);
|
||||
readUpdate(*o, a);
|
||||
typeIndexFuncs[std::type_index(typeid(T))].postUpdateFunc(static_cast<void*>(o.get()));
|
||||
o.send_server_update_to_all_clients(finalUpdate ? RELIABLE_COMMAND_CHANNEL : UNRELIABLE_COMMAND_CHANNEL, [&typeIndexFuncs = typeIndexFuncs](const NetObjTemporaryPtr<T>& o, cereal::PortableBinaryOutputArchive& a) {
|
||||
typeIndexFuncs[std::type_index(typeid(T))].writeUpdateFunc(static_cast<const void*>(o.get()), a);
|
||||
});
|
||||
}
|
||||
else {
|
||||
auto& [netID, updatingObjData] = *it;
|
||||
a(finalUpdate, *std::static_pointer_cast<T>(updatingObjData.objToUpdateCurrentData));
|
||||
a(finalUpdate);
|
||||
readUpdate(*std::static_pointer_cast<T>(updatingObjData.objToUpdateCurrentData), a);
|
||||
*updatingObjData.doLastAssignment = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -125,5 +159,13 @@ namespace NetworkingObjects {
|
|||
std::function<void()> setDataAfterTimeout;
|
||||
};
|
||||
std::unordered_map<NetObjID, UpdatingObject> updatingObjs;
|
||||
|
||||
struct ExtraFunctions {
|
||||
std::function<void(const void*, cereal::PortableBinaryOutputArchive&)> writeUpdateFunc;
|
||||
std::function<void(void*, const void*)> assignmentFunc;
|
||||
std::function<void(void*)> postUpdateFunc;
|
||||
std::function<std::shared_ptr<void>(const void*)> allocateCopyFunc;
|
||||
};
|
||||
std::unordered_map<std::type_index, ExtraFunctions> typeIndexFuncs;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ namespace NetworkingObjects {
|
|||
}
|
||||
|
||||
virtual bool contains(const NetObjID& p) const = 0;
|
||||
virtual const std::vector<NetObjOrderedListObjectInfoPtr<T>>& get_data() const = 0;
|
||||
virtual uint32_t size() const = 0;
|
||||
virtual const NetObjOrderedListObjectInfoPtr<T>& at(uint32_t index) const = 0;
|
||||
virtual bool empty() const = 0;
|
||||
|
|
@ -162,6 +163,9 @@ namespace NetworkingObjects {
|
|||
virtual bool contains(const NetObjID& id) const override {
|
||||
return idToDataMap.contains(id);
|
||||
}
|
||||
virtual const std::vector<NetObjOrderedListObjectInfoPtr<T>>& get_data() const override {
|
||||
return data;
|
||||
}
|
||||
virtual uint32_t size() const override {
|
||||
return data.size();
|
||||
}
|
||||
|
|
@ -330,6 +334,9 @@ namespace NetworkingObjects {
|
|||
virtual bool contains(const NetObjID& id) const override {
|
||||
return clientIdToDataMap.contains(id);
|
||||
}
|
||||
virtual const std::vector<NetObjOrderedListObjectInfoPtr<T>>& get_data() const override {
|
||||
return clientData;
|
||||
}
|
||||
virtual uint32_t size() const override {
|
||||
return clientData.size();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,10 @@ namespace NetworkingObjects {
|
|||
return NetObjTemporaryPtr<T>();
|
||||
return NetObjTemporaryPtr<T>(objMan, id, static_cast<T*>(it->second.p));
|
||||
}
|
||||
void reset() {
|
||||
objMan = nullptr;
|
||||
id = NetObjID({0, 0});
|
||||
}
|
||||
NetObjManager* get_obj_man() const {
|
||||
return objMan;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ class CanvasComponent {
|
|||
|
||||
protected:
|
||||
friend class CanvasComponentContainer;
|
||||
friend class CanvasComponentAllocator;
|
||||
|
||||
virtual void draw(SkCanvas* canvas, const DrawData& drawData) const = 0;
|
||||
virtual void initialize_draw_data(DrawingProgram& drawP) = 0;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
#include "CanvasComponentAllocator.hpp"
|
||||
#include "CanvasComponentContainer.hpp"
|
||||
#include "../DrawingProgram/DrawingProgram.hpp"
|
||||
|
||||
CanvasComponentAllocator::CanvasComponentAllocator() {}
|
||||
|
||||
|
|
@ -6,25 +8,35 @@ CanvasComponentAllocator::CanvasComponentAllocator(CanvasComponent::CompType typ
|
|||
comp = std::unique_ptr<CanvasComponent>(CanvasComponent::allocate_comp(typeToAllocate));
|
||||
}
|
||||
|
||||
void CanvasComponentAllocator::register_class(NetworkingObjects::NetObjManager& objMan) {
|
||||
objMan.register_class<CanvasComponentAllocator, CanvasComponentAllocator, CanvasComponentAllocator, CanvasComponentAllocator>({
|
||||
.writeConstructorFuncClient = write_constructor_func,
|
||||
.readConstructorFuncClient = read_constructor_func,
|
||||
.readUpdateFuncClient = nullptr,
|
||||
.writeConstructorFuncServer = write_constructor_func,
|
||||
.readConstructorFuncServer = read_constructor_func,
|
||||
.readUpdateFuncServer = nullptr,
|
||||
void CanvasComponentAllocator::register_class(DrawingProgram& drawP, NetworkingObjects::DelayUpdateSerializedClassManager& delayUpdateMan, NetworkingObjects::NetObjManager& objMan) {
|
||||
delayUpdateMan.register_class<CanvasComponentAllocator>(objMan, NetworkingObjects::DelayUpdateSerializedClassManager::CustomConstructors<CanvasComponentAllocator>{
|
||||
.writeConstructor = [](const CanvasComponentAllocator& o, cereal::PortableBinaryOutputArchive& a) {
|
||||
a(o.comp->get_type());
|
||||
a(*o.comp);
|
||||
},
|
||||
.readConstructor = [](CanvasComponentAllocator& o, cereal::PortableBinaryOutputArchive& a) {
|
||||
CanvasComponent::CompType typeToAllocate;
|
||||
a(typeToAllocate);
|
||||
o.comp = std::unique_ptr<CanvasComponent>(CanvasComponent::allocate_comp(typeToAllocate));
|
||||
a(*o.comp);
|
||||
},
|
||||
.writeUpdate = [](const CanvasComponentAllocator& o, cereal::PortableBinaryOutputArchive& a) {
|
||||
a(*o.comp);
|
||||
},
|
||||
.readUpdate = [](CanvasComponentAllocator& o, cereal::PortableBinaryOutputArchive& a) {
|
||||
a(*o.comp);
|
||||
},
|
||||
.allocateCopy = [](const CanvasComponentAllocator& o) {
|
||||
auto theCopy = std::make_shared<CanvasComponentAllocator>();
|
||||
theCopy->comp = std::unique_ptr<CanvasComponent>(CanvasComponent::allocate_comp(o.comp->get_type()));
|
||||
// Maybe do an assignment here
|
||||
return theCopy;
|
||||
},
|
||||
.assignmentFunc = [](CanvasComponentAllocator& o, const CanvasComponentAllocator& o2) {
|
||||
// Assignment still needs to be implemented
|
||||
},
|
||||
.postUpdateFunc = [&drawP](CanvasComponentAllocator& o) {
|
||||
o.comp->compContainer->commit_update(drawP);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void CanvasComponentAllocator::write_constructor_func(const NetworkingObjects::NetObjTemporaryPtr<CanvasComponentAllocator>& o, cereal::PortableBinaryOutputArchive& a) {
|
||||
a(o->comp->get_type());
|
||||
a(*o->comp);
|
||||
}
|
||||
|
||||
void CanvasComponentAllocator::read_constructor_func(const NetworkingObjects::NetObjTemporaryPtr<CanvasComponentAllocator>& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>& c) {
|
||||
CanvasComponent::CompType typeToAllocate;
|
||||
a(typeToAllocate);
|
||||
o->comp = std::unique_ptr<CanvasComponent>(CanvasComponent::allocate_comp(typeToAllocate));
|
||||
a(*o->comp);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@
|
|||
#include <memory>
|
||||
#include "CanvasComponent.hpp"
|
||||
#include <Helpers/NetworkingObjects/NetObjTemporaryPtr.hpp>
|
||||
#include <Helpers/NetworkingObjects/DelayUpdateSerializedClassManager.hpp>
|
||||
|
||||
class DrawingProgram;
|
||||
|
||||
class CanvasComponentAllocator {
|
||||
public:
|
||||
CanvasComponentAllocator();
|
||||
CanvasComponentAllocator(CanvasComponent::CompType typeToAllocate);
|
||||
std::unique_ptr<CanvasComponent> comp;
|
||||
static void register_class(NetworkingObjects::NetObjManager& objMan);
|
||||
private:
|
||||
static void write_constructor_func(const NetworkingObjects::NetObjTemporaryPtr<CanvasComponentAllocator>& o, cereal::PortableBinaryOutputArchive& a);
|
||||
static void read_constructor_func(const NetworkingObjects::NetObjTemporaryPtr<CanvasComponentAllocator>& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>& c);
|
||||
static void register_class(DrawingProgram& drawP, NetworkingObjects::DelayUpdateSerializedClassManager& delayUpdateMan, NetworkingObjects::NetObjManager& objMan);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
#include "CanvasComponentAllocator.hpp"
|
||||
#include "../DrawData.hpp"
|
||||
#include "../DrawingProgram/DrawingProgram.hpp"
|
||||
#include "../World.hpp"
|
||||
#include <Helpers/NetworkingObjects/DelayUpdateSerializedClassManager.hpp>
|
||||
|
||||
using namespace NetworkingObjects;
|
||||
|
||||
|
|
@ -22,6 +24,10 @@ void CanvasComponentContainer::register_class(NetObjManager& objMan) {
|
|||
});
|
||||
}
|
||||
|
||||
void CanvasComponentContainer::send_comp_update(DrawingProgram& drawP, bool finalUpdate) {
|
||||
drawP.world.delayedUpdateObjectManager.send_update_to_all<CanvasComponentAllocator>(compAllocator, finalUpdate);
|
||||
}
|
||||
|
||||
void CanvasComponentContainer::write_constructor_func(const NetworkingObjects::NetObjTemporaryPtr<CanvasComponentContainer>& o, cereal::PortableBinaryOutputArchive& a) {
|
||||
a(o->coords);
|
||||
o->compAllocator.write_create_message(a);
|
||||
|
|
@ -53,19 +59,25 @@ void CanvasComponentContainer::draw(SkCanvas* canvas, const DrawData& drawData)
|
|||
canvas->restore();
|
||||
}
|
||||
|
||||
void CanvasComponentContainer::set_owner_obj_info(const ObjInfoSharedPtr& ownerObjInfo) {
|
||||
objInfo = ownerObjInfo;
|
||||
}
|
||||
|
||||
void CanvasComponentContainer::commit_update(DrawingProgram& drawP) {
|
||||
//if(worldAABB.has_value()) // This is the only point where worldAABB could be nullopt
|
||||
// drawP.preupdate_component(objInfo);
|
||||
auto lockedObjInfo = objInfo.lock();
|
||||
if(worldAABB.has_value()) // This is the only point where worldAABB could be nullopt
|
||||
drawP.preupdate_component(lockedObjInfo);
|
||||
get_comp().initialize_draw_data(drawP);
|
||||
calculate_world_bounds();
|
||||
//drawP.preupdate_component(objInfo);
|
||||
drawP.preupdate_component(lockedObjInfo);
|
||||
}
|
||||
|
||||
void CanvasComponentContainer::commit_transform(DrawingProgram& drawP) {
|
||||
//if(worldAABB.has_value()) // This is the only point where worldAABB could be nullopt
|
||||
// drawP.preupdate_component(objInfo);
|
||||
auto lockedObjInfo = objInfo.lock();
|
||||
if(worldAABB.has_value()) // This is the only point where worldAABB could be nullopt
|
||||
drawP.preupdate_component(lockedObjInfo);
|
||||
calculate_world_bounds();
|
||||
//drawP.preupdate_component(objInfo);
|
||||
drawP.preupdate_component(lockedObjInfo);
|
||||
}
|
||||
|
||||
void CanvasComponentContainer::commit_update_dont_invalidate_cache(DrawingProgram& drawP) {
|
||||
|
|
@ -73,6 +85,10 @@ void CanvasComponentContainer::commit_update_dont_invalidate_cache(DrawingProgra
|
|||
calculate_world_bounds();
|
||||
}
|
||||
|
||||
void CanvasComponentContainer::commit_transform_dont_invalidate_cache(DrawingProgram& drawP) {
|
||||
calculate_world_bounds();
|
||||
}
|
||||
|
||||
bool CanvasComponentContainer::collides_with_world_coords(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection<WorldScalar>& checkAgainstWorld) const {
|
||||
return collides_with(camCoords, checkAgainstWorld, camCoords.world_collider_to_coords<SCollision::ColliderCollection<float>>(checkAgainstWorld));
|
||||
}
|
||||
|
|
@ -130,14 +146,10 @@ void CanvasComponentContainer::calculate_world_bounds() {
|
|||
worldAABB = coords.collider_to_world<SCollision::AABB<WorldScalar>, SCollision::AABB<float>>(get_comp().get_obj_coord_bounds());
|
||||
}
|
||||
|
||||
std::optional<SCollision::AABB<WorldScalar>> CanvasComponentContainer::get_world_bounds() const {
|
||||
return worldAABB;
|
||||
SCollision::AABB<WorldScalar> CanvasComponentContainer::get_world_bounds() const {
|
||||
return worldAABB.value();
|
||||
}
|
||||
|
||||
CanvasComponent& CanvasComponentContainer::get_comp() const {
|
||||
return *compAllocator->comp;
|
||||
}
|
||||
|
||||
const CoordSpaceHelper& CanvasComponentContainer::get_coords() const {
|
||||
return coords;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,14 +3,17 @@
|
|||
#include "CanvasComponent.hpp"
|
||||
#include <Helpers/NetworkingObjects/NetObjManagerTypeList.hpp>
|
||||
#include "../VersionConstants.hpp"
|
||||
#include "Helpers/NetworkingObjects/NetObjOrderedList.hpp"
|
||||
#include <Helpers/NetworkingObjects/NetObjOrderedList.hpp>
|
||||
#include <Helpers/NetworkingObjects/NetObjOwnerPtr.hpp>
|
||||
|
||||
class DrawingProgram;
|
||||
class CanvasComponentAllocator;
|
||||
class DrawingProgramCacheBVHNode;
|
||||
|
||||
class CanvasComponentContainer {
|
||||
public:
|
||||
typedef NetworkingObjects::NetObjOrderedList<CanvasComponentContainer> NetList;
|
||||
typedef NetworkingObjects::NetObjOwnerPtr<NetworkingObjects::NetObjOrderedList<CanvasComponentContainer>> NetListOwnerPtr;
|
||||
typedef NetworkingObjects::NetObjOrderedListObjectInfo<CanvasComponentContainer> ObjInfo;
|
||||
typedef std::shared_ptr<ObjInfo> ObjInfoSharedPtr;
|
||||
typedef std::weak_ptr<ObjInfo> ObjInfoWeakPtr;
|
||||
|
|
@ -29,16 +32,21 @@ class CanvasComponentContainer {
|
|||
void save_file(cereal::PortableBinaryOutputArchive& a) const;
|
||||
void load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version);
|
||||
CanvasComponent& get_comp() const;
|
||||
const CoordSpaceHelper& get_coords() const;
|
||||
std::optional<SCollision::AABB<WorldScalar>> get_world_bounds() const;
|
||||
SCollision::AABB<WorldScalar> get_world_bounds() const;
|
||||
void draw(SkCanvas* canvas, const DrawData& drawData) const;
|
||||
void commit_update(DrawingProgram& drawP);
|
||||
void commit_transform_dont_invalidate_cache(DrawingProgram& drawP); // Must be thread safe
|
||||
void commit_transform(DrawingProgram& drawP);
|
||||
void commit_update_dont_invalidate_cache(DrawingProgram& drawP); // Must be thread safe
|
||||
bool should_draw(const DrawData& drawData) const;
|
||||
bool collides_with_world_coords(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection<WorldScalar>& checkAgainstWorld) const;
|
||||
bool collides_with_cam_coords(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection<float>& checkAgainstCam) const;
|
||||
bool collides_with(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection<WorldScalar>& checkAgainstWorld, const SCollision::ColliderCollection<float>& checkAgainstCam) const;
|
||||
void set_owner_obj_info(const ObjInfoSharedPtr& ownerObjInfo);
|
||||
void send_comp_update(DrawingProgram& drawP, bool finalUpdate);
|
||||
|
||||
std::weak_ptr<DrawingProgramCacheBVHNode> parentBvhNode;
|
||||
CoordSpaceHelper coords;
|
||||
private:
|
||||
friend class BrushStrokeCanvasComponent;
|
||||
friend class ImageCanvasComponent;
|
||||
|
|
@ -58,9 +66,7 @@ class CanvasComponentContainer {
|
|||
TransformDrawData calculate_draw_transform(const DrawData& drawData) const;
|
||||
void calculate_world_bounds();
|
||||
|
||||
ObjInfo* objInfo = nullptr;
|
||||
|
||||
ObjInfoWeakPtr objInfo;
|
||||
std::optional<SCollision::AABB<WorldScalar>> worldAABB;
|
||||
NetworkingObjects::NetObjOwnerPtr<CanvasComponentAllocator> compAllocator;
|
||||
CoordSpaceHelper coords;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ void TextBoxCanvasComponent::draw(SkCanvas* canvas, const DrawData& drawData) co
|
|||
}
|
||||
|
||||
Vector2f TextBoxCanvasComponent::get_mouse_pos(DrawingProgram& drawP) const {
|
||||
return compContainer->get_coords().get_mouse_pos(drawP.world) - d.p1 - Vector2f{TEXTBOX_PADDING, TEXTBOX_PADDING};
|
||||
return compContainer->coords.get_mouse_pos(drawP.world) - d.p1 - Vector2f{TEXTBOX_PADDING, TEXTBOX_PADDING};
|
||||
}
|
||||
|
||||
void TextBoxCanvasComponent::initialize_draw_data(DrawingProgram& drawP) {
|
||||
|
|
|
|||
|
|
@ -1,383 +0,0 @@
|
|||
#pragma once
|
||||
#include "SharedTypes.hpp"
|
||||
#include <limits>
|
||||
|
||||
template <typename T, typename IDType> class CollabList {
|
||||
public:
|
||||
CollabList(const std::function<IDType()>& initGetNewIDFunc):
|
||||
getNewIDFunc(initGetNewIDFunc)
|
||||
{}
|
||||
|
||||
struct ObjectInfo {
|
||||
bool syncIgnore;
|
||||
T obj;
|
||||
uint64_t pos;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<ObjectInfo> ObjectInfoPtr;
|
||||
|
||||
const std::vector<std::shared_ptr<ObjectInfo>>& client_list() const { return clientSideList; }
|
||||
std::vector<std::shared_ptr<ObjectInfo>>& client_list() { return clientSideList; }
|
||||
const std::vector<std::shared_ptr<ObjectInfo>>& server_list() const { return serverSideList; }
|
||||
|
||||
std::function<void()> updateCallback;
|
||||
std::function<void(const ObjectInfoPtr& c)> clientInsertCallback;
|
||||
std::function<void(const ObjectInfoPtr& c)> clientEraseCallback;
|
||||
std::function<void(const std::vector<ObjectInfoPtr>& c)> clientInsertOrderedVectorCallback;
|
||||
std::function<void(uint64_t oldPos)> clientServerFirstPosShiftCallback;
|
||||
|
||||
void set_server_from_client_list() {
|
||||
serverSideList = clientSideList;
|
||||
for(auto& obj : serverSideList)
|
||||
obj->syncIgnore = false;
|
||||
}
|
||||
|
||||
ObjectInfoPtr get_item_by_id(IDType id) {
|
||||
auto it = idToObjectMap.find(id);
|
||||
return (idToObjectMap.end() == it) ? nullptr : it->second;
|
||||
}
|
||||
|
||||
void client_erase_if(const std::function<bool(uint64_t, const std::shared_ptr<ObjectInfo>&)>& func) {
|
||||
bool erasedSomething = false;
|
||||
for(int64_t i = 0; i < (int64_t)clientSideList.size(); i++) {
|
||||
if(func((uint64_t)i, clientSideList[i])) {
|
||||
if(clientEraseCallback)
|
||||
clientEraseCallback(clientSideList[i]);
|
||||
clientSideList[i]->obj->collabListInfo.reset();
|
||||
idToObjectMap.erase(clientSideList[i]->obj->id);
|
||||
clientSideList.erase(clientSideList.begin() + i);
|
||||
i--;
|
||||
if(updateCallback)
|
||||
updateCallback();
|
||||
erasedSomething = true;
|
||||
}
|
||||
}
|
||||
if(erasedSomething)
|
||||
complete_client_pos_refresh(false);
|
||||
}
|
||||
|
||||
ObjectInfoPtr client_insert(uint64_t pos, const T& item, bool createNewIDs = true) {
|
||||
auto newObj(std::make_shared<ObjectInfo>());
|
||||
newObj->syncIgnore = true;
|
||||
newObj->obj = item;
|
||||
if(createNewIDs)
|
||||
newObj->obj->id = getNewIDFunc();
|
||||
newObj->pos = std::min<uint64_t>(static_cast<uint64_t>(clientSideList.size()), pos);
|
||||
item->collabListInfo = newObj;
|
||||
|
||||
clientSideList.insert(clientSideList.begin() + newObj->pos, newObj);
|
||||
idToObjectMap.emplace(newObj->obj->id, newObj);
|
||||
|
||||
client_pos_refresh_after_insert(newObj->pos);
|
||||
|
||||
if(clientInsertCallback)
|
||||
clientInsertCallback(newObj);
|
||||
if(updateCallback)
|
||||
updateCallback();
|
||||
|
||||
return newObj;
|
||||
}
|
||||
|
||||
std::vector<ObjectInfoPtr> client_insert_ordered_vector_items(uint64_t pos, const std::vector<T>& items, bool createNewIDs = true) {
|
||||
std::vector<ObjectInfoPtr> itemsToPushIn;
|
||||
for(auto& item : items) {
|
||||
auto& newObj = itemsToPushIn.emplace_back(std::make_shared<ObjectInfo>());
|
||||
newObj->syncIgnore = true;
|
||||
newObj->obj = item;
|
||||
if(createNewIDs)
|
||||
newObj->obj->id = getNewIDFunc();
|
||||
item->collabListInfo = newObj;
|
||||
idToObjectMap.emplace(newObj->obj->id, newObj);
|
||||
}
|
||||
|
||||
clientSideList.insert(clientSideList.begin() + pos, itemsToPushIn.begin(), itemsToPushIn.end());
|
||||
|
||||
complete_client_pos_refresh(false);
|
||||
|
||||
if(clientInsertOrderedVectorCallback)
|
||||
clientInsertOrderedVectorCallback(itemsToPushIn);
|
||||
if(updateCallback)
|
||||
updateCallback();
|
||||
|
||||
return itemsToPushIn;
|
||||
}
|
||||
|
||||
IDType client_insert(const ObjectInfoPtr& objInfoToInsert, bool createNewIDs = true) {
|
||||
objInfoToInsert->syncIgnore = true;
|
||||
if(createNewIDs)
|
||||
objInfoToInsert->obj->id = getNewIDFunc();
|
||||
objInfoToInsert->pos = std::min<uint64_t>(static_cast<uint64_t>(clientSideList.size()), objInfoToInsert->pos);
|
||||
objInfoToInsert->obj->collabListInfo = objInfoToInsert;
|
||||
|
||||
clientSideList.insert(clientSideList.begin() + objInfoToInsert->pos, objInfoToInsert);
|
||||
idToObjectMap.emplace(objInfoToInsert->obj->id, objInfoToInsert);
|
||||
|
||||
client_pos_refresh_after_insert(objInfoToInsert->pos);
|
||||
|
||||
if(clientInsertCallback)
|
||||
clientInsertCallback(objInfoToInsert);
|
||||
if(updateCallback)
|
||||
updateCallback();
|
||||
|
||||
return objInfoToInsert->obj->id;
|
||||
}
|
||||
|
||||
void client_erase(const ObjectInfoPtr& objToErase) {
|
||||
bool erasedSomething = false;
|
||||
std::erase_if(clientSideList, [&](const auto& obj) {
|
||||
if(obj->obj == objToErase->obj) {
|
||||
if(clientEraseCallback)
|
||||
clientEraseCallback(obj);
|
||||
idToObjectMap.erase(objToErase->obj->id);
|
||||
obj->syncIgnore = true;
|
||||
obj->obj->collabListInfo.reset();
|
||||
erasedSomething = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if(erasedSomething) {
|
||||
if(updateCallback)
|
||||
updateCallback();
|
||||
complete_client_pos_refresh(false);
|
||||
}
|
||||
}
|
||||
|
||||
void client_erase_set(const std::unordered_set<ObjectInfoPtr>& objSet) {
|
||||
bool erasedSomething = false;
|
||||
std::erase_if(clientSideList, [&](const auto& obj) {
|
||||
if(objSet.contains(obj)) {
|
||||
if(clientEraseCallback)
|
||||
clientEraseCallback(obj);
|
||||
idToObjectMap.erase(obj->obj->id);
|
||||
obj->syncIgnore = true;
|
||||
obj->obj->collabListInfo.reset();
|
||||
erasedSomething = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if(erasedSomething) {
|
||||
complete_client_pos_refresh(false);
|
||||
if(updateCallback)
|
||||
updateCallback();
|
||||
for(auto& o : objSet)
|
||||
o->obj->collabListInfo.reset();
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Dont use this function unless you're certain every object in the vector isn't already in the client list
|
||||
void client_insert_ordered_vector(const std::vector<ObjectInfoPtr>& objOrderedVector, bool createNewIDs = true) {
|
||||
if(objOrderedVector.empty())
|
||||
return;
|
||||
|
||||
uint64_t startPoint = 0;
|
||||
objOrderedVector.front()->syncIgnore = true;
|
||||
if(createNewIDs)
|
||||
objOrderedVector.front()->obj->id = getNewIDFunc();
|
||||
objOrderedVector.front()->obj->collabListInfo = objOrderedVector.front();
|
||||
idToObjectMap.emplace(objOrderedVector.front()->obj->id, objOrderedVector.front());
|
||||
|
||||
for(uint64_t i = 1; i < static_cast<uint64_t>(objOrderedVector.size()); i++) {
|
||||
auto& obj = objOrderedVector[i];
|
||||
obj->syncIgnore = true;
|
||||
if(createNewIDs)
|
||||
obj->obj->id = getNewIDFunc();
|
||||
obj->obj->collabListInfo = obj;
|
||||
idToObjectMap.emplace(obj->obj->id, obj);
|
||||
if(obj->pos - objOrderedVector[startPoint]->pos != (i - startPoint)) {
|
||||
uint64_t insertPosition = std::min<uint64_t>(clientSideList.size(), objOrderedVector[startPoint]->pos);
|
||||
clientSideList.insert(clientSideList.begin() + insertPosition, objOrderedVector.begin() + startPoint, objOrderedVector.begin() + i);
|
||||
startPoint = i;
|
||||
}
|
||||
}
|
||||
clientSideList.insert(clientSideList.begin() + std::min<uint64_t>(clientSideList.size(), objOrderedVector[startPoint]->pos), objOrderedVector.begin() + startPoint, objOrderedVector.end());
|
||||
|
||||
complete_client_pos_refresh(false);
|
||||
if(updateCallback)
|
||||
updateCallback();
|
||||
if(clientInsertOrderedVectorCallback)
|
||||
clientInsertOrderedVectorCallback(objOrderedVector);
|
||||
}
|
||||
|
||||
std::vector<bool> server_insert_ordered_vector(std::vector<ObjectInfoPtr> objOrderedVector) {
|
||||
if(objOrderedVector.empty())
|
||||
return std::vector<bool>();
|
||||
|
||||
std::vector<bool> didntExistPreviously(objOrderedVector.size(), true);
|
||||
|
||||
uint64_t startPoint = 0;
|
||||
auto foundInClient1 = idToObjectMap.emplace(objOrderedVector.front()->obj->id, objOrderedVector.front());
|
||||
if(!foundInClient1.second) { // object already existed
|
||||
objOrderedVector.front() = foundInClient1.first->second;
|
||||
didntExistPreviously[0] = false;
|
||||
}
|
||||
objOrderedVector.front()->syncIgnore = false;
|
||||
objOrderedVector.front()->obj->collabListInfo = objOrderedVector.front();
|
||||
|
||||
for(uint64_t i = 1; i < static_cast<uint64_t>(objOrderedVector.size()); i++) {
|
||||
auto& obj = objOrderedVector[i];
|
||||
|
||||
auto foundInClient = idToObjectMap.emplace(obj->obj->id, obj);
|
||||
if(!foundInClient.second) { // object already existed
|
||||
obj = foundInClient.first->second;
|
||||
didntExistPreviously[i] = false;
|
||||
}
|
||||
obj->syncIgnore = false;
|
||||
obj->obj->collabListInfo = obj;
|
||||
|
||||
if(obj->pos - objOrderedVector[startPoint]->pos != (i - startPoint)) {
|
||||
uint64_t insertPosition = std::min<uint64_t>(clientSideList.size(), objOrderedVector[startPoint]->pos);
|
||||
clientSideList.insert(clientSideList.begin() + insertPosition, objOrderedVector.begin() + startPoint, objOrderedVector.begin() + i);
|
||||
uint64_t insertPositionServer = std::min<uint64_t>(serverSideList.size(), objOrderedVector[startPoint]->pos);
|
||||
serverSideList.insert(serverSideList.begin() + insertPositionServer, objOrderedVector.begin() + startPoint, objOrderedVector.begin() + i);
|
||||
startPoint = i;
|
||||
}
|
||||
}
|
||||
clientSideList.insert(clientSideList.begin() + std::min<uint64_t>(clientSideList.size(), objOrderedVector[startPoint]->pos), objOrderedVector.begin() + startPoint, objOrderedVector.end());
|
||||
serverSideList.insert(serverSideList.begin() + std::min<uint64_t>(serverSideList.size(), objOrderedVector[startPoint]->pos), objOrderedVector.begin() + startPoint, objOrderedVector.end());
|
||||
|
||||
if(clientInsertOrderedVectorCallback)
|
||||
clientInsertOrderedVectorCallback(objOrderedVector);
|
||||
|
||||
sync();
|
||||
|
||||
return didntExistPreviously;
|
||||
}
|
||||
|
||||
bool server_insert(uint64_t pos, const T& item) {
|
||||
auto foundInClient = idToObjectMap.find(item->id);
|
||||
if(foundInClient != idToObjectMap.end()) {
|
||||
ObjectInfoPtr& o = foundInClient->second;
|
||||
serverSideList.insert(serverSideList.begin() + pos, o);
|
||||
o->syncIgnore = false;
|
||||
sync();
|
||||
return false;
|
||||
}
|
||||
auto newObj(std::make_shared<ObjectInfo>());
|
||||
newObj->syncIgnore = false;
|
||||
newObj->obj = item;
|
||||
newObj->pos = std::min<uint64_t>(pos, clientSideList.size());
|
||||
idToObjectMap.emplace(newObj->obj->id, newObj);
|
||||
item->collabListInfo = newObj;
|
||||
|
||||
serverSideList.insert(serverSideList.begin() + pos, newObj);
|
||||
clientSideList.insert(clientSideList.begin() + newObj->pos, newObj);
|
||||
|
||||
for(uint64_t i = newObj->pos + 1; i < static_cast<uint64_t>(clientSideList.size()); i++)
|
||||
clientSideList[i]->pos++;
|
||||
|
||||
if(clientInsertCallback)
|
||||
clientInsertCallback(newObj);
|
||||
|
||||
sync();
|
||||
return true;
|
||||
}
|
||||
void server_erase(IDType id) {
|
||||
std::erase_if(serverSideList, [&](const auto& obj) {
|
||||
if(obj->obj->id == id) {
|
||||
obj->syncIgnore = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
std::erase_if(clientSideList, [&](const auto& obj) {
|
||||
if(obj->obj->id == id) {
|
||||
obj->syncIgnore = false;
|
||||
idToObjectMap.erase(obj->obj->id);
|
||||
if(clientEraseCallback)
|
||||
clientEraseCallback(obj);
|
||||
obj->obj->collabListInfo.reset();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
sync();
|
||||
}
|
||||
|
||||
void server_erase_set(const std::unordered_set<ServerClientID>& idsToErase) {
|
||||
std::erase_if(serverSideList, [&](const auto& obj) {
|
||||
if(idsToErase.contains(obj->obj->id)) {
|
||||
obj->syncIgnore = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
std::erase_if(clientSideList, [&](const auto& obj) {
|
||||
if(idsToErase.contains(obj->obj->id)) {
|
||||
obj->syncIgnore = false;
|
||||
if(clientEraseCallback)
|
||||
clientEraseCallback(obj);
|
||||
idToObjectMap.erase(obj->obj->id);
|
||||
obj->obj->collabListInfo.reset();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
sync();
|
||||
}
|
||||
|
||||
void init_emplace_back(const T& item) {
|
||||
auto newObj(std::make_shared<ObjectInfo>());
|
||||
newObj->syncIgnore = false;
|
||||
newObj->obj = item;
|
||||
item->collabListInfo = newObj;
|
||||
idToObjectMap.emplace(newObj->obj->id, newObj);
|
||||
|
||||
serverSideList.emplace_back(newObj);
|
||||
clientSideList.emplace_back(newObj);
|
||||
|
||||
newObj->pos = clientSideList.size() - 1;
|
||||
|
||||
if(clientInsertCallback)
|
||||
clientInsertCallback(newObj);
|
||||
if(updateCallback)
|
||||
updateCallback();
|
||||
}
|
||||
private:
|
||||
void complete_client_pos_refresh(bool syncFromServer) {
|
||||
uint64_t firstShiftPos = std::numeric_limits<uint64_t>::max();
|
||||
bool shiftHappened = false;
|
||||
for(uint64_t i = 0; i < clientSideList.size(); i++) {
|
||||
auto& c = clientSideList[i];
|
||||
if(c->pos != i) {
|
||||
firstShiftPos = std::min(firstShiftPos, c->pos);
|
||||
firstShiftPos = std::min(firstShiftPos, i);
|
||||
shiftHappened = true;
|
||||
c->pos = i;
|
||||
}
|
||||
}
|
||||
if(syncFromServer && shiftHappened && clientServerFirstPosShiftCallback)
|
||||
clientServerFirstPosShiftCallback(firstShiftPos);
|
||||
}
|
||||
|
||||
void client_pos_refresh_after_insert(uint64_t posToRefreshAfter) {
|
||||
for(uint64_t i = posToRefreshAfter + 1; i < static_cast<uint64_t>(clientSideList.size()); i++)
|
||||
clientSideList[i]->pos = i;
|
||||
}
|
||||
|
||||
void sync() {
|
||||
std::vector<std::pair<uint64_t, ObjectInfoPtr>> ignoredToReinsert;
|
||||
for(uint64_t i = 0; i < clientSideList.size(); i++)
|
||||
if(clientSideList[i]->syncIgnore)
|
||||
ignoredToReinsert.emplace_back(std::pair<uint64_t, std::shared_ptr<ObjectInfo>>{i, clientSideList[i]});
|
||||
clientSideList = serverSideList;
|
||||
std::erase_if(clientSideList, [&](const auto& obj) {
|
||||
return obj->syncIgnore;
|
||||
});
|
||||
for(auto& p : ignoredToReinsert) {
|
||||
if(p.first > clientSideList.size())
|
||||
clientSideList.emplace_back(p.second);
|
||||
else
|
||||
clientSideList.insert(clientSideList.begin() + p.first, p.second);
|
||||
}
|
||||
if(updateCallback)
|
||||
updateCallback();
|
||||
complete_client_pos_refresh(true);
|
||||
}
|
||||
|
||||
std::function<IDType()> getNewIDFunc;
|
||||
std::vector<ObjectInfoPtr> clientSideList;
|
||||
std::vector<ObjectInfoPtr> serverSideList;
|
||||
std::unordered_map<IDType, ObjectInfoPtr> idToObjectMap;
|
||||
};
|
||||
|
|
@ -1,460 +0,0 @@
|
|||
#include "DrawBrushStroke.hpp"
|
||||
#include <Helpers/Serializers.hpp>
|
||||
#include <cereal/types/vector.hpp>
|
||||
#include <limits>
|
||||
|
||||
#ifndef IS_SERVER
|
||||
#include <Eigen/Geometry>
|
||||
#include "Helpers/MathExtras.hpp"
|
||||
#include "../TimePoint.hpp"
|
||||
#include <include/core/SkVertices.h>
|
||||
#include <include/pathops/SkPathOps.h>
|
||||
#include "../DrawCollision.hpp"
|
||||
#endif
|
||||
|
||||
#define AABB_PRECHECK_BVH_LEVELS 2
|
||||
#define DEFAULT_SMOOTHNESS 5
|
||||
|
||||
void DrawBrushStroke::save(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(d->points, d->color, d->hasRoundCaps);
|
||||
}
|
||||
|
||||
void DrawBrushStroke::load(cereal::PortableBinaryInputArchive& a) {
|
||||
a(d->points, d->color, d->hasRoundCaps);
|
||||
}
|
||||
|
||||
void DrawBrushStroke::save_file(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(d->points, d->color, d->hasRoundCaps);
|
||||
}
|
||||
|
||||
void DrawBrushStroke::load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) {
|
||||
a(d->points, d->color, d->hasRoundCaps);
|
||||
}
|
||||
|
||||
DrawComponentType DrawBrushStroke::get_type() const {
|
||||
return DRAWCOMPONENT_BRUSHSTROKE;
|
||||
}
|
||||
|
||||
#ifndef IS_SERVER
|
||||
|
||||
std::shared_ptr<DrawComponent> DrawBrushStroke::copy(DrawingProgram& drawP) const {
|
||||
auto a = std::make_shared<DrawBrushStroke>();
|
||||
a->d = d;
|
||||
a->coords = coords;
|
||||
return a;
|
||||
}
|
||||
|
||||
std::shared_ptr<DrawComponent> DrawBrushStroke::deep_copy(DrawingProgram& drawP) const {
|
||||
auto a = std::make_shared<DrawBrushStroke>();
|
||||
a->d = d;
|
||||
a->coords = coords;
|
||||
// Just copy pointers over, brush strokes cant be edited so they can share the same memory
|
||||
a->brushPath = brushPath;
|
||||
a->brushPathLOD = brushPathLOD;
|
||||
a->bounds = bounds;
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
void DrawBrushStroke::update_from_delayed_ptr(const std::shared_ptr<DrawComponent>& delayedUpdatePtr) {
|
||||
std::shared_ptr<DrawBrushStroke> newPtr = std::static_pointer_cast<DrawBrushStroke>(delayedUpdatePtr);
|
||||
d = newPtr->d;
|
||||
}
|
||||
|
||||
Vector2f two_dim_vec_slerp(const Vector2f& v1, const Vector2f& v2) {
|
||||
Vector2f v1Norm = v1.normalized();
|
||||
Vector2f v2Norm = v2.normalized();
|
||||
Vector2f avg = (v1Norm + v2Norm) * 0.5f;
|
||||
if(std::fabs(avg.x()) < 0.00001f && std::fabs(avg.y()) < 0.00001f)
|
||||
return perpendicular_vec2(v1Norm).normalized();
|
||||
else
|
||||
return avg.normalized();
|
||||
}
|
||||
|
||||
void DrawBrushStroke::draw(SkCanvas* canvas, const DrawData& drawData) {
|
||||
if(drawSetupData.shouldDraw && brushPath) {
|
||||
//bool deepPrecheck = false;
|
||||
//for(auto& aabb : precheckAABBLevels) {
|
||||
// if(SCollision::collide(drawData.cam.viewingAreaGenerousCollider, aabb)) {
|
||||
// deepPrecheck = true;
|
||||
// break;
|
||||
// }
|
||||
//}
|
||||
//if(!deepPrecheck && !precheckAABBLevels.empty())
|
||||
// return;
|
||||
|
||||
SkPaint paint;
|
||||
//paint.setStroke(true);
|
||||
//paint.setStrokeWidth(0.0f);
|
||||
canvas->save();
|
||||
paint.setColor4f(SkColor4f{d->color.x(), d->color.y(), d->color.z(), d->color.w()});
|
||||
canvas_do_calculated_transform(canvas);
|
||||
if(drawSetupData.mipmapLevel == 0 || drawData.dontUseDrawProgCache) // check dontUseDrawProgCache, to make sure that SVG screenshots dont use LOD
|
||||
canvas->drawPath(*brushPath, paint);
|
||||
else
|
||||
canvas->drawPath(*brushPathLOD[drawSetupData.mipmapLevel - 1], paint);
|
||||
//SkPaint p2(SkColor4f{1, 0, 0, 1});
|
||||
//p2.setStroke(true);
|
||||
//SkPaint p3(SkColor4f{0, 1, 0, 1});
|
||||
//SkPaint p4(SkColor4f{0, 0, 1, 1});
|
||||
//p3.setStroke(true);
|
||||
//p4.setStroke(true);
|
||||
//for(auto& precheckAABBLevel : precheckAABBLevels)
|
||||
// draw_collider(canvas, drawData, precheckAABBLevel, p4);
|
||||
//draw_collider(canvas, drawData, collisionTree, p3);
|
||||
canvas->restore();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawBrushStroke::initialize_draw_data(DrawingProgram& drawP) {
|
||||
if(!d->points.empty()) {
|
||||
std::vector<DrawBrushStrokePoint> points = smooth_points(0, d->points.size() - 1, DEFAULT_SMOOTHNESS);
|
||||
auto triangleFunc = [&](Vector2f a, Vector2f b, Vector2f c) {
|
||||
return false;
|
||||
};
|
||||
|
||||
auto brushPathBuilder = std::make_shared<SkPathBuilder>();
|
||||
auto brushPathLOD0Builder = std::make_shared<SkPathBuilder>();
|
||||
auto brushPathLOD1Builder = std::make_shared<SkPathBuilder>();
|
||||
|
||||
create_triangles(triangleFunc, points, 0, brushPathBuilder);
|
||||
create_triangles(triangleFunc, points, 4, brushPathLOD0Builder);
|
||||
create_triangles(triangleFunc, points, 30, brushPathLOD1Builder);
|
||||
|
||||
brushPath = std::make_shared<SkPath>(brushPathBuilder->detach());
|
||||
brushPathLOD[0] = std::make_shared<SkPath>(brushPathLOD0Builder->detach());
|
||||
brushPathLOD[1] = std::make_shared<SkPath>(brushPathLOD1Builder->detach());
|
||||
}
|
||||
create_collider();
|
||||
}
|
||||
|
||||
std::vector<size_t> DrawBrushStroke::get_wedge_indices(const std::vector<DrawBrushStrokePoint>& points) {
|
||||
if(points.size() < 2)
|
||||
return {};
|
||||
std::vector<size_t> toRet;
|
||||
toRet.emplace_back(0);
|
||||
for(size_t i = 1; i < points.size() - 1; i++) {
|
||||
Vector2f v1Norm = (points[i - 1].pos - points[i].pos).normalized();
|
||||
Vector2f v2Norm = (points[i + 1].pos - points[i].pos).normalized();
|
||||
if(v1Norm.dot(v2Norm) > -0.6f)
|
||||
toRet.emplace_back(i);
|
||||
}
|
||||
toRet.emplace_back(points.size() - 1);
|
||||
return toRet;
|
||||
}
|
||||
|
||||
std::vector<DrawBrushStrokePoint> DrawBrushStroke::smooth_points(size_t beginIndex, size_t endIndex, unsigned numOfDivisions) {
|
||||
std::vector<DrawBrushStrokePoint>& points = d->points;
|
||||
|
||||
size_t pointsSize = endIndex - beginIndex + 1;
|
||||
if(pointsSize < 2)
|
||||
return {points[beginIndex], points[endIndex]};
|
||||
|
||||
std::vector<DrawBrushStrokePoint> toRet;
|
||||
|
||||
DrawBrushStrokePoint p0 = points[beginIndex];
|
||||
p0.pos = p0.pos + p0.width * (p0.pos - points[beginIndex + 1].pos).normalized() * DRAW_MINIMUM_LIMIT;
|
||||
DrawBrushStrokePoint p1 = points[beginIndex];
|
||||
DrawBrushStrokePoint p2 = points[beginIndex + 1];
|
||||
DrawBrushStrokePoint p3;
|
||||
|
||||
for(size_t i = beginIndex; i < endIndex; i++) {
|
||||
|
||||
if(i == endIndex - 1) {
|
||||
p3 = points[i + 1];
|
||||
p3.pos = p3.pos + p3.width * (p3.pos - points[i].pos).normalized() * DRAW_MINIMUM_LIMIT;
|
||||
}
|
||||
else
|
||||
p3 = points[i + 2];
|
||||
|
||||
toRet.emplace_back(p1);
|
||||
|
||||
float tMove = 1.0 / static_cast<float>(numOfDivisions);
|
||||
for(unsigned d = 1; d < numOfDivisions; d++) {
|
||||
float t = tMove * static_cast<float>(d);
|
||||
DrawBrushStrokePoint pToAdd;
|
||||
pToAdd.pos = catmull_rom(p0.pos, p1.pos, p2.pos, p3.pos, t);
|
||||
pToAdd.width = std::lerp(p1.width, p2.width, t);
|
||||
if(!(std::isnan(pToAdd.pos.x()) || std::isnan(pToAdd.pos.y()))) // Don't add NAN values
|
||||
toRet.emplace_back(pToAdd);
|
||||
}
|
||||
p0 = p1;
|
||||
p1 = p2;
|
||||
p2 = p3;
|
||||
}
|
||||
toRet.emplace_back(points[endIndex]);
|
||||
|
||||
return toRet;
|
||||
}
|
||||
|
||||
std::vector<DrawBrushStrokePoint> DrawBrushStroke::smooth_points_avg(size_t beginIndex, size_t endIndex, unsigned numOfDivisions) {
|
||||
std::vector<DrawBrushStrokePoint>& points = d->points;
|
||||
|
||||
size_t pointsSize = endIndex - beginIndex + 1;
|
||||
if(pointsSize < 2)
|
||||
return {points[beginIndex], points[endIndex]};
|
||||
|
||||
std::vector<DrawBrushStrokePoint> toRet;
|
||||
|
||||
for(size_t i = beginIndex; i < endIndex; i++) {
|
||||
const DrawBrushStrokePoint& p1 = points[i];
|
||||
const DrawBrushStrokePoint& p2 = points[i + 1];
|
||||
|
||||
float tMove = 1.0 / static_cast<float>(numOfDivisions);
|
||||
for(unsigned d = 0; d < numOfDivisions; d++) {
|
||||
float t = tMove * static_cast<float>(d);
|
||||
DrawBrushStrokePoint pToAdd;
|
||||
pToAdd.pos = lerp_vec(p1.pos, p2.pos, t);
|
||||
pToAdd.width = std::lerp(p1.width, p2.width, t);
|
||||
if(!(std::isnan(pToAdd.pos.x()) || std::isnan(pToAdd.pos.y()))) // Don't add NAN values
|
||||
toRet.emplace_back(pToAdd);
|
||||
}
|
||||
}
|
||||
|
||||
toRet.emplace_back(points[endIndex]);
|
||||
|
||||
return toRet;
|
||||
}
|
||||
|
||||
std::vector<DrawBrushStrokePoint> DrawBrushStroke::every_nth_point_include_front_and_back(const std::vector<DrawBrushStrokePoint>& pts, size_t n) {
|
||||
if(pts.size() <= 3 || n == 0)
|
||||
return pts;
|
||||
std::vector<DrawBrushStrokePoint> toRet;
|
||||
toRet.emplace_back(pts.front());
|
||||
for(size_t i = n; i < pts.size() - 1; i += n)
|
||||
toRet.emplace_back(pts[i]);
|
||||
toRet.emplace_back(pts.back());
|
||||
return toRet;
|
||||
}
|
||||
|
||||
void DrawBrushStroke::create_triangles(const std::function<bool(Vector2f, Vector2f, Vector2f)>& passTriangleFunc, const std::vector<DrawBrushStrokePoint>& smoothedPoints, size_t skipVertexCount, std::shared_ptr<SkPathBuilder> bPathBuilder) {
|
||||
const int ARC_SMOOTHNESS = 10;
|
||||
const int CIRCLE_SMOOTHNESS = 20;
|
||||
const std::vector<DrawBrushStrokePoint>& pointsN = d->points;
|
||||
std::vector<SkPoint> topPoints;
|
||||
std::vector<SkPoint> bottomPoints;
|
||||
|
||||
if(pointsN.size() == 1 && d->hasRoundCaps) {
|
||||
std::vector<Vector2f> circlePoints = gen_circle_points(pointsN.front().pos, pointsN.front().width / 2.0f, CIRCLE_SMOOTHNESS);
|
||||
for(size_t i = 0; i < circlePoints.size(); i++) {
|
||||
if(passTriangleFunc(pointsN.front().pos, circlePoints[i], circlePoints[(i + 1) % circlePoints.size()])) return;
|
||||
topPoints.emplace_back(convert_vec2<SkPoint>(circlePoints[i]));
|
||||
}
|
||||
}
|
||||
|
||||
if(pointsN.size() < 2) {
|
||||
if(bPathBuilder && !topPoints.empty())
|
||||
bPathBuilder->addPolygon({topPoints.data(), topPoints.size()}, false);
|
||||
return;
|
||||
}
|
||||
|
||||
auto points = every_nth_point_include_front_and_back(smoothedPoints, skipVertexCount);
|
||||
|
||||
std::vector<size_t> wedgeIndices = get_wedge_indices(points);
|
||||
for(size_t i = 0; i < wedgeIndices.size() - 1; i++) {
|
||||
|
||||
size_t pointsBegin = wedgeIndices[i];
|
||||
size_t pointsEnd = wedgeIndices[i + 1];
|
||||
|
||||
std::array<Vector2f, 3> vertArray;
|
||||
size_t newIndex = 0;
|
||||
|
||||
Vector2f perpPrev = perpendicular_vec2((points[pointsBegin + 1].pos - points[pointsBegin].pos).eval());
|
||||
perpPrev.normalize();
|
||||
|
||||
vertArray[0] = points[pointsBegin].pos + perpPrev * points[pointsBegin].width * 0.5f;
|
||||
topPoints.emplace_back(convert_vec2<SkPoint>(vertArray[0]));
|
||||
vertArray[1] = points[pointsBegin].pos - perpPrev * points[pointsBegin].width * 0.5f;
|
||||
bottomPoints.emplace_back(convert_vec2<SkPoint>(vertArray[1]));
|
||||
newIndex = 2;
|
||||
|
||||
if(d->hasRoundCaps && pointsBegin == 0) {
|
||||
Vector2f arcDirStart = (convert_vec2<Vector2f>(vertArray[0]) - points[0].pos).normalized();
|
||||
Vector2f arcDirEnd = (convert_vec2<Vector2f>(vertArray[1]) - points[0].pos).normalized();
|
||||
Vector2f arcDirCenter = (points[0].pos - points[1].pos).normalized();
|
||||
std::vector<Vector2f> arcPoints = arc_vec<float>(points[0].pos, arcDirStart, arcDirEnd, arcDirCenter, points[0].width * 0.5f, ARC_SMOOTHNESS);
|
||||
arcPoints.emplace(arcPoints.begin(), convert_vec2<Vector2f>(vertArray[0]));
|
||||
arcPoints.emplace_back(convert_vec2<Vector2f>(vertArray[1]));
|
||||
for(size_t i = 0; i < arcPoints.size() - 1; i++) {
|
||||
if(passTriangleFunc(points[0].pos, arcPoints[i], arcPoints[i + 1])) return;
|
||||
bottomPoints.emplace_back(convert_vec2<SkPoint>(arcPoints[i]));
|
||||
}
|
||||
bottomPoints.emplace_back(convert_vec2<SkPoint>(arcPoints.back()));
|
||||
}
|
||||
|
||||
for(size_t j = pointsBegin + 1; j < pointsEnd; j++) {
|
||||
if(j >= pointsEnd)
|
||||
j = pointsEnd - 1;
|
||||
|
||||
Vector2f v1 = points[j - 1].pos - points[j].pos;
|
||||
Vector2f v2 = points[j].pos - points[j + 1].pos;
|
||||
Vector2f perp = perpendicular_vec2((v1 + v2).eval()).normalized();
|
||||
if(std::isnan(perp.dot(perpPrev))) {
|
||||
std::cout << "NAN FOUND" << std::endl;
|
||||
std::cout << v1 << std::endl;
|
||||
std::cout << v2 << std::endl;
|
||||
std::cout << perp << std::endl;
|
||||
std::cout << perpPrev << std::endl;
|
||||
throw 1;
|
||||
}
|
||||
if(perp.dot(perpPrev) < 0.0)
|
||||
perp = -perp;
|
||||
|
||||
vertArray[newIndex] = points[j].pos + perp * 0.5f * points[j].width;
|
||||
topPoints.emplace_back(convert_vec2<SkPoint>(vertArray[newIndex]));
|
||||
newIndex = (newIndex + 1) % 3;
|
||||
if(passTriangleFunc(vertArray[0], vertArray[1], vertArray[2])) return;
|
||||
|
||||
vertArray[newIndex] = points[j].pos - perp * 0.5f * points[j].width;
|
||||
bottomPoints.emplace_back(convert_vec2<SkPoint>(vertArray[newIndex]));
|
||||
newIndex = (newIndex + 1) % 3;
|
||||
if(passTriangleFunc(vertArray[0], vertArray[1], vertArray[2])) return;
|
||||
|
||||
perpPrev = perp;
|
||||
}
|
||||
|
||||
Vector2f rectDir = points[pointsEnd - 1].pos - points[pointsEnd].pos;
|
||||
rectDir.normalize();
|
||||
Vector2f perp = perpendicular_vec2(rectDir);
|
||||
if(perp.dot(perpPrev) < 0.0)
|
||||
perp = -perp;
|
||||
vertArray[newIndex] = points[pointsEnd].pos + perp * points[pointsEnd].width * 0.5;
|
||||
topPoints.emplace_back(convert_vec2<SkPoint>(vertArray[newIndex]));
|
||||
newIndex = (newIndex + 1) % 3;
|
||||
if(passTriangleFunc(vertArray[0], vertArray[1], vertArray[2])) return;
|
||||
|
||||
vertArray[newIndex] = points[pointsEnd].pos - perp * points[pointsEnd].width * 0.5;
|
||||
bottomPoints.emplace_back(convert_vec2<SkPoint>(vertArray[newIndex]));
|
||||
newIndex = (newIndex + 1) % 3;
|
||||
if(passTriangleFunc(vertArray[0], vertArray[1], vertArray[2])) return;
|
||||
|
||||
// Render wedge
|
||||
if(pointsEnd != points.size() - 1) {
|
||||
// Find 4 points involved in intersection
|
||||
Vector2f currentP1 = points[pointsEnd].pos - perp * points[pointsEnd].width * 0.5;
|
||||
Vector2f currentP2 = points[pointsEnd].pos + perp * points[pointsEnd].width * 0.5;
|
||||
Vector2f nextRectDir = points[pointsEnd + 1].pos - points[pointsEnd].pos;
|
||||
nextRectDir.normalize();
|
||||
Vector2f nextPerp = perpendicular_vec2(nextRectDir);
|
||||
Vector2f nextP1 = points[pointsEnd].pos - nextPerp * points[pointsEnd].width * 0.5;
|
||||
Vector2f nextP2 = points[pointsEnd].pos + nextPerp * points[pointsEnd].width * 0.5;
|
||||
|
||||
// Find center of wedge
|
||||
Vector2f wedgeCenter = line_line_intersection(currentP1, currentP2, nextP1, nextP2);
|
||||
|
||||
Vector2f wedgeP1 = ((currentP1 - currentP2).dot(nextRectDir)) <= 0.0 ? currentP1 : currentP2;
|
||||
Vector2f wedgeP2 = ((nextP1 - nextP2).dot(rectDir)) <= 0.0 ? nextP1 : nextP2;
|
||||
bool isTopWedge = wedgeP1 == convert_vec2<Vector2f>(topPoints.back());
|
||||
|
||||
float arcRadius;
|
||||
|
||||
std::vector<Vector2f> arcPoints;
|
||||
|
||||
if(wedgeCenter.x() == std::numeric_limits<float>::max()) {
|
||||
wedgeCenter = (currentP1 + currentP2) * 0.5;
|
||||
wedgeP1 = currentP1;
|
||||
wedgeP2 = currentP2;
|
||||
arcRadius = points[pointsEnd].width * 0.5;
|
||||
arcPoints = arc_vec(wedgeCenter, (wedgeP1 - wedgeCenter).normalized(), (wedgeP2 - wedgeCenter).normalized(), (-rectDir).eval(), arcRadius, ARC_SMOOTHNESS);
|
||||
}
|
||||
else {
|
||||
arcRadius = (wedgeCenter - wedgeP1).norm();
|
||||
arcPoints = arc_vec(wedgeCenter, (wedgeP1 - wedgeCenter).normalized(), (wedgeP2 - wedgeCenter).normalized(), lerp_vec((-rectDir).eval(), (-nextRectDir).eval(), 0.5).normalized(), arcRadius, ARC_SMOOTHNESS);
|
||||
}
|
||||
|
||||
arcPoints.emplace(arcPoints.begin(), wedgeP1);
|
||||
arcPoints.emplace_back(wedgeP2);
|
||||
for(size_t i = 0; i < arcPoints.size() - 1; i++) {
|
||||
if(passTriangleFunc(wedgeCenter, arcPoints[i], arcPoints[i + 1])) return;
|
||||
if(isTopWedge)
|
||||
topPoints.emplace_back(convert_vec2<SkPoint>(arcPoints[i]));
|
||||
else
|
||||
bottomPoints.emplace_back(convert_vec2<SkPoint>(arcPoints[i]));
|
||||
}
|
||||
if(isTopWedge)
|
||||
topPoints.emplace_back(convert_vec2<SkPoint>(arcPoints.back()));
|
||||
else
|
||||
bottomPoints.emplace_back(convert_vec2<SkPoint>(arcPoints.back()));
|
||||
}
|
||||
else if(d->hasRoundCaps) { // It's the last point, cap it off
|
||||
Vector2f arcDirStart = (convert_vec2<Vector2f>(vertArray[(newIndex + 1) % 3]) - points.back().pos).normalized();
|
||||
Vector2f arcDirEnd = (convert_vec2<Vector2f>(vertArray[(newIndex + 2) % 3]) - points.back().pos).normalized();
|
||||
Vector2f arcDirCenter = -rectDir;
|
||||
std::vector<Vector2f> arcPoints = arc_vec<float>(points.back().pos, arcDirStart, arcDirEnd, arcDirCenter, points.back().width * 0.5f, ARC_SMOOTHNESS);
|
||||
arcPoints.emplace(arcPoints.begin(), convert_vec2<Vector2f>(vertArray[(newIndex + 1) % 3]));
|
||||
arcPoints.emplace_back(convert_vec2<Vector2f>(vertArray[(newIndex + 2) % 3]));
|
||||
for(size_t i = 0; i < arcPoints.size() - 1; i++) {
|
||||
if(passTriangleFunc(points.back().pos, arcPoints[i], arcPoints[i + 1])) return;
|
||||
topPoints.emplace_back(convert_vec2<SkPoint>(arcPoints[i]));
|
||||
}
|
||||
topPoints.emplace_back(convert_vec2<SkPoint>(arcPoints.back()));
|
||||
}
|
||||
}
|
||||
|
||||
if(bPathBuilder && !topPoints.empty()) {
|
||||
topPoints.insert(topPoints.begin(), bottomPoints.rbegin(), bottomPoints.rend());
|
||||
bPathBuilder->addPolygon({topPoints.data(), topPoints.size()}, false);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawBrushStroke::create_collider() {
|
||||
using namespace SCollision;
|
||||
if(d->points.empty())
|
||||
return;
|
||||
ColliderCollection<float> strokeObjects;
|
||||
std::vector<DrawBrushStrokePoint> points = smooth_points(0, d->points.size() - 1, DEFAULT_SMOOTHNESS);
|
||||
create_triangles([&](Vector2f a, Vector2f b, Vector2f c) {
|
||||
strokeObjects.triangle.emplace_back(a, b, c);
|
||||
return false;
|
||||
}, points, 0, nullptr);
|
||||
strokeObjects.recalculate_bounds();
|
||||
|
||||
SCollision::BVHContainer<float> collisionTree;
|
||||
|
||||
collisionTree.calculate_bvh_recursive(strokeObjects, AABB_PRECHECK_BVH_LEVELS + 1);
|
||||
precheckAABBLevels.clear();
|
||||
add_precheck_aabb_level(AABB_PRECHECK_BVH_LEVELS, collisionTree.children);
|
||||
|
||||
bounds = collisionTree.objects.bounds;
|
||||
}
|
||||
|
||||
void DrawBrushStroke::add_precheck_aabb_level(size_t level, const std::vector<SCollision::BVHContainer<float>>& levelArray) {
|
||||
for(const auto& a : levelArray) {
|
||||
if(level == 0 || a.children.empty())
|
||||
precheckAABBLevels.emplace_back(a.objects.bounds);
|
||||
else
|
||||
add_precheck_aabb_level(level - 1, a.children);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawBrushStroke::update(DrawingProgram& drawP) {
|
||||
}
|
||||
|
||||
bool DrawBrushStroke::collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst) {
|
||||
if(!SCollision::collide(checkAgainst, bounds))
|
||||
return false;
|
||||
|
||||
bool deepPrecheck = false;
|
||||
for(auto& aabb : precheckAABBLevels) {
|
||||
if(SCollision::collide(checkAgainst, aabb)) {
|
||||
deepPrecheck = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!deepPrecheck && !precheckAABBLevels.empty())
|
||||
return false;
|
||||
|
||||
bool toRet = false;
|
||||
std::vector<DrawBrushStrokePoint> points = smooth_points(0, d->points.size() - 1, DEFAULT_SMOOTHNESS);
|
||||
create_triangles([&](Vector2f a, Vector2f b, Vector2f c) {
|
||||
toRet = SCollision::collide(checkAgainst, SCollision::Triangle<float>{a, b, c});
|
||||
return toRet;
|
||||
}, points, 0, nullptr);
|
||||
return toRet;
|
||||
}
|
||||
|
||||
SCollision::AABB<float> DrawBrushStroke::get_obj_coord_bounds() const {
|
||||
return bounds;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
#pragma once
|
||||
#include "../SharedTypes.hpp"
|
||||
#include "DrawComponent.hpp"
|
||||
#include "Helpers/SCollision.hpp"
|
||||
#include <Helpers/Serializers.hpp>
|
||||
|
||||
#ifndef IS_SERVER
|
||||
#include <include/core/SkPath.h>
|
||||
#include <include/core/SkPathBuilder.h>
|
||||
#endif
|
||||
|
||||
#define DRAW_MINIMUM_LIMIT 0.3
|
||||
|
||||
|
||||
struct DrawBrushStrokePoint {
|
||||
Vector2f pos;
|
||||
float width;
|
||||
|
||||
template <typename Archive> void serialize(Archive& a) {
|
||||
a(pos, width);
|
||||
}
|
||||
};
|
||||
|
||||
class DrawBrushStroke : public DrawComponent {
|
||||
public:
|
||||
virtual DrawComponentType get_type() const override;
|
||||
virtual void save(cereal::PortableBinaryOutputArchive& a) const override;
|
||||
virtual void load(cereal::PortableBinaryInputArchive& a) override;
|
||||
virtual void save_file(cereal::PortableBinaryOutputArchive& a) const override;
|
||||
virtual void load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) override;
|
||||
|
||||
struct Data {
|
||||
std::vector<DrawBrushStrokePoint> points;
|
||||
Vector4f color;
|
||||
bool hasRoundCaps;
|
||||
};
|
||||
std::shared_ptr<Data> d = std::make_shared<Data>(); // It's a pointer here since brush strokes cant be edited
|
||||
|
||||
#ifndef IS_SERVER
|
||||
virtual std::shared_ptr<DrawComponent> copy(DrawingProgram& drawP) const override;
|
||||
virtual std::shared_ptr<DrawComponent> deep_copy(DrawingProgram& drawP) const override;
|
||||
|
||||
virtual void draw(SkCanvas* canvas, const DrawData& drawData) override;
|
||||
virtual void initialize_draw_data(DrawingProgram& drawP) override;
|
||||
virtual void update(DrawingProgram& drawP) override;
|
||||
virtual bool collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst) override;
|
||||
void create_collider();
|
||||
virtual void update_from_delayed_ptr(const std::shared_ptr<DrawComponent>& delayedUpdatePtr) override;
|
||||
|
||||
virtual SCollision::AABB<float> get_obj_coord_bounds() const override;
|
||||
|
||||
std::shared_ptr<SkPath> brushPath;
|
||||
SCollision::AABB<float> bounds;
|
||||
|
||||
std::array<std::shared_ptr<SkPath>, 2> brushPathLOD;
|
||||
private:
|
||||
std::vector<DrawBrushStrokePoint> every_nth_point_include_front_and_back(const std::vector<DrawBrushStrokePoint>& pts, size_t n);
|
||||
|
||||
void add_precheck_aabb_level(size_t level, const std::vector<SCollision::BVHContainer<float>>& levelArray);
|
||||
std::vector<SCollision::AABB<float>> precheckAABBLevels;
|
||||
void create_triangles(const std::function<bool(Vector2f, Vector2f, Vector2f)>& passTriangleFunc, const std::vector<DrawBrushStrokePoint>& smoothedPoints, size_t skipVertexCount, std::shared_ptr<SkPathBuilder> bPath);
|
||||
std::vector<size_t> get_wedge_indices(const std::vector<DrawBrushStrokePoint>& points);
|
||||
std::vector<DrawBrushStrokePoint> smooth_points(size_t beginIndex, size_t endIndex, unsigned numOfDivisions);
|
||||
std::vector<DrawBrushStrokePoint> smooth_points_avg(size_t beginIndex, size_t endIndex, unsigned numOfDivisions);
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
@ -1,229 +0,0 @@
|
|||
#include "DrawComponent.hpp"
|
||||
#include "DrawBrushStroke.hpp"
|
||||
#include "DrawEllipse.hpp"
|
||||
#include "DrawRectangle.hpp"
|
||||
#include "DrawTextBox.hpp"
|
||||
#include "DrawImage.hpp"
|
||||
#include "Helpers/Networking/NetLibrary.hpp"
|
||||
#include "../SharedTypes.hpp"
|
||||
#include "../Server/CommandList.hpp"
|
||||
#include <chrono>
|
||||
#include <cereal/types/unordered_set.hpp>
|
||||
|
||||
#ifndef IS_SERVER
|
||||
#include "../MainProgram.hpp"
|
||||
#endif
|
||||
|
||||
void save(cereal::PortableBinaryOutputArchive& a, const CollabListType::ObjectInfo& o) {
|
||||
// Client will send ID, but server should ignore ID and generate its own
|
||||
a(o.pos, o.obj->get_type(), o.obj->id, o.obj->coords, *o.obj);
|
||||
}
|
||||
|
||||
void load(cereal::PortableBinaryInputArchive& a, CollabListType::ObjectInfo& o) {
|
||||
DrawComponentType t;
|
||||
a(o.pos, t);
|
||||
o.obj = DrawComponent::allocate_comp_type(t);
|
||||
a(o.obj->id, o.obj->coords, *o.obj);
|
||||
}
|
||||
|
||||
void SendOrderedComponentVectorOp::save(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a((uint64_t)v->size());
|
||||
for(auto& c : *v)
|
||||
a(*c);
|
||||
}
|
||||
|
||||
void SendOrderedComponentVectorOp::load(cereal::PortableBinaryInputArchive& a) {
|
||||
v->clear();
|
||||
uint64_t vSize;
|
||||
a(vSize);
|
||||
for(uint64_t i = 0; i < vSize; i++)
|
||||
a(*v->emplace_back(std::make_shared<CollabListType::ObjectInfo>()));
|
||||
}
|
||||
|
||||
std::shared_ptr<DrawComponent> DrawComponent::allocate_comp_type(DrawComponentType type) {
|
||||
switch(type) {
|
||||
case DRAWCOMPONENT_BRUSHSTROKE:
|
||||
return std::make_shared<DrawBrushStroke>();
|
||||
case DRAWCOMPONENT_RECTANGLE:
|
||||
return std::make_shared<DrawRectangle>();
|
||||
case DRAWCOMPONENT_ELLIPSE:
|
||||
return std::make_shared<DrawEllipse>();
|
||||
case DRAWCOMPONENT_TEXTBOX:
|
||||
return std::make_shared<DrawTextBox>();
|
||||
case DRAWCOMPONENT_IMAGE:
|
||||
return std::make_shared<DrawImage>();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void DrawComponent::server_send_place_many(MainServer& server, std::vector<CollabListType::ObjectInfoPtr>& comps) {
|
||||
server.netServer->send_items_to_all_clients(RELIABLE_COMMAND_CHANNEL, CLIENT_PLACE_MANY_COMPONENTS, SendOrderedComponentVectorOp{&comps});
|
||||
}
|
||||
|
||||
void DrawComponent::server_send_place(MainServer& server, uint64_t placement) {
|
||||
server.netServer->send_items_to_all_clients(RELIABLE_COMMAND_CHANNEL, CLIENT_PLACE_SINGLE_COMPONENT, placement, get_type(), id, this->coords, *this);
|
||||
}
|
||||
|
||||
void DrawComponent::server_send_erase(MainServer& server, ServerClientID id) {
|
||||
server.netServer->send_items_to_all_clients(RELIABLE_COMMAND_CHANNEL, CLIENT_ERASE_SINGLE_COMPONENT, id);
|
||||
}
|
||||
|
||||
void DrawComponent::server_send_erase_set(MainServer& server, const std::unordered_set<ServerClientID>& ids) {
|
||||
server.netServer->send_items_to_all_clients(RELIABLE_COMMAND_CHANNEL, CLIENT_ERASE_MANY_COMPONENTS, ids);
|
||||
}
|
||||
|
||||
void DrawComponent::server_send_update(MainServer& server, bool final) {
|
||||
if(final)
|
||||
server.netServer->send_items_to_all_clients(RELIABLE_COMMAND_CHANNEL, CLIENT_UPDATE_COMPONENT, final, id, *this);
|
||||
else
|
||||
server.netServer->send_items_to_all_clients(UNRELIABLE_COMMAND_CHANNEL, CLIENT_UPDATE_COMPONENT, final, id, *this);
|
||||
}
|
||||
|
||||
void DrawComponent::server_send_transform_many(MainServer& server, const std::vector<std::pair<ServerClientID, CoordSpaceHelper>>& transforms) {
|
||||
server.netServer->send_items_to_all_clients(RELIABLE_COMMAND_CHANNEL, CLIENT_TRANSFORM_MANY_COMPONENTS, transforms);
|
||||
}
|
||||
|
||||
void DrawComponent::get_used_resources(std::unordered_set<ServerClientID>& v) const {
|
||||
}
|
||||
|
||||
void DrawComponent::remap_resource_ids(std::unordered_map<ServerClientID, ServerClientID>& newIDs) {
|
||||
}
|
||||
|
||||
#ifndef IS_SERVER
|
||||
|
||||
bool DrawComponent::bounds_draw_check(const DrawData& drawData) const {
|
||||
bool a = !drawData.clampDrawBetween || (drawData.clampDrawMinimum < coords.inverseScale);
|
||||
return a;
|
||||
}
|
||||
|
||||
bool DrawComponent::check_timers(DrawingProgram& drawP, const std::shared_ptr<DrawComponent>& delayedUpdatePtr) {
|
||||
if(delayedUpdatePtr) {
|
||||
if(std::chrono::steady_clock::now() - lastUpdateTime > CLIENT_DRAWCOMP_DELAY_TIMER_DURATION) {
|
||||
update_from_delayed_ptr(delayedUpdatePtr);
|
||||
commit_update(drawP);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DrawComponent::commit_update(DrawingProgram& drawP, bool invalidateCache) {
|
||||
auto lockedCollabInfo = collabListInfo.lock();
|
||||
if(invalidateCache && lockedCollabInfo && worldAABB)
|
||||
drawP.preupdate_component(lockedCollabInfo);
|
||||
initialize_draw_data(drawP);
|
||||
calculate_world_bounds();
|
||||
if(invalidateCache && lockedCollabInfo && worldAABB)
|
||||
drawP.preupdate_component(lockedCollabInfo);
|
||||
}
|
||||
|
||||
void DrawComponent::commit_transform(DrawingProgram& drawP, bool invalidateCache) {
|
||||
auto lockedCollabInfo = collabListInfo.lock();
|
||||
if(invalidateCache && lockedCollabInfo && worldAABB)
|
||||
drawP.preupdate_component(lockedCollabInfo);
|
||||
calculate_world_bounds();
|
||||
if(invalidateCache && lockedCollabInfo && worldAABB)
|
||||
drawP.preupdate_component(lockedCollabInfo);
|
||||
}
|
||||
|
||||
void DrawComponent::scale_up(const WorldScalar& scaleUpAmount) {
|
||||
coords.scale_about(WorldVec{0, 0}, scaleUpAmount, true);
|
||||
calculate_world_bounds();
|
||||
}
|
||||
|
||||
void DrawComponent::client_send_place_many(DrawingProgram& drawP, std::vector<CollabListType::ObjectInfoPtr>& comps) {
|
||||
drawP.world.con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_PLACE_MANY_COMPONENTS, SendOrderedComponentVectorOp{&comps});
|
||||
}
|
||||
|
||||
void DrawComponent::client_send_place(DrawingProgram& drawP) {
|
||||
//if(collabListInfo.lock()) If this fails, then there's a real issue
|
||||
drawP.world.con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_PLACE_SINGLE_COMPONENT, collabListInfo.lock()->pos, get_type(), id, this->coords, *this);
|
||||
}
|
||||
|
||||
void DrawComponent::client_send_update(DrawingProgram& drawP, bool final) {
|
||||
if(collabListInfo.lock()) {
|
||||
if(final)
|
||||
drawP.world.con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_UPDATE_COMPONENT, final, id, *this);
|
||||
else
|
||||
drawP.world.con.client_send_items_to_server(UNRELIABLE_COMMAND_CHANNEL, SERVER_UPDATE_COMPONENT, final, id, *this);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawComponent::client_send_transform_many(DrawingProgram& drawP, const std::vector<std::pair<ServerClientID, CoordSpaceHelper>>& transforms) {
|
||||
drawP.world.con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_TRANSFORM_MANY_COMPONENTS, transforms);
|
||||
}
|
||||
|
||||
void DrawComponent::client_send_erase(DrawingProgram& drawP) {
|
||||
if(collabListInfo.lock())
|
||||
drawP.world.con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_ERASE_SINGLE_COMPONENT, id);
|
||||
}
|
||||
|
||||
void DrawComponent::client_send_erase_set(DrawingProgram& drawP, const std::unordered_set<ServerClientID>& ids) {
|
||||
drawP.world.con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_ERASE_MANY_COMPONENTS, ids);
|
||||
}
|
||||
|
||||
bool DrawComponent::collides_with_world_coords(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection<WorldScalar>& checkAgainstWorld) {
|
||||
return collides_with(camCoords, checkAgainstWorld, camCoords.world_collider_to_coords<SCollision::ColliderCollection<float>>(checkAgainstWorld));
|
||||
}
|
||||
|
||||
bool DrawComponent::collides_with_cam_coords(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection<float>& checkAgainstCam) {
|
||||
return collides_with(camCoords, camCoords.collider_to_world<SCollision::ColliderCollection<WorldScalar>>(checkAgainstCam), checkAgainstCam);
|
||||
}
|
||||
|
||||
// We could just send one of the checkAgainst colliders, since they represent the same thing, but sending both saves on redundant transformations, since we can save both of the versions on the executor side
|
||||
bool DrawComponent::collides_with(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection<WorldScalar>& checkAgainstWorld, const SCollision::ColliderCollection<float>& checkAgainstCam) {
|
||||
if((camCoords.inverseScale << DRAWCOMP_MAX_SHIFT_BEFORE_STOP_COLLISIONS) < coords.inverseScale) // Object is too large, just dismiss the collision
|
||||
return false;
|
||||
else if((camCoords.inverseScale >> DRAWCOMP_COLLIDE_MIN_SHIFT_TINY) >= coords.inverseScale) {
|
||||
if(!worldAABB)
|
||||
return false;
|
||||
return SCollision::collide(checkAgainstCam, camCoords.to_space(worldAABB->min));
|
||||
}
|
||||
else {
|
||||
auto c = checkAgainstWorld.transform<float>(
|
||||
[&coords = coords](const WorldVec& a) {
|
||||
return coords.to_space(a);
|
||||
},
|
||||
[&coords = coords](const WorldScalar& a) {
|
||||
return coords.scalar_to_space(a);
|
||||
});
|
||||
return collides_within_coords(c);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawComponent::canvas_do_calculated_transform(SkCanvas* canvas) {
|
||||
canvas->scale(drawSetupData.transformData.scale, drawSetupData.transformData.scale);
|
||||
canvas->rotate(drawSetupData.transformData.rotation);
|
||||
canvas->translate(drawSetupData.transformData.translation.x(), drawSetupData.transformData.translation.y());
|
||||
}
|
||||
|
||||
void DrawComponent::calculate_draw_transform(const DrawData& drawData) {
|
||||
drawSetupData.shouldDraw = !drawData.clampDrawBetween || (drawData.clampDrawMinimum < coords.inverseScale);
|
||||
if(drawSetupData.shouldDraw) {
|
||||
drawSetupData.shouldDraw = !worldAABB || SCollision::collide(*worldAABB, drawData.cam.viewingAreaGenerousCollider);
|
||||
if(drawSetupData.shouldDraw) {
|
||||
if(drawData.mipMapLevelTwo > coords.inverseScale)
|
||||
drawSetupData.mipmapLevel = 2;
|
||||
else if(drawData.mipMapLevelOne > coords.inverseScale)
|
||||
drawSetupData.mipmapLevel = 1;
|
||||
else
|
||||
drawSetupData.mipmapLevel = 0;
|
||||
drawSetupData.transformData.translation = -coords.to_space(drawData.cam.c.pos);
|
||||
drawSetupData.transformData.rotation = (coords.rotation - drawData.cam.c.rotation) * 180.0 / std::numbers::pi;
|
||||
drawSetupData.transformData.scale = std::min(static_cast<float>(coords.inverseScale / drawData.cam.c.inverseScale), static_cast<float>(1 << DRAWCOMP_MAX_SHIFT_BEFORE_STOP_SCALING));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawComponent::calculate_world_bounds() {
|
||||
worldAABB = std::optional<SCollision::AABB<WorldScalar>>(coords.collider_to_world<SCollision::AABB<WorldScalar>, SCollision::AABB<float>>(get_obj_coord_bounds()));
|
||||
}
|
||||
|
||||
std::optional<SCollision::AABB<WorldScalar>> DrawComponent::get_world_bounds() const {
|
||||
return worldAABB;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
DrawComponent::~DrawComponent() { }
|
||||
|
|
@ -1,117 +0,0 @@
|
|||
#pragma once
|
||||
#include "Helpers/SCollision.hpp"
|
||||
#include <Helpers/Hashes.hpp>
|
||||
#include "cereal/archives/portable_binary.hpp"
|
||||
#include "../SharedTypes.hpp"
|
||||
#include "../CoordSpaceHelper.hpp"
|
||||
#include "../Server/MainServer.hpp"
|
||||
#include "../CollabList.hpp"
|
||||
#include <Helpers/VersionNumber.hpp>
|
||||
|
||||
#ifndef IS_SERVER
|
||||
#include "../DrawData.hpp"
|
||||
#include <chrono>
|
||||
#include <include/core/SkCanvas.h>
|
||||
#include <include/core/SkVertices.h>
|
||||
#endif
|
||||
|
||||
#define CLIENT_DRAWCOMP_DELAY_TIMER_DURATION std::chrono::seconds(6)
|
||||
|
||||
#define DRAWCOMP_MAX_SHIFT_BEFORE_STOP_COLLISIONS 14
|
||||
#define DRAWCOMP_MAX_SHIFT_BEFORE_STOP_SCALING 14
|
||||
#define DRAWCOMP_MIN_SHIFT_BEFORE_DISAPPEAR 11
|
||||
#define DRAWCOMP_COLLIDE_MIN_SHIFT_TINY 9
|
||||
#define DRAWCOMP_MIPMAP_LEVEL_ONE 2
|
||||
#define DRAWCOMP_MIPMAP_LEVEL_TWO 5
|
||||
|
||||
class DrawingProgram;
|
||||
class DrawComponent;
|
||||
class DrawingProgramCacheBVHNode;
|
||||
|
||||
typedef CollabList<std::shared_ptr<DrawComponent>, ServerClientID> CollabListType;
|
||||
|
||||
void save(cereal::PortableBinaryOutputArchive& a, const CollabListType::ObjectInfo& o);
|
||||
void load(cereal::PortableBinaryInputArchive& a, CollabListType::ObjectInfo& o);
|
||||
|
||||
struct SendOrderedComponentVectorOp {
|
||||
std::vector<CollabListType::ObjectInfoPtr>* v;
|
||||
void save(cereal::PortableBinaryOutputArchive& a) const;
|
||||
void load(cereal::PortableBinaryInputArchive& a);
|
||||
};
|
||||
|
||||
class DrawComponent {
|
||||
public:
|
||||
static std::shared_ptr<DrawComponent> allocate_comp_type(DrawComponentType type);
|
||||
virtual void save(cereal::PortableBinaryOutputArchive& a) const = 0;
|
||||
virtual void load(cereal::PortableBinaryInputArchive& a) = 0;
|
||||
virtual void save_file(cereal::PortableBinaryOutputArchive& a) const = 0;
|
||||
virtual void load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) = 0;
|
||||
virtual DrawComponentType get_type() const = 0;
|
||||
virtual void get_used_resources(std::unordered_set<ServerClientID>& v) const;
|
||||
virtual void remap_resource_ids(std::unordered_map<ServerClientID, ServerClientID>& newIDs);
|
||||
virtual ~DrawComponent();
|
||||
|
||||
// NOTE: Permanantly altering the coordinates during transformation can lead to loss of detail for smaller objects
|
||||
CoordSpaceHelper coords;
|
||||
|
||||
struct DrawSetupData {
|
||||
struct TransformDrawData {
|
||||
Vector2f translation;
|
||||
float rotation;
|
||||
float scale;
|
||||
} transformData;
|
||||
bool shouldDraw = false;
|
||||
uint8_t mipmapLevel = 0;
|
||||
} drawSetupData;
|
||||
|
||||
void canvas_do_calculated_transform(SkCanvas* canvas);
|
||||
void calculate_draw_transform(const DrawData& drawData);
|
||||
|
||||
void server_send_place(MainServer& server, uint64_t placement);
|
||||
|
||||
void scale_up(const WorldScalar& scaleUpAmount);
|
||||
|
||||
static void server_send_place_many(MainServer& server, std::vector<CollabListType::ObjectInfoPtr>& comps);
|
||||
static void server_send_erase(MainServer& server, ServerClientID id);
|
||||
static void server_send_erase_set(MainServer& server, const std::unordered_set<ServerClientID>& ids);
|
||||
void server_send_update(MainServer& server, bool final);
|
||||
static void server_send_transform_many(MainServer& server, const std::vector<std::pair<ServerClientID, CoordSpaceHelper>>& transforms);
|
||||
|
||||
std::weak_ptr<CollabListType::ObjectInfo> collabListInfo;
|
||||
std::weak_ptr<DrawingProgramCacheBVHNode> parentBvhNode;
|
||||
ServerClientID id = {0, 0};
|
||||
|
||||
#ifndef IS_SERVER
|
||||
std::chrono::time_point<std::chrono::steady_clock> lastUpdateTime;
|
||||
|
||||
bool bounds_draw_check(const DrawData& drawData) const;
|
||||
|
||||
bool check_timers(DrawingProgram& drawP, const std::shared_ptr<DrawComponent>& delayedUpdatePtr);
|
||||
static void client_send_place_many(DrawingProgram& drawP, std::vector<CollabListType::ObjectInfoPtr>& comps);
|
||||
void client_send_place(DrawingProgram& drawP);
|
||||
void client_send_erase(DrawingProgram& drawP);
|
||||
static void client_send_erase_set(DrawingProgram& drawP, const std::unordered_set<ServerClientID>& ids);
|
||||
void client_send_update(DrawingProgram& drawP, bool final);
|
||||
static void client_send_transform_many(DrawingProgram& drawP, const std::vector<std::pair<ServerClientID, CoordSpaceHelper>>& transforms);
|
||||
|
||||
std::optional<SCollision::AABB<WorldScalar>> worldAABB;
|
||||
|
||||
std::optional<SCollision::AABB<WorldScalar>> get_world_bounds() const;
|
||||
void calculate_world_bounds();
|
||||
virtual SCollision::AABB<float> get_obj_coord_bounds() const = 0;
|
||||
|
||||
virtual std::shared_ptr<DrawComponent> copy(DrawingProgram& drawP) const = 0;
|
||||
virtual std::shared_ptr<DrawComponent> deep_copy(DrawingProgram& drawP) const = 0;
|
||||
|
||||
virtual void draw(SkCanvas* canvas, const DrawData& drawData) = 0;
|
||||
void commit_update(DrawingProgram& drawP, bool invalidateCache = true); // invalidateCache = false version should be thread safe
|
||||
void commit_transform(DrawingProgram& drawP, bool invalidateCache = true); // invalidateCache = false version should be thread safe
|
||||
virtual void initialize_draw_data(DrawingProgram& drawP) = 0;
|
||||
virtual void update(DrawingProgram& drawP) = 0;
|
||||
virtual bool collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst) = 0;
|
||||
bool collides_with_world_coords(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection<WorldScalar>& checkAgainstWorld);
|
||||
bool collides_with_cam_coords(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection<float>& checkAgainstCam);
|
||||
bool collides_with(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection<WorldScalar>& checkAgainstWorld, const SCollision::ColliderCollection<float>& checkAgainstCam);
|
||||
virtual void update_from_delayed_ptr(const std::shared_ptr<DrawComponent>& delayedUpdatePtr) = 0;
|
||||
#endif
|
||||
};
|
||||
|
|
@ -1,154 +0,0 @@
|
|||
#include "DrawEllipse.hpp"
|
||||
#include "Helpers/ConvertVec.hpp"
|
||||
#include "Helpers/SCollision.hpp"
|
||||
#include <include/core/SkPathBuilder.h>
|
||||
|
||||
#ifndef IS_SERVER
|
||||
#include "../DrawCollision.hpp"
|
||||
#endif
|
||||
|
||||
DrawComponentType DrawEllipse::get_type() const {
|
||||
return DRAWCOMPONENT_ELLIPSE;
|
||||
}
|
||||
|
||||
void DrawEllipse::save(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(d.strokeColor, d.fillColor, d.strokeWidth, d.p1, d.p2, d.fillStrokeMode);
|
||||
}
|
||||
|
||||
void DrawEllipse::load(cereal::PortableBinaryInputArchive& a) {
|
||||
a(d.strokeColor, d.fillColor, d.strokeWidth, d.p1, d.p2, d.fillStrokeMode);
|
||||
}
|
||||
|
||||
void DrawEllipse::save_file(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(d.strokeColor, d.fillColor, d.strokeWidth, d.p1, d.p2, d.fillStrokeMode);
|
||||
}
|
||||
|
||||
void DrawEllipse::load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) {
|
||||
a(d.strokeColor, d.fillColor, d.strokeWidth, d.p1, d.p2, d.fillStrokeMode);
|
||||
}
|
||||
|
||||
#ifndef IS_SERVER
|
||||
std::shared_ptr<DrawComponent> DrawEllipse::copy(DrawingProgram& drawP) const {
|
||||
auto a = std::make_shared<DrawEllipse>();
|
||||
a->d = d;
|
||||
a->coords = coords;
|
||||
return a;
|
||||
}
|
||||
|
||||
std::shared_ptr<DrawComponent> DrawEllipse::deep_copy(DrawingProgram& drawP) const {
|
||||
auto a = std::make_shared<DrawEllipse>();
|
||||
a->d = d;
|
||||
a->coords = coords;
|
||||
a->ellipsePath = ellipsePath;
|
||||
a->collisionTree = collisionTree;
|
||||
return a;
|
||||
}
|
||||
|
||||
void DrawEllipse::update_from_delayed_ptr(const std::shared_ptr<DrawComponent>& delayedUpdatePtr) {
|
||||
std::shared_ptr<DrawEllipse> newPtr = std::static_pointer_cast<DrawEllipse>(delayedUpdatePtr);
|
||||
d = newPtr->d;
|
||||
}
|
||||
|
||||
void DrawEllipse::draw(SkCanvas* canvas, const DrawData& drawData) {
|
||||
if(drawSetupData.shouldDraw) {
|
||||
canvas->save();
|
||||
canvas_do_calculated_transform(canvas);
|
||||
if(d.fillStrokeMode == 0 || d.fillStrokeMode == 2) {
|
||||
SkPaint p;
|
||||
p.setStyle(SkPaint::kFill_Style);
|
||||
p.setColor4f(convert_vec4<SkColor4f>(d.fillColor));
|
||||
canvas->drawPath(ellipsePath, p);
|
||||
}
|
||||
if(d.fillStrokeMode == 1 || d.fillStrokeMode == 2) {
|
||||
SkPaint p;
|
||||
p.setStyle(SkPaint::kStroke_Style);
|
||||
p.setColor4f(convert_vec4<SkColor4f>(d.strokeColor));
|
||||
p.setStrokeWidth(d.strokeWidth);
|
||||
canvas->drawPath(ellipsePath, p);
|
||||
}
|
||||
canvas->restore();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawEllipse::create_draw_data() {
|
||||
SkPathBuilder ellipsePathBuilder;
|
||||
SkRect newRect = SkRect::MakeLTRB(d.p1.x(), d.p1.y(), d.p2.x(), d.p2.y());
|
||||
ellipsePathBuilder.addOval(newRect);
|
||||
ellipsePath = ellipsePathBuilder.detach();
|
||||
}
|
||||
|
||||
void DrawEllipse::initialize_draw_data(DrawingProgram& drawP) {
|
||||
create_draw_data();
|
||||
create_collider();
|
||||
}
|
||||
|
||||
void DrawEllipse::update(DrawingProgram& drawP) {
|
||||
}
|
||||
|
||||
bool DrawEllipse::collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst) {
|
||||
return collisionTree.is_collide(checkAgainst);
|
||||
}
|
||||
|
||||
void DrawEllipse::create_collider() {
|
||||
using namespace SCollision;
|
||||
ColliderCollection<float> strokeObjects;
|
||||
|
||||
if(d.fillStrokeMode == 0) {
|
||||
Vector2f ellipseCenter = (d.p1 + d.p2) * 0.5;
|
||||
float a = ellipseCenter.x() - d.p1.x();
|
||||
float b = ellipseCenter.y() - d.p1.y();
|
||||
Vector2f prevPoint = ellipseCenter + Vector2f{a * std::cos(0), b * std::sin(0)};
|
||||
unsigned numOfSegments = 20;
|
||||
float segmentStep = 1.0 / numOfSegments;
|
||||
float t = 0.0;
|
||||
for(unsigned i = 0; i <= numOfSegments; i++) {
|
||||
Vector2f nextPoint = ellipseCenter + Vector2f{a * std::cos(t), b * std::sin(t)};
|
||||
strokeObjects.triangle.emplace_back(ellipseCenter, prevPoint, nextPoint);
|
||||
prevPoint = nextPoint;
|
||||
t += segmentStep * std::numbers::pi * 2.0;
|
||||
}
|
||||
}
|
||||
else if(d.fillStrokeMode == 1) {
|
||||
std::vector<Vector2f> points;
|
||||
|
||||
Vector2f ellipseCenter = (d.p1 + d.p2) * 0.5;
|
||||
float a = ellipseCenter.x() - d.p1.x();
|
||||
float b = ellipseCenter.y() - d.p1.y();
|
||||
unsigned numOfSegments = 40;
|
||||
float segmentStep = 1.0 / numOfSegments;
|
||||
float t = 0.0;
|
||||
for(unsigned i = 0; i < numOfSegments; i++) {
|
||||
points.emplace_back(ellipseCenter + Vector2f{a * std::cos(t), b * std::sin(t)});
|
||||
t += segmentStep * std::numbers::pi * 2.0;
|
||||
}
|
||||
generate_polyline(strokeObjects, points, d.strokeWidth, true);
|
||||
|
||||
}
|
||||
else if(d.fillStrokeMode == 2) {
|
||||
float strokeRadius = d.strokeWidth * 0.5f;
|
||||
Vector2f wNewP1 = d.p1 - Vector2f{strokeRadius, strokeRadius};
|
||||
Vector2f wNewP2 = d.p2 + Vector2f{strokeRadius, strokeRadius};
|
||||
Vector2f ellipseCenter = (wNewP1 + wNewP2) * 0.5;
|
||||
float a = ellipseCenter.x() - wNewP1.x();
|
||||
float b = ellipseCenter.y() - wNewP1.y();
|
||||
Vector2f prevPoint = ellipseCenter + Vector2f{a * std::cos(0), b * std::sin(0)};
|
||||
unsigned numOfSegments = 20;
|
||||
float segmentStep = 1.0 / numOfSegments;
|
||||
float t = 0.0;
|
||||
for(unsigned i = 0; i <= numOfSegments; i++) {
|
||||
Vector2f nextPoint = ellipseCenter + Vector2f{a * std::cos(t), b * std::sin(t)};
|
||||
strokeObjects.triangle.emplace_back(ellipseCenter, prevPoint, nextPoint);
|
||||
prevPoint = nextPoint;
|
||||
t += segmentStep * std::numbers::pi * 2.0;
|
||||
}
|
||||
}
|
||||
|
||||
collisionTree.clear();
|
||||
collisionTree.calculate_bvh_recursive(strokeObjects);
|
||||
}
|
||||
|
||||
SCollision::AABB<float> DrawEllipse::get_obj_coord_bounds() const {
|
||||
return collisionTree.objects.bounds;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
#pragma once
|
||||
#include "../SharedTypes.hpp"
|
||||
#include "DrawComponent.hpp"
|
||||
#include "../CoordSpaceHelper.hpp"
|
||||
|
||||
#ifndef IS_SERVER
|
||||
#include <include/core/SkPath.h>
|
||||
#endif
|
||||
|
||||
class DrawEllipse : public DrawComponent {
|
||||
public:
|
||||
virtual void save(cereal::PortableBinaryOutputArchive& a) const override;
|
||||
virtual void load(cereal::PortableBinaryInputArchive& a) override;
|
||||
virtual void save_file(cereal::PortableBinaryOutputArchive& a) const override;
|
||||
virtual void load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) override;
|
||||
virtual DrawComponentType get_type() const override;
|
||||
|
||||
// User input data
|
||||
struct Data {
|
||||
Vector4f strokeColor;
|
||||
Vector4f fillColor;
|
||||
float strokeWidth;
|
||||
Vector2f p1;
|
||||
Vector2f p2;
|
||||
uint8_t fillStrokeMode;
|
||||
bool operator==(const Data& o) const = default;
|
||||
} d;
|
||||
|
||||
#ifndef IS_SERVER
|
||||
virtual std::shared_ptr<DrawComponent> copy(DrawingProgram& drawP) const override;
|
||||
virtual std::shared_ptr<DrawComponent> deep_copy(DrawingProgram& drawP) const override;
|
||||
|
||||
virtual void draw(SkCanvas* canvas, const DrawData& drawData) override;
|
||||
virtual void initialize_draw_data(DrawingProgram& drawP) override;
|
||||
virtual void update(DrawingProgram& drawP) override;
|
||||
virtual bool collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst) override;
|
||||
virtual void update_from_delayed_ptr(const std::shared_ptr<DrawComponent>& delayedUpdatePtr) override;
|
||||
void create_draw_data();
|
||||
void create_collider();
|
||||
|
||||
virtual SCollision::AABB<float> get_obj_coord_bounds() const override;
|
||||
|
||||
SkPath ellipsePath;
|
||||
|
||||
// Cache
|
||||
SCollision::BVHContainer<float> collisionTree;
|
||||
private:
|
||||
void create_triangles(const std::function<bool(Vector2f, Vector2f, Vector2f)>& passTriangleFunc);
|
||||
#endif
|
||||
};
|
||||
|
|
@ -1,136 +0,0 @@
|
|||
#include "DrawImage.hpp"
|
||||
#include "Helpers/ConvertVec.hpp"
|
||||
#include "Helpers/MathExtras.hpp"
|
||||
#include "Helpers/SCollision.hpp"
|
||||
#include "../SharedTypes.hpp"
|
||||
#include "../World.hpp"
|
||||
|
||||
#include "../MainProgram.hpp"
|
||||
|
||||
#ifndef IS_SERVER
|
||||
#include <include/core/SkSamplingOptions.h>
|
||||
#include "../DrawCollision.hpp"
|
||||
#endif
|
||||
|
||||
DrawComponentType DrawImage::get_type() const {
|
||||
return DRAWCOMPONENT_IMAGE;
|
||||
}
|
||||
|
||||
void DrawImage::save(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(d.p1, d.p2, d.imageID);
|
||||
}
|
||||
|
||||
void DrawImage::load(cereal::PortableBinaryInputArchive& a) {
|
||||
a(d.p1, d.p2, d.imageID);
|
||||
}
|
||||
|
||||
void DrawImage::save_file(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(d.p1, d.p2, d.imageID);
|
||||
}
|
||||
|
||||
void DrawImage::load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) {
|
||||
a(d.p1, d.p2, d.imageID);
|
||||
}
|
||||
|
||||
void DrawImage::get_used_resources(std::unordered_set<ServerClientID>& v) const {
|
||||
v.emplace(d.imageID);
|
||||
}
|
||||
|
||||
void DrawImage::remap_resource_ids(std::unordered_map<ServerClientID, ServerClientID>& newIDs) {
|
||||
auto it = newIDs.find(d.imageID);
|
||||
if(it != newIDs.end())
|
||||
d.imageID = it->second;
|
||||
}
|
||||
|
||||
#ifndef IS_SERVER
|
||||
std::shared_ptr<DrawComponent> DrawImage::copy(DrawingProgram& drawP) const {
|
||||
auto a = std::make_shared<DrawImage>();
|
||||
a->d = d;
|
||||
a->coords = coords;
|
||||
return a;
|
||||
}
|
||||
|
||||
std::shared_ptr<DrawComponent> DrawImage::deep_copy(DrawingProgram& drawP) const {
|
||||
auto a = std::make_shared<DrawImage>();
|
||||
a->d = d;
|
||||
a->coords = coords;
|
||||
a->imRect = imRect;
|
||||
a->collisionTree = collisionTree;
|
||||
return a;
|
||||
}
|
||||
|
||||
void DrawImage::update_from_delayed_ptr(const std::shared_ptr<DrawComponent>& delayedUpdatePtr) {
|
||||
std::shared_ptr<DrawImage> newPtr = std::static_pointer_cast<DrawImage>(delayedUpdatePtr);
|
||||
d = newPtr->d;
|
||||
}
|
||||
|
||||
void DrawImage::draw_download_progress_bar(SkCanvas* canvas, const DrawData& drawData, float progress) {
|
||||
if(progress == 0.0f)
|
||||
return;
|
||||
calculate_draw_transform(drawData);
|
||||
if(drawSetupData.shouldDraw) {
|
||||
canvas->save();
|
||||
canvas_do_calculated_transform(canvas);
|
||||
SkPaint p;
|
||||
p.setStroke(true);
|
||||
p.setStrokeWidth(10.0f);
|
||||
p.setStrokeCap(SkPaint::kRound_Cap);
|
||||
p.setColor4f(drawData.main->toolbar.io->theme->fillColor1);
|
||||
Vector2f center = convert_vec2<Vector2f>(imRect.center());
|
||||
|
||||
const float DOWNLOAD_ARC_RADIUS = 50.0f;
|
||||
canvas->drawArc(SkRect::MakeLTRB(center.x() - DOWNLOAD_ARC_RADIUS, center.y() - DOWNLOAD_ARC_RADIUS, center.x() + DOWNLOAD_ARC_RADIUS, center.y() + DOWNLOAD_ARC_RADIUS), 0.0f, progress * 360.0f, false, p);
|
||||
canvas->restore();
|
||||
drawSetupData.shouldDraw = false;
|
||||
}
|
||||
}
|
||||
|
||||
void DrawImage::draw(SkCanvas* canvas, const DrawData& drawData) {
|
||||
if(drawSetupData.shouldDraw) {
|
||||
canvas->save();
|
||||
canvas_do_calculated_transform(canvas);
|
||||
ResourceDisplay* display = drawData.rMan->get_display_data(d.imageID);
|
||||
if(display)
|
||||
display->draw(canvas, drawData, imRect);
|
||||
else
|
||||
canvas->drawRect(imRect, SkPaint({0.5f, 0.5f, 0.5f, 0.5f}));
|
||||
canvas->restore();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawImage::update(DrawingProgram& drawP) {
|
||||
ResourceDisplay* display = drawP.world.drawData.rMan->get_display_data(d.imageID);
|
||||
if(display) {
|
||||
if(display->update_draw() && collabListInfo.lock())
|
||||
drawP.invalidate_cache_at_component(collabListInfo.lock());
|
||||
}
|
||||
}
|
||||
|
||||
void DrawImage::initialize_draw_data(DrawingProgram& drawP) {
|
||||
create_draw_data();
|
||||
create_collider();
|
||||
}
|
||||
|
||||
bool DrawImage::collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst) {
|
||||
return collisionTree.is_collide(checkAgainst);
|
||||
}
|
||||
|
||||
void DrawImage::create_draw_data() {
|
||||
imRect = SkRect::MakeLTRB(d.p1.x(), d.p1.y(), d.p2.x(), d.p2.y());
|
||||
}
|
||||
|
||||
void DrawImage::create_collider() {
|
||||
using namespace SCollision;
|
||||
ColliderCollection<float> strokeObjects;
|
||||
std::array<Vector2f, 4> newT = triangle_from_rect_points(d.p1, d.p2);
|
||||
strokeObjects.triangle.emplace_back(newT[0], newT[1], newT[2]);
|
||||
strokeObjects.triangle.emplace_back(newT[2], newT[3], newT[0]);
|
||||
collisionTree.clear();
|
||||
collisionTree.calculate_bvh_recursive(strokeObjects);
|
||||
}
|
||||
|
||||
SCollision::AABB<float> DrawImage::get_obj_coord_bounds() const {
|
||||
return collisionTree.objects.bounds;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
#pragma once
|
||||
#include "../SharedTypes.hpp"
|
||||
#include "DrawComponent.hpp"
|
||||
#include "../CoordSpaceHelper.hpp"
|
||||
|
||||
#ifndef IS_SERVER
|
||||
#include <include/core/SkPath.h>
|
||||
#endif
|
||||
|
||||
class DrawImage : public DrawComponent {
|
||||
public:
|
||||
virtual void save(cereal::PortableBinaryOutputArchive& a) const override;
|
||||
virtual void load(cereal::PortableBinaryInputArchive& a) override;
|
||||
virtual void save_file(cereal::PortableBinaryOutputArchive& a) const override;
|
||||
virtual void load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) override;
|
||||
virtual DrawComponentType get_type() const override;
|
||||
virtual void get_used_resources(std::unordered_set<ServerClientID>& v) const override;
|
||||
virtual void remap_resource_ids(std::unordered_map<ServerClientID, ServerClientID>& newIDs) override;
|
||||
|
||||
void draw_download_progress_bar(SkCanvas* canvas, const DrawData& drawData, float progress);
|
||||
|
||||
// User input data
|
||||
struct Data {
|
||||
Vector2f p1;
|
||||
Vector2f p2;
|
||||
|
||||
ServerClientID imageID;
|
||||
} d;
|
||||
|
||||
#ifndef IS_SERVER
|
||||
virtual std::shared_ptr<DrawComponent> copy(DrawingProgram& drawP) const override;
|
||||
virtual std::shared_ptr<DrawComponent> deep_copy(DrawingProgram& drawP) const override;
|
||||
|
||||
virtual void draw(SkCanvas* canvas, const DrawData& drawData) override;
|
||||
virtual void initialize_draw_data(DrawingProgram& drawP) override;
|
||||
virtual void update(DrawingProgram& drawP) override;
|
||||
virtual bool collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst) override;
|
||||
void create_draw_data();
|
||||
void create_collider();
|
||||
virtual void update_from_delayed_ptr(const std::shared_ptr<DrawComponent>& delayedUpdatePtr) override;
|
||||
|
||||
virtual SCollision::AABB<float> get_obj_coord_bounds() const override;
|
||||
|
||||
SkRect imRect;
|
||||
|
||||
SCollision::BVHContainer<float> collisionTree;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
@ -1,138 +0,0 @@
|
|||
#include "DrawRectangle.hpp"
|
||||
#include "Helpers/ConvertVec.hpp"
|
||||
#include "Helpers/MathExtras.hpp"
|
||||
#include "Helpers/SCollision.hpp"
|
||||
#include <include/core/SkPaint.h>
|
||||
#include <include/core/SkPathBuilder.h>
|
||||
#include "../SharedTypes.hpp"
|
||||
|
||||
#ifndef IS_SERVER
|
||||
#include "../DrawCollision.hpp"
|
||||
#endif
|
||||
|
||||
DrawComponentType DrawRectangle::get_type() const {
|
||||
return DRAWCOMPONENT_RECTANGLE;
|
||||
}
|
||||
|
||||
void DrawRectangle::save(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(d.strokeColor, d.fillColor, d.cornerRadius, d.strokeWidth, d.p1, d.p2, d.fillStrokeMode);
|
||||
}
|
||||
|
||||
void DrawRectangle::load(cereal::PortableBinaryInputArchive& a) {
|
||||
a(d.strokeColor, d.fillColor, d.cornerRadius, d.strokeWidth, d.p1, d.p2, d.fillStrokeMode);
|
||||
}
|
||||
|
||||
void DrawRectangle::save_file(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(d.strokeColor, d.fillColor, d.cornerRadius, d.strokeWidth, d.p1, d.p2, d.fillStrokeMode);
|
||||
}
|
||||
|
||||
void DrawRectangle::load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) {
|
||||
a(d.strokeColor, d.fillColor, d.cornerRadius, d.strokeWidth, d.p1, d.p2, d.fillStrokeMode);
|
||||
}
|
||||
|
||||
#ifndef IS_SERVER
|
||||
std::shared_ptr<DrawComponent> DrawRectangle::copy(DrawingProgram& drawP) const {
|
||||
auto a = std::make_shared<DrawRectangle>();
|
||||
a->d = d;
|
||||
a->coords = coords;
|
||||
return a;
|
||||
}
|
||||
|
||||
std::shared_ptr<DrawComponent> DrawRectangle::deep_copy(DrawingProgram& drawP) const {
|
||||
auto a = std::make_shared<DrawRectangle>();
|
||||
a->d = d;
|
||||
a->coords = coords;
|
||||
a->rectPath = rectPath;
|
||||
a->collisionTree = collisionTree;
|
||||
return a;
|
||||
}
|
||||
|
||||
void DrawRectangle::update_from_delayed_ptr(const std::shared_ptr<DrawComponent>& delayedUpdatePtr) {
|
||||
std::shared_ptr<DrawRectangle> newPtr = std::static_pointer_cast<DrawRectangle>(delayedUpdatePtr);
|
||||
d = newPtr->d;
|
||||
}
|
||||
|
||||
void DrawRectangle::draw(SkCanvas* canvas, const DrawData& drawData) {
|
||||
if(drawSetupData.shouldDraw) {
|
||||
canvas->save();
|
||||
canvas_do_calculated_transform(canvas);
|
||||
if(d.fillStrokeMode == 0 || d.fillStrokeMode == 2) {
|
||||
SkPaint p;
|
||||
p.setStyle(SkPaint::kFill_Style);
|
||||
p.setStrokeCap(SkPaint::kRound_Cap);
|
||||
p.setColor4f(convert_vec4<SkColor4f>(d.fillColor));
|
||||
canvas->drawPath(rectPath, p);
|
||||
}
|
||||
if(d.fillStrokeMode == 1 || d.fillStrokeMode == 2) {
|
||||
SkPaint p;
|
||||
p.setStyle(SkPaint::kStroke_Style);
|
||||
p.setStrokeCap(SkPaint::kRound_Cap);
|
||||
p.setColor4f(convert_vec4<SkColor4f>(d.strokeColor));
|
||||
p.setStrokeWidth(d.strokeWidth);
|
||||
canvas->drawPath(rectPath, p);
|
||||
}
|
||||
canvas->restore();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawRectangle::update(DrawingProgram& drawP) {
|
||||
}
|
||||
|
||||
void DrawRectangle::initialize_draw_data(DrawingProgram& drawP) {
|
||||
create_draw_data();
|
||||
create_collider();
|
||||
}
|
||||
|
||||
bool DrawRectangle::collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst) {
|
||||
return collisionTree.is_collide(checkAgainst);
|
||||
}
|
||||
|
||||
void DrawRectangle::create_draw_data() {
|
||||
SkPathBuilder rectPathBuilder;
|
||||
if(d.p1.x() == d.p2.x() || d.p1.y() == d.p2.y()) {
|
||||
rectPathBuilder.moveTo(convert_vec2<SkPoint>(d.p1));
|
||||
rectPathBuilder.lineTo(convert_vec2<SkPoint>(d.p2));
|
||||
rectPath = rectPathBuilder.detach();
|
||||
}
|
||||
else {
|
||||
SkRRect newRect = SkRRect::MakeRectXY(SkRect::MakeLTRB(d.p1.x(), d.p1.y(), d.p2.x(), d.p2.y()), d.cornerRadius, d.cornerRadius);
|
||||
rectPathBuilder.addRRect(newRect);
|
||||
rectPath = rectPathBuilder.detach();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawRectangle::create_collider() {
|
||||
using namespace SCollision;
|
||||
ColliderCollection<float> strokeObjects;
|
||||
if(d.fillStrokeMode == 0) {
|
||||
std::array<Vector2f, 4> newT = triangle_from_rect_points(d.p1, d.p2);
|
||||
strokeObjects.triangle.emplace_back(newT[0], newT[1], newT[2]);
|
||||
strokeObjects.triangle.emplace_back(newT[2], newT[3], newT[0]);
|
||||
}
|
||||
else if(d.fillStrokeMode == 1) {
|
||||
if(d.p1.x() == d.p2.x() || d.p1.y() == d.p2.y()) {
|
||||
std::vector<Vector2f> points = {d.p1, d.p2};
|
||||
generate_polyline(strokeObjects, points, d.strokeWidth, true);
|
||||
}
|
||||
else {
|
||||
std::array<Vector2f, 4> pointArr = triangle_from_rect_points(d.p1, d.p2);
|
||||
std::vector<Vector2f> points(pointArr.begin(), pointArr.end());
|
||||
generate_polyline(strokeObjects, points, d.strokeWidth, true);
|
||||
}
|
||||
}
|
||||
else if(d.fillStrokeMode == 2) {
|
||||
float strokeRadius = d.strokeWidth * 0.5f;
|
||||
std::array<Vector2f, 4> newT = triangle_from_rect_points((d.p1 - Vector2f{strokeRadius, strokeRadius}).eval(), (d.p2 + Vector2f{strokeRadius, strokeRadius}).eval());
|
||||
strokeObjects.triangle.emplace_back(newT[0], newT[1], newT[2]);
|
||||
strokeObjects.triangle.emplace_back(newT[2], newT[3], newT[0]);
|
||||
}
|
||||
|
||||
collisionTree.clear();
|
||||
collisionTree.calculate_bvh_recursive(strokeObjects);
|
||||
}
|
||||
|
||||
SCollision::AABB<float> DrawRectangle::get_obj_coord_bounds() const {
|
||||
return collisionTree.objects.bounds;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
#pragma once
|
||||
#include "../SharedTypes.hpp"
|
||||
#include "DrawComponent.hpp"
|
||||
#include "../CoordSpaceHelper.hpp"
|
||||
|
||||
#ifndef IS_SERVER
|
||||
#include <include/core/SkPath.h>
|
||||
#endif
|
||||
|
||||
class DrawRectangle : public DrawComponent {
|
||||
public:
|
||||
virtual void save(cereal::PortableBinaryOutputArchive& a) const override;
|
||||
virtual void load(cereal::PortableBinaryInputArchive& a) override;
|
||||
virtual void save_file(cereal::PortableBinaryOutputArchive& a) const override;
|
||||
virtual void load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) override;
|
||||
virtual DrawComponentType get_type() const override;
|
||||
|
||||
// User input data
|
||||
struct Data {
|
||||
Vector4f strokeColor;
|
||||
Vector4f fillColor;
|
||||
float cornerRadius;
|
||||
float strokeWidth;
|
||||
Vector2f p1;
|
||||
Vector2f p2;
|
||||
uint8_t fillStrokeMode;
|
||||
bool operator==(const Data& o) const = default;
|
||||
} d;
|
||||
|
||||
#ifndef IS_SERVER
|
||||
virtual std::shared_ptr<DrawComponent> copy(DrawingProgram& drawP) const override;
|
||||
virtual std::shared_ptr<DrawComponent> deep_copy(DrawingProgram& drawP) const override;
|
||||
|
||||
virtual void draw(SkCanvas* canvas, const DrawData& drawData) override;
|
||||
virtual void initialize_draw_data(DrawingProgram& drawP) override;
|
||||
virtual void update(DrawingProgram& drawP) override;
|
||||
virtual bool collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst) override;
|
||||
void create_draw_data();
|
||||
void create_collider();
|
||||
virtual void update_from_delayed_ptr(const std::shared_ptr<DrawComponent>& delayedUpdatePtr) override;
|
||||
|
||||
virtual SCollision::AABB<float> get_obj_coord_bounds() const override;
|
||||
|
||||
SkPath rectPath;
|
||||
|
||||
SCollision::BVHContainer<float> collisionTree;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
@ -1,152 +0,0 @@
|
|||
#include "DrawTextBox.hpp"
|
||||
#include "Helpers/ConvertVec.hpp"
|
||||
#include <cereal/types/string.hpp>
|
||||
|
||||
#ifndef IS_SERVER
|
||||
#include "../MainProgram.hpp"
|
||||
#include "Helpers/SCollision.hpp"
|
||||
#include "../DrawingProgram/DrawingProgram.hpp"
|
||||
#include "../DrawCollision.hpp"
|
||||
#endif
|
||||
|
||||
using namespace RichText;
|
||||
|
||||
DrawTextBox::DrawTextBox():
|
||||
textBox(std::make_shared<TextBox>()),
|
||||
cursor(std::make_shared<TextBox::Cursor>())
|
||||
{
|
||||
skia::textlayout::TextStyle tStyle;
|
||||
textBox->set_initial_text_style(tStyle);
|
||||
|
||||
textBox->set_initial_text_style_modifier(std::make_shared<ColorTextStyleModifier>(Vector4f{1.0f, 1.0f, 1.0f, 1.0f}));
|
||||
textBox->set_initial_text_style_modifier(std::make_shared<SizeTextStyleModifier>(18.0f));
|
||||
textBox->set_initial_text_style_modifier(std::make_shared<FontFamiliesTextStyleModifier>(std::vector<SkString>{SkString{"Roboto"}}));
|
||||
}
|
||||
|
||||
DrawComponentType DrawTextBox::get_type() const {
|
||||
return DRAWCOMPONENT_TEXTBOX;
|
||||
}
|
||||
|
||||
void DrawTextBox::save(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(d.editing, d.p1, d.p2, textBox->get_rich_text_data(), *cursor);
|
||||
}
|
||||
|
||||
void DrawTextBox::load(cereal::PortableBinaryInputArchive& a) {
|
||||
TextData richText;
|
||||
a(d.editing, d.p1, d.p2, richText, *cursor);
|
||||
textBox->set_rich_text_data(richText);
|
||||
}
|
||||
|
||||
void DrawTextBox::save_file(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(d.p1, d.p2, textBox->get_rich_text_data());
|
||||
}
|
||||
|
||||
void DrawTextBox::load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) {
|
||||
if(version < VersionNumber(0, 3, 0)) {
|
||||
bool loadedEditing; // Unused
|
||||
Vector4f textColor;
|
||||
float textSize;
|
||||
RichText::TextBox::Cursor loadedCursor; // Unused, Equivalent to old cursor structure, just paragraph index and text index flipped
|
||||
std::string currentText;
|
||||
a(loadedEditing, d.p1, d.p2, textColor, textSize, loadedCursor, currentText);
|
||||
textBox->insert({0, 0}, currentText);
|
||||
textBox->set_text_style_modifier_between({0, 0}, textBox->move(RichText::TextBox::Movement::END, {0, 0}), std::make_shared<ColorTextStyleModifier>(textColor));
|
||||
textBox->set_text_style_modifier_between({0, 0}, textBox->move(RichText::TextBox::Movement::END, {0, 0}), std::make_shared<SizeTextStyleModifier>(textSize));
|
||||
}
|
||||
else {
|
||||
TextData richText;
|
||||
a(d.p1, d.p2, richText);
|
||||
textBox->set_rich_text_data(richText);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef IS_SERVER
|
||||
std::shared_ptr<DrawComponent> DrawTextBox::copy(DrawingProgram& drawP) const {
|
||||
auto a = std::make_shared<DrawTextBox>();
|
||||
a->d = d;
|
||||
*a->cursor = *cursor;
|
||||
a->coords = coords;
|
||||
a->textBox->set_rich_text_data(textBox->get_rich_text_data());
|
||||
a->init_text_box(drawP);
|
||||
return a;
|
||||
}
|
||||
|
||||
std::shared_ptr<DrawComponent> DrawTextBox::deep_copy(DrawingProgram& drawP) const {
|
||||
auto a = std::make_shared<DrawTextBox>();
|
||||
a->d = d;
|
||||
*a->cursor = *cursor;
|
||||
a->coords = coords;
|
||||
a->textBox->set_rich_text_data(textBox->get_rich_text_data());
|
||||
a->collisionTree = collisionTree;
|
||||
a->init_text_box(drawP);
|
||||
return a;
|
||||
}
|
||||
|
||||
void DrawTextBox::update_from_delayed_ptr(const std::shared_ptr<DrawComponent>& delayedUpdatePtr) {
|
||||
std::shared_ptr<DrawTextBox> newPtr = std::static_pointer_cast<DrawTextBox>(delayedUpdatePtr);
|
||||
d = newPtr->d;
|
||||
*cursor = *newPtr->cursor;
|
||||
textBox->set_rich_text_data(newPtr->textBox->get_rich_text_data());
|
||||
}
|
||||
|
||||
void DrawTextBox::init_text_box(DrawingProgram& drawP) {
|
||||
textBox->set_font_data(drawP.world.main.fonts); // Getting a segfault relating to the paragraph cache means that the font collection hasn't been set yet
|
||||
textBox->set_width(d.p2.x() - d.p1.x() - TEXTBOX_PADDING * 2.0f);
|
||||
}
|
||||
|
||||
void DrawTextBox::draw(SkCanvas* canvas, const DrawData& drawData) {
|
||||
if(drawSetupData.shouldDraw) {
|
||||
canvas->save();
|
||||
canvas_do_calculated_transform(canvas);
|
||||
SkRect clipR = SkRect::MakeLTRB(d.p1.x(), d.p1.y(), d.p2.x(), d.p2.y());
|
||||
if(d.editing) {
|
||||
SkPaint p;
|
||||
p.setStyle(SkPaint::kStroke_Style);
|
||||
p.setStrokeWidth(0.0f);
|
||||
p.setColor4f({0.5f, 0.5f, 0.5f, 1.0f});
|
||||
canvas->drawRect(clipR, p);
|
||||
}
|
||||
canvas->clipRect(clipR);
|
||||
canvas->translate(d.p1.x() + TEXTBOX_PADDING, d.p1.y() + TEXTBOX_PADDING);
|
||||
|
||||
TextBox::PaintOpts paintOpts;
|
||||
paintOpts.cursorColor = {0.7f, 0.7f, 1.0f};
|
||||
if(d.editing && cursor)
|
||||
paintOpts.cursor = *cursor;
|
||||
|
||||
textBox->paint(canvas, paintOpts);
|
||||
canvas->restore();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawTextBox::update(DrawingProgram& drawP) {
|
||||
}
|
||||
|
||||
Vector2f DrawTextBox::get_mouse_pos(DrawingProgram& drawP) {
|
||||
return coords.get_mouse_pos(drawP.world) - d.p1 - Vector2f{TEXTBOX_PADDING, TEXTBOX_PADDING};
|
||||
}
|
||||
|
||||
void DrawTextBox::initialize_draw_data(DrawingProgram& drawP) {
|
||||
init_text_box(drawP);
|
||||
create_collider();
|
||||
}
|
||||
|
||||
bool DrawTextBox::collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst) {
|
||||
return collisionTree.is_collide(checkAgainst);
|
||||
}
|
||||
|
||||
void DrawTextBox::create_collider() {
|
||||
using namespace SCollision;
|
||||
ColliderCollection<float> strokeObjects;
|
||||
std::array<Vector2f, 4> newT = triangle_from_rect_points(d.p1, d.p2);
|
||||
strokeObjects.triangle.emplace_back(newT[0], newT[1], newT[2]);
|
||||
strokeObjects.triangle.emplace_back(newT[2], newT[3], newT[0]);
|
||||
collisionTree.clear();
|
||||
collisionTree.calculate_bvh_recursive(strokeObjects);
|
||||
}
|
||||
|
||||
SCollision::AABB<float> DrawTextBox::get_obj_coord_bounds() const {
|
||||
return collisionTree.objects.bounds;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
#pragma once
|
||||
#include "../SharedTypes.hpp"
|
||||
#include "DrawComponent.hpp"
|
||||
#include "../CoordSpaceHelper.hpp"
|
||||
#include "../RichText/TextBox.hpp"
|
||||
|
||||
#ifndef IS_SERVER
|
||||
#include <include/core/SkPath.h>
|
||||
#endif
|
||||
|
||||
class DrawTextBox : public DrawComponent {
|
||||
public:
|
||||
constexpr static float TEXTBOX_PADDING = 5.0f;
|
||||
|
||||
DrawTextBox();
|
||||
virtual DrawComponentType get_type() const override;
|
||||
virtual void save(cereal::PortableBinaryOutputArchive& a) const override;
|
||||
virtual void load(cereal::PortableBinaryInputArchive& a) override;
|
||||
virtual void save_file(cereal::PortableBinaryOutputArchive& a) const override;
|
||||
virtual void load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) override;
|
||||
|
||||
// User input data
|
||||
struct Data {
|
||||
Vector2f p1;
|
||||
Vector2f p2;
|
||||
bool editing = false; // Should probably move this out of the Data struct
|
||||
bool operator==(const Data& o) const = default;
|
||||
bool operator!=(const Data& o) const = default;
|
||||
} d;
|
||||
|
||||
#ifndef IS_SERVER
|
||||
virtual std::shared_ptr<DrawComponent> copy(DrawingProgram& drawP) const override;
|
||||
virtual std::shared_ptr<DrawComponent> deep_copy(DrawingProgram& drawP) const override;
|
||||
|
||||
virtual void draw(SkCanvas* canvas, const DrawData& drawData) override;
|
||||
virtual void initialize_draw_data(DrawingProgram& drawP) override;
|
||||
virtual bool collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst) override;
|
||||
virtual void update(DrawingProgram& drawP) override;
|
||||
void create_collider();
|
||||
virtual void update_from_delayed_ptr(const std::shared_ptr<DrawComponent>& delayedUpdatePtr) override;
|
||||
void init_text_box(DrawingProgram& drawP);
|
||||
Vector2f get_mouse_pos(DrawingProgram& drawP);
|
||||
|
||||
virtual SCollision::AABB<float> get_obj_coord_bounds() const override;
|
||||
|
||||
std::shared_ptr<RichText::TextBox> textBox;
|
||||
std::shared_ptr<RichText::TextBox::Cursor> cursor;
|
||||
SCollision::BVHContainer<float> collisionTree;
|
||||
#endif
|
||||
};
|
||||
|
|
@ -1,10 +1,12 @@
|
|||
#include "BrushTool.hpp"
|
||||
#include <Helpers/ConvertVec.hpp>
|
||||
#include "../DrawComponents/DrawBrushStroke.hpp"
|
||||
#include "../GUIStuff/GUIManager.hpp"
|
||||
#include "DrawingProgram.hpp"
|
||||
#include "../MainProgram.hpp"
|
||||
#include "DrawingProgramToolBase.hpp"
|
||||
#include "../CanvasComponents/BrushStrokeCanvasComponent.hpp"
|
||||
#include "Helpers/NetworkingObjects/NetObjTemporaryPtr.decl.hpp"
|
||||
#include "Helpers/NetworkingObjects/NetObjWeakPtr.hpp"
|
||||
#include <chrono>
|
||||
|
||||
#define VEL_SMOOTH_MIN 0.6
|
||||
|
|
@ -23,8 +25,8 @@ void BrushTool::switch_tool(DrawingProgramToolType newTool) {
|
|||
commit_stroke();
|
||||
}
|
||||
|
||||
bool BrushTool::extensive_point_checking(const Vector2f& newPoint) {
|
||||
auto& points = controls.intermediateItem->d->points;
|
||||
bool BrushTool::extensive_point_checking(const BrushStrokeCanvasComponent& brushStroke, const Vector2f& newPoint) {
|
||||
auto& points = brushStroke.d->points;
|
||||
if(points.size() >= 1 && (newPoint - points[points.size() - 1].pos).norm() < MINIMUM_DISTANCE_TO_NEXT_POINT)
|
||||
return false;
|
||||
if(points.size() >= 2 && (newPoint - points[points.size() - 2].pos).norm() < MINIMUM_DISTANCE_TO_NEXT_POINT)
|
||||
|
|
@ -34,8 +36,8 @@ bool BrushTool::extensive_point_checking(const Vector2f& newPoint) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool BrushTool::extensive_point_checking_back(const Vector2f& newPoint) {
|
||||
auto& points = controls.intermediateItem->d->points;
|
||||
bool BrushTool::extensive_point_checking_back(const BrushStrokeCanvasComponent& brushStroke, const Vector2f& newPoint) {
|
||||
auto& points = brushStroke.d->points;
|
||||
if(points.size() >= 2 && (newPoint - points[points.size() - 2].pos).norm() < MINIMUM_DISTANCE_TO_NEXT_POINT)
|
||||
return false;
|
||||
if(points.size() >= 3 && (newPoint - points[points.size() - 3].pos).norm() < MINIMUM_DISTANCE_TO_NEXT_POINT)
|
||||
|
|
@ -49,107 +51,117 @@ void BrushTool::tool_update() {
|
|||
if(drawP.controls.cursorHoveringOverCanvas)
|
||||
drawP.world.main.input.hideCursor = true;
|
||||
|
||||
if(!controls.isDrawing)
|
||||
controls.penSmoothingData.clear();
|
||||
if(!isDrawing)
|
||||
penSmoothingData.clear();
|
||||
|
||||
if(drawP.world.main.input.pen.isDown && drawP.controls.leftClickHeld && drawP.world.main.toolbar.tabletOptions.pressureAffectsBrushWidth) {
|
||||
while(!controls.penSmoothingData.empty() && (std::chrono::steady_clock::now() - controls.penSmoothingData.front().t) > std::chrono::milliseconds(static_cast<int>(drawP.world.main.toolbar.tabletOptions.smoothingSamplingTime * 1000.0f)))
|
||||
controls.penSmoothingData.pop_front();
|
||||
controls.penSmoothingData.emplace_back(
|
||||
while(!penSmoothingData.empty() && (std::chrono::steady_clock::now() - penSmoothingData.front().t) > std::chrono::milliseconds(static_cast<int>(drawP.world.main.toolbar.tabletOptions.smoothingSamplingTime * 1000.0f)))
|
||||
penSmoothingData.pop_front();
|
||||
penSmoothingData.emplace_back(
|
||||
drawP.world.main.input.pen.pressure,
|
||||
std::chrono::steady_clock::now()
|
||||
);
|
||||
float averagePressure = 0.0f;
|
||||
for(auto& [width, t] : controls.penSmoothingData)
|
||||
for(auto& [width, t] : penSmoothingData)
|
||||
averagePressure += width;
|
||||
averagePressure /= controls.penSmoothingData.size();
|
||||
controls.penWidth = averagePressure;
|
||||
if(controls.penWidth != 0.0f) {
|
||||
averagePressure /= penSmoothingData.size();
|
||||
penWidth = averagePressure;
|
||||
if(penWidth != 0.0f) {
|
||||
float brushMinSize = drawP.world.main.toolbar.tabletOptions.brushMinimumSize;
|
||||
controls.penWidth = brushMinSize + controls.penWidth * (1.0f - brushMinSize);
|
||||
penWidth = brushMinSize + penWidth * (1.0f - brushMinSize);
|
||||
}
|
||||
}
|
||||
else
|
||||
controls.penWidth = 1.0f;
|
||||
penWidth = 1.0f;
|
||||
|
||||
bool isPenDown = false;
|
||||
if(drawP.world.main.input.pen.isDown && drawP.controls.leftClickHeld)
|
||||
isPenDown = controls.penWidth != 0.0;
|
||||
isPenDown = penWidth != 0.0;
|
||||
else if(drawP.controls.leftClickHeld)
|
||||
isPenDown = true;
|
||||
|
||||
if(isPenDown) {
|
||||
if(!controls.isDrawing) {
|
||||
controls.isDrawing = true;
|
||||
controls.intermediateItem = std::make_shared<DrawBrushStroke>();
|
||||
DrawBrushStrokePoint p;
|
||||
if(!isDrawing) {
|
||||
isDrawing = true;
|
||||
CanvasComponentContainer* newBrushStrokeContainer = new CanvasComponentContainer(drawP.world.netObjMan, CanvasComponent::CompType::BRUSHSTROKE);
|
||||
BrushStrokeCanvasComponent& newBrushStroke = static_cast<BrushStrokeCanvasComponent&>(newBrushStrokeContainer->get_comp());
|
||||
|
||||
BrushStrokeCanvasComponentPoint p;
|
||||
p.pos = drawP.world.main.input.mouse.pos;
|
||||
p.width = drawP.controls.relativeWidth * controls.penWidth;
|
||||
controls.prevPointUnaltered = p.pos;
|
||||
controls.intermediateItem->d->points.emplace_back(p);
|
||||
controls.intermediateItem->d->color = drawP.controls.foregroundColor;
|
||||
controls.intermediateItem->d->hasRoundCaps = controls.hasRoundCaps;
|
||||
controls.intermediateItem->coords = drawP.world.drawData.cam.c;
|
||||
controls.intermediateItem->lastUpdateTime = std::chrono::steady_clock::now();
|
||||
uint64_t placement = drawP.components.client_list().size();
|
||||
auto objAdd = drawP.components.client_insert(placement, controls.intermediateItem);
|
||||
controls.intermediateItem->commit_update(drawP);
|
||||
controls.intermediateItem->client_send_place(drawP);
|
||||
// NOTE: The data isnt finallized at this point, so a BVH isn't generated for an object that you undo while youre drawing (this isnt just for brush strokes)
|
||||
// The fix for now is to generate a BVH for an object when you redo it
|
||||
drawP.add_undo_place_component(objAdd);
|
||||
controls.addedTemporaryPoint = false;
|
||||
p.width = drawP.controls.relativeWidth * penWidth;
|
||||
prevPointUnaltered = p.pos;
|
||||
newBrushStroke.d->points.emplace_back(p);
|
||||
newBrushStroke.d->color = drawP.controls.foregroundColor;
|
||||
newBrushStroke.d->hasRoundCaps = hasRoundCaps;
|
||||
newBrushStrokeContainer->coords = drawP.world.drawData.cam.c;
|
||||
intermediateContainer = drawP.components->push_back_and_send_create(drawP.components, newBrushStrokeContainer)->obj;
|
||||
addedTemporaryPoint = false;
|
||||
}
|
||||
else {
|
||||
controls.intermediateItem->lastUpdateTime = std::chrono::steady_clock::now();
|
||||
NetworkingObjects::NetObjTemporaryPtr<CanvasComponentContainer> containerPtr = intermediateContainer.lock();
|
||||
if(!containerPtr) {
|
||||
isDrawing = false;
|
||||
intermediateContainer.reset();
|
||||
return;
|
||||
}
|
||||
BrushStrokeCanvasComponent& brushStroke = static_cast<BrushStrokeCanvasComponent&>(containerPtr->get_comp());
|
||||
|
||||
DrawBrushStrokePoint p;
|
||||
p.pos = controls.intermediateItem->coords.get_mouse_pos(drawP.world);
|
||||
p.width = drawP.controls.relativeWidth * controls.penWidth;
|
||||
BrushStrokeCanvasComponentPoint p;
|
||||
p.pos = containerPtr->coords.get_mouse_pos(drawP.world);
|
||||
p.width = drawP.controls.relativeWidth * penWidth;
|
||||
|
||||
if(!controls.addedTemporaryPoint) {
|
||||
if(extensive_point_checking(p.pos)) {
|
||||
controls.intermediateItem->d->points.emplace_back(p);
|
||||
controls.addedTemporaryPoint = true;
|
||||
if(!addedTemporaryPoint) {
|
||||
if(extensive_point_checking(brushStroke, p.pos)) {
|
||||
brushStroke.d->points.emplace_back(p);
|
||||
addedTemporaryPoint = true;
|
||||
}
|
||||
else
|
||||
controls.intermediateItem->d->points.back().width = std::max(controls.intermediateItem->d->points.back().width, p.width);
|
||||
brushStroke.d->points.back().width = std::max(brushStroke.d->points.back().width, p.width);
|
||||
}
|
||||
|
||||
if(controls.addedTemporaryPoint) {
|
||||
const DrawBrushStrokePoint& prevP = controls.intermediateItem->d->points[controls.intermediateItem->d->points.size() - 2];
|
||||
if(addedTemporaryPoint) {
|
||||
const BrushStrokeCanvasComponentPoint& prevP = brushStroke.d->points[brushStroke.d->points.size() - 2];
|
||||
float distToPrev = (p.pos - prevP.pos).norm();
|
||||
if(extensive_point_checking_back(p.pos)) {
|
||||
controls.intermediateItem->d->points.back().pos = p.pos;
|
||||
controls.intermediateItem->d->points.back().width = std::max(controls.intermediateItem->d->points.back().width, p.width);
|
||||
controls.intermediateItem->d->points[controls.intermediateItem->d->points.size() - 2].width = std::max(controls.intermediateItem->d->points[controls.intermediateItem->d->points.size() - 2].width, p.width);
|
||||
if(extensive_point_checking_back(brushStroke, p.pos)) {
|
||||
brushStroke.d->points.back().pos = p.pos;
|
||||
brushStroke.d->points.back().width = std::max(brushStroke.d->points.back().width, p.width);
|
||||
brushStroke.d->points[brushStroke.d->points.size() - 2].width = std::max(brushStroke.d->points[brushStroke.d->points.size() - 2].width, p.width);
|
||||
}
|
||||
if((!controls.drawingMinimumRelativeToSize && distToPrev >= 10.0) || (controls.drawingMinimumRelativeToSize && distToPrev >= drawP.controls.relativeWidth * DRAW_MINIMUM_LIMIT)) {
|
||||
controls.intermediateItem->d->points.back() = p;
|
||||
controls.addedTemporaryPoint = false;
|
||||
controls.intermediateItem->client_send_update(drawP, false);
|
||||
if((!drawingMinimumRelativeToSize && distToPrev >= 10.0) || (drawingMinimumRelativeToSize && distToPrev >= drawP.controls.relativeWidth * BrushStrokeCanvasComponent::DRAW_MINIMUM_LIMIT)) {
|
||||
brushStroke.d->points.back() = p;
|
||||
addedTemporaryPoint = false;
|
||||
containerPtr->send_comp_update(drawP, true);
|
||||
|
||||
if(controls.midwayInterpolation) {
|
||||
if(controls.intermediateItem->d->points.size() != 2) // Don't interpolate the first point
|
||||
controls.intermediateItem->d->points[controls.intermediateItem->d->points.size() - 2].pos = (controls.prevPointUnaltered + p.pos) * 0.5;
|
||||
controls.prevPointUnaltered = p.pos;
|
||||
if(midwayInterpolation) {
|
||||
if(brushStroke.d->points.size() != 2) // Don't interpolate the first point
|
||||
brushStroke.d->points[brushStroke.d->points.size() - 2].pos = (prevPointUnaltered + p.pos) * 0.5;
|
||||
prevPointUnaltered = p.pos;
|
||||
}
|
||||
}
|
||||
controls.intermediateItem->commit_update(drawP);
|
||||
containerPtr->commit_update(drawP);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(controls.isDrawing) {
|
||||
controls.isDrawing = false;
|
||||
else if(isDrawing) {
|
||||
isDrawing = false;
|
||||
commit_stroke();
|
||||
}
|
||||
}
|
||||
|
||||
void BrushTool::commit_stroke() {
|
||||
NetworkingObjects::NetObjTemporaryPtr<CanvasComponentContainer> lockedPtr = intermediateContainer.lock();
|
||||
if(lockedPtr) {
|
||||
lockedPtr->commit_update(drawP);
|
||||
lockedPtr->send_comp_update(drawP, true);
|
||||
}
|
||||
intermediateContainer.reset();
|
||||
}
|
||||
|
||||
void BrushTool::gui_toolbox() {
|
||||
Toolbar& t = drawP.world.main.toolbar;
|
||||
t.gui.push_id("brush tool");
|
||||
t.gui.text_label_centered("Brush");
|
||||
t.gui.checkbox_field("hasroundcaps", "Round Caps", &controls.hasRoundCaps);
|
||||
t.gui.checkbox_field("hasroundcaps", "Round Caps", &hasRoundCaps);
|
||||
t.gui.slider_scalar_field("relwidth", "Size", &drawP.controls.relativeWidth, 3.0f, 40.0f);
|
||||
t.gui.pop_id();
|
||||
}
|
||||
|
|
@ -161,17 +173,7 @@ bool BrushTool::right_click_popup_gui(Vector2f popupPos) {
|
|||
}
|
||||
|
||||
bool BrushTool::prevent_undo_or_redo() {
|
||||
return controls.intermediateItem != nullptr;
|
||||
}
|
||||
|
||||
void BrushTool::commit_stroke() {
|
||||
if(controls.intermediateItem && controls.intermediateItem->collabListInfo.lock()) {
|
||||
if(!controls.intermediateItem->d->points.empty()) {
|
||||
controls.intermediateItem->client_send_update(drawP, true);
|
||||
controls.intermediateItem->commit_update(drawP);
|
||||
}
|
||||
}
|
||||
controls.intermediateItem = nullptr;
|
||||
return isDrawing;
|
||||
}
|
||||
|
||||
void BrushTool::draw(SkCanvas* canvas, const DrawData& drawData) {
|
||||
|
|
@ -181,8 +183,8 @@ void BrushTool::draw(SkCanvas* canvas, const DrawData& drawData) {
|
|||
linePaint.setStroke(true);
|
||||
linePaint.setStrokeWidth(0.0f);
|
||||
float width;
|
||||
if(controls.isDrawing)
|
||||
width = drawP.controls.relativeWidth * controls.penWidth * 0.5f;
|
||||
if(isDrawing)
|
||||
width = drawP.controls.relativeWidth * penWidth * 0.5f;
|
||||
else
|
||||
width = drawP.controls.relativeWidth * 0.5f;
|
||||
Vector2f pos = drawData.main->input.mouse.pos;
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
#pragma once
|
||||
#include "../SharedTypes.hpp"
|
||||
#include "DrawingProgramToolBase.hpp"
|
||||
#include <Helpers/Hashes.hpp>
|
||||
#include <include/core/SkPath.h>
|
||||
#include <include/core/SkVertices.h>
|
||||
#include <Helpers/SCollision.hpp>
|
||||
#include <deque>
|
||||
#include <any>
|
||||
#include <include/core/SkCanvas.h>
|
||||
#include "../CanvasComponents/CanvasComponentContainer.hpp"
|
||||
#include <Helpers/NetworkingObjects/NetObjWeakPtr.hpp>
|
||||
|
||||
class DrawingProgram;
|
||||
class DrawBrushStroke;
|
||||
struct DrawData;
|
||||
class BrushStrokeCanvasComponent;
|
||||
|
||||
class BrushTool : public DrawingProgramToolBase {
|
||||
public:
|
||||
|
|
@ -24,24 +24,22 @@ class BrushTool : public DrawingProgramToolBase {
|
|||
virtual void draw(SkCanvas* canvas, const DrawData& drawData) override;
|
||||
virtual bool prevent_undo_or_redo() override;
|
||||
private:
|
||||
bool extensive_point_checking_back(const BrushStrokeCanvasComponent& brushStroke, const Vector2f& newPoint);
|
||||
bool extensive_point_checking(const BrushStrokeCanvasComponent& brushStroke, const Vector2f& newPoint);
|
||||
void commit_stroke();
|
||||
bool extensive_point_checking_back(const Vector2f& newPoint);
|
||||
bool extensive_point_checking(const Vector2f& newPoint);
|
||||
|
||||
struct BrushToolControls {
|
||||
float penWidth = 1.0f;
|
||||
bool addedTemporaryPoint = false;
|
||||
struct SmoothingPoint {
|
||||
float val;
|
||||
std::chrono::steady_clock::time_point t;
|
||||
};
|
||||
std::deque<SmoothingPoint> penSmoothingData;
|
||||
float penWidth = 1.0f;
|
||||
bool addedTemporaryPoint = false;
|
||||
struct SmoothingPoint {
|
||||
float val;
|
||||
std::chrono::steady_clock::time_point t;
|
||||
};
|
||||
std::deque<SmoothingPoint> penSmoothingData;
|
||||
|
||||
std::shared_ptr<DrawBrushStroke> intermediateItem;
|
||||
bool isDrawing = false;
|
||||
bool hasRoundCaps = true;
|
||||
bool drawingMinimumRelativeToSize = true;
|
||||
bool midwayInterpolation = true;
|
||||
Vector2f prevPointUnaltered = {0, 0};
|
||||
} controls;
|
||||
NetworkingObjects::NetObjWeakPtr<CanvasComponentContainer> intermediateContainer;
|
||||
bool isDrawing = false;
|
||||
bool hasRoundCaps = true;
|
||||
bool drawingMinimumRelativeToSize = true;
|
||||
bool midwayInterpolation = true;
|
||||
Vector2f prevPointUnaltered = {0, 0};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
#include <include/core/SkPaint.h>
|
||||
#include <include/core/SkVertices.h>
|
||||
#include "../DrawCamera.hpp"
|
||||
#include "../DrawComponents/DrawComponent.hpp"
|
||||
#include "EllipseDrawTool.hpp"
|
||||
#include "EyeDropperTool.hpp"
|
||||
#include "GridModifyTool.hpp"
|
||||
|
|
@ -14,13 +13,13 @@
|
|||
#include <optional>
|
||||
#include <Helpers/ConvertVec.hpp>
|
||||
#include <cereal/types/vector.hpp>
|
||||
#include "../DrawComponents/DrawImage.hpp"
|
||||
#include <include/core/SkImage.h>
|
||||
#include <include/core/SkSurface.h>
|
||||
#include "../World.hpp"
|
||||
#include "../MainProgram.hpp"
|
||||
#include "Helpers/FileDownloader.hpp"
|
||||
#include "Helpers/StringHelpers.hpp"
|
||||
#include <Helpers/FileDownloader.hpp>
|
||||
#include <Helpers/NetworkingObjects/NetObjID.hpp>
|
||||
#include <Helpers/StringHelpers.hpp>
|
||||
#include "LassoSelectTool.hpp"
|
||||
#include <Helpers/Logger.hpp>
|
||||
#include <Helpers/Parallel.hpp>
|
||||
|
|
@ -34,153 +33,66 @@
|
|||
DrawingProgram::DrawingProgram(World& initWorld):
|
||||
world(initWorld),
|
||||
compCache(*this),
|
||||
components([&](){ return world.get_new_id(); }),
|
||||
selection(*this)
|
||||
{
|
||||
drawTool = DrawingProgramToolBase::allocate_tool_type(*this, DrawingProgramToolType::BRUSH);
|
||||
}
|
||||
|
||||
components.clientInsertCallback = [&](const CollabListType::ObjectInfoPtr& c) {
|
||||
if(addToCompCacheOnInsert)
|
||||
compCache.add_component(c);
|
||||
DrawComponentType t = c->obj->get_type();
|
||||
if(t == DRAWCOMPONENT_IMAGE)
|
||||
updateableComponents.emplace(c->obj);
|
||||
};
|
||||
components.clientEraseCallback = [&](const CollabListType::ObjectInfoPtr& c) {
|
||||
void DrawingProgram::init() {
|
||||
if(world.netObjMan.is_server()) {
|
||||
components = world.netObjMan.make_obj<NetworkingObjects::NetObjOrderedList<CanvasComponentContainer>>();
|
||||
set_component_list_callbacks();
|
||||
}
|
||||
}
|
||||
|
||||
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(drawTool->get_type() == DrawingProgramToolType::ERASER) {
|
||||
EraserTool* eraserTool = static_cast<EraserTool*>(drawTool.get());
|
||||
eraserTool->erasedComponents.erase(c);
|
||||
}
|
||||
});
|
||||
components->set_erase_callback([&](const CanvasComponentContainer::ObjInfoSharedPtr& c) {
|
||||
compCache.erase_component(c);
|
||||
selection.erase_component(c);
|
||||
delayedUpdateComponents.erase(c->obj);
|
||||
updateableComponents.erase(c->obj);
|
||||
};
|
||||
components.clientInsertOrderedVectorCallback = [&](const std::vector<CollabListType::ObjectInfoPtr>& comps) {
|
||||
for(auto& c : comps)
|
||||
components.clientInsertCallback(c);
|
||||
};
|
||||
components.clientServerFirstPosShiftCallback = [&](uint64_t firstShiftPos) {
|
||||
});
|
||||
components->set_post_sync_callback([&]() {
|
||||
clear_draw_cache();
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
void DrawingProgram::init_client_callbacks() {
|
||||
world.con.client_add_recv_callback(CLIENT_UPDATE_COMPONENT, [&](cereal::PortableBinaryInputArchive& message) {
|
||||
ServerClientID id;
|
||||
bool isFinal; // Not actually used, but sent for debugging purposes
|
||||
message(isFinal, id);
|
||||
auto objPtr = components.get_item_by_id(id);
|
||||
if(!objPtr)
|
||||
return;
|
||||
std::shared_ptr<DrawComponent>& comp = objPtr->obj;
|
||||
if((std::chrono::steady_clock::now() - comp->lastUpdateTime) > CLIENT_DRAWCOMP_DELAY_TIMER_DURATION) {
|
||||
delayedUpdateComponents.erase(comp);
|
||||
message(*comp);
|
||||
comp->commit_update(*this);
|
||||
}
|
||||
else {
|
||||
auto delayedUpdatePtr = DrawComponent::allocate_comp_type(comp->get_type());
|
||||
message(*delayedUpdatePtr);
|
||||
delayedUpdateComponents[comp] = delayedUpdatePtr;
|
||||
}
|
||||
});
|
||||
world.con.client_add_recv_callback(CLIENT_TRANSFORM_MANY_COMPONENTS, [&](cereal::PortableBinaryInputArchive& message) {
|
||||
std::vector<std::pair<ServerClientID, CoordSpaceHelper>> transforms;
|
||||
message(transforms);
|
||||
if(transforms.size() >= DrawingProgramCache::MINIMUM_COMPONENTS_TO_START_REBUILD) {
|
||||
std::atomic<size_t> objsActuallyTransformed = 0;
|
||||
parallel_loop_container(transforms, [&](auto& p) {
|
||||
auto& [id, coords] = p;
|
||||
auto objPtr = components.get_item_by_id(id);
|
||||
if(!objPtr || selection.is_selected(objPtr)) // Whatever transformation we're doing right now will overwrite this transformation, so we can ignore this message
|
||||
return;
|
||||
std::shared_ptr<DrawComponent>& comp = objPtr->obj;
|
||||
if(comp->coords == coords)
|
||||
return;
|
||||
comp->coords = coords;
|
||||
objsActuallyTransformed++;
|
||||
comp->commit_transform(*this, false);
|
||||
});
|
||||
if(objsActuallyTransformed != 0)
|
||||
force_rebuild_cache();
|
||||
}
|
||||
else {
|
||||
for(auto& [id, coords] : transforms) {
|
||||
auto objPtr = components.get_item_by_id(id);
|
||||
if(!objPtr || selection.is_selected(objPtr)) // Whatever transformation we're doing right now will overwrite this transformation, so we can ignore this message
|
||||
continue;
|
||||
std::shared_ptr<DrawComponent>& comp = objPtr->obj;
|
||||
if(comp->coords == coords)
|
||||
return;
|
||||
comp->coords = coords;
|
||||
comp->commit_transform(*this);
|
||||
}
|
||||
}
|
||||
});
|
||||
world.con.client_add_recv_callback(CLIENT_PLACE_SINGLE_COMPONENT, [&](cereal::PortableBinaryInputArchive& message) {
|
||||
uint64_t insertPosition;
|
||||
DrawComponentType type;
|
||||
message(insertPosition, type);
|
||||
auto newObj = DrawComponent::allocate_comp_type(type);
|
||||
message(newObj->id, newObj->coords, *newObj);
|
||||
bool didntExistPreviously = components.server_insert(insertPosition, newObj);
|
||||
if(didntExistPreviously)
|
||||
newObj->commit_update(*this);
|
||||
});
|
||||
world.con.client_add_recv_callback(CLIENT_PLACE_MANY_COMPONENTS, [&](cereal::PortableBinaryInputArchive& message) {
|
||||
std::vector<CollabListType::ObjectInfoPtr> sortedPlacedComponents;
|
||||
|
||||
SendOrderedComponentVectorOp a{&sortedPlacedComponents};
|
||||
message(a);
|
||||
|
||||
std::vector<bool> didntExistPreviously = components.server_insert_ordered_vector(sortedPlacedComponents);
|
||||
bool isSingleThread = sortedPlacedComponents.size() < DrawingProgramCache::MINIMUM_COMPONENTS_TO_START_REBUILD;
|
||||
parallel_loop_container(sortedPlacedComponents, [&](const auto& c){
|
||||
c->obj->commit_update(*this, isSingleThread);
|
||||
}, isSingleThread);
|
||||
if(!isSingleThread)
|
||||
force_rebuild_cache();
|
||||
});
|
||||
world.con.client_add_recv_callback(CLIENT_ERASE_SINGLE_COMPONENT, [&](cereal::PortableBinaryInputArchive& message) {
|
||||
ServerClientID idToErase;
|
||||
message(idToErase);
|
||||
components.server_erase(idToErase);
|
||||
});
|
||||
world.con.client_add_recv_callback(CLIENT_ERASE_MANY_COMPONENTS, [&](cereal::PortableBinaryInputArchive& message) {
|
||||
std::unordered_set<ServerClientID> idsToErase;
|
||||
message(idsToErase);
|
||||
components.server_erase_set(idsToErase);
|
||||
});
|
||||
}
|
||||
|
||||
void DrawingProgram::scale_up(const WorldScalar& scaleUpAmount) {
|
||||
for(auto& c : components.client_list())
|
||||
c->obj->scale_up(scaleUpAmount);
|
||||
selection.deselect_all();
|
||||
force_rebuild_cache();
|
||||
switch_to_tool(drawTool->get_type() == DrawingProgramToolType::GRIDMODIFY ? DrawingProgramToolType::EDIT : drawTool->get_type(), true);
|
||||
//for(auto& c : components->get_data())
|
||||
// c->obj->scale_up(scaleUpAmount);
|
||||
//selection.deselect_all();
|
||||
//force_rebuild_cache();
|
||||
//switch_to_tool(drawTool->get_type() == DrawingProgramToolType::GRIDMODIFY ? DrawingProgramToolType::EDIT : drawTool->get_type(), true);
|
||||
}
|
||||
|
||||
void DrawingProgram::check_delayed_update_timers() {
|
||||
std::erase_if(delayedUpdateComponents, [&](auto& p) {
|
||||
auto& [comp, delayedUpdatePtr] = p;
|
||||
return comp->check_timers(*this, delayedUpdatePtr);
|
||||
});
|
||||
}
|
||||
|
||||
void DrawingProgram::check_updateable_components() {
|
||||
for(auto& comp : updateableComponents)
|
||||
comp->update(*this);
|
||||
}
|
||||
|
||||
void DrawingProgram::parallel_loop_all_components(std::function<void(const std::shared_ptr<CollabList<std::shared_ptr<DrawComponent>, ServerClientID>::ObjectInfo>&)> func) {
|
||||
parallel_loop_container(components.client_list(), func);
|
||||
void DrawingProgram::parallel_loop_all_components(std::function<void(const CanvasComponentContainer::ObjInfoSharedPtr&)> func) {
|
||||
parallel_loop_container(components->get_data(), func);
|
||||
}
|
||||
|
||||
std::unordered_set<ServerClientID> DrawingProgram::get_used_resources() const {
|
||||
std::unordered_set<ServerClientID> toRet;
|
||||
for(auto& c : components.client_list())
|
||||
c->obj->get_used_resources(toRet);
|
||||
//for(auto& c : components->get_data())
|
||||
// c->obj->get_used_resources(toRet);
|
||||
return toRet;
|
||||
}
|
||||
|
||||
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();
|
||||
|
|
@ -365,13 +277,6 @@ void DrawingProgram::update() {
|
|||
controls.previousMouseWorldPos = controls.currentMouseWorldPos;
|
||||
controls.currentMouseWorldPos = world.get_mouse_world_pos();
|
||||
|
||||
if(addFileInNextFrame) {
|
||||
add_file_to_canvas_by_path_execute(addFileInfo.first, addFileInfo.second);
|
||||
addFileInNextFrame = false;
|
||||
}
|
||||
|
||||
drag_drop_update();
|
||||
|
||||
if(world.main.input.key(InputManager::KEY_DRAW_TOOL_BRUSH).pressed)
|
||||
switch_to_tool(DrawingProgramToolType::BRUSH);
|
||||
else if(world.main.input.key(InputManager::KEY_DRAW_TOOL_ERASER).pressed)
|
||||
|
|
@ -406,18 +311,14 @@ void DrawingProgram::update() {
|
|||
if(controls.leftClickReleased)
|
||||
controls.leftClickReleased = false;
|
||||
|
||||
check_delayed_update_timers();
|
||||
check_updateable_components();
|
||||
update_downloading_dropped_files();
|
||||
|
||||
if(drawTool->get_type() == DrawingProgramToolType::ERASER) {
|
||||
EraserTool* eraserTool = static_cast<EraserTool*>(drawTool.get());
|
||||
compCache.test_rebuild_dont_include_set_dont_include_nodes(components.client_list(), eraserTool->erasedComponents, eraserTool->erasedBVHNodes);
|
||||
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.client_list(), selection.get_selected_set());
|
||||
compCache.test_rebuild_dont_include_set(components->get_data(), selection.get_selected_set());
|
||||
else
|
||||
compCache.test_rebuild(components.client_list());
|
||||
compCache.test_rebuild(components->get_data());
|
||||
}
|
||||
|
||||
bool DrawingProgram::is_actual_selection_tool(DrawingProgramToolType typeToCheck) {
|
||||
|
|
@ -463,287 +364,28 @@ SkPaint DrawingProgram::select_tool_line_paint() {
|
|||
void DrawingProgram::force_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.client_list(), eraserTool->erasedComponents, eraserTool->erasedBVHNodes, true);
|
||||
compCache.test_rebuild_dont_include_set_dont_include_nodes(components->get_data(), eraserTool->erasedComponents, eraserTool->erasedBVHNodes, true);
|
||||
}
|
||||
else if(is_selection_allowing_tool(drawTool->get_type()))
|
||||
compCache.test_rebuild_dont_include_set(components.client_list(), selection.get_selected_set(), true);
|
||||
compCache.test_rebuild_dont_include_set(components->get_data(), selection.get_selected_set(), true);
|
||||
else
|
||||
compCache.test_rebuild(components.client_list(), true);
|
||||
compCache.test_rebuild(components->get_data(), true);
|
||||
}
|
||||
|
||||
void DrawingProgram::drag_drop_update() {
|
||||
if(controls.cursorHoveringOverCanvas) {
|
||||
for(auto& droppedItem : world.main.input.droppedItems) {
|
||||
if(droppedItem.dataPath.has_value() && std::filesystem::is_regular_file(droppedItem.dataPath.value())) {
|
||||
#ifdef __EMSCRIPTEN_
|
||||
add_file_to_canvas_by_path(droppedItem.dataPath.value(), world.main.input.mouse.pos, true);
|
||||
#else
|
||||
add_file_to_canvas_by_path(droppedItem.dataPath.value(), droppedItem.pos, true);
|
||||
#endif
|
||||
}
|
||||
else if(droppedItem.dataText.has_value() && is_valid_http_url(droppedItem.dataText.value())) {
|
||||
auto img(std::make_shared<DrawImage>());
|
||||
img->coords = world.drawData.cam.c;
|
||||
Vector2f imDim = Vector2f{100.0f, 100.0f};
|
||||
img->d.p1 = droppedItem.pos - imDim;
|
||||
img->d.p2 = droppedItem.pos + imDim;
|
||||
img->d.imageID = {0, 0};
|
||||
img->commit_update(*this);
|
||||
uint64_t placement = components.client_list().size();
|
||||
auto objAdd = components.client_insert(placement, img);
|
||||
img->client_send_place(*this);
|
||||
add_undo_place_component(objAdd);
|
||||
droppedDownloadingFiles.emplace_back(objAdd, world.main.window.size.cast<float>(), FileDownloader::download_data_from_url(droppedItem.dataText.value()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawingProgram::update_downloading_dropped_files() {
|
||||
std::erase_if(droppedDownloadingFiles, [&](auto& downFile) {
|
||||
switch(downFile.downData->status) {
|
||||
case FileDownloader::DownloadData::Status::SUCCESS: {
|
||||
std::shared_ptr<DrawImage> img = std::static_pointer_cast<DrawImage>(downFile.comp->obj);
|
||||
Vector2f dropPos = (img->d.p1 + img->d.p2) * 0.5f;
|
||||
|
||||
ResourceData newResource;
|
||||
newResource.data = std::make_shared<std::string>(downFile.downData->str);
|
||||
newResource.name = downFile.downData->fileName;
|
||||
ServerClientID imageID = world.rMan.add_resource(newResource);
|
||||
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");
|
||||
client_erase_set({downFile.comp});
|
||||
}
|
||||
else {
|
||||
Vector2f imTrueDim = display->get_dimensions();
|
||||
|
||||
float imWidth = imTrueDim.x() / (imTrueDim.x() + imTrueDim.y());
|
||||
float imHeight = imTrueDim.y() / (imTrueDim.x() + imTrueDim.y());
|
||||
Vector2f imDim = Vector2f{downFile.windowSizeWhenDropped.x() * imWidth, downFile.windowSizeWhenDropped.x() * imHeight} * display->get_dimension_scale();
|
||||
img->d.p1 = dropPos - imDim;
|
||||
img->d.p2 = dropPos + imDim;
|
||||
img->d.imageID = imageID;
|
||||
img->commit_update(*this);
|
||||
img->client_send_update(*this, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case FileDownloader::DownloadData::Status::FAILURE:
|
||||
Logger::get().log("WORLDFATAL", "Failed to download data from URL");
|
||||
client_erase_set({downFile.comp});
|
||||
return true;
|
||||
case FileDownloader::DownloadData::Status::IN_PROGRESS:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
void DrawingProgram::add_file_to_canvas_by_path(const std::filesystem::path& filePath, Vector2f dropPos, bool addInSameThread) {
|
||||
if(addInSameThread)
|
||||
add_file_to_canvas_by_path_execute(filePath, dropPos);
|
||||
else if(!addFileInNextFrame) {
|
||||
addFileInfo = {filePath, dropPos};
|
||||
addFileInNextFrame = true;
|
||||
}
|
||||
}
|
||||
|
||||
void DrawingProgram::add_file_to_canvas_by_path_execute(const std::filesystem::path& filePath, Vector2f dropPos) {
|
||||
ServerClientID imageID = world.rMan.add_resource_file(filePath);
|
||||
if(imageID != ServerClientID{0, 0}) {
|
||||
ResourceDisplay* display = world.rMan.get_display_data(imageID);
|
||||
Vector2f imTrueDim = display->get_dimensions();
|
||||
auto img(std::make_shared<DrawImage>());
|
||||
img->coords = world.drawData.cam.c;
|
||||
float imWidth = imTrueDim.x() / (imTrueDim.x() + imTrueDim.y());
|
||||
float imHeight = imTrueDim.y() / (imTrueDim.x() + imTrueDim.y());
|
||||
Vector2f imDim = Vector2f{world.main.window.size.x() * imWidth, world.main.window.size.x() * imHeight} * display->get_dimension_scale();
|
||||
img->d.p1 = dropPos - imDim;
|
||||
img->d.p2 = dropPos + imDim;
|
||||
img->d.imageID = imageID;
|
||||
img->commit_update(*this);
|
||||
uint64_t placement = components.client_list().size();
|
||||
auto objAdd = components.client_insert(placement, img);
|
||||
img->client_send_place(*this);
|
||||
add_undo_place_component(objAdd);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawingProgram::add_file_to_canvas_by_data(const std::string& fileName, std::string_view fileBuffer, Vector2f dropPos) {
|
||||
ResourceData newResource;
|
||||
newResource.data = std::make_shared<std::string>(fileBuffer);
|
||||
newResource.name = fileName;
|
||||
ServerClientID imageID = world.rMan.add_resource(newResource);
|
||||
|
||||
ResourceDisplay* display = world.rMan.get_display_data(imageID);
|
||||
Vector2f imTrueDim = display->get_dimensions();
|
||||
auto img(std::make_shared<DrawImage>());
|
||||
img->coords = world.drawData.cam.c;
|
||||
float imWidth = imTrueDim.x() / (imTrueDim.x() + imTrueDim.y());
|
||||
float imHeight = imTrueDim.y() / (imTrueDim.x() + imTrueDim.y());
|
||||
Vector2f imDim = Vector2f{world.main.window.size.x() * imWidth, world.main.window.size.x() * imHeight} * display->get_dimension_scale();
|
||||
img->d.p1 = dropPos - imDim;
|
||||
img->d.p2 = dropPos + imDim;
|
||||
img->d.imageID = imageID;
|
||||
img->commit_update(*this);
|
||||
uint64_t placement = components.client_list().size();
|
||||
auto objAdd = components.client_insert(placement, img);
|
||||
img->client_send_place(*this);
|
||||
add_undo_place_component(objAdd);
|
||||
}
|
||||
|
||||
void DrawingProgram::initialize_draw_data(cereal::PortableBinaryInputArchive& a) {
|
||||
uint64_t compCount;
|
||||
a(compCount);
|
||||
for(uint64_t i = 0; i < compCount; i++) {
|
||||
DrawComponentType t;
|
||||
a(t);
|
||||
auto newComp = DrawComponent::allocate_comp_type(t);
|
||||
a(newComp->id, newComp->coords, *newComp);
|
||||
components.init_emplace_back(newComp);
|
||||
}
|
||||
parallel_loop_all_components([&](const auto& c){
|
||||
c->obj->commit_update(*this, false);
|
||||
});
|
||||
compCache.test_rebuild(components.client_list(), true);
|
||||
}
|
||||
|
||||
void DrawingProgram::load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) {
|
||||
uint64_t compCount;
|
||||
a(compCount);
|
||||
for(uint64_t i = 0; i < compCount; i++) {
|
||||
DrawComponentType t;
|
||||
a(t);
|
||||
auto newComp = DrawComponent::allocate_comp_type(t);
|
||||
a(newComp->id, newComp->coords);
|
||||
newComp->load_file(a, version);
|
||||
components.init_emplace_back(newComp);
|
||||
}
|
||||
parallel_loop_all_components([&](const auto& c){
|
||||
c->obj->commit_update(*this, false);
|
||||
});
|
||||
compCache.test_rebuild(components.client_list(), true);
|
||||
}
|
||||
|
||||
void DrawingProgram::client_erase_set(std::unordered_set<CollabListType::ObjectInfoPtr> erasedComponents) {
|
||||
std::unordered_set<ServerClientID> idsToErase;
|
||||
for(auto& c : erasedComponents)
|
||||
idsToErase.emplace(c->obj->id);
|
||||
DrawComponent::client_send_erase_set(*this, idsToErase);
|
||||
components.client_erase_set(erasedComponents);
|
||||
add_undo_erase_components(erasedComponents);
|
||||
}
|
||||
|
||||
void DrawingProgram::add_undo_place_component(const CollabListType::ObjectInfoPtr& objToUndo) {
|
||||
world.undo.push(UndoManager::UndoRedoPair{
|
||||
[&, objToUndo]() {
|
||||
objToUndo->obj->client_send_erase(*this);
|
||||
components.client_erase(objToUndo);
|
||||
return true;
|
||||
},
|
||||
[&, objToUndo]() {
|
||||
if(objToUndo->obj->collabListInfo.lock())
|
||||
return false;
|
||||
components.client_insert(objToUndo, false);
|
||||
objToUndo->obj->client_send_place(*this);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void DrawingProgram::invalidate_cache_at_component(const CollabListType::ObjectInfoPtr& objToCheck) {
|
||||
void DrawingProgram::invalidate_cache_at_component(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck) {
|
||||
if(selection.is_selected(objToCheck))
|
||||
selection.invalidate_cache_at_optional_aabb_before_pos(objToCheck->obj->worldAABB, objToCheck->pos);
|
||||
selection.invalidate_cache_at_aabb_before_pos(objToCheck->obj->get_world_bounds(), objToCheck->pos);
|
||||
else
|
||||
compCache.invalidate_cache_at_optional_aabb_before_pos(objToCheck->obj->worldAABB, objToCheck->pos);
|
||||
compCache.invalidate_cache_at_aabb_before_pos(objToCheck->obj->get_world_bounds(), objToCheck->pos);
|
||||
}
|
||||
|
||||
void DrawingProgram::preupdate_component(const CollabListType::ObjectInfoPtr& objToCheck) {
|
||||
void DrawingProgram::preupdate_component(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck) {
|
||||
if(selection.is_selected(objToCheck))
|
||||
selection.preupdate_component(objToCheck);
|
||||
else
|
||||
compCache.preupdate_component(objToCheck);
|
||||
}
|
||||
|
||||
void DrawingProgram::add_undo_place_components(const std::unordered_set<CollabListType::ObjectInfoPtr>& objSetToUndo) {
|
||||
world.undo.push(UndoManager::UndoRedoPair{
|
||||
[&, objSetToUndo = objSetToUndo]() {
|
||||
for(auto& comp : objSetToUndo)
|
||||
if(!comp->obj->collabListInfo.lock())
|
||||
return false;
|
||||
|
||||
std::unordered_set<ServerClientID> idsToErase;
|
||||
for(auto& c : objSetToUndo)
|
||||
idsToErase.emplace(c->obj->id);
|
||||
|
||||
DrawComponent::client_send_erase_set(*this, idsToErase);
|
||||
components.client_erase_set(objSetToUndo);
|
||||
|
||||
return true;
|
||||
},
|
||||
[&, objSetToUndo = objSetToUndo]() {
|
||||
std::vector<CollabListType::ObjectInfoPtr> sortedObjects(objSetToUndo.begin(), objSetToUndo.end());
|
||||
std::sort(sortedObjects.begin(), sortedObjects.end(), [](auto& a, auto& b) {
|
||||
return a->pos < b->pos;
|
||||
});
|
||||
|
||||
for(auto& comp : sortedObjects)
|
||||
if(comp->obj->collabListInfo.lock())
|
||||
return false;
|
||||
|
||||
components.client_insert_ordered_vector(sortedObjects, false);
|
||||
DrawComponent::client_send_place_many(*this, sortedObjects);
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void DrawingProgram::add_undo_erase_components(const std::unordered_set<CollabListType::ObjectInfoPtr>& objSetToUndo) {
|
||||
world.undo.push(UndoManager::UndoRedoPair{
|
||||
[&, objSetToUndo = objSetToUndo]() {
|
||||
std::vector<CollabListType::ObjectInfoPtr> sortedObjects(objSetToUndo.begin(), objSetToUndo.end());
|
||||
std::sort(sortedObjects.begin(), sortedObjects.end(), [](auto& a, auto& b) {
|
||||
return a->pos < b->pos;
|
||||
});
|
||||
|
||||
for(auto& comp : sortedObjects)
|
||||
if(comp->obj->collabListInfo.lock())
|
||||
return false;
|
||||
|
||||
components.client_insert_ordered_vector(sortedObjects, false);
|
||||
DrawComponent::client_send_place_many(*this, sortedObjects);
|
||||
|
||||
return true;
|
||||
},
|
||||
[&, objSetToUndo = objSetToUndo]() {
|
||||
for(auto& comp : objSetToUndo)
|
||||
if(!comp->obj->collabListInfo.lock())
|
||||
return false;
|
||||
|
||||
std::unordered_set<ServerClientID> idsToErase;
|
||||
for(auto& c : objSetToUndo)
|
||||
idsToErase.emplace(c->obj->id);
|
||||
|
||||
DrawComponent::client_send_erase_set(*this, idsToErase);
|
||||
components.client_erase_set(objSetToUndo);
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ClientPortionID DrawingProgram::get_max_id(ServerPortionID serverID) {
|
||||
ClientPortionID maxClientID = 0;
|
||||
auto& cList = components.client_list();
|
||||
for(auto& comp : cList) {
|
||||
if(comp->obj->id.first == serverID)
|
||||
maxClientID = std::max(maxClientID, comp->obj->id.second);
|
||||
}
|
||||
return maxClientID;
|
||||
}
|
||||
|
||||
float DrawingProgram::drag_point_radius() {
|
||||
return 8.0f * world.main.toolbar.final_gui_scale();
|
||||
}
|
||||
|
|
@ -758,24 +400,36 @@ void DrawingProgram::draw_drag_circle(SkCanvas* canvas, const Vector2f& sPos, co
|
|||
canvas->drawCircle(sPos.x(), sPos.y(), constantRadius, paintOutline);
|
||||
}
|
||||
|
||||
void DrawingProgram::load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) {
|
||||
//uint64_t compCount;
|
||||
//a(compCount);
|
||||
//for(uint64_t i = 0; i < compCount; i++) {
|
||||
// DrawComponentType t;
|
||||
// a(t);
|
||||
// auto newComp = DrawComponent::allocate_comp_type(t);
|
||||
// a(newComp->id, newComp->coords);
|
||||
// newComp->load_file(a, version);
|
||||
// components.init_emplace_back(newComp);
|
||||
//}
|
||||
//parallel_loop_all_components([&](const auto& c){
|
||||
// c->obj->commit_update(*this, false);
|
||||
//});
|
||||
//compCache.test_rebuild(components.client_list(), true);
|
||||
}
|
||||
|
||||
void DrawingProgram::save_file(cereal::PortableBinaryOutputArchive& a) const {
|
||||
auto& cList = components.client_list();
|
||||
a((uint64_t)cList.size());
|
||||
for(size_t i = 0; i < cList.size(); i++) {
|
||||
a(cList[i]->obj->get_type(), cList[i]->obj->id, cList[i]->obj->coords);
|
||||
cList[i]->obj->save_file(a);
|
||||
}
|
||||
//auto& cList = components->get_data();
|
||||
//a((uint64_t)cList.size());
|
||||
//for(size_t i = 0; i < cList.size(); i++) {
|
||||
// a(cList[i]->obj->get_type(), cList[i]->obj->id, cList[i]->obj->coords);
|
||||
// cList[i]->obj->save_file(a);
|
||||
//}
|
||||
}
|
||||
|
||||
void DrawingProgram::draw(SkCanvas* canvas, const DrawData& drawData) {
|
||||
if(drawData.dontUseDrawProgCache) {
|
||||
parallel_loop_all_components([&](auto& c) {
|
||||
c->obj->calculate_draw_transform(drawData);
|
||||
});
|
||||
for(auto& c : components.client_list()) {
|
||||
for(auto& c : components->get_data())
|
||||
c->obj->draw(canvas, drawData);
|
||||
c->obj->drawSetupData.shouldDraw = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
canvas->saveLayer(nullptr, nullptr);
|
||||
|
|
@ -786,22 +440,6 @@ void DrawingProgram::draw(SkCanvas* canvas, const DrawData& drawData) {
|
|||
selection.draw_components(canvas, drawData);
|
||||
canvas->restore();
|
||||
canvas->restore();
|
||||
|
||||
if(!drawData.main->takingScreenshot) {
|
||||
for(auto& droppedDownFile : droppedDownloadingFiles) {
|
||||
if(droppedDownFile.comp->obj->collabListInfo.lock())
|
||||
std::static_pointer_cast<DrawImage>(droppedDownFile.comp->obj)->draw_download_progress_bar(canvas, drawData, droppedDownFile.downData->progress);
|
||||
}
|
||||
|
||||
for(auto& c : updateableComponents) {
|
||||
if(c->get_type() == DRAWCOMPONENT_IMAGE) {
|
||||
auto img = std::static_pointer_cast<DrawImage>(c);
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,19 +11,20 @@
|
|||
#include <include/core/SkVertices.h>
|
||||
#include <Helpers/SCollision.hpp>
|
||||
#include <Helpers/Hashes.hpp>
|
||||
#include <list>
|
||||
#include <Helpers/Random.hpp>
|
||||
#include "DrawingProgramSelection.hpp"
|
||||
#include "../CollabList.hpp"
|
||||
#include "DrawingProgramToolBase.hpp"
|
||||
#include <Helpers/FileDownloader.hpp>
|
||||
#include <Helpers/NetworkingObjects/NetObjOrderedList.hpp>
|
||||
|
||||
class World;
|
||||
|
||||
class DrawingProgram {
|
||||
public:
|
||||
DrawingProgram(World& initWorld);
|
||||
void init();
|
||||
void init_client_callbacks();
|
||||
void set_component_list_callbacks();
|
||||
void toolbar_gui();
|
||||
void tool_options_gui();
|
||||
bool right_click_popup_gui(Vector2f popupPos);
|
||||
|
|
@ -31,39 +32,26 @@ class DrawingProgram {
|
|||
void scale_up(const WorldScalar& scaleUpAmount);
|
||||
void draw(SkCanvas* canvas, const DrawData& drawData);
|
||||
|
||||
void initialize_draw_data(cereal::PortableBinaryInputArchive& a);
|
||||
void load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version);
|
||||
|
||||
std::unordered_set<ServerClientID> get_used_resources() const;
|
||||
ClientPortionID get_max_id(ServerPortionID serverID);
|
||||
void load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version);
|
||||
void save_file(cereal::PortableBinaryOutputArchive& a) const;
|
||||
World& world;
|
||||
|
||||
bool prevent_undo_or_redo();
|
||||
|
||||
void add_file_to_canvas_by_path(const std::filesystem::path& filePath, Vector2f dropPos, bool addInSameThread);
|
||||
void add_file_to_canvas_by_data(const std::string& fileName, std::string_view fileBuffer, Vector2f dropPos);
|
||||
|
||||
void parallel_loop_all_components(std::function<void(const std::shared_ptr<CollabList<std::shared_ptr<DrawComponent>, ServerClientID>::ObjectInfo>&)> func);
|
||||
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 CollabListType::ObjectInfoPtr& objToCheck);
|
||||
void preupdate_component(const CollabListType::ObjectInfoPtr& objToCheck);
|
||||
void invalidate_cache_at_component(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck);
|
||||
void preupdate_component(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck);
|
||||
|
||||
DrawingProgramCache compCache;
|
||||
|
||||
CollabListType components;
|
||||
|
||||
std::unordered_set<std::shared_ptr<DrawComponent>> updateableComponents;
|
||||
std::unordered_map<std::shared_ptr<DrawComponent>, std::shared_ptr<DrawComponent>> delayedUpdateComponents;
|
||||
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:
|
||||
|
|
@ -71,31 +59,17 @@ class DrawingProgram {
|
|||
std::optional<std::chrono::steady_clock::time_point> unorderedObjectsExistTimePoint;
|
||||
|
||||
bool selection_action_menu(Vector2f popupPos);
|
||||
|
||||
void client_erase_set(std::unordered_set<CollabListType::ObjectInfoPtr> erasedComponents); // The set might be modified while this is being called, so dont pass by reference
|
||||
|
||||
void check_delayed_update_timers();
|
||||
void check_updateable_components();
|
||||
|
||||
void force_rebuild_cache();
|
||||
|
||||
bool addToCompCacheOnInsert = true;
|
||||
|
||||
std::atomic<bool> addFileInNextFrame = false;
|
||||
std::pair<std::filesystem::path, Vector2f> addFileInfo;
|
||||
|
||||
void add_file_to_canvas_by_path_execute(const std::filesystem::path& filePath, Vector2f dropPos);
|
||||
|
||||
void drag_drop_update();
|
||||
|
||||
float drag_point_radius();
|
||||
void draw_drag_circle(SkCanvas* canvas, const Vector2f& pos, const SkColor4f& c, const DrawData& drawData, float radiusMultiplier = 1.0f);
|
||||
|
||||
void add_undo_place_component(const CollabListType::ObjectInfoPtr& objToUndo);
|
||||
void add_undo_place_components(const std::unordered_set<CollabListType::ObjectInfoPtr>& objSetToUndo);
|
||||
void add_undo_erase_components(const std::unordered_set<CollabListType::ObjectInfoPtr>& objSetToUndo);
|
||||
|
||||
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;
|
||||
|
||||
|
|
@ -105,9 +79,6 @@ class DrawingProgram {
|
|||
bool temporaryPan = false;
|
||||
DrawingProgramToolType toolTypeAfterTempPan;
|
||||
|
||||
bool is_actual_selection_tool(DrawingProgramToolType typeToCheck);
|
||||
bool is_selection_allowing_tool(DrawingProgramToolType typeToCheck);
|
||||
|
||||
struct GlobalControls {
|
||||
float relativeWidth = 15.0f;
|
||||
|
||||
|
|
@ -130,15 +101,6 @@ class DrawingProgram {
|
|||
|
||||
uint32_t nextID = 0;
|
||||
|
||||
struct DroppedDownloadingFile {
|
||||
CollabListType::ObjectInfoPtr comp;
|
||||
Vector2f windowSizeWhenDropped;
|
||||
std::shared_ptr<FileDownloader::DownloadData> downData;
|
||||
};
|
||||
std::vector<DroppedDownloadingFile> droppedDownloadingFiles;
|
||||
|
||||
void update_downloading_dropped_files();
|
||||
|
||||
friend class EyeDropperTool;
|
||||
friend class RectDrawTool;
|
||||
friend class EllipseDrawTool;
|
||||
|
|
|
|||
|
|
@ -23,18 +23,10 @@ DrawingProgramCache::DrawingProgramCache(DrawingProgram& initDrawP):
|
|||
lastBvhBuildTime = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
void DrawingProgramCache::build(std::vector<CollabListType::ObjectInfoPtr> components) {
|
||||
void DrawingProgramCache::build(std::vector<CanvasComponentContainer::ObjInfoSharedPtr> components) {
|
||||
bvhRoot = std::make_shared<DrawingProgramCacheBVHNode>();
|
||||
unsortedComponents.clear();
|
||||
clear_own_cached_surfaces();
|
||||
std::erase_if(components, [&](auto& c) {
|
||||
if(!c->obj->worldAABB) {
|
||||
unsortedComponents.emplace_back(c);
|
||||
c->obj->parentBvhNode.reset();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
build_bvh_node(bvhRoot, components);
|
||||
lastBvhBuildTime = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
|
@ -59,17 +51,17 @@ bool DrawingProgramCache::check_if_rebuild_should_occur() {
|
|||
return (unsortedComponents.size() >= MINIMUM_COMPONENTS_TO_START_REBUILD);
|
||||
}
|
||||
|
||||
void DrawingProgramCache::test_rebuild(const std::vector<CollabListType::ObjectInfoPtr>& comps, bool force) {
|
||||
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<CollabListType::ObjectInfoPtr>& comps, const std::unordered_set<CollabListType::ObjectInfoPtr>& objsToNotInclude, bool force) {
|
||||
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<CollabListType::ObjectInfoPtr>& comps, const std::unordered_set<CollabListType::ObjectInfoPtr>& objsToNotInclude, const std::unordered_set<std::shared_ptr<DrawingProgramCacheBVHNode>>& nodesToNotInclude, bool force) {
|
||||
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);
|
||||
|
|
@ -77,30 +69,30 @@ void DrawingProgramCache::test_rebuild_dont_include_set_dont_include_nodes(const
|
|||
}
|
||||
}
|
||||
|
||||
void DrawingProgramCache::move_components_from_bvh_nodes_to_set(std::unordered_set<CollabListType::ObjectInfoPtr>& s, const std::unordered_set<std::shared_ptr<DrawingProgramCacheBVHNode>>& bvhNodes) {
|
||||
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<CollabListType::ObjectInfoPtr>& s, const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode) {
|
||||
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<CollabListType::ObjectInfoPtr> componentsToBuild, const std::unordered_set<CollabListType::ObjectInfoPtr>& objsToNotInclude) {
|
||||
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<CollabListType::ObjectInfoPtr>& componentsToBuild) {
|
||||
void DrawingProgramCache::force_rebuild(const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& componentsToBuild) {
|
||||
build(componentsToBuild);
|
||||
}
|
||||
|
||||
const std::vector<CollabListType::ObjectInfoPtr>& DrawingProgramCache::get_unsorted_component_list() const {
|
||||
const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& DrawingProgramCache::get_unsorted_component_list() const {
|
||||
return unsortedComponents;
|
||||
}
|
||||
|
||||
|
|
@ -108,12 +100,12 @@ const std::chrono::steady_clock::time_point& DrawingProgramCache::get_last_bvh_b
|
|||
return lastBvhBuildTime;
|
||||
}
|
||||
|
||||
void DrawingProgramCache::add_component(const CollabListType::ObjectInfoPtr& c) {
|
||||
void DrawingProgramCache::add_component(const CanvasComponentContainer::ObjInfoSharedPtr& c) {
|
||||
unsortedComponents.emplace_back(c);
|
||||
invalidate_cache_at_optional_aabb_before_pos(c->obj->worldAABB, c->pos);
|
||||
invalidate_cache_at_aabb_before_pos(c->obj->get_world_bounds(), c->pos);
|
||||
}
|
||||
|
||||
void DrawingProgramCache::erase_component(const CollabListType::ObjectInfoPtr& c) {
|
||||
void DrawingProgramCache::erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& c) {
|
||||
auto parentBvhNode = c->obj->parentBvhNode.lock();
|
||||
if(parentBvhNode) {
|
||||
std::erase(parentBvhNode->components, c);
|
||||
|
|
@ -121,33 +113,16 @@ void DrawingProgramCache::erase_component(const CollabListType::ObjectInfoPtr& c
|
|||
}
|
||||
else
|
||||
std::erase(unsortedComponents, c);
|
||||
invalidate_cache_at_optional_aabb_before_pos(c->obj->worldAABB, c->pos);
|
||||
invalidate_cache_at_aabb_before_pos(c->obj->get_world_bounds(), c->pos);
|
||||
}
|
||||
|
||||
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()};
|
||||
auto it = std::find_if(bvhNode->components.begin(), bvhNode->components.end(), [&](const auto& comp) {
|
||||
return comp->obj.get() == c;
|
||||
});
|
||||
if(it != bvhNode->components.end())
|
||||
return {bvhNode, it};
|
||||
for(auto& p : bvhNode->children) {
|
||||
auto toRet = get_bvh_node_fully_containing_recursive(p, c);
|
||||
if(toRet.first)
|
||||
return toRet;
|
||||
}
|
||||
return {nullptr, bvhNode->components.end()};
|
||||
}
|
||||
|
||||
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) {
|
||||
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<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<CanvasComponentContainer::ObjInfoSharedPtr>& components)> f) {
|
||||
if(bvhNode && SCollision::collide(aabb, bvhNode->bounds)) {
|
||||
if(f(bvhNode, bvhNode->components))
|
||||
return true;
|
||||
|
|
@ -158,22 +133,22 @@ bool DrawingProgramCache::traverse_bvh_erase_function_recursive(const std::share
|
|||
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<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<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<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<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);
|
||||
}
|
||||
}
|
||||
|
||||
CollabListType::ObjectInfoPtr DrawingProgramCache::get_front_object_colliding_with(const SCollision::ColliderCollection<float>& cC) {
|
||||
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);
|
||||
CollabListType::ObjectInfoPtr p;
|
||||
traverse_bvh_run_function(cCWorld.bounds, [&](const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const std::vector<CollabListType::ObjectInfoPtr>& components) {
|
||||
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;
|
||||
|
|
@ -183,23 +158,23 @@ CollabListType::ObjectInfoPtr DrawingProgramCache::get_front_object_colliding_wi
|
|||
return p;
|
||||
}
|
||||
|
||||
void DrawingProgramCache::preupdate_component(const CollabListType::ObjectInfoPtr& c) {
|
||||
void DrawingProgramCache::preupdate_component(const CanvasComponentContainer::ObjInfoSharedPtr& c) {
|
||||
auto parentBvhNode = c->obj->parentBvhNode.lock();
|
||||
if(parentBvhNode) {
|
||||
unsortedComponents.emplace_back(c);
|
||||
std::erase(parentBvhNode->components, c);
|
||||
c->obj->parentBvhNode.reset();
|
||||
}
|
||||
invalidate_cache_at_optional_aabb_before_pos(c->obj->worldAABB, c->pos);
|
||||
invalidate_cache_at_aabb_before_pos(c->obj->get_world_bounds(), c->pos);
|
||||
}
|
||||
|
||||
void DrawingProgramCache::build_bvh_node(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const std::vector<CollabListType::ObjectInfoPtr>& components) {
|
||||
void DrawingProgramCache::build_bvh_node(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components) {
|
||||
if(components.empty())
|
||||
return;
|
||||
|
||||
bvhNode->bounds = components.front()->obj->worldAABB.value();
|
||||
bvhNode->bounds = components.front()->obj->get_world_bounds();
|
||||
for(auto& c : components)
|
||||
bvhNode->bounds.include_aabb_in_bounds(c->obj->worldAABB.value());
|
||||
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;
|
||||
|
|
@ -215,10 +190,10 @@ void DrawingProgramCache::build_bvh_node(const std::shared_ptr<DrawingProgramCac
|
|||
|
||||
WorldVec boundsCenter = bvhNode->bounds.center();
|
||||
|
||||
std::array<std::vector<CollabListType::ObjectInfoPtr>, 4> parts;
|
||||
std::array<std::vector<CanvasComponentContainer::ObjInfoSharedPtr>, 4> parts;
|
||||
|
||||
for(auto& c : components) {
|
||||
const auto& cAABB = c->obj->worldAABB.value();
|
||||
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())
|
||||
|
|
@ -271,7 +246,7 @@ 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(uint64_t placementToInvalidateAt) {
|
||||
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)
|
||||
|
|
@ -286,14 +261,7 @@ void DrawingProgramCache::invalidate_cache_before_pos(uint64_t placementToInvali
|
|||
});
|
||||
}
|
||||
|
||||
void DrawingProgramCache::invalidate_cache_at_optional_aabb_before_pos(const std::optional<SCollision::AABB<WorldScalar>>& aabb, uint64_t placementToInvalidateAt) {
|
||||
if(aabb)
|
||||
invalidate_cache_at_aabb_before_pos(aabb.value(), placementToInvalidateAt);
|
||||
else
|
||||
invalidate_cache_before_pos(placementToInvalidateAt);
|
||||
}
|
||||
|
||||
void DrawingProgramCache::invalidate_cache_at_aabb_before_pos(const SCollision::AABB<WorldScalar>& aabb, uint64_t placementToInvalidateAt) {
|
||||
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();
|
||||
|
|
@ -312,7 +280,7 @@ void DrawingProgramCache::invalidate_cache_at_aabb_before_pos(const SCollision::
|
|||
|
||||
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) {
|
||||
traverse_bvh_run_function(drawData.cam.viewingAreaGenerousCollider, [&](std::shared_ptr<DrawingProgramCacheBVHNode> node, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& comps) {
|
||||
if(node && node->coords.inverseScale <= drawData.cam.c.inverseScale) {
|
||||
if(!node->drawCache || node->drawCache.value().isInvalid)
|
||||
nodeFlatList.emplace_front(node);
|
||||
|
|
@ -415,16 +383,16 @@ void DrawingProgramCache::refresh_draw_cache(const std::shared_ptr<DrawingProgra
|
|||
}
|
||||
|
||||
void DrawingProgramCache::draw_components_to_canvas(SkCanvas* canvas, const DrawData& drawData, DrawComponentsToCanvasOptionalData optData) {
|
||||
uint64_t lastComponentDrawn = 0;
|
||||
uint32_t lastComponentDrawn = 0;
|
||||
|
||||
std::vector<std::shared_ptr<DrawingProgramCacheBVHNode>> cachedNodesToDraw;
|
||||
struct UncachedObjectToDraw {
|
||||
CollabListType::ObjectInfoPtr comp;
|
||||
CanvasComponentContainer::ObjInfoSharedPtr comp;
|
||||
bool isAnUnsortedObject;
|
||||
};
|
||||
std::vector<UncachedObjectToDraw> uncachedCompsToDraw;
|
||||
|
||||
traverse_bvh_run_function(drawData.cam.viewingAreaGenerousCollider, [&](const std::shared_ptr<DrawingProgramCacheBVHNode>& node, const std::vector<CollabListType::ObjectInfoPtr>& comps) {
|
||||
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);
|
||||
|
|
@ -432,10 +400,8 @@ void DrawingProgramCache::draw_components_to_canvas(SkCanvas* canvas, const Draw
|
|||
}
|
||||
|
||||
for(auto& c : comps) {
|
||||
if(!optData.drawBounds.has_value() || !c->obj->worldAABB.has_value() || SCollision::collide(optData.drawBounds.value(), c->obj->worldAABB.value())) {
|
||||
c->obj->calculate_draw_transform(drawData);
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
@ -463,8 +429,6 @@ void DrawingProgramCache::draw_components_to_canvas(SkCanvas* canvas, const Draw
|
|||
uncachedComp.comp->obj->draw(canvas, drawData);
|
||||
if(uncachedComp.isAnUnsortedObject)
|
||||
unsortedCompDrawTime += std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - drawStartTime);
|
||||
|
||||
uncachedComp.comp->obj->drawSetupData.shouldDraw = false;
|
||||
}
|
||||
for(;nextCacheToRender < cachedNodesToDraw.size(); nextCacheToRender++)
|
||||
draw_cache_image_to_canvas(canvas, drawData, cachedNodesToDraw[nextCacheToRender]);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
#include <include/core/SkSurface.h>
|
||||
#include <unordered_set>
|
||||
#include "../DrawData.hpp"
|
||||
#include "../DrawComponents/DrawComponent.hpp"
|
||||
#include "../CanvasComponents/CanvasComponentContainer.hpp"
|
||||
|
||||
class DrawingProgram;
|
||||
class DrawingProgramCache;
|
||||
|
|
@ -12,13 +12,13 @@ class DrawingProgramCache;
|
|||
class DrawingProgramCacheBVHNode {
|
||||
public:
|
||||
SCollision::AABB<WorldScalar> bounds;
|
||||
std::vector<CollabListType::ObjectInfoPtr> components;
|
||||
std::vector<CanvasComponentContainer::ObjInfoSharedPtr> components;
|
||||
std::vector<std::shared_ptr<DrawingProgramCacheBVHNode>> children;
|
||||
|
||||
CoordSpaceHelper coords;
|
||||
Vector2i resolution;
|
||||
struct DrawCacheData {
|
||||
uint64_t lastDrawnComponentPlacement;
|
||||
uint32_t lastDrawnComponentPlacement;
|
||||
sk_sp<SkSurface> surface;
|
||||
std::chrono::steady_clock::time_point lastRenderTime;
|
||||
DrawingProgramCache* attachedCache;
|
||||
|
|
@ -38,56 +38,54 @@ class DrawingProgramCache {
|
|||
static constexpr size_t MAXIMUM_COMPONENTS_IN_SINGLE_NODE = 50;
|
||||
|
||||
bool check_if_rebuild_should_occur();
|
||||
void test_rebuild(const std::vector<CollabListType::ObjectInfoPtr>& comps, bool force = false);
|
||||
void test_rebuild_dont_include_set(const std::vector<CollabListType::ObjectInfoPtr>& comps, const std::unordered_set<CollabListType::ObjectInfoPtr>& objsToNotInclude, bool force = false);
|
||||
void test_rebuild_dont_include_set_dont_include_nodes(const std::vector<CollabListType::ObjectInfoPtr>& comps, const std::unordered_set<CollabListType::ObjectInfoPtr>& objsToNotInclude, const std::unordered_set<std::shared_ptr<DrawingProgramCacheBVHNode>>& nodesToNotInclude, bool force = false);
|
||||
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<CollabListType::ObjectInfoPtr>& s, const std::unordered_set<std::shared_ptr<DrawingProgramCacheBVHNode>>& bvhNodes);
|
||||
static void move_components_from_bvh_node_to_set(std::unordered_set<CollabListType::ObjectInfoPtr>& s, const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode);
|
||||
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 CollabListType::ObjectInfoPtr& c);
|
||||
void add_component(const CollabListType::ObjectInfoPtr& c);
|
||||
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<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 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 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);
|
||||
void invalidate_cache_at_optional_aabb_before_pos(const std::optional<SCollision::AABB<WorldScalar>>& aabb, uint64_t placementToInvalidateAt);
|
||||
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 {
|
||||
uint64_t* lastDrawnComponentPlacement = nullptr;
|
||||
uint32_t* lastDrawnComponentPlacement = nullptr;
|
||||
std::optional<SCollision::AABB<WorldScalar>> drawBounds;
|
||||
};
|
||||
|
||||
void draw_components_to_canvas(SkCanvas* canvas, const DrawData& drawData, DrawComponentsToCanvasOptionalData optData);
|
||||
const std::vector<CollabListType::ObjectInfoPtr>& get_unsorted_component_list() const;
|
||||
const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& get_unsorted_component_list() const;
|
||||
const std::chrono::steady_clock::time_point& get_last_bvh_build_time() const;
|
||||
void clear_own_cached_surfaces();
|
||||
|
||||
CollabListType::ObjectInfoPtr get_front_object_colliding_with(const SCollision::ColliderCollection<float>& cC);
|
||||
CanvasComponentContainer::ObjInfoSharedPtr get_front_object_colliding_with(const SCollision::ColliderCollection<float>& cC);
|
||||
|
||||
private:
|
||||
static std::unordered_set<std::shared_ptr<DrawingProgramCacheBVHNode>> nodesWithCachedSurfaces;
|
||||
|
||||
void draw_cache_image_to_canvas(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode);
|
||||
|
||||
void force_rebuild_dont_include_objs(std::vector<CollabListType::ObjectInfoPtr> componentsToBuild, const std::unordered_set<CollabListType::ObjectInfoPtr>& objsToNotInclude);
|
||||
void force_rebuild(const std::vector<CollabListType::ObjectInfoPtr>& componentsToBuild);
|
||||
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<CollabListType::ObjectInfoPtr>& components)> f);
|
||||
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<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(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<CollabListType::ObjectInfoPtr>& components);
|
||||
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::vector<CollabListType::ObjectInfoPtr> unsortedComponents;
|
||||
std::vector<CanvasComponentContainer::ObjInfoSharedPtr> unsortedComponents;
|
||||
DrawingProgram& drawP;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -27,18 +27,18 @@ DrawingProgramSelection::DrawingProgramSelection(DrawingProgram& initDrawP):
|
|||
drawP(initDrawP)
|
||||
{}
|
||||
|
||||
const std::unordered_set<CollabListType::ObjectInfoPtr>& DrawingProgramSelection::get_selected_set() {
|
||||
const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& DrawingProgramSelection::get_selected_set() {
|
||||
return selectedSet;
|
||||
}
|
||||
|
||||
void DrawingProgramSelection::erase_component(const CollabListType::ObjectInfoPtr& objToCheck) {
|
||||
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<CollabListType::ObjectInfoPtr>& selectedComponents, const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode) {
|
||||
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);
|
||||
|
|
@ -47,7 +47,7 @@ void DrawingProgramSelection::fully_collided_erase_select_objects_func(std::unor
|
|||
}
|
||||
}
|
||||
|
||||
std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>&, std::vector<CollabListType::ObjectInfoPtr>&)> DrawingProgramSelection::erase_select_objects_in_bvh_func(std::unordered_set<CollabListType::ObjectInfoPtr>& selectedComponents, const SCollision::ColliderCollection<float>& cC, const SCollision::ColliderCollection<WorldScalar>& cCWorld) {
|
||||
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)) &&
|
||||
|
|
@ -61,7 +61,7 @@ std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>&, std::vect
|
|||
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->worldAABB.value(), c->obj->collabListInfo.lock()->pos);
|
||||
drawP.compCache.invalidate_cache_at_aabb_before_pos(c->obj->get_world_bounds(), c->pos);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -73,9 +73,9 @@ std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>&, std::vect
|
|||
}
|
||||
|
||||
void DrawingProgramSelection::add_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection<float>& cC, bool frontObjectOnly) {
|
||||
std::unordered_set<CollabListType::ObjectInfoPtr> selectedComponents;
|
||||
std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr> selectedComponents;
|
||||
if(frontObjectOnly) {
|
||||
CollabListType::ObjectInfoPtr a = drawP.compCache.get_front_object_colliding_with(cC);
|
||||
CanvasComponentContainer::ObjInfoSharedPtr a = drawP.compCache.get_front_object_colliding_with(cC);
|
||||
if(a) {
|
||||
selectedComponents.emplace(a);
|
||||
drawP.compCache.erase_component(a);
|
||||
|
|
@ -90,9 +90,9 @@ void DrawingProgramSelection::add_from_cam_coord_collider_to_selection(const SCo
|
|||
|
||||
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<CollabListType::ObjectInfoPtr> selectedComponents;
|
||||
std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr> selectedComponents;
|
||||
if(frontObjectOnly) {
|
||||
CollabListType::ObjectInfoPtr a = cache.get_front_object_colliding_with(cC);
|
||||
CanvasComponentContainer::ObjInfoSharedPtr a = cache.get_front_object_colliding_with(cC);
|
||||
if(a) {
|
||||
selectedComponents.emplace(a);
|
||||
cache.erase_component(a);
|
||||
|
|
@ -117,7 +117,7 @@ void DrawingProgramSelection::remove_from_cam_coord_collider_to_selection(const
|
|||
}
|
||||
}
|
||||
|
||||
void DrawingProgramSelection::add_to_selection(const std::unordered_set<CollabListType::ObjectInfoPtr>& newSelection) {
|
||||
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);
|
||||
|
|
@ -125,7 +125,7 @@ void DrawingProgramSelection::add_to_selection(const std::unordered_set<CollabLi
|
|||
calculate_initial_rotate_center_location();
|
||||
}
|
||||
|
||||
void DrawingProgramSelection::set_to_selection(const std::unordered_set<CollabListType::ObjectInfoPtr>& newSelection) {
|
||||
void DrawingProgramSelection::set_to_selection(const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& newSelection) {
|
||||
selectedSet = newSelection;
|
||||
for(auto& obj : selectedSet)
|
||||
cache.add_component(obj);
|
||||
|
|
@ -139,9 +139,9 @@ bool DrawingProgramSelection::is_something_selected() {
|
|||
|
||||
void DrawingProgramSelection::calculate_aabb() {
|
||||
if(is_something_selected()) {
|
||||
initialSelectionAABB = (*selectedSet.begin())->obj->worldAABB.value();
|
||||
initialSelectionAABB = (*selectedSet.begin())->obj->get_world_bounds();
|
||||
for(auto& c : selectedSet)
|
||||
initialSelectionAABB.include_aabb_in_bounds(c->obj->worldAABB.value());
|
||||
initialSelectionAABB.include_aabb_in_bounds(c->obj->get_world_bounds());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -149,7 +149,7 @@ 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.client_list(), true);
|
||||
drawP.compCache.test_rebuild(drawP.components->get_data(), true);
|
||||
else {
|
||||
for(auto& obj : selectedSet)
|
||||
drawP.compCache.add_component(obj);
|
||||
|
|
@ -162,99 +162,51 @@ void DrawingProgramSelection::deselect_all() {
|
|||
void DrawingProgramSelection::commit_transform_selection() {
|
||||
auto selectedSetTemp = selectedSet;
|
||||
|
||||
std::vector<CollabListType::ObjectInfoPtr> a(selectedSet.begin(), selectedSet.end());
|
||||
std::vector<std::pair<CollabListType::ObjectInfoPtr, CoordSpaceHelper>> transformsFrom;
|
||||
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(drawP, false);
|
||||
obj->obj->commit_transform_dont_invalidate_cache(drawP);
|
||||
}, isSingleThread);
|
||||
std::vector<std::pair<CollabListType::ObjectInfoPtr, CoordSpaceHelper>> transformsTo;
|
||||
std::vector<std::pair<ServerClientID, CoordSpaceHelper>> transformsToSend;
|
||||
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->id, transformedObj->obj->coords);
|
||||
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);
|
||||
drawP.world.undo.push(UndoManager::UndoRedoPair{
|
||||
[isSingleThread, &drawP = drawP, transformsFrom = transformsFrom]() {
|
||||
std::vector<std::pair<ServerClientID, CoordSpaceHelper>> transformsToSend;
|
||||
for(auto& [comp, coords] : transformsFrom) {
|
||||
auto lockObjInfo = comp->obj->collabListInfo.lock();
|
||||
if(lockObjInfo)
|
||||
transformsToSend.emplace_back(lockObjInfo->obj->id, coords);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
parallel_loop_container(transformsFrom, [&](auto& p) {
|
||||
auto& [comp, coords] = p;
|
||||
comp->obj->coords = coords;
|
||||
comp->obj->commit_transform(drawP, isSingleThread);
|
||||
}, isSingleThread);
|
||||
|
||||
DrawComponent::client_send_transform_many(drawP, transformsToSend);
|
||||
|
||||
if(!isSingleThread)
|
||||
drawP.force_rebuild_cache();
|
||||
|
||||
return true;
|
||||
},
|
||||
[isSingleThread, &drawP = drawP, transformsTo = transformsTo]() {
|
||||
std::vector<std::pair<ServerClientID, CoordSpaceHelper>> transformsToSend;
|
||||
for(auto& [comp, coords] : transformsTo) {
|
||||
auto lockObjInfo = comp->obj->collabListInfo.lock();
|
||||
if(lockObjInfo)
|
||||
transformsToSend.emplace_back(lockObjInfo->obj->id, coords);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
parallel_loop_container(transformsTo, [&](auto& p) {
|
||||
auto& [comp, coords] = p;
|
||||
comp->obj->coords = coords;
|
||||
comp->obj->commit_transform(drawP, isSingleThread);
|
||||
}, isSingleThread);
|
||||
|
||||
DrawComponent::client_send_transform_many(drawP, transformsToSend);
|
||||
|
||||
if(!isSingleThread)
|
||||
drawP.force_rebuild_cache();
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
//DrawComponent::client_send_transform_many(drawP, transformsToSend);
|
||||
|
||||
reset_selection_data();
|
||||
reset_transform_data();
|
||||
set_to_selection(selectedSetTemp);
|
||||
}
|
||||
|
||||
CollabListType::ObjectInfoPtr DrawingProgramSelection::get_front_object_colliding_with(const SCollision::ColliderCollection<float>& cC) {
|
||||
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_optional_aabb_before_pos(const std::optional<SCollision::AABB<WorldScalar>>& aabb, uint64_t placementToInvalidateAt) {
|
||||
cache.invalidate_cache_at_optional_aabb_before_pos(aabb, placementToInvalidateAt);
|
||||
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 CollabListType::ObjectInfoPtr& objToCheck) {
|
||||
void DrawingProgramSelection::preupdate_component(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck) {
|
||||
cache.preupdate_component(objToCheck);
|
||||
}
|
||||
|
||||
bool DrawingProgramSelection::is_selected(const CollabListType::ObjectInfoPtr& 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);
|
||||
//drawP.client_erase_set(selectedSet);
|
||||
reset_selection_data();
|
||||
reset_transform_data();
|
||||
}
|
||||
|
|
@ -262,26 +214,26 @@ void DrawingProgramSelection::delete_all() {
|
|||
|
||||
// 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<CollabListType::ObjectInfoPtr> 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);
|
||||
//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<CollabListType::ObjectInfoPtr>(selectedSet.begin(), selectedSet.end()), true);
|
||||
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());
|
||||
|
|
@ -397,45 +349,42 @@ bool DrawingProgramSelection::is_being_transformed() {
|
|||
}
|
||||
|
||||
void DrawingProgramSelection::paste_clipboard(Vector2f pasteScreenPos) {
|
||||
auto& clipboard = drawP.world.main.clipboard;
|
||||
//auto& clipboard = drawP.world.main.clipboard;
|
||||
|
||||
if(!drawP.is_selection_allowing_tool(drawP.drawTool->get_type()))
|
||||
drawP.switch_to_tool(DrawingProgramToolType::EDIT);
|
||||
//if(!drawP.is_selection_allowing_tool(drawP.drawTool->get_type()))
|
||||
// drawP.switch_to_tool(DrawingProgramToolType::EDIT);
|
||||
|
||||
uint64_t allPlacement = drawP.components.client_list().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;
|
||||
}
|
||||
}
|
||||
//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->coords.translate(-moveVec);
|
||||
c->obj->coords.scale_about(mousePos, scaleMultiplier);
|
||||
//std::cout << c->obj->coords.inverseScale << std::endl;
|
||||
//if(c->obj->coords.inverseScale == WorldScalar(0))
|
||||
// throw std::runtime_error("DONE");
|
||||
c->obj->commit_transform(drawP, false);
|
||||
});
|
||||
DrawComponent::client_send_place_many(drawP, compListInserted);
|
||||
drawP.addToCompCacheOnInsert = true;
|
||||
std::unordered_set<CollabListType::ObjectInfoPtr> compSetInserted(compListInserted.begin(), compListInserted.end());
|
||||
set_to_selection(compSetInserted);
|
||||
drawP.add_undo_place_components(compSetInserted);
|
||||
//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() {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ class DrawingProgramSelection {
|
|||
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);
|
||||
|
||||
CollabListType::ObjectInfoPtr get_front_object_colliding_with(const SCollision::ColliderCollection<float>& cC);
|
||||
CanvasComponentContainer::ObjInfoSharedPtr get_front_object_colliding_with(const SCollision::ColliderCollection<float>& cC);
|
||||
|
||||
void deselect_all();
|
||||
void update();
|
||||
|
|
@ -18,31 +18,31 @@ class DrawingProgramSelection {
|
|||
void draw_gui(SkCanvas* canvas, const DrawData& drawData);
|
||||
bool is_something_selected();
|
||||
bool mouse_collided_with_selection();
|
||||
void erase_component(const CollabListType::ObjectInfoPtr& objToCheck);
|
||||
bool is_selected(const CollabListType::ObjectInfoPtr& objToCheck);
|
||||
const std::unordered_set<CollabListType::ObjectInfoPtr>& get_selected_set();
|
||||
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_optional_aabb_before_pos(const std::optional<SCollision::AABB<WorldScalar>>& aabb, uint64_t placementToInvalidateAt);
|
||||
void invalidate_cache_at_aabb_before_pos(const SCollision::AABB<WorldScalar>& aabb, uint64_t placementToInvalidateAt);
|
||||
void clear_own_cached_surfaces();
|
||||
void preupdate_component(const CollabListType::ObjectInfoPtr& objToCheck);
|
||||
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<CollabListType::ObjectInfoPtr>&)> erase_select_objects_in_bvh_func(std::unordered_set<CollabListType::ObjectInfoPtr>& selectedComponents, const SCollision::ColliderCollection<float>& cC, const SCollision::ColliderCollection<WorldScalar>& cCWorld);
|
||||
void fully_collided_erase_select_objects_func(std::unordered_set<CollabListType::ObjectInfoPtr>& selectedComponents, const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode);
|
||||
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<CollabListType::ObjectInfoPtr>& newSelection);
|
||||
void add_to_selection(const std::unordered_set<CollabListType::ObjectInfoPtr>& newSelection);
|
||||
void add_erased_objects_back_to_cache(const std::unordered_set<CollabListType::ObjectInfoPtr>& erasedObjects);
|
||||
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();
|
||||
|
||||
|
|
@ -59,7 +59,7 @@ class DrawingProgramSelection {
|
|||
|
||||
CoordSpaceHelperTransform selectionTransformCoords;
|
||||
|
||||
std::unordered_set<CollabListType::ObjectInfoPtr> selectedSet;
|
||||
std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr> selectedSet;
|
||||
DrawingProgramCache cache;
|
||||
DrawingProgram& drawP;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,12 @@
|
|||
#include "EllipseDrawTool.hpp"
|
||||
#include <chrono>
|
||||
#include "DrawingProgram.hpp"
|
||||
#include "../Server/CommandList.hpp"
|
||||
#include "../MainProgram.hpp"
|
||||
#include "../DrawData.hpp"
|
||||
#include "DrawingProgramToolBase.hpp"
|
||||
#include "Helpers/MathExtras.hpp"
|
||||
#include "Helpers/SCollision.hpp"
|
||||
#include "Helpers/Serializers.hpp"
|
||||
#include "../InputManager.hpp"
|
||||
#include "../SharedTypes.hpp"
|
||||
#include <cereal/types/vector.hpp>
|
||||
#include "../CanvasComponents/EllipseCanvasComponent.hpp"
|
||||
|
||||
EllipseDrawTool::EllipseDrawTool(DrawingProgram& initDrawP):
|
||||
DrawingProgramToolBase(initDrawP)
|
||||
|
|
@ -24,10 +20,10 @@ void EllipseDrawTool::gui_toolbox() {
|
|||
Toolbar& t = drawP.world.main.toolbar;
|
||||
t.gui.push_id("ellipse draw tool");
|
||||
t.gui.text_label_centered("Draw Ellipse");
|
||||
if(t.gui.radio_button_field("fillonly", "Fill only", controls.fillStrokeMode == 0)) controls.fillStrokeMode = 0;
|
||||
if(t.gui.radio_button_field("outlineonly", "Outline only", controls.fillStrokeMode == 1)) controls.fillStrokeMode = 1;
|
||||
if(t.gui.radio_button_field("filloutline", "Fill and Outline", controls.fillStrokeMode == 2)) controls.fillStrokeMode = 2;
|
||||
if(controls.fillStrokeMode == 1 || controls.fillStrokeMode == 2)
|
||||
if(t.gui.radio_button_field("fillonly", "Fill only", fillStrokeMode == 0)) fillStrokeMode = 0;
|
||||
if(t.gui.radio_button_field("outlineonly", "Outline only", fillStrokeMode == 1)) fillStrokeMode = 1;
|
||||
if(t.gui.radio_button_field("filloutline", "Fill and Outline", fillStrokeMode == 2)) fillStrokeMode = 2;
|
||||
if(fillStrokeMode == 1 || fillStrokeMode == 2)
|
||||
t.gui.slider_scalar_field("relstrokewidth", "Outline Size", &drawP.controls.relativeWidth, 3.0f, 40.0f);
|
||||
t.gui.pop_id();
|
||||
}
|
||||
|
|
@ -43,46 +39,45 @@ void EllipseDrawTool::switch_tool(DrawingProgramToolType newTool) {
|
|||
}
|
||||
|
||||
void EllipseDrawTool::tool_update() {
|
||||
switch(controls.drawStage) {
|
||||
switch(drawStage) {
|
||||
case 0: {
|
||||
if(drawP.controls.leftClick) {
|
||||
controls.startAt = drawP.world.main.input.mouse.pos;
|
||||
controls.intermediateItem = std::make_shared<DrawEllipse>();
|
||||
controls.intermediateItem->d.strokeColor = drawP.controls.foregroundColor;
|
||||
controls.intermediateItem->d.fillColor = drawP.controls.backgroundColor;
|
||||
controls.intermediateItem->d.strokeWidth = drawP.controls.relativeWidth;
|
||||
controls.intermediateItem->d.p1 = controls.startAt;
|
||||
controls.intermediateItem->d.p2 = controls.startAt;
|
||||
controls.intermediateItem->d.p2 = ensure_points_have_distance(controls.intermediateItem->d.p1, controls.intermediateItem->d.p2, MINIMUM_DISTANCE_BETWEEN_BOUNDS);
|
||||
controls.intermediateItem->d.fillStrokeMode = static_cast<uint8_t>(controls.fillStrokeMode);
|
||||
controls.intermediateItem->coords = drawP.world.drawData.cam.c;
|
||||
controls.intermediateItem->lastUpdateTime = std::chrono::steady_clock::now();
|
||||
uint64_t placement = drawP.components.client_list().size();
|
||||
auto objAdd = drawP.components.client_insert(placement, controls.intermediateItem);
|
||||
controls.intermediateItem->client_send_place(drawP);
|
||||
drawP.add_undo_place_component(objAdd);
|
||||
controls.intermediateItem->commit_update(drawP);
|
||||
controls.drawStage = 1;
|
||||
CanvasComponentContainer* newContainer = new CanvasComponentContainer(drawP.world.netObjMan, CanvasComponent::CompType::ELLIPSE);
|
||||
EllipseCanvasComponent& newEllipse = static_cast<EllipseCanvasComponent&>(newContainer->get_comp());
|
||||
|
||||
startAt = drawP.world.main.input.mouse.pos;
|
||||
newEllipse.d.strokeColor = drawP.controls.foregroundColor;
|
||||
newEllipse.d.fillColor = drawP.controls.backgroundColor;
|
||||
newEllipse.d.strokeWidth = drawP.controls.relativeWidth;
|
||||
newEllipse.d.p1 = startAt;
|
||||
newEllipse.d.p2 = startAt;
|
||||
newEllipse.d.p2 = ensure_points_have_distance(newEllipse.d.p1, newEllipse.d.p2, MINIMUM_DISTANCE_BETWEEN_BOUNDS);
|
||||
newEllipse.d.fillStrokeMode = static_cast<uint8_t>(fillStrokeMode);
|
||||
newContainer->coords = drawP.world.drawData.cam.c;
|
||||
|
||||
intermediateContainer = drawP.components->push_back_and_send_create(drawP.components, newContainer)->obj;
|
||||
drawStage = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
if(drawP.controls.leftClickHeld) {
|
||||
controls.intermediateItem->lastUpdateTime = std::chrono::steady_clock::now();
|
||||
Vector2f newPos = controls.intermediateItem->coords.get_mouse_pos(drawP.world);
|
||||
NetworkingObjects::NetObjTemporaryPtr<CanvasComponentContainer> containerPtr = intermediateContainer.lock();
|
||||
if(drawP.controls.leftClickHeld && containerPtr) {
|
||||
Vector2f newPos = containerPtr->coords.get_mouse_pos(drawP.world);
|
||||
if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held) {
|
||||
float height = std::fabs(controls.startAt.y() - newPos.y());
|
||||
newPos.x() = controls.startAt.x() + (((newPos.x() - controls.startAt.x()) < 0.0f ? -1.0f : 1.0f) * height);
|
||||
float height = std::fabs(startAt.y() - newPos.y());
|
||||
newPos.x() = startAt.x() + (((newPos.x() - startAt.x()) < 0.0f ? -1.0f : 1.0f) * height);
|
||||
}
|
||||
controls.intermediateItem->d.p1 = cwise_vec_min(controls.startAt, newPos);
|
||||
controls.intermediateItem->d.p2 = cwise_vec_max(controls.startAt, newPos);
|
||||
controls.intermediateItem->d.p2 = ensure_points_have_distance(controls.intermediateItem->d.p1, controls.intermediateItem->d.p2, MINIMUM_DISTANCE_BETWEEN_BOUNDS);
|
||||
controls.intermediateItem->client_send_update(drawP, false);
|
||||
controls.intermediateItem->commit_update(drawP);
|
||||
EllipseCanvasComponent& ellipse = static_cast<EllipseCanvasComponent&>(containerPtr->get_comp());
|
||||
ellipse.d.p1 = cwise_vec_min(startAt, newPos);
|
||||
ellipse.d.p2 = cwise_vec_max(startAt, newPos);
|
||||
ellipse.d.p2 = ensure_points_have_distance(ellipse.d.p1, ellipse.d.p2, MINIMUM_DISTANCE_BETWEEN_BOUNDS);
|
||||
containerPtr->send_comp_update(drawP, true);
|
||||
containerPtr->commit_update(drawP);
|
||||
}
|
||||
else {
|
||||
commit();
|
||||
controls.drawStage = 0;
|
||||
drawStage = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -90,15 +85,16 @@ void EllipseDrawTool::tool_update() {
|
|||
}
|
||||
|
||||
bool EllipseDrawTool::prevent_undo_or_redo() {
|
||||
return controls.intermediateItem != nullptr;
|
||||
return (drawStage == 1);
|
||||
}
|
||||
|
||||
void EllipseDrawTool::commit() {
|
||||
if(controls.intermediateItem && controls.intermediateItem->collabListInfo.lock()) {
|
||||
controls.intermediateItem->client_send_update(drawP, true);
|
||||
controls.intermediateItem->commit_update(drawP);
|
||||
NetworkingObjects::NetObjTemporaryPtr<CanvasComponentContainer> lockedPtr = intermediateContainer.lock();
|
||||
if(lockedPtr) {
|
||||
lockedPtr->commit_update(drawP);
|
||||
drawP.world.delayedUpdateObjectManager.send_update_to_all<CanvasComponentContainer>(lockedPtr, true);
|
||||
}
|
||||
controls.intermediateItem = nullptr;
|
||||
intermediateContainer.reset();
|
||||
}
|
||||
|
||||
void EllipseDrawTool::draw(SkCanvas* canvas, const DrawData& drawData) {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
#pragma once
|
||||
#include "DrawingProgramToolBase.hpp"
|
||||
#include "../DrawComponents/DrawEllipse.hpp"
|
||||
#include <any>
|
||||
#include <Helpers/SCollision.hpp>
|
||||
#include <Helpers/NetworkingObjects/NetObjWeakPtr.hpp>
|
||||
#include "../CanvasComponents/CanvasComponentContainer.hpp"
|
||||
|
||||
class DrawingProgram;
|
||||
|
||||
|
|
@ -19,10 +19,8 @@ class EllipseDrawTool : public DrawingProgramToolBase {
|
|||
private:
|
||||
void commit();
|
||||
|
||||
struct EllipseDrawControls {
|
||||
Vector2f startAt;
|
||||
int fillStrokeMode = 1;
|
||||
int drawStage = 0;
|
||||
std::shared_ptr<DrawEllipse> intermediateItem;
|
||||
} controls;
|
||||
Vector2f startAt;
|
||||
int fillStrokeMode = 1;
|
||||
int drawStage = 0;
|
||||
NetworkingObjects::NetObjWeakPtr<CanvasComponentContainer> intermediateContainer;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -27,9 +27,9 @@ bool EraserTool::right_click_popup_gui(Vector2f popupPos) {
|
|||
}
|
||||
|
||||
void EraserTool::switch_tool(DrawingProgramToolType newTool) {
|
||||
DrawingProgramCache::move_components_from_bvh_nodes_to_set(erasedComponents, erasedBVHNodes);
|
||||
if(!erasedComponents.empty()) {
|
||||
DrawingProgramCache::move_components_from_bvh_nodes_to_set(erasedComponents, erasedBVHNodes);
|
||||
drawP.client_erase_set(erasedComponents);
|
||||
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
|
||||
|
|
@ -62,7 +62,7 @@ void EraserTool::tool_update() {
|
|||
std::erase_if(comps, [&](auto& c) {
|
||||
if(c->obj->collides_with(drawP.world.drawData.cam.c, cCWorld, cC)) {
|
||||
erasedComponents.emplace(c);
|
||||
drawP.compCache.invalidate_cache_at_aabb_before_pos(c->obj->worldAABB.value(), c->obj->collabListInfo.lock()->pos);
|
||||
drawP.compCache.invalidate_cache_at_aabb_before_pos(c->obj->get_world_bounds(), c->pos);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
#pragma once
|
||||
#include <include/core/SkCanvas.h>
|
||||
#include "../DrawData.hpp"
|
||||
#include "../DrawComponents/DrawComponent.hpp"
|
||||
#include "DrawingProgramToolBase.hpp"
|
||||
#include "DrawingProgramCache.hpp"
|
||||
|
||||
class DrawingProgram;
|
||||
|
||||
|
|
@ -17,6 +17,6 @@ class EraserTool : public DrawingProgramToolBase {
|
|||
virtual void draw(SkCanvas* canvas, const DrawData& drawData) override;
|
||||
virtual bool prevent_undo_or_redo() override;
|
||||
|
||||
std::unordered_set<CollabListType::ObjectInfoPtr> erasedComponents;
|
||||
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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ void GridModifyTool::tool_update() {
|
|||
selectionMode = 0;
|
||||
break;
|
||||
}
|
||||
drawP.world.delayedUpdateObjectManager.send_update_to_all<WorldGrid>(gLock, nullptr, false);
|
||||
drawP.world.delayedUpdateObjectManager.send_update_to_all<WorldGrid>(gLock, false);
|
||||
}
|
||||
else
|
||||
selectionMode = 0;
|
||||
|
|
@ -187,5 +187,5 @@ void GridModifyTool::draw(SkCanvas* canvas, const DrawData& drawData) {
|
|||
void GridModifyTool::switch_tool(DrawingProgramToolType newTool) {
|
||||
NetworkingObjects::NetObjTemporaryPtr<WorldGrid> gLock = grid.lock();
|
||||
if(gLock)
|
||||
drawP.world.delayedUpdateObjectManager.send_update_to_all<WorldGrid>(gLock, nullptr, true);
|
||||
drawP.world.delayedUpdateObjectManager.send_update_to_all<WorldGrid>(gLock, true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
#include "../MainProgram.hpp"
|
||||
#include "../DrawData.hpp"
|
||||
#include "DrawingProgramToolBase.hpp"
|
||||
#include <cereal/types/vector.hpp>
|
||||
#include "../CanvasComponents/RectangleCanvasComponent.hpp"
|
||||
|
||||
RectDrawTool::RectDrawTool(DrawingProgram& initDrawP):
|
||||
DrawingProgramToolBase(initDrawP)
|
||||
|
|
@ -17,11 +17,11 @@ void RectDrawTool::gui_toolbox() {
|
|||
Toolbar& t = drawP.world.main.toolbar;
|
||||
t.gui.push_id("rect draw tool");
|
||||
t.gui.text_label_centered("Draw Rectangle");
|
||||
t.gui.slider_scalar_field("relradiuswidth", "Corner Radius", &controls.relativeRadiusWidth, 0.0f, 40.0f);
|
||||
if(t.gui.radio_button_field("fillonly", "Fill only", controls.fillStrokeMode == 0)) controls.fillStrokeMode = 0;
|
||||
if(t.gui.radio_button_field("outlineonly", "Outline only", controls.fillStrokeMode == 1)) controls.fillStrokeMode = 1;
|
||||
if(t.gui.radio_button_field("filloutline", "Fill and Outline", controls.fillStrokeMode == 2)) controls.fillStrokeMode = 2;
|
||||
if(controls.fillStrokeMode == 1 || controls.fillStrokeMode == 2)
|
||||
t.gui.slider_scalar_field("relradiuswidth", "Corner Radius", &relativeRadiusWidth, 0.0f, 40.0f);
|
||||
if(t.gui.radio_button_field("fillonly", "Fill only", fillStrokeMode == 0)) fillStrokeMode = 0;
|
||||
if(t.gui.radio_button_field("outlineonly", "Outline only", fillStrokeMode == 1)) fillStrokeMode = 1;
|
||||
if(t.gui.radio_button_field("filloutline", "Fill and Outline", fillStrokeMode == 2)) fillStrokeMode = 2;
|
||||
if(fillStrokeMode == 1 || fillStrokeMode == 2)
|
||||
t.gui.slider_scalar_field("relstrokewidth", "Outline Size", &drawP.controls.relativeWidth, 3.0f, 40.0f);
|
||||
t.gui.pop_id();
|
||||
}
|
||||
|
|
@ -34,51 +34,49 @@ bool RectDrawTool::right_click_popup_gui(Vector2f popupPos) {
|
|||
|
||||
void RectDrawTool::switch_tool(DrawingProgramToolType newTool) {
|
||||
commit_rectangle();
|
||||
controls.drawStage = 0;
|
||||
drawStage = 0;
|
||||
}
|
||||
|
||||
void RectDrawTool::tool_update() {
|
||||
switch(controls.drawStage) {
|
||||
switch(drawStage) {
|
||||
case 0: {
|
||||
if(drawP.controls.leftClick) {
|
||||
controls.startAt = drawP.world.main.input.mouse.pos;
|
||||
controls.intermediateItem = std::make_shared<DrawRectangle>();
|
||||
controls.intermediateItem->d.strokeColor = drawP.controls.foregroundColor;
|
||||
controls.intermediateItem->d.fillColor = drawP.controls.backgroundColor;
|
||||
controls.intermediateItem->d.cornerRadius = controls.relativeRadiusWidth;
|
||||
controls.intermediateItem->d.strokeWidth = drawP.controls.relativeWidth;
|
||||
controls.intermediateItem->d.p1 = controls.startAt;
|
||||
controls.intermediateItem->d.p2 = controls.startAt;
|
||||
controls.intermediateItem->d.p2 = ensure_points_have_distance(controls.intermediateItem->d.p1, controls.intermediateItem->d.p2, MINIMUM_DISTANCE_BETWEEN_BOUNDS);
|
||||
controls.intermediateItem->d.fillStrokeMode = static_cast<uint8_t>(controls.fillStrokeMode);
|
||||
controls.intermediateItem->coords = drawP.world.drawData.cam.c;
|
||||
controls.intermediateItem->lastUpdateTime = std::chrono::steady_clock::now();
|
||||
uint64_t placement = drawP.components.client_list().size();
|
||||
auto objAdd = drawP.components.client_insert(placement, controls.intermediateItem);
|
||||
controls.intermediateItem->commit_update(drawP);
|
||||
controls.intermediateItem->client_send_place(drawP);
|
||||
drawP.add_undo_place_component(objAdd);
|
||||
controls.drawStage = 1;
|
||||
CanvasComponentContainer* newContainer = new CanvasComponentContainer(drawP.world.netObjMan, CanvasComponent::CompType::RECTANGLE);
|
||||
RectangleCanvasComponent& newRectangle = static_cast<RectangleCanvasComponent&>(newContainer->get_comp());
|
||||
|
||||
startAt = drawP.world.main.input.mouse.pos;
|
||||
newRectangle.d.strokeColor = drawP.controls.foregroundColor;
|
||||
newRectangle.d.fillColor = drawP.controls.backgroundColor;
|
||||
newRectangle.d.cornerRadius = relativeRadiusWidth;
|
||||
newRectangle.d.strokeWidth = drawP.controls.relativeWidth;
|
||||
newRectangle.d.p1 = startAt;
|
||||
newRectangle.d.p2 = startAt;
|
||||
newRectangle.d.p2 = ensure_points_have_distance(newRectangle.d.p1, newRectangle.d.p2, MINIMUM_DISTANCE_BETWEEN_BOUNDS);
|
||||
newRectangle.d.fillStrokeMode = static_cast<uint8_t>(fillStrokeMode);
|
||||
newContainer->coords = drawP.world.drawData.cam.c;
|
||||
intermediateContainer = drawP.components->push_back_and_send_create(drawP.components, newContainer)->obj;
|
||||
drawStage = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
if(drawP.controls.leftClickHeld) {
|
||||
controls.intermediateItem->lastUpdateTime = std::chrono::steady_clock::now();
|
||||
Vector2f newPos = controls.intermediateItem->coords.get_mouse_pos(drawP.world);
|
||||
NetworkingObjects::NetObjTemporaryPtr<CanvasComponentContainer> containerPtr = intermediateContainer.lock();
|
||||
if(drawP.controls.leftClickHeld && containerPtr) {
|
||||
Vector2f newPos = containerPtr->coords.get_mouse_pos(drawP.world);
|
||||
if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held) {
|
||||
float height = std::fabs(controls.startAt.y() - newPos.y());
|
||||
newPos.x() = controls.startAt.x() + (((newPos.x() - controls.startAt.x()) < 0.0f ? -1.0f : 1.0f) * height);
|
||||
float height = std::fabs(startAt.y() - newPos.y());
|
||||
newPos.x() = startAt.x() + (((newPos.x() - startAt.x()) < 0.0f ? -1.0f : 1.0f) * height);
|
||||
}
|
||||
controls.intermediateItem->d.p1 = cwise_vec_min(controls.startAt, newPos);
|
||||
controls.intermediateItem->d.p2 = cwise_vec_max(controls.startAt, newPos);
|
||||
controls.intermediateItem->d.p2 = ensure_points_have_distance(controls.intermediateItem->d.p1, controls.intermediateItem->d.p2, MINIMUM_DISTANCE_BETWEEN_BOUNDS);
|
||||
controls.intermediateItem->client_send_update(drawP, false);
|
||||
controls.intermediateItem->commit_update(drawP);
|
||||
RectangleCanvasComponent& rectangle = static_cast<RectangleCanvasComponent&>(containerPtr->get_comp());
|
||||
rectangle.d.p1 = cwise_vec_min(startAt, newPos);
|
||||
rectangle.d.p2 = cwise_vec_max(startAt, newPos);
|
||||
rectangle.d.p2 = ensure_points_have_distance(rectangle.d.p1, rectangle.d.p2, MINIMUM_DISTANCE_BETWEEN_BOUNDS);
|
||||
containerPtr->send_comp_update(drawP, true);
|
||||
containerPtr->commit_update(drawP);
|
||||
}
|
||||
else {
|
||||
commit_rectangle();
|
||||
controls.drawStage = 0;
|
||||
drawStage = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -86,15 +84,16 @@ void RectDrawTool::tool_update() {
|
|||
}
|
||||
|
||||
bool RectDrawTool::prevent_undo_or_redo() {
|
||||
return controls.intermediateItem != nullptr;
|
||||
return (drawStage == 1);
|
||||
}
|
||||
|
||||
void RectDrawTool::commit_rectangle() {
|
||||
if(controls.intermediateItem && controls.intermediateItem->collabListInfo.lock()) {
|
||||
controls.intermediateItem->client_send_update(drawP, true);
|
||||
controls.intermediateItem->commit_update(drawP);
|
||||
NetworkingObjects::NetObjTemporaryPtr<CanvasComponentContainer> lockedPtr = intermediateContainer.lock();
|
||||
if(lockedPtr) {
|
||||
lockedPtr->commit_update(drawP);
|
||||
drawP.world.delayedUpdateObjectManager.send_update_to_all<CanvasComponentContainer>(lockedPtr, true);
|
||||
}
|
||||
controls.intermediateItem = nullptr;
|
||||
intermediateContainer.reset();
|
||||
}
|
||||
|
||||
void RectDrawTool::draw(SkCanvas* canvas, const DrawData& drawData) {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
#pragma once
|
||||
#include "DrawingProgramToolBase.hpp"
|
||||
#include "../DrawComponents/DrawRectangle.hpp"
|
||||
#include <Helpers/SCollision.hpp>
|
||||
#include <any>
|
||||
#include "../CanvasComponents/CanvasComponentContainer.hpp"
|
||||
|
||||
class DrawingProgram;
|
||||
|
||||
|
|
@ -19,11 +18,9 @@ class RectDrawTool : public DrawingProgramToolBase {
|
|||
private:
|
||||
void commit_rectangle();
|
||||
|
||||
struct RectDrawControls {
|
||||
Vector2f startAt;
|
||||
float relativeRadiusWidth = 10.0f;
|
||||
int fillStrokeMode = 1;
|
||||
int drawStage = 0;
|
||||
std::shared_ptr<DrawRectangle> intermediateItem;
|
||||
} controls;
|
||||
Vector2f startAt;
|
||||
float relativeRadiusWidth = 10.0f;
|
||||
int fillStrokeMode = 1;
|
||||
int drawStage = 0;
|
||||
NetworkingObjects::NetObjWeakPtr<CanvasComponentContainer> intermediateContainer;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -12,9 +12,6 @@ enum ServerCommands : MessageCommandType {
|
|||
SERVER_PLACE_SINGLE_COMPONENT,
|
||||
SERVER_PLACE_MANY_COMPONENTS,
|
||||
SERVER_TRANSFORM_MANY_COMPONENTS,
|
||||
SERVER_ERASE_SINGLE_COMPONENT,
|
||||
SERVER_ERASE_MANY_COMPONENTS,
|
||||
SERVER_UPDATE_COMPONENT,
|
||||
SERVER_NEW_RESOURCE_ID,
|
||||
SERVER_NEW_RESOURCE_DATA,
|
||||
SERVER_CHAT_MESSAGE,
|
||||
|
|
@ -29,12 +26,7 @@ enum ClientCommands : MessageCommandType {
|
|||
CLIENT_UPDATE_NETWORK_OBJECT,
|
||||
CLIENT_USER_CONNECT,
|
||||
CLIENT_USER_DISCONNECT,
|
||||
CLIENT_PLACE_SINGLE_COMPONENT,
|
||||
CLIENT_PLACE_MANY_COMPONENTS,
|
||||
CLIENT_TRANSFORM_MANY_COMPONENTS,
|
||||
CLIENT_ERASE_SINGLE_COMPONENT,
|
||||
CLIENT_ERASE_MANY_COMPONENTS,
|
||||
CLIENT_UPDATE_COMPONENT,
|
||||
CLIENT_MOVE_CAMERA,
|
||||
CLIENT_MOVE_SCREEN_MOUSE,
|
||||
CLIENT_NEW_RESOURCE_ID,
|
||||
|
|
|
|||
|
|
@ -3,9 +3,6 @@
|
|||
#include "Helpers/HsvRgb.hpp"
|
||||
#include "Helpers/Networking/NetLibrary.hpp"
|
||||
#include "ServerData.hpp"
|
||||
#include "../DrawComponents/DrawBrushStroke.hpp"
|
||||
#include "../DrawComponents/DrawRectangle.hpp"
|
||||
#include "../DrawComponents/DrawEllipse.hpp"
|
||||
#include "../SharedTypes.hpp"
|
||||
#include "../CoordSpaceHelper.hpp"
|
||||
#include <cereal/archives/portable_binary.hpp>
|
||||
|
|
@ -19,17 +16,11 @@
|
|||
#include <chrono>
|
||||
#include <limits>
|
||||
#include <fstream>
|
||||
#include "../DrawComponents/DrawComponent.hpp"
|
||||
#include "../World.hpp"
|
||||
|
||||
MainServer::MainServer(World& initWorld, const std::string& serverLocalID):
|
||||
world(initWorld)
|
||||
{
|
||||
for(auto& c : world.drawProg.components.client_list()) {
|
||||
data.components.emplace_back(c->obj->copy(world.drawProg));
|
||||
data.components.back()->id = c->obj->id;
|
||||
data.idToComponentMap.emplace(data.components.back()->id, data.components.back());
|
||||
}
|
||||
data.canvasScale = world.canvasScale;
|
||||
data.resources = world.rMan.resource_list();
|
||||
data.canvasBackColor = convert_vec3<Vector3f>(world.canvasTheme.backColor);
|
||||
|
|
@ -97,116 +88,6 @@ MainServer::MainServer(World& initWorld, const std::string& serverLocalID):
|
|||
message(c.cursorPos);
|
||||
netServer->send_items_to_all_clients_except(client, UNRELIABLE_COMMAND_CHANNEL, CLIENT_MOVE_SCREEN_MOUSE, c.serverID, c.cursorPos);
|
||||
});
|
||||
netServer->add_recv_callback(SERVER_PLACE_SINGLE_COMPONENT, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
|
||||
uint64_t placement;
|
||||
DrawComponentType type;
|
||||
message(placement, type);
|
||||
auto newComp = DrawComponent::allocate_comp_type(type);
|
||||
message(newComp->id, newComp->coords, *newComp);
|
||||
placement = std::min<uint64_t>(placement, data.components.size());
|
||||
data.components.insert(data.components.begin() + placement, newComp);
|
||||
data.idToComponentMap.emplace(newComp->id, newComp);
|
||||
|
||||
auto& c = clients[client->customID];
|
||||
if(c.canvasScale < data.canvasScale)
|
||||
newComp->scale_up(FixedPoint::pow_int(CANVAS_SCALE_UP_STEP, data.canvasScale - c.canvasScale));
|
||||
|
||||
newComp->server_send_place(*this, placement);
|
||||
});
|
||||
netServer->add_recv_callback(SERVER_PLACE_MANY_COMPONENTS, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
|
||||
std::vector<CollabListType::ObjectInfoPtr> objOrderedVector;
|
||||
SendOrderedComponentVectorOp a{&objOrderedVector};
|
||||
message(a);
|
||||
|
||||
std::vector<std::shared_ptr<DrawComponent>> objOrderedComponentPairs;
|
||||
for(auto& o : objOrderedVector)
|
||||
objOrderedComponentPairs.emplace_back(o->obj);
|
||||
|
||||
uint64_t startPoint = 0;
|
||||
|
||||
if(objOrderedVector.empty())
|
||||
return;
|
||||
|
||||
data.idToComponentMap.emplace(objOrderedVector.front()->obj->id, objOrderedVector.front()->obj);
|
||||
|
||||
for(uint64_t i = 1; i < static_cast<uint64_t>(objOrderedVector.size()); i++) {
|
||||
auto& obj = objOrderedVector[i];
|
||||
data.idToComponentMap.emplace(obj->obj->id, obj->obj);
|
||||
if(obj->pos - objOrderedVector[startPoint]->pos != (i - startPoint)) {
|
||||
uint64_t insertPosition = std::min<uint64_t>(data.components.size(), objOrderedVector[startPoint]->pos);
|
||||
data.components.insert(data.components.begin() + insertPosition, objOrderedComponentPairs.begin() + startPoint, objOrderedComponentPairs.begin() + i);
|
||||
startPoint = i;
|
||||
}
|
||||
}
|
||||
|
||||
data.components.insert(data.components.begin() + std::min<uint64_t>(data.components.size(), objOrderedVector[startPoint]->pos), objOrderedComponentPairs.begin() + startPoint, objOrderedComponentPairs.end());
|
||||
|
||||
startPoint = 0;
|
||||
|
||||
for(uint64_t i = 0; i < data.components.size(); i++) {
|
||||
if(startPoint >= objOrderedVector.size())
|
||||
break;
|
||||
auto& c = data.components[i];
|
||||
auto& o = objOrderedVector[startPoint];
|
||||
if(c == o->obj) {
|
||||
o->pos = i;
|
||||
startPoint++;
|
||||
}
|
||||
}
|
||||
|
||||
auto& c = clients[client->customID];
|
||||
if(c.canvasScale < data.canvasScale) {
|
||||
for(auto& o : objOrderedVector)
|
||||
o->obj->scale_up(FixedPoint::pow_int(CANVAS_SCALE_UP_STEP, data.canvasScale - c.canvasScale));
|
||||
}
|
||||
|
||||
DrawComponent::server_send_place_many(*this, objOrderedVector);
|
||||
});
|
||||
netServer->add_recv_callback(SERVER_ERASE_SINGLE_COMPONENT, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
|
||||
ServerClientID compToRemove;
|
||||
message(compToRemove);
|
||||
DrawComponent::server_send_erase(*this, compToRemove);
|
||||
data.idToComponentMap.erase(compToRemove);
|
||||
std::erase_if(data.components, [&](auto& p) {
|
||||
return p->id == compToRemove;
|
||||
});
|
||||
});
|
||||
netServer->add_recv_callback(SERVER_ERASE_MANY_COMPONENTS, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
|
||||
std::unordered_set<ServerClientID> idsToRemove;
|
||||
message(idsToRemove);
|
||||
DrawComponent::server_send_erase_set(*this, idsToRemove);
|
||||
for(auto& id : idsToRemove)
|
||||
data.idToComponentMap.erase(id);
|
||||
std::erase_if(data.components, [&](auto& p) {
|
||||
return idsToRemove.contains(p->id);
|
||||
});
|
||||
});
|
||||
netServer->add_recv_callback(SERVER_TRANSFORM_MANY_COMPONENTS, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
|
||||
std::vector<std::pair<ServerClientID, CoordSpaceHelper>> transformsToSend;
|
||||
message(transformsToSend);
|
||||
for(auto& t : transformsToSend) {
|
||||
auto it = data.idToComponentMap.find(t.first);
|
||||
if(it != data.idToComponentMap.end())
|
||||
it->second->coords = t.second;
|
||||
}
|
||||
auto& c = clients[client->customID];
|
||||
if(c.canvasScale < data.canvasScale) {
|
||||
for(auto& [id, coord] : transformsToSend)
|
||||
coord.scale_about(WorldVec{0, 0}, FixedPoint::pow_int(CANVAS_SCALE_UP_STEP, data.canvasScale - c.canvasScale), true);
|
||||
}
|
||||
DrawComponent::server_send_transform_many(*this, transformsToSend);
|
||||
});
|
||||
netServer->add_recv_callback(SERVER_UPDATE_COMPONENT, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
|
||||
bool isFinalUpdate;
|
||||
ServerClientID idToUpdate;
|
||||
message(isFinalUpdate, idToUpdate);
|
||||
auto it = data.idToComponentMap.find(idToUpdate);
|
||||
if(it != data.idToComponentMap.end()) {
|
||||
std::shared_ptr<DrawComponent>& comp = it->second;
|
||||
message(*comp);
|
||||
comp->server_send_update(*this, isFinalUpdate);
|
||||
}
|
||||
});
|
||||
netServer->add_recv_callback(SERVER_NEW_RESOURCE_ID, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
|
||||
auto& c = clients[client->customID];
|
||||
message(c.pendingResourceID);
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@ class DrawComponent;
|
|||
|
||||
class ServerData {
|
||||
public:
|
||||
std::vector<std::shared_ptr<DrawComponent>> components;
|
||||
std::unordered_map<ServerClientID, std::shared_ptr<DrawComponent>> idToComponentMap;
|
||||
std::unordered_map<ServerClientID, ResourceData> resources;
|
||||
uint64_t canvasScale = 0;
|
||||
Vector3f canvasBackColor;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
#include "World.hpp"
|
||||
#include "DrawComponents/DrawComponent.hpp"
|
||||
#include <Helpers/HsvRgb.hpp>
|
||||
#include <Helpers/MathExtras.hpp>
|
||||
#include <Helpers/Networking/ByteStream.hpp>
|
||||
|
|
@ -14,7 +13,7 @@
|
|||
#include "Toolbar.hpp"
|
||||
#include "VersionConstants.hpp"
|
||||
#include "WorldGrid.hpp"
|
||||
#include "cereal/archives/portable_binary.hpp"
|
||||
#include <cereal/archives/portable_binary.hpp>
|
||||
#include <fstream>
|
||||
#include "FileHelpers.hpp"
|
||||
#include "FontData.hpp"
|
||||
|
|
@ -24,6 +23,8 @@
|
|||
#include <cereal/types/vector.hpp>
|
||||
#include <Helpers/Networking/NetLibrary.hpp>
|
||||
#include <zstd.h>
|
||||
#include "CanvasComponents/CanvasComponentContainer.hpp"
|
||||
#include "CanvasComponents/CanvasComponentAllocator.hpp"
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <EmscriptenHelpers/emscripten_browser_file.h>
|
||||
|
|
@ -40,6 +41,7 @@ World::World(MainProgram& initMain, OpenWorldInfo& worldInfo):
|
|||
init_net_obj_type_list();
|
||||
gridMan.init();
|
||||
bMan.init();
|
||||
drawProg.init();
|
||||
|
||||
set_canvas_background_color(main.defaultCanvasBackgroundColor);
|
||||
displayName = main.displayName;
|
||||
|
|
@ -82,6 +84,8 @@ void World::init_net_obj_type_list() {
|
|||
NetworkingObjects::register_ordered_list_class<Bookmark>(netObjMan);
|
||||
delayedUpdateObjectManager.register_class<WorldGrid>(netObjMan);
|
||||
NetworkingObjects::register_ordered_list_class<WorldGrid>(netObjMan);
|
||||
CanvasComponentAllocator::register_class(delayedUpdateObjectManager, netObjMan);
|
||||
CanvasComponentContainer::register_class(netObjMan);
|
||||
}
|
||||
|
||||
void World::init_client_callbacks() {
|
||||
|
|
@ -99,7 +103,7 @@ void World::init_client_callbacks() {
|
|||
message(newBackColor);
|
||||
set_canvas_background_color(newBackColor, false);
|
||||
message(canvasScale);
|
||||
drawProg.initialize_draw_data(message);
|
||||
//drawProg.initialize_draw_data(message);
|
||||
bMan.bookmarks = netObjMan.read_create_message<NetworkingObjects::NetObjOrderedList<Bookmark>>(message, nullptr);
|
||||
gridMan.grids = netObjMan.read_create_message<NetworkingObjects::NetObjOrderedList<WorldGrid>>(message, nullptr);
|
||||
}
|
||||
|
|
@ -302,7 +306,6 @@ void World::start_hosting(const std::string& initNetSource, const std::string& s
|
|||
con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_INITIAL_DATA, displayName, true, drawData.cam.c, main.window.size.cast<float>().eval());
|
||||
conType = CONNECTIONTYPE_SERVER;
|
||||
netSource = initNetSource;
|
||||
drawProg.components.set_server_from_client_list();
|
||||
clientStillConnecting = true;
|
||||
}
|
||||
|
||||
|
|
@ -387,7 +390,7 @@ void World::load_from_file(const std::filesystem::path& filePathToLoadFrom, std:
|
|||
}
|
||||
|
||||
ClientPortionID World::get_max_id(ServerPortionID serverID) {
|
||||
return std::max(rMan.get_max_id(serverID), drawProg.get_max_id(serverID));
|
||||
return rMan.get_max_id(serverID);
|
||||
}
|
||||
|
||||
void World::save_file(cereal::PortableBinaryOutputArchive& a) const {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue