diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index 2b840e4240..64cce0bf12 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -311,7 +311,7 @@ int main(int argc, char** argv) { "if an object does not have any specified material in the IFC file.") ("validate", "Checks whether geometrical output conforms to the included explicit quantities."); - std::string bounds, offset_str; + std::string bounds, offset_str, rotation_str; #ifdef HAVE_ICU std::string unicode_mode; #endif @@ -348,7 +348,9 @@ int main(int argc, char** argv) { "Centers the elements upon serialization by applying the center point of " "all placements as an offset. Applicable for OBJ and DAE output. Can take several minutes on large models.") ("model-offset", po::value(&offset_str), - "Applies an arbitrary offset of form 'x;y;z' to all placements. Applicable for OBJ and DAE output.") + "Applies an arbitrary offset of form 'x;y;z' to all placements.") + ("model-rotation", po::value(&rotation_str), + "Applies an arbitrary quaternion rotation of form 'x;y;z;w' to all placements.") ("site-local-placement", "Place elements locally in the IfcSite coordinate system, instead of placing " "them in the IFC global coords. Applicable for OBJ and DAE output.") @@ -417,6 +419,7 @@ int main(int argc, char** argv) { const bool no_normals = vmap.count("no-normals") != 0; const bool center_model = vmap.count("center-model") != 0; const bool model_offset = vmap.count("model-offset") != 0; + const bool model_rotation = vmap.count("model-rotation") != 0; const bool site_local_placement = vmap.count("site-local-placement") != 0; const bool building_local_placement = vmap.count("building-local-placement") != 0; const bool generate_uvs = vmap.count("generate-uvs") != 0; @@ -758,6 +761,52 @@ int main(int argc, char** argv) { Logger::SetOutput(quiet ? nullptr : &cout_, &log_stream); + if (model_rotation) { + std::array &rotation = settings.rotation; + if (sscanf(rotation_str.c_str(), "%lf;%lf;%lf;%lf", &rotation[0], &rotation[1], &rotation[2], &rotation[3]) != 4) { + cerr_ << "[Error] Invalid use of --model-rotation\n"; + IfcUtil::path::delete_file(IfcUtil::path::to_utf8(output_temp_filename)); + print_options(serializer_options); + return EXIT_FAILURE; + } + + std::stringstream msg; + msg << "Using model rotation (" << rotation[0] << "," << rotation[1] << "," << rotation[2] << "," << rotation[3] << ")"; + Logger::Notice(msg.str()); + } + + if (is_tesselated && (center_model || model_offset)) { + std::array &offset = settings.offset; + if (center_model) { + if (site_local_placement || building_local_placement) { + Logger::Error("Cannot use --center-model together with --{site,building}-local-placement"); + return EXIT_FAILURE; + } + + IfcGeom::Iterator tmp_context_iterator(settings, ifc_file, filter_funcs, num_threads); + + if (!quiet) Logger::Status("Computing bounds..."); + tmp_context_iterator.compute_bounds(); + if (!quiet) Logger::Status("Done!"); + + gp_XYZ center = (tmp_context_iterator.bounds_min() + tmp_context_iterator.bounds_max()) * 0.5; + offset[0] = -center.X(); + offset[1] = -center.Y(); + offset[2] = -center.Z(); + } else { + if (sscanf(offset_str.c_str(), "%lf;%lf;%lf", &offset[0], &offset[1], &offset[2]) != 3) { + cerr_ << "[Error] Invalid use of --model-offset\n"; + IfcUtil::path::delete_file(IfcUtil::path::to_utf8(output_temp_filename)); + print_options(serializer_options); + return EXIT_FAILURE; + } + } + + std::stringstream msg; + msg << "Using model offset (" << offset[0] << "," << offset[1] << "," << offset[2] << ")"; + Logger::Notice(msg.str()); + } + IfcGeom::Iterator context_iterator(settings, ifc_file, filter_funcs, num_threads); if (!context_iterator.initialize()) { /// @todo It would be nice to know and print separate error prints for a case where we found no entities @@ -781,36 +830,6 @@ int main(int argc, char** argv) { int old_progress = quiet ? 0 : -1; - if (is_tesselated && (center_model || model_offset)) { - double* offset = serializer->settings().offset; - if (center_model) { - if (site_local_placement || building_local_placement) { - Logger::Error("Cannot use --center-model together with --{site,building}-local-placement"); - return EXIT_FAILURE; - } - - if (!quiet) Logger::Status("Computing bounds..."); - context_iterator.compute_bounds(); - if (!quiet) Logger::Status("Done!"); - - gp_XYZ center = (context_iterator.bounds_min() + context_iterator.bounds_max()) * 0.5; - offset[0] = -center.X(); - offset[1] = -center.Y(); - offset[2] = -center.Z(); - } else { - if (sscanf(offset_str.c_str(), "%lf;%lf;%lf", &offset[0], &offset[1], &offset[2]) != 3) { - cerr_ << "[Error] Invalid use of --model-offset\n"; - IfcUtil::path::delete_file(IfcUtil::path::to_utf8(output_temp_filename)); - print_options(serializer_options); - return EXIT_FAILURE; - } - } - - std::stringstream msg; - msg << "Using model offset (" << offset[0] << "," << offset[1] << "," << offset[2] << ")"; - Logger::Notice(msg.str()); - } - if (!quiet) { if (num_threads == 1) { Logger::Status("Creating geometry..."); diff --git a/src/ifcgeom/IfcGeom.h b/src/ifcgeom/IfcGeom.h index 87664e7fcd..9c593765b7 100644 --- a/src/ifcgeom/IfcGeom.h +++ b/src/ifcgeom/IfcGeom.h @@ -21,6 +21,7 @@ #define IFCGEOM_H #include +#include static const double ALMOST_ZERO = 1.e-9; @@ -37,6 +38,7 @@ inline static bool ALMOST_THE_SAME(const T& a, const T& b, double tolerance=ALMO #include #include #include +#include #include #include #include @@ -222,6 +224,9 @@ private: double modelling_precision; double dimensionality; double layerset_first; + gp_Vec offset = gp_Vec{0.0, 0.0, 0.0}; + gp_Quaternion rotation = gp_Quaternion{}; + gp_Trsf offset_and_rotation = gp_Trsf(); #ifndef NO_CACHE MAKE_TYPE_NAME(Cache) cache; @@ -264,6 +269,9 @@ public: return *this; } + void set_offset(const std::array& offset); + void set_rotation(const std::array& rotation); + bool convert_wire_to_face(const TopoDS_Wire& wire, TopoDS_Face& face); bool convert_curve_to_wire(const Handle(Geom_Curve)& curve, TopoDS_Wire& wire); bool convert_shapes(const IfcUtil::IfcBaseClass* L, IfcRepresentationShapeItems& result); diff --git a/src/ifcgeom/IfcGeomFunctions.cpp b/src/ifcgeom/IfcGeomFunctions.cpp index 0a5557dc34..8b7750cd0f 100644 --- a/src/ifcgeom/IfcGeomFunctions.cpp +++ b/src/ifcgeom/IfcGeomFunctions.cpp @@ -505,6 +505,28 @@ namespace { usd.Build(); return usd.Shape(); } + + gp_Trsf combine_offset_and_rotation(const gp_Vec &offset, const gp_Quaternion& rotation) { + auto offset_transform = gp_Trsf{}; + offset_transform.SetTranslation(offset); + + auto rotation_transform = gp_Trsf{}; + rotation_transform.SetRotation(rotation); + + return rotation_transform * offset_transform; + } +} + +void IfcGeom::Kernel::set_offset(const std::array &p_offset) { + offset = gp_Vec(p_offset[0], p_offset[1], p_offset[2]); + + offset_and_rotation = combine_offset_and_rotation(offset, rotation); +} + +void IfcGeom::Kernel::set_rotation(const std::array &p_rotation) { + rotation = gp_Quaternion(p_rotation[0], p_rotation[1], p_rotation[2], p_rotation[3]); + + offset_and_rotation = combine_offset_and_rotation(offset, rotation); } bool IfcGeom::Kernel::create_solid_from_compound(const TopoDS_Shape& compound, TopoDS_Shape& shape) { diff --git a/src/ifcgeom/IfcGeomHelpers.cpp b/src/ifcgeom/IfcGeomHelpers.cpp index 24c95ebbdf..b7549908ca 100644 --- a/src/ifcgeom/IfcGeomHelpers.cpp +++ b/src/ifcgeom/IfcGeomHelpers.cpp @@ -418,6 +418,9 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcObjectPlacement* l, gp_Trsf& t else break; } else break; } + + trsf.PreMultiply(offset_and_rotation); + CACHE(IfcObjectPlacement,l,trsf) return true; } diff --git a/src/ifcgeom/IfcGeomIteratorImplementation.h b/src/ifcgeom/IfcGeomIteratorImplementation.h index 2926999ba4..1f7f2c4acb 100644 --- a/src/ifcgeom/IfcGeomIteratorImplementation.h +++ b/src/ifcgeom/IfcGeomIteratorImplementation.h @@ -985,6 +985,8 @@ namespace IfcGeom { } else if (settings.get(IteratorSettings::SITE_LOCAL_PLACEMENT)) { kernel.set_conversion_placement_rel_to(&IfcSchema::IfcSite::Class()); } + kernel.set_offset(settings.offset); + kernel.set_rotation(settings.rotation); } public: diff --git a/src/ifcgeom/IfcGeomIteratorSettings.h b/src/ifcgeom/IfcGeomIteratorSettings.h index a78acbd2ef..68258bf474 100644 --- a/src/ifcgeom/IfcGeomIteratorSettings.h +++ b/src/ifcgeom/IfcGeomIteratorSettings.h @@ -20,6 +20,8 @@ #ifndef IFCGEOMITERATORSETTINGS_H #define IFCGEOMITERATORSETTINGS_H +#include + #include "ifc_geom_api.h" #include "../ifcparse/IfcException.h" #include "../ifcparse/IfcBaseClass.h" @@ -132,6 +134,11 @@ namespace IfcGeom } } + /// Optional offset that is applied to serialized objects, (0,0,0) by default. + std::array offset = std::array{0.0, 0.0, 0.0}; + /// Optional rotation that is applied to serialized objects, (0,0,0,1) by default. + std::array rotation = std::array{0.0, 0.0, 0.0, 1.0}; + protected: SettingField settings_; double deflection_tolerance_; diff --git a/src/serializers/ColladaSerializer.cpp b/src/serializers/ColladaSerializer.cpp index 4a4ae6934b..9413ef3285 100644 --- a/src/serializers/ColladaSerializer.cpp +++ b/src/serializers/ColladaSerializer.cpp @@ -212,11 +212,6 @@ void ColladaSerializer::ColladaExporter::ColladaScene::add( { (double)posmatrix[2], (double)posmatrix[5], (double)posmatrix[8], (double)posmatrix[11] }, { 0, 0, 0, 1 } }; - - /// @todo: TFK: Rather than applying this offset to all leafs (which might be undesirable) should this offset be applied to a node higher up in the hierarchy? - matrix_array[0][3] += serializer->settings().offset[0]; - matrix_array[1][3] += serializer->settings().offset[1]; - matrix_array[2][3] += serializer->settings().offset[2]; delete relative_trsf; diff --git a/src/serializers/GeometrySerializer.h b/src/serializers/GeometrySerializer.h index 0673a1cc65..5e40a2ef59 100644 --- a/src/serializers/GeometrySerializer.h +++ b/src/serializers/GeometrySerializer.h @@ -55,13 +55,7 @@ public: }; SerializerSettings() - : precision(DEFAULT_PRECISION) - { - memset(offset, 0, sizeof(offset)); - } - - /// Optional offset that is applied to serialized objects, (0,0,0) by default. - double offset[3]; + : precision(DEFAULT_PRECISION) { } /// Sets the precision used to format floating-point values, 15 by default. /// Use a negative value to use the system's default precision (should be 6 typically). diff --git a/src/serializers/WavefrontObjSerializer.cpp b/src/serializers/WavefrontObjSerializer.cpp index 920cfa376d..0b5de33548 100644 --- a/src/serializers/WavefrontObjSerializer.cpp +++ b/src/serializers/WavefrontObjSerializer.cpp @@ -93,9 +93,9 @@ void WaveFrontOBJSerializer::write(const IfcGeom::TriangulationElement* const int vcount = (int)mesh.verts().size() / 3; for ( std::vector::const_iterator it = mesh.verts().begin(); it != mesh.verts().end(); ) { - const real_t x = *(it++) + (real_t)settings().offset[0]; - const real_t y = *(it++) + (real_t)settings().offset[1]; - const real_t z = *(it++) + (real_t)settings().offset[2]; + const real_t x = *(it++); + const real_t y = *(it++); + const real_t z = *(it++); obj_stream << "v " << x << " " << y << " " << z << "\n"; }