Example #1
0
int IFCImp::DoImport(const TCHAR *name, ImpInterface *impitfc, Interface *itfc, BOOL suppressPrompts) {

	IfcGeom::IteratorSettings settings;
	settings.use_world_coords() = false;
	settings.weld_vertices() = true;
	settings.sew_shells() = true;

#ifdef _UNICODE
	int fn_buffer_size = WideCharToMultiByte(CP_UTF8, 0, name, -1, 0, 0, 0, 0);
	char* fn_mb = new char[fn_buffer_size];
	WideCharToMultiByte(CP_UTF8, 0, name, -1, fn_mb, fn_buffer_size, 0, 0);
#else
	const char* fn_mb = name;
#endif

	IfcGeom::Iterator<float> iterator(settings, fn_mb);

	if (!iterator.initialize()) return false;

	itfc->ProgressStart(_T("Importing file..."), TRUE, fn, NULL);

	MtlBaseLib* mats = itfc->GetSceneMtls();
	int slot = mats->Count();

	std::map<std::vector<std::string>, Mtl*> material_cache;

	do{
		const IfcGeom::TriangulationElement<float>* o = static_cast<const IfcGeom::TriangulationElement<float>*>(iterator.get());

		TSTR o_type = S(o->type());
		TSTR o_guid = S(o->guid());

		Mtl *m = ComposeMultiMaterial(material_cache, mats, itfc, slot, o->geometry().materials(), o->type(), o->geometry().material_ids());

		TriObject* tri = CreateNewTriObject();

		const int numVerts = o->geometry().verts().size()/3;
		tri->mesh.setNumVerts(numVerts);
		for( int i = 0; i < numVerts; i ++ ) {
			tri->mesh.setVert(i,o->geometry().verts()[3*i+0],o->geometry().verts()[3*i+1],o->geometry().verts()[3*i+2]);
		}
		const int numFaces = o->geometry().faces().size()/3;
		tri->mesh.setNumFaces(numFaces);

		bool needs_default = std::find(o->geometry().material_ids().begin(), o->geometry().material_ids().end(), -1) != o->geometry().material_ids().end();

		typedef std::pair<int, int> edge_t;

		std::set<edge_t> face_boundaries;
		for(std::vector<int>::const_iterator it = o->geometry().edges().begin(); it != o->geometry().edges().end();) {
			const int v1 = *it++;
			const int v2 = *it++;

			const edge_t e((std::min)(v1, v2), (std::max)(v1, v2));
			face_boundaries.insert(e);
		}

		for( int i = 0; i < numFaces; i ++ ) {
			const int v1 = o->geometry().faces()[3*i+0];
			const int v2 = o->geometry().faces()[3*i+1];
			const int v3 = o->geometry().faces()[3*i+2];
			
			const edge_t e1((std::min)(v1, v2), (std::max)(v1, v2));
			const edge_t e2((std::min)(v2, v3), (std::max)(v2, v3));
			const edge_t e3((std::min)(v3, v1), (std::max)(v3, v1));

			const bool b1 = face_boundaries.find(e1) != face_boundaries.end();
			const bool b2 = face_boundaries.find(e2) != face_boundaries.end();
			const bool b3 = face_boundaries.find(e3) != face_boundaries.end();

			tri->mesh.faces[i].setVerts(v1, v2, v3);
			tri->mesh.faces[i].setEdgeVisFlags(b1, b2, b3);

			MtlID mtlid = o->geometry().material_ids()[i];
			if (needs_default) {
				mtlid ++;
			}
			tri->mesh.faces[i].setMatID(mtlid);
		}
				
		tri->mesh.buildNormals();
		// Either use this or undefine the FACESETS_AS_COMPOUND option in IfcGeom.h to have
		// properly oriented normals. Using only the line below will result in a consistent
		// orientation of normals accross shells, but not always oriented towards the
		// outside.
		// tri->mesh.UnifyNormals(false);
		tri->mesh.BuildStripsAndEdges();
		tri->mesh.InvalidateTopologyCache();
		tri->mesh.InvalidateGeomCache();

		ImpNode* node = impitfc->CreateNode();
		node->Reference(tri);
		node->SetName(o_guid);
		node->GetINode()->Hide(o->type() == "IfcOpeningElement" || o->type() == "IfcSpace");
		if (m) {
			node->GetINode()->SetMtl(m);
		}
		const std::vector<float>& matrix_data = o->transformation().matrix().data();
		node->SetTransform(0,Matrix3 ( Point3(matrix_data[0],matrix_data[1],matrix_data[2]),Point3(matrix_data[3],matrix_data[4],matrix_data[5]),
			Point3(matrix_data[6],matrix_data[7],matrix_data[8]),Point3(matrix_data[9],matrix_data[10],matrix_data[11]) ));
		impitfc->AddNodeToScene(node);

		itfc->ProgressUpdate(iterator.progress(), true, _T(""));

	} while (iterator.next());

	itfc->ProgressEnd();
	
	return true;
}
ifc_objects_t<ColorType>
extract_objects(std::string& root_guid, const fs::path& ifc_file, bool shared_vertices) {
    typedef typename cartan::openmesh_t<ColorType>::Point        point_t;
    typedef typename cartan::openmesh_t<ColorType>::Normal       normal_t;
    typedef typename cartan::openmesh_t<ColorType>::VertexHandle vertex_t;

    if (!fs::exists(ifc_file)) {
        throw std::runtime_error("Unable to open file \"" + ifc_file.string() + "\" for reading.");
    }

    IfcGeom::IteratorSettings settings;

    settings.set(IfcGeom::IteratorSettings::APPLY_DEFAULT_MATERIALS,      true);
    settings.set(IfcGeom::IteratorSettings::USE_WORLD_COORDS,             true);
    settings.set(IfcGeom::IteratorSettings::WELD_VERTICES,                shared_vertices);
    settings.set(IfcGeom::IteratorSettings::DISABLE_TRIANGULATION,        false);

    IfcGeom::Iterator<double> context_iterator(settings, ifc_file.string());

    IfcParse::IfcFile* file_ptr = context_iterator.getFile();
    auto root_entity = file_ptr->entitiesByType<IfcSchema::IfcRoot>();
    root_guid = (*root_entity->begin())->GlobalId();

    std::set<std::string> ignore_types;
    ignore_types.insert("ifcopeningelement");
    ignore_types.insert("ifcspace");

    context_iterator.excludeEntities(ignore_types);
    context_iterator.initialize();

    ifc_objects_t<ColorType> objects;

    do {
        ifc_object_t<ColorType> object;
        std::get<0>(object).request_vertex_normals();
        std::get<0>(object).request_face_normals();

        const IfcGeom::Element<double>* geom_object = context_iterator.get();
        auto triangulation = static_cast<const IfcGeom::TriangulationElement<double>*>(geom_object);
        const IfcGeom::Representation::Triangulation<double>& mesh = triangulation->geometry();

        // get type
        std::get<2>(object) = geom_object->type();
        for (std::string::iterator c = std::get<2>(object).begin(); c != std::get<2>(object).end(); ++c) {
            *c = tolower(*c);
        }

        std::get<1>(object) = geom_object->guid();

        // get polygon data
        auto& fs = mesh.faces();
        auto& vs = mesh.verts();
        auto& ns = mesh.normals();

        uint32_t v_count = vs.size() / 3;
        std::vector<vertex_t> vertex_handles;
        for (uint32_t v = 0; v < v_count; ++v) {
            auto vertex = std::get<0>(object).add_vertex(point_t(vs[3*v + 0], vs[3*v + 1], vs[3*v + 2]));

            if (!shared_vertices) {
                auto normal = normal_t(ns[3*v + 0], ns[3*v + 1], ns[3*v + 2]);
                float length = std::sqrt(normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2]);
                normal[0] /= length;
                normal[1] /= length;
                normal[2] /= length;
                std::get<0>(object).set_normal(vertex, normal);
            }

            vertex_handles.push_back(vertex);
        }

        for (uint32_t f = 0; f < fs.size(); f += 3) {
            std::vector<vertex_t> vertices;
            for (uint32_t v = 0; v < 3; ++v) {
                int idx = fs[f+v];
                vertices.push_back(vertex_handles[idx]);
            }
            std::get<0>(object).add_face(vertices);
        }

        // update mesh-internal normal state
        if (shared_vertices) {
            std::get<0>(object).update_normals();
        } else {
            std::get<0>(object).update_face_normals();
        }

        objects.push_back(object);
    } while (context_iterator.next());

    return objects;
}
Example #3
0
int main () {
	// Redirect stdout to this stream, so that involuntary 
	// writes to stdout do not interfere with our protocol.
	std::ostringstream oss;
	stdout_redir = oss.rdbuf();
	stdout_orig = std::cout.rdbuf();
	std::cout.rdbuf(stdout_redir);

#ifdef SET_BINARY_STREAMS
	_setmode(_fileno(stdout), _O_BINARY);
	std::cout.setf(std::ios_base::binary);
	_setmode(_fileno(stdin), _O_BINARY);
	std::cin.setf(std::ios_base::binary);
#endif

	bool has_more = false;

	IfcGeom::Iterator<float>* iterator = 0;

	Hello().write(std::cout);

	int exit_code = 0;
	for (;;) {
		const int32_t msg_type = sread<int32_t>(std::cin);
		switch (msg_type) {
		case IFC_MODEL: {
			IfcModel m; m.read(std::cin);
			std::string::size_type len = m.string().size();
			char* data = new char[len];
			memcpy(data, m.string().c_str(), len);

			IfcGeom::IteratorSettings settings;
            settings.set(IfcGeom::IteratorSettings::USE_WORLD_COORDS, false);
            settings.set(IfcGeom::IteratorSettings::WELD_VERTICES, false);
            settings.set(IfcGeom::IteratorSettings::CONVERT_BACK_UNITS, true);
            settings.set(IfcGeom::IteratorSettings::INCLUDE_CURVES, true);

			iterator = new IfcGeom::Iterator<float>(settings, data, (int)len);
			has_more = iterator->initialize();

			More(has_more).write(std::cout);
			continue;
		}
		case GET: {
			Get g; g.read(std::cin);
			if (!has_more) {
				exit_code = 1;
				break;
			}
			const IfcGeom::TriangulationElement<float>* geom = static_cast<const IfcGeom::TriangulationElement<float>*>(iterator->get());
			Entity(geom).write(std::cout);
			continue;
		}
		case NEXT: {
			Next n; n.read(std::cin);
			has_more = iterator->next();
			if (!has_more) {
				delete iterator;
				iterator = 0;
			}
			More(has_more).write(std::cout);
			continue;
		}
		case GET_LOG: {
			GetLog gl; gl.read(std::cin);
			WriteLog(iterator->getLog()).write(std::cout);
			continue;
		}
		case BYE: {
			Bye().write(std::cout);
			exit_code = 0;
			break;
		}
		default: 
			exit_code = 1; 
			break;
		}
		break;
	}
	std::cout.rdbuf(stdout_orig);
	return exit_code;
}
Example #4
0
int main(int argc, char** argv) {
	boost::program_options::options_description generic_options;
	generic_options.add_options()
		("help", "display usage information")
		("version", "display version information")
		("verbose,v", "more verbose output");

	boost::program_options::options_description fileio_options;
	fileio_options.add_options()
		("input-file", boost::program_options::value<std::string>(), "input IFC file")
		("output-file", boost::program_options::value<std::string>(), "output geometry file");

	std::string bounds;
	std::vector<std::string> entity_vector;
	boost::program_options::options_description geom_options;
	geom_options.add_options()
		("plan",
			"Specifies whether to include curves in the output result. Typically "
			"these are representations of type Plan or Axis. Excluded by default.")
		("model",
			"Specifies whether to include surfaces and solids in the output result. "
			"Typically these are representations of type Body or Facetation. "
			"Included by default.")
		("weld-vertices",
			"Specifies whether vertices are welded, meaning that the coordinates "
			"vector will only contain unique xyz-triplets. This results in a "
			"manifold mesh which is useful for modelling applications, but might "
			"result in unwanted shading artefacts in rendering applications.")
		("use-world-coords", 
			"Specifies whether to apply the local placements of building elements "
			"directly to the coordinates of the representation mesh rather than "
			"to represent the local placement in the 4x3 matrix, which will in that "
			"case be the identity matrix.")
		("convert-back-units",
			"Specifies whether to convert back geometrical output back to the "
			"unit of measure in which it is defined in the IFC file. Default is "
			"to use meters.")
		("sew-shells", 
			"Specifies whether to sew the faces of IfcConnectedFaceSets together. "
			"This is a potentially time consuming operation, but guarantees a "
			"consistent orientation of surface normals, even if the faces are not "
			"properly oriented in the IFC file.")
#if OCC_VERSION_HEX < 0x60900
		// In Open CASCADE version prior to 6.9.0 boolean operations with multiple
		// arguments where not introduced yet and a work-around was implemented to
		// subtract multiple openings as a single compound. This hack is obsolete
		// for newer versions of Open CASCADE.
		("merge-boolean-operands", 
			"Specifies whether to merge all IfcOpeningElement operands into a single "
			"operand before applying the subtraction operation. This may "
			"introduce a performance improvement at the risk of failing, in "
			"which case the subtraction is applied one-by-one.")
#endif
		("disable-opening-subtractions", 
			"Specifies whether to disable the boolean subtraction of "
			"IfcOpeningElement Representations from their RelatingElements.")
		("bounds", boost::program_options::value<std::string>(&bounds),
			"Specifies the bounding rectangle, for example 512x512, to which the " 
			"output will be scaled. Only used when converting to SVG.")
		("include", 
			"Specifies that the entities listed after --entities are to be included")
		("exclude", 
			"Specifies that the entities listed after --entities are to be excluded")
		("entities", boost::program_options::value< std::vector<std::string> >(&entity_vector)->multitoken(), 
			"A list of entities that should be included in or excluded from the "
			"geometrical output, depending on whether --ignore or --include is "
			"specified. Defaults to IfcOpeningElement and IfcSpace to be excluded.");
	
	boost::program_options::options_description cmdline_options;
	cmdline_options.add(generic_options).add(fileio_options).add(geom_options);

	boost::program_options::positional_options_description positional_options;
	positional_options.add("input-file", 1);
	positional_options.add("output-file", 1);

	boost::program_options::variables_map vmap;
	try {
		boost::program_options::store(boost::program_options::command_line_parser(argc, argv).
				  options(cmdline_options).positional(positional_options).run(), vmap);
	} catch (const boost::program_options::unknown_option& e) {
		std::cerr << "[Error] Unknown option '" << e.get_option_name() << "'" << std::endl << std::endl;
		// Usage information will be emitted below
	} catch (...) {
		// Catch other errors such as invalid command line syntax
	}
	boost::program_options::notify(vmap);

	if (vmap.count("version")) {
		printVersion();
		return 0;
	} else if (vmap.count("help") || !vmap.count("input-file")) {
		printUsage(generic_options, geom_options);
		return vmap.count("help") ? 0 : 1;
	} else if (vmap.count("include") && vmap.count("exclude")) {
		std::cerr << "[Error] --include and --ignore can not be specified together" << std::endl;
		printUsage(generic_options, geom_options);
		return 1;
	}

	const bool verbose = vmap.count("verbose") != 0;
	const bool weld_vertices = vmap.count("weld-vertices") != 0;
	const bool use_world_coords = vmap.count("use-world-coords") != 0;
	const bool convert_back_units = vmap.count("convert-back-units") != 0;
	const bool sew_shells = vmap.count("sew-shells") != 0;
#if OCC_VERSION_HEX < 0x60900
	const bool merge_boolean_operands = vmap.count("merge-boolean-operands") != 0;
#endif
	const bool disable_opening_subtractions = vmap.count("disable-opening-subtractions") != 0;
	bool include_entities = vmap.count("include") != 0;
	const bool include_plan = vmap.count("plan") != 0;
	const bool include_model = vmap.count("model") != 0 || (!include_plan);
	boost::optional<int> bounding_width, bounding_height;
	if (vmap.count("bounds") == 1) {
		int w, h;
		if (sscanf(bounds.c_str(), "%ux%u", &w, &h) == 2) {
			bounding_width = w;
			bounding_height = h;
		} else {
			std::cerr << "[Error] Invalid use of --bounds" << std::endl;
			printUsage(generic_options, geom_options);
			return 1;
		}
	}

	// Gets the set ifc types to be ignored from the command line. 
	std::set<std::string> entities;
	for (std::vector<std::string>::const_iterator it = entity_vector.begin(); it != entity_vector.end(); ++it) {
		const std::string& mixed_case_type = *it;
		entities.insert(boost::to_lower_copy(mixed_case_type));
	}
	
	const std::string input_filename = vmap["input-file"].as<std::string>();
	// If no output filename is specified a Wavefront OBJ file will be output
	// to maintain backwards compatibility with the obsolete IfcObj executable.
	const std::string output_filename = vmap.count("output-file") == 1 
		? vmap["output-file"].as<std::string>()
		: change_extension(input_filename, DEFAULT_EXTENSION);
	
	if (output_filename.size() < 5) {
		printUsage(generic_options, geom_options);
		return 1;
	}

	std::string output_extension = output_filename.substr(output_filename.size()-4);
	boost::to_lower(output_extension);

	// If no entities are specified these are the defaults to skip from output
	if (entity_vector.empty()) {
		if (output_extension == ".svg") {
			entities.insert("ifcspace");
			include_entities = true;
		} else {
			entities.insert("ifcopeningelement");
			entities.insert("ifcspace");
		}
	}

	Logger::SetOutput(&std::cout, &log_stream);
	Logger::Verbosity(verbose ? Logger::LOG_NOTICE : Logger::LOG_ERROR);

	if (output_extension == ".xml") {
		int exit_code = 1;
		try {
			XmlSerializer s(output_filename);
			IfcParse::IfcFile f;
			if (!f.Init(input_filename)) {
				Logger::Message(Logger::LOG_ERROR, "Unable to parse .ifc file");
			} else {
				s.setFile(&f);
				s.finalize();
				exit_code = 0;
			}
		} catch (...) {}
		write_log();
		return exit_code;
	}

	IfcGeom::IteratorSettings settings;

	settings.set(IfcGeom::IteratorSettings::APPLY_DEFAULT_MATERIALS,      true);
	settings.set(IfcGeom::IteratorSettings::USE_WORLD_COORDS,             use_world_coords);
	settings.set(IfcGeom::IteratorSettings::WELD_VERTICES,                weld_vertices);
	settings.set(IfcGeom::IteratorSettings::SEW_SHELLS,                   sew_shells);
	settings.set(IfcGeom::IteratorSettings::CONVERT_BACK_UNITS,           convert_back_units);
#if OCC_VERSION_HEX < 0x60900
	settings.set(IfcGeom::IteratorSettings::FASTER_BOOLEANS,              merge_boolean_operands);
#endif
	settings.set(IfcGeom::IteratorSettings::DISABLE_OPENING_SUBTRACTIONS, disable_opening_subtractions);
	settings.set(IfcGeom::IteratorSettings::INCLUDE_CURVES,               include_plan);
	settings.set(IfcGeom::IteratorSettings::EXCLUDE_SOLIDS_AND_SURFACES,  !include_model);

	GeometrySerializer* serializer;
	if (output_extension == ".obj") {
		const std::string mtl_filename = output_filename.substr(0,output_filename.size()-3) + "mtl";
		if (!use_world_coords) {
			Logger::Message(Logger::LOG_NOTICE, "Using world coords when writing WaveFront OBJ files");
			settings.set(IfcGeom::IteratorSettings::USE_WORLD_COORDS, true);
		}
		serializer = new WaveFrontOBJSerializer(output_filename, mtl_filename);
#ifdef WITH_OPENCOLLADA
	} else if (output_extension == ".dae") {
		serializer = new ColladaSerializer(output_filename);
#endif
	} else if (output_extension == ".stp") {
		serializer = new StepSerializer(output_filename);
	} else if (output_extension == ".igs") {
		// Not sure why this is needed, but it is.
		// See: http://tracker.dev.opencascade.org/view.php?id=23679
		IGESControl_Controller::Init();
		serializer = new IgesSerializer(output_filename);
	} else if (output_extension == ".svg") {
		settings.set(IfcGeom::IteratorSettings::DISABLE_TRIANGULATION, true);
		serializer = new SvgSerializer(output_filename);
		if (bounding_width && bounding_height) {
			((SvgSerializer*) serializer)->setBoundingRectangle(
				static_cast<double>(*bounding_width), 
				static_cast<double>(*bounding_height)
			);
		}
	} else {
		Logger::Message(Logger::LOG_ERROR, "Unknown output filename extension");
		write_log();
		printUsage(generic_options, geom_options);
		return 1;
	}

	if (!serializer->isTesselated()) {
		if (weld_vertices) {
			Logger::Message(Logger::LOG_NOTICE, "Weld vertices setting ignored when writing STEP or IGES files");
		}
		settings.disable_triangulation() = true;
	}

	IfcGeom::Iterator<double> context_iterator(settings, input_filename);

	IfcGeom::ProductFilter productFilter;
	try {
		if (include_entities) {
			productFilter.includeEntities(entities);
		} else {
			productFilter.excludeEntities(entities);
		}
		context_iterator.setProductFilter(&productFilter, &IfcGeom::ProductFilter::shouldConvertProduct);
	} catch (const IfcParse::IfcException& e) {
		std::cout << "[Error] " << e.what() << std::endl;
		return 1;
	}

	if (!serializer->ready()) {
		Logger::Message(Logger::LOG_ERROR, "Unable to open output file for writing");
		write_log();
		return 1;
	}

	time_t start,end;
	time(&start);
	
	if (!context_iterator.initialize()) {
		Logger::Message(Logger::LOG_ERROR, "Unable to parse .ifc file or no geometrical entities found");
		write_log();
		return 1;
	}

	serializer->setFile(context_iterator.getFile());

	if (convert_back_units) {
		serializer->setUnitNameAndMagnitude(context_iterator.getUnitName(), static_cast<const float>(context_iterator.getUnitMagnitude()));
	} else {
		serializer->setUnitNameAndMagnitude("METER", 1.0f);
	}

	serializer->writeHeader();

	std::set<std::string> materials;

	int old_progress = -1;
	Logger::Status("Creating geometry...");

	// The functions IfcGeom::Iterator::get() and IfcGeom::Iterator::next() 
	// wrap an iterator of all geometrical products in the Ifc file. 
	// IfcGeom::Iterator::get() returns an IfcGeom::TriangulationElement or 
	// -BRepElement pointer, based on current settings. (see IfcGeomIterator.h 
	// for definition) IfcGeom::Iterator::next() is used to poll whether more 
	// geometrical entities are available. None of these functions throw 
	// exceptions, neither for parsing errors or geometrical errors. Upon 
	// calling next() the entity to be returned has already been processed, a 
	// true return value guarantees that a successfully processed product is 
	// available. 
	do {
		const IfcGeom::Element<double>* geom_object = context_iterator.get();
		
		if (serializer->isTesselated()) {
			serializer->write(static_cast<const IfcGeom::TriangulationElement<double>*>(geom_object));
		} else {
			serializer->write(static_cast<const IfcGeom::BRepElement<double>*>(geom_object));
		}
		
		const int progress = context_iterator.progress() / 2;
		if (old_progress!= progress) Logger::ProgressBar(progress);
		old_progress = progress;
	
	} while (context_iterator.next());

	serializer->finalize();
	delete serializer;

	Logger::Status("\rDone creating geometry                                ");

	write_log();

	time(&end);
	int dif = (int) difftime (end,start);	
	printf ("\nConversion took %d seconds\n", dif );

	return 0;
}