Skip to content
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

added read/write support of OFF files to vdb_tool #1971

Merged
merged 20 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
9 changes: 5 additions & 4 deletions openvdb_cmd/vdb_tool/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ This command-line tool, dubbed vdb_tool, can combine any number of the of high-l
| **eval** | Evaluate an expression written in our Reverse Polish Notation (see below) |
| **config** | Load a configuration file and add the actions for processing |
| **default** | Set default values used by all subsequent actions |
| **read** | Read mesh, points and level sets as obj, ply, abc, stl, pts, vdb or nvdb files |
| **write** | Write a polygon mesh, points or level set as a obj, ply, stl, abc or vdb file |
| **read** | Read mesh, points and level sets as obj, ply, abc, stl, off, pts, vdb or nvdb files |
| **write** | Write a polygon mesh, points or level set as a obj, ply, stl, off, abc or vdb file |
| **vdb2points** | Extracts points from a VDB grid |
| **mesh2ls** | Convert a polygon mesh to a narrow-band level set |
| **points2ls** | Convert points into a narrow-band level set |
Expand Down Expand Up @@ -59,9 +59,10 @@ For support, bug-reports or ideas for improvements please contact ken.museth@gma
| Extension | Actions | Description |
|-------|-------|-------|
| vdb | read and write | OpenVDB sparse volume files with float, Vec3f and points |
| obj | read and write | ASCII OBJ mesh files with triangle, quad or points |
| ply | read and write | Binary and ASCII PLY mesh files with triangle, quad or points |
| obj | read and write | ASCII OBJ mesh files with triangles, quads or points |
| ply | read and write | Binary and ASCII PLY mesh files with triangles, quads or points |
| stl | read and write | Binary STL mesh files with triangles |
| off | read and write | ASCI OFF mesh files with triangles, quads or points |
| pts | read | ASCII PTS points files with one or more point clouds |
| abc | optional read and write | Alembic binary mesh files |
| nvdb| optional read and write | NanoVDB file with voxels or points |
Expand Down
93 changes: 82 additions & 11 deletions openvdb_cmd/vdb_tool/include/Geometry.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,17 +104,20 @@ class Geometry
// Reads all the vertices in the file and treats them as Geometry
void write(const std::string &fileName) const;
void writeOBJ(const std::string &fileName) const;
void writeOFF(const std::string &fileName) const;
void writePLY(const std::string &fileName) const;
void writeSTL(const std::string &fileName) const;
void writeGEO(const std::string &fileName) const;
void writeABC(const std::string &fileName) const;

void writeOBJ(std::ostream &os) const;
void writeOFF(std::ostream &os) const;
void writePLY(std::ostream &os) const;
void writeSTL(std::ostream &os) const;

void read(const std::string &fileName);
void readOBJ(const std::string &fileName);
void readOFF(const std::string &fileName);
void readPLY(const std::string &fileName);
void readSTL(const std::string &fileName);
void readPTS(const std::string &fileName);
Expand All @@ -125,6 +128,7 @@ class Geometry
void readNVDB(const std::string &fileName);

void readOBJ(std::istream &is);
void readOFF(std::istream &is);
void readPLY(std::istream &is);

size_t vtxCount() const { return mVtx.size(); }
Expand Down Expand Up @@ -229,7 +233,7 @@ const math::BBox<Vec3s>& Geometry::bbox() const

void Geometry::write(const std::string &fileName) const
{
switch (findFileExt(fileName, {"geo", "obj", "ply", "stl", "abc"})) {
switch (findFileExt(fileName, {"geo", "obj", "ply", "stl", "abc", "off"})) {
case 1:
this->writeGEO(fileName);
break;
Expand All @@ -245,6 +249,9 @@ void Geometry::write(const std::string &fileName) const
case 5:
this->writeABC(fileName);
break;
case 6:
this->writeOFF(fileName);
break;
default:
throw std::invalid_argument("Geometry file \"" + fileName + "\" has an invalid extension");
}
Expand Down Expand Up @@ -323,17 +330,32 @@ void Geometry::writeOBJ(const std::string &fileName) const
void Geometry::writeOBJ(std::ostream &os) const
{
os << "# Created by vdb_tool\n";
for (auto &v : mVtx) {
os << "v " << v[0] << " " << v[1] << " " << v[2] << "\n";
}
for (auto &t : mTri) {
os << "f " << t[0]+1 << " " << t[1]+1 << " " << t[2]+1 << "\n";// obj is 1-based
}
for (auto &q : mQuad) {
os << "f " << q[0]+1 << " " << q[1]+1 << " " << q[2]+1 << " " << q[3]+1 << "\n";// obj is 1-based
}
for (auto &v : mVtx) os << "v " << v[0] << " " << v[1] << " " << v[2] << "\n";
for (auto &t : mTri) os << "f " << t[0]+1 << " " << t[1]+1 << " " << t[2]+1 << "\n";// obj is 1-based
for (auto &q : mQuad) os << "f " << q[0]+1 << " " << q[1]+1 << " " << q[2]+1 << " " << q[3]+1 << "\n";// obj is 1-based
}// Geometry::writeOBJ

void Geometry::writeOFF(const std::string &fileName) const
{
if (fileName=="stdout.off") {
this->writeOFF(std::cout);
} else {
std::ofstream outfile(fileName);
if (!outfile.is_open()) throw std::invalid_argument("Error writing to off file \""+fileName+"\"");
this->writeOFF(outfile);;
}
}// Geometry::writeOFF

void Geometry::writeOFF(std::ostream &os) const
{
os << "OFF\n";
os << "# Created by vdb_tool\n";
os << mVtx.size() << " " << (mTri.size() + mQuad.size()) << " " << 0 << "\n";
for (auto &v : mVtx) os << v[0] << " " << v[1] << " " << v[2] << "\n";
for (auto &t : mTri) os << "3 " << t[0] << " " << t[1] << " " << t[2] << "\n";
for (auto &q : mQuad) os << "4 " << q[0] << " " << q[1] << " " << q[2] << " " << q[3] << "\n";
}// Geometry::writeOFF

void Geometry::writeSTL(const std::string &fileName) const
{
if (fileName == "stdout.stl") {
Expand Down Expand Up @@ -381,7 +403,7 @@ void Geometry::writeGEO(const std::string &fileName) const

void Geometry::read(const std::string &fileName)
{
switch (findFileExt(fileName, {"obj", "ply", "pts", "stl", "abc", "vdb", "nvdb", "geo"})) {
switch (findFileExt(fileName, {"obj", "ply", "pts", "stl", "abc", "vdb", "nvdb", "geo", "off"})) {
case 1:
this->readOBJ(fileName);
break;
Expand All @@ -406,6 +428,9 @@ void Geometry::read(const std::string &fileName)
case 8:
this->readGEO(fileName);
break;
case 9:
this->readOFF(fileName);
break;
default:
#if VDB_TOOL_USE_PDAL
pdal::StageFactory factory;
Expand Down Expand Up @@ -519,6 +544,52 @@ void Geometry::readPDAL(const std::string &fileName)
mBBox = BBoxT(); //invalidate BBox
}// Geometry::readPDAL

void Geometry::readOFF(const std::string &fileName)
{
if (fileName == "stdin.off") {
this->readOFF(std::cin);
} else {
std::ifstream infile(fileName);
if (!infile.is_open()) throw std::invalid_argument("Error opening Geometry file \""+fileName+"\"");
this->readOFF(infile);
}
}// Geometry::readOFF

void Geometry::readOFF(std::istream &is)
{
std::string line;
std::getline(is, line);
if (line!="OFF") throw std::invalid_argument("Geometry::readOFF: expected header \"OFF\" but read \"" + line + "\"");
int vtxCount=0, faceCount=0, edgeCount=0;
while (vtxCount==0 && std::getline(is, line)) {
if (line[0]=='#') continue;
std::istringstream iss(line);
iss >> vtxCount >> faceCount >> edgeCount;
}
Vec3f p;
for (int i=0; i<vtxCount && std::getline(is, line); ++i) {
kmuseth marked this conversation as resolved.
Show resolved Hide resolved
std::istringstream iss(line);
iss >> p[0] >> p[1] >> p[2];
mVtx.push_back(p);
}
int f[4];
for (int i=0; i<faceCount && std::getline(is, line); ++i) {
std::istringstream iss(line);
int n=0;
iss >> n;
if (n==3) {
iss >> f[0] >> f[1] >> f[2];
mTri.emplace_back(f[0],f[1],f[2]);
} else if(n==4) {
iss >> f[0] >> f[1] >> f[2] >> f[3];
mQuad.emplace_back(f[0],f[1],f[2],f[3]);
} else {
throw std::invalid_argument("Geometry::readOFF: " + std::to_string(n) + "-gons are not supported");
}
}
mBBox = BBoxT();//invalidate BBox
}// Geometry::readOFF

void Geometry::readPLY(const std::string &fileName)
{
if (fileName == "stdin.ply") {
Expand Down
12 changes: 6 additions & 6 deletions openvdb_cmd/vdb_tool/include/Tool.h
Original file line number Diff line number Diff line change
Expand Up @@ -369,14 +369,14 @@ void Tool::init()

mParser.addAction(
"read", "i", "Read one or more geometry or VDB files from disk or STDIN.",
{{"files", "", "{file|stdin}.{abc|obj|ply|stl|vdb}", "list of files or the input stream, e.g. file.vdb,stdin.vdb. Note that \"files=\" is optional since any argument without \"=\" is intrepreted as a file and appended to \"files\""},
{{"files", "", "{file|stdin}.{abc|obj|ply|stl|off|vdb}", "list of files or the input stream, e.g. file.vdb,stdin.vdb. Note that \"files=\" is optional since any argument without \"=\" is intrepreted as a file and appended to \"files\""},
{"grids", "*", "*|grid_name,...", "list of VDB grids name to be imported (defaults to \"*\", i.e. import all available grids)"},
{"delayed", "true", "1|0|true|false", "toggle delayed loading of VDB grids (enabled by default). This option is ignored by other file types"}},
[](){}, [&](){this->read();}, 0);// anonymous options are treated as to the first option,i.e. "files"

mParser.addAction(
"write", "o", "Write list of geometry, VDB or config files to disk or STDOUT",
{{"files", "", "{file|stdout}.{obj|ply|stl|vdb|nvdb}", "list of files or the output stream, e.g. file.vdb or stdin.vdb. Note that \"files=\" is optional since any argument without the \"=\" character is intrepreted as a file and appended to \"files\"."},
{{"files", "", "{file|stdout}.{obj|ply|stl|off|vdb|nvdb}", "list of files or the output stream, e.g. file.vdb or stdin.vdb. Note that \"files=\" is optional since any argument without the \"=\" character is intrepreted as a file and appended to \"files\"."},
{"geo", "0", "0|1...", "geometry to write (defaults to \"0\" which is the latest)."},
{"vdb", "*", "0,1,...", "list of VDB grids to write (defaults to \"*\", i.e. all available grids)."},
{"keep", "", "1|0|true|false", "toggle wether to preserved or deleted geometry and grids after they have been written."},
Expand Down Expand Up @@ -925,8 +925,8 @@ std::string Tool::examples() const
{
const int w = 16;
std::stringstream ss;
ss << std::left << std::setw(w) << "Surface points:" << mCmdName << " -read points.[obj/ply/stl/pts] -points2ls d=256 r=2.0 w=3 -dilate r=2 -gauss i=1 w=1 -erode r=2 -ls2m a=0.25 -write output.[ply/obj/stl]\n";
ss << std::left << std::setw(w) << "Convert mesh: " << mCmdName << " -read mesh.[ply/obj] -mesh2ls d=256 -write output.vdb config.txt\n";
ss << std::left << std::setw(w) << "Surface points:" << mCmdName << " -read points.[obj/ply/stl/off/pts] -points2ls d=256 r=2.0 w=3 -dilate r=2 -gauss i=1 w=1 -erode r=2 -ls2m a=0.25 -write output.[ply/obj/stl]\n";
ss << std::left << std::setw(w) << "Convert mesh: " << mCmdName << " -read mesh.[ply/obj/off] -mesh2ls d=256 -write output.vdb config.txt\n";
ss << std::left << std::setw(w) << "Config example:" << mCmdName << " -config config.txt\n";
return ss.str();
}
Expand Down Expand Up @@ -963,7 +963,7 @@ void Tool::read()
{
OPENVDB_ASSERT(mParser.getAction().name == "read");
for (auto &fileName : mParser.getVec<std::string>("files")) {
switch (findFileExt(fileName, {"geo,obj,ply,abc,pts,stl", "vdb", "nvdb"})) {
switch (findFileExt(fileName, {"geo,obj,ply,abc,pts,off,stl", "vdb", "nvdb"})) {
case 1:
this->readGeo(fileName);
break;
Expand Down Expand Up @@ -1135,7 +1135,7 @@ void Tool::write()
{
OPENVDB_ASSERT(mParser.getAction().name == "write");
for (std::string &fileName : mParser.getVec<std::string>("files")) {
switch (findFileExt(fileName, {"geo,obj,ply,stl,abc", "vdb", "nvdb", "txt"})) {
switch (findFileExt(fileName, {"geo,obj,ply,stl,off,abc", "vdb", "nvdb", "txt"})) {
case 1:
this->writeGeo(fileName);
break;
Expand Down
65 changes: 42 additions & 23 deletions openvdb_cmd/vdb_tool/src/unittest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -371,18 +371,18 @@ TEST_F(Test_vdb_tool, Geometry)
EXPECT_EQ(4, geo2.vtxCount());
EXPECT_EQ(2, geo2.triCount());
EXPECT_EQ(1, geo2.quadCount());
EXPECT_EQ(openvdb::Vec3f(1,2,3), geo.bbox().min());
EXPECT_EQ(openvdb::Vec3f(10,11,12), geo.bbox().max());
EXPECT_EQ(openvdb::Vec3f(1,2,3), geo2.bbox().min());
EXPECT_EQ(openvdb::Vec3f(10,11,12), geo2.bbox().max());

EXPECT_EQ(openvdb::Vec3f(1,2,3), geo.vtx()[0]);
EXPECT_EQ(openvdb::Vec3f(4,5,6), geo.vtx()[1]);
EXPECT_EQ(openvdb::Vec3f(7,8,9), geo.vtx()[2]);
EXPECT_EQ(openvdb::Vec3f(10,11,12), geo.vtx()[3]);
EXPECT_EQ(openvdb::Vec3f(1,2,3), geo2.vtx()[0]);
EXPECT_EQ(openvdb::Vec3f(4,5,6), geo2.vtx()[1]);
EXPECT_EQ(openvdb::Vec3f(7,8,9), geo2.vtx()[2]);
EXPECT_EQ(openvdb::Vec3f(10,11,12), geo2.vtx()[3]);

EXPECT_EQ(openvdb::Vec3I(0,1,2), geo.tri()[0]);
EXPECT_EQ(openvdb::Vec3I(1,2,3), geo.tri()[1]);
EXPECT_EQ(openvdb::Vec3I(0,1,2), geo2.tri()[0]);
EXPECT_EQ(openvdb::Vec3I(1,2,3), geo2.tri()[1]);

EXPECT_EQ(openvdb::Vec4I(0,1,2,3), geo.quad()[0]);
EXPECT_EQ(openvdb::Vec4I(0,1,2,3), geo2.quad()[0]);
}
{// write to file
std::ofstream os("data/test.geo", std::ios_base::binary);
Expand All @@ -395,18 +395,38 @@ TEST_F(Test_vdb_tool, Geometry)
EXPECT_EQ(4, geo2.vtxCount());
EXPECT_EQ(2, geo2.triCount());
EXPECT_EQ(1, geo2.quadCount());
EXPECT_EQ(openvdb::Vec3f(1,2,3), geo.bbox().min());
EXPECT_EQ(openvdb::Vec3f(10,11,12), geo.bbox().max());
EXPECT_EQ(openvdb::Vec3f(1,2,3), geo2.bbox().min());
EXPECT_EQ(openvdb::Vec3f(10,11,12), geo2.bbox().max());

EXPECT_EQ(openvdb::Vec3f(1,2,3), geo.vtx()[0]);
EXPECT_EQ(openvdb::Vec3f(4,5,6), geo.vtx()[1]);
EXPECT_EQ(openvdb::Vec3f(7,8,9), geo.vtx()[2]);
EXPECT_EQ(openvdb::Vec3f(10,11,12), geo.vtx()[3]);
EXPECT_EQ(openvdb::Vec3f(1,2,3), geo2.vtx()[0]);
EXPECT_EQ(openvdb::Vec3f(4,5,6), geo2.vtx()[1]);
EXPECT_EQ(openvdb::Vec3f(7,8,9), geo2.vtx()[2]);
EXPECT_EQ(openvdb::Vec3f(10,11,12), geo2.vtx()[3]);

EXPECT_EQ(openvdb::Vec3I(0,1,2), geo.tri()[0]);
EXPECT_EQ(openvdb::Vec3I(1,2,3), geo.tri()[1]);
EXPECT_EQ(openvdb::Vec3I(0,1,2), geo2.tri()[0]);
EXPECT_EQ(openvdb::Vec3I(1,2,3), geo2.tri()[1]);

EXPECT_EQ(openvdb::Vec4I(0,1,2,3), geo.quad()[0]);
EXPECT_EQ(openvdb::Vec4I(0,1,2,3), geo2.quad()[0]);
}
{// test readOFF and writeOFF
geo.write("data/test.off");
openvdb::vdb_tool::Geometry geo2;
EXPECT_TRUE(geo2.read("data/test.off"));
EXPECT_EQ(4, geo2.vtxCount());
EXPECT_EQ(2, geo2.triCount());
EXPECT_EQ(1, geo2.quadCount());
EXPECT_EQ(openvdb::Vec3f(1,2,3), geo2.bbox().min());
EXPECT_EQ(openvdb::Vec3f(10,11,12), geo2.bbox().max());

EXPECT_EQ(openvdb::Vec3f(1,2,3), geo2.vtx()[0]);
EXPECT_EQ(openvdb::Vec3f(4,5,6), geo2.vtx()[1]);
EXPECT_EQ(openvdb::Vec3f(7,8,9), geo2.vtx()[2]);
EXPECT_EQ(openvdb::Vec3f(10,11,12), geo2.vtx()[3]);

EXPECT_EQ(openvdb::Vec3I(0,1,2), geo2.tri()[0]);
EXPECT_EQ(openvdb::Vec3I(1,2,3), geo2.tri()[1]);

EXPECT_EQ(openvdb::Vec4I(0,1,2,3), geo2.quad()[0]);
}
#ifdef VDB_TOOL_USE_PDAL
{// read from PDAL-supported ASCII format file
Expand All @@ -425,11 +445,10 @@ TEST_F(Test_vdb_tool, Geometry)
geo2.read("data/test.txt");
EXPECT_EQ(4, geo2.vtxCount());

EXPECT_EQ(openvdb::Vec3f(1,2,3), geo.vtx()[0]);
EXPECT_EQ(openvdb::Vec3f(4,5,6), geo.vtx()[1]);
EXPECT_EQ(openvdb::Vec3f(7,8,9), geo.vtx()[2]);
EXPECT_EQ(openvdb::Vec3f(10,11,12), geo.vtx()[3]);

EXPECT_EQ(openvdb::Vec3f(1,2,3), geo2.vtx()[0]);
EXPECT_EQ(openvdb::Vec3f(4,5,6), geo2.vtx()[1]);
EXPECT_EQ(openvdb::Vec3f(7,8,9), geo2.vtx()[2]);
EXPECT_EQ(openvdb::Vec3f(10,11,12), geo2.vtx()[3]);
}
#endif
}// Geometry
Expand Down
1 change: 1 addition & 0 deletions pendingchanges/vdb_tool.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
added read and write support for OFF (Object File Format) files to vdb_tool
Loading