diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index 451abdc..fa6f39e 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -46,6 +46,19 @@ set_target_properties(partio PROPERTIES POSITION_INDEPENDENT_CODE ON SOVERSION ${PARTIO_VERSION_MAJOR}) +set(TBB_INCLUDE_DIRS "$ENV{TBB_ROOT}/include") +set(CMAKE_MODULE_PATH "$ENV{OpenVDB_ROOT}/lib/cmake/OpenVDB" + "${OpenVDB_ROOT}/lib/cmake/OpenVDB" + ${CMAKE_MODULE_PATH}) +find_package(OpenVDB COMPONENTS openvdb) +find_package(Alembic) +find_package(Boost) +find_package(TBB) + + +target_link_libraries(partio PRIVATE Alembic::Alembic OpenVDB::openvdb) +target_include_directories(partio PRIVATE ${TBB_INCLUDE_DIRS}) + target_include_directories(partio PUBLIC $ diff --git a/src/lib/io/ABC.cpp b/src/lib/io/ABC.cpp new file mode 100644 index 0000000..ead6d1f --- /dev/null +++ b/src/lib/io/ABC.cpp @@ -0,0 +1,429 @@ + +#include "../Partio.h" +#include "../core/ParticleHeaders.h" +#include "PartioEndian.h" + +#include +#include +#include +#include +#include + +namespace Partio +{ + +namespace AbcA = Alembic::AbcCoreAbstract; +namespace Abc = Alembic::Abc; +namespace AbcGeom = Alembic::AbcGeom; + +void findPoints(Abc::IObject & iObj, double iTime, int & oNumPts, + std::map & oAttrMap, + ParticlesDataMutable * ioData) +{ + if (AbcGeom::IPoints::matches(iObj.getMetaData())) + { + + AbcGeom::IPoints ptsObj(iObj.getParent(), iObj.getName()); + + // check native properties starting with all important position + Abc::IP3fArrayProperty ptsProp = + ptsObj.getSchema().getPositionsProperty(); + + if (ptsProp.getNumSamples() > 0) + { + Alembic::Util::Dimensions dims; + ptsProp.getDimensions(dims, Alembic::Abc::ISampleSelector(iTime)); + oNumPts += dims.numPoints(); + if (oAttrMap.find("P") == oAttrMap.end()) + { + oAttrMap["P"] = ioData->addAttribute("position",VECTOR,3); + } + } + + if (ptsObj.getSchema().getVelocitiesProperty().valid() && + oAttrMap.find(".velocities") == oAttrMap.end()) + { + oAttrMap[".velocities"] = ioData->addAttribute("velocity", VECTOR, 3); + } + + if (ptsObj.getSchema().getWidthsParam().valid() && + oAttrMap.find(".widths") == oAttrMap.end()) + { + oAttrMap[".widths"] = ioData->addAttribute("width", FLOAT, 1); + } + + if (ptsObj.getSchema().getWidthsParam().valid() && + oAttrMap.find(".pointIds") == oAttrMap.end()) + { + oAttrMap[".pointIds"] = ioData->addAttribute("id", INT, 1); + } + + Abc::ICompoundProperty arbGeom = + ptsObj.getSchema().getArbGeomParams(); + + std::size_t numArb = 0; + if (arbGeom.valid()) + { + numArb = arbGeom.getNumProperties(); + } + + for (std::size_t i = 0; i < numArb; ++i) + { + const AbcA::PropertyHeader * prop = &arbGeom.getPropertyHeader(i); + AbcGeom::GeometryScope scope = + AbcGeom::GetGeometryScope( prop->getMetaData() ); + + std::string propName = prop->getName(); + if (prop->isCompound() && AbcGeom::IsGeomParam(*prop)) + { + Abc::ICompoundProperty cp(arbGeom, propName); + prop = cp.getPropertyHeader(".vals"); + } + + if (prop->isArray() && (scope == AbcGeom::kVaryingScope || + scope == AbcGeom::kVertexScope || + scope == AbcGeom::kFacevaryingScope)) + { + std::string keyName = ".arbGeomParams/" + propName; + int extent = prop->getDataType().getExtent(); + switch(prop->getDataType().getPod()) + { + case Alembic::Util::kBooleanPOD: + case Alembic::Util::kInt8POD: + case Alembic::Util::kInt16POD: + case Alembic::Util::kInt32POD: + case Alembic::Util::kInt64POD: + case Alembic::Util::kUint8POD: + case Alembic::Util::kUint16POD: + case Alembic::Util::kUint32POD: + case Alembic::Util::kUint64POD: + { + if (oAttrMap.find(keyName) == oAttrMap.end()) + { + oAttrMap[keyName] = ioData->addAttribute( + propName.c_str(), INT, extent); + } + } + + case Alembic::Util::kFloat16POD: + case Alembic::Util::kFloat32POD: + case Alembic::Util::kFloat64POD: + { + if (oAttrMap.find(keyName) == oAttrMap.end()) + { + ParticleAttributeType ptype = FLOAT; + std::string interp = + prop->getMetaData().get("interpretation"); + if (interp == "vector" || interp == "normal" || + interp == "point") + { + ptype = VECTOR; + } + oAttrMap[keyName] = ioData->addAttribute( + propName.c_str(), ptype, extent); + } + } + + case Alembic::Util::kStringPOD: + { + if (oAttrMap.find(keyName) == oAttrMap.end()) + { + oAttrMap[keyName] = ioData->addAttribute( + propName.c_str(), INDEXEDSTR, extent); + } + } + + default: + { + continue; + } + + } + } + } + } + + for (std::size_t i = 0; i < iObj.getNumChildren(); ++i) + { + Abc::IObject child = iObj.getChild(i); + findPoints(child, iTime, oNumPts, oAttrMap, ioData); + } +} + +void writePoints(Abc::IObject & iObj, double iTime, + const std::map & iAttrMap, + ParticlesDataMutable * iData, + uint64_t & oIndex, + std::vector & oMatStack) +{ + + bool shouldPop = false; + + if (AbcGeom::IXform::matches(iObj.getMetaData())) + { + AbcGeom::IXform xformObj(iObj.getParent(), iObj.getName()); + AbcGeom::IXformSchema schema = xformObj.getSchema(); + AbcGeom::XformSample samp; + + // should we need to linearly interpolate? + schema.get(samp, Alembic::Abc::ISampleSelector(iTime)); + Abc::M44d mat = samp.getMatrix(); + + // doesnt inherit? just push this matrix on the stack + if (!samp.getInheritsXforms()) + { + shouldPop = true; + oMatStack.push_back(mat); + } + else if (mat != Abc::M44d()) + { + shouldPop = true; + oMatStack.push_back(mat * oMatStack[oMatStack.size() - 1]); + } + } + else if (AbcGeom::IPoints::matches(iObj.getMetaData())) + { + Abc::ISampleSelector sel(iTime); + Abc::ICompoundProperty geomProp(iObj.getProperties(), ".geom"); + Abc::ICompoundProperty arbGeomProp; + if (geomProp.getPropertyHeader(".arbGeomParams") != NULL) + { + arbGeomProp = Abc::ICompoundProperty(geomProp, ".arbGeomParams"); + } + + std::size_t numPts = 0; + + Abc::IArrayProperty pProp(geomProp, "P"); + if (pProp.getNumSamples() > 0) + { + Alembic::Util::Dimensions dims; + pProp.getDimensions(dims, sel); + numPts = dims.numPoints(); + } + + std::map::const_iterator it; + for (it = iAttrMap.begin(); it != iAttrMap.end(); ++it) + { + Abc::IArrayProperty dataProp; + Abc::IArrayProperty indexProp; + + if (it->first.find(".arbGeomParams/") == 0 && arbGeomProp.valid()) + { + std::string propName = it->first.substr(15); + const AbcA::PropertyHeader * header = arbGeomProp.getPropertyHeader(propName); + if (header && header->isCompound()) + { + Abc::ICompoundProperty prop(arbGeomProp, propName); + dataProp = Abc::IArrayProperty(prop, ".vals"); + indexProp = Abc::IArrayProperty(prop, ".indices"); + } + else if (header) + { + dataProp = Abc::IArrayProperty(arbGeomProp, propName); + } + } + else + { + dataProp = Abc::IArrayProperty(geomProp, it->first); + } + + Alembic::Util::Dimensions dims; + dataProp.getDimensions(dims, sel); + std::size_t dataPts = dims.numPoints(); + std::size_t extent = dataProp.getHeader().getDataType().getExtent(); + if (it->second.type == INT) + { + std::vector dataVec(dataPts * extent); + dataProp.getAs(dataVec.data(), Alembic::Util::kInt32POD, sel); + for (std::size_t i=0; i < numPts; ++i) + { + int * ptr = iData->dataWrite(it->second, i); + + std::size_t idx = i * extent; + // more points than we have data, so copy over the last + // extent worth of data + if (i >= dataPts) + { + idx = dataVec.size() - extent; + } + + memcpy(ptr, &(dataVec[idx]), extent * sizeof(int)); + } + } + else if (it->second.type == INDEXEDSTR) + { + std::vector indexVec; + if (indexProp.valid()) + { + Alembic::Util::Dimensions indexDims; + indexProp.getDimensions(indexDims, sel); + indexVec.resize(indexDims.numPoints()); + indexProp.getAs(indexVec.data(), Alembic::Util::kInt32POD, sel); + } + + std::vector dataVec(dataPts * extent); + dataProp.getAs(dataVec.data(), Alembic::Util::kStringPOD, sel); + + // not indexed, we need to add them manually + if (indexVec.empty()) + { + + for (std::size_t i=0; i < numPts; ++i) + { + int * ptr = iData->dataWrite(it->second, i); + + std::size_t idx = i; + + // more points than we have data, so copy over the last + // extent worth of data + if (i >= dataPts) + { + idx = dataVec.size() - 1; + } + *ptr = iData->registerIndexedStr(it->second, dataVec[idx].c_str()); + } + } + else + { + // first register our strings + std::vector indexMap(dataVec.size()); + for (std::size_t i=0; i < dataVec.size(); ++i) + { + indexMap[i] = iData->registerIndexedStr(it->second, dataVec[i].c_str()); + } + + // now remap our indices according to what was registered + for (std::size_t i=0; i < numPts; ++i) + { + int * ptr = iData->dataWrite(it->second, i); + + std::size_t idx = i; + + // more points than we have data, so copy over the last + // extent worth of data + if (i >= indexVec.size()) + { + idx = indexVec.size() - 1; + } + *ptr = indexMap[indexVec[idx]]; + } + } + } + else + { + std::vector dataVec(dataPts * extent); + dataProp.getAs(dataVec.data(), Alembic::Util::kFloat32POD, sel); + + // do we need to multiply the matrix by our points? + if (it->first == "P" && + oMatStack[oMatStack.size() - 1] != Imath::M44d()) + { + Imath::M44d mat = oMatStack[oMatStack.size() - 1]; + for (std::size_t i = 0; i < dataPts; ++i) + { + Imath::V3f v(dataVec[i*3], dataVec[i*3+1], dataVec[i*3+2]); + v = v * mat; + dataVec[i*3] = v.x; + dataVec[i*3+1] = v.y; + dataVec[i*3+2] = v.z; + } + } + + for (std::size_t i=0; i < numPts; ++i) + { + float * ptr = iData->dataWrite(it->second, i); + + std::size_t idx = i * extent; + // more points than we have data, so copy over the last + // extent worth of data + if (i >= dataPts) + { + idx = dataVec.size() - extent; + } + + memcpy(ptr, &(dataVec[idx]), extent * sizeof(float)); + } + } + } + } + + for (std::size_t i = 0; i < iObj.getNumChildren(); ++i) + { + Abc::IObject child = iObj.getChild(i); + writePoints(child, iTime, iAttrMap, iData, oIndex, oMatStack); + } + + if (shouldPop) + { + oMatStack.pop_back(); + } +} + +ParticlesDataMutable* readABC(const char* filename, + const bool headersOnly, + std::ostream* errorStream) +{ + // partio looks for the extension .abc to decide to call readABC + // and we want to be able to accept a time to read (right now in seconds) + // so we will pass in /tmp/foo@42.5.abc + std::string name = filename; + std::size_t lastAt = name.rfind('@'); + double timeVal = -DBL_MAX; + if (lastAt != std::string::npos) + { + try + { + // we want to get everything FROM @ to but not including .abc + timeVal = stod(name.substr(lastAt + 1, name.size() - lastAt - 4)); + name = name.substr(0, lastAt) + ".abc"; + } + catch(...) + {} + } + + Alembic::AbcCoreFactory::IFactory factory; + Abc::IArchive archive = factory.getArchive(name); + + if (!archive.valid()) + { + if(errorStream) + { + *errorStream << "Partio: Unable to open file Alembic: " << name << std::endl; + *errorStream << "(" << filename << ")" << std::endl; + } + return 0; + } + // look for attr names + + // Allocate a simple particle with the appropriate number of points + ParticlesDataMutable* simple = 0; + if(headersOnly) + { + simple = new ParticleHeaders; + } + else + { + simple = create(); + } + + int nPoints = 0; + std::map attrMap; + + Abc::IObject top = archive.getTop(); + findPoints(top, timeVal, nPoints, attrMap, simple); + simple->addParticles(nPoints); + + if (headersOnly || nPoints == 0) + { + return simple; + } + + std::vector matStack; + matStack.push_back(Imath::M44d()); + uint64_t index = 0; + writePoints(top, timeVal, attrMap, simple, index, matStack); + + return simple; +} + +} \ No newline at end of file diff --git a/src/lib/io/ParticleIO.cpp b/src/lib/io/ParticleIO.cpp index ba4698f..af1d02f 100644 --- a/src/lib/io/ParticleIO.cpp +++ b/src/lib/io/ParticleIO.cpp @@ -54,6 +54,8 @@ readers() static bool initialized=false; if(!initialized){ initializationMutex.lock(); + data["abc"]=readABC; + data["vdb"]=readVDB; data["bgeo"]=readBGEO; data["bhclassic"]=readBGEO; data["geo"]=readGEO; diff --git a/src/lib/io/VDB.cpp b/src/lib/io/VDB.cpp new file mode 100644 index 0000000..153ff5e --- /dev/null +++ b/src/lib/io/VDB.cpp @@ -0,0 +1,371 @@ + +#include "../Partio.h" +#include "../core/ParticleHeaders.h" +#include "PartioEndian.h" + +#include +#include +#include + +#include +#include +#include +namespace Partio +{ + +static void initLibrary() { + static struct OpenVDBLib { + OpenVDBLib() { openvdb::initialize(); } + ~OpenVDBLib() { openvdb::uninitialize(); } + } vdblib_init; +} + +template void +convertScalar(const openvdb::points::AttributeHandle& vdbAttr, + const IndexType& vdbIndex, + ParticlesDataMutable* particles, + ParticleAttribute& partioAttr, + size_t partioIndex) +{ + auto* attrStorage = particles->dataWrite(partioAttr, partioIndex); + for (int i = 0; i < partioAttr.count; ++i) + attrStorage[i] = vdbAttr.get(vdbIndex, i); +} + +template PartioType* +convertVector(const openvdb::points::AttributeHandle& vdbAttr, + const IndexType& vdbIndex, + ParticlesDataMutable* particles, + ParticleAttribute& partioAttr, + size_t partioIndex) +{ + openvdb::Vec3d vdbData = vdbAttr.get(vdbIndex); + PartioType* partioData = particles->dataWrite(partioAttr, partioIndex); + + for (unsigned i = 0; i < 3; ++i) + partioData[i] = vdbData[i]; + + return partioData; +} + +enum { + Precision32, + Precision64 +}; + +ParticlesDataMutable* readVDB(const char* filename, + const bool headersOnly, + std::ostream* errorStream) +{ + + std::size_t numThreads = 0; + char *cueThreads = getenv("CUE_THREADS"); + if (cueThreads) + { + numThreads = atoi(cueThreads); + } + tbb::task_scheduler_init schedulerInit((numThreads == 0) ? tbb::task_scheduler_init::automatic : numThreads); + + initLibrary(); + + openvdb::io::File file(filename); + file.open(); + if (!file.isOpen()) { + if (errorStream) + *errorStream << "Partio.VDB: Unable to open VDB file: '" << filename << "'\n"; + return nullptr; + } + + // Since we can be merging multiple VDB-Points objects: + // 1: + // Walk the file collecting all the VDB-Point objects + // Save the point attributes into an AttributeCache to ensure all attribute types match + // Cache the index to the attribute (and it's precision) + // 2: + // Re-walk all the found grids, copying data (using the cached attribute index and precision) + // + using AttributeIndex = std::tuple; + struct AttributeCache { + std::vector indices; + ParticleAttribute partio; // partio.type contains the type that partio will store / allocate + unsigned tupleSize; + AttributeCache(ParticleAttributeType type, + unsigned tplSz) : tupleSize(tplSz) { partio.type = type; } + }; + + size_t totalPoints = 0; + std::unordered_map vdbAttributes; + std::vector> pointGrids; + + for (openvdb::io::File::NameIterator nameIter = file.beginName(), nameEnd = file.endName(); nameIter != nameEnd; ++nameIter) { + const std::string& name = *nameIter; + openvdb::GridBase::Ptr grid = file.readGrid(name); + if (!grid) { + if (errorStream) + *errorStream << "Partio.VDB: Could not open grid: '" << name << "'\n"; + continue; + } + + // This seems a silly way to detect points or not, so just use casting below + //if (grid->getGridClass() != openvdb::GridClass::GRID_UNKNOWN) + + openvdb::points::PointDataGrid::Ptr ptGrid = openvdb::gridPtrCast(grid); + if (!ptGrid) { + if (errorStream) { + *errorStream << "Partio.VDB: grid[" << name << "] is not a PointDataGrid, but a " + << openvdb::GridBase::gridClassToString(grid->getGridClass()) << "\n"; + } + continue; + } + // From VRAY_OPenVDB_Points.cc: + // // + // // enable streaming mode to auto-collapse attributes + // // on read for improved memory efficiency + // points::setStreamingMode(ptGrid->tree(), /*on=*/true); + + const auto& leafIter = ptGrid->tree().cbeginLeaf(); + if (!leafIter) + continue; + + const auto& attrs = leafIter->attributeSet(); + for (auto&& attrNameIndx : attrs.descriptor().map()) { + const auto& vdbAttr = attrs.get(attrNameIndx.second); + const auto& vdbType = vdbAttr->type(); + // VDB-6 ABI vdbAttr->valueTypeSize() vdbAttr->storageTypeSize() + bool spatial = false; + bool precision = Precision32; + ParticleAttributeType vdbRepr = NONE; + ParticleAttributeType partioType = NONE; + unsigned tupleSize = vdbAttr->dataSize() / vdbAttr->size(); + + if (vdbType.first == "vec3s") { + tupleSize = 3; // Partio wants VECTOR types to have a tupleSize / count of 3 + std::tie(partioType, vdbRepr) = std::make_tuple(VECTOR, VECTOR); + spatial = vdbType.second.find(openvdb::points::PositionRange::name()) == 0; + } + else if (vdbType.first == "vec3d") { + tupleSize = 3; // Partio wants VECTOR types to have a tupleSize / count of 3 + std::tie(partioType, vdbRepr, precision) = std::make_tuple(VECTOR, VECTOR, Precision64); + spatial = vdbType.second.find(openvdb::points::PositionRange::name()) == 0; + } + else if (vdbType.first == "float") + std::tie(partioType, vdbRepr) = std::make_tuple(tupleSize == 3 ? VECTOR : FLOAT, FLOAT); + else if (vdbType.first == "double") + std::tie(partioType, vdbRepr, precision) = std::make_tuple(tupleSize == 3 ? VECTOR : FLOAT, FLOAT, Precision64); + else if (vdbType.first == "int32") + std::tie(partioType, vdbRepr) = std::make_tuple(INT, INT); + else if (vdbType.first == "int64") + std::tie(partioType, vdbRepr, precision) = std::make_tuple(INT, INT, Precision64); + else if (vdbAttr->isType()) + std::tie(partioType, vdbRepr) = std::make_tuple(INDEXEDSTR, INDEXEDSTR); + else { + // WARN ? + continue; + } + + // If this attribute already existed, make sure it's the same type and tuple size + // The VDB representation can vary as that is going to be pulled directly from the + // VDB attribute stream. + // + auto inserted = vdbAttributes.emplace(std::piecewise_construct, + std::forward_as_tuple(attrNameIndx.first), + std::forward_as_tuple(partioType, tupleSize)); + if (!inserted.second) { + const AttributeCache& cache = inserted.first->second; + if (cache.partio.type != partioType || cache.tupleSize != tupleSize) + continue; + } + inserted.first->second.indices.emplace_back(attrNameIndx.second, vdbRepr, precision, spatial); + } + + pointGrids.emplace_back(std::move(ptGrid), totalPoints); + totalPoints += openvdb::points::pointCount(pointGrids.back().first->tree()); + } + + const std::unordered_map kHoudiniMapping = { + {"P", "position"}, + {"pscale", "width"}, + {"v", "velocity"}, + }; + + // Allocate a simple particle with the appropriate number of points + ParticlesDataMutable* particles = headersOnly ? new ParticleHeaders : create(); + particles->addParticles(totalPoints); + + const size_t nGrids = pointGrids.size(); + for (auto& attrIndex : vdbAttributes) { + AttributeCache& cache = attrIndex.second; + const ParticleAttributeType partioType = cache.partio.type; + + // Check the attribute isn't ignored / disabled + // + if (cache.indices.size() != nGrids || partioType == NONE) { + cache.indices.clear(); + cache.partio.type = NONE; + continue; + } + assert(partioType != NONE && "Invalid Partio type"); + + // Create the PartIO attribute + // + const std::string& vdbName = attrIndex.first; + const char* name = vdbName.c_str(); + + // Remap to Partio / Katana names if they don't already exist + // + const auto itr = kHoudiniMapping.find(vdbName); + if (itr != kHoudiniMapping.end() && vdbAttributes.count(itr->second) == 0) + name = itr->second.c_str(); + + cache.partio = particles->addAttribute(name, partioType, cache.tupleSize); + if (headersOnly) + continue; + + // Now walk the grids and copy the leaf attribute data + // If this loop needs to be threaded, pointGrids[i].second contains the starting offset + // (the particleIdx for partio) of the grid in pointGrids[i].first + // + for (size_t i = 0; i < nGrids; ++i) { + const size_t attrIdx = std::get<0>(cache.indices[i]); + const auto vdbType = std::get<1>(cache.indices[i]); + const auto precision = std::get<2>(cache.indices[i]); + const bool spatial = std::get<3>(cache.indices[i]); + assert(vdbType != NONE && "Invalid VDB representation"); + + openvdb::points::PointDataGrid *ptGrid = pointGrids[i].first.get(); + const auto &ptTree = ptGrid->constTree(); + const auto &transform = ptGrid->constTransform(); + std::vector offsets; + std::ignore = openvdb::points::pointOffsets(offsets, ptTree); + + if (vdbType == VECTOR) + { + openvdb::tree::LeafManager leafManager(ptTree); + leafManager.foreach ( + [&](const openvdb::points::PointDataTree::LeafNodeType &leafIter, size_t idx) { + assert(idx < offsets.size()); + openvdb::Index64 offset = pointGrids[i].second; + if (idx > 0) + offset += offsets[idx - 1]; + + const auto &attrs = leafIter.attributeSet(); + const auto *vdbAttr = attrs.get(attrIdx); + + openvdb::points::AttributeHandle::Ptr vec32; + openvdb::points::AttributeHandle::Ptr vec64; + if (precision == Precision32) + vec32.reset(new openvdb::points::AttributeHandle(*vdbAttr)); + else + vec64.reset(new openvdb::points::AttributeHandle(*vdbAttr)); + for (auto indexIter = leafIter.beginIndexOn(); indexIter; ++indexIter) + { + float *partioData; + if (vec32) + partioData = convertVector(*vec32, *indexIter, particles, cache.partio, static_cast(offset++)); + else + partioData = convertVector(*vec64, *indexIter, particles, cache.partio, static_cast(offset++)); + + // Reinterpret the voxel-data to world-space co-ordinates + if (spatial) + { + // Extract the voxel-space position of the point. + openvdb::Vec3f *voxelPosition = reinterpret_cast(partioData); + // Compute the world-space position of the point. + *voxelPosition = transform.indexToWorld(*voxelPosition + indexIter.getCoord().asVec3d()); + } + } + }, + /*threaded*/ true); + } + else if (vdbType == FLOAT) + { + openvdb::tree::LeafManager leafManager(ptTree); + leafManager.foreach ( + [&](const openvdb::points::PointDataTree::LeafNodeType &leafIter, size_t idx) { + assert(idx < offsets.size()); + openvdb::Index64 offset = pointGrids[i].second; + if (idx > 0) + offset += offsets[idx - 1]; + + const auto &attrs = leafIter.attributeSet(); + const auto *vdbAttr = attrs.get(attrIdx); + + openvdb::points::AttributeHandle::Ptr float32; + openvdb::points::AttributeHandle::Ptr float64; + if (precision == Precision32) + float32.reset(new openvdb::points::AttributeHandle(*vdbAttr)); + else + float64.reset(new openvdb::points::AttributeHandle(*vdbAttr)); + + for (auto indexIter = leafIter.beginIndexOn(); indexIter; ++indexIter) + { + if (float32) + convertScalar(*float32, *indexIter, particles, cache.partio, static_cast(offset++)); + else + convertScalar(*float64, *indexIter, particles, cache.partio, static_cast(offset++)); + } + }, + /*threaded*/ true); + } + else if (vdbType == INT) + { + openvdb::tree::LeafManager leafManager(ptTree); + leafManager.foreach ( + [&](const openvdb::points::PointDataTree::LeafNodeType &leafIter, size_t idx) { + assert(idx < offsets.size()); + openvdb::Index64 offset = pointGrids[i].second; + if (idx > 0) + offset += offsets[idx - 1]; + + const auto &attrs = leafIter.attributeSet(); + const auto *vdbAttr = attrs.get(attrIdx); + + openvdb::points::AttributeHandle::Ptr int32; + openvdb::points::AttributeHandle::Ptr int64; + if (precision == Precision32) + int32.reset(new openvdb::points::AttributeHandle(*vdbAttr)); + else + int64.reset(new openvdb::points::AttributeHandle(*vdbAttr)); + + for (auto indexIter = leafIter.beginIndexOn(); indexIter; ++indexIter) + { + if (int32) + convertScalar(*int32, *indexIter, particles, cache.partio, static_cast(offset++)); + else + convertScalar(*int64, *indexIter, particles, cache.partio, static_cast(offset++)); + } + }, + /*threaded*/ true); + } + else if (vdbType == INDEXEDSTR) + { + // handle string type none-thread version. + openvdb::tree::LeafManager leafManager(ptTree); + leafManager.foreach ( + [&](const openvdb::points::PointDataTree::LeafNodeType &leafIter, size_t idx) { + const auto &attrs = leafIter.attributeSet(); + const auto *vdbAttr = attrs.get(attrIdx); + openvdb::points::StringAttributeHandle strs(*vdbAttr, attrs.descriptor().getMetadata()); + + int id(0); + for (auto indexIter = leafIter.beginIndexOn(); indexIter; ++indexIter, ++id) + { + int index = particles->registerIndexedStr(cache.partio, strs.get(*indexIter).c_str()); + int *idd = particles->dataWrite(cache.partio, id); + *idd = index; + } + }, + /*threaded*/ false); + } + else + { + assert(0 && "Unsupported attribute type"); + } + } + } + + file.close(); + return particles; +} + +} // namespace Partio diff --git a/src/lib/io/readers.h b/src/lib/io/readers.h index 0e8d60d..1f721a9 100644 --- a/src/lib/io/readers.h +++ b/src/lib/io/readers.h @@ -36,6 +36,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. #define _READERS_h_ namespace Partio{ +ParticlesDataMutable* readABC( const char* filename,const bool headersOnly,std::ostream* errorStream); +ParticlesDataMutable* readVDB( const char* filename,const bool headersOnly,std::ostream* errorStream); ParticlesDataMutable* readBGEO( const char* filename,const bool headersOnly,std::ostream* errorStream); ParticlesDataMutable* readGEO( const char* filename,const bool headersOnly,std::ostream* errorStream); ParticlesDataMutable* readPDB( const char* filename,const bool headersOnly,std::ostream* errorStream);