Skip to content

Commit

Permalink
MeshPrimitive : Add subdivision options
Browse files Browse the repository at this point in the history
  • Loading branch information
danieldresser-ie committed Apr 18, 2024
1 parent d2da6b3 commit 2206127
Show file tree
Hide file tree
Showing 5 changed files with 287 additions and 2 deletions.
5 changes: 5 additions & 0 deletions Changes
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
10.5.x.x (relative to 10.5.3.0)
========

Features
--------

- MeshPrimitive : Added interpolateBoundary, faceVaryingLinearInterpolation and triangleSubdivisionRule properties for controlling the shape of the subdivision limit surface.

Fixes
-----

Expand Down
50 changes: 49 additions & 1 deletion include/IECoreScene/MeshPrimitive.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include "IECoreScene/Export.h"
#include "IECoreScene/Primitive.h"

#include "IECore/InternedString.h"
#include "IECore/VectorTypedData.h"

namespace IECoreScene
Expand All @@ -61,12 +62,30 @@ class IECORESCENE_API MeshPrimitive : public Primitive

IE_CORE_DECLAREEXTENSIONOBJECT( MeshPrimitive, MeshPrimitiveTypeId, Primitive );

//! @name Define supported interpolations
/// \todo : In the future, we hope to use InternedStrings whenever we get/set
/// interpolations.
/// \todo : The meaning of "linear" has ended up being somewhat misaligned to
/// what we actually want. The ideal would probably be if "linear" was instead
/// named "none" - indicating that no subdivision is requested, and there was
/// a new value "bilinear", which indicated that the limit surface is simple
/// polygons, but subdivision is still being requested.
/////////////////////////////////////////////////////////////////////////////
//@{
static const IECore::InternedString interpolationLinear;
static const IECore::InternedString interpolationCatmullClark;
static const IECore::InternedString interpolationLoop;
//@}

/// Construct a MeshPrimitive with no faces.
MeshPrimitive();
/// Construct a MeshPrimitive. The number of faces specified by verticesPerFace->readable()->size().
/// Copies of the IntVectorData objects are taken rather than references to the initial data.
MeshPrimitive( IECore::ConstIntVectorDataPtr verticesPerFace, IECore::ConstIntVectorDataPtr vertexIds,
const std::string &interpolation = "linear", IECore::V3fVectorDataPtr p = nullptr );
const std::string &interpolation = interpolationLinear.string(), IECore::V3fVectorDataPtr p = nullptr );

/// Destructor
~MeshPrimitive() override;

//! @name Topology access
/// These functions allow access to get and set topology after construction.
Expand Down Expand Up @@ -102,6 +121,35 @@ class IECORESCENE_API MeshPrimitive : public Primitive
void removeCreases();
//@}

//! @name Subdivision options
/// These parameters control various details that affect the shape of the limit surface
/////////////////////////////////////////////////////////////////////////////
//@{

const IECore::InternedString &getInterpolateBoundary() const;
void setInterpolateBoundary( const IECore::InternedString &interpolateBoundary );

static const IECore::InternedString interpolateBoundaryNone;
static const IECore::InternedString interpolateBoundaryEdgeOnly;
static const IECore::InternedString interpolateBoundaryEdgeAndCorner;

const IECore::InternedString &getFaceVaryingLinearInterpolation() const;
void setFaceVaryingLinearInterpolation( const IECore::InternedString &faceVaryingLinearInterpolation );

static const IECore::InternedString faceVaryingLinearInterpolationNone;
static const IECore::InternedString faceVaryingLinearInterpolationCornersOnly;
static const IECore::InternedString faceVaryingLinearInterpolationCornersPlus1;
static const IECore::InternedString faceVaryingLinearInterpolationCornersPlus2;
static const IECore::InternedString faceVaryingLinearInterpolationBoundaries;
static const IECore::InternedString faceVaryingLinearInterpolationAll;

const IECore::InternedString &getTriangleSubdivisionRule() const;
void setTriangleSubdivisionRule( const IECore::InternedString &triangleSubdivisionRule );

static const IECore::InternedString triangleSubdivisionRuleCatmullClark;
static const IECore::InternedString triangleSubdivisionRuleSmooth;
//@}

size_t variableSize( PrimitiveVariable::Interpolation interpolation ) const override;

/// Render the mesh
Expand Down
129 changes: 128 additions & 1 deletion src/IECoreScene/MeshPrimitive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,13 @@
#include "IECoreScene/PolygonIterator.h"
#include "IECoreScene/Renderer.h"

#include "IECore/ClassData.h"
#include "IECore/MurmurHash.h"

#include "boost/format.hpp"

#include "tbb/spin_rw_mutex.h"

#include <algorithm>
#include <numeric>

Expand All @@ -62,6 +65,9 @@ IndexedIO::EntryID g_cornerSharpnessesEntry("cornerSharpnesses");
IndexedIO::EntryID g_creaseLengthsEntry("creaseLengths");
IndexedIO::EntryID g_creaseIdsEntry("creaseIds");
IndexedIO::EntryID g_creaseSharpnessesEntry("creaseSharpnesses");
IndexedIO::EntryID g_interpolateBoundaryEntry("interpolateBoundary");
IndexedIO::EntryID g_faceVaryingLinearInterpolationEntry("faceVaryingLinearInterpolation");
IndexedIO::EntryID g_triangleSubdivisionRuleEntry("triangleSubdivisionRule");

const IntVectorData *emptyIntVectorData()
{
Expand All @@ -75,21 +81,61 @@ const FloatVectorData *emptyFloatVectorData()
return g_d.get();
}

// \todo : Replace these with actual class members once we can break binary compatibility
struct MeshClassData
{
IECore::InternedString interpolateBoundary;
IECore::InternedString faceVaryingLinearInterpolation;
IECore::InternedString triangleSubdivisionRule;
};

// We intentionally don't clean up this static memory at shutdown, because we can't destruct it until
// every mesh has been destructed, and other static globals ( like the ObjectPool ) might be holding onto meshes.
// There's no real problem with leaking memory when the process is shutting down anyway.
static IECore::ClassData< MeshPrimitive, MeshClassData > *g_classData = new IECore::ClassData< MeshPrimitive, MeshClassData >();

// This lock must be held in read mode to read or write properties of individual class data entries,
// and held in write mode to add or remove class data entries
static tbb::spin_rw_mutex *g_classDataMutex = new tbb::spin_rw_mutex();

} // namespace

const IECore::InternedString MeshPrimitive::interpolationLinear( "linear" );
const IECore::InternedString MeshPrimitive::interpolationCatmullClark( "catmullClark" );
const IECore::InternedString MeshPrimitive::interpolationLoop( "loop" );
const IECore::InternedString MeshPrimitive::interpolateBoundaryNone( "none" );
const IECore::InternedString MeshPrimitive::interpolateBoundaryEdgeOnly( "edgeOnly" );
const IECore::InternedString MeshPrimitive::interpolateBoundaryEdgeAndCorner( "edgeAndCorner" );
const IECore::InternedString MeshPrimitive::faceVaryingLinearInterpolationNone( "none" );
const IECore::InternedString MeshPrimitive::faceVaryingLinearInterpolationCornersOnly( "cornersOnly" );
const IECore::InternedString MeshPrimitive::faceVaryingLinearInterpolationCornersPlus1( "cornersPlus1" );
const IECore::InternedString MeshPrimitive::faceVaryingLinearInterpolationCornersPlus2( "cornersPlus2" );
const IECore::InternedString MeshPrimitive::faceVaryingLinearInterpolationBoundaries( "boundaries" );
const IECore::InternedString MeshPrimitive::faceVaryingLinearInterpolationAll( "all" );
const IECore::InternedString MeshPrimitive::triangleSubdivisionRuleCatmullClark( "catmullClark" );
const IECore::InternedString MeshPrimitive::triangleSubdivisionRuleSmooth( "smooth" );

const unsigned int MeshPrimitive::m_ioVersion = 0;
IE_CORE_DEFINEOBJECTTYPEDESCRIPTION(MeshPrimitive);

MeshPrimitive::MeshPrimitive()
: m_verticesPerFace( new IntVectorData ), m_vertexIds( new IntVectorData ), m_numVertices( 0 ), m_interpolation( "linear" ), m_minVerticesPerFace( 0 ), m_maxVerticesPerFace( 0 )
{
{
tbb::spin_rw_mutex::scoped_lock lock( *g_classDataMutex, true );
g_classData->create( this, { interpolateBoundaryEdgeAndCorner, faceVaryingLinearInterpolationCornersPlus1, triangleSubdivisionRuleCatmullClark } );
}
removeCorners();
removeCreases();
}

MeshPrimitive::MeshPrimitive( ConstIntVectorDataPtr verticesPerFace, ConstIntVectorDataPtr vertexIds,
const std::string &interpolation, V3fVectorDataPtr p )
{
{
tbb::spin_rw_mutex::scoped_lock lock( *g_classDataMutex, true );
g_classData->create( this, { interpolateBoundaryEdgeAndCorner, faceVaryingLinearInterpolationCornersPlus1, triangleSubdivisionRuleCatmullClark } );
}
setTopology( verticesPerFace, vertexIds, interpolation );
if( p )
{
Expand All @@ -99,6 +145,12 @@ MeshPrimitive::MeshPrimitive( ConstIntVectorDataPtr verticesPerFace, ConstIntVec
}
}

MeshPrimitive::~MeshPrimitive()
{
tbb::spin_rw_mutex::scoped_lock lock( *g_classDataMutex, true );
g_classData->erase( this );
}

size_t MeshPrimitive::numFaces() const
{
return m_verticesPerFace->readable().size();
Expand Down Expand Up @@ -343,6 +395,42 @@ void MeshPrimitive::removeCreases()
m_creaseSharpnesses = emptyFloatVectorData();
}

const IECore::InternedString &MeshPrimitive::getInterpolateBoundary() const
{
tbb::spin_rw_mutex::scoped_lock lock( *g_classDataMutex, false );
return (*g_classData)[ this ].interpolateBoundary;
}

void MeshPrimitive::setInterpolateBoundary( const IECore::InternedString &interpolateBoundary )
{
tbb::spin_rw_mutex::scoped_lock lock( *g_classDataMutex, false );
(*g_classData)[ this ].interpolateBoundary = interpolateBoundary;
}

const IECore::InternedString &MeshPrimitive::getFaceVaryingLinearInterpolation() const
{
tbb::spin_rw_mutex::scoped_lock lock( *g_classDataMutex, false );
return (*g_classData)[ this ].faceVaryingLinearInterpolation;
}

void MeshPrimitive::setFaceVaryingLinearInterpolation( const IECore::InternedString &faceVaryingLinearInterpolation )
{
tbb::spin_rw_mutex::scoped_lock lock( *g_classDataMutex, false );
(*g_classData)[ this ].faceVaryingLinearInterpolation = faceVaryingLinearInterpolation;
}

const IECore::InternedString &MeshPrimitive::getTriangleSubdivisionRule() const
{
tbb::spin_rw_mutex::scoped_lock lock( *g_classDataMutex, false );
return (*g_classData)[ this ].triangleSubdivisionRule;
}

void MeshPrimitive::setTriangleSubdivisionRule( const IECore::InternedString &triangleSubdivisionRule )
{
tbb::spin_rw_mutex::scoped_lock lock( *g_classDataMutex, false );
(*g_classData)[ this ].triangleSubdivisionRule = triangleSubdivisionRule;
}

size_t MeshPrimitive::variableSize( PrimitiveVariable::Interpolation interpolation ) const
{
switch(interpolation)
Expand Down Expand Up @@ -388,6 +476,9 @@ void MeshPrimitive::copyFrom( const Object *other, IECore::Object::CopyContext *
m_creaseLengths = tOther->m_creaseLengths;
m_creaseIds = tOther->m_creaseIds;
m_creaseSharpnesses = tOther->m_creaseSharpnesses;
setInterpolateBoundary( tOther->getInterpolateBoundary() );
setFaceVaryingLinearInterpolation( tOther->getFaceVaryingLinearInterpolation() );
setTriangleSubdivisionRule( tOther->getTriangleSubdivisionRule() );
}

void MeshPrimitive::save( IECore::Object::SaveContext *context ) const
Expand Down Expand Up @@ -416,6 +507,10 @@ void MeshPrimitive::save( IECore::Object::SaveContext *context ) const
context->save( m_creaseIds.get(), container.get(), g_creaseIdsEntry );
context->save( m_creaseSharpnesses.get(), container.get(), g_creaseSharpnessesEntry );
}

container->write( g_interpolateBoundaryEntry, getInterpolateBoundary().string() );
container->write( g_faceVaryingLinearInterpolationEntry, getFaceVaryingLinearInterpolation().string() );
container->write( g_triangleSubdivisionRuleEntry, getTriangleSubdivisionRule().string() );
}

void MeshPrimitive::load( IECore::Object::LoadContextPtr context )
Expand Down Expand Up @@ -454,6 +549,23 @@ void MeshPrimitive::load( IECore::Object::LoadContextPtr context )
{
removeCreases();
}

std::string interpolateBoundary, faceVaryingLinearInterpolation, triangleSubdivisionRule;
if( container->hasEntry( g_interpolateBoundaryEntry ) )
{
container->read( g_interpolateBoundaryEntry, interpolateBoundary );
setInterpolateBoundary( interpolateBoundary );
}
if( container->hasEntry( g_faceVaryingLinearInterpolationEntry ) )
{
container->read( g_faceVaryingLinearInterpolationEntry, faceVaryingLinearInterpolation );
setFaceVaryingLinearInterpolation( faceVaryingLinearInterpolation );
}
if( container->hasEntry( g_triangleSubdivisionRuleEntry ) )
{
container->read( g_triangleSubdivisionRuleEntry, triangleSubdivisionRule );
setTriangleSubdivisionRule( triangleSubdivisionRule );
}
}

bool MeshPrimitive::isEqualTo( const Object *other ) const
Expand Down Expand Up @@ -501,6 +613,18 @@ bool MeshPrimitive::isEqualTo( const Object *other ) const
{
return false;
}
if( getInterpolateBoundary() != tOther->getInterpolateBoundary() )
{
return false;
}
if( getFaceVaryingLinearInterpolation() != tOther->getFaceVaryingLinearInterpolation() )
{
return false;
}
if( getTriangleSubdivisionRule() != tOther->getTriangleSubdivisionRule() )
{
return false;
}

return true;
}
Expand All @@ -526,6 +650,9 @@ void MeshPrimitive::hash( MurmurHash &h ) const
m_creaseIds->hash( h );
m_creaseSharpnesses->hash( h );
h.append( m_interpolation );
h.append( getInterpolateBoundary() );
h.append( getFaceVaryingLinearInterpolation() );
h.append( getTriangleSubdivisionRule() );
}

void MeshPrimitive::topologyHash( MurmurHash &h ) const
Expand Down Expand Up @@ -677,7 +804,7 @@ MeshPrimitivePtr MeshPrimitive::createPlane( const Box2f &b, const Imath::V2i &d
nData->setInterpretation( GeometricData::Normal );
Canceller::check( canceller );
nData->writable().resize( p.size(), V3f( 0, 0, 1 ) );

result->variables["N"] = PrimitiveVariable( PrimitiveVariable::Vertex, nData );

return result;
Expand Down
39 changes: 39 additions & 0 deletions src/IECoreScene/bindings/MeshPrimitiveBinding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,21 @@ MeshPrimitivePtr createSphereWrapper( float radius, float zMin, float zMax, floa
return MeshPrimitive::createSphere( radius, zMin, zMax, thetaMax, divisions, canceller );
}

InternedString getInterpolateBoundaryWrapper( const MeshPrimitive &p )
{
return p.getInterpolateBoundary();
}

InternedString getFaceVaryingLinearInterpolationWrapper( const MeshPrimitive &p )
{
return p.getFaceVaryingLinearInterpolation();
}

InternedString getTriangleSubdivisionRuleWrapper( const MeshPrimitive &p )
{
return p.getTriangleSubdivisionRule();
}

} // namespace

void IECoreSceneModule::bindMeshPrimitive()
Expand Down Expand Up @@ -122,6 +137,30 @@ void IECoreSceneModule::bindMeshPrimitive()
.def( "creaseIds", &creaseIds )
.def( "creaseSharpnesses", &creaseSharpnesses )
.def( "removeCreases", &MeshPrimitive::removeCreases )

// Note these are bound as functions, not properties - this is inconsistent with how interpolation is
// bound, but we hope to switch interpolation at some point, see todo above.
.def( "getInterpolateBoundary", &getInterpolateBoundaryWrapper )
.def( "setInterpolateBoundary", &MeshPrimitive::setInterpolateBoundary )
.def( "getFaceVaryingLinearInterpolation", &getFaceVaryingLinearInterpolationWrapper )
.def( "setFaceVaryingLinearInterpolation", &MeshPrimitive::setFaceVaryingLinearInterpolation )
.def( "getTriangleSubdivisionRule", &getTriangleSubdivisionRuleWrapper )
.def( "setTriangleSubdivisionRule", &MeshPrimitive::setTriangleSubdivisionRule )
.def_readonly( "interpolationLinear", MeshPrimitive::interpolationLinear )
.def_readonly( "interpolationCatmullClark", MeshPrimitive::interpolationCatmullClark )
.def_readonly( "interpolationLoop", MeshPrimitive::interpolationLoop )
.def_readonly( "interpolateBoundaryNone", MeshPrimitive::interpolateBoundaryNone )
.def_readonly( "interpolateBoundaryEdgeOnly", MeshPrimitive::interpolateBoundaryEdgeOnly )
.def_readonly( "interpolateBoundaryEdgeAndCorner", MeshPrimitive::interpolateBoundaryEdgeAndCorner )
.def_readonly( "faceVaryingLinearInterpolationNone", MeshPrimitive::faceVaryingLinearInterpolationNone )
.def_readonly( "faceVaryingLinearInterpolationCornersOnly", MeshPrimitive::faceVaryingLinearInterpolationCornersOnly )
.def_readonly( "faceVaryingLinearInterpolationCornersPlus1", MeshPrimitive::faceVaryingLinearInterpolationCornersPlus1 )
.def_readonly( "faceVaryingLinearInterpolationCornersPlus2", MeshPrimitive::faceVaryingLinearInterpolationCornersPlus2 )
.def_readonly( "faceVaryingLinearInterpolationBoundaries", MeshPrimitive::faceVaryingLinearInterpolationBoundaries )
.def_readonly( "faceVaryingLinearInterpolationAll", MeshPrimitive::faceVaryingLinearInterpolationAll )
.def_readonly( "triangleSubdivisionRuleCatmullClark", MeshPrimitive::triangleSubdivisionRuleCatmullClark )
.def_readonly( "triangleSubdivisionRuleSmooth", MeshPrimitive::triangleSubdivisionRuleSmooth )

.def( "createBox", &MeshPrimitive::createBox, ( arg_( "bounds" ) ) ).staticmethod( "createBox" )
.def( "createPlane", &createPlaneWrapper, ( arg_( "bounds" ), arg_( "divisions" ) = Imath::V2i( 1 ), arg( "canceller" ) = object() ) ).staticmethod( "createPlane" )
.def( "createSphere", &createSphereWrapper, ( arg_( "radius" ), arg_( "zMin" ) = -1.0f, arg_( "zMax" ) = 1.0f, arg_( "thetaMax" ) = 360.0f, arg_( "divisions" ) = Imath::V2i( 20, 40 ), arg( "canceller" ) = object() ) ).staticmethod( "createSphere" )
Expand Down
Loading

0 comments on commit 2206127

Please sign in to comment.