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

647 lines
21 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 "util.h"
#include <Alembic/AbcGeom/All.h>
using namespace Alembic::AbcGeom;
using namespace Alembic::Abc;
using namespace Alembic::AbcCoreAbstract;
void TimeAndSamplesMap::add(TimeSamplingPtr iTime, size_t iNumSamples)
{
if (iNumSamples == 0)
{
iNumSamples = 1;
}
for (size_t i = 0; i < mTimeSampling.size(); ++i)
{
if (mTimeSampling[i]->getTimeSamplingType() ==
iTime->getTimeSamplingType())
{
chrono_t curLastTime =
mTimeSampling[i]->getSampleTime(mExpectedSamples[i]);
chrono_t lastTime = iTime->getSampleTime(iNumSamples);
if (lastTime < curLastTime)
{
lastTime = curLastTime;
}
if (mTimeSampling[i]->getSampleTime(0) > iTime->getSampleTime(0))
{
mTimeSampling[i] = iTime;
}
mExpectedSamples[i] = mTimeSampling[i]->getNearIndex(lastTime,
std::numeric_limits< index_t >::max()).first;
return;
}
}
mTimeSampling.push_back(iTime);
mExpectedSamples.push_back(iNumSamples);
}
TimeSamplingPtr TimeAndSamplesMap::get(TimeSamplingPtr iTime,
std::size_t & oNumSamples) const
{
for (size_t i = 0; i < mTimeSampling.size(); ++i)
{
if (mTimeSampling[i]->getTimeSamplingType() ==
iTime->getTimeSamplingType())
{
oNumSamples = mExpectedSamples[i];
return mTimeSampling[i];
}
}
oNumSamples = 0;
return TimeSamplingPtr();
}
index_t getIndexSample(index_t iCurOutIndex, TimeSamplingPtr iOutTime,
index_t iInNumSamples, TimeSamplingPtr iInTime, index_t & oNumEmpty)
{
// see if we are missing any samples for oNumEmpty
chrono_t curTime = iOutTime->getSampleTime(iCurOutIndex);
chrono_t inChrono = iInTime->getSampleTime(0);
if (curTime < inChrono)
{
index_t emptyEnd = iOutTime->getNearIndex(inChrono,
std::numeric_limits<index_t>::max()).first;
if (emptyEnd > iCurOutIndex)
{
oNumEmpty = emptyEnd - iCurOutIndex;
}
else
{
oNumEmpty = 0;
}
}
else
{
oNumEmpty = 0;
}
for (index_t i = 0; i < iInNumSamples; ++i)
{
inChrono = iInTime->getSampleTime(i);
if (curTime <= inChrono ||
Imath::equalWithAbsError(curTime, inChrono, 1e-5))
{
return i;
}
}
return iInNumSamples;
}
void checkAcyclic(const TimeSamplingType & tsType,
const std::string & fullNodeName)
{
if (tsType.isAcyclic())
{
std::cerr << "No support for stitching acyclic sampling node "
<< fullNodeName << std::endl;
exit(1);
}
}
void stitchArrayProp(const PropertyHeader & propHeader,
const ICompoundPropertyVec & iCompoundProps,
OCompoundProperty & oCompoundProp,
const TimeAndSamplesMap & iTimeMap)
{
size_t totalSamples = 0;
TimeSamplingPtr timePtr =
iTimeMap.get(propHeader.getTimeSampling(), totalSamples);
const DataType & dataType = propHeader.getDataType();
const MetaData & metaData = propHeader.getMetaData();
const std::string & propName = propHeader.getName();
Dimensions emptyDims(0);
ArraySample emptySample(NULL, dataType, emptyDims);
OArrayProperty writer(oCompoundProp, propName, dataType, metaData, timePtr);
size_t numInputs = iCompoundProps.size();
for (size_t iCpIndex = 0; iCpIndex < numInputs; iCpIndex++)
{
if (!iCompoundProps[iCpIndex].valid())
{
continue;
}
const PropertyHeader * childHeader =
iCompoundProps[iCpIndex].getPropertyHeader(propName);
if (!childHeader || dataType != childHeader->getDataType())
{
continue;
}
IArrayProperty reader(iCompoundProps[iCpIndex], propName);
index_t numSamples = reader.getNumSamples();
ArraySamplePtr dataPtr;
index_t numEmpty;
index_t k = getIndexSample(writer.getNumSamples(),
writer.getTimeSampling(), numSamples,
reader.getTimeSampling(), numEmpty);
for (index_t j = 0; j < numEmpty; ++j)
{
if (!iTimeMap.isHold())
{
writer.set(emptySample);
}
else if (writer.getNumSamples() == 0)
{
reader.get(dataPtr, 0);
writer.set(*dataPtr);
}
else
{
writer.setFromPrevious();
}
}
for (; k < numSamples; k++)
{
reader.get(dataPtr, k);
writer.set(*dataPtr);
}
}
// fill in any other empties
for (size_t i = writer.getNumSamples(); i < totalSamples; ++i)
{
if (!iTimeMap.isHold())
{
writer.set(emptySample);
}
else
{
writer.setFromPrevious();
}
}
}
// return true if we needed to stitch the geom param
bool stitchArbGeomParam(const PropertyHeader & propHeader,
const ICompoundPropertyVec & iCompoundProps,
OCompoundProperty & oCompoundProp,
const TimeAndSamplesMap & iTimeMap)
{
// go through all the inputs to see if all the property types are the same
size_t numInputs = iCompoundProps.size();
const std::string & propName = propHeader.getName();
PropertyType ptype = propHeader.getPropertyType();
bool diffProp = false;
for (size_t iCpIndex = 1; iCpIndex < numInputs && diffProp == false;
iCpIndex++)
{
if (!iCompoundProps[iCpIndex].valid())
{
continue;
}
const PropertyHeader * childHeader =
iCompoundProps[iCpIndex].getPropertyHeader(propName);
if (childHeader && childHeader->getPropertyType() != ptype)
{
diffProp = true;
}
}
// all of the props are the same, lets stitch them like normal
if (!diffProp)
{
return false;
}
// we have a mismatch of indexed and non-index geom params, lets stitch them
// together AS indexed
std::vector< IArrayProperty > valsProp(numInputs);
std::vector< IArrayProperty > indicesProp(numInputs);
bool firstVals = true;
DataType dataType;
MetaData metaData;
TimeSamplingPtr timePtr;
// first we need to get our attrs
for (size_t iCpIndex = 0; iCpIndex < numInputs; iCpIndex++)
{
if (!iCompoundProps[iCpIndex].valid())
{
continue;
}
const PropertyHeader * childHeader =
iCompoundProps[iCpIndex].getPropertyHeader(propName);
if (childHeader && childHeader->isArray())
{
valsProp[iCpIndex] = IArrayProperty(iCompoundProps[iCpIndex],
propName);
if (firstVals)
{
firstVals = false;
dataType = valsProp[iCpIndex].getDataType();
metaData = valsProp[iCpIndex].getMetaData();
timePtr = valsProp[iCpIndex].getTimeSampling();
}
}
else if (childHeader && childHeader->isCompound())
{
ICompoundProperty cprop(iCompoundProps[iCpIndex], propName);
if (cprop.getPropertyHeader(".vals") != NULL &&
cprop.getPropertyHeader(".indices") != NULL)
{
valsProp[iCpIndex] = IArrayProperty(cprop, ".vals");
indicesProp[iCpIndex] = IArrayProperty(cprop, ".indices");
if (firstVals)
{
firstVals = false;
dataType = valsProp[iCpIndex].getDataType();
metaData = valsProp[iCpIndex].getMetaData();
timePtr = valsProp[iCpIndex].getTimeSampling();
}
}
}
}
size_t totalSamples = 0;
timePtr = iTimeMap.get(timePtr, totalSamples);
DataType indicesType(Alembic::Util::kUint32POD);
Dimensions emptyDims(0);
ArraySample emptySample(NULL, dataType, emptyDims);
ArraySample emptyIndicesSample(NULL, indicesType, emptyDims);
// we write indices and vals together
OCompoundProperty ocProp(oCompoundProp, propName, metaData);
OArrayProperty valsWriter(ocProp, ".vals", dataType, metaData, timePtr);
OArrayProperty indicesWriter(ocProp, ".indices", indicesType, timePtr);
for (size_t index = 0; index < numInputs; index++)
{
if (!valsProp[index].valid())
{
continue;
}
index_t numSamples = valsProp[index].getNumSamples();
ArraySamplePtr dataPtr;
index_t numEmpty;
index_t k = getIndexSample(valsWriter.getNumSamples(),
valsWriter.getTimeSampling(), numSamples,
valsProp[index].getTimeSampling(), numEmpty);
for (index_t j = 0; j < numEmpty; ++j)
{
if (!iTimeMap.isHold())
{
valsWriter.set(emptySample);
indicesWriter.set(emptyIndicesSample);
}
else if (valsWriter.getNumSamples() == 0)
{
valsProp[index].get(dataPtr, 0);
valsWriter.set(*dataPtr);
//
if (indicesProp[index].valid())
{
indicesProp[index].get(dataPtr, 0);
indicesWriter.set(*dataPtr);
}
}
else
{
valsWriter.setFromPrevious();
indicesWriter.setFromPrevious();
}
}
for (; k < numSamples; k++)
{
valsProp[index].get(dataPtr, k);
valsWriter.set(*dataPtr);
if (indicesProp[index].valid())
{
indicesProp[index].get(dataPtr, k);
indicesWriter.set(*dataPtr);
}
else
{
// we need to construct our indices manually
Dimensions dataDims = dataPtr->getDimensions();
std::vector<Alembic::Util::uint32_t> indicesVec(
dataDims.numPoints());
for (size_t dataIdx = 0; dataIdx < indicesVec.size(); ++dataIdx)
{
indicesVec[dataIdx] = (Alembic::Util::uint32_t) dataIdx;
}
// set the empty sample
if (indicesVec.empty())
{
indicesWriter.set(emptyIndicesSample);
}
else
{
ArraySample indicesSamp(&indicesVec.front(), indicesType,
dataDims);
indicesWriter.set(indicesSamp);
}
}
}
}
// fill in any other empties
for (size_t i = valsWriter.getNumSamples(); i < totalSamples; ++i)
{
if (!iTimeMap.isHold())
{
valsWriter.set(emptySample);
indicesWriter.set(emptyIndicesSample);
}
else
{
valsWriter.setFromPrevious();
indicesWriter.setFromPrevious();
}
}
return true;
}
template< typename T >
void scalarPropIO(IScalarProperty & reader,
Alembic::Util::uint8_t extent,
OScalarProperty & writer, bool hold)
{
std::vector< T > data(extent);
std::vector< T > emptyData(extent);
void * emptyPtr = static_cast< void* >(&emptyData.front());
void * vPtr = static_cast< void* >(&data.front());
index_t numSamples = reader.getNumSamples();
index_t numEmpty;
index_t k = getIndexSample(writer.getNumSamples(), writer.getTimeSampling(),
numSamples, reader.getTimeSampling(), numEmpty);
// not really empty, but set to a default 0 or empty string value
for (index_t i = 0; i < numEmpty; ++i)
{
if (!hold)
{
writer.set(emptyPtr);
}
else if (writer.getNumSamples() == 0)
{
reader.get(vPtr, 0);
writer.set(vPtr);
}
else
{
writer.setFromPrevious();
}
}
for (; k < numSamples; ++k)
{
reader.get(vPtr, k);
writer.set(vPtr);
}
}
void stitchScalarProp(const PropertyHeader & propHeader,
const ICompoundPropertyVec & iCompoundProps,
OCompoundProperty & oCompoundProp,
const TimeAndSamplesMap & iTimeMap)
{
size_t totalSamples = 0;
TimeSamplingPtr timePtr =
iTimeMap.get(propHeader.getTimeSampling(), totalSamples);
const DataType & dataType = propHeader.getDataType();
const MetaData & metaData = propHeader.getMetaData();
const std::string & propName = propHeader.getName();
Alembic::Util::PlainOldDataType pod = dataType.getPod();
OScalarProperty writer(oCompoundProp, propName, dataType, metaData,
timePtr);
size_t numInputs = iCompoundProps.size();
for (size_t iCpIndex = 0; iCpIndex < numInputs; iCpIndex++)
{
if (!iCompoundProps[iCpIndex].valid())
{
continue;
}
const PropertyHeader * childHeader =
iCompoundProps[iCpIndex].getPropertyHeader(propName);
if (!childHeader || dataType != childHeader->getDataType())
{
continue;
}
IScalarProperty reader(iCompoundProps[iCpIndex], propName);
Alembic::Util::uint8_t extent = dataType.getExtent();
switch(pod)
{
case Alembic::Util::kBooleanPOD:
scalarPropIO< Alembic::Util::bool_t >(reader, extent, writer, iTimeMap.isHold());
break;
case Alembic::Util::kUint8POD:
scalarPropIO< Alembic::Util::uint8_t >(reader, extent, writer, iTimeMap.isHold());
break;
case Alembic::Util::kInt8POD:
scalarPropIO< Alembic::Util::int8_t >(reader, extent, writer, iTimeMap.isHold());
break;
case Alembic::Util::kUint16POD:
scalarPropIO< Alembic::Util::uint16_t >(reader, extent, writer, iTimeMap.isHold());
break;
case Alembic::Util::kInt16POD:
scalarPropIO< Alembic::Util::int16_t >(reader, extent, writer, iTimeMap.isHold());
break;
case Alembic::Util::kUint32POD:
scalarPropIO< Alembic::Util::uint32_t >(reader, extent, writer, iTimeMap.isHold());
break;
case Alembic::Util::kInt32POD:
scalarPropIO< Alembic::Util::int32_t >(reader, extent, writer, iTimeMap.isHold());
break;
case Alembic::Util::kUint64POD:
scalarPropIO< Alembic::Util::uint64_t >(reader, extent, writer, iTimeMap.isHold());
break;
case Alembic::Util::kInt64POD:
scalarPropIO< Alembic::Util::int64_t >(reader, extent, writer, iTimeMap.isHold());
break;
case Alembic::Util::kFloat16POD:
scalarPropIO< Alembic::Util::float16_t >(reader, extent, writer, iTimeMap.isHold());
break;
case Alembic::Util::kFloat32POD:
scalarPropIO< Alembic::Util::float32_t >(reader, extent, writer, iTimeMap.isHold());
break;
case Alembic::Util::kFloat64POD:
scalarPropIO< Alembic::Util::float64_t >(reader, extent, writer, iTimeMap.isHold());
break;
case Alembic::Util::kStringPOD:
scalarPropIO< Alembic::Util::string >(reader, extent, writer, iTimeMap.isHold());
break;
case Alembic::Util::kWstringPOD:
scalarPropIO< Alembic::Util::wstring >(reader, extent, writer, iTimeMap.isHold());
break;
default:
break;
}
}
// set any extra empties, or hold
std::vector< Alembic::Util::string > emptyStr(dataType.getExtent());
std::vector< Alembic::Util::wstring > emptyWstr(dataType.getExtent());
std::vector< Alembic::Util::uint8_t > emptyBuffer(dataType.getNumBytes());
for (size_t i = writer.getNumSamples(); i < totalSamples; ++i)
{
if (iTimeMap.isHold())
{
writer.setFromPrevious();
}
else if (pod == Alembic::Util::kStringPOD)
{
writer.set(&emptyStr.front());
}
else if (pod == Alembic::Util::kWstringPOD)
{
writer.set(&emptyWstr.front());
}
else
{
writer.set(&emptyBuffer.front());
}
}
}
void stitchCompoundProp(ICompoundPropertyVec & iCompoundProps,
OCompoundProperty & oCompoundProp,
const TimeAndSamplesMap & iTimeMap)
{
size_t numCompounds = iCompoundProps.size();
for (size_t i = 0; i < numCompounds; ++i)
{
if (!iCompoundProps[i].valid())
{
continue;
}
size_t numProps = iCompoundProps[i].getNumProperties();
for (size_t propIndex = 0; propIndex < numProps; propIndex++)
{
const PropertyHeader & propHeader =
iCompoundProps[i].getPropertyHeader(propIndex);
if (oCompoundProp.getPropertyHeader(propHeader.getName()) != NULL)
{
continue;
}
if (propHeader.getMetaData().get("isGeomParam") == "true" &&
stitchArbGeomParam(propHeader, iCompoundProps, oCompoundProp,
iTimeMap))
{
continue;
}
else if (propHeader.isCompound())
{
ICompoundPropertyVec childProps;
for (size_t j = i; j < numCompounds; ++j)
{
if (!iCompoundProps[j].valid() ||
iCompoundProps[j].getPropertyHeader(
propHeader.getName()) == NULL)
{
continue;
}
childProps.push_back(ICompoundProperty(iCompoundProps[j],
propHeader.getName()));
}
OCompoundProperty child(oCompoundProp, propHeader.getName(),
propHeader.getMetaData());
stitchCompoundProp(childProps, child, iTimeMap);
}
else if (propHeader.isScalar())
{
stitchScalarProp(propHeader, iCompoundProps,
oCompoundProp, iTimeMap);
}
else if (propHeader.isArray())
{
stitchArrayProp(propHeader, iCompoundProps,
oCompoundProp, iTimeMap);
}
}
}
}