//-***************************************************************************** // // 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 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::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 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); } } } }