Files
UnrealEngine/Engine/Source/ThirdParty/Alembic/alembic-1.8.7/bin/AbcStitcher/AbcStitcher.cpp
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

1269 lines
44 KiB
C++

//-*****************************************************************************
//
// Copyright (c) 2009-2014,
// Sony Pictures Imageworks Inc. and
// Industrial Light & Magic, a division of Lucasfilm Entertainment Company Ltd.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Sony Pictures Imageworks, nor
// Industrial Light & Magic, nor the names of their contributors may be used
// to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//-*****************************************************************************
#include <Alembic/AbcGeom/All.h>
#ifdef ALEMBIC_WITH_HDF5
#include <Alembic/AbcCoreHDF5/All.h>
#endif
#include <Alembic/AbcCoreOgawa/All.h>
#include <Alembic/AbcCoreFactory/All.h>
#include "util.h"
#include <iostream>
#include <sstream>
#include <vector>
#include <algorithm>
#include <string>
using namespace Alembic::AbcGeom;
using namespace Alembic::AbcCoreAbstract;
namespace{
inline void stitchVisible(ICompoundPropertyVec & iCompoundProps,
OCompoundProperty & oCompoundProp,
const TimeAndSamplesMap & iTimeMap)
{
const PropertyHeader * propHeaderPtr =
iCompoundProps[0].getPropertyHeader("visible");
stitchScalarProp(*propHeaderPtr, iCompoundProps, oCompoundProp, iTimeMap);
}
template< class IPARAM, class IPARAMSAMP, class OPARAMSAMP >
void getOGeomParamSamp(IPARAM & iGeomParam, IPARAMSAMP & iGeomSamp,
OPARAMSAMP & oGeomSamp, index_t iIndex)
{
if (iGeomParam.isIndexed())
{
iGeomParam.getIndexed(iGeomSamp, iIndex);
oGeomSamp.setVals(*(iGeomSamp.getVals()));
oGeomSamp.setScope(iGeomSamp.getScope());
oGeomSamp.setIndices(*(iGeomSamp.getIndices()));
}
else
{
iGeomParam.getExpanded(iGeomSamp, iIndex);
oGeomSamp.setVals(*(iGeomSamp.getVals()));
oGeomSamp.setScope(iGeomSamp.getScope());
}
}
template< class IData, class IDataSchema, class OData, class ODataSchema >
void init(std::vector< IObject > & iObjects, OObject & oParentObj,
ODataSchema & oSchema, const TimeAndSamplesMap & iTimeMap,
std::size_t & oTotalSamples)
{
// find the first valid IObject
IObject inObj;
for (std::size_t i = 0; i < iObjects.size(); ++i)
{
if (iObjects[i].valid())
{
inObj = iObjects[i];
break;
}
}
const std::string fullNodeName = inObj.getFullName();
// gather information from the first input node in the list:
IDataSchema iSchema0 = IData(inObj).getSchema();
TimeSamplingPtr tsPtr0 = iTimeMap.get(iSchema0.getTimeSampling(),
oTotalSamples);
TimeSamplingType tsType0 = tsPtr0->getTimeSamplingType();
checkAcyclic(tsType0, fullNodeName);
ICompoundPropertyVec iCompoundProps;
iCompoundProps.reserve(iObjects.size());
ICompoundPropertyVec iArbGeomCompoundProps;
iArbGeomCompoundProps.reserve(iObjects.size());
ICompoundPropertyVec iUserCompoundProps;
iUserCompoundProps.reserve(iObjects.size());
ICompoundPropertyVec iSchemaProps;
iSchemaProps.reserve(iObjects.size());
Abc::IBox3dProperty childBounds = iSchema0.getChildBoundsProperty();
TimeSamplingPtr ctsPtr0;
TimeSamplingType ctsType0;
if (childBounds)
{
ctsPtr0 = childBounds.getTimeSampling();
ctsType0 = ctsPtr0->getTimeSamplingType();
std::string nameAndBounds = fullNodeName + " child bounds";
checkAcyclic(ctsType0, nameAndBounds);
}
bool hasVisible = inObj.getProperties().getPropertyHeader("visible") != NULL;
// sanity check (no frame range checking here)
// - timesamplying type has to be the same and can't be acyclic
for (size_t i = 0; i < iObjects.size(); i++)
{
if (!iObjects[i].valid())
{
continue;
}
IDataSchema iSchema =
IData(iObjects[i]).getSchema();
TimeSamplingPtr tsPtr = iSchema.getTimeSampling();
TimeSamplingType tsType = tsPtr->getTimeSamplingType();
checkAcyclic(tsType, fullNodeName);
if (!(tsType0 == tsType))
{
std::cerr << "Can not stitch different sampling type for node \""
<< fullNodeName << "\"" << std::endl;
// more details on this
if (tsType.getNumSamplesPerCycle()
!= tsType0.getNumSamplesPerCycle())
{
std::cerr << "\tnumSamplesPerCycle values are different"
<< std::endl;
}
if (tsType.getTimePerCycle() != tsType0.getTimePerCycle())
{
std::cerr << "\ttimePerCycle values are different"
<< std::endl;
}
exit(1);
}
ICompoundProperty cp = iObjects[i].getProperties();
iCompoundProps.push_back(cp);
ICompoundProperty arbProp = iSchema.getArbGeomParams();
if (arbProp) // might be empty
iArbGeomCompoundProps.push_back(arbProp);
ICompoundProperty userProp = iSchema.getUserProperties();
if (userProp) // might be empty
iUserCompoundProps.push_back(userProp);
Abc::IBox3dProperty childBounds = iSchema.getChildBoundsProperty();
TimeSamplingPtr ctsPtr;
TimeSamplingType ctsType;
if (childBounds)
{
ctsPtr = childBounds.getTimeSampling();
ctsType = ctsPtr->getTimeSamplingType();
iSchemaProps.push_back(iSchema);
}
if (!(ctsType0 == ctsType))
{
std::cerr <<
"Can not stitch different sampling type for child bounds on\""
<< fullNodeName << "\"" << std::endl;
// more details on this
if (ctsType.getNumSamplesPerCycle()
!= ctsType0.getNumSamplesPerCycle())
{
std::cerr << "\tnumSamplesPerCycle values are different"
<< std::endl;
}
if (ctsType.getTimePerCycle() != ctsType0.getTimePerCycle())
{
std::cerr << "\ttimePerCycle values are different"
<< std::endl;
}
if (!ctsPtr0 || !ctsPtr)
{
std::cerr << "\tchild bounds are missing on some archives"
<< std::endl;
}
exit(1);
}
}
OData oData(oParentObj, inObj.getName(), tsPtr0);
oSchema = oData.getSchema();
// stitch "visible" if it's points
//
if (hasVisible)
{
OCompoundProperty oCompoundProp = oData.getProperties();
stitchVisible(iCompoundProps, oCompoundProp, iTimeMap);
}
// stitch ArbGeomParams and User Properties
//
if (iArbGeomCompoundProps.size() == iObjects.size())
{
OCompoundProperty oArbGeomCompoundProp = oSchema.getArbGeomParams();
stitchCompoundProp(iArbGeomCompoundProps, oArbGeomCompoundProp, iTimeMap);
}
if (iUserCompoundProps.size() == iObjects.size())
{
OCompoundProperty oUserCompoundProp = oSchema.getUserProperties();
stitchCompoundProp(iUserCompoundProps, oUserCompoundProp, iTimeMap);
}
if (!iSchemaProps.empty())
{
stitchScalarProp(childBounds.getHeader(), iSchemaProps, oSchema, iTimeMap);
}
}
};
//-*****************************************************************************
// a recursive function that reads all inputs and write to the given oObject
// node if there's no gap in the frame range for animated nodes
//
void visitObjects(std::vector< IObject > & iObjects, OObject & oParentObj,
const TimeAndSamplesMap & iTimeMap, bool atRoot)
{
OObject outObj;
IObject inObj;
std::vector< IObject >::iterator it;
for (it = iObjects.begin(); it != iObjects.end(); ++it)
{
if (it->valid())
{
inObj = *it;
break;
}
}
assert(inObj.valid());
if (iTimeMap.isVerbose())
{
std::cout << inObj.getFullName() << std::endl;
}
const AbcA::ObjectHeader & header = inObj.getHeader();
std::size_t totalSamples = 0;
// keep track of how many we have set so far to work around issue 430
std::size_t totalSet = 0;
// there are a number of things that needs to be checked for each node
// to make sure they can be properly stitched together
//
// for xform node:
// locator or normal xform node
// if an xform node, numOps and type of ops match
// static or no, and if not, timesampling type matches
// if sampled, timesampling type should match
// if sampled, no frame gaps
//
if (IXform::matches(header))
{
OXformSchema oSchema;
init< IXform, IXformSchema, OXform, OXformSchema >(
iObjects, oParentObj, oSchema, iTimeMap, totalSamples);
outObj = oSchema.getObject();
ICompoundPropertyVec iCompoundProps;
iCompoundProps.reserve(iObjects.size());
const PropertyHeader * locHeader = NULL;
for (size_t i = 0; i < iObjects.size(); i++)
{
if (!iObjects[i].valid())
{
iCompoundProps.push_back(ICompoundProperty());
continue;
}
ICompoundProperty cp = iObjects[i].getProperties();
iCompoundProps.push_back(cp);
const PropertyHeader * childLocHeader =
cp.getPropertyHeader("locator");
if (!locHeader && childLocHeader)
{
locHeader = childLocHeader;
}
}
// stitch the operations if this is an xform node
size_t i = 0;
for (i = 0; i < iObjects.size(); i++)
{
if (!iObjects[i].valid())
{
continue;
}
IXformSchema iSchema =
IXform(iObjects[i]).getSchema();
index_t numSamples = iSchema.getNumSamples();
index_t numEmpty = 0;
index_t reqIdx = getIndexSample(totalSet,
oSchema.getTimeSampling(), numSamples,
iSchema.getTimeSampling(), numEmpty);
// write empties only if we are also writing a sample, as the
// first sample will be repeated over and over again
for (index_t emptyIdx = 0;
reqIdx < numSamples && emptyIdx < numEmpty; ++emptyIdx)
{
XformSample samp = iSchema.getValue(reqIdx);
oSchema.set(samp);
}
totalSet += numEmpty;
for (; reqIdx < numSamples; reqIdx++, totalSet++)
{
XformSample samp = iSchema.getValue(reqIdx);
oSchema.set(samp);
}
}
// make sure we've set a sample, if we are going to extend them
for (i = totalSet; i != 0 && i < totalSamples; ++i)
{
oSchema.setFromPrevious();
}
// stitch "locator" if it's a locator
OCompoundProperty oCompoundProp = outObj.getProperties();
if (locHeader)
{
stitchScalarProp(*locHeader, iCompoundProps, oCompoundProp,
iTimeMap);
}
}
else if (ISubD::matches(header))
{
OSubDSchema oSchema;
init< ISubD, ISubDSchema, OSubD, OSubDSchema >(
iObjects, oParentObj, oSchema, iTimeMap, totalSamples);
outObj = oSchema.getObject();
OSubDSchema::Sample emptySample(P3fArraySample::emptySample(),
Int32ArraySample::emptySample(), Int32ArraySample::emptySample());
// stitch the SubDSchema
//
for (size_t i = 0; i < iObjects.size(); i++)
{
if (!iObjects[i].valid())
{
continue;
}
ISubDSchema iSchema =
ISubD(iObjects[i]).getSchema();
index_t numSamples = iSchema.getNumSamples();
IV2fGeomParam uvs = iSchema.getUVsParam();
if (totalSet == 0 && uvs)
{
oSchema.setUVSourceName(GetSourceName(uvs.getMetaData()));
}
index_t numEmpty = 0;
index_t reqIdx = getIndexSample(totalSet,
oSchema.getTimeSampling(), numSamples,
iSchema.getTimeSampling(), numEmpty);
// not hold, then set empties, other set previous if we have previous samples
if (!iTimeMap.isHold() || totalSet > 0)
{
for (index_t emptyIdx = 0; emptyIdx < numEmpty; ++emptyIdx)
{
if (!iTimeMap.isHold())
{
oSchema.set(emptySample);
}
else
{
oSchema.setFromPrevious();
}
}
totalSet += numEmpty;
}
for (; reqIdx < numSamples; reqIdx++, totalSet++)
{
ISubDSchema::Sample iSamp = iSchema.getValue(reqIdx);
OSubDSchema::Sample oSamp;
Abc::P3fArraySamplePtr posPtr = iSamp.getPositions();
if (posPtr)
oSamp.setPositions(*posPtr);
Abc::V3fArraySamplePtr velocPtr = iSamp.getVelocities();
if (velocPtr)
oSamp.setVelocities(*velocPtr);
Abc::Int32ArraySamplePtr faceIndicesPtr = iSamp.getFaceIndices();
if (faceIndicesPtr)
oSamp.setFaceIndices(*faceIndicesPtr);
Abc::Int32ArraySamplePtr faceCntPtr = iSamp.getFaceCounts();
if (faceCntPtr)
oSamp.setFaceCounts(*faceCntPtr);
oSamp.setFaceVaryingInterpolateBoundary(iSamp.getFaceVaryingInterpolateBoundary());
oSamp.setFaceVaryingPropagateCorners(iSamp.getFaceVaryingPropagateCorners());
oSamp.setInterpolateBoundary(iSamp.getInterpolateBoundary());
Abc::Int32ArraySamplePtr creaseIndicesPtr = iSamp.getCreaseIndices();
if (creaseIndicesPtr)
oSamp.setCreaseIndices(*creaseIndicesPtr);
Abc::Int32ArraySamplePtr creaseLenPtr = iSamp.getCreaseLengths();
if (creaseLenPtr)
oSamp.setCreaseLengths(*creaseLenPtr);
Abc::FloatArraySamplePtr creaseSpPtr = iSamp.getCreaseSharpnesses();
if (creaseSpPtr)
oSamp.setCreaseSharpnesses(*creaseSpPtr);
Abc::Int32ArraySamplePtr cornerIndicesPtr = iSamp.getCornerIndices();
if (cornerIndicesPtr)
oSamp.setCornerIndices(*cornerIndicesPtr);
Abc::FloatArraySamplePtr cornerSpPtr = iSamp.getCornerSharpnesses();
if (cornerSpPtr)
oSamp.setCornerSharpnesses(*cornerSpPtr);
Abc::Int32ArraySamplePtr holePtr = iSamp.getHoles();
if (holePtr)
oSamp.setHoles(*holePtr);
oSamp.setSubdivisionScheme(iSamp.getSubdivisionScheme());
// set uvs
IV2fGeomParam::Sample iUVSample;
OV2fGeomParam::Sample oUVSample;
if (uvs)
{
getOGeomParamSamp <IV2fGeomParam, IV2fGeomParam::Sample,
OV2fGeomParam::Sample>(uvs, iUVSample,
oUVSample, reqIdx);
oSamp.setUVs(oUVSample);
}
oSchema.set(oSamp);
// our first sample was written, AND we want to hold so set our previous samples to this one!
if (iTimeMap.isHold() && totalSet == 0)
{
for (index_t emptyIdx = 0; emptyIdx < numEmpty; ++emptyIdx)
{
oSchema.setFromPrevious();
}
totalSet += numEmpty;
}
}
}
for (size_t i = totalSet; i < totalSamples; ++i)
{
if (!iTimeMap.isHold())
{
oSchema.set(emptySample);
}
else
{
oSchema.setFromPrevious();
}
}
}
else if (IPolyMesh::matches(header))
{
OPolyMeshSchema oSchema;
init< IPolyMesh, IPolyMeshSchema, OPolyMesh, OPolyMeshSchema >(
iObjects, oParentObj, oSchema, iTimeMap, totalSamples);
outObj = oSchema.getObject();
OPolyMeshSchema::Sample emptySample(P3fArraySample::emptySample(),
Int32ArraySample::emptySample(), Int32ArraySample::emptySample());
// stitch the PolySchema
//
for (size_t i = 0; i < iObjects.size(); i++)
{
if (!iObjects[i].valid())
{
continue;
}
IPolyMeshSchema iSchema =
IPolyMesh(iObjects[i]).getSchema();
index_t numSamples = iSchema.getNumSamples();
IN3fGeomParam normals = iSchema.getNormalsParam();
IV2fGeomParam uvs = iSchema.getUVsParam();
if (totalSet == 0 && uvs)
{
oSchema.setUVSourceName(GetSourceName(uvs.getMetaData()));
}
index_t numEmpty = 0;
index_t reqIdx = getIndexSample(totalSet,
oSchema.getTimeSampling(), numSamples,
iSchema.getTimeSampling(), numEmpty);
// not hold, then set empties, other set previous if we have previous samples
if (!iTimeMap.isHold() || totalSet > 0)
{
for (index_t emptyIdx = 0; emptyIdx < numEmpty; ++emptyIdx)
{
if (!iTimeMap.isHold())
{
oSchema.set(emptySample);
}
else
{
oSchema.setFromPrevious();
}
}
totalSet += numEmpty;
}
for (; reqIdx < numSamples; reqIdx++, totalSet++)
{
IPolyMeshSchema::Sample iSamp = iSchema.getValue(reqIdx);
OPolyMeshSchema::Sample oSamp;
Abc::P3fArraySamplePtr posPtr = iSamp.getPositions();
if (posPtr)
oSamp.setPositions(*posPtr);
Abc::V3fArraySamplePtr velocPtr = iSamp.getVelocities();
if (velocPtr)
oSamp.setVelocities(*velocPtr);
Abc::Int32ArraySamplePtr faceIndicesPtr = iSamp.getFaceIndices();
if (faceIndicesPtr)
oSamp.setFaceIndices(*faceIndicesPtr);
Abc::Int32ArraySamplePtr faceCntPtr = iSamp.getFaceCounts();
if (faceCntPtr)
oSamp.setFaceCounts(*faceCntPtr);
// set uvs
IV2fGeomParam::Sample iUVSample;
OV2fGeomParam::Sample oUVSample;
if (uvs)
{
getOGeomParamSamp <IV2fGeomParam, IV2fGeomParam::Sample,
OV2fGeomParam::Sample>(uvs, iUVSample,
oUVSample, reqIdx);
oSamp.setUVs(oUVSample);
}
// set normals
IN3fGeomParam::Sample iNormalsSample;
ON3fGeomParam::Sample oNormalsSample;
if (normals)
{
getOGeomParamSamp <IN3fGeomParam, IN3fGeomParam::Sample,
ON3fGeomParam::Sample>(normals, iNormalsSample,
oNormalsSample, reqIdx);
oSamp.setNormals(oNormalsSample);
}
oSchema.set(oSamp);
// our first sample was written, AND we want to hold so set our previous samples to this one!
if (iTimeMap.isHold() && totalSet == 0)
{
for (index_t emptyIdx = 0; emptyIdx < numEmpty; ++emptyIdx)
{
oSchema.setFromPrevious();
}
totalSet += numEmpty;
}
}
}
for (size_t i = totalSet; i < totalSamples; ++i)
{
if (!iTimeMap.isHold())
{
oSchema.set(emptySample);
}
else
{
oSchema.setFromPrevious();
}
}
}
else if (ICamera::matches(header))
{
OCameraSchema oSchema;
init< ICamera, ICameraSchema, OCamera, OCameraSchema >(
iObjects, oParentObj, oSchema, iTimeMap, totalSamples);
outObj = oSchema.getObject();
// stitch the CameraSchemas, we dont need to worry about isHold
// because we set from previous here
for (size_t i = 0; i < iObjects.size(); i++)
{
if (!iObjects[i].valid())
{
continue;
}
ICameraSchema iSchema =
ICamera(iObjects[i]).getSchema();
index_t numSamples = iSchema.getNumSamples();
index_t numEmpty = 0;
index_t reqIdx = getIndexSample(totalSet,
oSchema.getTimeSampling(), numSamples,
iSchema.getTimeSampling(), numEmpty);
// write empties only if we are also writing a sample, as the
// first sample will be repeated over and over again
for (index_t emptyIdx = 0;
reqIdx < numSamples && emptyIdx < numEmpty; ++emptyIdx)
{
oSchema.set(iSchema.getValue(reqIdx));
}
totalSet += numEmpty;
for (; reqIdx < numSamples; reqIdx++, totalSet++)
{
oSchema.set(iSchema.getValue(reqIdx));
}
}
// for the rest of the samples just set the last one as long as
// a sample has been already set
for (size_t i = totalSet; i != 0 && i < totalSamples;++i)
{
oSchema.setFromPrevious();
}
}
else if (ICurves::matches(header))
{
OCurvesSchema oSchema;
init< ICurves, ICurvesSchema, OCurves, OCurvesSchema >(
iObjects, oParentObj, oSchema, iTimeMap, totalSamples);
outObj = oSchema.getObject();
OCurvesSchema::Sample emptySample(P3fArraySample::emptySample(),
Int32ArraySample::emptySample());
// stitch the CurvesSchemas
//
for (size_t i = 0; i < iObjects.size(); i++)
{
if (!iObjects[i].valid())
{
continue;
}
ICurvesSchema iSchema =
ICurves(iObjects[i]).getSchema();
IV2fGeomParam iUVs = iSchema.getUVsParam();
IN3fGeomParam iNormals = iSchema.getNormalsParam();
IFloatGeomParam iWidths = iSchema.getWidthsParam();
IFloatArrayProperty iKnots = iSchema.getKnotsProperty();
IUcharArrayProperty iOrders = iSchema.getOrdersProperty();
index_t numSamples = iSchema.getNumSamples();
index_t numEmpty = 0;
index_t reqIdx = getIndexSample(totalSet,
oSchema.getTimeSampling(), numSamples,
iSchema.getTimeSampling(), numEmpty);
// not hold, then set empties, other set previous if we have previous samples
if (!iTimeMap.isHold() || totalSet > 0)
{
for (index_t emptyIdx = 0; emptyIdx < numEmpty; ++emptyIdx)
{
if (!iTimeMap.isHold())
{
oSchema.set(emptySample);
}
else
{
oSchema.setFromPrevious();
}
}
totalSet += numEmpty;
}
for (; reqIdx < numSamples; reqIdx++, totalSet++)
{
ICurvesSchema::Sample iSamp = iSchema.getValue(reqIdx);
OCurvesSchema::Sample oSamp;
Abc::P3fArraySamplePtr posPtr = iSamp.getPositions();
if (posPtr)
oSamp.setPositions(*posPtr);
Abc::V3fArraySamplePtr velocPtr = iSamp.getVelocities();
if (velocPtr)
oSamp.setVelocities(*velocPtr);
oSamp.setType(iSamp.getType());
Abc::Int32ArraySamplePtr curvsNumPtr = iSamp.getCurvesNumVertices();
if (curvsNumPtr)
oSamp.setCurvesNumVertices(*curvsNumPtr);
oSamp.setWrap(iSamp.getWrap());
oSamp.setBasis(iSamp.getBasis());
Abc::FloatArraySamplePtr knotsPtr = iSamp.getKnots();
if (knotsPtr)
{
oSamp.setKnots(*knotsPtr);
}
Abc::UcharArraySamplePtr ordersPtr = iSamp.getOrders();
if (ordersPtr)
{
oSamp.setOrders(*ordersPtr);
}
IFloatGeomParam::Sample iWidthSample;
OFloatGeomParam::Sample oWidthSample;
if (iWidths)
{
getOGeomParamSamp <IFloatGeomParam, IFloatGeomParam::Sample,
OFloatGeomParam::Sample>(iWidths, iWidthSample,
oWidthSample, reqIdx);
oSamp.setWidths(oWidthSample);
}
IV2fGeomParam::Sample iUVSample;
OV2fGeomParam::Sample oUVSample;
if (iUVs)
{
getOGeomParamSamp <IV2fGeomParam, IV2fGeomParam::Sample,
OV2fGeomParam::Sample>(iUVs, iUVSample,
oUVSample, reqIdx);
oSamp.setUVs(oUVSample);
}
IN3fGeomParam::Sample iNormalsSample;
ON3fGeomParam::Sample oNormalsSample;
if (iNormals)
{
getOGeomParamSamp <IN3fGeomParam, IN3fGeomParam::Sample,
ON3fGeomParam::Sample>(iNormals, iNormalsSample,
oNormalsSample, reqIdx);
oSamp.setNormals(oNormalsSample);
}
oSchema.set(oSamp);
// our first sample was written, AND we want to hold so set our previous samples to this one!
if (iTimeMap.isHold() && totalSet == 0)
{
for (index_t emptyIdx = 0; emptyIdx < numEmpty; ++emptyIdx)
{
oSchema.setFromPrevious();
}
totalSet += numEmpty;
}
}
}
for (size_t i = totalSet; i < totalSamples; ++i)
{
if (!iTimeMap.isHold())
{
oSchema.set(emptySample);
}
else
{
oSchema.setFromPrevious();
}
}
}
else if (IPoints::matches(header))
{
OPointsSchema oSchema;
init< IPoints, IPointsSchema, OPoints, OPointsSchema >(
iObjects, oParentObj, oSchema, iTimeMap, totalSamples);
outObj = oSchema.getObject();
OPointsSchema::Sample emptySample(P3fArraySample::emptySample(),
UInt64ArraySample::emptySample());
// stitch the PointsSchemas
//
for (size_t i = 0; i < iObjects.size(); i++)
{
if (!iObjects[i].valid())
{
continue;
}
IPointsSchema iSchema =
IPoints(iObjects[i]).getSchema();
IFloatGeomParam iWidths = iSchema.getWidthsParam();
index_t numSamples = iSchema.getNumSamples();
index_t numEmpty = 0;
index_t reqIdx = getIndexSample(totalSet,
oSchema.getTimeSampling(), numSamples,
iSchema.getTimeSampling(), numEmpty);
// not hold, then set empties, other set previous if we have previous samples
if (!iTimeMap.isHold() || totalSet > 0)
{
for (index_t emptyIdx = 0; emptyIdx < numEmpty; ++emptyIdx)
{
if (!iTimeMap.isHold())
{
oSchema.set(emptySample);
}
else
{
oSchema.setFromPrevious();
}
}
totalSet += numEmpty;
}
for (; reqIdx < numSamples; reqIdx++, totalSet++)
{
IPointsSchema::Sample iSamp = iSchema.getValue(reqIdx);
OPointsSchema::Sample oSamp;
Abc::P3fArraySamplePtr posPtr = iSamp.getPositions();
if (posPtr)
oSamp.setPositions(*posPtr);
Abc::UInt64ArraySamplePtr idPtr = iSamp.getIds();
if (idPtr)
oSamp.setIds(*idPtr);
Abc::V3fArraySamplePtr velocPtr = iSamp.getVelocities();
if (velocPtr)
oSamp.setVelocities(*velocPtr);
IFloatGeomParam::Sample iWidthSample;
OFloatGeomParam::Sample oWidthSample;
if (iWidths)
{
getOGeomParamSamp <IFloatGeomParam, IFloatGeomParam::Sample,
OFloatGeomParam::Sample>(iWidths, iWidthSample,
oWidthSample, reqIdx);
oSamp.setWidths(oWidthSample);
}
oSchema.set(oSamp);
// our first sample was written, AND we want to hold so set our previous samples to this one!
if (iTimeMap.isHold() && totalSet == 0)
{
for (index_t emptyIdx = 0; emptyIdx < numEmpty; ++emptyIdx)
{
oSchema.setFromPrevious();
}
totalSet += numEmpty;
}
}
}
for (size_t i = totalSet; i < totalSamples; ++i)
{
if (!iTimeMap.isHold())
{
oSchema.set(emptySample);
}
else
{
oSchema.setFromPrevious();
}
}
}
else if (INuPatch::matches(header))
{
ONuPatchSchema oSchema;
init< INuPatch, INuPatchSchema, ONuPatch, ONuPatchSchema >(
iObjects, oParentObj, oSchema, iTimeMap, totalSamples);
outObj = oSchema.getObject();
Alembic::Util::int32_t zeroVal = 0;
ONuPatchSchema::Sample emptySample(P3fArraySample::emptySample(),
zeroVal, zeroVal, zeroVal, zeroVal,
FloatArraySample::emptySample(), FloatArraySample::emptySample());
// stitch the NuPatchSchemas
//
for (size_t i = 0; i < iObjects.size(); i++)
{
if (!iObjects[i].valid())
{
continue;
}
INuPatchSchema iSchema =
INuPatch(iObjects[i]).getSchema();
index_t numSamples = iSchema.getNumSamples();
IN3fGeomParam normals = iSchema.getNormalsParam();
IV2fGeomParam uvs = iSchema.getUVsParam();
index_t numEmpty = 0;
index_t reqIdx = getIndexSample(totalSet,
oSchema.getTimeSampling(), numSamples,
iSchema.getTimeSampling(), numEmpty);
// not hold, then set empties, other set previous if we have previous samples
if (!iTimeMap.isHold() || totalSet > 0)
{
for (index_t emptyIdx = 0; emptyIdx < numEmpty; ++emptyIdx)
{
if (!iTimeMap.isHold())
{
oSchema.set(emptySample);
}
else
{
oSchema.setFromPrevious();
}
}
totalSet += numEmpty;
}
for (; reqIdx < numSamples; reqIdx++, totalSet++)
{
INuPatchSchema::Sample iSamp = iSchema.getValue(reqIdx);
ONuPatchSchema::Sample oSamp;
Abc::P3fArraySamplePtr posPtr = iSamp.getPositions();
if (posPtr)
oSamp.setPositions(*posPtr);
Abc::V3fArraySamplePtr velocPtr = iSamp.getVelocities();
if (velocPtr)
oSamp.setVelocities(*velocPtr);
oSamp.setNu(iSamp.getNumU());
oSamp.setNv(iSamp.getNumV());
oSamp.setUOrder(iSamp.getUOrder());
oSamp.setVOrder(iSamp.getVOrder());
Abc::FloatArraySamplePtr uKnotsPtr = iSamp.getUKnot();
if (uKnotsPtr)
oSamp.setUKnot(*uKnotsPtr);
Abc::FloatArraySamplePtr vKnotsPtr = iSamp.getVKnot();
if (vKnotsPtr)
oSamp.setVKnot(*vKnotsPtr);
IV2fGeomParam::Sample iUVSample;
OV2fGeomParam::Sample oUVSample;
if (uvs)
{
getOGeomParamSamp <IV2fGeomParam, IV2fGeomParam::Sample,
OV2fGeomParam::Sample>(uvs, iUVSample,
oUVSample, reqIdx);
oSamp.setUVs(oUVSample);
}
IN3fGeomParam::Sample iNormalsSample;
ON3fGeomParam::Sample oNormalsSample;
if (normals)
{
getOGeomParamSamp <IN3fGeomParam, IN3fGeomParam::Sample,
ON3fGeomParam::Sample>(normals, iNormalsSample,
oNormalsSample, reqIdx);
oSamp.setNormals(oNormalsSample);
}
if (iSchema.hasTrimCurve())
{
oSamp.setTrimCurve(iSamp.getTrimNumLoops(),
*(iSamp.getTrimNumCurves()),
*(iSamp.getTrimNumVertices()),
*(iSamp.getTrimOrders()),
*(iSamp.getTrimKnots()),
*(iSamp.getTrimMins()),
*(iSamp.getTrimMaxes()),
*(iSamp.getTrimU()),
*(iSamp.getTrimV()),
*(iSamp.getTrimW()));
}
oSchema.set(oSamp);
// our first sample was written, AND we want to hold so set our previous samples to this one!
if (iTimeMap.isHold() && totalSet == 0)
{
for (index_t emptyIdx = 0; emptyIdx < numEmpty; ++emptyIdx)
{
oSchema.setFromPrevious();
}
totalSet += numEmpty;
}
}
}
for (size_t i = totalSet; i < totalSamples; ++i)
{
if (!iTimeMap.isHold())
{
oSchema.set(emptySample);
}
else
{
oSchema.setFromPrevious();
}
}
}
else
{
if (!atRoot)
{
outObj = OObject(oParentObj, header.getName(), header.getMetaData());
}
else
{
// for stitching properties of the top level objects
outObj = oParentObj;
}
// collect the top level compound property
ICompoundPropertyVec iCompoundProps(iObjects.size());
for (size_t i = 0; i < iObjects.size(); ++i)
{
if (!iObjects[i].valid())
{
continue;
}
iCompoundProps[i] = iObjects[i].getProperties();
}
OCompoundProperty oCompoundProperty = outObj.getProperties();
stitchCompoundProp(iCompoundProps, oCompoundProperty, iTimeMap);
}
// After done writing THIS OObject node, if input nodes have children,
// go deeper, otherwise we are done here
for (size_t i = 0 ; i < iObjects.size(); i++ )
{
if (!iObjects[i].valid())
{
continue;
}
for (size_t j = 0; j < iObjects[i].getNumChildren(); ++j)
{
std::vector< IObject > childObjects;
std::string childName = iObjects[i].getChildHeader(j).getName();
// skip names that we've already written out
if (outObj.getChildHeader(childName) != NULL)
{
continue;
}
for (size_t k =i; k < iObjects.size(); ++k)
{
childObjects.push_back(iObjects[k].getChild(childName));
}
visitObjects(childObjects, outObj, iTimeMap, false);
}
}
}
//-*****************************************************************************
//-*****************************************************************************
// DO IT.
//-*****************************************************************************
//-*****************************************************************************
int main( int argc, char *argv[] )
{
if (argc < 4)
{
std::cerr << "USAGE: " << argv[0] << " [-v] [-hold] outFile.abc inFile1.abc"
<< " inFile2.abc (inFile3.abc ...)" << std::endl;
std::cerr << "Where -v is a verbosity flag which prints the IObject"
<< " being processed." << std::endl;
std::cerr << "And -hold flags whether to fill in the first or previous "
<< "sample instead of an empty one for missing samples." << std::endl;
return -1;
}
{
size_t numInputs = argc - 2;
std::string fileName;
TimeAndSamplesMap timeMap;
std::vector< chrono_t > minVec;
minVec.reserve(numInputs);
std::vector< IArchive > iArchives;
iArchives.reserve(numInputs);
std::map< chrono_t, size_t > minIndexMap;
Alembic::AbcCoreFactory::IFactory factory;
factory.setPolicy(ErrorHandler::kThrowPolicy);
Alembic::AbcCoreFactory::IFactory::CoreType coreType;
for (int i = 1; i < argc; ++i)
{
// check for optional verbose
if (argv[i] == std::string("-v"))
{
timeMap.setVerbose(true);
numInputs --;
continue;
}
// check for optional hold
else if (argv[i] == std::string("-hold"))
{
timeMap.setHold(true);
numInputs --;
continue;
}
// first non flag argument is our output file name
else if (fileName.empty())
{
fileName = argv[i];
continue;
}
IArchive archive = factory.getArchive(argv[i], coreType);
if (!archive.valid())
{
std::cerr << "ERROR: " << argv[i] <<
" not a valid Alembic file" << std::endl;
return 1;
}
// reorder the input files according to their mins
chrono_t min = DBL_MAX;
Alembic::Util::uint32_t numSamplings = archive.getNumTimeSamplings();
timeMap.add(archive.getTimeSampling(0),
archive.getMaxNumSamplesForTimeSamplingIndex(0));
if (numSamplings > 1)
{
// timesampling index 0 is special, so it will be skipped
// use the first time on the next time sampling to determine
// our archive order the archive order
min = archive.getTimeSampling(1)->getSampleTime(0);
for (Alembic::Util::uint32_t s = 1; s < numSamplings; ++s)
{
timeMap.add(archive.getTimeSampling(s),
archive.getMaxNumSamplesForTimeSamplingIndex(s));
}
minVec.push_back(min);
if (minIndexMap.count(min) == 0)
{
minIndexMap.insert(std::make_pair(min, minVec.size() - 1));
}
else if (!iArchives.empty())
{
std::cerr << "ERROR: overlapping frame range between "
<< iArchives[0].getName() << " and " << argv[i] << std::endl;
return 1;
}
}
iArchives.push_back(archive);
}
// now reorder the input nodes so they are in increasing order of their
// min values in the frame range
std::sort(minVec.begin(), minVec.end());
std::vector< IArchive > iOrderedArchives;
iOrderedArchives.reserve(numInputs);
for (size_t f = 0; f < numInputs; ++f)
{
size_t index = minIndexMap.find(minVec[f])->second;
iOrderedArchives.push_back(iArchives[index]);
}
// since important meta data hints can be on the archive
// and will likely be part of every input in the sequence
// propagate the one from the first archive to our output
MetaData md = iOrderedArchives[0].getTop().getMetaData();
std::string appWriter = "AbcStitcher";
std::string userStr = md.get(Abc::kUserDescriptionKey);
if (!userStr.empty())
{
userStr = "AbcStitcher: " + userStr;
}
// Create an archive with the default writer
OArchive oArchive;
if (coreType == Alembic::AbcCoreFactory::IFactory::kOgawa)
{
oArchive = CreateArchiveWithInfo(
Alembic::AbcCoreOgawa::WriteArchive(),
fileName, appWriter, userStr, md, ErrorHandler::kThrowPolicy);
}
#ifdef ALEMBIC_WITH_HDF5
else if (coreType == Alembic::AbcCoreFactory::IFactory::kHDF5)
{
oArchive = CreateArchiveWithInfo(
Alembic::AbcCoreHDF5::WriteArchive(),
fileName, appWriter, userStr, md, ErrorHandler::kThrowPolicy);
}
#endif
OObject oRoot = oArchive.getTop();
if (!oRoot.valid())
{
return -1;
}
std::vector<IObject> iRoots(numInputs);
for (size_t e = 0; e < numInputs; ++e)
{
iRoots[e] = iOrderedArchives[e].getTop();
}
visitObjects(iRoots, oRoot, timeMap, true);
}
return 0;
}