From 5a1741f029057a47cea7aaec1e4e7fa69c176c28 Mon Sep 17 00:00:00 2001 From: Jae Choi Date: Mon, 25 May 2026 11:39:36 -0700 Subject: [PATCH 1/3] LiveScene: Support point-based geometry in readObject The previous check only handled eParticlesSprite with exactly one primitive, which missed point geometry from nodes like DeepToPoints that output eParticles primitives. This caused the points to be routed through MeshFromNuke, producing a broken empty mesh that didn't render in Caribou/Gaffer. --- src/IECoreNuke/LiveScene.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/IECoreNuke/LiveScene.cpp b/src/IECoreNuke/LiveScene.cpp index cd455180b4..b15c26cd31 100644 --- a/src/IECoreNuke/LiveScene.cpp +++ b/src/IECoreNuke/LiveScene.cpp @@ -498,11 +498,15 @@ ConstObjectPtr LiveScene::readObject( double time, const IECore::Canceller *canc if ( result == IECore::PathMatcher::ExactMatch ) { auto geoInfo = object( i, &time ); - if ( !geoInfo ) + if ( !geoInfo || geoInfo->primitives() == 0 ) { return IECore::NullObject::defaultNullObject(); } - if ( geoInfo->primitives() == 1 && ( geoInfo->primitive( 0 )->getPrimitiveType() == DD::Image::PrimitiveType::eParticlesSprite ) ) + + auto primitiveType = geoInfo->primitive( 0 )->getPrimitiveType(); + if ( primitiveType == DD::Image::PrimitiveType::eParticlesSprite + || primitiveType == DD::Image::PrimitiveType::eParticles + || primitiveType == DD::Image::PrimitiveType::ePoint ) { auto converter = new IECoreNuke::FromNukePointsConverter( geoInfo, m_op->input0() ); return converter->convert(); From 2291ec5b8dccaa1c5a0ee1ff9a2e0dbf19c3c661 Mon Sep 17 00:00:00 2001 From: Jae Choi Date: Mon, 25 May 2026 11:40:29 -0700 Subject: [PATCH 2/3] LiveSceneKnobTest: Add readObject tests for point types --- test/IECoreNuke/LiveSceneKnobTest.py | 67 ++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/test/IECoreNuke/LiveSceneKnobTest.py b/test/IECoreNuke/LiveSceneKnobTest.py index 581484643d..d31dbe009c 100644 --- a/test/IECoreNuke/LiveSceneKnobTest.py +++ b/test/IECoreNuke/LiveSceneKnobTest.py @@ -377,6 +377,73 @@ def testReadObjet( self ): self.assertEqual( objectA.topologyHash(), expectedObjectA.topologyHash() ) self.assertEqual( objectA.keys(), [ "P", "uv" ] ) + def testReadObjectParticles(self): + import IECoreScene + import tempfile, os + + noise = nuke.createNode("Noise") + card = nuke.createNode("Card2") + card.setInput(0, noise) + particle = nuke.createNode("ParticleEmitter") + particle.setInput(1, card) + + # Read through ieLiveScene at frame 5 (after simulation) + nuke.frame(5) + n = nuke.createNode("ieLiveScene") + n.setInput(0, particle) + n.forceValidate() + + liveScene = n.knob("scene").getValue() + self.assertGreater(len(liveScene.childNames()), 0) + + child = liveScene.scene(["object0"]) + self.assertTrue(child.hasObject()) + + obj = child.readObject(1.0) + self.assertIsInstance(obj, IECoreScene.PointsPrimitive) + + def testReadObjectDeepToPoints(self): + import IECoreScene + + checker = nuke.createNode("CheckerBoard2") + deepFromImage = nuke.createNode("DeepFromImage") + deepFromImage["z"].setValue(1000) + deepFromImage["set_z"].setValue(True) + deepFromImage.setInput(0, checker) + + camera = nuke.createNode("Camera") + + deepToPoints = nuke.createNode("DeepToPoints") + deepToPoints.setInput(0, deepFromImage) + deepToPoints.setInput(1, camera) + + n = nuke.createNode("ieLiveScene") + n.setInput(0, deepToPoints) + n.forceValidate() + + liveScene = n.knob("scene").getValue() + self.assertGreater(len(liveScene.childNames()), 0) + + child = liveScene.scene(["object0"]) + self.assertTrue(child.hasObject()) + + obj = child.readObject(0) + self.assertIsInstance(obj, IECoreScene.PointsPrimitive) + + def testReadObjectMesh(self): + import IECoreScene + + sphere = nuke.createNode("Sphere") + + n = nuke.createNode("ieLiveScene") + n.setInput(0, sphere) + + liveScene = n.knob("scene").getValue() + child = liveScene.scene(["object0"]) + self.assertTrue(child.hasObject()) + + obj = child.readObject(0) + self.assertIsInstance(obj, IECoreScene.MeshPrimitive) if __name__ == "__main__": unittest.main() From 02e4601777232e74a2eaa4851db4d0f793e5e3ab Mon Sep 17 00:00:00 2001 From: Jae Choi Date: Mon, 25 May 2026 14:08:16 -0700 Subject: [PATCH 3/3] SConstruct: Added missing distutils import --- SConstruct | 1 + 1 file changed, 1 insertion(+) diff --git a/SConstruct b/SConstruct index a5a0786324..2d7093e87b 100644 --- a/SConstruct +++ b/SConstruct @@ -47,6 +47,7 @@ import os import re import subprocess import platform +import distutils EnsureSConsVersion( 3, 0, 2 ) # Substfile is a default builder as of 3.0.2 SConsignFile()