Make UVs a property of IfcGeom::Representation::Triangulation. Implement writing of UVs for OBJ output.

This commit is contained in:
Stinkfist0
2016-03-17 00:25:33 +02:00
parent 923f1212f5
commit 76d3823bd4
5 changed files with 85 additions and 64 deletions
+20 -47
View File
@@ -38,35 +38,6 @@ static void collada_id(std::string &s)
IfcUtil::escape_xml(s);
}
/// @todo Very simple impl. Assumes that input vertices and normals match 1:1.
static std::vector<real_t> box_project_uvs(const std::vector<real_t> &vertices, const std::vector<real_t> &normals)
{
std::vector<real_t> uvs;
uvs.resize(vertices.size() / 3 * 2);
for(size_t uv_idx = 0, v_idx = 0;
uv_idx < uvs.size() && v_idx < vertices.size() && v_idx < normals.size();
uv_idx += 2, v_idx += 3) {
real_t n_x = normals[v_idx], n_y = normals[v_idx+1], n_z = normals[v_idx+2];
real_t v_x = vertices[v_idx], v_y = vertices[v_idx+1], v_z = vertices[v_idx+2];
if (std::abs(n_x) > std::abs(n_y) && std::abs(n_x) > std::abs(n_z)) {
uvs[uv_idx] = v_z;
uvs[uv_idx+1] = v_y;
}
if (std::abs(n_y) > std::abs(n_x) && std::abs(n_y) > std::abs(n_z)) {
uvs[uv_idx] = v_x;
uvs[uv_idx+1] = v_z;
}
if (std::abs(n_z) > std::abs(n_x) && std::abs(n_z) > std::abs(n_y)) {
uvs[uv_idx] = v_x;
uvs[uv_idx+1] = v_y;
}
}
return uvs;
}
void ColladaSerializer::ColladaExporter::ColladaGeometries::addFloatSource(const std::string& mesh_id,
const std::string& suffix, const std::vector<real_t>& floats, const char* coords /* = "XYZ" */)
{
@@ -89,20 +60,21 @@ void ColladaSerializer::ColladaExporter::ColladaGeometries::addFloatSource(const
void ColladaSerializer::ColladaExporter::ColladaGeometries::write(
const std::string &mesh_id, const std::string& default_material_name, const std::vector<real_t>& positions,
const std::vector<real_t>& normals, const std::vector<int>& faces, const std::vector<int>& edges,
const std::vector<int> material_ids, const std::vector<IfcGeom::Material>& materials)
const std::vector<int> material_ids, const std::vector<IfcGeom::Material>& materials,
const std::vector<real_t>& uvs)
{
openMesh(mesh_id);
// The normals vector can be empty for example when the WELD_VERTICES setting is used.
// IfcOpenShell does not provide them with multiple face normals collapsed into a single vertex.
const bool has_normals = !normals.empty();
const bool generate_uvs = (has_normals && serializer->settings().get(IfcGeom::IteratorSettings::GENERATE_UVS));
const bool has_uvs = !uvs.empty();
addFloatSource(mesh_id, COLLADASW::LibraryGeometries::POSITIONS_SOURCE_ID_SUFFIX, positions);
if (has_normals) {
addFloatSource(mesh_id, COLLADASW::LibraryGeometries::NORMALS_SOURCE_ID_SUFFIX, normals);
if (generate_uvs) {
addFloatSource(mesh_id, COLLADASW::LibraryGeometries::TEXCOORDS_SOURCE_ID_SUFFIX, box_project_uvs(positions, normals), "UV");
if (has_uvs) {
addFloatSource(mesh_id, COLLADASW::LibraryGeometries::TEXCOORDS_SOURCE_ID_SUFFIX, uvs, "UV");
}
}
@@ -129,13 +101,13 @@ void ColladaSerializer::ColladaExporter::ColladaGeometries::write(
if (has_normals) {
triangles.getInputList().push_back(COLLADASW::Input(COLLADASW::InputSemantic::NORMAL,"#" + mesh_id + COLLADASW::LibraryGeometries::NORMALS_SOURCE_ID_SUFFIX, offset++));
}
if (generate_uvs) {
if (has_uvs) {
triangles.getInputList().push_back(COLLADASW::Input(COLLADASW::InputSemantic::TEXCOORD,"#" + mesh_id + COLLADASW::LibraryGeometries::TEXCOORDS_SOURCE_ID_SUFFIX, offset++));
}
triangles.prepareToAppendValues();
for (std::vector<int>::const_iterator jt = index_range_start; jt != it; ++jt) {
const int idx = *jt;
if (has_normals && generate_uvs) {
if (has_normals && has_uvs) {
triangles.appendValues(idx, idx, idx);
} else if(has_normals) {
triangles.appendValues(idx, idx);
@@ -318,13 +290,14 @@ void ColladaSerializer::ColladaExporter::startDocument(const std::string& unit_n
asset.add();
}
void ColladaSerializer::ColladaExporter::write(const std::string& unique_id, const std::string& type,
const std::vector<real_t>& matrix, const std::vector<real_t>& vertices, const std::vector<real_t>& normals,
const std::vector<int>& faces, const std::vector<int>& edges, const std::vector<int>& material_ids,
const std::vector<IfcGeom::Material>& _materials)
void ColladaSerializer::ColladaExporter::write(const IfcGeom::TriangulationElement<real_t>* o)
{
const IfcGeom::Representation::Triangulation<real_t>& mesh = o->geometry();
const std::string name = serializer->settings().get(IfcGeom::IteratorSettings::USE_ELEMENT_GUIDS) ?
o->guid() : (serializer->settings().get(IfcGeom::IteratorSettings::USE_ELEMENT_NAMES) ? o->name() : o->unique_id());
std::vector<std::string> material_references;
foreach(const IfcGeom::Material& material, _materials) {
foreach(const IfcGeom::Material& material, mesh.materials()) {
if (!materials.contains(material)) {
materials.add(material);
}
@@ -333,7 +306,11 @@ void ColladaSerializer::ColladaExporter::write(const std::string& unique_id, con
collada_id(material_name);
material_references.push_back(material_name);
}
deferreds.push_back(DeferredObject(unique_id, type, matrix, vertices, normals, faces, edges, material_ids, _materials, material_references));
deferreds.push_back(
DeferredObject(name, o->type(), o->transformation().matrix().data(), mesh.verts(), mesh.normals(),
mesh.faces(), mesh.edges(), mesh.material_ids(), mesh.materials(), material_references, mesh.uvs())
);
}
void ColladaSerializer::ColladaExporter::endDocument() {
@@ -342,7 +319,7 @@ void ColladaSerializer::ColladaExporter::endDocument() {
materials.write();
for (std::vector<DeferredObject>::const_iterator it = deferreds.begin(); it != deferreds.end(); ++it) {
const std::string object_name = it->unique_id + "-representation";
geometries.write(object_name, it->type, it->vertices, it->normals, it->faces, it->edges, it->material_ids, it->materials);
geometries.write(object_name, it->type, it->vertices, it->normals, it->faces, it->edges, it->material_ids, it->materials, it->uvs);
}
geometries.close();
for (std::vector<DeferredObject>::const_iterator it = deferreds.begin(); it != deferreds.end(); ++it) {
@@ -362,11 +339,7 @@ void ColladaSerializer::writeHeader() {
}
void ColladaSerializer::write(const IfcGeom::TriangulationElement<real_t>* o) {
const IfcGeom::Representation::Triangulation<real_t>& mesh = o->geometry();
const std::string name = settings().get(IfcGeom::IteratorSettings::USE_ELEMENT_GUIDS) ?
o->guid() : (settings().get(IfcGeom::IteratorSettings::USE_ELEMENT_NAMES) ? o->name() : o->unique_id());
exporter.write(name, o->type(), o->transformation().matrix().data(), mesh.verts(),
mesh.normals(), mesh.faces(), mesh.edges(), mesh.material_ids(), mesh.materials());
exporter.write(o);
}
void ColladaSerializer::finalize() {
+6 -5
View File
@@ -60,7 +60,8 @@ private:
void write(const std::string &mesh_id, const std::string& default_material_name,
const std::vector<real_t>& positions, const std::vector<real_t>& normals,
const std::vector<int>& faces, const std::vector<int>& edges,
const std::vector<int> material_ids, const std::vector<IfcGeom::Material>& materials);
const std::vector<int> material_ids, const std::vector<IfcGeom::Material>& materials,
const std::vector<real_t>& uvs);
void close();
ColladaSerializer *serializer;
};
@@ -125,10 +126,11 @@ private:
std::vector<int> material_ids;
std::vector<IfcGeom::Material> materials;
std::vector<std::string> material_references;
std::vector<real_t> uvs;
DeferredObject(const std::string& unique_id, const std::string& type, const std::vector<real_t>& matrix,
const std::vector<real_t>& vertices, const std::vector<real_t>& normals, const std::vector<int>& faces,
const std::vector<int>& edges, const std::vector<int>& material_ids, const std::vector<IfcGeom::Material>& materials,
const std::vector<std::string>& material_references)
const std::vector<std::string>& material_references, const std::vector<real_t>& uvs)
: unique_id(unique_id)
, type(type)
, matrix(matrix)
@@ -139,6 +141,7 @@ private:
, material_ids(material_ids)
, materials(materials)
, material_references(material_references)
, uvs(uvs)
{}
};
COLLADABU::NativeString filename;
@@ -160,9 +163,7 @@ private:
std::vector<DeferredObject> deferreds;
virtual ~ColladaExporter() {}
void startDocument(const std::string& unit_name, float unit_magnitude);
void write(const std::string& unique_id, const std::string& type, const std::vector<real_t>& matrix,
const std::vector<real_t>& vertices, const std::vector<real_t>& normals, const std::vector<int>& faces,
const std::vector<int>& edges, const std::vector<int>& material_ids, const std::vector<IfcGeom::Material>& materials);
void write(const IfcGeom::TriangulationElement<real_t>* o);
void endDocument();
};
ColladaExporter exporter;
+10 -11
View File
@@ -177,7 +177,10 @@ int main(int argc, char** argv) {
"in instances where you're going to recompute normals for the exported "
"model in other modelling application in any case.")
("deflection-tolerance", boost::program_options::value<double>(&deflection_tolerance),
"Sets the deflection tolerance of the mesher, 1e-3 by default if not specified.");
"Sets the deflection tolerance of the mesher, 1e-3 by default if not specified.")
("generate-uvs",
"Generates UVs (texture coordinates) by using simple box projection. Requires normals. "
"Not guaranteed to work properly if used with --weld-vertices.");
std::string bounds;
boost::program_options::options_description serializer_options("Serialization options");
@@ -196,10 +199,7 @@ int main(int argc, char** argv) {
"Applicable for OBJ and DAE output.")
("center-model",
"Centers the models upon serialization by applying the center point of "
"the scene bounds as an offset. Applicable only for DAE output currently.")
("generate-uvs",
"Generates UVs (texture coordinates) by using simple box projection. Requires normals. Not guaranteed to work "
"properly if used with --weld-vertices. Applicable only for DAE output currently.");
"the scene bounds as an offset. Applicable only for DAE output currently.");
boost::program_options::options_description cmdline_options;
cmdline_options.add(generic_options).add(fileio_options).add(geom_options).add(serializer_options);
@@ -369,7 +369,7 @@ int main(int argc, char** argv) {
static_cast<SvgSerializer*>(serializer)->setBoundingRectangle(*bounding_width, *bounding_height);
}
} else {
Logger::Message(Logger::LOG_ERROR, "Unknown output filename extension");
Logger::Message(Logger::LOG_ERROR, "Unknown output filename extension '" + output_extension + "'");
write_log();
print_usage();
return 1;
@@ -381,16 +381,15 @@ int main(int argc, char** argv) {
settings.set(IfcGeom::IteratorSettings::CENTER_MODEL, false);
center_model = false;
}
if (generate_uvs) {
Logger::Message(Logger::LOG_NOTICE, "--generate-uvs setting ignored for non-DAE output");
settings.set(IfcGeom::IteratorSettings::GENERATE_UVS, false);
}
}
if (!serializer->isTesselated()) {
if (weld_vertices) {
Logger::Message(Logger::LOG_NOTICE, "Weld vertices setting ignored when writing STEP or IGES files");
Logger::Message(Logger::LOG_NOTICE, "Weld vertices setting ignored when writing non-tesselated output");
}
if (generate_uvs) {
Logger::Message(Logger::LOG_NOTICE, "Generate UVs setting ignored when writing non-tesselated output");
}
settings.set(IfcGeom::IteratorSettings::DISABLE_TRIANGULATION, true);
}
+11 -1
View File
@@ -22,6 +22,7 @@
#include "../ifcgeom/IfcGeomRenderStyles.h"
#include <boost/lexical_cast.hpp>
#include <iomanip>
bool WaveFrontOBJSerializer::ready() {
@@ -96,9 +97,16 @@ void WaveFrontOBJSerializer::write(const IfcGeom::TriangulationElement<real_t>*
obj_stream << "vn " << x << " " << y << " " << z << "\n";
}
for (std::vector<real_t>::const_iterator it = mesh.uvs().begin(); it != mesh.uvs().end();) {
const real_t u = *it++;
const real_t v = *it++;
obj_stream << "vt " << u << " " << v << "\n";
}
int previous_material_id = -2;
std::vector<int>::const_iterator material_it = mesh.material_ids().begin();
const bool has_uvs = !mesh.uvs().empty();
for ( std::vector<int>::const_iterator it = mesh.faces().begin(); it != mesh.faces().end(); ) {
const int material_id = *(material_it++);
@@ -118,7 +126,9 @@ void WaveFrontOBJSerializer::write(const IfcGeom::TriangulationElement<real_t>*
const int v1 = *(it++)+vcount_total;
const int v2 = *(it++)+vcount_total;
const int v3 = *(it++)+vcount_total;
obj_stream << "f " << v1 << "//" << v1 << " " << v2 << "//" << v2 << " " << v3 << "//" << v3 << "\n";
obj_stream << "f " << v1 << "/" << (has_uvs ? boost::lexical_cast<std::string>(v1) : "") << "/" << v1 << " "
<< v2 << "/" << (has_uvs ? boost::lexical_cast<std::string>(v2) : "") << "/" << v2 << " "
<< v3 << "/" << (has_uvs ? boost::lexical_cast<std::string>(v3) : "") << "/" << v3 << "\n";
}
+38
View File
@@ -102,6 +102,7 @@ namespace IfcGeom {
std::vector<int> _faces;
std::vector<int> _edges;
std::vector<P> _normals;
std::vector<P> uvs_;
std::vector<int> _material_ids;
std::vector<Material> _materials;
VertexKeyMap welds;
@@ -112,8 +113,10 @@ namespace IfcGeom {
const std::vector<int>& faces() const { return _faces; }
const std::vector<int>& edges() const { return _edges; }
const std::vector<P>& normals() const { return _normals; }
const std::vector<P>& uvs() const { return uvs_; }
const std::vector<int>& material_ids() const { return _material_ids; }
const std::vector<Material>& materials() const { return _materials; }
Triangulation(const BRep& shape_model)
: Representation(shape_model.settings())
, _id(shape_model.getId())
@@ -246,6 +249,10 @@ namespace IfcGeom {
}
}
if (!_normals.empty() && settings().get(IfcGeom::IteratorSettings::GENERATE_UVS)) {
uvs_ = box_project_uvs(_verts, _normals);
}
if (num_faces == 0) {
// Edges are only emitted if there are no faces. A mixed representation of faces
// and loose edges is discouraged by the standard. An alternative would be to use
@@ -276,6 +283,37 @@ namespace IfcGeom {
}
}
virtual ~Triangulation() {}
/// Generates UVs for a single mesh using box projection.
/// @todo Very simple impl. Assumes that input vertices and normals match 1:1.
static std::vector<P> box_project_uvs(const std::vector<P> &vertices, const std::vector<P> &normals)
{
std::vector<P> uvs;
uvs.resize(vertices.size() / 3 * 2);
for (size_t uv_idx = 0, v_idx = 0;
uv_idx < uvs.size() && v_idx < vertices.size() && v_idx < normals.size();
uv_idx += 2, v_idx += 3) {
P n_x = normals[v_idx], n_y = normals[v_idx + 1], n_z = normals[v_idx + 2];
P v_x = vertices[v_idx], v_y = vertices[v_idx + 1], v_z = vertices[v_idx + 2];
if (std::abs(n_x) > std::abs(n_y) && std::abs(n_x) > std::abs(n_z)) {
uvs[uv_idx] = v_z;
uvs[uv_idx + 1] = v_y;
}
if (std::abs(n_y) > std::abs(n_x) && std::abs(n_y) > std::abs(n_z)) {
uvs[uv_idx] = v_x;
uvs[uv_idx + 1] = v_z;
}
if (std::abs(n_z) > std::abs(n_x) && std::abs(n_z) > std::abs(n_y)) {
uvs[uv_idx] = v_x;
uvs[uv_idx + 1] = v_y;
}
}
return uvs;
}
private:
// Welds vertices that belong to different faces
int addVertex(int material_index, const gp_XYZ& p) {