Skip to content

Commit 59b9349

Browse files
authored
Merge pull request #1371 from ImageEngine/tmpMergeRB-10.4-main
RB-10.4
2 parents 1ff35bb + 3a0ce6e commit 59b9349

21 files changed

+770
-92
lines changed

Changes

+21-1
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,29 @@ Breaking Changes
3838
- Primitive : Changed `variableIndexedView()` return type from `boost::optional` to `std::optional`.
3939
- MeshAlgo `distributePoints()` : Bug fixes mean subtle changes to the resulting points.
4040

41-
10.4.x.x (relative to 10.4.9.1)
41+
10.4.10.0 (relative to 10.4.9.1)
4242
========
4343

44+
Improvements
45+
------------
46+
47+
- IECoreNuke::LiveScene :
48+
- Adding caching to speed up querying Nuke's GeometryList for large hierarchy
49+
- Support for `ParticleSprite`
50+
- `setOp` method to avoid re creating `LiveScene` from scratch
51+
52+
Fixes
53+
-----
54+
55+
- IECoreMaya.SceneShape : Fixed reading of time samples for expanded link locations
56+
- Traverse the time plug connections to check for a maya global time connection
57+
- CheckedGILRelease : Safe to call if thread doesn't hold the GIL
58+
- IECoreNuke::SceneCacheReader : release GIL during `_validate` to avoid crash with Caribou
59+
- IECoreNuke::LiveScene :
60+
- Fix crash when there is not input `GeoOp`.
61+
- Fix uninitialized bounds
62+
- Fix bug with duplicated parents
63+
- Fix bug when querying children combined between multiple parents
4464

4565
10.4.9.1 (relative to 10.4.9.0)
4666
========

config/ie/options

-3
Original file line numberDiff line numberDiff line change
@@ -415,9 +415,6 @@ if targetApp=="rv" :
415415
# find doxygen
416416
DOXYGEN = os.path.join( "/software/apps/doxygen", os.environ["DOXYGEN_VERSION"], platform, "bin", "doxygen" )
417417

418-
# we need this so IECoreImage tests use a standard color conversion config
419-
os.environ["OCIO"] = "/software/config/openColorIO/nuke-default/config.ocio"
420-
421418
# import vars we need to get our doxygen and python wrappers working
422419
envVarsToImport = [
423420
"PATH",

include/IECoreNuke/Convert.h

+3
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,9 @@ IECORENUKE_API Imath::V3f convert( const DD::Image::Vector4 &from );
112112
template<>
113113
IECORENUKE_API Imath::Color3f convert( const DD::Image::Vector4 &from );
114114

115+
template<>
116+
IECORENUKE_API Imath::Color4f convert( const DD::Image::Vector4 &from );
117+
115118
/// Discards from.w
116119
template<>
117120
IECORENUKE_API Imath::V3d convert( const DD::Image::Vector4 &from );

include/IECoreNuke/FromNukePointsConverter.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ class IECORENUKE_API FromNukePointsConverter : public FromNukeConverter
5656

5757
/// The caller is responsible for ensuring that geo is alive
5858
/// for as long as the converter is.
59-
FromNukePointsConverter( const DD::Image::GeoInfo *geo );
59+
FromNukePointsConverter( const DD::Image::GeoInfo *geo, DD::Image::Op* op );
6060
virtual ~FromNukePointsConverter();
6161

6262
protected :
@@ -66,6 +66,7 @@ class IECORENUKE_API FromNukePointsConverter : public FromNukeConverter
6666
private :
6767

6868
const DD::Image::GeoInfo *m_geo;
69+
DD::Image::Op* m_op;
6970

7071
};
7172

include/IECoreNuke/LiveScene.h

+16-4
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class IECORENUKE_API LiveScene : public IECoreScene::SceneInterface
6060
IE_CORE_DECLARERUNTIMETYPEDEXTENSION( LiveScene, LiveSceneTypeId, IECoreScene::SceneInterface );
6161

6262
LiveScene();
63-
LiveScene( DD::Image::GeoOp *op, const std::string rootPath="/" );
63+
LiveScene( DD::Image::GeoOp *op, const IECoreScene::SceneInterface::Path& rootPath=IECoreScene::SceneInterface::rootPath );
6464

6565
~LiveScene() override;
6666

@@ -111,18 +111,30 @@ class IECORENUKE_API LiveScene : public IECoreScene::SceneInterface
111111
static double timeToFrame( const double& time );
112112
static double frameToTime( const int& frame );
113113

114+
typedef std::map<double, DD::Image::GeometryList> FrameGeometryCache;
115+
typedef std::map<DD::Image::Hash, FrameGeometryCache> OpGeometryCache;
116+
typedef std::map<const LiveScene*, OpGeometryCache> LiveSceneGeometryCache;
117+
118+
void setOp( DD::Image::GeoOp* op );
119+
const DD::Image::GeoOp *getOp() const;
120+
114121
private:
115122

116-
DD::Image::GeoOp *op() const;
117-
DD::Image::GeometryList geometryList( const double* time=nullptr ) const;
123+
DD::Image::GeometryList geometryList( const double& frame ) const;
124+
DD::Image::GeometryList geometryList( DD::Image::Op* op, const double& frame ) const;
125+
unsigned objectNum( const double* time=nullptr ) const;
126+
DD::Image::GeoInfo* object( const unsigned& index, const double* time=nullptr ) const;
118127

119128
std::string geoInfoPath( const int& index ) const;
120129

121130
DD::Image::GeoOp *m_op;
122-
std::string m_rootPath;
131+
//std::string m_rootPath;
132+
IECoreScene::SceneInterface::Path m_rootPath;
123133
IECore::PathMatcher m_pathMatcher;
124134
typedef std::map<unsigned, std::string> objectPathMap;
125135
mutable objectPathMap m_objectPathMap;
136+
137+
void cacheGeometryList( const double& frame ) const;
126138
};
127139

128140
IE_CORE_DECLAREPTR( LiveScene );

include/IECoreNuke/LiveSceneHolder.h

+1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ class IECORENUKE_API LiveSceneHolder : public DD::Image::GeoOp
5757
virtual void knobs( DD::Image::Knob_Callback f );
5858
virtual const char *Class() const;
5959
virtual const char *node_help() const;
60+
IECoreNuke::LiveScenePtr liveScene();
6061

6162
private :
6263

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
//////////////////////////////////////////////////////////////////////////
2+
//
3+
// Copyright (c) 2023, Image Engine Design Inc. All rights reserved.
4+
//
5+
// Redistribution and use in source and binary forms, with or without
6+
// modification, are permitted provided that the following conditions are
7+
// met:
8+
//
9+
// * Redistributions of source code must retain the above copyright
10+
// notice, this list of conditions and the following disclaimer.
11+
//
12+
// * Redistributions in binary form must reproduce the above copyright
13+
// notice, this list of conditions and the following disclaimer in the
14+
// documentation and/or other materials provided with the distribution.
15+
//
16+
// * Neither the name of Image Engine Design nor the names of any
17+
// other contributors to this software may be used to endorse or
18+
// promote products derived from this software without specific prior
19+
// written permission.
20+
//
21+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
22+
// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
23+
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24+
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
25+
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26+
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27+
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28+
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29+
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30+
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31+
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32+
//
33+
//////////////////////////////////////////////////////////////////////////
34+
35+
#ifndef IECOREPYTHON_CHECKEDGILRELEASE_H
36+
#define IECOREPYTHON_CHECKEDGILRELEASE_H
37+
38+
#include "boost/python.hpp"
39+
40+
#include "IECorePython/Export.h"
41+
42+
namespace IECorePython
43+
{
44+
45+
/// This class checks if we hold the GIL and if we do releases the GIL upon construction and reacquires it
46+
/// upon destruction, otherwise it's a no op. It should be used in bindings to C++ functions
47+
/// which may take a while to return - for instance heavy computations
48+
/// or IO.
49+
class IECOREPYTHON_API CheckedGILRelease : boost::noncopyable
50+
{
51+
52+
public :
53+
54+
CheckedGILRelease();
55+
~CheckedGILRelease();
56+
57+
private :
58+
59+
bool m_gilThreadsInitialised;
60+
PyThreadState *m_state;
61+
62+
};
63+
64+
} // namespace IECorePython
65+
66+
#endif // IECOREPYTHON_CHECKEDGILRELEASE_H

src/IECoreMaya/SceneShape.cpp

+30-13
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,18 @@
4545
#include "maya/MFnDagNode.h"
4646
#include "maya/MTime.h"
4747
#include "maya/MEvaluationNode.h"
48+
#include "maya/MItDependencyGraph.h"
49+
4850

4951
using namespace IECore;
5052
using namespace IECoreScene;
5153
using namespace IECoreMaya;
5254

55+
namespace
56+
{
57+
const MString g_mayaGlobalTimeNodeName( "time1" );
58+
}
59+
5360
MTypeId SceneShape::id = SceneShapeId;
5461
MObject SceneShape::aSceneFilePlug;
5562
MObject SceneShape::aSceneRootPlug;
@@ -256,7 +263,7 @@ ConstObjectPtr SceneShape::readSceneShapeLink( const MDagPath &p )
256263
SceneShape *sceneShape = findScene( p, true, &dagPath );
257264
if ( !sceneShape )
258265
{
259-
throw Exception("readSceneShapeLink: Could not find SceneShape!");
266+
throw Exception( "readSceneShapeLink: Could not find SceneShape!" );
260267
}
261268

262269
const SceneInterface *scene = sceneShape->getSceneInterface().get();
@@ -270,26 +277,36 @@ ConstObjectPtr SceneShape::readSceneShapeLink( const MDagPath &p )
270277
MPlug timePlug = fnChildDag.findPlug( aTime, false, &st );
271278
if( !st )
272279
{
273-
throw Exception( "Could not find 'time' plug in SceneShape!");
280+
throw Exception( "Could not find 'time' plug in SceneShape!" );
274281
}
275282

276-
// if time plug is connected to maya global time, then we assume there's no time remapping between the Maya scene and the loaded scene.
277-
MPlugArray array;
278-
timePlug.connectedTo( array, true, false, &st );
279-
if( !st )
283+
MItDependencyGraph it = MItDependencyGraph( timePlug, MFn::kInvalid, MItDependencyGraph::kUpstream, MItDependencyGraph::kDepthFirst, MItDependencyGraph::kPlugLevel );
284+
for ( ; !it.isDone() ; it.next() )
280285
{
281-
throw Exception( "Could not find 'time' plug connections in SceneShape!");
282-
}
286+
MPlug currPlug = it.thisPlug( &st );
287+
CHECK_MSTATUS(st);
288+
MFnDependencyNode nodeFn( currPlug.node(), &st );
289+
CHECK_MSTATUS(st);
283290

284-
for ( unsigned int i = 0; i < array.length(); i++ )
285-
{
286-
if ( array[i].name() == "time1.outTime" )
291+
if ( nodeFn.name() == g_mayaGlobalTimeNodeName )
287292
{
288-
/// connected to time, so no time remapping between maya scene and loaded scene.
293+
/// The plug is connected to maya global time, so no time remapping is happening
289294
return LinkedScene::linkAttributeData( scene );
290295
}
296+
297+
unsigned int nodeId = nodeFn.typeId().id();
298+
bool isSceneShape = nodeId == SceneShapeId || nodeId == SceneShapeProxyId;
299+
if ( !isSceneShape )
300+
{
301+
/// The time plug is not connected to global maya time or a scene shape (expanded hierarchy),
302+
/// so we assume some kind of time manipulation (a maya add node to offset the value or similiar)
303+
MTime time;
304+
timePlug.getValue( time );
305+
return LinkedScene::linkAttributeData( scene, time.as( MTime::kSeconds ) );
306+
}
291307
}
292-
/// couldn't find connection to maya time, so this node is mapping the time some other way.
308+
309+
/// The time plug doesn't have any connection at all (time hold)
293310
MTime time;
294311
timePlug.getValue( time );
295312
return LinkedScene::linkAttributeData( scene, time.as( MTime::kSeconds ) );

src/IECoreNuke/Convert.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,12 @@ Imath::Color3f convert( const DD::Image::Vector4 &from )
121121
return Imath::Color3f( from.x, from.y, from.z );
122122
}
123123

124+
template<>
125+
Imath::Color4f convert( const DD::Image::Vector4 &from )
126+
{
127+
return Imath::Color4f( from.x, from.y, from.z, from.w );
128+
}
129+
124130
template<>
125131
Imath::M44f convert( const DD::Image::Matrix4 &from )
126132
{

src/IECoreNuke/FromNukeCameraConverter.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ IECore::ObjectPtr FromNukeCameraConverter::doConversion( IECore::ConstCompoundOb
9090
// TODO - I haven't tested any of this - I'm not sure how to use it, because it doesn't appear to
9191
// be used anywhere. Why does it even exist?
9292
V2f screenWindowScale = IECore::convert<Imath::V2f>( m_camera->win_scale() );
93-
V2f screenWindowTranslate = IECore::convert<Imath::V2f>( m_camera->win_scale() );
93+
V2f screenWindowTranslate = IECore::convert<Imath::V2f>( m_camera->win_translate() );
9494
result->setAperture( V2f( m_camera->film_width(), m_camera->film_height() ) * screenWindowScale );
9595
result->setApertureOffset( screenWindowTranslate );
9696

src/IECoreNuke/FromNukePointsConverter.cpp

+81-6
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,15 @@
3838

3939
#include "IECoreScene/PointsPrimitive.h"
4040

41+
#include "DDImage/ParticleOp.h"
42+
4143
using namespace IECoreNuke;
4244
using namespace IECore;
4345
using namespace IECoreScene;
46+
using namespace DD::Image;
4447

45-
FromNukePointsConverter::FromNukePointsConverter( const DD::Image::GeoInfo *geo )
46-
: FromNukeConverter( "Converts nuke meshes to IECore meshes." ), m_geo( geo )
48+
FromNukePointsConverter::FromNukePointsConverter( const DD::Image::GeoInfo *geo, DD::Image::Op* op )
49+
: FromNukeConverter( "Converts nuke ParticleSprites to IECore PointsPrimitive." ), m_geo( geo ), m_op( op )
4750
{
4851
}
4952

@@ -66,15 +69,87 @@ IECore::ObjectPtr FromNukePointsConverter::doConversion( IECore::ConstCompoundOb
6669

6770
// get colour
6871
const DD::Image::Attribute *colorAttr = m_geo->get_typed_attribute( "Cf", DD::Image::VECTOR4_ATTRIB );
69-
if( colorAttr && colorAttr->size()==result->getNumPoints() )
72+
if( colorAttr && colorAttr->size() == result->getNumPoints() )
7073
{
71-
Color3fVectorDataPtr colorData = new Color3fVectorData();
74+
Color4fVectorDataPtr colorData = new Color4fVectorData();
7275
colorData->writable().resize( result->getNumPoints() );
73-
std::transform( colorAttr->vector4_list->begin(), colorAttr->vector4_list->end(), colorData->writable().begin(), IECore::convert<Imath::Color3f, DD::Image::Vector4> );
76+
std::transform( colorAttr->vector4_list->begin(), colorAttr->vector4_list->end(), colorData->writable().begin(), IECore::convert<Imath::Color4f, DD::Image::Vector4> );
7477
result->variables["Cs"] = PrimitiveVariable( PrimitiveVariable::Vertex, colorData );
78+
79+
// Adding a separate alpha primvar as according to my test
80+
// Cs is a Color3f in Gaffer. While we could also use 3f here, I think it is reasonable
81+
// to combine alpha inside Cs and hope it gets supported by Gaffer and then we can remove
82+
// the alpha primvar.
83+
FloatVectorDataPtr alphaData = new FloatVectorData();
84+
auto& alpha = alphaData->writable();
85+
alpha.resize( result->getNumPoints() );
86+
87+
for( size_t i=0; i < result->getNumPoints(); i++ )
88+
{
89+
alpha[i] = colorAttr->vector4( i ).w;
90+
}
91+
result->variables["alpha"] = PrimitiveVariable( PrimitiveVariable::Vertex, alphaData );
92+
}
93+
94+
// get pid
95+
const DD::Image::Attribute *idAttr = m_geo->get_typed_attribute( "id", DD::Image::INT_ATTRIB );
96+
if( idAttr && idAttr->size() == result->getNumPoints() )
97+
{
98+
IntVectorDataPtr idData = new IntVectorData();
99+
auto& id = idData->writable();
100+
id.resize( result->getNumPoints() );
101+
for( size_t i=0; i < result->getNumPoints(); i++ )
102+
{
103+
id[i] = idAttr->integer( i );
104+
}
105+
result->variables["pid"] = PrimitiveVariable( PrimitiveVariable::Vertex, idData );
106+
}
107+
108+
// get size/width
109+
const DD::Image::Attribute *sizeAttr = m_geo->get_typed_attribute( "size", DD::Image::FLOAT_ATTRIB );
110+
if( sizeAttr && sizeAttr->size() == result->getNumPoints() )
111+
{
112+
FloatVectorDataPtr widthData = new FloatVectorData();
113+
auto& width = widthData->writable();
114+
width.resize( result->getNumPoints() );
115+
116+
for( size_t i=0; i < result->getNumPoints(); i++ )
117+
{
118+
width[i] = sizeAttr->flt( i );
119+
}
120+
result->variables["width"] = PrimitiveVariable( PrimitiveVariable::Vertex, widthData );
75121
}
76122

77-
/// \todo Other primitive variables
123+
// get vel
124+
// Nuke's particle system seems to be a bit ad-hock rather than integrated in the 3D sub-system.
125+
// To get the particle velocity, we mix the API ( ParticleOp and GeoOp ). Arguably, we could switch
126+
// to use the ParticleOp API for everything but for now I decided to keep accessing what can be using the
127+
// GeoOp/GeoInfo API, hoping that Foundry will ultimately get the particle to geo fully supported.
128+
//
129+
// Another important detail here is that we are using the m_op->input0, this is based on the expectation
130+
// that the LiveScene will always be the client of this kind of converter.
131+
// In which case, the LiveScene is always internal to a GeoOp derived node (LiveSceneHolder or WriteGeo)
132+
auto particleOp = m_op->particleOp();
133+
if ( particleOp )
134+
{
135+
OutputContext oc;
136+
oc.setFrame( m_op->outputContext().frame() );
137+
particleOp->setOutputContext( oc );
138+
float prevTime, outTime;
139+
const auto particleSystem = particleOp->getParticleSystem(prevTime, outTime, true, nullptr);
140+
141+
V3fVectorDataPtr velData = new V3fVectorData();
142+
auto& vel = velData->writable();
143+
vel.resize( result->getNumPoints() );
144+
for( size_t i=0; i < result->getNumPoints(); i++ )
145+
{
146+
// velocity seems to be calculated per time step so we need to multiply by the frames per second to get velocity compatible with motion blur rendering.
147+
vel[i] = IECore::convert<Imath::V3f>( particleSystem->particleVelocity( i ) ) * DD::Image::root_real_fps();
148+
}
149+
150+
result->variables["velocity"] = PrimitiveVariable( PrimitiveVariable::Vertex, velData );
151+
}
152+
// \todo Other primitive variables
78153

79154
return result;
80155
}

0 commit comments

Comments
 (0)