diff --git a/SpeckleConnector/Connector/Interface/Browser/Bridge/Send/SendBridge.cpp b/SpeckleConnector/Connector/Interface/Browser/Bridge/Send/SendBridge.cpp index 7248f7f..970afc2 100644 --- a/SpeckleConnector/Connector/Interface/Browser/Bridge/Send/SendBridge.cpp +++ b/SpeckleConnector/Connector/Interface/Browser/Bridge/Send/SendBridge.cpp @@ -48,6 +48,7 @@ SendBridge::SendBridge() : BrowserBridge{"sendBinding"} { #endif } //SendBridge::SendBridge + /*-------------------------------------------------------------------- Handle an element change @@ -56,23 +57,20 @@ SendBridge::SendBridge() : BrowserBridge{"sendBinding"} { return: True if the event should be closed --------------------------------------------------------------------*/ bool SendBridge::handle(const ElementEvent& event) { - + using enum ElementEvent::Type; auto eventType = event.getEventType(); - switch (eventType) - { - case ElementEvent::EventType::Begin: { + switch (eventType) { + case begin: m_changedElements.clear(); - } break; - case ElementEvent::EventType::End: { + break; + case end: { auto modelCardDatabase = connector()->getModelCardDatabase(); auto modelCards = modelCardDatabase->getCards(); - // POC: this is probably not efficient, should test, review and refactor it RecordIDList expiredModelCardIds; for (const auto& modelCard : modelCards) { if (auto senderCard = dynamic_cast(modelCard.get())) { auto modelCardSelection = senderCard->getFilter().getElementIDs(); - for (const auto& elemId : modelCardSelection) { if (std::find(m_changedElements.begin(), m_changedElements.end(), elemId) != m_changedElements.end()) { expiredModelCardIds.push_back(modelCard->getID()); @@ -81,18 +79,17 @@ bool SendBridge::handle(const ElementEvent& event) { } } } - if (!expiredModelCardIds.empty()) { auto wrapped = std::make_unique, RecordIDList>>(std::move(expiredModelCardIds)); sendEvent("setModelsExpired", std::move(wrapped)); } - } break; - case ElementEvent::EventType::Change: - case ElementEvent::EventType::Edit: - case ElementEvent::EventType::Delete: { - auto changedElement = event.getChangedElement(); - m_changedElements.push_back(changedElement); - } break; + break; + } + case changeElem: case editElem: case deleteElem: { + if (event.getElmentID()) + m_changedElements.push_back(*event.getElmentID()); + break; + } default: break; } diff --git a/SpeckleConnector/Connector/Record/Collection/ProjectCollection.cpp b/SpeckleConnector/Connector/Record/Collection/ProjectCollection.cpp index 3b46167..5ded6b7 100644 --- a/SpeckleConnector/Connector/Record/Collection/ProjectCollection.cpp +++ b/SpeckleConnector/Connector/Record/Collection/ProjectCollection.cpp @@ -9,6 +9,7 @@ #include "Connector/Record/Collection/FinishProxy.h" #include "Speckle/Database/BIMAttributeDatabase.h" #include "Speckle/Database/BIMElementDatabase.h" +#include "Speckle/Record/Attribute/Finish.h" #include "Speckle/Record/Element/Element.h" #ifdef ARCHICAD @@ -24,7 +25,7 @@ using namespace speckle::utility; #ifdef ARCHICAD namespace connector::record { - class ProjectCollection::FinishCache : public std::unordered_map {}; + class ProjectCollection::FinishCache : public std::unordered_map {}; } #endif @@ -131,14 +132,11 @@ bool ProjectCollection::addMaterialProxy(const speckle::database::BIMIndex& mate 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()); - auto iter = m_finishes->find(finishID); - if (iter == m_finishes->end()) { - auto finish = std::make_unique(material); - iter = m_finishes->insert({ finishID, std::move(finish) }).first; - } - return addMaterialProxy(finishID, objectID); +bool ProjectCollection::addMaterialProxy(const Finish& finish, const speckle::database::BIMRecordID& objectID) { + auto iter = m_finishes->find(finish.getBIMID()); + if (iter == m_finishes->end()) + iter = m_finishes->insert({finish.getBIMID(), finish}).first; + return addMaterialProxy(speckle::database::BIMIndex{finish.getBIMID()}, objectID); } //ProjectCollection::addMaterialProxy #endif @@ -181,7 +179,7 @@ Cargo::Unique ProjectCollection::getCargo(const Inventory::Item& item) const { std::advance(iter, item.available); const Finish* finish = nullptr; if (auto fin = m_finishes->find(iter->first); fin != m_finishes->end()) - finish = fin->second.get(); + finish = &fin->second; else if (auto attribute = m_project->getAttributeDatabase()->getAttribute(iter->first, iter->first.tableID); attribute) finish = dynamic_cast(attribute.get()); if (finish != nullptr) { diff --git a/SpeckleConnector/Connector/Record/Collection/ProjectCollection.h b/SpeckleConnector/Connector/Record/Collection/ProjectCollection.h index 7d2f2c6..acfb68d 100644 --- a/SpeckleConnector/Connector/Record/Collection/ProjectCollection.h +++ b/SpeckleConnector/Connector/Record/Collection/ProjectCollection.h @@ -70,15 +70,13 @@ namespace connector::record { @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) 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 finish A finish @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 + bool addMaterialProxy(const speckle::record::attribute::Finish& finish, const speckle::database::BIMRecordID& objectID) override; // MARK: - Serialisation @@ -101,11 +99,9 @@ namespace connector::record { std::unique_ptr m_management; ///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 m_finishes; -#endif }; } diff --git a/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Element/ArchicadElementDBaseEngine.cpp b/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Element/ArchicadElementDBaseEngine.cpp index 8ace187..5be5d29 100644 --- a/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Element/ArchicadElementDBaseEngine.cpp +++ b/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Element/ArchicadElementDBaseEngine.cpp @@ -128,7 +128,7 @@ BIMLinkList ArchicadElementDBaseEngine::getSelection() const { --------------------------------------------------------------------*/ void ArchicadElementDBaseEngine::setSelection(const BIMLinkList& elementIDs) const { GS::Array selNeigs; - for (const auto elemID : elementIDs) { + for (const auto& elemID : elementIDs) { API_Neig neig(elemID); selNeigs.Push(neig); } diff --git a/SpeckleLib/Speckle/Environment/Addon.cpp b/SpeckleLib/Speckle/Environment/Addon.cpp index 1714805..a573ea6 100644 --- a/SpeckleLib/Speckle/Environment/Addon.cpp +++ b/SpeckleLib/Speckle/Environment/Addon.cpp @@ -77,6 +77,7 @@ std::weak_ptr Addon::getActiveProject() const { void Addon::publishExternal(const active::event::Event& event) { if (!logCallback()) return; + ++m_sessionCount; try { preprocessEvent(event); publish(event); @@ -165,7 +166,7 @@ bool Addon::logCallback(bool initialise) { return false; } return true; -} //Addon::publishExternalEvent +} //Addon::logCallback /*-------------------------------------------------------------------- diff --git a/SpeckleLib/Speckle/Environment/Addon.h b/SpeckleLib/Speckle/Environment/Addon.h index bc0ea7b..c3efb35 100644 --- a/SpeckleLib/Speckle/Environment/Addon.h +++ b/SpeckleLib/Speckle/Environment/Addon.h @@ -41,6 +41,11 @@ namespace speckle::environment { @return The active project (nullptr = no open project) */ std::weak_ptr getActiveProject() const; + /*! + Get the session index + @return The session index (non-recursive entries into the add-on) + */ + uint32_t getSession() const { return m_sessionCount; } // MARK: - Functions (mutating) @@ -103,6 +108,8 @@ namespace speckle::environment { std::shared_ptr m_activeProject; ///The depth of nested callbacks - the root call starts at depth 0 (important for some entry-point initialisation) uint32_t m_callDepth = 0; + ///The index of the active session (non-recursive entries into the add-on) + uint32_t m_sessionCount = 0; }; diff --git a/SpeckleLib/Speckle/Event/Subscriber/ElementSubscriber.cpp b/SpeckleLib/Speckle/Event/Subscriber/ElementSubscriber.cpp index 1767bcd..cd3c6ca 100644 --- a/SpeckleLib/Speckle/Event/Subscriber/ElementSubscriber.cpp +++ b/SpeckleLib/Speckle/Event/Subscriber/ElementSubscriber.cpp @@ -16,6 +16,8 @@ using namespace speckle::event; namespace { + using enum ElementEvent::Type; + #ifdef ARCHICAD /*! Callback for an Archicad element change @@ -30,22 +32,22 @@ namespace { { case APINotifyElement_New: { ACAPI_Element_AttachObserver(elemType->elemHead.guid); - addon()->publishExternal(ElementEvent{ ElementID{ elemType->elemHead.guid }, ElementEvent::EventType::New }); + addon()->publishExternal(ElementEvent{newElem, ElementID{elemType->elemHead.guid}}); } break; case APINotifyElement_Change: { - addon()->publishExternal(ElementEvent{ ElementID{ elemType->elemHead.guid }, ElementEvent::EventType::Change }); + addon()->publishExternal(ElementEvent{changeElem, ElementID{elemType->elemHead.guid}}); } break; case APINotifyElement_Edit: { - addon()->publishExternal(ElementEvent{ ElementID{ elemType->elemHead.guid }, ElementEvent::EventType::Edit }); + addon()->publishExternal(ElementEvent{editElem, ElementID{elemType->elemHead.guid}}); } break; case APINotifyElement_Delete: { - addon()->publishExternal(ElementEvent{ ElementID{ elemType->elemHead.guid }, ElementEvent::EventType::Delete }); + addon()->publishExternal(ElementEvent{deleteElem, ElementID{ elemType->elemHead.guid}}); } break; case APINotifyElement_BeginEvents: - addon()->publishExternal(ElementEvent{ ElementID{}, ElementEvent::EventType::Begin }); + addon()->publishExternal(ElementEvent{begin}); break; case APINotifyElement_EndEvents: { - addon()->publishExternal(ElementEvent{ ElementID{}, ElementEvent::EventType::End }); + addon()->publishExternal(ElementEvent{end}); } break; default: break; diff --git a/SpeckleLib/Speckle/Event/Type/ElementEvent.h b/SpeckleLib/Speckle/Event/Type/ElementEvent.h index 80cce25..31f0c9f 100644 --- a/SpeckleLib/Speckle/Event/Type/ElementEvent.h +++ b/SpeckleLib/Speckle/Event/Type/ElementEvent.h @@ -9,23 +9,31 @@ namespace speckle::event { /*! - Class representing a selection change event + Class representing an event signaling a database transaction operating on a BIM element, e.g. adding, editing, deleting etc */ class ElementEvent : public active::event::Event { public: - enum EventType { New, Begin, End, Change, Edit, Delete }; + enum Type { + newElem, /// @@ -45,5 +46,6 @@ std::unique_ptr GetCallResult::getResult(const WrappedResultAr return nullptr; String jsonOutput; JSONTransport().send(std::forward(*item), Identity{}, jsonOutput); + record::element::ModelElement::resetCache(); return std::make_unique(jsonOutput); } //GetCallResult::getResult diff --git a/SpeckleLib/Speckle/Primitive/Mesh/Mesh.cpp b/SpeckleLib/Speckle/Primitive/Mesh/Mesh.cpp index eeed03d..1e0e9e4 100644 --- a/SpeckleLib/Speckle/Primitive/Mesh/Mesh.cpp +++ b/SpeckleLib/Speckle/Primitive/Mesh/Mesh.cpp @@ -98,6 +98,6 @@ void Mesh::useManagement(Management* management) const { //NB: This object only exists to populate the finish collection - it doesn't carry any serialisable content if (management != nullptr) { if (auto collector = management->get(); collector != nullptr) - collector->addMaterialProxy(m_material, getBIMID()); + collector->addMaterialProxy(m_finish, getBIMID()); } } //Mesh::useManagement diff --git a/SpeckleLib/Speckle/Primitive/Mesh/Mesh.h b/SpeckleLib/Speckle/Primitive/Mesh/Mesh.h index c9a8e9a..59497ef 100644 --- a/SpeckleLib/Speckle/Primitive/Mesh/Mesh.h +++ b/SpeckleLib/Speckle/Primitive/Mesh/Mesh.h @@ -3,10 +3,7 @@ #include "Speckle/Database/Content/BIMRecord.h" #include "Speckle/Utility/String.h" - -#ifdef ARCHICAD -#include "ModelMaterial.hpp" -#endif +#include "Speckle/Record/Attribute/Finish.h" namespace speckle::primitive { @@ -30,23 +27,23 @@ namespace speckle::primitive { /*! Constructor @param unit The mesh unit type - @param material The mesh material + @param finish The mesh finish */ - Mesh(const ModelerAPI::Material& material, + Mesh(const record::attribute::Finish& finish, active::measure::LengthType unit = active::measure::LengthType::metre) : - base{ utility::Guid{true}, utility::Guid{}, unit }, m_material{ material } {} + base{ utility::Guid{true}, utility::Guid{}, unit }, m_finish{ finish } {} /*! Constructor - @param unit The mesh unit type @param vertices The mesh vertices @param faces The mesh faces (the number of indices in the face followed by the vertex indices) @param colors The mesh face colours - @param material The mesh material + @param finish The mesh material + @param unit The mesh unit type */ - Mesh(std::vector&& vertices, std::vector&& faces, std::vector&& colors, const ModelerAPI::Material& material, + Mesh(std::vector&& vertices, std::vector&& faces, std::vector&& colors, const record::attribute::Finish& finish, active::measure::LengthType unit = active::measure::LengthType::metre) : - base{utility::Guid{true}, utility::Guid{}, unit}, m_vertices{std::move(vertices)}, m_faces{std::move(faces)}, m_colors{std::move(colors)}, m_material{material} {} - + base{utility::Guid{true}, utility::Guid{}, unit}, m_vertices{std::move(vertices)}, m_faces{std::move(faces)}, m_colors{std::move(colors)}, m_finish{finish} {} + // MARK: - Functions (const) /*! @@ -85,9 +82,7 @@ namespace speckle::primitive { std::vector m_vertices; std::vector m_faces; std::vector m_colors; -#ifdef ARCHICAD - ModelerAPI::Material m_material; -#endif + record::attribute::Finish m_finish; }; } diff --git a/SpeckleLib/Speckle/Record/Attribute/Finish.cpp b/SpeckleLib/Speckle/Record/Attribute/Finish.cpp index 1861b6a..ef82c17 100644 --- a/SpeckleLib/Speckle/Record/Attribute/Finish.cpp +++ b/SpeckleLib/Speckle/Record/Attribute/Finish.cpp @@ -156,6 +156,21 @@ Finish::Finish(const Finish& source) : base{source} { Finish::~Finish() {} +/*-------------------------------------------------------------------- + Assignment operator + + source: The object to assign + + return: A reference to this + --------------------------------------------------------------------*/ +Finish& Finish::operator=(const Finish& source) { + if (this != &source) { + m_data = source.m_data ? std::make_unique(*source.m_data) : nullptr; + } + return *this; +} //Finish::operator= + + #ifdef ARCHICAD /*-------------------------------------------------------------------- Get the (immutable) API attribute header data diff --git a/SpeckleLib/Speckle/Record/Attribute/Finish.h b/SpeckleLib/Speckle/Record/Attribute/Finish.h index 72fb19d..c1aaffd 100644 --- a/SpeckleLib/Speckle/Record/Attribute/Finish.h +++ b/SpeckleLib/Speckle/Record/Attribute/Finish.h @@ -77,7 +77,15 @@ namespace speckle::record::attribute { @return A clone of this object */ Finish* clonePtr() const override { return new Finish{*this}; } - + + // MARK: - Operators + + /*! + Assignment operator + @param source The object to assign + @return A reference to this + */ + Finish& operator=(const Finish& source); // MARK: - Functions (const) diff --git a/SpeckleLib/Speckle/Record/Element/ModelElement.cpp b/SpeckleLib/Speckle/Record/Element/ModelElement.cpp index 54108df..8f42eb2 100644 --- a/SpeckleLib/Speckle/Record/Element/ModelElement.cpp +++ b/SpeckleLib/Speckle/Record/Element/ModelElement.cpp @@ -10,17 +10,19 @@ #include "Speckle/Utility/Guid.h" #ifdef ARCHICAD -#include -#include -#include -#include #include -#include +#include #include +#include +#include +#include +#include +#include #endif using namespace active::measure; using namespace active::serialise; +using namespace speckle::database; using namespace speckle::environment; using namespace speckle::record::attribute; using namespace speckle::record::element; @@ -45,6 +47,39 @@ namespace speckle::record::element { std::unique_ptr m_cache; }; + + /*! + Cache for materials in Archicad + + NB: This has been implemented because looking up materials through the API is very slow (can easily triple processing time) + This can be eliminated in future if this problem is remedied + */ + class ModelElement::FinishCache : public std::unordered_map { + public: + using base = std::unordered_map; + using base::base; + + FinishCache() : base{} { activeSession = addon()->getSession(); } + + ///The active session value when the cache was created - enables the content to be reset between sessions + uint32_t activeSession = 0; + + /*! + Validate the cache (ensure matches the active session) + @return True if the content is valid + */ + bool validate() { + if (activeSession != addon()->getSession()) { + m_finishCache.reset(); + return false; + } + return true; + } + + }; + + std::unique_ptr ModelElement::m_finishCache; + } namespace { @@ -60,7 +95,7 @@ namespace { Identity{"displayValue"}, Identity{"properties"}, }; - + #ifdef ARCHICAD template void getSubElementIds(T* ptr, std::set& subIds) @@ -76,7 +111,7 @@ namespace { elem.header.guid = elemId; ACAPI_Element_Get(&elem); API_ElementMemo memo{}; - GSErrCode err = ACAPI_Element_GetMemo(elemId, &memo); + ACAPI_Element_GetMemo(elemId, &memo); std::set subIds{}; subIds.insert(elemId); @@ -161,6 +196,43 @@ ModelElement::ModelElement(const ModelElement& source) : base{source}, Classifie ModelElement::~ModelElement() {} +/*-------------------------------------------------------------------- + Get mesh finish from the cache + + finishID: The finish ID + + return: A pointer to the requested material (nullptr on failure) + --------------------------------------------------------------------*/ +Finish* ModelElement::getFinish(const Guid& finishID) { + if (!m_finishCache || !m_finishCache->validate()) + return nullptr; + if (auto iter = m_finishCache->find(finishID); iter != m_finishCache->end()) + return &iter->second; + return nullptr; +} //ModelElement::getFinish + + +/*-------------------------------------------------------------------- + Add a mesh finish to the cache + + finishID: The finish ID + finish: The mesh finish + --------------------------------------------------------------------*/ +Finish* ModelElement::cacheFinish(const Guid& finishID, const Finish& finish) { + if (!m_finishCache || !m_finishCache->validate()) + m_finishCache = std::make_unique(); + return &m_finishCache->insert({finishID, finish}).first->second; +} //ModelElement::cacheFinish + + +/*-------------------------------------------------------------------- + Reset the Archicad material cache + --------------------------------------------------------------------*/ +void ModelElement::resetCache() { + m_finishCache.reset(); +} //ModelElement::resetCache + + /*-------------------------------------------------------------------- Get the element body as a list of faces or Meshes @@ -168,93 +240,74 @@ ModelElement::~ModelElement() {} --------------------------------------------------------------------*/ ModelElement::Body* ModelElement::getBody() const { #ifdef ARCHICAD - if (m_data && m_data->m_cache) { + if (m_data && m_data->m_cache) return m_data->m_cache.get(); - } - void* dummy = nullptr; GSErrCode err = ACAPI_Sight_GetCurrentWindowSight(&dummy); - if (err != NoError) - { + if (err != NoError) { // TODO: should this throw? } - Modeler::SightPtr currentSightPtr((Modeler::Sight*)dummy); // init the shared ptr with the raw pointer ModelerAPI::Model acModel; Modeler::IAttributeReader* attrReader = ACAPI_Attribute_GetCurrentAttributeSetReader(); - err = EXPGetModel(currentSightPtr, &acModel, attrReader); - if (err != NoError) - { + if (err != NoError) { // TODO: should this throw? } - auto elementBody = new ModelElement::Body(); - // Map to collect meshes per material name - std::map materialMeshMap; - - auto subIds = collectSubIds(getHead().guid); - + std::unordered_map materialMeshMap; + auto subIds = collectSubIds(getHead().guid); Int32 nElements = acModel.GetElementCount(); - for (Int32 iElement = 1; iElement <= nElements; iElement++) - { + for (Int32 iElement = 1; iElement <= nElements; iElement++) { ModelerAPI::Element elem{}; acModel.GetElement(iElement, &elem); - - auto apiGuid = reinterpret_cast (elem.GetElemGuid()); + API_Guid apiGuid{GSGuid2APIGuid(elem.GetElemGuid())}; if (subIds.find(apiGuid) == subIds.end()) continue; - Int32 nBodies = elem.GetTessellatedBodyCount(); - for (Int32 bodyIndex = 1; bodyIndex <= nBodies; ++bodyIndex) - { + ModelerAPI::Material material{}; + for (Int32 bodyIndex = 1; bodyIndex <= nBodies; ++bodyIndex) { ModelerAPI::MeshBody body{}; elem.GetTessellatedBody(bodyIndex, &body); - Int32 polyCount = body.GetPolygonCount(); - for (Int32 polyIndex = 1; polyIndex <= polyCount; ++polyIndex) - { + for (Int32 polyIndex = 1; polyIndex <= polyCount; ++polyIndex) { ModelerAPI::Polygon polygon{}; body.GetPolygon(polyIndex, &polygon); - - ModelerAPI::Material material{}; - polygon.GetMaterial(&material); - auto materialName = material.GetName(); - if (materialMeshMap.find(materialName) == materialMeshMap.end()) { - materialMeshMap[materialName] = primitive::Mesh(material); + ModelerAPI::AttributeIndex attrIndex{}; + polygon.GetMaterialIndex(attrIndex); + Guid finishID{Guid::fromInt(attrIndex.GetIndex())}; + auto faceFinish = ModelElement::getFinish(finishID); + if (faceFinish == nullptr) { + ModelerAPI::Material material{}; + polygon.GetMaterial(&material); + Finish finish{material}; + faceFinish = ModelElement::cacheFinish(finishID, finish); } - + auto materialName = faceFinish->getName(); + if (materialMeshMap.find(materialName) == materialMeshMap.end()) + materialMeshMap[materialName] = primitive::Mesh(*faceFinish); Int32 convexPolyCount = polygon.GetConvexPolygonCount(); - - for (Int32 convPolyIndex = 1; convPolyIndex <= convexPolyCount; ++convPolyIndex) - { + for (Int32 convPolyIndex = 1; convPolyIndex <= convexPolyCount; ++convPolyIndex) { std::vector vertices; ModelerAPI::ConvexPolygon convexPolygon{}; polygon.GetConvexPolygon(convPolyIndex, &convexPolygon); Int32 vertexCount = convexPolygon.GetVertexCount(); - - for (Int32 vertexIndex = 1; vertexIndex <= vertexCount; ++vertexIndex) - { + for (Int32 vertexIndex = 1; vertexIndex <= vertexCount; ++vertexIndex) { ModelerAPI::Vertex vertex{}; body.GetVertex(convexPolygon.GetVertexIndex(vertexIndex), &vertex); - - // Collect vertices (as doubles for now, but should be changed to Vertex type) + // Collect vertices (as doubles for now, but should be changed to Vertex type) vertices.push_back(vertex.x); vertices.push_back(vertex.y); vertices.push_back(vertex.z); } - materialMeshMap[materialName].appendFace(std::move(vertices)); } } } } - - for (auto& [materialName, mesh] : materialMeshMap) - { + for (auto& [materialName, mesh] : materialMeshMap) elementBody->push_back(std::move(mesh)); - } m_data = std::make_unique(); m_data->m_cache.reset(elementBody); return m_data->m_cache.get(); diff --git a/SpeckleLib/Speckle/Record/Element/ModelElement.h b/SpeckleLib/Speckle/Record/Element/ModelElement.h index f57529a..2fbd272 100644 --- a/SpeckleLib/Speckle/Record/Element/ModelElement.h +++ b/SpeckleLib/Speckle/Record/Element/ModelElement.h @@ -6,6 +6,10 @@ #include "Speckle/Record/Element/Element.h" #include "Speckle/Record/Property/Propertied.h" +namespace speckle::record::attribute { + class Finish; +} + namespace speckle::record::element { /*! @@ -25,7 +29,27 @@ namespace speckle::record::element { using Option = std::optional; ///A model element 3D body primitive using Body = std::vector; - + + // MARK: - Constructors + + /*! + Get a mesh finish from the cache + @param finishID A finish ID + @return A pointer to the requested finish (nullptr on failure) + */ + static record::attribute::Finish* getFinish(const utility::Guid& finishID); + /*! + Add a mesh finish to the cache + @param finishID A finish ID + @param finish The mesh finish + @return A pointer to the cached finish + */ + static record::attribute::Finish* cacheFinish(const utility::Guid& finishID, const record::attribute::Finish& finish); + /*! + Reset the Archicad material cache + */ + static void resetCache(); + // MARK: - Constructors using base::base; @@ -87,6 +111,9 @@ namespace speckle::record::element { class Data; ///The element data mutable std::unique_ptr m_data; + class FinishCache; + ///Cached finishes (API lookup is very slow) + static std::unique_ptr m_finishCache; }; } diff --git a/SpeckleLib/Speckle/Serialise/Collection/FinishCollector.h b/SpeckleLib/Speckle/Serialise/Collection/FinishCollector.h index 44d5f87..efd9531 100644 --- a/SpeckleLib/Speckle/Serialise/Collection/FinishCollector.h +++ b/SpeckleLib/Speckle/Serialise/Collection/FinishCollector.h @@ -3,11 +3,9 @@ #include "Active/Serialise/Management/Manager.h" -#ifdef ARCHICAD -namespace ModelerAPI { - class Material; +namespace speckle::record::attribute { + class Finish; } -#endif namespace speckle::serialise { @@ -32,15 +30,13 @@ namespace speckle::serialise { @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 finish A finish @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 + virtual bool addMaterialProxy(const record::attribute::Finish& finish, const speckle::database::BIMRecordID& objectID) = 0; }; } diff --git a/SpeckleLib/SpeckleLib.xcodeproj/project.pbxproj b/SpeckleLib/SpeckleLib.xcodeproj/project.pbxproj index 1d3db79..59f28cd 100644 --- a/SpeckleLib/SpeckleLib.xcodeproj/project.pbxproj +++ b/SpeckleLib/SpeckleLib.xcodeproj/project.pbxproj @@ -17,6 +17,9 @@ 212A88132AE48821001EAFE7 /* libArchicad27.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 21379E082AE47A6400A1584C /* libArchicad27.a */; platformFilters = (macos, ); }; 21384BAB2CCDA9B400D4602B /* Host.h in Headers */ = {isa = PBXBuildFile; fileRef = 21384BA92CCDA9B400D4602B /* Host.h */; }; 21384BAC2CCDA9B400D4602B /* Host.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21384BAA2CCDA9B400D4602B /* Host.cpp */; }; + 21384BC52CD24AB200D4602B /* ElementSubscriber.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21384BC12CD24AB200D4602B /* ElementSubscriber.cpp */; }; + 21384BC62CD24AB200D4602B /* ElementSubscriber.h in Headers */ = {isa = PBXBuildFile; fileRef = 21384BC42CD24AB200D4602B /* ElementSubscriber.h */; }; + 21384BC82CD24ADC00D4602B /* ElementEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = 21384BC72CD24ADC00D4602B /* ElementEvent.h */; }; 215F08552C99DA8D00CD343B /* Project.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 215F08512C99DA8D00CD343B /* Project.cpp */; }; 215F08562C99DA8D00CD343B /* Project.h in Headers */ = {isa = PBXBuildFile; fileRef = 215F08542C99DA8D00CD343B /* Project.h */; }; 215F08662C9B006800CD343B /* ProjectEvent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 215F08652C9B006700CD343B /* ProjectEvent.cpp */; }; @@ -200,6 +203,9 @@ 21379E082AE47A6400A1584C /* libArchicad27.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libArchicad27.a; sourceTree = BUILT_PRODUCTS_DIR; }; 21384BA92CCDA9B400D4602B /* Host.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Host.h; sourceTree = ""; }; 21384BAA2CCDA9B400D4602B /* Host.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Host.cpp; sourceTree = ""; }; + 21384BC12CD24AB200D4602B /* ElementSubscriber.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ElementSubscriber.cpp; sourceTree = ""; }; + 21384BC42CD24AB200D4602B /* ElementSubscriber.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ElementSubscriber.h; sourceTree = ""; }; + 21384BC72CD24ADC00D4602B /* ElementEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ElementEvent.h; sourceTree = ""; }; 214EA4C52BA374FD008E5358 /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; 214EA4C62BA3762D008E5358 /* CMakeLists.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = SOURCE_ROOT; }; 215F08512C99DA8D00CD343B /* Project.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Project.cpp; sourceTree = ""; }; @@ -537,6 +543,8 @@ children = ( 21D0BDBB2C90F2830077E104 /* DocStoreSubscriber.cpp */, 21D0BDBA2C90F2830077E104 /* DocStoreSubscriber.h */, + 21384BC12CD24AB200D4602B /* ElementSubscriber.cpp */, + 21384BC42CD24AB200D4602B /* ElementSubscriber.h */, 219351782C624FC100E5A69C /* MenuSubscriber.cpp */, 219351792C624FC100E5A69C /* MenuSubscriber.h */, 21D0BDC22C9241940077E104 /* ProjectSubscriber.cpp */, @@ -551,6 +559,7 @@ isa = PBXGroup; children = ( 21D0BDBE2C90F36B0077E104 /* DocStoreMergeEvent.h */, + 21384BC72CD24ADC00D4602B /* ElementEvent.h */, 219351892C62655700E5A69C /* MenuEvent.h */, 215F08652C9B006700CD343B /* ProjectEvent.cpp */, 21D0BDC62C9245E40077E104 /* ProjectEvent.h */, @@ -955,6 +964,7 @@ 21AE19802CC7D265004DBCFC /* PropertiedWrapper.h in Headers */, 21A0FC0B2CBE5E220023F24E /* Segment.h in Headers */, 215F088D2CA195EC00CD343B /* ArchicadElementDBaseEngine.h in Headers */, + 21384BC82CD24ADC00D4602B /* ElementEvent.h in Headers */, 2196F2F42CB483D600450DFC /* Finish.h in Headers */, 21A0FBED2CBD6B1A0023F24E /* Part.h in Headers */, 21B67D002C7CE15100FD64FC /* Exception.h in Headers */, @@ -988,6 +998,7 @@ 21AE196A2CC57832004DBCFC /* Propertied.h in Headers */, 2196F2EC2CB4816B00450DFC /* ArchicadAttributeDBaseEngine.h in Headers */, 21AE19582CC27DB3004DBCFC /* Value.h in Headers */, + 21384BC62CD24AB200D4602B /* ElementSubscriber.h in Headers */, 21D0BD312C86FE090077E104 /* Index.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1174,6 +1185,7 @@ 215F08552C99DA8D00CD343B /* Project.cpp in Sources */, 21AE19882CC7FF5F004DBCFC /* Group.cpp in Sources */, 21F69F3B2C6B880C008B6A06 /* JSBaseTransport.cpp in Sources */, + 21384BC52CD24AB200D4602B /* ElementSubscriber.cpp in Sources */, 2196F2EB2CB4816B00450DFC /* ArchicadAttributeDBaseEngine.cpp in Sources */, 21AE19532CC273F1004DBCFC /* Template.cpp in Sources */, 21384BAC2CCDA9B400D4602B /* Host.cpp in Sources */,