diff --git a/ci/install_windows.ps1 b/ci/install_windows.ps1 index 6054cb96bf..ce8d908951 100644 --- a/ci/install_windows.ps1 +++ b/ci/install_windows.ps1 @@ -7,6 +7,7 @@ $vcpkgPackages = @( "zlib", "libpng", "openexr", + "pdal", "tbb", "gtest", "cppunit", diff --git a/openvdb_cmd/vdb_tool/CMakeLists.txt b/openvdb_cmd/vdb_tool/CMakeLists.txt index f9b846b8aa..877fcd853d 100644 --- a/openvdb_cmd/vdb_tool/CMakeLists.txt +++ b/openvdb_cmd/vdb_tool/CMakeLists.txt @@ -34,6 +34,7 @@ option(OPENVDB_TOOL_NANO_USE_BLOSC "Compile NanoVDB with Blosc compression suppo option(OPENVDB_TOOL_USE_PNG "Compile with PNG support" OFF) option(OPENVDB_TOOL_USE_EXR "Compile with EXR support" OFF) option(OPENVDB_TOOL_USE_JPG "Compile with JPG support" OFF) +option(OPENVDB_TOOL_USE_PDAL "Compile with extended points support" OFF) option(OPENVDB_TOOL_USE_ABC "Compile with Alembic support" OFF) option(OPENVDB_TOOL_USE_ALL "Compile with all optional components" OFF) if(OPENVDB_TOOL_USE_ALL) @@ -42,6 +43,8 @@ if(OPENVDB_TOOL_USE_ALL) set(OPENVDB_TOOL_USE_EXR ON) set(OPENVDB_TOOL_USE_JPG ON) set(OPENVDB_TOOL_USE_ABC ON) + set(OPENVDB_TOOL_USE_PDAL ON) + endif() if(OPENVDB_TOOL_USE_NANO) @@ -83,6 +86,18 @@ if(OPENVDB_TOOL_USE_PNG) target_link_libraries(vdb_tool_common INTERFACE png) endif() +if(OPENVDB_TOOL_USE_PDAL) + target_compile_definitions(vdb_tool_common INTERFACE "VDB_TOOL_USE_PDAL") + if(WIN32) + find_package(libdpal CONFIG REQUIRED) + else() + find_package(PDAL REQUIRED) + endif() + message(STATUS "PDAL: ${PDAL_LIBRARIES} ${PDAL_INCLUDE_DIRS}") + target_link_libraries(vdb_tool_common INTERFACE ${PDAL_LIBRARIES}) + target_include_directories(vdb_tool_common INTERFACE ${PDAL_INCLUDE_DIRS}) +endif() + if(OPENVDB_TOOL_USE_JPG) target_compile_definitions(vdb_tool_common INTERFACE "VDB_TOOL_USE_JPG") find_package(JPEG REQUIRED) diff --git a/openvdb_cmd/vdb_tool/include/Geometry.h b/openvdb_cmd/vdb_tool/include/Geometry.h index 219a1dd4a7..2d6472dcde 100644 --- a/openvdb_cmd/vdb_tool/include/Geometry.h +++ b/openvdb_cmd/vdb_tool/include/Geometry.h @@ -45,6 +45,14 @@ #include #endif +#ifdef VDB_TOOL_USE_PDAL +#include "pdal/pdal.hpp" +#include "pdal/PipelineManager.hpp" +#include "pdal/PipelineReaderJSON.hpp" +#include "pdal/util/FileUtils.hpp" +#include +#endif + #if defined(_WIN32) #include #else @@ -82,9 +90,13 @@ class Geometry const std::vector& vtx() const { return mVtx; } const std::vector& tri() const { return mTri; } const std::vector& quad() const { return mQuad; } + const std::vector& rgb() const { return mRGB; } + std::vector& vtx() { return mVtx; } std::vector& tri() { return mTri; } std::vector& quad() { return mQuad; } + std::vector& rgb() { return mRGB; } + const BBoxT& bbox() const; void clear(); @@ -108,6 +120,7 @@ class Geometry void readPTS(const std::string &fileName); void readGEO(const std::string &fileName); void readABC(const std::string &fileName); + void readPDAL(const std::string &fileName); void readVDB(const std::string &fileName); void readNVDB(const std::string &fileName); @@ -138,6 +151,7 @@ class Geometry std::vector mVtx; std::vector mTri; std::vector mQuad; + std::vector mRGB; mutable BBoxT mBBox; std::string mName; @@ -393,8 +407,16 @@ void Geometry::read(const std::string &fileName) this->readGEO(fileName); break; default: - throw std::invalid_argument("Geometry::read: File \""+fileName+"\" has an invalid extension"); - break; +#if VDB_TOOL_USE_PDAL + pdal::StageFactory factory; + const std::string driver = factory.inferReaderDriver(fileName); + if (driver != "") { + this->readPDAL(fileName); + break; + } +#endif + throw std::invalid_argument("Geometry::read: File \""+fileName+"\" has an invalid extension"); + break; } }// Geometry::read @@ -442,6 +464,61 @@ void Geometry::readOBJ(std::istream &is) mBBox = BBoxT();//invalidate BBox }// Geometry::readOBJ +void Geometry::readPDAL(const std::string &fileName) +{ + #if VDB_TOOL_USE_PDAL + if (!pdal::FileUtils::fileExists(fileName)) throw std::invalid_argument("Error opening file \""+fileName+"\" - it doesn't exist!"); + + pdal::StageFactory factory; + std::string type = factory.inferReaderDriver(fileName); + std::string pipelineJson = R"({ + "pipeline" : [ + { + "type" : ")" + type + R"(", + "filename" : ")" + fileName + R"(" + } + ] + })"; + + Vec3f p; + Vec3s rgb; + try { + pdal::PipelineManager manager; + std::stringstream s(pipelineJson); + manager.readPipeline(s); + manager.execute(pdal::ExecMode::Standard); + + for (const std::shared_ptr& view : manager.views()) { + bool hasColor = false; + if (view->hasDim(pdal::Dimension::Id::Red) && view->hasDim(pdal::Dimension::Id::Green) && view->hasDim(pdal::Dimension::Id::Blue)) + hasColor = true; + for (const pdal::PointRef& point : *view) { + p[0] = point.getFieldAs(pdal::Dimension::Id::X); + p[1] = point.getFieldAs(pdal::Dimension::Id::Y); + p[2] = point.getFieldAs(pdal::Dimension::Id::Z); + mVtx.push_back(p); + if (hasColor) { + rgb[0] = point.getFieldAs(pdal::Dimension::Id::Red); + rgb[1] = point.getFieldAs(pdal::Dimension::Id::Green); + rgb[2] = point.getFieldAs(pdal::Dimension::Id::Blue); + mRGB.push_back(rgb); + } + } + } + + } + catch (const pdal::pdal_error& e) { + throw std::runtime_error("PDAL failed: " + std::string(e.what())); + } + catch (const std::exception& e) { + throw std::runtime_error("Reading file failed: " + std::string(e.what())); + } +#else + throw std::runtime_error("Cannot read file \"" + fileName + "\". PDAL support is not enabled in this build, please recompile with PDAL support"); +#endif + mBBox = BBoxT(); //invalidate BBox +}// Geometry::readPDAL + void Geometry::readPLY(const std::string &fileName) { if (fileName == "stdin.ply") { @@ -753,6 +830,8 @@ void Geometry::readPTS(const std::string &fileName) if (!infile.is_open()) throw std::runtime_error("Error opening particle file \""+fileName+"\""); std::string line; std::istringstream iss; + bool readColor = false; + Vec3s rgb; while(std::getline(infile, line)) { const size_t n = mVtx.size(), m = std::stoi(line); mVtx.resize(n + m); @@ -764,6 +843,18 @@ void Geometry::readPTS(const std::string &fileName) if (!(iss >> p[0] >> p[1] >> p[2])) {;//ignore intensity, r, g, b throw std::invalid_argument("Geometry::readPTS: error parsing line: \""+line+"\""); } + if (readColor) { + if (!(iss >> i) ) { // converting intensity to a multiplier on rgb might be appropriate, but i can't find a good spec for it + readColor = false; + continue; + } + if (!(iss >> rgb[0] >> rgb[1] >> rgb[2])) { + readColor = false; + continue; + } + mRGB.push_back(rgb/255.0); + } + }// loop over points }// loop over scans mBBox = BBoxT();//invalidate BBox diff --git a/openvdb_cmd/vdb_tool/include/Tool.h b/openvdb_cmd/vdb_tool/include/Tool.h index 9fc7745f40..5e4cae6899 100644 --- a/openvdb_cmd/vdb_tool/include/Tool.h +++ b/openvdb_cmd/vdb_tool/include/Tool.h @@ -47,6 +47,7 @@ #include // for tools::interiorMask() #include #include +#include #include #include @@ -69,6 +70,10 @@ #include #endif +#ifdef VDB_TOOL_USE_PDAL +#include +#endif + #ifdef VDB_TOOL_USE_JPG #include #endif @@ -969,6 +974,14 @@ void Tool::read() this->readNVDB(fileName); break; default: +#if VDB_TOOL_USE_PDAL + pdal::StageFactory factory; + if (factory.inferReaderDriver(fileName) != "") + { + this->readGeo(fileName); + break; + } +#endif throw std::invalid_argument("File \""+fileName+"\" has an invalid extension"); break; } @@ -1421,6 +1434,7 @@ void Tool::pointsToVdb() const int bits = mParser.get("bits"); std::string grid_name = mParser.get("name"); using GridT = points::PointDataGrid; + using IdGridT = tools::PointIndexGrid; if (mParser.verbose) mTimer.start("Points to VDB"); auto it = this->getGeom(age); Points points((*it)->vtx()); @@ -1428,19 +1442,48 @@ void Tool::pointsToVdb() auto xform = math::Transform::createLinearTransform(voxelSize); GridT::Ptr grid; + IdGridT::Ptr indexGrid; + + points::PointAttributeVector positionsWrapper((*it)->vtx()); + openvdb::NamePair rgbAttribute ; switch (bits) { case 8: - grid = points::createPointDataGrid, GridT>((*it)->vtx(), *xform); + indexGrid = tools::createPointIndexGrid(positionsWrapper, *xform); + grid = points::createPointDataGrid, GridT>(*indexGrid, positionsWrapper, *xform); + openvdb::points::TypedAttributeArray::registerType(); + rgbAttribute = + openvdb::points::TypedAttributeArray::attributeType(); + openvdb::points::appendAttribute(grid->tree(), "Cd", rgbAttribute); break; case 16: - grid = points::createPointDataGrid, GridT>((*it)->vtx(), *xform); + indexGrid = tools::createPointIndexGrid(positionsWrapper, *xform); + grid = points::createPointDataGrid, GridT>(*indexGrid, positionsWrapper, *xform); + openvdb::points::TypedAttributeArray::registerType(); + rgbAttribute = + openvdb::points::TypedAttributeArray::attributeType(); + openvdb::points::appendAttribute(grid->tree(), "Cd", rgbAttribute); break; case 32: - grid = points::createPointDataGrid((*it)->vtx(), *xform); + indexGrid = tools::createPointIndexGrid(positionsWrapper, *xform); + grid = points::createPointDataGrid(*indexGrid, positionsWrapper, *xform); + + openvdb::points::TypedAttributeArray::registerType(); + rgbAttribute = + openvdb::points::TypedAttributeArray::attributeType(); + openvdb::points::appendAttribute(grid->tree(), "Cd", rgbAttribute); break; default: throw std::invalid_argument("pointsToVdb: unsupported bit-width: "+std::to_string(bits)); } + + if ((*it)->rgb().size() == (*it)->vtx().size()) { + + points::PointAttributeVector rgbWrapper((*it)->rgb()); + points::populateAttribute>( + grid->tree(), indexGrid->tree(), "Cd", rgbWrapper); + + } if (grid_name.empty()) grid_name = "points2vdb_"+(*it)->getName(); grid->setName(grid_name); mGrid.push_back(grid); diff --git a/openvdb_cmd/vdb_tool/src/unittest.cpp b/openvdb_cmd/vdb_tool/src/unittest.cpp index 16a6ef557c..71fc4c9ade 100644 --- a/openvdb_cmd/vdb_tool/src/unittest.cpp +++ b/openvdb_cmd/vdb_tool/src/unittest.cpp @@ -408,6 +408,30 @@ TEST_F(Test_vdb_tool, Geometry) EXPECT_EQ(openvdb::Vec4I(0,1,2,3), geo.quad()[0]); } + #ifdef VDB_TOOL_USE_PDAL + {// read from PDAL-supported ASCII format file + // (NOTE: PDAL also supports other formats e.g. LAS, LAZ, E57, Draco, FBX, NumPy, OBJ,…) + + // write a test file + std::ofstream os("data/test.txt"); + os << "X,Y,Z\n"; + for (size_t i=0; i