Skip to content

Commit

Permalink
added read/write support of OFF files to vdb_tool
Browse files Browse the repository at this point in the history
Signed-off-by: Ken Museth <[email protected]>
  • Loading branch information
kmuseth committed Nov 20, 2024
1 parent 58724e5 commit f073367
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 44 deletions.
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) {
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

0 comments on commit f073367

Please sign in to comment.