Added all canvas component classes
This commit is contained in:
parent
cfd6f4b4e0
commit
228469c33d
32 changed files with 1369 additions and 143 deletions
|
|
@ -79,9 +79,14 @@ set(sources "src/main.cpp"
|
|||
"src/DrawingProgram/EllipseDrawEditTool.cpp"
|
||||
"src/DrawingProgram/RectDrawEditTool.cpp"
|
||||
"src/DrawingProgram/TextBoxEditTool.cpp"
|
||||
#"src/CanvasComponents/CanvasComponentContainer.cpp"
|
||||
#"src/CanvasComponents/CanvasComponentBrushStroke.cpp"
|
||||
#"src/CanvasComponents/CanvasComponent.cpp"
|
||||
"src/CanvasComponents/CanvasComponentContainer.cpp"
|
||||
"src/CanvasComponents/CanvasComponentAllocator.cpp"
|
||||
"src/CanvasComponents/BrushStrokeCanvasComponent.cpp"
|
||||
"src/CanvasComponents/RectangleCanvasComponent.cpp"
|
||||
"src/CanvasComponents/TextBoxCanvasComponent.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"
|
||||
|
|
|
|||
|
|
@ -7,40 +7,47 @@
|
|||
namespace NetworkingObjects {
|
||||
class DelayUpdateSerializedClassManager {
|
||||
public:
|
||||
template <typename T> void register_class(NetObjManagerTypeList& t) {
|
||||
t.register_class<T, T, T, T>({
|
||||
.writeConstructorFuncClient = [&](const NetObjTemporaryPtr<T>& o, cereal::PortableBinaryOutputArchive& a){client_write_func<T>(std::nullopt, o, a);},
|
||||
.readConstructorFuncClient = [&](const NetObjTemporaryPtr<T>& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>&){delay_read_func_constructor<T>(o, a);},
|
||||
.readUpdateFuncClient = [&](const NetObjTemporaryPtr<T>& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>&){delay_read_func<T>(o, a);},
|
||||
.writeConstructorFuncServer = [&](const NetObjTemporaryPtr<T>& o, cereal::PortableBinaryOutputArchive& a){server_write_func<T>(o, a);},
|
||||
.readConstructorFuncServer = [&](const NetObjTemporaryPtr<T>& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>&){delay_read_func_constructor<T>(o, a);},
|
||||
template <typename T> void register_class(NetObjManager& objMan) {
|
||||
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);}
|
||||
});
|
||||
}
|
||||
template <typename T> void send_update_to_all(const NetworkingObjects::NetObjTemporaryPtr<T>& o, bool finalUpdate) {
|
||||
template <typename T> void send_update_to_all(const NetworkingObjects::NetObjTemporaryPtr<T>& o, const std::function<void(T&)>& postAssignmentFunc, 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);
|
||||
server_write_func(o, a, postAssignmentFunc);
|
||||
else
|
||||
client_write_func(finalUpdate, o, a);
|
||||
client_write_update_func(o, a, postAssignmentFunc, finalUpdate);
|
||||
});
|
||||
}
|
||||
void update();
|
||||
private:
|
||||
template <typename T> void client_write_func(std::optional<bool> finalUpdate, const NetObjTemporaryPtr<T>& o, cereal::PortableBinaryOutputArchive& a) {
|
||||
if(finalUpdate.has_value())
|
||||
a(finalUpdate.value());
|
||||
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) {
|
||||
a(finalUpdate);
|
||||
a(*o);
|
||||
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.doLastAssignment = std::make_shared<bool>(true);
|
||||
toAdd.lastUpdateTimePoint = std::chrono::steady_clock::now();
|
||||
toAdd.setDataAfterTimeout = [oWeak = NetObjWeakPtr<T>(o), currentData = toAdd.objToUpdateCurrentData, doLastAssignment = toAdd.doLastAssignment]() {
|
||||
toAdd.setDataAfterTimeout = [postAssignmentFunc, oWeak = NetObjWeakPtr<T>(o), currentData = toAdd.objToUpdateCurrentData, doLastAssignment = toAdd.doLastAssignment]() {
|
||||
NetObjTemporaryPtr<T> oLock = oWeak.lock();
|
||||
if(oLock && *doLastAssignment)
|
||||
if(oLock && *doLastAssignment) {
|
||||
*oLock = *std::static_pointer_cast<T>(currentData);
|
||||
if(postAssignmentFunc)
|
||||
postAssignmentFunc(*oLock);
|
||||
}
|
||||
};
|
||||
updatingObjs.emplace(o.get_net_id(), toAdd);
|
||||
}
|
||||
|
|
@ -50,11 +57,11 @@ namespace NetworkingObjects {
|
|||
}
|
||||
}
|
||||
|
||||
template <typename T> void delay_read_func_constructor(const NetObjTemporaryPtr<T>& o, cereal::PortableBinaryInputArchive& a) {
|
||||
template <typename T> void read_constructor_func(const NetObjTemporaryPtr<T>& o, cereal::PortableBinaryInputArchive& a) {
|
||||
a(*o);
|
||||
}
|
||||
|
||||
template <typename T> void delay_read_func(const NetObjTemporaryPtr<T>& o, cereal::PortableBinaryInputArchive& a) {
|
||||
template <typename T> void client_read_update_func(const NetObjTemporaryPtr<T>& o, cereal::PortableBinaryInputArchive& a) {
|
||||
auto it = updatingObjs.find(o.get_net_id());
|
||||
if(it == updatingObjs.end())
|
||||
a(*o);
|
||||
|
|
@ -65,18 +72,21 @@ namespace NetworkingObjects {
|
|||
}
|
||||
}
|
||||
|
||||
template <typename T> void server_write_func(const NetObjTemporaryPtr<T>& o, cereal::PortableBinaryOutputArchive& a) {
|
||||
template <typename T> void server_write_func(const NetObjTemporaryPtr<T>& o, cereal::PortableBinaryOutputArchive& a, const std::function<void(T&)>& postAssignmentFunc) {
|
||||
a(*o);
|
||||
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.lastUpdateTimePoint = std::chrono::steady_clock::now();
|
||||
toAdd.doLastAssignment = std::make_shared<bool>(false);
|
||||
toAdd.setDataAfterTimeout = [oWeak = NetObjWeakPtr<T>(o), currentData = toAdd.objToUpdateCurrentData, doLastAssignment = toAdd.doLastAssignment]() {
|
||||
toAdd.setDataAfterTimeout = [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);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ namespace NetworkingObjects {
|
|||
a(*o);
|
||||
}
|
||||
|
||||
template <typename T> void register_generic_serialized_class(NetObjManagerTypeList& t) {
|
||||
t.register_class<T, T, T, T>({
|
||||
template <typename T> void register_generic_serialized_class(NetObjManager& objMan) {
|
||||
objMan.register_class<T, T, T, T>({
|
||||
.writeConstructorFuncClient = default_serialize_write_func<T>,
|
||||
.readConstructorFuncClient = default_serialize_read_constructor_func<T>,
|
||||
.readUpdateFuncClient = default_serialize_read_func_client<T>,
|
||||
|
|
|
|||
|
|
@ -2,9 +2,8 @@
|
|||
#include "NetObjTemporaryPtr.hpp"
|
||||
|
||||
namespace NetworkingObjects {
|
||||
NetObjManager::NetObjManager(std::shared_ptr<NetObjManagerTypeList> initTypeList, bool initIsServer):
|
||||
NetObjManager::NetObjManager(bool initIsServer):
|
||||
isServer(initIsServer),
|
||||
typeList(initTypeList),
|
||||
nextTypeID(0)
|
||||
{}
|
||||
bool NetObjManager::is_server() const {
|
||||
|
|
@ -16,7 +15,7 @@ namespace NetworkingObjects {
|
|||
auto it = objectData.find(id);
|
||||
if(it == objectData.end())
|
||||
return;
|
||||
typeList->get_net_type_data(isServer, it->second.netTypeID).readUpdateFunc(NetObjTemporaryPtr<void>(this, id, it->second.p), a, clientReceivedFrom);
|
||||
typeList.get_net_type_data(isServer, it->second.netTypeID).readUpdateFunc(NetObjTemporaryPtr<void>(this, id, it->second.p), a, clientReceivedFrom);
|
||||
}
|
||||
void NetObjManager::set_client(std::shared_ptr<NetClient> initClient, MessageCommandType initUpdateCommandID) {
|
||||
client = initClient;
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
namespace NetworkingObjects {
|
||||
class NetObjManager {
|
||||
public:
|
||||
NetObjManager(std::shared_ptr<NetObjManagerTypeList> initTypeList, bool initIsServer);
|
||||
NetObjManager(bool initIsServer);
|
||||
void read_update_message(cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>& clientReceivedFrom);
|
||||
void set_client(std::shared_ptr<NetClient> initClient, MessageCommandType initUpdateCommandID);
|
||||
void set_server(std::shared_ptr<NetServer> initServer, MessageCommandType initUpdateCommandID);
|
||||
|
|
@ -30,8 +30,8 @@ namespace NetworkingObjects {
|
|||
auto it = objectData.find(id);
|
||||
if(it != objectData.end())
|
||||
throw std::runtime_error("[NetObjManager::read_create_message] Attempted to create an object with a used ID");
|
||||
NetObjOwnerPtr<T> newPtr = emplace_raw_ptr(id, static_cast<T*>(typeList->get_type_index_data<T>(isServer).allocatorFunc()));
|
||||
typeList->get_type_index_data<T>(isServer).readConstructorFunc(NetObjTemporaryPtr(newPtr).template cast<void>(), a, clientReceivedFrom);
|
||||
NetObjOwnerPtr<T> newPtr = emplace_raw_ptr(id, static_cast<T*>(typeList.get_type_index_data<T>(isServer).allocatorFunc()));
|
||||
typeList.get_type_index_data<T>(isServer).readConstructorFunc(NetObjTemporaryPtr(newPtr).template cast<void>(), a, clientReceivedFrom);
|
||||
return newPtr;
|
||||
}
|
||||
template <typename T> NetObjTemporaryPtr<T> read_get_obj_ref_from_message(cereal::PortableBinaryInputArchive& a) {
|
||||
|
|
@ -43,7 +43,7 @@ namespace NetworkingObjects {
|
|||
return NetObjTemporaryPtr<T>(this, id, std::static_pointer_cast<T>(it->second.p));
|
||||
}
|
||||
template <typename T> NetObjOwnerPtr<T> make_obj() {
|
||||
return emplace_raw_ptr<T>(NetObjID::random_gen(), static_cast<T*>(typeList->get_type_index_data<T>(isServer).allocatorFunc()));
|
||||
return emplace_raw_ptr<T>(NetObjID::random_gen(), static_cast<T*>(typeList.get_type_index_data<T>(isServer).allocatorFunc()));
|
||||
}
|
||||
// Don't use this function unless you're sure that class T isn't a base class
|
||||
template <typename T, typename... Args> NetObjOwnerPtr<T> make_obj_direct(Args&&... items) {
|
||||
|
|
@ -58,9 +58,12 @@ namespace NetworkingObjects {
|
|||
throw std::runtime_error("[NetObjManager::get_obj_from_id] ID doesn't exist");
|
||||
return NetObjOwnerPtr<T>(this, id, static_cast<T*>(it->second.p));
|
||||
}
|
||||
template <typename ClientT, typename ServerT, typename ClientAllocatedType, typename ServerAllocatedType> void register_class(const NetObjManagerTypeList::ServerClientClassFunctions<ClientT, ServerT>& funcs) {
|
||||
typeList.register_class<ClientT, ServerT, ClientAllocatedType, ServerAllocatedType>(funcs);
|
||||
}
|
||||
private:
|
||||
template <typename T> NetObjOwnerPtr<T> emplace_raw_ptr(NetObjID id, T* rawPtr) {
|
||||
if(!objectData.emplace(id, SingleObjectData{.netTypeID = typeList->get_type_index_data<T>(isServer).netTypeID, .p = rawPtr}).second)
|
||||
if(!objectData.emplace(id, SingleObjectData{.netTypeID = typeList.get_type_index_data<T>(isServer).netTypeID, .p = rawPtr}).second)
|
||||
throw std::runtime_error("[NetObjManager::emplace_raw_ptr] ID Collision");
|
||||
return NetObjOwnerPtr<T>(this, id, rawPtr);
|
||||
}
|
||||
|
|
@ -122,7 +125,7 @@ namespace NetworkingObjects {
|
|||
|
||||
template <typename T> static void write_create_message(const NetObjTemporaryPtr<T>& ptr, cereal::PortableBinaryOutputArchive& a) {
|
||||
a(ptr.get_net_id());
|
||||
ptr.get_obj_man()->typeList->template get_type_index_data<T>(ptr.get_obj_man()->isServer).writeConstructorFunc(ptr.template cast<void>(), a);
|
||||
ptr.get_obj_man()->typeList.template get_type_index_data<T>(ptr.get_obj_man()->isServer).writeConstructorFunc(ptr.template cast<void>(), a);
|
||||
}
|
||||
|
||||
template <typename T> friend class NetObjOwnerPtr;
|
||||
|
|
@ -139,7 +142,7 @@ namespace NetworkingObjects {
|
|||
std::shared_ptr<NetServer> server;
|
||||
MessageCommandType updateCommandID;
|
||||
std::unordered_map<NetObjID, SingleObjectData> objectData;
|
||||
std::shared_ptr<NetObjManagerTypeList> typeList;
|
||||
NetObjManagerTypeList typeList;
|
||||
NetTypeIDType nextTypeID;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,8 +20,7 @@ class NetObjManagerTypeList {
|
|||
template <typename ClientT, typename ServerT, typename ClientAllocatedType, typename ServerAllocatedType> void register_class(const ServerClientClassFunctions<ClientT, ServerT>& funcs) {
|
||||
typeIndexFunctionsClient[std::type_index(typeid(ClientT*))] = TypeIndexFunctions {
|
||||
.readConstructorFunc = [f = funcs.readConstructorFuncClient](const NetObjTemporaryPtr<void>& obj, cereal::PortableBinaryInputArchive& message, const std::shared_ptr<NetServer::ClientData>& c) {
|
||||
if(f)
|
||||
f(obj.cast<ClientT>(), message, c);
|
||||
f(obj.cast<ClientT>(), message, c);
|
||||
},
|
||||
.writeConstructorFunc = [f = funcs.writeConstructorFuncClient](const NetObjTemporaryPtr<void>& obj, cereal::PortableBinaryOutputArchive& message) {
|
||||
f(obj.cast<ClientT>(), message);
|
||||
|
|
@ -32,8 +31,7 @@ class NetObjManagerTypeList {
|
|||
|
||||
typeIndexFunctionsServer[std::type_index(typeid(ServerT*))] = TypeIndexFunctions {
|
||||
.readConstructorFunc = [f = funcs.readConstructorFuncServer](const NetObjTemporaryPtr<void>& obj, cereal::PortableBinaryInputArchive& message, const std::shared_ptr<NetServer::ClientData>& c) {
|
||||
if(f)
|
||||
f(obj.cast<ServerT>(), message, c);
|
||||
f(obj.cast<ServerT>(), message, c);
|
||||
},
|
||||
.writeConstructorFunc = [f = funcs.writeConstructorFuncServer](const NetObjTemporaryPtr<void>& obj, cereal::PortableBinaryOutputArchive& message) {
|
||||
f(obj.cast<ServerT>(), message);
|
||||
|
|
@ -44,15 +42,13 @@ class NetObjManagerTypeList {
|
|||
|
||||
netTypeIDFunctionsClient[netTypeID] = NetTypeIDFunctions {
|
||||
.readUpdateFunc = [f = funcs.readUpdateFuncClient](const NetObjTemporaryPtr<void>& obj, cereal::PortableBinaryInputArchive& message, const std::shared_ptr<NetServer::ClientData>& c) {
|
||||
if(f)
|
||||
f(obj.cast<ClientT>(), message, c);
|
||||
f(obj.cast<ClientT>(), message, c);
|
||||
}
|
||||
};
|
||||
|
||||
netTypeIDFunctionsServer[netTypeID] = NetTypeIDFunctions {
|
||||
.readUpdateFunc = [f = funcs.readUpdateFuncServer](const NetObjTemporaryPtr<void>& obj, cereal::PortableBinaryInputArchive& message, const std::shared_ptr<NetServer::ClientData>& c) {
|
||||
if(f)
|
||||
f(obj.cast<ServerT>(), message, c);
|
||||
f(obj.cast<ServerT>(), message, c);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -39,38 +39,38 @@ namespace NetworkingObjects {
|
|||
template <typename T> class NetObjOrderedList {
|
||||
public:
|
||||
// Don't use this function unless you're sure that class T isn't a base class
|
||||
template <typename ...Args> NetObjTemporaryPtr<T> emplace_back_direct(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, Args&&... items) {
|
||||
template <typename ...Args> const NetObjOrderedListObjectInfoPtr<T>& emplace_back_direct(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, Args&&... items) {
|
||||
NetObjOwnerPtr<T> newObj = l.get_obj_man()->template make_obj_direct<T>(items...);
|
||||
NetObjTemporaryPtr<T> tempPtr(newObj);
|
||||
l->insert(l, nullptr, l->size(), std::move(newObj));
|
||||
return tempPtr;
|
||||
return l->insert(l, nullptr, l->size(), std::move(newObj));
|
||||
}
|
||||
static void push_back_and_send_create(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, T* newObj) {
|
||||
l->insert(l, nullptr, l->size(), newObj);
|
||||
static const NetObjOrderedListObjectInfoPtr<T>& push_back_and_send_create(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, T* newObj) {
|
||||
NetObjOwnerPtr<T> newObjOwner = l.get_obj_man()->template make_obj<T>(newObj);
|
||||
return l->insert(l, nullptr, l->size(), std::move(newObjOwner));
|
||||
}
|
||||
static void insert_and_send_create(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, uint32_t posToInsertAt, const NetObjTemporaryPtr<T>& newObj) {
|
||||
l->insert(l, nullptr, posToInsertAt, newObj);
|
||||
static const NetObjOrderedListObjectInfoPtr<T>& insert_and_send_create(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, uint32_t posToInsertAt, T* newObj) {
|
||||
NetObjOwnerPtr<T> newObjOwner = l.get_obj_man()->template make_obj<T>(newObj);
|
||||
return l->insert(l, nullptr, posToInsertAt, std::move(newObjOwner));
|
||||
}
|
||||
static void erase(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, uint32_t indexToErase) {
|
||||
return l->erase_by_index(l, indexToErase);
|
||||
l->erase_by_index(l, indexToErase);
|
||||
}
|
||||
static uint32_t erase(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, const NetObjTemporaryPtr<T>& p) {
|
||||
return l->erase_by_ptr(l, p);
|
||||
}
|
||||
virtual bool contains(const NetObjTemporaryPtr<T>& p) const = 0;
|
||||
virtual uint32_t size() const = 0;
|
||||
virtual const NetObjOwnerPtr<T>& at(uint32_t index) const = 0;
|
||||
virtual const NetObjOrderedListObjectInfoPtr<T>& at(uint32_t index) const = 0;
|
||||
virtual bool empty() const = 0;
|
||||
virtual ~NetObjOrderedList() {}
|
||||
protected:
|
||||
virtual void insert(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, const std::shared_ptr<NetServer::ClientData>& clientInserting, uint32_t posToInsertAt, NetObjOwnerPtr<T> newObj) = 0;
|
||||
virtual const NetObjOrderedListObjectInfoPtr<T>& insert(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, const std::shared_ptr<NetServer::ClientData>& clientInserting, uint32_t posToInsertAt, NetObjOwnerPtr<T> newObj) = 0;
|
||||
virtual void erase_by_index(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, uint32_t indexToErase) = 0;
|
||||
virtual uint32_t erase_by_ptr(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, const NetObjTemporaryPtr<T>& p) = 0;
|
||||
virtual void write_constructor(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, cereal::PortableBinaryOutputArchive& a) = 0;
|
||||
virtual void read_update(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>&) = 0;
|
||||
virtual void read_constructor(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>&) = 0;
|
||||
private:
|
||||
template <typename S> friend void register_ordered_list_class(NetObjManagerTypeList& t);
|
||||
template <typename S> friend void register_ordered_list_class(NetObjManager& t);
|
||||
static void write_constructor_func(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, cereal::PortableBinaryOutputArchive& a) {
|
||||
l->write_constructor(l, a);
|
||||
}
|
||||
|
|
@ -91,14 +91,14 @@ namespace NetworkingObjects {
|
|||
virtual uint32_t size() const override {
|
||||
return data.size();
|
||||
}
|
||||
virtual const NetObjOwnerPtr<T>& at(uint32_t index) const override {
|
||||
return data[index]->obj;
|
||||
virtual const NetObjOrderedListObjectInfoPtr<T>& at(uint32_t index) const override {
|
||||
return data[index];
|
||||
}
|
||||
virtual bool empty() const override {
|
||||
return data.empty();
|
||||
}
|
||||
protected:
|
||||
virtual void insert(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, const std::shared_ptr<NetServer::ClientData>& clientInserting, uint32_t posToInsertAt, NetObjOwnerPtr<T> newObj) override {
|
||||
virtual const NetObjOrderedListObjectInfoPtr<T>& insert(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, const std::shared_ptr<NetServer::ClientData>& clientInserting, uint32_t posToInsertAt, NetObjOwnerPtr<T> newObj) override {
|
||||
posToInsertAt = std::min<uint32_t>(posToInsertAt, data.size());
|
||||
auto newObjInfoPtr = std::make_shared<NetObjOrderedListObjectInfo<T>>(std::move(newObj), 0);
|
||||
data.insert(data.begin() + posToInsertAt, newObjInfoPtr);
|
||||
|
|
@ -111,6 +111,7 @@ namespace NetworkingObjects {
|
|||
a(ObjPtrOrderedListCommand_StoC::INSERT_SINGLE_REFERENCE, posToInsertAt, newObjInfoPtr->obj.get_net_id());
|
||||
});
|
||||
set_positions_for_object_info_vector<T>(data, posToInsertAt);
|
||||
return data[posToInsertAt];
|
||||
}
|
||||
virtual void erase_by_index(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, uint32_t indexToErase) override {
|
||||
if(data.empty())
|
||||
|
|
@ -188,14 +189,14 @@ namespace NetworkingObjects {
|
|||
virtual uint32_t size() const override {
|
||||
return clientData.size();
|
||||
}
|
||||
virtual const NetObjOwnerPtr<T>& at(uint32_t index) const override {
|
||||
return clientData[index]->obj;
|
||||
virtual const NetObjOrderedListObjectInfoPtr<T>& at(uint32_t index) const override {
|
||||
return clientData[index];
|
||||
}
|
||||
virtual bool empty() const override {
|
||||
return clientData.empty();
|
||||
}
|
||||
protected:
|
||||
virtual void insert(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, const std::shared_ptr<NetServer::ClientData>&, uint32_t posToInsertAt, NetObjOwnerPtr<T> newObj) {
|
||||
virtual const NetObjOrderedListObjectInfoPtr<T>& insert(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, const std::shared_ptr<NetServer::ClientData>&, uint32_t posToInsertAt, NetObjOwnerPtr<T> newObj) {
|
||||
posToInsertAt = std::min<uint32_t>(posToInsertAt, clientData.size());
|
||||
auto newObjInfoPtr = std::make_shared<NetObjOrderedListObjectInfo<T>>(std::move(newObj), 0);
|
||||
clientData.insert(clientData.begin() + posToInsertAt, newObjInfoPtr);
|
||||
|
|
@ -206,6 +207,7 @@ namespace NetworkingObjects {
|
|||
newObjInfoPtr->obj.write_create_message(a);
|
||||
});
|
||||
set_positions_for_object_info_vector<T>(clientData, posToInsertAt);
|
||||
return clientData[posToInsertAt];
|
||||
}
|
||||
virtual void erase_by_index(const NetObjTemporaryPtr<NetObjOrderedList<T>>& l, uint32_t indexToErase) override {
|
||||
if(clientData.empty())
|
||||
|
|
@ -329,8 +331,8 @@ namespace NetworkingObjects {
|
|||
std::unordered_map<NetObjID, NetObjOrderedListObjectInfoPtr<T>> clientJustInsertedSyncIgnore;
|
||||
};
|
||||
|
||||
template <typename S> void register_ordered_list_class(NetObjManagerTypeList& t) {
|
||||
t.register_class<NetObjOrderedList<S>, NetObjOrderedList<S>, NetObjOrderedListClient<S>, NetObjOrderedListServer<S>>({
|
||||
template <typename S> void register_ordered_list_class(NetObjManager& objMan) {
|
||||
objMan.register_class<NetObjOrderedList<S>, NetObjOrderedList<S>, NetObjOrderedListClient<S>, NetObjOrderedListServer<S>>({
|
||||
.writeConstructorFuncClient = NetObjOrderedList<S>::write_constructor_func,
|
||||
.readConstructorFuncClient = NetObjOrderedList<S>::read_constructor_func,
|
||||
.readUpdateFuncClient = NetObjOrderedList<S>::read_update_func,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
#include "NetObjOwnerPtr.decl.hpp"
|
||||
#include "NetObjTemporaryPtr.hpp"
|
||||
#include "NetObjManager.hpp"
|
||||
#include "NetObjManagerTypeList.hpp"
|
||||
|
||||
namespace NetworkingObjects {
|
||||
template <typename T> NetObjOwnerPtr<T>::NetObjOwnerPtr():
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
#include <type_traits>
|
||||
#include "NetObjTemporaryPtr.decl.hpp"
|
||||
#include "NetObjManager.hpp"
|
||||
#include "NetObjManagerTypeList.hpp"
|
||||
|
||||
namespace NetworkingObjects {
|
||||
template <typename T> NetObjTemporaryPtr<T>::NetObjTemporaryPtr():
|
||||
|
|
|
|||
|
|
@ -32,10 +32,10 @@ void BookmarkManager::remove_bookmark(uint32_t bookmarkIndex) {
|
|||
|
||||
void BookmarkManager::jump_to_bookmark(uint32_t bookmarkIndex) {
|
||||
auto& b = bookmarks->at(bookmarkIndex);
|
||||
world.drawData.cam.smooth_move_to(world, b->coords, b->windowSize.cast<float>());
|
||||
world.drawData.cam.smooth_move_to(world, b->obj->coords, b->obj->windowSize.cast<float>());
|
||||
}
|
||||
|
||||
void BookmarkManager::scale_up(const WorldScalar& scaleUpAmount) {
|
||||
for(uint32_t i = 0; i < bookmarks->size(); i++)
|
||||
bookmarks->at(i)->scale_up(scaleUpAmount);
|
||||
bookmarks->at(i)->obj->scale_up(scaleUpAmount);
|
||||
}
|
||||
|
|
|
|||
398
src/CanvasComponents/BrushStrokeCanvasComponent.cpp
Normal file
398
src/CanvasComponents/BrushStrokeCanvasComponent.cpp
Normal file
|
|
@ -0,0 +1,398 @@
|
|||
#include "BrushStrokeCanvasComponent.hpp"
|
||||
#include <Helpers/Serializers.hpp>
|
||||
#include <cereal/types/vector.hpp>
|
||||
#include <limits>
|
||||
#include <Eigen/Geometry>
|
||||
#include "Helpers/MathExtras.hpp"
|
||||
#include "../TimePoint.hpp"
|
||||
#include <include/core/SkVertices.h>
|
||||
#include <include/pathops/SkPathOps.h>
|
||||
#include "../DrawCollision.hpp"
|
||||
#include "CanvasComponentContainer.hpp"
|
||||
|
||||
#define AABB_PRECHECK_BVH_LEVELS 2
|
||||
#define DEFAULT_SMOOTHNESS 5
|
||||
|
||||
void BrushStrokeCanvasComponent::save(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(d->points, d->color, d->hasRoundCaps);
|
||||
}
|
||||
|
||||
void BrushStrokeCanvasComponent::load(cereal::PortableBinaryInputArchive& a) {
|
||||
a(d->points, d->color, d->hasRoundCaps);
|
||||
}
|
||||
|
||||
void BrushStrokeCanvasComponent::save_file(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(d->points, d->color, d->hasRoundCaps);
|
||||
}
|
||||
|
||||
void BrushStrokeCanvasComponent::load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) {
|
||||
a(d->points, d->color, d->hasRoundCaps);
|
||||
}
|
||||
|
||||
CanvasComponent::CompType BrushStrokeCanvasComponent::get_type() const {
|
||||
return CompType::BRUSHSTROKE;
|
||||
}
|
||||
|
||||
void BrushStrokeCanvasComponent::draw(SkCanvas* canvas, const DrawData& drawData) const {
|
||||
SkPaint paint;
|
||||
paint.setColor4f(SkColor4f{d->color.x(), d->color.y(), d->color.z(), d->color.w()});
|
||||
|
||||
unsigned mipmapLevel = compContainer ? compContainer->get_mipmap_level(drawData) : 0;
|
||||
|
||||
if(mipmapLevel == 0 || drawData.dontUseDrawProgCache) // check dontUseDrawProgCache, to make sure that SVG screenshots dont use LOD
|
||||
canvas->drawPath(*brushPath, paint);
|
||||
else
|
||||
canvas->drawPath(*brushPathLOD[mipmapLevel - 1], paint);
|
||||
canvas->restore();
|
||||
}
|
||||
|
||||
void BrushStrokeCanvasComponent::initialize_draw_data(DrawingProgram& drawP) {
|
||||
if(!d->points.empty()) {
|
||||
std::vector<BrushStrokeCanvasComponentPoint> 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> BrushStrokeCanvasComponent::get_wedge_indices(const std::vector<BrushStrokeCanvasComponentPoint>& points) const {
|
||||
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<BrushStrokeCanvasComponentPoint> BrushStrokeCanvasComponent::smooth_points(size_t beginIndex, size_t endIndex, unsigned numOfDivisions) const {
|
||||
std::vector<BrushStrokeCanvasComponentPoint>& points = d->points;
|
||||
|
||||
size_t pointsSize = endIndex - beginIndex + 1;
|
||||
if(pointsSize < 2)
|
||||
return {points[beginIndex], points[endIndex]};
|
||||
|
||||
std::vector<BrushStrokeCanvasComponentPoint> toRet;
|
||||
|
||||
BrushStrokeCanvasComponentPoint p0 = points[beginIndex];
|
||||
p0.pos = p0.pos + p0.width * (p0.pos - points[beginIndex + 1].pos).normalized() * DRAW_MINIMUM_LIMIT;
|
||||
BrushStrokeCanvasComponentPoint p1 = points[beginIndex];
|
||||
BrushStrokeCanvasComponentPoint p2 = points[beginIndex + 1];
|
||||
BrushStrokeCanvasComponentPoint 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);
|
||||
BrushStrokeCanvasComponentPoint 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<BrushStrokeCanvasComponentPoint> BrushStrokeCanvasComponent::smooth_points_avg(size_t beginIndex, size_t endIndex, unsigned numOfDivisions) const {
|
||||
std::vector<BrushStrokeCanvasComponentPoint>& points = d->points;
|
||||
|
||||
size_t pointsSize = endIndex - beginIndex + 1;
|
||||
if(pointsSize < 2)
|
||||
return {points[beginIndex], points[endIndex]};
|
||||
|
||||
std::vector<BrushStrokeCanvasComponentPoint> toRet;
|
||||
|
||||
for(size_t i = beginIndex; i < endIndex; i++) {
|
||||
const BrushStrokeCanvasComponentPoint& p1 = points[i];
|
||||
const BrushStrokeCanvasComponentPoint& 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);
|
||||
BrushStrokeCanvasComponentPoint 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<BrushStrokeCanvasComponentPoint> BrushStrokeCanvasComponent::every_nth_point_include_front_and_back(const std::vector<BrushStrokeCanvasComponentPoint>& pts, size_t n) const {
|
||||
if(pts.size() <= 3 || n == 0)
|
||||
return pts;
|
||||
std::vector<BrushStrokeCanvasComponentPoint> 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 BrushStrokeCanvasComponent::create_triangles(const std::function<bool(Vector2f, Vector2f, Vector2f)>& passTriangleFunc, const std::vector<BrushStrokeCanvasComponentPoint>& smoothedPoints, size_t skipVertexCount, std::shared_ptr<SkPathBuilder> bPathBuilder) const {
|
||||
const int ARC_SMOOTHNESS = 10;
|
||||
const int CIRCLE_SMOOTHNESS = 20;
|
||||
const std::vector<BrushStrokeCanvasComponentPoint>& 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 BrushStrokeCanvasComponent::create_collider() {
|
||||
using namespace SCollision;
|
||||
if(d->points.empty())
|
||||
return;
|
||||
ColliderCollection<float> strokeObjects;
|
||||
std::vector<BrushStrokeCanvasComponentPoint> 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 BrushStrokeCanvasComponent::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 BrushStrokeCanvasComponent::update(DrawingProgram& drawP) {
|
||||
}
|
||||
|
||||
bool BrushStrokeCanvasComponent::collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst) const {
|
||||
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<BrushStrokeCanvasComponentPoint> 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> BrushStrokeCanvasComponent::get_obj_coord_bounds() const {
|
||||
return bounds;
|
||||
}
|
||||
|
|
@ -5,42 +5,37 @@
|
|||
#include <include/core/SkPath.h>
|
||||
#include <include/core/SkPathBuilder.h>
|
||||
|
||||
struct DrawBrushStrokePoint {
|
||||
struct BrushStrokeCanvasComponentPoint {
|
||||
Vector2f pos;
|
||||
float width;
|
||||
|
||||
template <typename Archive> void serialize(Archive& a) {
|
||||
a(pos, width);
|
||||
}
|
||||
};
|
||||
|
||||
class CanvasComponentBrushStroke : public CanvasComponent {
|
||||
class BrushStrokeCanvasComponent : public CanvasComponent {
|
||||
public:
|
||||
constexpr static float DRAW_MINIMUM_LIMIT = 0.3;
|
||||
|
||||
virtual DrawComponentType get_type() const override;
|
||||
virtual void write_update(cereal::PortableBinaryOutputArchive& a) const override;
|
||||
virtual void read_update(cereal::PortableBinaryInputArchive& a) override;
|
||||
virtual CompType 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;
|
||||
std::vector<BrushStrokeCanvasComponentPoint> 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;
|
||||
private:
|
||||
virtual void draw(SkCanvas* canvas, const DrawData& drawData) const 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 bool collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst) const 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;
|
||||
|
||||
|
|
@ -48,15 +43,14 @@ class CanvasComponentBrushStroke : public CanvasComponent {
|
|||
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);
|
||||
|
||||
std::vector<BrushStrokeCanvasComponentPoint> every_nth_point_include_front_and_back(const std::vector<BrushStrokeCanvasComponentPoint>& pts, size_t n) const;
|
||||
|
||||
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
|
||||
void create_triangles(const std::function<bool(Vector2f, Vector2f, Vector2f)>& passTriangleFunc, const std::vector<BrushStrokeCanvasComponentPoint>& smoothedPoints, size_t skipVertexCount, std::shared_ptr<SkPathBuilder> bPath) const;
|
||||
std::vector<size_t> get_wedge_indices(const std::vector<BrushStrokeCanvasComponentPoint>& points) const;
|
||||
std::vector<BrushStrokeCanvasComponentPoint> smooth_points(size_t beginIndex, size_t endIndex, unsigned numOfDivisions) const;
|
||||
std::vector<BrushStrokeCanvasComponentPoint> smooth_points_avg(size_t beginIndex, size_t endIndex, unsigned numOfDivisions) const;
|
||||
};
|
||||
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
#include "CanvasComponent.hpp"
|
||||
#include "BrushStrokeCanvasComponent.hpp"
|
||||
#include "ImageCanvasComponent.hpp"
|
||||
#include "EllipseCanvasComponent.hpp"
|
||||
#include "RectangleCanvasComponent.hpp"
|
||||
#include "TextBoxCanvasComponent.hpp"
|
||||
|
||||
void CanvasComponent::update(DrawingProgram& drawP) {
|
||||
}
|
||||
|
||||
CanvasComponent* CanvasComponent::allocate_comp(CanvasComponent::CompType type) {
|
||||
switch(type) {
|
||||
case CanvasComponent::CompType::BRUSHSTROKE:
|
||||
return new BrushStrokeCanvasComponent;
|
||||
case CanvasComponent::CompType::RECTANGLE:
|
||||
return new RectangleCanvasComponent;
|
||||
case CanvasComponent::CompType::ELLIPSE:
|
||||
return new EllipseCanvasComponent;
|
||||
case CanvasComponent::CompType::TEXTBOX:
|
||||
return new TextBoxCanvasComponent;
|
||||
case CanvasComponent::CompType::IMAGE:
|
||||
return new ImageCanvasComponent;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CanvasComponent::~CanvasComponent() {}
|
||||
|
|
@ -2,6 +2,12 @@
|
|||
#include <cstdint>
|
||||
#include <cereal/archives/portable_binary.hpp>
|
||||
#include <Helpers/VersionNumber.hpp>
|
||||
#include <include/core/SkCanvas.h>
|
||||
#include <Helpers/SCollision.hpp>
|
||||
|
||||
class DrawingProgram;
|
||||
class DrawData;
|
||||
class CanvasComponentContainer;
|
||||
|
||||
class CanvasComponent {
|
||||
public:
|
||||
|
|
@ -12,9 +18,23 @@ class CanvasComponent {
|
|||
TEXTBOX,
|
||||
IMAGE
|
||||
};
|
||||
virtual CompType get_type() const = 0;
|
||||
virtual ~CanvasComponent();
|
||||
static CanvasComponent* allocate_comp(CanvasComponent::CompType 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;
|
||||
|
||||
protected:
|
||||
virtual void write_update() const;
|
||||
virtual void read_update();
|
||||
virtual void save_file(cereal::PortableBinaryOutputArchive& a) const;
|
||||
virtual void load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version);
|
||||
friend class CanvasComponentContainer;
|
||||
|
||||
virtual void draw(SkCanvas* canvas, const DrawData& drawData) const = 0;
|
||||
virtual void initialize_draw_data(DrawingProgram& drawP) = 0;
|
||||
virtual void update(DrawingProgram& drawP);
|
||||
virtual bool collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst) const = 0;
|
||||
|
||||
virtual SCollision::AABB<float> get_obj_coord_bounds() const = 0;
|
||||
|
||||
CanvasComponentContainer* compContainer = nullptr;
|
||||
};
|
||||
|
|
|
|||
30
src/CanvasComponents/CanvasComponentAllocator.cpp
Normal file
30
src/CanvasComponents/CanvasComponentAllocator.cpp
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#include "CanvasComponentAllocator.hpp"
|
||||
|
||||
CanvasComponentAllocator::CanvasComponentAllocator() {}
|
||||
|
||||
CanvasComponentAllocator::CanvasComponentAllocator(CanvasComponent::CompType typeToAllocate) {
|
||||
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::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);
|
||||
}
|
||||
15
src/CanvasComponents/CanvasComponentAllocator.hpp
Normal file
15
src/CanvasComponents/CanvasComponentAllocator.hpp
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
#include <memory>
|
||||
#include "CanvasComponent.hpp"
|
||||
#include <Helpers/NetworkingObjects/NetObjTemporaryPtr.hpp>
|
||||
|
||||
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);
|
||||
};
|
||||
|
|
@ -1,41 +1,143 @@
|
|||
#include "CanvasComponentContainer.hpp"
|
||||
#include "CanvasComponentAllocator.hpp"
|
||||
#include "../DrawData.hpp"
|
||||
#include "../DrawingProgram/DrawingProgram.hpp"
|
||||
|
||||
using namespace NetworkingObjects;
|
||||
|
||||
CanvasComponentContainer::CanvasComponentContainer() { }
|
||||
|
||||
CanvasComponentContainer::CanvasComponentContainer(CanvasComponent::CompType type) {
|
||||
allocate_comp(type);
|
||||
CanvasComponentContainer::CanvasComponentContainer(NetworkingObjects::NetObjManager& objMan, CanvasComponent::CompType type) {
|
||||
compAllocator = objMan.make_obj_direct<CanvasComponentAllocator>(type);
|
||||
}
|
||||
|
||||
void CanvasComponentContainer::register_class(NetworkingObjects::NetObjManagerTypeList& t) {
|
||||
t.register_class<CanvasComponentContainer, CanvasComponentContainer, CanvasComponentContainer, CanvasComponentContainer>({
|
||||
void CanvasComponentContainer::register_class(NetObjManager& objMan) {
|
||||
objMan.register_class<CanvasComponentContainer, CanvasComponentContainer, CanvasComponentContainer, CanvasComponentContainer>({
|
||||
.writeConstructorFuncClient = write_constructor_func,
|
||||
.readConstructorFuncClient = read_constructor_func,
|
||||
.readUpdateFuncClient = nullptr,
|
||||
.writeConstructorFuncServer = write_constructor_func,
|
||||
.readConstructorFuncServer = read_constructor_func,
|
||||
.readUpdateFuncServer = nullptr,
|
||||
});
|
||||
}
|
||||
|
||||
std::unique_ptr<CanvasComponent> CanvasComponentContainer::allocate_comp(CanvasComponent::CompType type) {
|
||||
switch(type) {
|
||||
case CanvasComponent::CompType::BRUSHSTROKE:
|
||||
return std::make_unique<CanvasComponentBrushStroke>();
|
||||
case CanvasComponent::CompType::RECTANGLE:
|
||||
return std::make_unique<CanvasComponentRectangle>();
|
||||
case CanvasComponent::CompType::ELLIPSE:
|
||||
return std::make_unique<CanvasComponentEllipse>();
|
||||
case CanvasComponent::CompType::TEXTBOX:
|
||||
return std::make_unique<CanvasComponentTextBox>();
|
||||
case CanvasComponent::CompType::IMAGE:
|
||||
return std::make_unique<CanvasComponentImage>();
|
||||
}
|
||||
void CanvasComponentContainer::write_constructor_func(const NetworkingObjects::NetObjTemporaryPtr<CanvasComponentContainer>& o, cereal::PortableBinaryOutputArchive& a) {
|
||||
a(o->coords);
|
||||
o->compAllocator.write_create_message(a);
|
||||
}
|
||||
|
||||
void CanvasComponentContainer::read_constructor_func(const NetworkingObjects::NetObjTemporaryPtr<CanvasComponentContainer>& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>& c) {
|
||||
a(o->coords);
|
||||
o->compAllocator = o.get_obj_man()->read_create_message<CanvasComponentAllocator>(a, c);
|
||||
}
|
||||
|
||||
void CanvasComponentContainer::delay_post_assignment_update(DrawingProgram& drawP, CanvasComponent& comp) {
|
||||
comp.compContainer->commit_update(drawP);
|
||||
}
|
||||
|
||||
void CanvasComponentContainer::save_file(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(coords);
|
||||
get_comp().save_file(a);
|
||||
}
|
||||
|
||||
void CanvasComponentContainer::load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) {
|
||||
a(coords);
|
||||
get_comp().load_file(a, version);
|
||||
}
|
||||
|
||||
const std::unique_ptr<CanvasComponent>& CanvasComponentContainer::get_comp() {
|
||||
return comp;
|
||||
void CanvasComponentContainer::draw(SkCanvas* canvas, const DrawData& drawData) const {
|
||||
canvas->save();
|
||||
canvas_do_transform(canvas, calculate_draw_transform(drawData));
|
||||
get_comp().draw(canvas, drawData);
|
||||
canvas->restore();
|
||||
}
|
||||
|
||||
const CoordSpaceHelper& CanvasComponentContainer::get_coords() {
|
||||
void CanvasComponentContainer::commit_update(DrawingProgram& drawP) {
|
||||
//if(worldAABB.has_value()) // This is the only point where worldAABB could be nullopt
|
||||
// drawP.preupdate_component(objInfo);
|
||||
get_comp().initialize_draw_data(drawP);
|
||||
calculate_world_bounds();
|
||||
//drawP.preupdate_component(objInfo);
|
||||
}
|
||||
|
||||
void CanvasComponentContainer::commit_transform(DrawingProgram& drawP) {
|
||||
//if(worldAABB.has_value()) // This is the only point where worldAABB could be nullopt
|
||||
// drawP.preupdate_component(objInfo);
|
||||
calculate_world_bounds();
|
||||
//drawP.preupdate_component(objInfo);
|
||||
}
|
||||
|
||||
void CanvasComponentContainer::commit_update_dont_invalidate_cache(DrawingProgram& drawP) {
|
||||
get_comp().initialize_draw_data(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));
|
||||
}
|
||||
|
||||
bool CanvasComponentContainer::collides_with_cam_coords(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection<float>& checkAgainstCam) const {
|
||||
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 CanvasComponentContainer::collides_with(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection<WorldScalar>& checkAgainstWorld, const SCollision::ColliderCollection<float>& checkAgainstCam) const {
|
||||
if((camCoords.inverseScale << COMP_MAX_SHIFT_BEFORE_STOP_COLLISIONS) < coords.inverseScale) // Object is too large, just dismiss the collision
|
||||
return false;
|
||||
else if((camCoords.inverseScale >> COMP_COLLIDE_MIN_SHIFT_TINY) >= coords.inverseScale)
|
||||
return SCollision::collide(checkAgainstCam, camCoords.to_space(worldAABB.value().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 get_comp().collides_within_coords(c);
|
||||
}
|
||||
}
|
||||
|
||||
void CanvasComponentContainer::canvas_do_transform(SkCanvas* canvas, const TransformDrawData& transformData) const {
|
||||
canvas->scale(transformData.scale, transformData.scale);
|
||||
canvas->rotate(transformData.rotation);
|
||||
canvas->translate(transformData.translation.x(), transformData.translation.y());
|
||||
}
|
||||
|
||||
bool CanvasComponentContainer::should_draw(const DrawData& drawData) const {
|
||||
return (!drawData.clampDrawBetween || (drawData.clampDrawMinimum < coords.inverseScale)) && SCollision::collide(worldAABB.value(), drawData.cam.viewingAreaGenerousCollider);
|
||||
}
|
||||
|
||||
CanvasComponentContainer::TransformDrawData CanvasComponentContainer::calculate_draw_transform(const DrawData& drawData) const {
|
||||
TransformDrawData toRet;
|
||||
toRet.translation = -coords.to_space(drawData.cam.c.pos);
|
||||
toRet.rotation = (coords.rotation - drawData.cam.c.rotation) * 180.0 / std::numbers::pi;
|
||||
toRet.scale = std::min(static_cast<float>(coords.inverseScale / drawData.cam.c.inverseScale), static_cast<float>(1 << COMP_MAX_SHIFT_BEFORE_STOP_SCALING));
|
||||
return toRet;
|
||||
}
|
||||
|
||||
unsigned CanvasComponentContainer::get_mipmap_level(const DrawData& drawData) const {
|
||||
if(drawData.mipMapLevelTwo > coords.inverseScale)
|
||||
return 2;
|
||||
else if(drawData.mipMapLevelOne > coords.inverseScale)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
CanvasComponent& CanvasComponentContainer::get_comp() const {
|
||||
return *compAllocator->comp;
|
||||
}
|
||||
|
||||
const CoordSpaceHelper& CanvasComponentContainer::get_coords() const {
|
||||
return coords;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,18 +3,64 @@
|
|||
#include "CanvasComponent.hpp"
|
||||
#include <Helpers/NetworkingObjects/NetObjManagerTypeList.hpp>
|
||||
#include "../VersionConstants.hpp"
|
||||
#include "Helpers/NetworkingObjects/NetObjOrderedList.hpp"
|
||||
#include <Helpers/NetworkingObjects/NetObjOwnerPtr.hpp>
|
||||
|
||||
class DrawingProgram;
|
||||
class CanvasComponentAllocator;
|
||||
|
||||
class CanvasComponentContainer {
|
||||
public:
|
||||
typedef NetworkingObjects::NetObjOrderedListObjectInfo<CanvasComponentContainer> ObjInfo;
|
||||
typedef std::shared_ptr<ObjInfo> ObjInfoSharedPtr;
|
||||
typedef std::weak_ptr<ObjInfo> ObjInfoWeakPtr;
|
||||
|
||||
constexpr static int COMP_MAX_SHIFT_BEFORE_STOP_COLLISIONS = 14;
|
||||
constexpr static int COMP_MAX_SHIFT_BEFORE_STOP_SCALING = 14;
|
||||
constexpr static int COMP_MIN_SHIFT_BEFORE_DISAPPEAR = 11;
|
||||
constexpr static int COMP_COLLIDE_MIN_SHIFT_TINY = 9;
|
||||
constexpr static int COMP_MIPMAP_LEVEL_ONE = 2;
|
||||
constexpr static int COMP_MIPMAP_LEVEL_TWO = 5;
|
||||
|
||||
CanvasComponentContainer();
|
||||
CanvasComponentContainer(CanvasComponent::CompType type);
|
||||
static void register_class(NetworkingObjects::NetObjManagerTypeList& t);
|
||||
CanvasComponentContainer(NetworkingObjects::NetObjManager& objMan, CanvasComponent::CompType type);
|
||||
static void register_class(NetworkingObjects::NetObjManager& t);
|
||||
static void delay_post_assignment_update(DrawingProgram& drawP, CanvasComponent& comp);
|
||||
void save_file(cereal::PortableBinaryOutputArchive& a) const;
|
||||
void load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version);
|
||||
const std::unique_ptr<CanvasComponent>& get_comp();
|
||||
const CoordSpaceHelper& get_coords();
|
||||
CanvasComponent& get_comp() const;
|
||||
const CoordSpaceHelper& get_coords() const;
|
||||
std::optional<SCollision::AABB<WorldScalar>> get_world_bounds() const;
|
||||
void draw(SkCanvas* canvas, const DrawData& drawData) const;
|
||||
void commit_update(DrawingProgram& drawP);
|
||||
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;
|
||||
private:
|
||||
std::unique_ptr<CanvasComponent> allocate_comp(CanvasComponent::CompType type);
|
||||
std::unique_ptr<CanvasComponent> comp;
|
||||
friend class BrushStrokeCanvasComponent;
|
||||
friend class ImageCanvasComponent;
|
||||
|
||||
static void write_constructor_func(const NetworkingObjects::NetObjTemporaryPtr<CanvasComponentContainer>& o, cereal::PortableBinaryOutputArchive& a);
|
||||
static void read_constructor_func(const NetworkingObjects::NetObjTemporaryPtr<CanvasComponentContainer>& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>& c);
|
||||
|
||||
struct TransformDrawData {
|
||||
Vector2f translation;
|
||||
float rotation;
|
||||
float scale;
|
||||
};
|
||||
|
||||
unsigned get_mipmap_level(const DrawData& drawData) const;
|
||||
CanvasComponent* allocate_comp(CanvasComponent::CompType type);
|
||||
void canvas_do_transform(SkCanvas* canvas, const TransformDrawData& transformData) const;
|
||||
TransformDrawData calculate_draw_transform(const DrawData& drawData) const;
|
||||
void calculate_world_bounds();
|
||||
|
||||
ObjInfo* objInfo = nullptr;
|
||||
|
||||
std::optional<SCollision::AABB<WorldScalar>> worldAABB;
|
||||
NetworkingObjects::NetObjOwnerPtr<CanvasComponentAllocator> compAllocator;
|
||||
CoordSpaceHelper coords;
|
||||
};
|
||||
|
|
|
|||
121
src/CanvasComponents/EllipseCanvasComponent.cpp
Normal file
121
src/CanvasComponents/EllipseCanvasComponent.cpp
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
#include "EllipseCanvasComponent.hpp"
|
||||
#include "Helpers/ConvertVec.hpp"
|
||||
#include "Helpers/SCollision.hpp"
|
||||
#include <include/core/SkPathBuilder.h>
|
||||
|
||||
#ifndef IS_SERVER
|
||||
#include "../DrawCollision.hpp"
|
||||
#endif
|
||||
|
||||
CanvasComponent::CompType EllipseCanvasComponent::get_type() const {
|
||||
return CompType::ELLIPSE;
|
||||
}
|
||||
|
||||
void EllipseCanvasComponent::save(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(d.strokeColor, d.fillColor, d.strokeWidth, d.p1, d.p2, d.fillStrokeMode);
|
||||
}
|
||||
|
||||
void EllipseCanvasComponent::load(cereal::PortableBinaryInputArchive& a) {
|
||||
a(d.strokeColor, d.fillColor, d.strokeWidth, d.p1, d.p2, d.fillStrokeMode);
|
||||
}
|
||||
|
||||
void EllipseCanvasComponent::save_file(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(d.strokeColor, d.fillColor, d.strokeWidth, d.p1, d.p2, d.fillStrokeMode);
|
||||
}
|
||||
|
||||
void EllipseCanvasComponent::load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) {
|
||||
a(d.strokeColor, d.fillColor, d.strokeWidth, d.p1, d.p2, d.fillStrokeMode);
|
||||
}
|
||||
|
||||
void EllipseCanvasComponent::draw(SkCanvas* canvas, const DrawData& drawData) const {
|
||||
SkPaint p;
|
||||
if(d.fillStrokeMode == 0 || d.fillStrokeMode == 2) {
|
||||
p.setStyle(SkPaint::kFill_Style);
|
||||
p.setColor4f(convert_vec4<SkColor4f>(d.fillColor));
|
||||
canvas->drawPath(ellipsePath, p);
|
||||
}
|
||||
if(d.fillStrokeMode == 1 || d.fillStrokeMode == 2) {
|
||||
p.setStyle(SkPaint::kStroke_Style);
|
||||
p.setColor4f(convert_vec4<SkColor4f>(d.strokeColor));
|
||||
p.setStrokeWidth(d.strokeWidth);
|
||||
canvas->drawPath(ellipsePath, p);
|
||||
}
|
||||
}
|
||||
|
||||
void EllipseCanvasComponent::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 EllipseCanvasComponent::initialize_draw_data(DrawingProgram& drawP) {
|
||||
create_draw_data();
|
||||
create_collider();
|
||||
}
|
||||
|
||||
bool EllipseCanvasComponent::collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst) const {
|
||||
return collisionTree.is_collide(checkAgainst);
|
||||
}
|
||||
|
||||
void EllipseCanvasComponent::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> EllipseCanvasComponent::get_obj_coord_bounds() const {
|
||||
return collisionTree.objects.bounds;
|
||||
}
|
||||
35
src/CanvasComponents/EllipseCanvasComponent.hpp
Normal file
35
src/CanvasComponents/EllipseCanvasComponent.hpp
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
#include "CanvasComponent.hpp"
|
||||
#include "../CoordSpaceHelper.hpp"
|
||||
#include <include/core/SkPath.h>
|
||||
|
||||
class EllipseCanvasComponent : public CanvasComponent {
|
||||
public:
|
||||
virtual CompType 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 {
|
||||
Vector4f strokeColor;
|
||||
Vector4f fillColor;
|
||||
float strokeWidth;
|
||||
Vector2f p1;
|
||||
Vector2f p2;
|
||||
uint8_t fillStrokeMode;
|
||||
bool operator==(const Data& o) const = default;
|
||||
} d;
|
||||
|
||||
private:
|
||||
void create_draw_data();
|
||||
void create_collider();
|
||||
virtual void draw(SkCanvas* canvas, const DrawData& drawData) const override;
|
||||
virtual void initialize_draw_data(DrawingProgram& drawP) override;
|
||||
virtual bool collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst) const override;
|
||||
virtual SCollision::AABB<float> get_obj_coord_bounds() const override;
|
||||
void create_triangles(const std::function<bool(Vector2f, Vector2f, Vector2f)>& passTriangleFunc);
|
||||
SCollision::BVHContainer<float> collisionTree;
|
||||
SkPath ellipsePath;
|
||||
};
|
||||
92
src/CanvasComponents/ImageCanvasComponent.cpp
Normal file
92
src/CanvasComponents/ImageCanvasComponent.cpp
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
#include "ImageCanvasComponent.hpp"
|
||||
#include "Helpers/ConvertVec.hpp"
|
||||
#include "Helpers/MathExtras.hpp"
|
||||
#include "Helpers/SCollision.hpp"
|
||||
#include "../SharedTypes.hpp"
|
||||
#include "../World.hpp"
|
||||
#include "../MainProgram.hpp"
|
||||
#include <include/core/SkSamplingOptions.h>
|
||||
#include "../DrawCollision.hpp"
|
||||
#include "CanvasComponentContainer.hpp"
|
||||
|
||||
CanvasComponent::CompType ImageCanvasComponent::get_type() const {
|
||||
return CompType::IMAGE;
|
||||
}
|
||||
|
||||
void ImageCanvasComponent::save(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(d.p1, d.p2, d.imageID);
|
||||
}
|
||||
|
||||
void ImageCanvasComponent::load(cereal::PortableBinaryInputArchive& a) {
|
||||
a(d.p1, d.p2, d.imageID);
|
||||
}
|
||||
|
||||
void ImageCanvasComponent::save_file(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(d.p1, d.p2, d.imageID);
|
||||
}
|
||||
|
||||
void ImageCanvasComponent::load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) {
|
||||
a(d.p1, d.p2, d.imageID);
|
||||
}
|
||||
|
||||
void ImageCanvasComponent::draw_download_progress_bar(SkCanvas* canvas, const DrawData& drawData, float progress) const {
|
||||
if(progress == 0.0f)
|
||||
return;
|
||||
if(compContainer->should_draw(drawData)) {
|
||||
canvas->save();
|
||||
compContainer->canvas_do_transform(canvas, compContainer->calculate_draw_transform(drawData));
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
void ImageCanvasComponent::draw(SkCanvas* canvas, const DrawData& drawData) const {
|
||||
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}));
|
||||
}
|
||||
|
||||
void ImageCanvasComponent::update(DrawingProgram& drawP) {
|
||||
ResourceDisplay* display = drawP.world.drawData.rMan->get_display_data(d.imageID);
|
||||
if(display) {
|
||||
//if(display->update_draw())
|
||||
// drawP.invalidate_cache_at_component(compContainer->objInfo);
|
||||
}
|
||||
}
|
||||
|
||||
void ImageCanvasComponent::initialize_draw_data(DrawingProgram& drawP) {
|
||||
create_draw_data();
|
||||
create_collider();
|
||||
}
|
||||
|
||||
bool ImageCanvasComponent::collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst) const {
|
||||
return collisionTree.is_collide(checkAgainst);
|
||||
}
|
||||
|
||||
void ImageCanvasComponent::create_draw_data() {
|
||||
imRect = SkRect::MakeLTRB(d.p1.x(), d.p1.y(), d.p2.x(), d.p2.y());
|
||||
}
|
||||
|
||||
void ImageCanvasComponent::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> ImageCanvasComponent::get_obj_coord_bounds() const {
|
||||
return collisionTree.objects.bounds;
|
||||
}
|
||||
39
src/CanvasComponents/ImageCanvasComponent.hpp
Normal file
39
src/CanvasComponents/ImageCanvasComponent.hpp
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
#include "../SharedTypes.hpp"
|
||||
#include "CanvasComponent.hpp"
|
||||
#include "../CoordSpaceHelper.hpp"
|
||||
#include <include/core/SkPath.h>
|
||||
|
||||
class ImageCanvasComponent : public CanvasComponent {
|
||||
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 CompType get_type() const override;
|
||||
|
||||
void draw_download_progress_bar(SkCanvas* canvas, const DrawData& drawData, float progress) const;
|
||||
|
||||
// User input data
|
||||
struct Data {
|
||||
Vector2f p1;
|
||||
Vector2f p2;
|
||||
|
||||
ServerClientID imageID;
|
||||
} d;
|
||||
|
||||
private:
|
||||
virtual void draw(SkCanvas* canvas, const DrawData& drawData) const 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) const override;
|
||||
void create_draw_data();
|
||||
void create_collider();
|
||||
|
||||
virtual SCollision::AABB<float> get_obj_coord_bounds() const override;
|
||||
|
||||
SkRect imRect;
|
||||
|
||||
SCollision::BVHContainer<float> collisionTree;
|
||||
};
|
||||
|
||||
103
src/CanvasComponents/RectangleCanvasComponent.cpp
Normal file
103
src/CanvasComponents/RectangleCanvasComponent.cpp
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
#include "RectangleCanvasComponent.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"
|
||||
#include "../DrawCollision.hpp"
|
||||
|
||||
CanvasComponent::CompType RectangleCanvasComponent::get_type() const {
|
||||
return CompType::RECTANGLE;
|
||||
}
|
||||
|
||||
void RectangleCanvasComponent::save(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(d.strokeColor, d.fillColor, d.cornerRadius, d.strokeWidth, d.p1, d.p2, d.fillStrokeMode);
|
||||
}
|
||||
|
||||
void RectangleCanvasComponent::load(cereal::PortableBinaryInputArchive& a) {
|
||||
a(d.strokeColor, d.fillColor, d.cornerRadius, d.strokeWidth, d.p1, d.p2, d.fillStrokeMode);
|
||||
}
|
||||
|
||||
void RectangleCanvasComponent::save_file(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(d.strokeColor, d.fillColor, d.cornerRadius, d.strokeWidth, d.p1, d.p2, d.fillStrokeMode);
|
||||
}
|
||||
|
||||
void RectangleCanvasComponent::load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) {
|
||||
a(d.strokeColor, d.fillColor, d.cornerRadius, d.strokeWidth, d.p1, d.p2, d.fillStrokeMode);
|
||||
}
|
||||
|
||||
void RectangleCanvasComponent::draw(SkCanvas* canvas, const DrawData& drawData) const {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void RectangleCanvasComponent::initialize_draw_data(DrawingProgram& drawP) {
|
||||
create_draw_data();
|
||||
create_collider();
|
||||
}
|
||||
|
||||
bool RectangleCanvasComponent::collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst) const {
|
||||
return collisionTree.is_collide(checkAgainst);
|
||||
}
|
||||
|
||||
void RectangleCanvasComponent::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 RectangleCanvasComponent::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> RectangleCanvasComponent::get_obj_coord_bounds() const {
|
||||
return collisionTree.objects.bounds;
|
||||
}
|
||||
39
src/CanvasComponents/RectangleCanvasComponent.hpp
Normal file
39
src/CanvasComponents/RectangleCanvasComponent.hpp
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
#include "../SharedTypes.hpp"
|
||||
#include "CanvasComponent.hpp"
|
||||
#include "../CoordSpaceHelper.hpp"
|
||||
#include <include/core/SkPath.h>
|
||||
|
||||
class RectangleCanvasComponent : public CanvasComponent {
|
||||
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 CompType 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;
|
||||
|
||||
private:
|
||||
virtual void draw(SkCanvas* canvas, const DrawData& drawData) const override;
|
||||
virtual void initialize_draw_data(DrawingProgram& drawP) override;
|
||||
virtual bool collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst) const override;
|
||||
void create_draw_data();
|
||||
void create_collider();
|
||||
virtual SCollision::AABB<float> get_obj_coord_bounds() const override;
|
||||
|
||||
SkPath rectPath;
|
||||
|
||||
SCollision::BVHContainer<float> collisionTree;
|
||||
};
|
||||
|
||||
111
src/CanvasComponents/TextBoxCanvasComponent.cpp
Normal file
111
src/CanvasComponents/TextBoxCanvasComponent.cpp
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
#include "TextBoxCanvasComponent.hpp"
|
||||
#include "Helpers/ConvertVec.hpp"
|
||||
#include <cereal/types/string.hpp>
|
||||
#include "../MainProgram.hpp"
|
||||
#include "Helpers/SCollision.hpp"
|
||||
#include "../DrawingProgram/DrawingProgram.hpp"
|
||||
#include "CanvasComponentContainer.hpp"
|
||||
#include "../DrawCollision.hpp"
|
||||
|
||||
using namespace RichText;
|
||||
|
||||
TextBoxCanvasComponent::TextBoxCanvasComponent():
|
||||
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"}}));
|
||||
}
|
||||
|
||||
CanvasComponent::CompType TextBoxCanvasComponent::get_type() const {
|
||||
return CompType::TEXTBOX;
|
||||
}
|
||||
|
||||
void TextBoxCanvasComponent::save(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(d.editing, d.p1, d.p2, textBox->get_rich_text_data(), *cursor);
|
||||
}
|
||||
|
||||
void TextBoxCanvasComponent::load(cereal::PortableBinaryInputArchive& a) {
|
||||
TextData richText;
|
||||
a(d.editing, d.p1, d.p2, richText, *cursor);
|
||||
textBox->set_rich_text_data(richText);
|
||||
}
|
||||
|
||||
void TextBoxCanvasComponent::save_file(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(d.p1, d.p2, textBox->get_rich_text_data());
|
||||
}
|
||||
|
||||
void TextBoxCanvasComponent::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);
|
||||
}
|
||||
}
|
||||
|
||||
void TextBoxCanvasComponent::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 TextBoxCanvasComponent::draw(SkCanvas* canvas, const DrawData& drawData) const {
|
||||
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);
|
||||
}
|
||||
|
||||
Vector2f TextBoxCanvasComponent::get_mouse_pos(DrawingProgram& drawP) const {
|
||||
return compContainer->get_coords().get_mouse_pos(drawP.world) - d.p1 - Vector2f{TEXTBOX_PADDING, TEXTBOX_PADDING};
|
||||
}
|
||||
|
||||
void TextBoxCanvasComponent::initialize_draw_data(DrawingProgram& drawP) {
|
||||
init_text_box(drawP);
|
||||
create_collider();
|
||||
}
|
||||
|
||||
bool TextBoxCanvasComponent::collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst) const {
|
||||
return collisionTree.is_collide(checkAgainst);
|
||||
}
|
||||
|
||||
void TextBoxCanvasComponent::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> TextBoxCanvasComponent::get_obj_coord_bounds() const {
|
||||
return collisionTree.objects.bounds;
|
||||
}
|
||||
41
src/CanvasComponents/TextBoxCanvasComponent.hpp
Normal file
41
src/CanvasComponents/TextBoxCanvasComponent.hpp
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#pragma once
|
||||
#include "../SharedTypes.hpp"
|
||||
#include "CanvasComponent.hpp"
|
||||
#include "../CoordSpaceHelper.hpp"
|
||||
#include "../RichText/TextBox.hpp"
|
||||
#include <include/core/SkPath.h>
|
||||
|
||||
class TextBoxCanvasComponent : public CanvasComponent {
|
||||
public:
|
||||
constexpr static float TEXTBOX_PADDING = 5.0f;
|
||||
|
||||
TextBoxCanvasComponent();
|
||||
virtual CompType 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;
|
||||
|
||||
private:
|
||||
virtual void draw(SkCanvas* canvas, const DrawData& drawData) const override;
|
||||
virtual void initialize_draw_data(DrawingProgram& drawP) override;
|
||||
virtual bool collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst) const override;
|
||||
void create_collider();
|
||||
void init_text_box(DrawingProgram& drawP);
|
||||
Vector2f get_mouse_pos(DrawingProgram& drawP) const;
|
||||
|
||||
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;
|
||||
};
|
||||
|
|
@ -141,7 +141,7 @@ void GridModifyTool::tool_update() {
|
|||
selectionMode = 0;
|
||||
break;
|
||||
}
|
||||
drawP.world.delayedUpdateObjectManager.send_update_to_all<WorldGrid>(gLock, false);
|
||||
drawP.world.delayedUpdateObjectManager.send_update_to_all<WorldGrid>(gLock, nullptr, 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, true);
|
||||
drawP.world.delayedUpdateObjectManager.send_update_to_all<WorldGrid>(gLock, nullptr, true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,8 +33,9 @@ void GridManager::remove_grid(uint32_t indexToRemove) {
|
|||
void GridManager::draw_back(SkCanvas* canvas, const DrawData& drawData) {
|
||||
if(grids) {
|
||||
for(uint32_t i = 0; i < grids->size(); i++) {
|
||||
if(!grids->at(i)->displayInFront)
|
||||
grids->at(i)->draw(*this, canvas, drawData);
|
||||
auto& g = grids->at(i)->obj;
|
||||
if(!g->displayInFront)
|
||||
g->draw(*this, canvas, drawData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -42,8 +43,9 @@ void GridManager::draw_back(SkCanvas* canvas, const DrawData& drawData) {
|
|||
void GridManager::draw_front(SkCanvas* canvas, const DrawData& drawData) {
|
||||
if(grids) {
|
||||
for(uint32_t i = 0; i < grids->size(); i++) {
|
||||
if(grids->at(i)->displayInFront)
|
||||
grids->at(i)->draw(*this, canvas, drawData);
|
||||
auto& g = grids->at(i)->obj;
|
||||
if(g->displayInFront)
|
||||
g->draw(*this, canvas, drawData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -52,14 +54,14 @@ void GridManager::draw_coordinates(SkCanvas* canvas, const DrawData& drawData) {
|
|||
if(grids) {
|
||||
Vector2f axisOffset{0.0f, 0.0f};
|
||||
for(uint32_t i = 0; i < grids->size(); i++) {
|
||||
auto& g = grids->at(i);
|
||||
auto& g = grids->at(i)->obj;
|
||||
if(g->coordinatesWillBeDrawn && g->coordinatesAxisOnBounds) {
|
||||
g->draw_coordinates(canvas, drawData, axisOffset);
|
||||
g->coordinatesWillBeDrawn = false;
|
||||
}
|
||||
}
|
||||
for(uint32_t i = 0; i < grids->size(); i++) {
|
||||
auto& g = grids->at(i);
|
||||
auto& g = grids->at(i)->obj;
|
||||
if(g->coordinatesWillBeDrawn && !g->coordinatesAxisOnBounds)
|
||||
g->draw_coordinates(canvas, drawData, axisOffset);
|
||||
g->coordinatesWillBeDrawn = false;
|
||||
|
|
@ -69,8 +71,7 @@ void GridManager::draw_coordinates(SkCanvas* canvas, const DrawData& drawData) {
|
|||
|
||||
void GridManager::scale_up(const WorldScalar& scaleUpAmount) {
|
||||
if(grids) {
|
||||
for(uint32_t i = 0; i < grids->size(); i++) {
|
||||
grids->at(i)->scale_up(scaleUpAmount);
|
||||
}
|
||||
for(uint32_t i = 0; i < grids->size(); i++)
|
||||
grids->at(i)->obj->scale_up(scaleUpAmount);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -713,7 +713,7 @@ void Toolbar::grid_menu(bool justOpened) {
|
|||
gui.text_label_centered("No grids yet...");
|
||||
uint32_t toDelete = std::numeric_limits<uint32_t>::max();
|
||||
gui.scroll_bar_many_entries_area("grid menu entries", entryHeight, main.world->gridMan.grids->size(), true, [&](size_t i, bool isListHovered) {
|
||||
auto& grid = main.world->gridMan.grids->at(i);
|
||||
auto& grid = main.world->gridMan.grids->at(i)->obj;
|
||||
bool selectedEntry = gridMenu.selectedGrid == i;
|
||||
CLAY_AUTO_ID({
|
||||
.layout = {
|
||||
|
|
@ -767,7 +767,7 @@ void Toolbar::grid_menu(bool justOpened) {
|
|||
});
|
||||
if(gui.svg_icon_button("grid add button", "data/icons/plus.svg", false, GUIStuff::GUIManager::SMALL_BUTTON_SIZE) || (addByEnter && !gridMenu.newName.empty())) {
|
||||
main.world->gridMan.add_default_grid(gridMenu.newName);
|
||||
main.world->drawProg.modify_grid(main.world->gridMan.grids->at(main.world->gridMan.grids->size() - 1));
|
||||
main.world->drawProg.modify_grid(main.world->gridMan.grids->at(main.world->gridMan.grids->size() - 1)->obj);
|
||||
stop_displaying_grid_menu();
|
||||
}
|
||||
});
|
||||
|
|
@ -806,7 +806,7 @@ void Toolbar::bookmark_menu(bool justOpened) {
|
|||
gui.text_label_centered("No bookmarks yet...");
|
||||
uint32_t toDelete = std::numeric_limits<uint32_t>::max();
|
||||
gui.scroll_bar_many_entries_area("bookmark menu entries", entryHeight, main.world->bMan.bookmarks->size(), true, [&](size_t i, bool isListHovered) {
|
||||
const Bookmark& bookmark = *main.world->bMan.bookmarks->at(i);
|
||||
const Bookmark& bookmark = *main.world->bMan.bookmarks->at(i)->obj;
|
||||
bool selectedEntry = i == bookMenu.selectedBookmark;
|
||||
CLAY_AUTO_ID({
|
||||
.layout = {
|
||||
|
|
@ -838,6 +838,7 @@ void Toolbar::bookmark_menu(bool justOpened) {
|
|||
}
|
||||
if(Clay_Hovered() && io->mouse.leftClick && isListHovered && !miniButtonClicked) {
|
||||
bookMenu.newName = bookmark.name;
|
||||
bookMenu.selectedBookmark = i;
|
||||
if(io->mouse.leftClick >= 2)
|
||||
main.world->bMan.jump_to_bookmark(i);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,8 +30,7 @@
|
|||
#endif
|
||||
|
||||
World::World(MainProgram& initMain, OpenWorldInfo& worldInfo):
|
||||
netObjectTypeList(std::make_shared<NetworkingObjects::NetObjManagerTypeList>()),
|
||||
netObjMan(netObjectTypeList, worldInfo.conType != CONNECTIONTYPE_CLIENT),
|
||||
netObjMan(worldInfo.conType != CONNECTIONTYPE_CLIENT),
|
||||
main(initMain),
|
||||
rMan(*this),
|
||||
drawProg(*this),
|
||||
|
|
@ -79,10 +78,10 @@ World::World(MainProgram& initMain, OpenWorldInfo& worldInfo):
|
|||
}
|
||||
|
||||
void World::init_net_obj_type_list() {
|
||||
NetworkingObjects::register_generic_serialized_class<Bookmark>(*netObjectTypeList);
|
||||
NetworkingObjects::register_ordered_list_class<Bookmark>(*netObjectTypeList);
|
||||
delayedUpdateObjectManager.register_class<WorldGrid>(*netObjectTypeList);
|
||||
NetworkingObjects::register_ordered_list_class<WorldGrid>(*netObjectTypeList);
|
||||
NetworkingObjects::register_generic_serialized_class<Bookmark>(netObjMan);
|
||||
NetworkingObjects::register_ordered_list_class<Bookmark>(netObjMan);
|
||||
delayedUpdateObjectManager.register_class<WorldGrid>(netObjMan);
|
||||
NetworkingObjects::register_ordered_list_class<WorldGrid>(netObjMan);
|
||||
}
|
||||
|
||||
void World::init_client_callbacks() {
|
||||
|
|
@ -395,8 +394,8 @@ void World::save_file(cereal::PortableBinaryOutputArchive& a) const {
|
|||
a(drawData.cam.c, main.window.size.cast<float>().eval());
|
||||
a(convert_vec3<Vector3f>(canvasTheme.backColor));
|
||||
drawProg.save_file(a);
|
||||
a(bMan);
|
||||
a(gridMan);
|
||||
//a(bMan);
|
||||
//a(gridMan);
|
||||
rMan.save_strip_unused_resources(a, drawProg.get_used_resources());
|
||||
}
|
||||
|
||||
|
|
@ -419,9 +418,9 @@ void World::load_file(cereal::PortableBinaryInputArchive& a, VersionNumber versi
|
|||
|
||||
drawData.cam.smooth_move_to(*this, coordsToJumpTo, windowSizeToJumpTo, true);
|
||||
drawProg.load_file(a, version);
|
||||
a(bMan);
|
||||
if(version >= VersionNumber(0, 2, 0))
|
||||
a(gridMan);
|
||||
//a(bMan);
|
||||
//if(version >= VersionNumber(0, 2, 0))
|
||||
// a(gridMan);
|
||||
a(rMan);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ class World {
|
|||
|
||||
World(MainProgram& initMain, OpenWorldInfo& worldInfo);
|
||||
|
||||
std::shared_ptr<NetworkingObjects::NetObjManagerTypeList> netObjectTypeList;
|
||||
// NOTE: Keep at the very beginning so that it's destroyed last
|
||||
NetworkingObjects::NetObjManager netObjMan;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue