Skip to content

feat: read support for vn and vt added for OBJ file format #8857

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 58 additions & 22 deletions Stream_support/include/CGAL/IO/OBJ.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
#include <boost/range/value_type.hpp>
#include <CGAL/Named_function_parameters.h>

#include <CGAL/Kernel_traits.h>

#include <algorithm>
#include <fstream>
#include <iostream>
Expand All @@ -41,14 +43,20 @@ namespace CGAL {
namespace IO {
namespace internal {

template <typename PointRange, typename PolygonRange, typename VertexNormalOutputIterator, typename VertexTextureOutputIterator>
template <typename PointRange,
typename PolygonRange,
typename VertexNormalOutputIterator,
typename VertexTextureOutputIterator,
typename VertexNormalIndexOutputIterator,
typename VertexTextureIndexOutputIterator>
bool read_OBJ(std::istream& is,
PointRange& points,
PolygonRange& polygons,
VertexNormalOutputIterator,
VertexTextureOutputIterator,
const bool verbose = false)
{
VertexNormalOutputIterator vn_out,
VertexTextureOutputIterator vt_out,
VertexNormalIndexOutputIterator vn_id_out,
VertexTextureIndexOutputIterator vt_id_out,
const bool verbose = false) {
if(!is.good())
{
if(verbose)
Expand All @@ -57,13 +65,20 @@ bool read_OBJ(std::istream& is,
}

typedef typename boost::range_value<PointRange>::type Point;
typedef typename CGAL::Kernel_traits<Point>::Kernel Kernel;
typedef typename Kernel::Point_2 Texture;
typedef typename Kernel::Vector_3 Normal;
typedef typename Kernel::FT FT;

set_ascii_mode(is); // obj is ASCII only

int mini(1), maxi(-1);
std::string s;
Point p;

int normal_count = 0;
int texture_count = 0;

std::string line;
bool tex_found(false), norm_found(false);
while(getline(is, line))
Expand Down Expand Up @@ -107,39 +122,61 @@ bool read_OBJ(std::istream& is,
else if(s == "vt")
{
tex_found = true;
texture_count++;
double u = 0.0, v = 0.0;
iss >> u >> v;
*vt_out++ = Texture{FT(u), FT(v)};
}
else if(s == "vn")
{
norm_found = true;
normal_count++;
double nx = 0.0;
double ny = 0.0;
double nz = 0.0;
iss >> nx >> ny >> nz;
*vn_out++ = Normal{FT(nx), FT(ny), FT(nz)};
}
else if(s == "f")
{
int i;
std::string vertex_token;
polygons.emplace_back();
while(iss >> i)
while(iss >> vertex_token)
{
if(i < 1)
{
std::istringstream token_stream(vertex_token);
std::string v_str, vt_str, vn_str;

std::getline(token_stream, v_str, '/');
std::getline(token_stream, vt_str, '/');
std::getline(token_stream, vn_str, '/');

int i = std::stoi(v_str);

if(i < 1) {
const std::size_t n = polygons.back().size();
::CGAL::internal::resize(polygons.back(), n + 1);
polygons.back()[n] = static_cast<int>(points.size()) + i; // negative indices are relative references
if(i < mini)
mini = i;
}
else
{
} else {
const std::size_t n = polygons.back().size();
::CGAL::internal::resize(polygons.back(), n + 1);
polygons.back()[n] = i - 1;
if(i-1 > maxi)
maxi = i-1;
if(i - 1 > maxi)
maxi = i - 1;
}

// We read vt and vn in the given format and then we move on to next vertex
// Texture index
if(!vt_str.empty()) {
int vti = std::stoi(vt_str);
*vt_id_out++ = (vti < 0) ? static_cast<int>(texture_count) + vti : vti - 1;
}

// the format can be "f v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3 ..." and we only read vertex ids for now,
// so skip to the next vertex, but be tolerant about which whitespace is used
if (!std::isspace(iss.peek())) {
std::string ignoreme;
iss >> ignoreme;
// Normal index
if(!vn_str.empty()) {
int vni = std::stoi(vn_str);
*vn_id_out++ = (vni < 0) ? static_cast<int>(normal_count) + vni : vni - 1;
}
}

Expand Down Expand Up @@ -244,9 +281,8 @@ bool read_OBJ(std::istream& is,
{
const bool verbose = parameters::choose_parameter(parameters::get_parameter(np, internal_np::verbose), false);

return internal::read_OBJ(is, points, polygons,
CGAL::Emptyset_iterator(), CGAL::Emptyset_iterator(),
verbose);
return internal::read_OBJ(is, points, polygons, CGAL::Emptyset_iterator(), CGAL::Emptyset_iterator(),
CGAL::Emptyset_iterator(), CGAL::Emptyset_iterator(), verbose);
}

/// \ingroup PkgStreamSupportIoFuncsOBJ
Expand Down
20 changes: 20 additions & 0 deletions Stream_support/test/Stream_support/test_OBJ.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,19 @@
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point;
typedef std::vector<std::size_t> Face;
typedef Kernel::Point_2 Texture;
typedef Kernel::Vector_3 Normal;

int main(int argc, char** argv)
{
const char* obj_file = (argc > 1) ? argv[1] : "data/90089.obj";

std::vector<Point> points;
std::vector<Face> polygons;
std::vector<Normal> normals;
std::vector<Texture> UVcoords;
std::vector<int> normal_indices;
std::vector<int> texture_indices;

bool ok = CGAL::IO::read_OBJ(obj_file, points, polygons, CGAL::parameters::verbose(true));
assert(ok);
Expand All @@ -25,6 +31,20 @@ int main(int argc, char** argv)
if(argc == 1)
assert(points.size() == 434 && polygons.size() == 864);

points.clear();
polygons.clear();

// Check for internal read_OBJ with normal and texture coordinates reading capapbility
std::ifstream input(obj_file);
ok = CGAL::IO::internal::read_OBJ(input, points, polygons, std::back_inserter(normals), std::back_inserter(UVcoords),
std::back_inserter(normal_indices), std::back_inserter(texture_indices), true);
assert(ok);
std::cout << points.size() << " points " << polygons.size() << " polygons " << normals.size() << " normals "
<< UVcoords.size() << " texture coords" << std::endl;

if(argc == 1)
assert(points.size() == 434 && polygons.size() == 864 && normals.size() == 242 && UVcoords.size() == 627);

points.clear();
polygons.clear();
std::string obj_string(obj_file);
Expand Down