Files
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

906 lines
29 KiB
C++

//-*****************************************************************************
//
// Copyright (c) 2009-2011,
// 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 <ri.h>
#include "WriteGeo.h"
#include "SampleUtil.h"
#include "ArbAttrUtil.h"
#include "SubDTags.h"
#ifdef PRMAN_USE_ABCMATERIAL
#include "WriteMaterial.h"
#endif
//-*****************************************************************************
void RestoreResource( const std::string & resourceName )
{
ParamListBuilder paramListBuilder;
paramListBuilder.addStringValue("restore", true);
paramListBuilder.add( "string operation",
paramListBuilder.finishStringVector() );
paramListBuilder.addStringValue("shading", true);
paramListBuilder.add( "string subset",
paramListBuilder.finishStringVector() );
RiResourceV(
const_cast<char *>( resourceName.c_str() ),
const_cast<char *>( "attributes" ),
paramListBuilder.n(),
paramListBuilder.nms(),
paramListBuilder.vals());
}
void SaveResource( const std::string & resourceName )
{
ParamListBuilder paramListBuilder;
paramListBuilder.addStringValue("save", true);
paramListBuilder.add( "string operation",
paramListBuilder.finishStringVector() );
RiResourceV(
const_cast<char *>( resourceName.c_str() ),
const_cast<char *>( "attributes" ),
paramListBuilder.n(),
paramListBuilder.nms(),
paramListBuilder.vals());
}
void ApplyResources( IObject object, ProcArgs &args )
{
std::string resourceName;
//first check full name...
resourceName = args.getResource( object.getFullName() );
if ( resourceName.empty() )
{
//...and then base name
resourceName = args.getResource( object.getName() );
}
if ( !resourceName.empty() )
{
RestoreResource(resourceName);
}
}
//-*****************************************************************************
void ProcessXform( IXform &xform, ProcArgs &args )
{
IXformSchema &xs = xform.getSchema();
TimeSamplingPtr ts = xs.getTimeSampling();
size_t xformSamps = xs.getNumSamples();
SampleTimeSet sampleTimes;
GetRelevantSampleTimes( args, ts, xformSamps, sampleTimes );
bool multiSample = sampleTimes.size() > 1;
std::vector<XformSample> sampleVectors;
sampleVectors.resize( sampleTimes.size() );
//fetch all operators at each sample time first
size_t sampleTimeIndex = 0;
for ( SampleTimeSet::iterator I = sampleTimes.begin();
I != sampleTimes.end(); ++I, ++sampleTimeIndex )
{
ISampleSelector sampleSelector( *I );
xs.get( sampleVectors[sampleTimeIndex], sampleSelector );
}
if (xs.getInheritsXforms () == false)
{
RiIdentity ();
}
//loop through the operators individually since a MotionBegin block
//can enclose only homogenous statements
for ( size_t i = 0, e = xs.getNumOps(); i < e; ++i )
{
if ( multiSample ) { WriteMotionBegin(args, sampleTimes); }
for ( size_t j = 0; j < sampleVectors.size(); ++j )
{
XformOp &op = sampleVectors[j][i];
switch ( op.getType() )
{
case kScaleOperation:
{
V3d value = op.getScale();
RiScale( value.x, value.y, value.z );
break;
}
case kTranslateOperation:
{
V3d value = op.getTranslate();
RiTranslate( value.x, value.y, value.z );
break;
}
case kRotateOperation:
case kRotateXOperation:
case kRotateYOperation:
case kRotateZOperation:
{
V3d axis = op.getAxis();
float degrees = op.getAngle();
RiRotate( degrees, axis.x, axis.y, axis.z );
break;
}
case kMatrixOperation:
{
WriteConcatTransform( op.getMatrix() );
break;
}
}
}
if ( multiSample ) { RiMotionEnd(); }
}
}
//-*****************************************************************************
void ProcessPolyMesh( IPolyMesh &polymesh, ProcArgs &args )
{
IPolyMeshSchema &ps = polymesh.getSchema();
TimeSamplingPtr ts = ps.getTimeSampling();
SampleTimeSet sampleTimes;
GetRelevantSampleTimes( args, ts, ps.getNumSamples(), sampleTimes );
bool multiSample = sampleTimes.size() > 1;
if ( multiSample ) { WriteMotionBegin( args, sampleTimes ); }
for ( SampleTimeSet::iterator iter = sampleTimes.begin();
iter != sampleTimes.end(); ++ iter )
{
ISampleSelector sampleSelector( *iter );
IPolyMeshSchema::Sample sample = ps.getValue( sampleSelector );
RtInt npolys = (RtInt) sample.getFaceCounts()->size();
ParamListBuilder paramListBuilder;
paramListBuilder.add( "P", (RtPointer)sample.getPositions()->get() );
IV2fGeomParam uvParam = ps.getUVsParam();
if ( uvParam.valid() )
{
ICompoundProperty parent = uvParam.getParent();
if ( !args.flipv )
{
AddGeomParamToParamListBuilder<IV2fGeomParam>(
parent,
uvParam.getHeader(),
sampleSelector,
"float",
paramListBuilder,
2,
"st");
}
else if ( std::vector<float> * values =
AddGeomParamToParamListBuilderAsFloat<IV2fGeomParam, float>(
parent,
uvParam.getHeader(),
sampleSelector,
"float",
paramListBuilder,
"st") )
{
for ( size_t i = 1, e = values->size(); i < e; i += 2 )
{
(*values)[i] = 1.0 - (*values)[i];
}
}
}
IN3fGeomParam nParam = ps.getNormalsParam();
if ( nParam.valid() )
{
ICompoundProperty parent = nParam.getParent();
AddGeomParamToParamListBuilder<IN3fGeomParam>(
parent,
nParam.getHeader(),
sampleSelector,
"normal",
paramListBuilder);
}
ICompoundProperty arbGeomParams = ps.getArbGeomParams();
AddArbitraryGeomParams( arbGeomParams,
sampleSelector, paramListBuilder );
RiPointsPolygonsV(
npolys,
(RtInt*) sample.getFaceCounts()->get(),
(RtInt*) sample.getFaceIndices()->get(),
paramListBuilder.n(),
paramListBuilder.nms(),
paramListBuilder.vals() );
}
if (multiSample) RiMotionEnd();
}
//-*****************************************************************************
void ProcessSubD( ISubD &subd, ProcArgs &args, const std::string & facesetName )
{
ISubDSchema &ss = subd.getSchema();
TimeSamplingPtr ts = ss.getTimeSampling();
SampleTimeSet sampleTimes;
GetRelevantSampleTimes( args, ts, ss.getNumSamples(), sampleTimes );
bool multiSample = sampleTimes.size() > 1;
//include this code path for future expansion
bool isHierarchicalSubD = false;
bool hasLocalResources = false;
std::vector<IFaceSet> faceSets;
std::vector<std::string> faceSetResourceNames;
if ( facesetName.empty() )
{
std::vector <std::string> childFaceSetNames;
ss.getFaceSetNames(childFaceSetNames);
faceSets.reserve(childFaceSetNames.size());
faceSetResourceNames.reserve(childFaceSetNames.size());
for (size_t i = 0; i < childFaceSetNames.size(); ++i)
{
faceSets.push_back(ss.getFaceSet(childFaceSetNames[i]));
IFaceSet & faceSet = faceSets.back();
std::string resourceName = args.getResource(
faceSet.getFullName() );
if ( resourceName.empty() )
{
resourceName = args.getResource( faceSet.getName() );
}
#ifdef PRMAN_USE_ABCMATERIAL
Mat::MaterialFlatten mafla(faceSet);
if (!mafla.empty())
{
if (!hasLocalResources)
{
RiResourceBegin();
hasLocalResources = true;
}
RiAttributeBegin();
if ( !resourceName.empty() )
{
//restore existing resource state here
RestoreResource( resourceName );
}
WriteMaterial( mafla, args );
resourceName = faceSet.getFullName();
SaveResource( resourceName );
RiAttributeEnd();
}
#endif
faceSetResourceNames.push_back(resourceName);
}
}
#ifdef PRMAN_USE_ABCMATERIAL
else
{
//handle single faceset material directly
if ( ss.hasFaceSet( facesetName ) )
{
IFaceSet faceSet = ss.getFaceSet( facesetName );
ApplyObjectMaterial(faceSet, args);
}
}
#endif
if ( multiSample ) { WriteMotionBegin( args, sampleTimes ); }
for ( SampleTimeSet::iterator iter = sampleTimes.begin();
iter != sampleTimes.end(); ++iter )
{
ISampleSelector sampleSelector( *iter );
ISubDSchema::Sample sample = ss.getValue( sampleSelector );
RtInt npolys = (RtInt) sample.getFaceCounts()->size();
ParamListBuilder paramListBuilder;
paramListBuilder.add( "P", (RtPointer)sample.getPositions()->get() );
IV2fGeomParam uvParam = ss.getUVsParam();
if ( uvParam.valid() )
{
ICompoundProperty parent = uvParam.getParent();
if ( !args.flipv )
{
AddGeomParamToParamListBuilder<IV2fGeomParam>(
parent,
uvParam.getHeader(),
sampleSelector,
"float",
paramListBuilder,
2,
"st");
}
else if ( std::vector<float> * values =
AddGeomParamToParamListBuilderAsFloat<IV2fGeomParam, float>(
parent,
uvParam.getHeader(),
sampleSelector,
"float",
paramListBuilder,
"st") )
{
for ( size_t i = 1, e = values->size(); i < e; i += 2 )
{
(*values)[i] = 1.0 - (*values)[i];
}
}
}
ICompoundProperty arbGeomParams = ss.getArbGeomParams();
AddArbitraryGeomParams( arbGeomParams,
sampleSelector, paramListBuilder );
std::string subdScheme = sample.getSubdivisionScheme();
SubDTagBuilder tags;
ProcessFacevaryingInterpolateBoundry( tags, sample );
ProcessInterpolateBoundry( tags, sample );
ProcessFacevaryingPropagateCorners( tags, sample );
ProcessHoles( tags, sample );
ProcessCreases( tags, sample );
ProcessCorners( tags, sample );
if ( !facesetName.empty() )
{
if ( ss.hasFaceSet( facesetName ) )
{
IFaceSet faceSet = ss.getFaceSet( facesetName );
ApplyResources( faceSet, args );
// TODO, move the hold test outside of MotionBegin
// as it's not meaningful to change per sample
IFaceSetSchema::Sample faceSetSample =
faceSet.getSchema().getValue( sampleSelector );
std::set<int> facesToKeep;
facesToKeep.insert( faceSetSample.getFaces()->get(),
faceSetSample.getFaces()->get() +
faceSetSample.getFaces()->size() );
for ( int i = 0; i < npolys; ++i )
{
if ( facesToKeep.find( i ) == facesToKeep.end() )
{
tags.add( "hole" );
tags.addIntArg( i );
}
}
}
}
else
{
//loop through the facesets and determine whether there are any
//resources assigned to each
for (size_t i = 0; i < faceSetResourceNames.size(); ++i)
{
const std::string & resourceName = faceSetResourceNames[i];
//TODO, visibility?
if ( !resourceName.empty() )
{
IFaceSet & faceSet = faceSets[i];
isHierarchicalSubD = true;
tags.add("faceedit");
Int32ArraySamplePtr faces = faceSet.getSchema().getValue(
sampleSelector ).getFaces();
for (size_t j = 0, e = faces->size(); j < e; ++j)
{
tags.addIntArg(1); //yep, every face gets a 1 in front of it too
tags.addIntArg( (int) faces->get()[j]);
}
tags.addStringArg( "attributes" );
tags.addStringArg( resourceName );
tags.addStringArg( "shading" );
}
}
}
if ( isHierarchicalSubD )
{
RiHierarchicalSubdivisionMeshV(
const_cast<RtToken>( subdScheme.c_str() ),
npolys,
(RtInt*) sample.getFaceCounts()->get(),
(RtInt*) sample.getFaceIndices()->get(),
tags.nt(),
tags.tags(),
tags.nargs( true ),
tags.intargs(),
tags.floatargs(),
tags.stringargs(),
paramListBuilder.n(),
paramListBuilder.nms(),
paramListBuilder.vals()
);
}
else
{
RiSubdivisionMeshV(
const_cast<RtToken>(subdScheme.c_str() ),
npolys,
(RtInt*) sample.getFaceCounts()->get(),
(RtInt*) sample.getFaceIndices()->get(),
tags.nt(),
tags.tags(),
tags.nargs( false ),
tags.intargs(),
tags.floatargs(),
paramListBuilder.n(),
paramListBuilder.nms(),
paramListBuilder.vals()
);
}
}
if ( multiSample ) { RiMotionEnd(); }
if ( hasLocalResources ) { RiResourceEnd(); }
}
//-*****************************************************************************
void ProcessNuPatch( INuPatch &patch, ProcArgs &args )
{
INuPatchSchema &ps = patch.getSchema();
TimeSamplingPtr ts = ps.getTimeSampling();
SampleTimeSet sampleTimes;
GetRelevantSampleTimes( args, ts, ps.getNumSamples(), sampleTimes );
//trim curves are described outside the motion blocks
if ( ps.hasTrimCurve() )
{
//get the current time sample independent of any shutter values
INuPatchSchema::Sample sample = ps.getValue(
ISampleSelector( args.frame / args.fps ) );
RiTrimCurve( sample.getTrimNumCurves()->size(), //numloops
(RtInt*) sample.getTrimNumCurves()->get(),
(RtInt*) sample.getTrimOrders()->get(),
(RtFloat*) sample.getTrimKnots()->get(),
(RtFloat*) sample.getTrimMins()->get(),
(RtFloat*) sample.getTrimMaxes()->get(),
(RtInt*) sample.getTrimNumVertices()->get(),
(RtFloat*) sample.getTrimU()->get(),
(RtFloat*) sample.getTrimV()->get(),
(RtFloat*) sample.getTrimW()->get() );
}
bool multiSample = sampleTimes.size() > 1;
if ( multiSample ) { WriteMotionBegin( args, sampleTimes ); }
for ( SampleTimeSet::iterator iter = sampleTimes.begin();
iter != sampleTimes.end(); ++iter )
{
ISampleSelector sampleSelector( *iter );
INuPatchSchema::Sample sample = ps.getValue( sampleSelector );
ParamListBuilder paramListBuilder;
//build this here so that it's still in scope when RiNuPatchV is
//called.
std::vector<RtFloat> pwValues;
if ( sample.getPositionWeights() )
{
if ( sample.getPositionWeights()->size() == sample.getPositions()->size() )
{
//need to combine P with weight form Pw
pwValues.reserve( sample.getPositions()->size() * 4 );
const float32_t * pStart = reinterpret_cast<const float32_t * >(
sample.getPositions()->get() );
const float32_t * wStart = reinterpret_cast<const float32_t * >(
sample.getPositionWeights()->get() );
for ( size_t i = 0, e = sample.getPositionWeights()->size();
i < e; ++i )
{
pwValues.push_back( pStart[i*3] );
pwValues.push_back( pStart[i*3+1] );
pwValues.push_back( pStart[i*3+2] );
pwValues.push_back( wStart[i] );
}
paramListBuilder.add( "Pw", (RtPointer) &pwValues[0] );
}
}
if ( pwValues.empty() )
{
//no Pw so go straight with P
paramListBuilder.add( "P",
(RtPointer)sample.getPositions()->get() );
}
ICompoundProperty arbGeomParams = ps.getArbGeomParams();
AddArbitraryGeomParams( arbGeomParams,
sampleSelector, paramListBuilder );
//For now, use the last knot value for umin and umax as it's
//not described in the alembic data
RiNuPatchV(
sample.getNumU(),
sample.getUOrder(),
(RtFloat *) sample.getUKnot()->get(),
0.0, //umin
sample.getUKnot()->get()[sample.getUKnot()->size()-1],//umax
sample.getNumV(),
sample.getVOrder(),
(RtFloat *) sample.getVKnot()->get(),
0.0, //vmin
sample.getVKnot()->get()[sample.getVKnot()->size()-1], //vmax
paramListBuilder.n(),
paramListBuilder.nms(),
paramListBuilder.vals() );
}
if ( multiSample ) { RiMotionEnd(); }
}
//-*****************************************************************************
void ProcessPoints( IPoints &points, ProcArgs &args )
{
IPointsSchema &ps = points.getSchema();
TimeSamplingPtr ts = ps.getTimeSampling();
SampleTimeSet sampleTimes;
//for now, punt on the changing point count case -- even for frame ranges
//for which the point count isn't changing
if ( ps.getIdsProperty().isConstant() )
{
//grab only the current time
sampleTimes.insert( args.frame / args.fps );
}
else
{
GetRelevantSampleTimes( args, ts, ps.getNumSamples(), sampleTimes );
}
bool multiSample = sampleTimes.size() > 1;
if ( multiSample ) { WriteMotionBegin( args, sampleTimes ); }
for ( SampleTimeSet::iterator iter = sampleTimes.begin();
iter != sampleTimes.end(); ++iter )
{
ISampleSelector sampleSelector( *iter );
IPointsSchema::Sample sample = ps.getValue( sampleSelector );
ParamListBuilder paramListBuilder;
paramListBuilder.add( "P", (RtPointer)sample.getPositions()->get() );
IFloatGeomParam widthParam = ps.getWidthsParam();
if ( widthParam.valid() )
{
ICompoundProperty parent = widthParam.getParent();
//prman requires "width" to be named "constantwidth" when
//constant instead of declared as "constant float width".
//It's even got an error message specifically for it.
std::string widthName;
if ( widthParam.getScope() == kConstantScope ||
widthParam.getScope() == kUnknownScope )
{
widthName = "constantwidth";
}
else
{
widthName = "width";
}
AddGeomParamToParamListBuilder<IFloatGeomParam>(
parent,
widthParam.getHeader(),
sampleSelector,
"float",
paramListBuilder,
1,
widthName);
}
ICompoundProperty arbGeomParams = ps.getArbGeomParams();
AddArbitraryGeomParams( arbGeomParams,
sampleSelector, paramListBuilder );
RiPointsV(sample.getPositions()->size(),
paramListBuilder.n(),
paramListBuilder.nms(),
paramListBuilder.vals() );
}
if ( multiSample ) { RiMotionEnd(); }
}
//-*****************************************************************************
void ProcessCurves( ICurves &curves, ProcArgs &args )
{
ICurvesSchema &cs = curves.getSchema();
TimeSamplingPtr ts = cs.getTimeSampling();
SampleTimeSet sampleTimes;
GetRelevantSampleTimes( args, ts, cs.getNumSamples(), sampleTimes );
bool multiSample = sampleTimes.size() > 1;
bool firstSample = true;
for ( SampleTimeSet::iterator iter = sampleTimes.begin();
iter != sampleTimes.end(); ++iter )
{
ISampleSelector sampleSelector( *iter );
ICurvesSchema::Sample sample = cs.getValue( sampleSelector );
//need to set the basis prior to the MotionBegin block
if ( firstSample )
{
firstSample = false;
BasisType basisType = sample.getBasis();
if ( basisType != kNoBasis )
{
RtBasis * basis = NULL;
RtInt step = 0;
switch ( basisType )
{
case kBezierBasis:
basis = &RiBezierBasis;
step = RI_BEZIERSTEP;
break;
case kBsplineBasis:
basis = &RiBSplineBasis;
step = RI_BSPLINESTEP;
break;
case kCatmullromBasis:
basis = &RiCatmullRomBasis;
step = RI_CATMULLROMSTEP;
break;
case kHermiteBasis:
basis = &RiHermiteBasis;
step = RI_HERMITESTEP;
break;
case kPowerBasis:
basis = &RiPowerBasis;
step = RI_POWERSTEP;
break;
default:
break;
}
if ( basis != NULL )
{
RiBasis( *basis, step, *basis, step);
}
}
if ( multiSample ) { WriteMotionBegin( args, sampleTimes ); }
}
ParamListBuilder paramListBuilder;
paramListBuilder.add( "P", (RtPointer)sample.getPositions()->get() );
IFloatGeomParam widthParam = cs.getWidthsParam();
if ( widthParam.valid() )
{
ICompoundProperty parent = widthParam.getParent();
//prman requires "width" to be named "constantwidth" when
//constant instead of declared as "constant float width".
//It's even got an error message specifically for it.
std::string widthName;
if ( widthParam.getScope() == kConstantScope ||
widthParam.getScope() == kUnknownScope )
{
widthName = "constantwidth";
}
else
{
widthName = "width";
}
AddGeomParamToParamListBuilder<IFloatGeomParam>(
parent,
widthParam.getHeader(),
sampleSelector,
"float",
paramListBuilder,
1,
widthName);
}
IN3fGeomParam nParam = cs.getNormalsParam();
if ( nParam.valid() )
{
ICompoundProperty parent = nParam.getParent();
AddGeomParamToParamListBuilder<IN3fGeomParam>(
parent,
nParam.getHeader(),
sampleSelector,
"normal",
paramListBuilder);
}
IV2fGeomParam uvParam = cs.getUVsParam();
if ( uvParam.valid() )
{
ICompoundProperty parent = uvParam.getParent();
AddGeomParamToParamListBuilder<IV2fGeomParam>(
parent,
uvParam.getHeader(),
sampleSelector,
"float",
paramListBuilder,
2,
"st");
}
ICompoundProperty arbGeomParams = cs.getArbGeomParams();
AddArbitraryGeomParams( arbGeomParams,
sampleSelector, paramListBuilder );
RtToken curveType;
switch ( sample.getType() )
{
case kCubic:
curveType = const_cast<RtToken>( "cubic" );
break;
default:
curveType = const_cast<RtToken>( "linear" );
}
RtToken wrap;
switch ( sample.getWrap() )
{
case kPeriodic:
wrap = const_cast<RtToken>( "periodic" );
break;
default:
wrap = const_cast<RtToken>( "nonperiodic" );
}
RiCurvesV(curveType,
sample.getNumCurves(),
(RtInt*) sample.getCurvesNumVertices()->get(),
wrap,
paramListBuilder.n(),
paramListBuilder.nms(),
paramListBuilder.vals() );
}
if ( multiSample ) { RiMotionEnd(); }
}
//-*****************************************************************************
void WriteIdentifier( const ObjectHeader &ohead )
{
std::string name = ohead.getFullName();
char* nameArray[] = { const_cast<char*>( name.c_str() ), RI_NULL };
RiAttribute(const_cast<char*>( "identifier" ), const_cast<char*>( "name" ),
nameArray, RI_NULL );
}