#include "CacheReaderAlembic.h"
#include "CacheAlembicUtil.h"
#include "gpuCacheUtil.h"
#include "gpuCacheStrings.h"
#include <Alembic/AbcCoreFactory/IFactory.h>
#include <Alembic/AbcGeom/Visibility.h>
#include <Alembic/AbcGeom/ArchiveBounds.h>
#include <maya/MStringArray.h>
#include <maya/MAnimControl.h>
#include <maya/MString.h>
#include <maya/MGlobal.h>
#include <maya/MFnMesh.h>
#include <maya/MFnNurbsCurve.h>
#include <maya/MFnNurbsCurveData.h>
#include <maya/MTrimBoundaryArray.h>
#include <maya/MFloatArray.h>
#include <maya/MFloatVector.h>
#include <maya/MPointArray.h>
#include <maya/MFloatPointArray.h>
#include <maya/MFloatVectorArray.h>
#include <maya/MUintArray.h>
#include <unordered_map>
#include <utility>
#include <fstream>
#include <cassert>
typedef std::pair< int, int > TEdge;
namespace std
{
template<>
struct hash<TEdge>
{
size_t operator()(TEdge const& v) const
{
std::size_t h = std::hash<int>()(v.first);
GPUCache::hash_combine<int>(h, v.second);
return h;
}
};
}
using namespace CacheAlembicUtil;
namespace GPUCache {
namespace CacheReaderAlembicPrivate {
template <class ArrayProperty>
struct AlembicArray<ArrayProperty>::MakeSharedEnabler: public AlembicArray<ArrayProperty> {
MakeSharedEnabler(const ArraySamplePtr& arraySamplePtr, const Digest& digest)
: AlembicArray<ArrayProperty>(arraySamplePtr,digest){}
};
template <class ArrayProperty>
std::shared_ptr<ReadableArray<typename AlembicArray<ArrayProperty>::T> >
AlembicArray<ArrayProperty>::create(
const ArraySamplePtr& arraySamplePtr, const Digest& digest )
{
const size_t size = (arraySamplePtr->size() *
ArrayProperty::traits_type::dataType().getExtent());
#ifndef NDEBUG
Digest checkDigest;
Alembic::Util::MurmurHash3_x64_128(
arraySamplePtr->get(), size * sizeof(T), sizeof(T), checkDigest.words);
assert(digest == checkDigest);
#endif
std::shared_ptr<ReadableArray<T> > ret;
{
std::lock_guard<std::mutex> lock(ArrayRegistry<T>::mutex());
ret = ArrayRegistry<T>::lookupReadable(digest, size);
if (!ret) {
ret = std::make_shared<MakeSharedEnabler>(
arraySamplePtr, digest);
ArrayRegistry<T>::insert(ret);
}
}
return ret;
}
template <class ArrayProperty>
AlembicArray<ArrayProperty>::~AlembicArray() {}
template <class ArrayProperty>
const typename AlembicArray<ArrayProperty>::T*
AlembicArray<ArrayProperty>::get() const
{
return reinterpret_cast<const T*>(fArraySamplePtr->get());
}
template <typename PROPERTY>
typename ArrayPropertyCacheWithConverter<PROPERTY>::ConvertionMap
ArrayPropertyCacheWithConverter<PROPERTY>::fsConvertionMap;
template class ArrayPropertyCacheWithConverter<
Alembic::Abc::IInt32ArrayProperty>;
class ScopedUnlockAlembic
{
public:
ScopedUnlockAlembic()
{
gsAlembicMutex.unlock();
}
~ScopedUnlockAlembic()
{
gsAlembicMutex.lock();
}
ScopedUnlockAlembic(const ScopedUnlockAlembic&) = delete;
ScopedUnlockAlembic& operator=(const ScopedUnlockAlembic&) = delete;
};
void CheckInterruptAndPause(const char* state)
{
GlobalReaderCache& readerCache = GlobalReaderCache::theCache();
if (readerCache.isInterrupted()) {
throw CacheReaderInterruptException(state);
}
else if (readerCache.isPaused()) {
ScopedUnlockAlembic unlock;
readerCache.pauseUntilNotified();
}
}
template<class INFO>
DataProvider::DataProvider(
Alembic::AbcGeom::IGeomBaseSchema<INFO>& abcGeom,
Alembic::Abc::TimeSamplingPtr timeSampling,
size_t numSamples,
bool needUVs)
: fAnimTimeRange(TimeInterval::kInvalid),
fBBoxAndVisValidityInterval(TimeInterval::kInvalid),
fValidityInterval(TimeInterval::kInvalid),
fNeedUVs(needUVs)
{
Alembic::Abc::IObject shapeObject = abcGeom.getObject();
Alembic::AbcGeom::IVisibilityProperty visibility =
Alembic::AbcGeom::GetVisibilityProperty(shapeObject);
if (visibility) {
fVisibilityCache.init(visibility);
}
fBoundingBoxCache.init(abcGeom.getSelfBoundsProperty());
std::vector<Alembic::Abc::IObject> parents;
Alembic::Abc::IObject current = shapeObject.getParent();
while (current.valid()) {
parents.push_back(current);
current = current.getParent();
}
fParentVisibilityCache.resize(parents.size());
for (size_t i = 0; i < fParentVisibilityCache.size(); i++) {
Alembic::AbcGeom::IVisibilityProperty visibilityProp =
Alembic::AbcGeom::GetVisibilityProperty(parents[i]);
if (visibilityProp) {
fParentVisibilityCache[i].init(visibilityProp);
}
}
fAnimTimeRange = TimeInterval(
timeSampling->getSampleTime(0),
timeSampling->getSampleTime(numSamples > 0 ? numSamples-1 : 0)
);
}
DataProvider::~DataProvider()
{
fValidityInterval = TimeInterval::kInvalid;
fVisibilityCache.reset();
fBoundingBoxCache.reset();
fParentVisibilityCache.clear();
}
bool DataProvider::valid() const
{
return fBoundingBoxCache.valid();
}
std::shared_ptr<const ShapeSample>
DataProvider::getBBoxPlaceHolderSample(double seconds)
{
std::shared_ptr<ShapeSample> sample =
ShapeSample::createBoundingBoxPlaceHolderSample(
seconds,
getBoundingBox(),
isVisible()
);
return sample;
}
void DataProvider::fillBBoxAndVisSample(chrono_t time)
{
fBBoxAndVisValidityInterval = updateBBoxAndVisCache(time);
assert(fBBoxAndVisValidityInterval.valid());
}
void DataProvider::fillTopoAndAttrSample(chrono_t time)
{
fValidityInterval = updateCache(time);
assert(fValidityInterval.valid());
}
TimeInterval DataProvider::updateBBoxAndVisCache(chrono_t time)
{
if (fVisibilityCache.valid()) {
fVisibilityCache.setTime(time);
}
fBoundingBoxCache.setTime(time);
for(ScalarPropertyCache<Alembic::Abc::ICharProperty>&
parentVisPropCache : fParentVisibilityCache) {
if (parentVisPropCache.valid()) {
parentVisPropCache.setTime(time);
}
}
TimeInterval validityInterval(TimeInterval::kInfinite);
if (fVisibilityCache.valid()) {
validityInterval &= fVisibilityCache.getValidityInterval();
}
validityInterval &= fBoundingBoxCache.getValidityInterval();
for(ScalarPropertyCache<Alembic::Abc::ICharProperty>&
parentVisPropCache : fParentVisibilityCache) {
if (parentVisPropCache.valid()) {
validityInterval &= parentVisPropCache.getValidityInterval();
}
}
return validityInterval;
}
TimeInterval DataProvider::updateCache(chrono_t time)
{
return updateBBoxAndVisCache(time);
}
bool DataProvider::isVisible() const
{
if (fVisibilityCache.valid() &&
fVisibilityCache.getValue() == char(Alembic::AbcGeom::kVisibilityHidden)) {
return false;
}
for(const ScalarPropertyCache<Alembic::Abc::ICharProperty>&
parentVisPropCache : fParentVisibilityCache) {
if (parentVisPropCache.valid() &&
parentVisPropCache.getValue() == char(Alembic::AbcGeom::kVisibilityHidden)) {
return false;
}
}
return true;
}
template<class SCHEMA>
PolyDataProvider::PolyDataProvider(
SCHEMA& abcMesh,
const bool needUVs)
: DataProvider(abcMesh, abcMesh.getTimeSampling(),
abcMesh.getNumSamples(), needUVs)
{
fFaceCountsCache.init(abcMesh.getFaceCountsProperty());
fPositionsCache.init(abcMesh.getPositionsProperty());
}
PolyDataProvider::~PolyDataProvider()
{
fFaceCountsCache.reset();
fPositionsCache.reset();
}
bool PolyDataProvider::valid() const
{
return DataProvider::valid() &&
fFaceCountsCache.valid() &&
fPositionsCache.valid();
}
TimeInterval
PolyDataProvider::updateCache(chrono_t time)
{
TimeInterval validityInterval(DataProvider::updateCache(time));
fFaceCountsCache.setTime(time);
fPositionsCache.setTime(time);
validityInterval &= fFaceCountsCache.getValidityInterval();
validityInterval &= fPositionsCache.getValidityInterval();
return validityInterval;
}
RawDataProvider::RawDataProvider(
Alembic::AbcGeom::IPolyMeshSchema& abcMesh,
const bool needUVs)
: PolyDataProvider(abcMesh, needUVs),
fFaceIndicesCache(correctPolygonWinding)
{
fFaceIndicesCache.init(abcMesh.getFaceIndicesProperty());
if (abcMesh.getPropertyHeader(kCustomPropertyWireIndices) != NULL) {
fWireIndicesCache.init(
Alembic::Abc::IInt32ArrayProperty(abcMesh.getPtr(), kCustomPropertyWireIndices));
}
else if (abcMesh.getPropertyHeader(kCustomPropertyWireIndicesOld) != NULL) {
fWireIndicesCache.init(
Alembic::Abc::IInt32ArrayProperty(abcMesh.getPtr(), kCustomPropertyWireIndicesOld));
}
if (abcMesh.getPropertyHeader(kCustomPropertyShadingGroupSizes) != NULL) {
fGroupSizesCache.init(
Alembic::Abc::IInt32ArrayProperty(abcMesh.getPtr(), kCustomPropertyShadingGroupSizes));
}
if (abcMesh.getPropertyHeader(kCustomPropertyDiffuseColor) != NULL) {
fDiffuseColorCache.init(
Alembic::Abc::IC4fProperty(abcMesh.getPtr(), kCustomPropertyDiffuseColor));
}
Alembic::AbcGeom::IN3fGeomParam normals = abcMesh.getNormalsParam();
if (normals.valid()) {
assert(!normals.isIndexed());
assert(normals.getScope() == Alembic::AbcGeom::kVertexScope);
fNormalsCache.init(normals.getValueProperty());
}
if (fNeedUVs) {
Alembic::AbcGeom::IV2fGeomParam UVs = abcMesh.getUVsParam();
if (UVs.valid()) {
assert(!UVs.isIndexed());
assert(UVs.getScope() == Alembic::AbcGeom::kVertexScope);
fUVsCache.init(UVs.getValueProperty());
}
}
}
RawDataProvider::~RawDataProvider()
{
fFaceIndicesCache.reset();
fWireIndicesCache.reset();
fGroupSizesCache.reset();
fDiffuseColorCache.reset();
fNormalsCache.reset();
fUVsCache.reset();
}
bool RawDataProvider::valid() const
{
return PolyDataProvider::valid() &&
fFaceIndicesCache.valid() &&
fWireIndicesCache.valid() &&
fDiffuseColorCache.valid() &&
fNormalsCache.valid();
}
std::shared_ptr<const ShapeSample>
RawDataProvider::getSample(double seconds)
{
std::vector<std::shared_ptr<IndexBuffer> > triangleVertIndices;
const std::shared_ptr<ReadableArray<IndexBuffer::index_t> > indexBuffer =
fFaceIndicesCache.getValue();
if (fGroupSizesCache.valid()) {
const std::shared_ptr<ReadableArray<IndexBuffer::index_t> > groupSizes =
fGroupSizesCache.getValue();
const IndexBuffer::index_t* groupSizesPtr = groupSizes->get();
for(size_t i=0, offset=0; i<groupSizes->size(); offset+=3*groupSizesPtr[i], ++i) {
triangleVertIndices.push_back(
IndexBuffer::create(
indexBuffer, offset, offset+3*groupSizesPtr[i]));
}
}
else {
triangleVertIndices.push_back(IndexBuffer::create(indexBuffer));
}
Alembic::Abc::C4f diffuseColor = fDiffuseColorCache.getValue();
std::shared_ptr<ShapeSample> sample = ShapeSample::create(
seconds,
fWireIndicesCache.getValue()->size() / 2,
fPositionsCache.getValue()->size() / 3,
IndexBuffer::create(fWireIndicesCache.getValue()),
triangleVertIndices,
VertexBuffer::createPositions(fPositionsCache.getValue()),
getBoundingBox(),
MColor(diffuseColor.r, diffuseColor.g, diffuseColor.b, diffuseColor.a),
isVisible()
);
if (fNormalsCache.valid()) {
sample->setNormals(
VertexBuffer::createNormals(fNormalsCache.getValue()));
}
if (fUVsCache.valid()) {
sample->setUVs(
VertexBuffer::createUVs(fUVsCache.getValue()));
}
return sample;
}
std::shared_ptr<ReadableArray<IndexBuffer::index_t> >
RawDataProvider::correctPolygonWinding(const Alembic::Abc::Int32ArraySamplePtr& indices)
{
size_t count = (*indices).size();
GPUCache::shared_array<IndexBuffer::index_t> faceIndicesCCW(
new IndexBuffer::index_t[count]);
for (size_t i = 0; i < count; i += 3) {
faceIndicesCCW[i + 2] = (*indices)[i + 0];
faceIndicesCCW[i + 1] = (*indices)[i + 1];
faceIndicesCCW[i + 0] = (*indices)[i + 2];
}
return SharedArray<IndexBuffer::index_t>::create(
faceIndicesCCW, count);
}
TimeInterval RawDataProvider::updateCache(chrono_t time)
{
TimeInterval validityInterval(PolyDataProvider::updateCache(time));
fFaceIndicesCache.setTime(time);
fWireIndicesCache.setTime(time);
if (fGroupSizesCache.valid()) {
fGroupSizesCache.setTime(time);
}
fNormalsCache.setTime(time);
if (fUVsCache.valid()) {
fUVsCache.setTime(time);
}
fDiffuseColorCache.setTime(time);
validityInterval &= fFaceIndicesCache.getValidityInterval();
validityInterval &= fWireIndicesCache.getValidityInterval();
if (fGroupSizesCache.valid()) {
validityInterval &= fGroupSizesCache.getValidityInterval();
}
validityInterval &= fNormalsCache.getValidityInterval();
if (fUVsCache.valid()) {
validityInterval &= fUVsCache.getValidityInterval();
}
validityInterval &= fDiffuseColorCache.getValidityInterval();
size_t numVerts = fPositionsCache.getValue()->size() / 3;
size_t numTriangles = fFaceIndicesCache.getValue()->size() / 3;
if (fFaceCountsCache.getValue()->size() != numTriangles) {
assert(fFaceCountsCache.getValue()->size() == numTriangles);
return TimeInterval::kInvalid;
}
if (fNormalsCache.getValue()->size() / 3 != numVerts) {
assert(fNormalsCache.getValue()->size() / 3 == numVerts);
return TimeInterval::kInvalid;
}
if (fUVsCache.valid()) {
if (fUVsCache.getValue()->size() / 2 != numVerts) {
assert(fUVsCache.getValue()->size() / 2 == numVerts);
return TimeInterval::kInvalid;
}
}
return validityInterval;
}
Triangulator::Triangulator(
Alembic::AbcGeom::IPolyMeshSchema& abcMesh,
const bool needUVs)
: PolyDataProvider(abcMesh, needUVs),
fNormalsScope(Alembic::AbcGeom::kUnknownScope), fUVsScope(Alembic::AbcGeom::kUnknownScope)
{
fFaceIndicesCache.init(abcMesh.getFaceIndicesProperty());
Alembic::AbcGeom::IN3fGeomParam normals = abcMesh.getNormalsParam();
if (normals.valid()) {
fNormalsScope = normals.getScope();
if (fNormalsScope == Alembic::AbcGeom::kVaryingScope ||
fNormalsScope == Alembic::AbcGeom::kVertexScope ||
fNormalsScope == Alembic::AbcGeom::kFacevaryingScope) {
fNormalsCache.init(normals.getValueProperty());
if (normals.isIndexed()) {
fNormalIndicesCache.init(normals.getIndexProperty());
}
}
}
if (fNeedUVs) {
Alembic::AbcGeom::IV2fGeomParam UVs = abcMesh.getUVsParam();
if (UVs.valid()) {
fUVsScope = UVs.getScope();
if (fUVsScope == Alembic::AbcGeom::kVaryingScope ||
fUVsScope == Alembic::AbcGeom::kVertexScope ||
fUVsScope == Alembic::AbcGeom::kFacevaryingScope) {
fUVsCache.init(UVs.getValueProperty());
if (UVs.isIndexed()) {
fUVIndicesCache.init(UVs.getIndexProperty());
}
}
}
}
}
Triangulator::~Triangulator()
{
fFaceIndicesCache.reset();
fNormalsScope = Alembic::AbcGeom::kUnknownScope;
fNormalsCache.reset();
fNormalIndicesCache.reset();
fUVsScope = Alembic::AbcGeom::kUnknownScope;
fUVsCache.reset();
fUVIndicesCache.reset();
}
bool Triangulator::valid() const
{
return PolyDataProvider::valid() &&
fFaceIndicesCache.valid();
}
std::shared_ptr<const ShapeSample>
Triangulator::getSample(double seconds)
{
if (!fWireIndices || !fTriangleIndices) {
std::shared_ptr<ShapeSample> sample =
ShapeSample::createEmptySample(seconds);
return sample;
}
std::vector<std::shared_ptr<IndexBuffer> > triangleVertIndices;
triangleVertIndices.push_back(IndexBuffer::create(fTriangleIndices));
std::shared_ptr<ShapeSample> sample = ShapeSample::create(
seconds,
fWireIndices->size() / 2,
fMappedPositions->size() / 3,
IndexBuffer::create(fWireIndices),
triangleVertIndices,
VertexBuffer::createPositions(fMappedPositions),
getBoundingBox(),
Config::kDefaultGrayColor,
isVisible()
);
if (fMappedNormals) {
sample->setNormals(
VertexBuffer::createNormals(fMappedNormals));
}
if (fMappedUVs) {
sample->setUVs(
VertexBuffer::createUVs(fMappedUVs));
}
return sample;
}
TimeInterval Triangulator::updateCache(chrono_t time)
{
bool topologyChanged = fFaceCountsCache.setTime(time);
bool positionChanged = fPositionsCache.setTime(time);
TimeInterval validityInterval(PolyDataProvider::updateCache(time));
topologyChanged = fFaceIndicesCache.setTime(time) || topologyChanged;
if (fNormalsCache.valid()) {
fNormalsCache.setTime(time);
if (fNormalIndicesCache.valid()) {
fNormalIndicesCache.setTime(time);
}
}
if (fUVsCache.valid()) {
fUVsCache.setTime(time);
if (fUVIndicesCache.valid()) {
fUVIndicesCache.setTime(time);
}
}
validityInterval &= fFaceIndicesCache.getValidityInterval();
if (fNormalsCache.valid()) {
validityInterval &= fNormalsCache.getValidityInterval();
if (fNormalIndicesCache.valid()) {
validityInterval &= fNormalIndicesCache.getValidityInterval();
}
}
if (fUVsCache.valid()) {
validityInterval &= fUVsCache.getValidityInterval();
if (fUVIndicesCache.valid()) {
validityInterval &= fUVIndicesCache.getValidityInterval();
}
}
check();
if (positionChanged || topologyChanged || !fComputedNormals) {
computeNormals();
}
if (topologyChanged || !fVertAttribsIndices) {
convertMultiIndexedStreams();
}
remapVertAttribs();
if (topologyChanged || !fWireIndices) {
computeWireIndices();
}
if (topologyChanged || !fTriangleIndices) {
triangulate();
}
return validityInterval;
}
template<size_t SIZE>
std::shared_ptr<ReadableArray<float> > Triangulator::convertMultiIndexedStream(
std::shared_ptr<ReadableArray<float> > attribArray,
std::shared_ptr<ReadableArray<IndexBuffer::index_t> > indexArray)
{
size_t numVerts = indexArray->size();
const float* srcAttribs = attribArray->get();
const IndexBuffer::index_t* srcIndices = indexArray->get();
GPUCache::shared_array<float> mappedAttribs(new float[numVerts * SIZE]);
for (size_t i = 0; i < numVerts; i++) {
for (size_t j = 0; j < SIZE; j++) {
mappedAttribs[i * SIZE + j] = srcAttribs[srcIndices[i] * SIZE + j];
}
}
return SharedArray<float>::create(mappedAttribs, numVerts * SIZE);
}
void Triangulator::check()
{
size_t numFaceIndices = fFaceIndicesCache.getValue()->size();
size_t numVerts = fPositionsCache.getValue()->size() / 3;
size_t numExpectedNormals = 0;
if (fNormalsScope == Alembic::AbcGeom::kVaryingScope ||
fNormalsScope == Alembic::AbcGeom::kVertexScope) {
numExpectedNormals = numVerts;
}
else if (fNormalsScope == Alembic::AbcGeom::kFacevaryingScope) {
numExpectedNormals = numFaceIndices;
}
size_t numActualNormals = 0;
if (fNormalsCache.valid()) {
if (fNormalIndicesCache.valid()) {
numActualNormals = fNormalIndicesCache.getValue()->size();
}
else {
numActualNormals = fNormalsCache.getValue()->size() / 3;
}
}
fCheckedNormalsScope = Alembic::AbcGeom::kUnknownScope;
fCheckedNormals.reset();
fCheckedNormalIndices.reset();
if (numExpectedNormals == numActualNormals) {
if (fNormalsCache.valid()) {
fCheckedNormalsScope = fNormalsScope;
fCheckedNormals = fNormalsCache.getValue();
if (fNormalIndicesCache.valid()) {
fCheckedNormalIndices = fNormalIndicesCache.getValue();
}
}
}
else {
DisplayWarning(kBadNormalsMsg);
}
size_t numExpectedUVs = 0;
if (fUVsScope == Alembic::AbcGeom::kVaryingScope ||
fUVsScope == Alembic::AbcGeom::kVertexScope) {
numExpectedUVs = numVerts;
}
else if (fUVsScope == Alembic::AbcGeom::kFacevaryingScope) {
numExpectedUVs = numFaceIndices;
}
size_t numActualUVs = 0;
if (fUVsCache.valid()) {
if (fUVIndicesCache.valid()) {
numActualUVs = fUVIndicesCache.getValue()->size();
}
else {
numActualUVs = fUVsCache.getValue()->size() / 2;
}
}
fCheckedUVsScope = Alembic::AbcGeom::kUnknownScope;
fCheckedUVs.reset();
fCheckedUVIndices.reset();
if (numExpectedUVs == numActualUVs) {
if (fUVsCache.valid()) {
fCheckedUVsScope = fUVsScope;
fCheckedUVs = fUVsCache.getValue();
if (fUVIndicesCache.valid()) {
fCheckedUVIndices = fUVIndicesCache.getValue();
}
}
}
else {
DisplayWarning(kBadUVsMsg);
}
}
void Triangulator::computeNormals()
{
if (fCheckedNormals && (fCheckedNormalsScope == Alembic::AbcGeom::kVaryingScope
|| fCheckedNormalsScope == Alembic::AbcGeom::kVertexScope
|| fCheckedNormalsScope == Alembic::AbcGeom::kFacevaryingScope)) {
fComputedNormals = fCheckedNormals;
fComputedNormalsScope = fCheckedNormalsScope;
fComputedNormalIndices.reset();
if (fCheckedNormalIndices) {
fComputedNormalIndices = fCheckedNormalIndices;
}
return;
}
size_t numFaceCounts = fFaceCountsCache.getValue()->size();
const unsigned int* faceCounts = fFaceCountsCache.getValue()->get();
const IndexBuffer::index_t* faceIndices = fFaceIndicesCache.getValue()->get();
size_t numPositions = fPositionsCache.getValue()->size();
const float* positions = fPositionsCache.getValue()->get();
size_t numVerts = numPositions / 3;
if (numVerts == 0) {
fComputedNormalsScope = Alembic::AbcGeom::kUnknownScope;
fComputedNormals.reset();
fComputedNormalIndices.reset();
return;
}
GPUCache::shared_array<float> computedFaceNormals(new float[numFaceCounts * 3]);
GPUCache::shared_array<float> computedNormals(new float[numVerts * 3]);
for (size_t i = 0, polyVertOffset = 0; i < numFaceCounts; polyVertOffset += faceCounts[i], i++) {
size_t numPoints = faceCounts[i];
for (size_t j = 0; j < numPoints; j++) {
size_t thisJ = numPoints - j - 1;
size_t nextJ = numPoints - ((j + 1) % numPoints) - 1;
const float* thisPoint = &positions[faceIndices[polyVertOffset + thisJ] * 3];
const float* nextPoint = &positions[faceIndices[polyVertOffset + nextJ] * 3];
faceNormal.x += (thisPoint[1] - nextPoint[1]) * (thisPoint[2] + nextPoint[2]);
faceNormal.y += (thisPoint[2] - nextPoint[2]) * (thisPoint[0] + nextPoint[0]);
faceNormal.z += (thisPoint[0] - nextPoint[0]) * (thisPoint[1] + nextPoint[1]);
}
faceNormal.normalize();
computedFaceNormals[i * 3 + 0] = faceNormal.x;
computedFaceNormals[i * 3 + 1] = faceNormal.y;
computedFaceNormals[i * 3 + 2] = faceNormal.z;
}
memset(&computedNormals[0], 0, sizeof(float) * numVerts * 3);
for (size_t i = 0, polyVertOffset = 0; i < numFaceCounts; polyVertOffset += faceCounts[i], i++) {
size_t numPoints = faceCounts[i];
const float* faceNormal = &computedFaceNormals[i * 3];
for (size_t j = 0; j < numPoints; j++) {
float* normal = &computedNormals[faceIndices[polyVertOffset + j] * 3];
normal[0] += faceNormal[0];
normal[1] += faceNormal[1];
normal[2] += faceNormal[2];
}
}
for (size_t i = 0; i < numVerts; i++) {
float* normal = &computedNormals[i * 3];
vector.normalize();
normal[0] = vector.x;
normal[1] = vector.y;
normal[2] = vector.z;
}
fComputedNormalsScope = Alembic::AbcGeom::kVertexScope;
fComputedNormals = SharedArray<float>::create(computedNormals, numVerts * 3);
fComputedNormalIndices.reset();
}
void Triangulator::convertMultiIndexedStreams()
{
size_t numFaceIndices = fFaceIndicesCache.getValue()->size();
const IndexBuffer::index_t* faceIndices = fFaceIndicesCache.getValue()->get();
bool normalFaceVarying = false;
const IndexBuffer::index_t* normalIndices = NULL;
if (fComputedNormals) {
normalFaceVarying = (fComputedNormalsScope == Alembic::AbcGeom::kFacevaryingScope);
if (fComputedNormalIndices) {
normalIndices = fComputedNormalIndices->get();
assert(fComputedNormalIndices->size() == numFaceIndices);
}
}
bool uvFaceVarying = false;
const IndexBuffer::index_t* uvIndices = NULL;
if (fCheckedUVs) {
uvFaceVarying = (fCheckedUVsScope == Alembic::AbcGeom::kFacevaryingScope);
if (fCheckedUVIndices) {
uvIndices = fCheckedUVIndices->get();
assert(fCheckedUVIndices->size() == numFaceIndices);
}
}
MultiIndexedStreamsConverter<IndexBuffer::index_t> converter(
numFaceIndices, faceIndices);
if (normalFaceVarying) {
converter.addMultiIndexedStream(normalIndices);
}
if (uvFaceVarying) {
converter.addMultiIndexedStream(uvIndices);
}
if (converter.numStreams() == 1) {
fVertAttribsIndices.reset();
fMappedFaceIndices = fFaceIndicesCache.getValue();
fNumVertices = fPositionsCache.getValue()->size() / 3;
return;
}
converter.compute();
fMappedFaceIndices = SharedArray<IndexBuffer::index_t>::create(
converter.mappedFaceIndices(), numFaceIndices);
fVertAttribsIndices = converter.vertAttribsIndices();
fNumVertices = converter.numVertices();
}
void Triangulator::remapVertAttribs()
{
if (!fVertAttribsIndices) {
fMappedPositions = fPositionsCache.getValue();
if (fComputedNormals) {
if (fComputedNormalIndices) {
fMappedNormals = convertMultiIndexedStream<3>(
fComputedNormals, fComputedNormalIndices);
}
else {
fMappedNormals = fComputedNormals;
}
}
if (fCheckedUVs) {
if (fCheckedUVIndices) {
fMappedUVs = convertMultiIndexedStream<2>(
fCheckedUVs, fCheckedUVIndices);
}
else {
fMappedUVs = fCheckedUVs;
}
}
return;
}
const float* positions = fPositionsCache.getValue()->get();
const IndexBuffer::index_t* faceIndices = fFaceIndicesCache.getValue()->get();
const float* normals = NULL;
const IndexBuffer::index_t* normalIndices = NULL;
if (fComputedNormals) {
normals = fComputedNormals->get();
if (fComputedNormalIndices) {
normalIndices = fComputedNormalIndices->get();
}
}
const float* UVs = NULL;
const IndexBuffer::index_t* uvIndices = NULL;
if (fCheckedUVs) {
UVs = fCheckedUVs->get();
if (fCheckedUVIndices) {
uvIndices = fCheckedUVIndices->get();
}
}
MultiIndexedStreamsRemapper<IndexBuffer::index_t> remapper(
faceIndices, fNumVertices, fVertAttribsIndices.get());
remapper.addMultiIndexedStream(positions, NULL, false, 3);
if (normals) {
remapper.addMultiIndexedStream(normals, normalIndices,
fComputedNormalsScope == Alembic::AbcGeom::kFacevaryingScope, 3);
}
if (UVs) {
remapper.addMultiIndexedStream(UVs, uvIndices,
fCheckedUVsScope == Alembic::AbcGeom::kFacevaryingScope, 2);
}
remapper.compute();
fMappedPositions = SharedArray<float>::create(
remapper.mappedVertAttribs(0), fNumVertices * 3);
unsigned int streamIndex = 1;
if (normals) {
fMappedNormals = SharedArray<float>::create(
remapper.mappedVertAttribs(streamIndex++), fNumVertices * 3);
}
if (UVs) {
fMappedUVs = SharedArray<float>::create(
remapper.mappedVertAttribs(streamIndex++), fNumVertices * 2);
}
}
void Triangulator::computeWireIndices()
{
size_t numFaceCounts = fFaceCountsCache.getValue()->size();
const unsigned int* faceCounts = fFaceCountsCache.getValue()->get();
size_t numFaceIndices = fFaceIndicesCache.getValue()->size();
const IndexBuffer::index_t* faceIndices = fFaceIndicesCache.getValue()->get();
const IndexBuffer::index_t* mappedFaceIndices = fMappedFaceIndices->get();
WireIndicesGenerator<IndexBuffer::index_t> wireIndicesGenerator(
numFaceCounts, faceCounts, numFaceIndices, faceIndices, mappedFaceIndices);
wireIndicesGenerator.compute();
if (wireIndicesGenerator.numWires() == 0) {
fWireIndices.reset();
return;
}
fWireIndices = SharedArray<IndexBuffer::index_t>::create(
wireIndicesGenerator.wireIndices(), wireIndicesGenerator.numWires() * 2);
}
void Triangulator::triangulate()
{
size_t numFaceCounts = fFaceCountsCache.getValue()->size();
const unsigned int* faceCounts = fFaceCountsCache.getValue()->get();
const IndexBuffer::index_t* faceIndices = fMappedFaceIndices->get();
const float* positions = fMappedPositions->get();
const float* normals = fMappedNormals ? fMappedNormals->get() : NULL;
if (numFaceCounts == 0) {
fTriangleIndices.reset();
return;
}
PolyTriangulator<IndexBuffer::index_t> polyTriangulator(
numFaceCounts, faceCounts, faceIndices, true, positions, normals);
polyTriangulator.compute();
fTriangleIndices = SharedArray<IndexBuffer::index_t>::create(
polyTriangulator.triangleIndices(), polyTriangulator.numTriangles() * 3);
}
NurbsTessellator::NurbsTessellator(
Alembic::AbcGeom::INuPatchSchema& abcNurbs,
const bool needUVs)
: DataProvider(abcNurbs, abcNurbs.getTimeSampling(),
abcNurbs.getNumSamples(), needUVs),
fSurfaceValid(false)
{
fPositionsCache.init(abcNurbs.getPositionsProperty());
fNumUCache.init(Alembic::Abc::IInt32Property(abcNurbs.getPtr(), "nu"));
fNumVCache.init(Alembic::Abc::IInt32Property(abcNurbs.getPtr(), "nv"));
fUOrderCache.init(Alembic::Abc::IInt32Property(abcNurbs.getPtr(), "uOrder"));
fVOrderCache.init(Alembic::Abc::IInt32Property(abcNurbs.getPtr(), "vOrder"));
fUKnotCache.init(abcNurbs.getUKnotsProperty());
fVKnotCache.init(abcNurbs.getVKnotsProperty());
Alembic::AbcGeom::IFloatArrayProperty positionWeights = abcNurbs.getPositionWeightsProperty();
if (positionWeights.valid()) {
fPositionWeightsCache.init(positionWeights);
}
if (abcNurbs.hasTrimCurve()) {
fTrimNumLoopsCache.init(
Alembic::Abc::IInt32Property(abcNurbs.getPtr(), "trim_nloops"));
fTrimNumCurvesCache.init(
Alembic::Abc::IInt32ArrayProperty(abcNurbs.getPtr(), "trim_ncurves"));
fTrimNumVerticesCache.init(
Alembic::Abc::IInt32ArrayProperty(abcNurbs.getPtr(), "trim_n"));
fTrimOrderCache.init(
Alembic::Abc::IInt32ArrayProperty(abcNurbs.getPtr(), "trim_order"));
fTrimKnotCache.init(
Alembic::Abc::IFloatArrayProperty(abcNurbs.getPtr(), "trim_knot"));
fTrimUCache.init(
Alembic::Abc::IFloatArrayProperty(abcNurbs.getPtr(), "trim_u"));
fTrimVCache.init(
Alembic::Abc::IFloatArrayProperty(abcNurbs.getPtr(), "trim_v"));
fTrimWCache.init(
Alembic::Abc::IFloatArrayProperty(abcNurbs.getPtr(), "trim_w"));
}
}
NurbsTessellator::~NurbsTessellator()
{
fPositionsCache.reset();
fNumUCache.reset();
fNumVCache.reset();
fUOrderCache.reset();
fVOrderCache.reset();
fUKnotCache.reset();
fVKnotCache.reset();
fPositionWeightsCache.reset();
fTrimNumLoopsCache.reset();
fTrimNumCurvesCache.reset();
fTrimNumVerticesCache.reset();
fTrimOrderCache.reset();
fTrimKnotCache.reset();
fTrimUCache.reset();
fTrimVCache.reset();
fTrimWCache.reset();
}
bool NurbsTessellator::valid() const
{
return DataProvider::valid() &&
fPositionsCache.valid() &&
fNumUCache.valid() &&
fNumVCache.valid() &&
fUOrderCache.valid() &&
fVOrderCache.valid() &&
fUKnotCache.valid() &&
fVKnotCache.valid();
}
std::shared_ptr<const ShapeSample>
NurbsTessellator::getSample(double seconds)
{
if (!fWireIndices || !fTriangleIndices) {
std::shared_ptr<ShapeSample> sample =
ShapeSample::createEmptySample(seconds);
return sample;
}
std::vector<std::shared_ptr<IndexBuffer> > triangleVertIndices;
triangleVertIndices.push_back(IndexBuffer::create(fTriangleIndices));
std::shared_ptr<ShapeSample> sample = ShapeSample::create(
seconds,
fWireIndices->size() / 2,
fPositions->size() / 3,
IndexBuffer::create(fWireIndices),
triangleVertIndices,
VertexBuffer::createPositions(fPositions),
getBoundingBox(),
Config::kDefaultGrayColor,
isVisible()
);
if (fNormals) {
sample->setNormals(
VertexBuffer::createNormals(fNormals));
}
if (fUVs) {
sample->setUVs(
VertexBuffer::createUVs(fUVs));
}
return sample;
}
TimeInterval NurbsTessellator::updateCache(chrono_t time)
{
TimeInterval validityInterval(DataProvider::updateCache(time));
bool positionsChanged = fPositionsCache.setTime(time);
bool topologyChanged = fNumUCache.setTime(time);
topologyChanged = fNumVCache.setTime(time) || topologyChanged;
topologyChanged = fUOrderCache.setTime(time) || topologyChanged;
topologyChanged = fVOrderCache.setTime(time) || topologyChanged;
bool knotChanged = fUKnotCache.setTime(time);
knotChanged = fVKnotCache.setTime(time) || knotChanged;
if (fPositionWeightsCache.valid()) {
positionsChanged = fPositionWeightsCache.setTime(time) || positionsChanged;
}
bool trimCurvesChanged = false;
if (fTrimNumLoopsCache.valid()) {
trimCurvesChanged = fTrimNumLoopsCache.setTime(time) || trimCurvesChanged;
trimCurvesChanged = fTrimNumCurvesCache.setTime(time) || trimCurvesChanged;
trimCurvesChanged = fTrimNumVerticesCache.setTime(time) || trimCurvesChanged;
trimCurvesChanged = fTrimOrderCache.setTime(time) || trimCurvesChanged;
trimCurvesChanged = fTrimKnotCache.setTime(time) || trimCurvesChanged;
trimCurvesChanged = fTrimUCache.setTime(time) || trimCurvesChanged;
trimCurvesChanged = fTrimVCache.setTime(time) || trimCurvesChanged;
trimCurvesChanged = fTrimWCache.setTime(time) || trimCurvesChanged;
}
validityInterval &= fPositionsCache.getValidityInterval();
validityInterval &= fNumUCache.getValidityInterval();
validityInterval &= fNumVCache.getValidityInterval();
validityInterval &= fUOrderCache.getValidityInterval();
validityInterval &= fVOrderCache.getValidityInterval();
validityInterval &= fUKnotCache.getValidityInterval();
validityInterval &= fVKnotCache.getValidityInterval();
if (fPositionWeightsCache.valid()) {
validityInterval &= fPositionWeightsCache.getValidityInterval();
}
if (fTrimNumLoopsCache.valid()) {
validityInterval &= fTrimNumLoopsCache.getValidityInterval();
validityInterval &= fTrimNumCurvesCache.getValidityInterval();
validityInterval &= fTrimNumVerticesCache.getValidityInterval();
validityInterval &= fTrimOrderCache.getValidityInterval();
validityInterval &= fTrimKnotCache.getValidityInterval();
validityInterval &= fTrimUCache.getValidityInterval();
validityInterval &= fTrimVCache.getValidityInterval();
validityInterval &= fTrimWCache.getValidityInterval();
}
check();
bool rebuild = topologyChanged || knotChanged ||
trimCurvesChanged || fNurbsData.object().isNull();
setNurbs(rebuild, positionsChanged);
if (rebuild || positionsChanged) {
tessellate();
}
if (isVisible()) {
convertToPoly();
}
return validityInterval;
}
void NurbsTessellator::check()
{
fSurfaceValid = true;
int uDegree = fUOrderCache.getValue() - 1;
int vDegree = fVOrderCache.getValue() - 1;
int numUCV = fNumUCache.getValue();
int numVCV = fNumVCache.getValue();
int numUKnots = (int)fUKnotCache.getValue()->size();
int numVKnots = (int)fVKnotCache.getValue()->size();
if (numUKnots != numUCV + uDegree + 1 || numVKnots != numVCV + vDegree + 1) {
fSurfaceValid = false;
DisplayWarning(kBadNurbsMsg);
return;
}
size_t numCVs = numUCV * numVCV;
if (numCVs * 3 != fPositionsCache.getValue()->size()) {
fSurfaceValid = false;
DisplayWarning(kBadNurbsMsg);
return;
}
if (fPositionWeightsCache.valid() &&
numCVs != fPositionWeightsCache.getValue()->size()) {
fSurfaceValid = false;
DisplayWarning(kBadNurbsMsg);
return;
}
}
void NurbsTessellator::setNurbs(bool rebuild, bool positionsChanged)
{
if (!fSurfaceValid) {
return;
}
unsigned int numU = 0;
unsigned int numV = 0;
if (rebuild || positionsChanged) {
numU = fNumUCache.getValue();
numV = fNumVCache.getValue();
const float* positions = fPositionsCache.getValue()->
get();
const float* positionWeights = NULL;
if (fPositionWeightsCache.valid()) {
positionWeights = fPositionWeightsCache.getValue()->get();
}
for (unsigned int u = 0; u < numU; u++) {
for (unsigned int v = 0; v < numV; v++) {
unsigned int alembicIndex = v * numU + u;
unsigned int mayaIndex = u * numV + (numV - v - 1);
MPoint point(positions[alembicIndex * 3 + 0],
positions[alembicIndex * 3 + 1],
positions[alembicIndex * 3 + 2],
1.0);
if (positionWeights) {
point.w = positionWeights[alembicIndex];
}
mayaPositions[mayaIndex] = point;
}
}
}
if (rebuild) {
unsigned int uDegree = fUOrderCache.getValue() - 1;
unsigned int vDegree = fVOrderCache.getValue() - 1;
bool notOpen = true;
for (unsigned int v = 0; notOpen && v < numV; v++) {
for (unsigned int u = 0; u < uDegree; u++) {
unsigned int firstIndex = u * numV + (numV - v - 1);
unsigned int lastPeriodicIndex = (numU - uDegree + u) * numV + (numV - v - 1);
if (!mayaPositions[firstIndex].isEquivalent(mayaPositions[lastPeriodicIndex])) {
notOpen = false;
break;
}
}
}
for (unsigned int v = 0; v < numV; v++) {
unsigned int lastUIndex = (numU - 1) * numV + (numV - v - 1);
if (!mayaPositions[numV-v-1].isEquivalent(mayaPositions[lastUIndex])) {
break;
}
}
}
notOpen = true;
for (unsigned int u = 0; notOpen && u < numU; u++) {
for (unsigned int v = 0; v < vDegree; v++) {
unsigned int firstIndex = u * numV + (numV - v - 1);
unsigned int lastPeriodicIndex = u * numV + (vDegree - v - 1);
if (!mayaPositions[firstIndex].isEquivalent(mayaPositions[lastPeriodicIndex])) {
notOpen = false;
break;
}
}
}
for (unsigned int u = 0; u < numU; u++) {
if (!mayaPositions[u*numV+(numV-1)].isEquivalent(mayaPositions[u*numV])) {
break;
}
}
}
unsigned int numUKnot = (unsigned int)fUKnotCache.getValue()->size();
unsigned int numVKnot = (unsigned int)fVKnotCache.getValue()->size();
const float* uKnot = fUKnotCache.getValue()->get();
const float* vKnot = fVKnotCache.getValue()->get();
MObject nurbsData = fNurbsData.create();
MObject nurbs = fNurbs.create(mayaPositions,
mayaUKnots, mayaVKnots,
uDegree, vDegree,
uForm,
vForm,
true,
nurbsData,
&status);
if (status != MS::kSuccess || nurbs.
isNull()) {
return;
}
if (fTrimNumLoopsCache.valid()) {
unsigned int trimNumLoops = fTrimNumLoopsCache.getValue();
double startU, endU, startV, endV;
fNurbs.getKnotDomain(startU, endU, startV, endV);
double offsetV = startV + endV;
const unsigned int* trimNumCurves =
fTrimNumCurvesCache.getValue()->
get();
const unsigned int* trimNumVertices =
fTrimNumVerticesCache.getValue()->
get();
const unsigned int* trimOrder =
fTrimOrderCache.getValue()->get();
const float* trimKnot = fTrimKnotCache.getValue()->get();
const float* trimU = fTrimUCache.getValue()->get();
const float* trimV = fTrimVCache.getValue()->get();
const float* trimW = fTrimWCache.getValue()->get();
for (unsigned int i = 0; i < trimNumLoops; i++) {
unsigned int numCurves = *trimNumCurves;
for (unsigned int j = 0; j < numCurves; j++) {
unsigned int numVertices = *trimNumVertices;
unsigned int degree = *trimOrder - 1;
unsigned int numKnots = numVertices + degree + 1;
for (unsigned int k = 0; k < numVertices; k++) {
MPoint point(trimU[k], offsetV - trimV[k], 0, trimW[k]);
controlPoints[k] = point;
}
if (status == MS::kSuccess && !curveObject.
isNull()) {
boundary[j] = curveDataObject;
}
trimNumVertices++;
trimOrder++;
trimKnot += numKnots;
trimU += numVertices;
trimV += numVertices;
trimW += numVertices;
}
boundaryArray.
append(boundary);
trimNumCurves++;
}
for (
unsigned int i = 0; i < boundaryArray.
length(); i++) {
if (i > 0) {
if (status != MS::kSuccess) continue;
if (status != MS::kSuccess) continue;
bool isOuterBoundary = false;
double length = loop.length();
unsigned int segment = std::max(loop.numCVs(), 10);
for (unsigned int j = 0; j < segment; j++) {
double param = loop.findParamFromLength(length * j / segment);
loop.getPointAtParam(param, curvePoints[j]);
}
MPoint rightMostPoint = curvePoints[0];
unsigned int rightMostIndex = 0;
for (unsigned int j = 0; j < curvePoints.length(); j++) {
if (rightMostPoint.
x < curvePoints[j].x) {
rightMostPoint = curvePoints[j];
rightMostIndex = j;
}
}
unsigned int beforeIndex = (rightMostIndex == 0) ? curvePoints.length() - 1 : rightMostIndex - 1;
unsigned int afterIndex = (rightMostIndex == curvePoints.length() - 1) ? 0 : rightMostIndex + 1;
for (unsigned int j = 0; j < curvePoints.length(); j++) {
if (fabs(curvePoints[beforeIndex].x - curvePoints[rightMostIndex].x) < 1e-5) {
beforeIndex = (beforeIndex == 0) ? curvePoints.length() - 1 : beforeIndex - 1;
}
}
for (unsigned int j = 0; j < curvePoints.length(); j++) {
if (fabs(curvePoints[afterIndex].x - curvePoints[rightMostIndex].x) < 1e-5) {
afterIndex = (afterIndex == curvePoints.length() - 1) ? 0 : afterIndex + 1;
}
}
if (fabs(curvePoints[afterIndex].x - curvePoints[rightMostIndex].x) < 1e-5 &&
fabs(curvePoints[beforeIndex].x - curvePoints[rightMostIndex].x) < 1e-5) {
continue;
}
MVector vector1 = curvePoints[beforeIndex] - curvePoints[rightMostIndex];
MVector vector2 = curvePoints[afterIndex] - curvePoints[rightMostIndex];
if ((vector1 ^ vector2).z < 0) {
isOuterBoundary = true;
}
if (isOuterBoundary) {
status = fNurbs.trimWithBoundaries(oneRegion, false, 1e-3, 1e-5, true);
if (status != MS::kSuccess) {
return;
}
}
}
oneRegion.
append(boundaryArray[i]);
}
status = fNurbs.trimWithBoundaries(oneRegion, false, 1e-3, 1e-5, true);
if (status != MS::kSuccess) {
return;
}
}
}
else {
assert(!fNurbsData.object().isNull());
if (positionsChanged) {
fNurbs.setCVs(mayaPositions);
}
}
}
void NurbsTessellator::tessellate()
{
if (!fSurfaceValid || fNurbsData.object().isNull()) {
return;
}
MObject polyMeshData = fPolyMeshData.create();
MObject polyObject = fNurbs.tesselate(params, polyMeshData, &status);
return;
}
status = fPolyMesh.setObject(polyObject);
assert(status == MS::kSuccess);
}
void NurbsTessellator::convertToPoly()
{
if (!fSurfaceValid || fPolyMeshData.object().isNull() ||
fPolyMesh.numVertices() == 0 || fPolyMesh.numFaceVertices() == 0) {
fTriangleIndices.reset();
fWireIndices.reset();
fPositions.reset();
fNormals.reset();
fUVs.reset();
return;
}
MayaMeshExtractor<IndexBuffer::index_t> extractor(fPolyMeshData.object());
extractor.setWantUVs(fNeedUVs);
extractor.compute();
fTriangleIndices = extractor.triangleIndices();
fWireIndices = extractor.wireIndices();
fPositions = extractor.positions();
fNormals = extractor.normals();
fUVs.reset();
if (fNeedUVs) {
fUVs = extractor.uvs();
}
}
SubDSmoother::SubDSmoother(
Alembic::AbcGeom::ISubDSchema& abcSubD,
const bool needUVs)
: PolyDataProvider(abcSubD, needUVs)
{
fFaceIndicesCache.init(abcSubD.getFaceIndicesProperty());
Alembic::Abc::IInt32ArrayProperty creaseIndicesProp =
abcSubD.getCreaseIndicesProperty();
Alembic::Abc::IInt32ArrayProperty creaseLengthsProp =
abcSubD.getCreaseLengthsProperty();
Alembic::Abc::IFloatArrayProperty creaseSharpnessesProp =
abcSubD.getCreaseSharpnessesProperty();
if (creaseIndicesProp.valid() &&
creaseLengthsProp.valid() &&
creaseSharpnessesProp.valid()) {
fCreaseIndicesCache.init(creaseIndicesProp);
fCreaseLengthsCache.init(creaseLengthsProp);
fCreaseSharpnessesCache.init(creaseSharpnessesProp);
}
Alembic::Abc::IInt32ArrayProperty cornerIndicesProp =
abcSubD.getCornerIndicesProperty();
Alembic::Abc::IFloatArrayProperty cornerSharpnessesProp =
abcSubD.getCornerSharpnessesProperty();
if (cornerIndicesProp.valid() && cornerSharpnessesProp.valid()) {
fCornerIndicesCache.init(cornerIndicesProp);
fCornerSharpnessesCache.init(cornerSharpnessesProp);
}
Alembic::Abc::IInt32ArrayProperty holesProp =
abcSubD.getHolesProperty();
if (holesProp.valid()) {
fHolesCache.init(holesProp);
}
if (fNeedUVs) {
Alembic::AbcGeom::IV2fGeomParam UVs = abcSubD.getUVsParam();
if (UVs.valid()) {
fUVsScope = UVs.getScope();
if (fUVsScope == Alembic::AbcGeom::kVaryingScope ||
fUVsScope == Alembic::AbcGeom::kVertexScope ||
fUVsScope == Alembic::AbcGeom::kFacevaryingScope) {
fUVsCache.init(UVs.getValueProperty());
if (UVs.isIndexed()) {
fUVIndicesCache.init(UVs.getIndexProperty());
}
}
}
}
}
SubDSmoother::~SubDSmoother()
{
fFaceIndicesCache.reset();
fCreaseIndicesCache.reset();
fCreaseLengthsCache.reset();
fCreaseSharpnessesCache.reset();
fCornerIndicesCache.reset();
fCornerSharpnessesCache.reset();
fHolesCache.reset();
fUVsScope = Alembic::AbcGeom::kUnknownScope;
fUVsCache.reset();
fUVIndicesCache.reset();
}
bool SubDSmoother::valid() const
{
return PolyDataProvider::valid() &&
fFaceIndicesCache.valid();
}
std::shared_ptr<const ShapeSample>
SubDSmoother::getSample(double seconds)
{
if (!fWireIndices || !fTriangleIndices) {
std::shared_ptr<ShapeSample> sample =
ShapeSample::createEmptySample(seconds);
return sample;
}
std::vector<std::shared_ptr<IndexBuffer> > triangleVertIndices;
triangleVertIndices.push_back(IndexBuffer::create(fTriangleIndices));
std::shared_ptr<ShapeSample> sample = ShapeSample::create(
seconds,
fWireIndices->size() / 2,
fPositions->size() / 3,
IndexBuffer::create(fWireIndices),
triangleVertIndices,
VertexBuffer::createPositions(fPositions),
getBoundingBox(),
Config::kDefaultGrayColor,
isVisible()
);
if (fNormals) {
sample->setNormals(
VertexBuffer::createNormals(fNormals));
}
if (fUVs) {
sample->setUVs(
VertexBuffer::createUVs(fUVs));
}
return sample;
}
TimeInterval SubDSmoother::updateCache(chrono_t time)
{
bool topologyChanged = fFaceCountsCache.setTime(time);
bool positionChanged = fPositionsCache.setTime(time);
TimeInterval validityInterval(PolyDataProvider::updateCache(time));
topologyChanged = fFaceIndicesCache.setTime(time) || topologyChanged;
bool creaseEdgeChanged = false;
if (fCreaseSharpnessesCache.valid()) {
creaseEdgeChanged = fCreaseIndicesCache.setTime(time) || creaseEdgeChanged;
creaseEdgeChanged = fCreaseLengthsCache.setTime(time) || creaseEdgeChanged;
creaseEdgeChanged = fCreaseSharpnessesCache.setTime(time) || creaseEdgeChanged;
}
bool creaseVertexChanged = false;
if (fCornerSharpnessesCache.valid()) {
creaseVertexChanged = fCornerIndicesCache.setTime(time) || creaseVertexChanged;
creaseVertexChanged = fCornerSharpnessesCache.setTime(time) || creaseVertexChanged;
}
bool invisibleFaceChanged = false;
if (fHolesCache.valid()) {
invisibleFaceChanged = fHolesCache.setTime(time);
}
bool uvChanged = false;
if (fUVsCache.valid()) {
uvChanged = fUVsCache.setTime(time);
if (fUVIndicesCache.valid()) {
uvChanged = fUVIndicesCache.setTime(time) || uvChanged;
}
}
validityInterval &= fFaceIndicesCache.getValidityInterval();
if (fCreaseSharpnessesCache.valid()) {
validityInterval &= fCreaseIndicesCache.getValidityInterval();
validityInterval &= fCreaseLengthsCache.getValidityInterval();
validityInterval &= fCreaseSharpnessesCache.getValidityInterval();
}
if (fCornerSharpnessesCache.valid()) {
validityInterval &= fCornerIndicesCache.getValidityInterval();
validityInterval &= fCornerSharpnessesCache.getValidityInterval();
}
if (fHolesCache.valid()) {
validityInterval &= fHolesCache.getValidityInterval();
}
if (fUVsCache.valid()) {
validityInterval &= fUVsCache.getValidityInterval();
if (fUVIndicesCache.valid()) {
validityInterval &= fUVIndicesCache.getValidityInterval();
}
}
check();
if (topologyChanged || creaseEdgeChanged || creaseVertexChanged ||
invisibleFaceChanged || fSubDData.object().isNull()) {
rebuildSubD();
setCreaseEdges();
setCreaseVertices();
setInvisibleFaces();
setUVs();
}
else {
if (positionChanged) {
setPositions();
}
if (uvChanged) {
setUVs();
}
}
if (isVisible()) {
convertToPoly();
}
return validityInterval;
}
void SubDSmoother::check()
{
size_t numFaceIndices = fFaceIndicesCache.getValue()->size();
size_t numVerts = fPositionsCache.getValue()->size() / 3;
size_t numExpectedUVs = 0;
if (fUVsScope == Alembic::AbcGeom::kVaryingScope ||
fUVsScope == Alembic::AbcGeom::kVertexScope) {
numExpectedUVs = numVerts;
}
else if (fUVsScope == Alembic::AbcGeom::kFacevaryingScope) {
numExpectedUVs = numFaceIndices;
}
size_t numActualUVs = 0;
if (fUVsCache.valid()) {
if (fUVIndicesCache.valid()) {
numActualUVs = fUVIndicesCache.getValue()->size();
}
else {
numActualUVs = fUVsCache.getValue()->size() / 2;
}
}
fCheckedUVsScope = Alembic::AbcGeom::kUnknownScope;
fCheckedUVs.reset();
fCheckedUVIndices.reset();
if (numExpectedUVs == numActualUVs) {
if (fUVsCache.valid()) {
fCheckedUVsScope = fUVsScope;
fCheckedUVs = fUVsCache.getValue();
if (fUVIndicesCache.valid()) {
fCheckedUVIndices = fUVIndicesCache.getValue();
}
}
}
else {
DisplayWarning(kBadUVsMsg);
}
}
void SubDSmoother::rebuildSubD()
{
size_t numFaceCounts = fFaceCountsCache.getValue()->size();
const unsigned int* faceCounts = fFaceCountsCache.getValue()->get();
size_t numFaceIndices = fFaceIndicesCache.getValue()->size();
const IndexBuffer::index_t* faceIndices = fFaceIndicesCache.getValue()->get();
size_t numPositions = fPositionsCache.getValue()->size();
const float* positions = fPositionsCache.getValue()->get();
size_t numVertices = numPositions / 3;
mayaCounts.
setLength((
unsigned int)numFaceCounts);
mayaConnects.
setLength((
unsigned int)numFaceIndices);
for (unsigned int i = 0, polyVertOffset = 0; i < numFaceCounts; i++) {
unsigned int faceCount = mayaCounts[i] = faceCounts[i];
for (unsigned int j = 0; j < faceCount; j++) {
mayaConnects[polyVertOffset + j] = faceIndices[polyVertOffset + faceCount - j - 1];
}
polyVertOffset += faceCount;
}
mayaPositions.
setLength((
unsigned int)numVertices);
for (unsigned int i = 0; i < numVertices; i++) {
positions[i * 3 + 1],
positions[i * 3 + 2]);
}
MObject subdData = fSubDData.create(&status);
assert(status == MS::kSuccess);
fSubD.setCheckSamePointTwice(false);
MObject subd = fSubD.create((
int)numVertices, (
int)numFaceCounts,
mayaPositions, mayaCounts, mayaConnects, subdData, &status);
if (status != MS::kSuccess || subd.
isNull()) {
}
}
void SubDSmoother::setPositions()
{
if (fSubDData.object().isNull()) {
return;
}
size_t numPositions = fPositionsCache.getValue()->size();
const float* positions = fPositionsCache.getValue()->get();
size_t numVertices = numPositions / 3;
mayaPositions.
setLength((
unsigned int)numVertices);
for (unsigned int i = 0; i < numVertices; i++) {
positions[i * 3 + 1],
positions[i * 3 + 2]);
}
fSubD.setPoints(mayaPositions);
}
void SubDSmoother::setCreaseEdges()
{
if (fSubDData.object().isNull() ||
!fCreaseIndicesCache.valid() ||
!fCreaseLengthsCache.valid() ||
!fCreaseSharpnessesCache.valid()) {
return;
}
size_t numCreaseIndices = fCreaseIndicesCache.getValue()->size();
const unsigned int* creaseIndices = fCreaseIndicesCache.getValue()->get();
size_t numCreaseLengths = fCreaseLengthsCache.getValue()->size();
const unsigned int* creaseLengths = fCreaseLengthsCache.getValue()->get();
size_t numCreaseSharpnesses = fCreaseSharpnessesCache.getValue()->size();
const float* creaseSharpnesses = fCreaseSharpnessesCache.getValue()->get();
if (numCreaseSharpnesses == 0) {
return;
}
typedef std::unordered_map<TEdge,int> EdgeMap;
EdgeMap edgeMap(size_t(fSubD.numEdges() / 0.75f));
int numEdges = fSubD.numEdges();
for (int i = 0; i < numEdges; i++) {
int2 vertexList;
fSubD.getEdgeVertices(i, vertexList);
if (vertexList[0] > vertexList[1]) {
std::swap(vertexList[0], vertexList[1]);
}
edgeMap.insert(std::make_pair(std::make_pair(vertexList[0], vertexList[1]), i));
}
for (size_t i = 0, index = 0; i < numCreaseLengths && i < numCreaseSharpnesses; i++) {
unsigned int length = creaseLengths[i];
float sharpness = creaseSharpnesses[i];
if (length == 2 && index + length <= numCreaseIndices) {
std::pair<int,int> edge = std::make_pair(creaseIndices[index],creaseIndices[index+1]);
if (edge.first > edge.second) {
std::swap(edge.first, edge.second);
}
EdgeMap::iterator iter = edgeMap.find(edge);
if (iter != edgeMap.end() && iter->second < numEdges) {
mayaEdgeIds.
append(iter->second);
mayaCreaseData.
append(sharpness);
}
}
index += length;
}
status = fSubD.setCreaseEdges(mayaEdgeIds, mayaCreaseData);
assert(status == MS::kSuccess);
}
void SubDSmoother::setCreaseVertices()
{
if (fSubDData.object().isNull() ||
!fCornerIndicesCache.valid() ||
!fCornerSharpnessesCache.valid()) {
return;
}
size_t numCornerIndices = fCornerIndicesCache.getValue()->size();
const unsigned int* cornerIndices = fCornerIndicesCache.getValue()->get();
size_t numCornerSharpnesses = fCornerSharpnessesCache.getValue()->size();
const float* cornerSharpnesses = fCornerSharpnessesCache.getValue()->get();
if (cornerSharpnesses == 0) {
return;
}
size_t numCreaseVertices = std::min(numCornerIndices, numCornerSharpnesses);
mayaVertexIds.
setLength((
unsigned int)numCreaseVertices);
mayaCreaseData.
setLength((
unsigned int)numCreaseVertices);
for (unsigned int i = 0; i < numCreaseVertices; i++) {
mayaVertexIds[i] = cornerIndices[i];
mayaCreaseData[i] = cornerSharpnesses[i];
}
status = fSubD.setCreaseVertices(mayaVertexIds, mayaCreaseData);
assert(status == MS::kSuccess);
}
void SubDSmoother::setInvisibleFaces()
{
if (fSubDData.object().isNull() ||
!fHolesCache.valid()) {
return;
}
size_t numHoles = fHolesCache.getValue()->size();
const unsigned int* holes = fHolesCache.getValue()->get();
if (numHoles == 0) {
return;
}
MUintArray mayaFaceIds(holes, (
unsigned int)numHoles);
status = fSubD.setInvisibleFaces(mayaFaceIds);
assert(status == MS::kSuccess);
}
void SubDSmoother::setUVs()
{
if (fSubDData.object().isNull()) {
return;
}
if (fCheckedUVsScope != Alembic::AbcGeom::kVaryingScope &&
fCheckedUVsScope != Alembic::AbcGeom::kVertexScope &&
fCheckedUVsScope != Alembic::AbcGeom::kFacevaryingScope) {
return;
}
if (!fCheckedUVs) {
return;
}
size_t numFaceCounts = fFaceCountsCache.getValue()->size();
const unsigned int* faceCounts = fFaceCountsCache.getValue()->get();
size_t numFaceIndices = fFaceIndicesCache.getValue()->size();
const IndexBuffer::index_t* faceIndices = fFaceIndicesCache.getValue()->get();
size_t numUVs = fCheckedUVs->size();
const float* UVs = fCheckedUVs->get();
const IndexBuffer::index_t* uvIndices = NULL;
if (fCheckedUVIndices) {
uvIndices = fCheckedUVIndices->get();
}
if (int(numUVs) != fSubD.numUVs()) {
fSubD.clearUVs();
}
if (numUVs == 0) {
return;
}
for (unsigned int i = 0; i < numUVs; i++) {
mayaUArray[i] = UVs[i * 2 + 0];
mayaVArray[i] = UVs[i * 2 + 1];
}
MIntArray mayaUVCounts((
unsigned int)numFaceCounts);
MIntArray mayaUVIds((
unsigned int)numFaceIndices);
for (unsigned int i = 0, polyVertOffset = 0; i < numFaceCounts; i++) {
unsigned int faceCount = mayaUVCounts[i] = faceCounts[i];
for (unsigned int j = 0; j < faceCount; j++) {
unsigned int uvIndex = 0;
unsigned int polyVertIndex = polyVertOffset + faceCount - j - 1;
if (fCheckedUVsScope == Alembic::AbcGeom::kVaryingScope ||
fCheckedUVsScope == Alembic::AbcGeom::kVertexScope) {
unsigned int vertIndex = faceIndices[polyVertIndex];
uvIndex = uvIndices ? uvIndices[vertIndex] : vertIndex;
}
else if (fCheckedUVsScope == Alembic::AbcGeom::kFacevaryingScope) {
uvIndex = uvIndices ? uvIndices[polyVertIndex] : polyVertIndex;
}
else {
assert(0);
}
mayaUVIds[polyVertOffset + j] = uvIndex;
}
polyVertOffset += faceCount;
}
status = fSubD.setUVs(mayaUArray, mayaVArray);
assert(status == MS::kSuccess);
status = fSubD.assignUVs(mayaUVCounts, mayaUVIds);
assert(status == MS::kSuccess);
}
void SubDSmoother::convertToPoly()
{
if (fSubDData.object().isNull() ||
fSubD.numVertices() == 0 || fSubD.numFaceVertices() == 0) {
fTriangleIndices.reset();
fWireIndices.reset();
fPositions.reset();
fNormals.reset();
fUVs.reset();
return;
}
MObject smoothMeshObj = fSubD.generateSmoothMesh(smoothMeshDataObj, &smoothOptions);
MayaMeshExtractor<IndexBuffer::index_t> extractor(smoothMeshDataObj);
extractor.setWantUVs(fNeedUVs);
extractor.compute();
fTriangleIndices = extractor.triangleIndices();
fWireIndices = extractor.wireIndices();
fPositions = extractor.positions();
fNormals = extractor.normals();
fUVs.reset();
if (fNeedUVs) {
fUVs = extractor.uvs();
}
}
AlembicCacheObjectReader::Ptr
AlembicCacheObjectReader::create(Alembic::Abc::IObject& abcObj, bool needUVs)
{
CheckInterruptAndPause("reader initialization");
if (Alembic::AbcGeom::IPolyMesh::matches(abcObj.getHeader()) ||
Alembic::AbcGeom::INuPatch::matches(abcObj.getHeader()) ||
Alembic::AbcGeom::ISubD::matches(abcObj.getHeader())) {
Ptr reader = std::make_shared<AlembicCacheMeshReader>(abcObj, needUVs);
return reader->valid() ? reader : Ptr();
}
if (Alembic::AbcGeom::IXform::matches(abcObj.getHeader())) {
Ptr reader = std::make_shared<AlembicCacheXformReader>(abcObj, needUVs);
return reader->valid() ? reader : Ptr();
}
return Ptr();
}
AlembicCacheObjectReader::~AlembicCacheObjectReader()
{}
AlembicCacheTopReader::AlembicCacheTopReader(
Alembic::Abc::IObject abcObj,
const bool needUVs
)
: fBoundingBoxValidityInterval(TimeInterval::kInvalid)
{
fXformData = XformData::create();
size_t numChildren = abcObj.getNumChildren();
for (size_t ii=0; ii<numChildren; ++ii)
{
Alembic::Abc::IObject child(abcObj, abcObj.getChildHeader(ii).getName());
Ptr childReader = create(child, needUVs);
if (childReader)
fChildren.push_back(childReader);
}
TimeInterval animTimeRange(TimeInterval::kInvalid);
for(const AlembicCacheObjectReader::Ptr& childReader : fChildren) {
animTimeRange |= childReader->getAnimTimeRange();
}
fXformData->setAnimTimeRange(animTimeRange);
}
AlembicCacheTopReader::~AlembicCacheTopReader()
{}
bool AlembicCacheTopReader::valid() const
{
return true;
}
TimeInterval AlembicCacheTopReader::sampleHierarchy(double seconds,
const MMatrix& rootMatrix, TimeInterval rootMatrixInterval)
{
TimeInterval validityInterval(TimeInterval::kInfinite);
TimeInterval bboxValIntrvl(TimeInterval::kInfinite);
for(const AlembicCacheObjectReader::Ptr& childReader : fChildren) {
validityInterval &= childReader->sampleHierarchy(seconds,
rootMatrix, rootMatrixInterval);
bbox.
expand(childReader->getBoundingBox());
bboxValIntrvl &= childReader->getBoundingBoxValidityInterval();
}
assert(validityInterval.contains(seconds));
assert(!(fBoundingBoxValidityInterval & bboxValIntrvl).valid() ||
fBoundingBoxValidityInterval == bboxValIntrvl);
if (seconds == bboxValIntrvl.startTime()) {
fBoundingBox = bbox;
fBoundingBoxValidityInterval = bboxValIntrvl;
std::shared_ptr<GPUCache::XformSample> sample =
GPUCache::XformSample::create(
seconds,
fBoundingBox,
true
);
fXformData->addSample(sample);
}
return validityInterval;
}
TimeInterval AlembicCacheTopReader::sampleShape(double seconds)
{
assert(0);
return TimeInterval(TimeInterval::kInvalid);
}
SubNode::MPtr AlembicCacheTopReader::get() const
{
SubNode::MPtr node =
SubNode::create(
MString(
"|"), fXformData);
for(const AlembicCacheObjectReader::Ptr& childReader : fChildren) {
SubNode::MPtr child = childReader->get();
if (child)
SubNode::connect(node, child);
}
if (node->getChildren().empty()) {
return SubNode::MPtr();
}
return node;
}
{
return fBoundingBox;
}
TimeInterval AlembicCacheTopReader::getBoundingBoxValidityInterval() const
{
return fBoundingBoxValidityInterval;
}
TimeInterval AlembicCacheTopReader::getAnimTimeRange() const
{
return fXformData->animTimeRange();
}
void AlembicCacheTopReader::saveAndReset(AlembicCacheReader& cacheReader)
{
for(const AlembicCacheObjectReader::Ptr& childReader : fChildren) {
childReader->saveAndReset(cacheReader);
}
}
AlembicCacheXformReader::AlembicCacheXformReader(
Alembic::Abc::IObject abcObj,
const bool needUVs
)
: fName(abcObj.getName()),
fValidityInterval(TimeInterval::kInvalid),
fBoundingBoxValidityInterval(TimeInterval::kInvalid)
{
Alembic::AbcGeom::IXform xform(abcObj, Alembic::Abc::kWrapExisting);
Alembic::AbcGeom::IXformSchema schema = xform.getSchema();
fXformCache.init(schema);
Alembic::AbcGeom::IVisibilityProperty visibility =
Alembic::AbcGeom::GetVisibilityProperty(abcObj);
if (visibility) {
fVisibilityCache.init(visibility);
}
fXformData = XformData::create();
const size_t numChildren = abcObj.getNumChildren();
for (size_t ii=0; ii<numChildren; ++ii)
{
Alembic::Abc::IObject child(abcObj, abcObj.getChildHeader(ii).getName());
Ptr childReader = create(child, needUVs);
if (childReader)
fChildren.push_back(childReader);
}
Alembic::Abc::TimeSamplingPtr timeSampling = schema.getTimeSampling();
size_t numSamples = schema.getNumSamples();
TimeInterval animTimeRange(
timeSampling->getSampleTime(0),
timeSampling->getSampleTime(numSamples > 0 ? numSamples-1 : 0) );
for(const AlembicCacheObjectReader::Ptr& childReader : fChildren) {
animTimeRange |= childReader->getAnimTimeRange();
}
fXformData->setAnimTimeRange(animTimeRange);
}
AlembicCacheXformReader::~AlembicCacheXformReader()
{}
bool AlembicCacheXformReader::valid() const
{
return fXformCache.valid();
}
TimeInterval AlembicCacheXformReader::sampleHierarchy(double seconds,
const MMatrix& rootMatrix, TimeInterval rootMatrixInterval)
{
if (!fValidityInterval.contains(seconds)) {
fillTopoAndAttrSample(seconds);
}
MMatrix newRootMatrix = fXformCache.getValue() * rootMatrix;
TimeInterval newRootMatrixInterval =
fXformCache.getValidityInterval() & rootMatrixInterval;
TimeInterval validityInterval = fValidityInterval;
TimeInterval bboxValIntrvl(TimeInterval::kInfinite);
for(const AlembicCacheObjectReader::Ptr& childReader : fChildren) {
validityInterval &= childReader->sampleHierarchy(seconds,
newRootMatrix, newRootMatrixInterval);
bbox.
expand(childReader->getBoundingBox());
bboxValIntrvl &= childReader->getBoundingBoxValidityInterval();
}
assert(validityInterval.contains(seconds));
assert(!(fBoundingBoxValidityInterval & bboxValIntrvl).valid() ||
fBoundingBoxValidityInterval == bboxValIntrvl);
if (seconds == (fValidityInterval & bboxValIntrvl).startTime()) {
fBoundingBox = bbox;
fBoundingBoxValidityInterval = bboxValIntrvl;
std::shared_ptr<GPUCache::XformSample> sample =
GPUCache::XformSample::create(
seconds,
fXformCache.getValue(),
fBoundingBox,
isVisible()
);
fXformData->addSample(sample);
}
return validityInterval;
}
TimeInterval AlembicCacheXformReader::sampleShape(double seconds)
{
assert(0);
return TimeInterval(TimeInterval::kInvalid);
}
SubNode::MPtr AlembicCacheXformReader::get() const
{
SubNode::MPtr node =
SubNode::create(
MString(fName.c_str()), fXformData);
for(const AlembicCacheObjectReader::Ptr& childReader : fChildren) {
SubNode::MPtr child = childReader->get();
if (child)
SubNode::connect(node, child);
}
if (node->getChildren().empty()) {
return SubNode::MPtr();
}
return node;
}
void AlembicCacheXformReader::fillTopoAndAttrSample(chrono_t time)
{
fXformCache.setTime(time);
if (fVisibilityCache.valid()) {
fVisibilityCache.setTime(time);
}
TimeInterval validityInterval(TimeInterval::kInfinite);
validityInterval &= fXformCache.getValidityInterval();
if (fVisibilityCache.valid()) {
validityInterval &= fVisibilityCache.getValidityInterval();
}
assert(validityInterval.valid());
fValidityInterval = validityInterval;
}
bool AlembicCacheXformReader::isVisible() const
{
if (fVisibilityCache.valid() &&
fVisibilityCache.getValue() == char(Alembic::AbcGeom::kVisibilityHidden)) {
return false;
}
return true;
}
MBoundingBox AlembicCacheXformReader::getBoundingBox()
const
{
return fBoundingBox;
}
TimeInterval AlembicCacheXformReader::getBoundingBoxValidityInterval() const
{
return fBoundingBoxValidityInterval;
}
TimeInterval AlembicCacheXformReader::getAnimTimeRange() const
{
return fXformData->animTimeRange();
}
void AlembicCacheXformReader::saveAndReset(AlembicCacheReader& cacheReader)
{
for(const AlembicCacheObjectReader::Ptr& childReader : fChildren) {
childReader->saveAndReset(cacheReader);
}
}
AlembicCacheMeshReader::AlembicCacheMeshReader(
Alembic::Abc::IObject object,
const bool needUVs
)
: fName(object.getName()),
fFullName(object.getFullName()),
fBoundingBoxValidityInterval(TimeInterval::kInvalid),
fNumTransparentSample(0)
{
if (Alembic::AbcGeom::IPolyMesh::matches(object.getHeader())) {
Alembic::AbcGeom::IPolyMesh meshObj(object, Alembic::Abc::kWrapExisting);
Alembic::AbcGeom::IPolyMeshSchema schema = meshObj.getSchema();
if (schema.getPropertyHeader(kCustomPropertyWireIndices) != NULL ||
schema.getPropertyHeader(kCustomPropertyWireIndicesOld) != NULL) {
fDataProvider.reset(new RawDataProvider(schema, needUVs));
}
else {
fDataProvider.reset(new Triangulator(schema, needUVs));
}
}
else if (Alembic::AbcGeom::INuPatch::matches(object.getHeader())) {
Alembic::AbcGeom::INuPatch nurbsObj(object, Alembic::Abc::kWrapExisting);
Alembic::AbcGeom::INuPatchSchema schema = nurbsObj.getSchema();
fDataProvider.reset(new NurbsTessellator(schema, needUVs));
}
else if (Alembic::AbcGeom::ISubD::matches(object.getHeader())) {
Alembic::AbcGeom::ISubD subdObj(object, Alembic::Abc::kWrapExisting);
Alembic::AbcGeom::ISubDSchema schema = subdObj.getSchema();
fDataProvider.reset(new SubDSmoother(schema, needUVs));
}
else {
DisplayWarning(kUnsupportedGeomMsg);
}
fShapeData = ShapeData::create();
fShapeData->setAnimTimeRange(fDataProvider->getAnimTimeRange());
std::string materialAssignmentPath;
if (Alembic::AbcMaterial::getMaterialAssignmentPath(
object, materialAssignmentPath)) {
std::string prefix = "/";
prefix += kMaterialsObject;
prefix += "/";
if (std::equal(prefix.begin(), prefix.end(), materialAssignmentPath.begin())) {
std::string objectName = materialAssignmentPath.substr(prefix.size()).c_str();
if (objectName.find("/") == std::string::npos) {
material = objectName.c_str();
}
}
}
fShapeData->setMaterial(material);
}
}
AlembicCacheMeshReader::~AlembicCacheMeshReader()
{
fDataProvider.reset();
}
bool AlembicCacheMeshReader::valid() const
{
return fDataProvider && fDataProvider->valid();
}
TimeInterval AlembicCacheMeshReader::sampleHierarchy(double seconds,
const MMatrix& rootMatrix, TimeInterval rootMatrixInterval)
{
CheckInterruptAndPause("sampling hierarchy");
if (!fDataProvider->getBBoxAndVisValidityInterval().contains(seconds)) {
fDataProvider->fillBBoxAndVisSample(seconds);
}
TimeInterval validityInterval = fDataProvider->getBBoxAndVisValidityInterval();
fBoundingBox = fDataProvider->getBoundingBox();
fBoundingBox.transformUsing(rootMatrix);
fBoundingBoxValidityInterval = rootMatrixInterval &
fDataProvider->getBoundingBoxValidityInterval();
if (seconds == validityInterval.startTime()) {
std::shared_ptr<const ShapeSample> sample =
fDataProvider->getBBoxPlaceHolderSample(seconds);
fShapeData->addSample(sample);
}
return validityInterval;
}
TimeInterval AlembicCacheMeshReader::sampleShape(double seconds)
{
CheckInterruptAndPause("sampling shape");
if (!fDataProvider->getValidityInterval().contains(seconds)) {
fDataProvider->fillTopoAndAttrSample(seconds);
}
TimeInterval validityInterval = fDataProvider->getValidityInterval();
if (seconds == validityInterval.startTime()) {
if (fDataProvider->isVisible()) {
std::shared_ptr<const ShapeSample> sample =
fDataProvider->getSample(seconds);
fShapeData->addSample(sample);
float alpha = sample->diffuseColor()[3];
if (alpha > 0.0f && alpha < 1.0f) {
fNumTransparentSample++;
}
}
else {
std::shared_ptr<ShapeSample> sample =
ShapeSample::createEmptySample(seconds);
fShapeData->addSample(sample);
}
}
return validityInterval;
}
SubNode::MPtr AlembicCacheMeshReader::get() const
{
if (fShapeData->getSamples().size() == 1 &&
!fShapeData->getSamples().begin()->second->visibility()) {
return SubNode::MPtr();
}
SubNode::MPtr subNode = SubNode::create(
MString(fName.c_str()), fShapeData);
if (fNumTransparentSample == 0) {
subNode->setTransparentType(SubNode::kOpaque);
}
else if (fNumTransparentSample == fShapeData->getSamples().size()) {
subNode->setTransparentType(SubNode::kTransparent);
}
else {
subNode->setTransparentType(SubNode::kOpaqueAndTransparent);
}
return subNode;
}
{
return fBoundingBox;
}
TimeInterval AlembicCacheMeshReader::getBoundingBoxValidityInterval() const
{
return fBoundingBoxValidityInterval;
}
TimeInterval AlembicCacheMeshReader::getAnimTimeRange() const
{
return fShapeData->animTimeRange();
}
void AlembicCacheMeshReader::saveAndReset(AlembicCacheReader& cacheReader)
{
fBoundingBoxValidityInterval = TimeInterval(TimeInterval::kInvalid);
fNumTransparentSample = 0;
ShapeData::MPtr newShapeData = ShapeData::create();
newShapeData->setAnimTimeRange(fShapeData->animTimeRange());
newShapeData->setMaterials(fShapeData->getMaterials());
fShapeData = newShapeData;
Ptr thisPtr = shared_from_this();
cacheReader.saveReader(fFullName, thisPtr);
}
AlembicCacheMaterialReader::AlembicCacheMaterialReader(Alembic::Abc::IObject abcObj)
: fName(abcObj.getName()),
fValidityInterval(TimeInterval::kInvalid)
{
Alembic::AbcMaterial::IMaterial material(abcObj, Alembic::Abc::kWrapExisting);
Alembic::AbcMaterial::IMaterialSchema schema = material.getSchema();
fMaterialGraph = std::make_shared<MaterialGraph>(
MString(fName.c_str()));
size_t numNetworkNodes = schema.getNumNetworkNodes();
typedef std::pair<Alembic::AbcMaterial::IMaterialSchema::NetworkNode,MaterialNode::MPtr> NodePair;
typedef std::unordered_map<std::string,NodePair> NodeMap;
NodeMap nodeMap;
for (size_t i = 0; i < numNetworkNodes; i++) {
Alembic::AbcMaterial::IMaterialSchema::NetworkNode abcNode = schema.getNetworkNode(i);
std::string target;
if (!abcNode.valid() || !abcNode.getTarget(target) || target != kMaterialsGpuCacheTarget) {
continue;
}
std::string type;
if (!abcNode.getNodeType(type) || type.empty()) {
continue;
}
std::string name = abcNode.getName();
assert(!name.empty());
MaterialNode::MPtr node = MaterialNode::create(name.c_str(), type.c_str());
assert(node);
fMaterialGraph->addNode(node);
nodeMap.insert(std::make_pair(name, std::make_pair(abcNode, node)));
}
for(NodeMap::value_type& val : nodeMap) {
Alembic::AbcMaterial::IMaterialSchema::NetworkNode& abcNode = val.second.first;
MaterialNode::MPtr& node = val.second.second;
Alembic::Abc::ICompoundProperty compoundProp = abcNode.getParameters();
size_t numProps = compoundProp.getNumProperties();
for (size_t i = 0; i < numProps; i++) {
const Alembic::Abc::PropertyHeader& header = compoundProp.getPropertyHeader(i);
const std::string propName = header.getName();
if (Alembic::Abc::IBoolProperty::matches(header)) {
fBoolCaches.push_back(
ScalarMaterialProp<Alembic::Abc::IBoolProperty>(compoundProp, propName, node));
}
else if (Alembic::Abc::IInt32Property::matches(header)) {
fInt32Caches.push_back(
ScalarMaterialProp<Alembic::Abc::IInt32Property>(compoundProp, propName, node));
}
else if (Alembic::Abc::IFloatProperty::matches(header)) {
fFloatCaches.push_back(
ScalarMaterialProp<Alembic::Abc::IFloatProperty>(compoundProp, propName, node));
}
else if (Alembic::Abc::IV2fProperty::matches(header)) {
fFloat2Caches.push_back(
ScalarMaterialProp<Alembic::Abc::IV2fProperty>(compoundProp, propName, node));
}
else if (Alembic::Abc::IV3fProperty::matches(header)) {
fFloat3Caches.push_back(
ScalarMaterialProp<Alembic::Abc::IV3fProperty>(compoundProp, propName, node));
}
else if (Alembic::Abc::IC3fProperty::matches(header)) {
fRGBCaches.push_back(
ScalarMaterialProp<Alembic::Abc::IC3fProperty>(compoundProp, propName, node));
}
else if (Alembic::Abc::IWstringProperty::matches(header)) {
fStringCaches.push_back(
ScalarMaterialProp<Alembic::Abc::IWstringProperty>(compoundProp, propName, node));
}
}
}
for(NodeMap::value_type& val : nodeMap) {
Alembic::AbcMaterial::IMaterialSchema::NetworkNode& abcNode = val.second.first;
MaterialNode::MPtr& node = val.second.second;
size_t numConnections = abcNode.getNumConnections();
for (size_t i = 0; i < numConnections; i++) {
std::string inputName, connectedNodeName, connectedOutputName;
abcNode.getConnection(i, inputName, connectedNodeName, connectedOutputName);
MaterialProperty::MPtr prop = node->findProperty(inputName.c_str());
MaterialNode::MPtr srcNode;
NodeMap::iterator it = nodeMap.find(connectedNodeName);
if (it != nodeMap.end()) {
srcNode = (*it).second.second;
}
MaterialProperty::MPtr srcProp;
if (srcNode) {
srcProp = srcNode->findProperty(connectedOutputName.c_str());
}
if (prop && srcNode && srcProp) {
prop->connect(srcNode, srcProp);
}
}
}
std::string rootNodeName, rootOutput;
if (schema.getNetworkTerminal(kMaterialsGpuCacheTarget, kMaterialsGpuCacheType, rootNodeName, rootOutput)) {
NodeMap::iterator it = nodeMap.find(rootNodeName);
if (it != nodeMap.end()) {
fMaterialGraph->setRootNode((*it).second.second);
}
}
}
AlembicCacheMaterialReader::~AlembicCacheMaterialReader()
{}
TimeInterval AlembicCacheMaterialReader::sampleMaterial(double seconds)
{
TimeInterval validityInterval(TimeInterval::kInfinite);
for(ScalarMaterialProp<Alembic::Abc::IBoolProperty>& cache : fBoolCaches) {
validityInterval &= cache.sample(seconds);
}
for(ScalarMaterialProp<Alembic::Abc::IInt32Property>& cache : fInt32Caches) {
validityInterval &= cache.sample(seconds);
}
for(ScalarMaterialProp<Alembic::Abc::IFloatProperty>& cache : fFloatCaches) {
validityInterval &= cache.sample(seconds);
}
for(ScalarMaterialProp<Alembic::Abc::IV2fProperty>& cache : fFloat2Caches) {
validityInterval &= cache.sample(seconds);
}
for(ScalarMaterialProp<Alembic::Abc::IV3fProperty>& cache : fFloat3Caches) {
validityInterval &= cache.sample(seconds);
}
for(ScalarMaterialProp<Alembic::Abc::IC3fProperty>& cache : fRGBCaches) {
validityInterval &= cache.sample(seconds);
}
for(ScalarMaterialProp<Alembic::Abc::IWstringProperty>& cache : fStringCaches) {
validityInterval &= cache.sample(seconds);
}
return validityInterval;
}
MaterialGraph::MPtr AlembicCacheMaterialReader::get() const
{
if (!fMaterialGraph || !fMaterialGraph->rootNode() || fMaterialGraph->getNodes().empty()) {
return MaterialGraph::MPtr();
}
return fMaterialGraph;
}
}
struct AlembicCacheReader::MakeSharedEnabler: public AlembicCacheReader
{
MakeSharedEnabler(
const MFileObject& file): AlembicCacheReader(file){}
};
std::shared_ptr<CacheReader> AlembicCacheReader::create(
const MFileObject& file)
{
return std::make_shared<MakeSharedEnabler>(file);
}
AlembicCacheReader::AlembicCacheReader(
const MFileObject& file)
: fFile(file)
{
try {
std::lock_guard<std::mutex> alembicLock(gsAlembicMutex);
if (resolvedFullName.
length() != 0 && std::ifstream(resolvedFullName.
asChar()).good()) {
Alembic::AbcCoreFactory::IFactory factory;
factory.setSampleCache( Alembic::AbcCoreAbstract::ReadArraySampleCachePtr());
factory.setPolicy(Alembic::Abc::ErrorHandler::kThrowPolicy);
fAbcArchive = factory.getArchive(resolvedFullName.
asChar());
if (!fAbcArchive.valid()) {
}
}
else {
}
}
catch (CacheReaderInterruptException& ex) {
throw ex;
}
catch (std::exception& ex) {
DisplayError(kCacheOpenFileErrorMsg, file.
rawFullName(), ex.what());
}
}
AlembicCacheReader::~AlembicCacheReader()
{
try {
std::lock_guard<std::mutex> alembicLock(gsAlembicMutex);
fAbcArchive.reset();
}
catch (CacheReaderInterruptException& ex) {
throw ex;
}
catch (std::exception& ex) {
DisplayError(kCloseFileErrorMsg, fFile.resolvedFullName(), ex.what());
}
}
bool AlembicCacheReader::valid() const
{
std::lock_guard<std::mutex> alembicLock(gsAlembicMutex);
return fAbcArchive.valid();
}
bool AlembicCacheReader::validateGeomPath(
{
if (!valid()) {
return false;
}
try {
std::lock_guard<std::mutex> alembicLock(gsAlembicMutex);
geomPath.
split(
'|', pathArray);
bool valid = true;
Alembic::Abc::IObject current = fAbcArchive.getTop();
for (
unsigned int i = 0; i < pathArray.
length(); i++) {
current = current.getChild(step.
asChar());
if (!current.valid()) {
valid = false;
break;
}
validatedGeomPath += step;
}
if (validatedGeomPath.
length() == 0) {
}
return valid;
}
catch (CacheReaderInterruptException& ex) {
throw ex;
}
catch (std::exception& ex) {
DisplayError(kReadMeshErrorMsg, fFile.resolvedFullName(), geomPath, ex.what());
return false;
}
}
SubNode::Ptr AlembicCacheReader::readScene(
const MString& geomPath,
bool needUVs)
{
SubNode::Ptr top = readHierarchy(geomPath, needUVs);
if (!top) return SubNode::Ptr();
ShapePathVisitor::ShapePathAndSubNodeList shapeGeomPaths;
ShapePathVisitor shapePathVisitor(shapeGeomPaths);
top->accept(shapePathVisitor);
int lastStep = geomPath.
rindexW(
'|');
if (lastStep > 0) {
}
for(const ShapePathVisitor::ShapePathAndSubNode& pair : shapeGeomPaths) {
SubNode::Ptr shape = readShape(prefix + pair.first, needUVs);
if (shape && pair.first.length() > 0) {
ReplaceSubNodeData(top, shape, pair.first);
}
}
SubNodeTransparentTypeVisitor transparentTypeVisitor;
top->accept(transparentTypeVisitor);
return top;
}
SubNode::Ptr AlembicCacheReader::readHierarchy(
const MString& geomPath,
bool needUVs)
{
using namespace CacheReaderAlembicPrivate;
if (!valid()) return SubNode::Ptr();
try {
std::lock_guard<std::mutex> alembicLock(gsAlembicMutex);
geomPath.
split(
'|', pathArray);
Alembic::Abc::IObject current = fAbcArchive.getTop();
AlembicCacheObjectReader::Ptr reader;
if (pathArray.
length() == 0) {
size_t numChildren = 0;
size_t lastChild = 0;
for (size_t i = 0; i < current.getNumChildren(); i++) {
if (Alembic::AbcGeom::IPolyMesh::matches(current.getChildHeader(i)) ||
Alembic::AbcGeom::INuPatch::matches(current.getChildHeader(i)) ||
Alembic::AbcGeom::ISubD::matches(current.getChildHeader(i)) ||
Alembic::AbcGeom::IXform::matches(current.getChildHeader(i))) {
numChildren++;
lastChild = i;
}
}
if (numChildren == 1) {
current = Alembic::Abc::IObject(
current, current.getChildHeader(lastChild).getName());
if (current.valid())
reader = AlembicCacheObjectReader::create(
current, needUVs);
}
else if (numChildren > 1) {
reader = std::make_shared<AlembicCacheTopReader>(
current, needUVs);
}
}
else {
bool geometryFound = true;
for (
unsigned int i = 0; i < pathArray.
length(); i++) {
current = current.getChild(step.
asChar());
if (!current.valid()) {
geometryFound = false;
break;
}
}
if (geometryFound)
reader = AlembicCacheObjectReader::create(current, needUVs);
}
if (!reader || !reader->valid()) return SubNode::Ptr();
{
TimeInterval interval = reader->sampleHierarchy(
-std::numeric_limits<double>::max(),
while (interval.endTime() != std::numeric_limits<double>::max()) {
interval = reader->sampleHierarchy(
interval.endTime(),
}
}
SubNode::Ptr top = reader->get();
reader->saveAndReset(*this);
return top;
}
catch (CacheReaderInterruptException& ex) {
throw ex;
}
catch (std::exception& ex) {
DisplayError(kReadMeshErrorMsg, fFile.resolvedFullName(), geomPath, ex.what());
return SubNode::Ptr();
}
}
SubNode::Ptr AlembicCacheReader::readShape(
const MString& geomPath,
bool needUVs)
{
using namespace CacheReaderAlembicPrivate;
if (!valid()) return SubNode::Ptr();
try {
std::lock_guard<std::mutex> alembicLock(gsAlembicMutex);
AlembicCacheObjectReader::Ptr reader;
ObjectReaderMap::iterator iter = fSavedReaders.find(geomPath.
asChar());
if (iter != fSavedReaders.end()) {
reader = (*iter).second;
}
else {
geomPath.
split(
'|', pathArray);
Alembic::Abc::IObject current = fAbcArchive.getTop();
bool geometryFound = true;
for (
unsigned int i = 0; i < pathArray.
length(); i++) {
current = current.getChild(step.
asChar());
if (!current.valid()) {
geometryFound = false;
break;
}
}
if (geometryFound) {
reader = AlembicCacheObjectReader::create(current, needUVs);
}
}
}
if (!reader || !reader->valid()) return SubNode::Ptr();
{
TimeInterval interval = reader->sampleShape(
-std::numeric_limits<double>::max());
while (interval.endTime() != std::numeric_limits<double>::max()) {
interval = reader->sampleShape(
interval.endTime());
}
}
SubNode::Ptr top = reader->get();
reader->saveAndReset(*this);
return top;
}
catch (CacheReaderInterruptException& ex) {
throw ex;
}
catch (std::exception& ex) {
DisplayError(kReadMeshErrorMsg, fFile.resolvedFullName(), geomPath, ex.what());
return SubNode::Ptr();
}
}
MaterialGraphMap::Ptr AlembicCacheReader::readMaterials()
{
using namespace CacheReaderAlembicPrivate;
if (!valid()) return MaterialGraphMap::Ptr();
try {
std::lock_guard<std::mutex> alembicLock(gsAlembicMutex);
Alembic::Abc::IObject topObject = fAbcArchive.getTop();
Alembic::Abc::IObject materialsObject = topObject.getChild(kMaterialsObject);
if (!materialsObject.valid()) {
return MaterialGraphMap::Ptr();
}
MaterialGraphMap::MPtr materials = std::make_shared<MaterialGraphMap>();
for (size_t i = 0; i < materialsObject.getNumChildren(); i++) {
Alembic::Abc::IObject object = materialsObject.getChild(i);
if (Alembic::AbcMaterial::IMaterial::matches(object.getHeader())) {
AlembicCacheMaterialReader reader(object);
TimeInterval interval = reader.sampleMaterial(
-std::numeric_limits<double>::max());
while (interval.endTime() != std::numeric_limits<double>::max()) {
interval = reader.sampleMaterial(interval.endTime());
}
MaterialGraph::MPtr graph = reader.get();
if (graph) {
materials->addMaterialGraph(graph);
}
}
}
if (materials->getGraphs().empty()) {
return MaterialGraphMap::Ptr();
}
return materials;
}
catch (CacheReaderInterruptException& ex) {
throw ex;
}
catch (std::exception& ex) {
DisplayError(kReadFileErrorMsg, fFile.resolvedFullName(), ex.what());
return MaterialGraphMap::Ptr();
}
}
bool AlembicCacheReader::readAnimTimeRange(GPUCache::TimeInterval& range)
{
if (!valid()) return false;
try {
std::lock_guard<std::mutex> alembicLock(gsAlembicMutex);
double samplesMin = std::numeric_limits<double>::infinity();
double samplesMax = -std::numeric_limits<double>::infinity();
unsigned int numTimeSamplings = fAbcArchive.getNumTimeSamplings();
for (unsigned int i = 0; i < numTimeSamplings; i++) {
std::stringstream propName;
propName << i << ".samples";
Alembic::Abc::IUInt32Property samplesProp(
fAbcArchive.getTop().getProperties(),
propName.str(),
Alembic::Abc::ErrorHandler::kQuietNoopPolicy
);
Alembic::Abc::TimeSamplingPtr timeSampling = fAbcArchive.getTimeSampling(i);
if (samplesProp && timeSampling) {
unsigned int numSamples = 0;
samplesProp.get(numSamples);
if (numSamples > 0) {
samplesMin = std::min(samplesMin, timeSampling->getSampleTime(0));
samplesMax = std::max(samplesMax, timeSampling->getSampleTime(numSamples - 1));
}
}
}
if (samplesMin <= samplesMax) {
range = TimeInterval(samplesMin, samplesMax);
return true;
}
Alembic::Abc::IBox3dProperty boxProp = Alembic::AbcGeom::GetIArchiveBounds(
fAbcArchive, Alembic::Abc::ErrorHandler::kQuietNoopPolicy);
if (boxProp) {
size_t numSamples = boxProp.getNumSamples();
Alembic::Abc::TimeSamplingPtr timeSampling = boxProp.getTimeSampling();
if (numSamples > 0 && timeSampling) {
range = TimeInterval(
timeSampling->getSampleTime(0),
timeSampling->getSampleTime(numSamples - 1)
);
return true;
}
}
return false;
}
catch (CacheReaderInterruptException& ex) {
throw ex;
}
catch (std::exception& ex) {
DisplayError(kReadFileErrorMsg, fFile.resolvedFullName(), ex.what());
return false;
}
}
void AlembicCacheReader::saveReader(
const std::string& fullName,
CacheReaderAlembicPrivate::AlembicCacheObjectReader::Ptr& reader
)
{
if (reader && reader->valid()) {
std::string geometryPath = fullName;
std::replace(geometryPath.begin(), geometryPath.end(), '/', '|');
fSavedReaders.insert(std::make_pair(geometryPath, reader));
}
}
}