Added FinishCollector interface as a serialisation manager to collect finishes from meshes as they are serialised

ProjectCollection is now a FinishCollector (accumulates the finishes to write the material proxies at the end)
This commit is contained in:
Ralph Wessel
2024-10-11 09:58:26 +01:00
parent 0c74a28982
commit 21fc2cff25
8 changed files with 191 additions and 10 deletions
@@ -10,12 +10,22 @@
#include "Speckle/Database/BIMElementDatabase.h"
#include "Speckle/Record/Element/Element.h"
#ifdef ARCHICAD
#include <ModelMaterial.hpp>
#endif
using namespace active::serialise;
using namespace connector::record;
using namespace speckle::database;
using namespace speckle::record::attribute;
using namespace speckle::utility;
#ifdef ARCHICAD
namespace connector::record {
class ProjectCollection::FinishCache : public std::unordered_map<active::utility::Guid, Finish::Unique> {};
}
#endif
namespace {
///Serialisation fields
@@ -32,6 +42,23 @@ namespace {
}
/*--------------------------------------------------------------------
Constructor
project: The source project
--------------------------------------------------------------------*/
ProjectCollection::ProjectCollection(speckle::environment::Project::Shared project) : base{project->getInfo().name, project} {
m_finishes = std::make_unique<FinishCache>();
} //ProjectCollection::ProjectCollection
/*--------------------------------------------------------------------
Destructor
--------------------------------------------------------------------*/
ProjectCollection::~ProjectCollection() {
} //ProjectCollection::~ProjectCollection
/*--------------------------------------------------------------------
Add an element to the collection hierarchy
@@ -90,6 +117,25 @@ bool ProjectCollection::addMaterialProxy(const speckle::database::BIMIndex& mate
} //ProjectCollection::addMaterialProxy
#ifdef ARCHICAD
/*--------------------------------------------------------------------
Add a ModelerAPI material to the collection (NB: These are not persistent so need to be captured by this method)
material: A material
objectID: The object the material is applied to
return: True if the material proxy was added (false typically means the record already exists)
--------------------------------------------------------------------*/
bool ProjectCollection::addMaterialProxy(const ModelerAPI::Material& material, const speckle::database::BIMRecordID& objectID) {
auto finishID = Guid::fromInt(material.GenerateHashValue());
if (m_finishes->find(finishID) != m_finishes->end())
return false;
auto finish = std::make_unique<Finish>(material);
return m_finishes->insert({finishID, std::move(finish)}).second;
} //ProjectCollection::addMaterialProxy
#endif
/*--------------------------------------------------------------------
Fill an inventory with the package items
@@ -126,11 +172,14 @@ Cargo::Unique ProjectCollection::getCargo(const Inventory::Item& item) const {
if (item.available < m_finishProxies.size()) {
auto iter = m_finishProxies.begin();
std::advance(iter, item.available);
if (auto attribute = m_project->getAttributeDatabase()->getAttribute(iter->first, iter->first.tableID); attribute) {
if (auto finish = dynamic_cast<const Finish*>(attribute.get()); finish != nullptr) {
auto proxy = std::make_unique<FinishProxy>(*finish, iter->second);
return std::make_unique<WrappedProxy>(std::move(proxy));
}
const Finish* finish = nullptr;
if (auto fin = m_finishes->find(iter->first); fin != m_finishes->end())
finish = fin->second.get();
else if (auto attribute = m_project->getAttributeDatabase()->getAttribute(iter->first, iter->first.tableID); attribute)
finish = dynamic_cast<const Finish*>(attribute.get());
if (finish != nullptr) {
auto proxy = std::make_unique<FinishProxy>(*finish, iter->second);
return std::make_unique<WrappedProxy>(std::move(proxy));
}
}
break;
@@ -2,6 +2,7 @@
#define CONNECTOR_RECORD_ROOT_COLLECTiON
#include "Connector/Record/Collection/RecordCollection.h"
#include "Speckle/Serialise/Collection/FinishCollector.h"
#include <stack>
@@ -20,7 +21,7 @@ namespace connector::record {
- Other attributes, e.g. materials
Add all this supplementary data to the root container as required
*/
class ProjectCollection : public RecordCollection {
class ProjectCollection : public RecordCollection, public speckle::serialise::FinishCollector {
public:
// MARK: - Types
@@ -33,7 +34,12 @@ namespace connector::record {
Constructor
@param project The source project
*/
ProjectCollection(speckle::environment::Project::Shared project) : base{project->getInfo().name, project} {}
ProjectCollection(speckle::environment::Project::Shared project);
ProjectCollection(const ProjectCollection&) = delete;
/*!
Destructor
*/
~ProjectCollection();
using base::base;
@@ -59,7 +65,16 @@ namespace connector::record {
@param objectID The object the material is applied to
@return True if the material proxy was added (false typically means the record already exists)
*/
bool addMaterialProxy(const speckle::database::BIMIndex& materialIndex, const speckle::database::BIMRecordID& objectID);
bool addMaterialProxy(const speckle::database::BIMIndex& materialIndex, const speckle::database::BIMRecordID& objectID) override;
#ifdef ARCHICAD
/*!
Add a ModelerAPI material to the collection (NB: These are not persistent so need to be captured by this method)
@param material A material
@param objectID The object the material is applied to
@return True if the material proxy was added (false typically means the record already exists)
*/
bool addMaterialProxy(const ModelerAPI::Material& material, const speckle::database::BIMRecordID& objectID) override;
#endif
// MARK: - Serialisation
@@ -81,6 +96,11 @@ namespace connector::record {
///Finish proxies accumulated from meshes generated from the collection elements
FinishProxies m_finishProxies;
#ifdef ARCHICAD
class FinishCache;
///Finishes cached from ModelerAPI materials
std::unique_ptr<FinishCache> m_finishes;
#endif
};
}
@@ -1,8 +1,13 @@
#include "Speckle/Record/Attribute/Finish.h"
#include "Active/Serialise/Item/Wrapper/ValueWrap.h"
#include "Active/Utility/BufferOut.h"
#include "Speckle/Utility/Guid.h"
#ifdef ARCHICAD
#include <ModelMaterial.hpp>
#endif
using namespace active::serialise;
using namespace speckle::database;
using namespace speckle::record::attribute;
@@ -37,6 +42,16 @@ namespace {
Identity{"surfaceColour"},
};
#ifdef ARCHICAD
/*!
Copy a ModelerAPI colour to an AC RGB colour
*/
void copyModelerColor(const ModelerAPI::Color& modelColour, API_RGBColor& colour) {
colour.f_red = modelColour.red;
colour.f_green = modelColour.green;
colour.f_blue = modelColour.blue;
} //copyModelerColor
#endif
}
/*--------------------------------------------------------------------
@@ -65,6 +80,31 @@ Finish::Finish(const database::BIMRecordID& ID) : base{ID, Finish::table} {
Finish::Finish(const API_Attribute& attrData, const BIMRecordID& tableID) : base{attrData.header.guid, Finish::table} {
m_data = std::make_unique<Data>(attrData);
}
/*--------------------------------------------------------------------
Constructor
material: A ModelerAPI material definition
--------------------------------------------------------------------*/
Finish::Finish(const ModelerAPI::Material& material) {
API_Attribute attr;
active::utility::Memory::erase(attr);
String{material.GetName()}.writeUTF8(active::utility::BufferOut{attr.header.name});
attr.header.guid = Guid{Guid::fromInt(material.GenerateHashValue())};
attr.material.mtype = static_cast<API_MaterTypeID>(material.GetType());
attr.material.ambientPc = material.GetAmbientReflection();
attr.material.diffusePc = material.GetDiffuseReflection();
attr.material.specularPc = material.GetSpecularReflection();
attr.material.transpPc = material.GetTransparency();
attr.material.shine = material.GetShining();
attr.material.transpAtt = material.GetTransparencyAttenuation();
attr.material.emissionAtt = material.GetEmissionAttenuation();
copyModelerColor(material.GetSurfaceColor(), attr.material.surfaceRGB);
copyModelerColor(material.GetSpecularColor(), attr.material.specularRGB);
copyModelerColor(material.GetEmissionColor(), attr.material.emissionRGB);
m_data = std::make_unique<Data>(attr);
} //Finish::Finish
#endif
@@ -95,6 +135,7 @@ const API_Attr_Head& Finish::getHead() const {
return m_data->root.head;
} //Finish::getHead
/*--------------------------------------------------------------------
Get the (mutable) API attribute header data
@@ -3,6 +3,12 @@
#include "Speckle/Record/Attribute/Attribute.h"
#ifdef ARCHICAD
namespace ModelerAPI {
class Material;
}
#endif
namespace speckle::record::attribute {
/*!
@@ -50,6 +56,11 @@ namespace speckle::record::attribute {
@param tableID The ID of the parent table
*/
Finish(const API_Attribute& attrData, const database::BIMRecordID& tableID);
/*!
Constructor
@param material A ModelerAPI material definition
*/
Finish(const ModelerAPI::Material& material);
#endif
/*!
Copy constructor
@@ -0,0 +1,48 @@
#ifndef SPECKLE_SERIALISE_FINISH_COLLECTOR
#define SPECKLE_SERIALISE_FINISH_COLLECTOR
#include "Active/Serialise/Management/Manager.h"
#ifdef ARCHICAD
namespace ModelerAPI {
class Material;
}
#endif
namespace speckle::serialise {
/*!
Collector for object finishes
Used as a serialisation manager to collect finishes from serialised objects
*/
class FinishCollector : public active::serialise::Manager {
public:
/*!
Destructor
*/
virtual ~FinishCollector() {}
// MARK: - Functions (mutating)
/*!
Add a material proxy record to the collection
@param materialIndex The index of the material to add
@param objectID The object the material is applied to
@return True if the material proxy was added (false typically means the record already exists)
*/
virtual bool addMaterialProxy(const speckle::database::BIMIndex& materialIndex, const speckle::database::BIMRecordID& objectID) = 0;
#ifdef ARCHICAD
/*!
Add a ModelerAPI material to the collection (NB: These are not persistent so need to be captured by this method)
@param material A material
@param objectID The object the material is applied to
@return True if the material proxy was added (false typically means the record already exists)
*/
virtual bool addMaterialProxy(const ModelerAPI::Material& material, const speckle::database::BIMRecordID& objectID) = 0;
#endif
};
}
#endif //SPECKLE_SERIALISE_FINISH_COLLECTOR
@@ -64,7 +64,7 @@ Cargo::Unique DetachedReference::getCargo(const Inventory::Item& item) const {
//If we don't have an allocated string for receiving a reference (from 'setDefault') then we need to create one
if (!m_reference) {
//Ask a manager to send the detached data and provide a reference
auto detachmentManager = getManager<DetachmentManager>();
auto detachmentManager = (management() == nullptr) ? nullptr : management()->get<DetachmentManager>();
if (detachmentManager == nullptr)
return nullptr; //TODO: Discuss if this is a serious error - possibly throwing an exception is warranted
m_reference = detachmentManager->send(std::forward<Package&&>(base::get()), item.identity());
@@ -1,7 +1,7 @@
#ifndef SPECKLE_SERIALISE_DETACHMENT_MANAGER
#define SPECKLE_SERIALISE_DETACHMENT_MANAGER
#include "Active/Serialise/Manager.h"
#include "Active/Serialise/Management/Manager.h"
#include "Active/Serialise/Transport.h"
#include "Speckle/Database/Identity/RecordID.h"
@@ -48,6 +48,7 @@
2196F3042CB57E8000450DFC /* Storey.h in Headers */ = {isa = PBXBuildFile; fileRef = 2196F3022CB57E7F00450DFC /* Storey.h */; };
2196F3052CB57E8000450DFC /* Storey.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2196F3032CB57E7F00450DFC /* Storey.cpp */; };
2199881E2BD833830035E5EA /* libArchicad27.a in CopyFiles */ = {isa = PBXBuildFile; fileRef = 21379E082AE47A6400A1584C /* libArchicad27.a */; };
21A0FBA42CB880690023F24E /* FinishCollector.h in Headers */ = {isa = PBXBuildFile; fileRef = 21A0FB9F2CB880690023F24E /* FinishCollector.h */; };
21AEF9BA2CA606B5000B8681 /* DetachedReference.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21AEF9B92CA606B4000B8681 /* DetachedReference.cpp */; };
21AEF9BC2CA6DF84000B8681 /* DetachmentManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21AEF9BB2CA6DF84000B8681 /* DetachmentManager.cpp */; };
21AEF9BE2CA6FDA4000B8681 /* DetachedWrap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21AEF9BD2CA6FDA4000B8681 /* DetachedWrap.cpp */; };
@@ -186,6 +187,7 @@
2196F3022CB57E7F00450DFC /* Storey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Storey.h; sourceTree = "<group>"; };
2196F3032CB57E7F00450DFC /* Storey.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Storey.cpp; sourceTree = "<group>"; };
219712682BE7E2D500D9EF7E /* Serialisation.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = Serialisation.md; sourceTree = "<group>"; };
21A0FB9F2CB880690023F24E /* FinishCollector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FinishCollector.h; sourceTree = "<group>"; };
21AEF9B32CA5F7CF000B8681 /* DetachedWrap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DetachedWrap.h; sourceTree = "<group>"; };
21AEF9B52CA5FA02000B8681 /* DetachedReference.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DetachedReference.h; sourceTree = "<group>"; };
21AEF9B72CA5FCB6000B8681 /* DetachmentManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DetachmentManager.h; sourceTree = "<group>"; };
@@ -290,6 +292,7 @@
isa = PBXGroup;
children = (
2167E27C2C49121F000827D3 /* CMakeLists.txt */,
21A0FBA02CB880690023F24E /* Collection */,
21AEF9C72CA818EA000B8681 /* Detached */,
21F69F3A2C6B880B008B6A06 /* JSBase */,
2196F2DF2CB0566500450DFC /* Units */,
@@ -472,6 +475,14 @@
path = SpeckleLibDoctest;
sourceTree = "<group>";
};
21A0FBA02CB880690023F24E /* Collection */ = {
isa = PBXGroup;
children = (
21A0FB9F2CB880690023F24E /* FinishCollector.h */,
);
path = Collection;
sourceTree = "<group>";
};
21AEF9C72CA818EA000B8681 /* Detached */ = {
isa = PBXGroup;
children = (
@@ -693,6 +704,7 @@
21D0BD562C890B1C0077E104 /* ServerMigration.h in Headers */,
210CC88F2C81A98500610F58 /* Guid64.h in Headers */,
21AEF9DD2CAAA4EA000B8681 /* DetachedObjectStore.h in Headers */,
21A0FBA42CB880690023F24E /* FinishCollector.h in Headers */,
215F088D2CA195EC00CD343B /* ArchicadElementDBaseEngine.h in Headers */,
2196F2F42CB483D600450DFC /* Finish.h in Headers */,
21B67D002C7CE15100FD64FC /* Exception.h in Headers */,