// // Copyright 2014 DreamWorks Animation LLC. // // Licensed under the Apache License, Version 2.0 (the "Apache License") // with the following modification; you may not use this file except in // compliance with the Apache License and the following modification to it: // Section 6. Trademarks. is deleted and replaced with: // // 6. Trademarks. This License does not grant permission to use the trade // names, trademarks, service marks, or product names of the Licensor // and its affiliates, except as required to comply with Section 4(c) of // the License and to reproduce the content of the NOTICE file. // // You may obtain a copy of the Apache License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the Apache License with the above modification is // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the Apache License for the specific // language governing permissions and limitations under the Apache License. // #ifndef OPENSUBDIV3_FAR_TOPOLOGY_REFINER_FACTORY_H #define OPENSUBDIV3_FAR_TOPOLOGY_REFINER_FACTORY_H #include "../version.h" #include "../far/topologyRefiner.h" #include "../far/error.h" #include namespace OpenSubdiv { namespace OPENSUBDIV_VERSION { namespace Far { ///\brief Private base class of Factories for constructing TopologyRefiners /// /// TopologyRefinerFactoryBase is the base class for subclasses that are intended to /// construct TopologyRefiners directly from meshes in their native representations. /// The subclasses are parameterized by the mesh type \ and are expected /// to inherit the details related to assembly and validation provided here that are /// independent of the subclass' mesh type. // class TopologyRefinerFactoryBase { protected: // // Protected methods invoked by the subclass template to verify and process each // stage of construction implemented by the subclass: // typedef Vtr::internal::Level::ValidationCallback TopologyCallback; static bool prepareComponentTopologySizing(TopologyRefiner& refiner); static bool prepareComponentTopologyAssignment(TopologyRefiner& refiner, bool fullValidation, TopologyCallback callback, void const * callbackData); static bool prepareComponentTagsAndSharpness(TopologyRefiner& refiner); static bool prepareFaceVaryingChannels(TopologyRefiner& refiner); }; ///\brief Factory for constructing TopologyRefiners from specific mesh classes. /// /// TopologyRefinerFactory is the factory class template to convert an instance of /// TopologyRefiner from an arbitrary mesh class. While a class template, the implementation /// is not (cannot) be complete, so specialization of a few methods is required (it is a /// stateless factory, so no instance and only static methods). /// /// This template provides both the interface and high level assembly for the construction /// of the TopologyRefiner instance. The high level construction executes a specific set /// of operations to convert the client's MESH into TopologyRefiner. This set of operations /// combines methods independent of MESH from the base class with those specialized here for /// class MESH. /// template class TopologyRefinerFactory : public TopologyRefinerFactoryBase { public: /// \brief Options related to the construction of each TopologyRefiner. /// struct Options { Options(Sdc::SchemeType sdcType = Sdc::SCHEME_CATMARK, Sdc::Options sdcOptions = Sdc::Options()) : schemeType(sdcType), schemeOptions(sdcOptions), validateFullTopology(false) { } Sdc::SchemeType schemeType; ///< The subdivision scheme type identifier Sdc::Options schemeOptions; ///< The full set of options for the scheme, ///< e.g. boundary interpolation rules... unsigned int validateFullTopology : 1; ///< Apply more extensive validation of ///< the constructed topology -- intended ///< for debugging. }; /// \brief Instantiates a TopologyRefiner from client-provided topological /// representation. /// /// If only the face-vertices topological relationships are specified /// with this factory, edge relationships have to be inferred, which /// requires additional processing. If the client topological rep can /// provide this information, it is highly recommended to do so. /// /// @param mesh Client's topological representation (or a converter) // /// @param options Options controlling the creation of the TopologyRefiner /// /// @return A new instance of TopologyRefiner or 0 for failure /// static TopologyRefiner* Create(MESH const& mesh, Options options = Options()); protected: typedef Vtr::internal::Level::TopologyError TopologyError; //@{ /// @name Methods to be provided to complete assembly of the TopologyRefiner /// /// /// These methods are to be specialized to implement all details specific to /// class MESH required to convert MESH data to TopologyRefiner. Note that /// some of these *must* be specialized in order to complete construction while /// some are optional. /// /// There are two minimal construction requirements (to specify the size and /// content of all topology relations) and three optional (to specify feature /// tags, face-varying data, and runtime validation and error reporting). /// /// See comments in the generic stubs, the factory for Far::TopologyDescriptor /// or the tutorials for more details on writing these. /// /// \brief Specify the number of vertices, faces, face-vertices, etc. static bool resizeComponentTopology(TopologyRefiner& newRefiner, MESH const& mesh); /// \brief Specify the relationships between vertices, faces, etc. ie the /// face-vertices, vertex-faces, edge-vertices, etc. static bool assignComponentTopology(TopologyRefiner& newRefiner, MESH const& mesh); /// \brief (Optional) Specify edge or vertex sharpness or face holes static bool assignComponentTags(TopologyRefiner& newRefiner, MESH const& mesh); /// \brief (Optional) Specify face-varying data per face static bool assignFaceVaryingTopology(TopologyRefiner& newRefiner, MESH const& mesh); /// \brief (Optional) Control run-time topology validation and error reporting static void reportInvalidTopology(TopologyError errCode, char const * msg, MESH const& mesh); //@} protected: //@{ /// @name Base level assembly methods to be used within resizeComponentTopology() /// /// \brief These methods specify sizes of various quantities, e.g. the number of /// vertices, faces, face-vertices, etc. The number of the primary components /// (vertices, faces and edges) should be specified prior to anything else that /// references them (e.g. we need to know the number of faces before specifying /// the vertices for that face. /// /// If a full boundary representation with all neighborhood information is not /// available, e.g. faces and vertices are available but not edges, only the /// face-vertices should be specified. The remaining topological relationships /// will be constructed later in the assembly (though at greater cost than if /// specified directly). /// /// The sizes for topological relationships between individual components should be /// specified in order, i.e. the number of face-vertices for each successive face. /// /// \brief Specify the number of vertices to be accommodated static void setNumBaseVertices(TopologyRefiner & newRefiner, int count); /// \brief Specify the number of faces to be accommodated static void setNumBaseFaces(TopologyRefiner & newRefiner, int count); /// \brief Specify the number of edges to be accommodated static void setNumBaseEdges(TopologyRefiner & newRefiner, int count); /// \brief Specify the number of vertices incident each face static void setNumBaseFaceVertices(TopologyRefiner & newRefiner, Index f, int count); /// \brief Specify the number of faces incident each edge static void setNumBaseEdgeFaces(TopologyRefiner & newRefiner, Index e, int count); /// \brief Specify the number of faces incident each vertex static void setNumBaseVertexFaces(TopologyRefiner & newRefiner, Index v, int count); /// \brief Specify the number of edges incident each vertex static void setNumBaseVertexEdges(TopologyRefiner & newRefiner, Index v, int count); static int getNumBaseVertices(TopologyRefiner const & newRefiner); static int getNumBaseFaces(TopologyRefiner const & newRefiner); static int getNumBaseEdges(TopologyRefiner const & newRefiner); //@} //@{ /// @name Base level assembly methods to be used within assignComponentTopology() /// /// \brief These methods populate relationships between components -- in much the /// same manner as they are inspected once the TopologyRefiner is completed. /// /// An array of fixed size is returned from these methods and its entries are to be /// populated with the appropriate indices for its neighbors. At minimum, the /// vertices for each face must be specified. As noted previously, the remaining /// relationships will be constructed as needed. /// /// The ordering of entries in these arrays is important -- they are expected to /// be ordered counter-clockwise for a right-hand orientation. /// /// Non-manifold components must be explicitly tagged as such and they do not /// require the ordering expected of manifold components. Special consideration /// must also be given to certain non-manifold situations, e.g. the same edge /// cannot appear twice in a face, and a degenerate edge (same vertex at both /// ends) can only have one incident face. Such considerations are typically /// achievable by creating multiple instances of an edge. So while there will /// always be a one-to-one correspondence between vertices and faces, the same /// is not guaranteed of edges in certain non-manifold circumstances. /// /// \brief Assign the vertices incident each face static IndexArray getBaseFaceVertices(TopologyRefiner & newRefiner, Index f); /// \brief Assign the edges incident each face static IndexArray getBaseFaceEdges(TopologyRefiner & newRefiner, Index f); /// \brief Assign the vertices incident each edge static IndexArray getBaseEdgeVertices(TopologyRefiner & newRefiner, Index e); /// \brief Assign the faces incident each edge static IndexArray getBaseEdgeFaces(TopologyRefiner & newRefiner, Index e); /// \brief Assign the faces incident each vertex static IndexArray getBaseVertexFaces(TopologyRefiner & newRefiner, Index v); /// \brief Assign the edges incident each vertex static IndexArray getBaseVertexEdges(TopologyRefiner & newRefiner, Index v); /// \brief Assign the local indices of a vertex within each of its incident faces static LocalIndexArray getBaseVertexFaceLocalIndices(TopologyRefiner & newRefiner, Index v); /// \brief Assign the local indices of a vertex within each of its incident edges static LocalIndexArray getBaseVertexEdgeLocalIndices(TopologyRefiner & newRefiner, Index v); /// \brief Assign the local indices of an edge within each of its incident faces static LocalIndexArray getBaseEdgeFaceLocalIndices(TopologyRefiner & newRefiner, Index e); /// \brief Determine all local indices by inspection (only for pure manifold meshes) static void populateBaseLocalIndices(TopologyRefiner & newRefiner); /// \brief Tag an edge as non-manifold static void setBaseEdgeNonManifold(TopologyRefiner & newRefiner, Index e, bool b); /// \brief Tag a vertex as non-manifold static void setBaseVertexNonManifold(TopologyRefiner & newRefiner, Index v, bool b); //@} //@{ /// @name Base level assembly methods to be used within assignComponentTags() /// /// These methods are used to assign edge or vertex sharpness, for tagging faces /// as holes, etc. Unlike topological assignment, only those components that /// possess a feature of interest need be explicitly assigned. /// /// Since topological construction is largely complete by this point, a method is /// available to identify an edge for sharpness assignment given a pair of vertices. /// /// \brief Identify an edge to be assigned a sharpness value given a vertex pair static Index findBaseEdge(TopologyRefiner const & newRefiner, Index v0, Index v1); /// \brief Assign a sharpness value to a given edge static void setBaseEdgeSharpness(TopologyRefiner & newRefiner, Index e, float sharpness); /// \brief Assign a sharpness value to a given vertex static void setBaseVertexSharpness(TopologyRefiner & newRefiner, Index v, float sharpness); /// \brief Tag a face as a hole static void setBaseFaceHole(TopologyRefiner & newRefiner, Index f, bool isHole); //@} //@{ /// @name Base level assembly methods to be used within assignFaceVaryingTopology() /// /// Face-varying data is assigned to faces in much the same way as face-vertex /// topology is assigned -- indices for face-varying values are assigned to the /// corners of each face just as indices for vertices were assigned. /// /// Independent sets of face-varying data are stored in channels. The identifier /// of each channel (an integer) is expected whenever referring to face-varying /// data in any form. /// /// \brief Create a new face-varying channel with the given number of values static int createBaseFVarChannel(TopologyRefiner & newRefiner, int numValues); /// \brief Create a new face-varying channel with the given number of values and independent interpolation options static int createBaseFVarChannel(TopologyRefiner & newRefiner, int numValues, Sdc::Options const& fvarOptions); /// \brief Assign the face-varying values for the corners of each face static IndexArray getBaseFaceFVarValues(TopologyRefiner & newRefiner, Index face, int channel = 0); //@} protected: // // Not to be specialized: // static bool populateBaseLevel(TopologyRefiner& refiner, MESH const& mesh, Options options); }; // // Generic implementations: // template TopologyRefiner* TopologyRefinerFactory::Create(MESH const& mesh, Options options) { TopologyRefiner * refiner = new TopologyRefiner(options.schemeType, options.schemeOptions); if (! populateBaseLevel(*refiner, mesh, options)) { delete refiner; return 0; } // Eventually want to move the Refiner's inventory initialization here. Currently it // is handled after topology assignment, but if the inventory is to include additional // features (e.g. holes, etc.) it is better off deferred to here. return refiner; } template bool TopologyRefinerFactory::populateBaseLevel(TopologyRefiner& refiner, MESH const& mesh, Options options) { // // Construction of a specialized topology refiner involves four steps, each of which // involves a method specialized for MESH followed by one that takes an action in // response to it or in preparation for the next step. // // Both the specialized methods and those that follow them may find fault in the // construction and trigger failure at any time: // // // Sizing of the topology -- this is a required specialization for MESH. This defines // an inventory of all components and their relations that is used to allocate buffers // to be efficiently populated in the subsequent topology assignment step. // if (! resizeComponentTopology(refiner, mesh)) return false; if (! prepareComponentTopologySizing(refiner)) return false; // // Assignment of the topology -- this is a required specialization for MESH. If edges // are specified, all other topological relations are expected to be defined for them. // Otherwise edges and remaining topology will be completed from the face-vertices: // bool validate = options.validateFullTopology; TopologyCallback callback = reinterpret_cast(reportInvalidTopology); void const * userData = &mesh; if (! assignComponentTopology(refiner, mesh)) return false; if (! prepareComponentTopologyAssignment(refiner, validate, callback, userData)) return false; // // User assigned and internal tagging of components -- an optional specialization for // MESH. Allows the specification of sharpness values, holes, etc. // if (! assignComponentTags(refiner, mesh)) return false; if (! prepareComponentTagsAndSharpness(refiner)) return false; // // Defining channels of face-varying primvar data -- an optional specialization for MESH. // if (! assignFaceVaryingTopology(refiner, mesh)) return false; if (! prepareFaceVaryingChannels(refiner)) return false; return true; } template inline void TopologyRefinerFactory::setNumBaseFaces(TopologyRefiner & newRefiner, int count) { newRefiner._levels[0]->resizeFaces(count); } template inline void TopologyRefinerFactory::setNumBaseEdges(TopologyRefiner & newRefiner, int count) { newRefiner._levels[0]->resizeEdges(count); } template inline void TopologyRefinerFactory::setNumBaseVertices(TopologyRefiner & newRefiner, int count) { newRefiner._levels[0]->resizeVertices(count); } template inline int TopologyRefinerFactory::getNumBaseFaces(TopologyRefiner const & newRefiner) { return newRefiner._levels[0]->getNumFaces(); } template inline int TopologyRefinerFactory::getNumBaseEdges(TopologyRefiner const & newRefiner) { return newRefiner._levels[0]->getNumEdges(); } template inline int TopologyRefinerFactory::getNumBaseVertices(TopologyRefiner const & newRefiner) { return newRefiner._levels[0]->getNumVertices(); } template inline void TopologyRefinerFactory::setNumBaseFaceVertices(TopologyRefiner & newRefiner, Index f, int count) { newRefiner._levels[0]->resizeFaceVertices(f, count); } template inline void TopologyRefinerFactory::setNumBaseEdgeFaces(TopologyRefiner & newRefiner, Index e, int count) { newRefiner._levels[0]->resizeEdgeFaces(e, count); } template inline void TopologyRefinerFactory::setNumBaseVertexFaces(TopologyRefiner & newRefiner, Index v, int count) { newRefiner._levels[0]->resizeVertexFaces(v, count); } template inline void TopologyRefinerFactory::setNumBaseVertexEdges(TopologyRefiner & newRefiner, Index v, int count) { newRefiner._levels[0]->resizeVertexEdges(v, count); } template inline IndexArray TopologyRefinerFactory::getBaseFaceVertices(TopologyRefiner & newRefiner, Index f) { return newRefiner._levels[0]->getFaceVertices(f); } template inline IndexArray TopologyRefinerFactory::getBaseFaceEdges(TopologyRefiner & newRefiner, Index f) { return newRefiner._levels[0]->getFaceEdges(f); } template inline IndexArray TopologyRefinerFactory::getBaseEdgeVertices(TopologyRefiner & newRefiner, Index e) { return newRefiner._levels[0]->getEdgeVertices(e); } template inline IndexArray TopologyRefinerFactory::getBaseEdgeFaces(TopologyRefiner & newRefiner, Index e) { return newRefiner._levels[0]->getEdgeFaces(e); } template inline IndexArray TopologyRefinerFactory::getBaseVertexFaces(TopologyRefiner & newRefiner, Index v) { return newRefiner._levels[0]->getVertexFaces(v); } template inline IndexArray TopologyRefinerFactory::getBaseVertexEdges(TopologyRefiner & newRefiner, Index v) { return newRefiner._levels[0]->getVertexEdges(v); } template inline LocalIndexArray TopologyRefinerFactory::getBaseEdgeFaceLocalIndices(TopologyRefiner & newRefiner, Index e) { return newRefiner._levels[0]->getEdgeFaceLocalIndices(e); } template inline LocalIndexArray TopologyRefinerFactory::getBaseVertexFaceLocalIndices(TopologyRefiner & newRefiner, Index v) { return newRefiner._levels[0]->getVertexFaceLocalIndices(v); } template inline LocalIndexArray TopologyRefinerFactory::getBaseVertexEdgeLocalIndices(TopologyRefiner & newRefiner, Index v) { return newRefiner._levels[0]->getVertexEdgeLocalIndices(v); } template inline Index TopologyRefinerFactory::findBaseEdge(TopologyRefiner const & newRefiner, Index v0, Index v1) { return newRefiner._levels[0]->findEdge(v0, v1); } template inline void TopologyRefinerFactory::populateBaseLocalIndices(TopologyRefiner & newRefiner) { newRefiner._levels[0]->populateLocalIndices(); } template inline void TopologyRefinerFactory::setBaseEdgeNonManifold(TopologyRefiner & newRefiner, Index e, bool b) { newRefiner._levels[0]->setEdgeNonManifold(e, b); } template inline void TopologyRefinerFactory::setBaseVertexNonManifold(TopologyRefiner & newRefiner, Index v, bool b) { newRefiner._levels[0]->setVertexNonManifold(v, b); } template inline void TopologyRefinerFactory::setBaseEdgeSharpness(TopologyRefiner & newRefiner, Index e, float s) { newRefiner._levels[0]->getEdgeSharpness(e) = s; } template inline void TopologyRefinerFactory::setBaseVertexSharpness(TopologyRefiner & newRefiner, Index v, float s) { newRefiner._levels[0]->getVertexSharpness(v) = s; } template inline void TopologyRefinerFactory::setBaseFaceHole(TopologyRefiner & newRefiner, Index f, bool b) { newRefiner._levels[0]->setFaceHole(f, b); newRefiner._hasHoles |= b; } template inline int TopologyRefinerFactory::createBaseFVarChannel(TopologyRefiner & newRefiner, int numValues) { return newRefiner._levels[0]->createFVarChannel(numValues, newRefiner._subdivOptions); } template inline int TopologyRefinerFactory::createBaseFVarChannel(TopologyRefiner & newRefiner, int numValues, Sdc::Options const& fvarOptions) { Sdc::Options newOptions = newRefiner._subdivOptions; newOptions.SetFVarLinearInterpolation(fvarOptions.GetFVarLinearInterpolation()); return newRefiner._levels[0]->createFVarChannel(numValues, newOptions); } template inline IndexArray TopologyRefinerFactory::getBaseFaceFVarValues(TopologyRefiner & newRefiner, Index face, int channel) { return newRefiner._levels[0]->getFaceFVarValues(face, channel); } template bool TopologyRefinerFactory::resizeComponentTopology(TopologyRefiner& /* refiner */, MESH const& /* mesh */) { Error(FAR_RUNTIME_ERROR, "Failure in TopologyRefinerFactory<>::resizeComponentTopology() -- no specialization provided."); // // Sizing the topology tables: // This method is for determining the sizes of the various topology tables (and other // data) associated with the mesh. Once completed, appropriate memory will be allocated // and an additional method invoked to populate it accordingly. // // The following methods should be called -- first those to specify the number of faces, // edges and vertices in the mesh: // // void setBaseFaceCount( TopologyRefiner& newRefiner, int count) // void setBaseEdgeCount( TopologyRefiner& newRefiner, int count) // void setBaseVertexCount(TopologyRefiner& newRefiner, int count) // // and then for each face, edge and vertex, the number of its incident components: // // void setBaseFaceVertexCount(TopologyRefiner& newRefiner, Index face, int count) // void setBaseEdgeFaceCount( TopologyRefiner& newRefiner, Index edge, int count) // void setBaseVertexFaceCount(TopologyRefiner& newRefiner, Index vertex, int count) // void setBaseVertexEdgeCount(TopologyRefiner& newRefiner, Index vertex, int count) // // The count/size for a component type must be set before indices associated with that // component type can be used. // // Note that it is only necessary to size 4 of the 6 supported topological relations -- // the number of edge-vertices is fixed at two per edge, and the number of face-edges is // the same as the number of face-vertices. // // So a single pass through your mesh to gather up all of this sizing information will // allow the Tables to be allocated appropriately once and avoid any dynamic resizing as // it grows. // return false; } template bool TopologyRefinerFactory::assignComponentTopology(TopologyRefiner& /* refiner */, MESH const& /* mesh */) { Error(FAR_RUNTIME_ERROR, "Failure in TopologyRefinerFactory<>::assignComponentTopology() -- no specialization provided."); // // Assigning the topology tables: // Once the topology tables have been allocated, the six required topological // relations can be directly populated using the following methods: // // IndexArray setBaseFaceVertices(TopologyRefiner& newRefiner, Index face) // IndexArray setBaseFaceEdges(TopologyRefiner& newRefiner, Index face) // // IndexArray setBaseEdgeVertices(TopologyRefiner& newRefiner, Index edge) // IndexArray setBaseEdgeFaces(TopologyRefiner& newRefiner, Index edge) // // IndexArray setBaseVertexEdges(TopologyRefiner& newRefiner, Index vertex) // IndexArray setBaseVertexFaces(TopologyRefiner& newRefiner, Index vertex) // // For the last two relations -- the faces and edges incident a vertex -- there are // also "local indices" that must be specified (considering doing this internally), // where the "local index" of each incident face or edge is the index of the vertex // within that face or edge, and so ranging from 0-3 for incident quads and 0-1 for // incident edges. These are assigned through similarly retrieved arrays: // // LocalIndexArray setBaseVertexFaceLocalIndices(TopologyRefiner& newRefiner, Index vertex) // LocalIndexArray setBaseVertexEdgeLocalIndices(TopologyRefiner& newRefiner, Index vertex) // LocalIndexArray setBaseEdgeFaceLocalIndices( TopologyRefiner& newRefiner, Index edge) // // or, if the mesh is manifold, explicit assignment of these can be deferred and // all can be determined by calling: // // void populateBaseLocalIndices(TopologyRefiner& newRefiner) // // All components are assumed to be locally manifold and ordering of components in // the above relations is expected to be counter-clockwise. // // For non-manifold components, no ordering/orientation of incident components is // assumed or required, but be sure to explicitly tag such components (vertices and // edges) as non-manifold: // // void setBaseEdgeNonManifold(TopologyRefiner& newRefiner, Index edge, bool b); // // void setBaseVertexNonManifold(TopologyRefiner& newRefiner, Index vertex, bool b); // // Also consider using TopologyLevel::ValidateTopology() when debugging to ensure // that topology has been completely and correctly specified. // return false; } template bool TopologyRefinerFactory::assignFaceVaryingTopology(TopologyRefiner& /* refiner */, MESH const& /* mesh */) { // // Optional assigning face-varying topology tables: // // Create independent face-varying primitive variable channels: // int createBaseFVarChannel(TopologyRefiner& newRefiner, int numValues) // // For each channel, populate the face-vertex values: // IndexArray setBaseFaceFVarValues(TopologyRefiner& newRefiner, Index face, int channel = 0) // return true; } template bool TopologyRefinerFactory::assignComponentTags(TopologyRefiner& /* refiner */, MESH const& /* mesh */) { // // Optional tagging: // This is where any additional feature tags -- sharpness, holes, etc. -- can be // specified using: // // void setBaseEdgeSharpness(TopologyRefiner& newRefiner, Index edge, float sharpness) // void setBaseVertexSharpness(TopologyRefiner& newRefiner, Index vertex, float sharpness) // // void setBaseFaceHole(TopologyRefiner& newRefiner, Index face, bool hole) // return true; } template void TopologyRefinerFactory::reportInvalidTopology( TopologyError /* errCode */, char const * /* msg */, MESH const& /* mesh */) { // // Optional topology validation error reporting: // This method is called whenever the factory encounters topology validation // errors. By default, nothing is reported // } } // end namespace Far } // end namespace OPENSUBDIV_VERSION using namespace OPENSUBDIV_VERSION; } // end namespace OpenSubdiv #endif /* OPENSUBDIV3_FAR_TOPOLOGY_REFINER_FACTORY_H */