Groups keyboard shortcuts have been updated
Dismiss
See shortcuts

Curves render different between USD and Alembic

96 views
Skip to first unread message

Nico Rehberg

unread,
Dec 17, 2024, 6:26:21 PM12/17/24
to gaffer-dev
Hi,

I'm struggling to render curves coming from an alembic correctly. In openGL they look fine, but when rendering in Arnold it seems like the first and last segment is missing.
Using an USD works though.

Top is USD, bottom ABC.

ksnip_20241218-001550.jpg
ksnip_20241218-001552.jpg

What could be the reason? I tried deleting, recreating primitive attributes. Even changed the curve type from BSpline (which the Scenereader assigns the abc curve) to catmull-rom (which the USD curves have) by channeling the ABC through an USD stage. But no luck.
When I exporting the scene into an .ass the curves look identical ... haven't tried kick on that .ass though. Will do that in the morning.
I'll attach the sample. Any help or pointers would be nice.

Thanks,
Nico
curvesAbcvsUSD.gfr
curves.abc
curves.usda

Nicholas Yue

unread,
Dec 17, 2024, 6:37:44 PM12/17/24
to gaffe...@googlegroups.com
I came across something similar in a different context (not Arnold) but thought I might share.

For that situation I was in, it was a case of knots multiplicity difference/handling between the broken application and OpenGL

HTH

--
You received this message because you are subscribed to the Google Groups "gaffer-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to gaffer-dev+...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/gaffer-dev/99e584a2-455e-4fae-b606-304a6bfc37bfn%40googlegroups.com.


--

Daniel Dresser

unread,
Dec 19, 2024, 6:25:15 PM12/19/24
to gaffer-dev
If the interpolation is supposed to catmullRom, but is coming through as bSpline, that is certainly sufficient to explain the difference. Everything else seems to be working properly ( bSpline is an interpolation where the curve doesn't reach the final control point unless you double it up, and the control points wouldn't be doubled up if they're intended for use with catmullRom ).

Unfortunately, I'm getting an error trying to open your "curves.abc" ... I haven't been able to verify where the incorrect interpolation is coming from ( it could be a bug in our loader, or the alembic writer could be writing the wrong the interpolation ).

As for why it didn't work to change the curve type,  I don't know much about USD layering, but maybe it doesn't work to layer a USD curve type over an ABC source?

I would recommend just overriding the curve type in Gaffer, but unfortunately we don't yet ship a node for doing that in the standard release. If you're able to install it, there is a CurvesType node available here: https://github.com/Mini-Engine/Mini-Tech

-Daniel

Nico Rehberg

unread,
Dec 20, 2024, 9:32:20 AM12/20/24
to gaffer-dev
Hi,

thanks for the input, it helps me in understanding this issue.
It seems indeed that there is a difference in how Arnold and Maya export curves. Because even when going through MayaUSD I get the same issues as in Alembic.

Arnold-USD doubles first and last control point and defines BasisCurves

            def BasisCurves "whiskers" (   )
            {
                uniform token basis = "catmullRom"
                int[] curveVertexCounts = [5]
                point3f[] points = [(0.9590006, 19.363008, 21.704794), (0.9590006, 19.363008, 21.704794), (2.8932302, 17.848091, 21.600443), (4.000618, 16.37891, 21.600443), (4.000618, 16.37891, 21.600443)]
                token primvars:arnold:basis = "catmull-rom"
                uniform token type = "cubic"
..

While MayaUSD (and maybe alembic) exports this as NurbsCurves, without doubling first and last point:

    def NurbsCurves "whiskers"
    {
        int[] curveVertexCounts = [5]
        float3[] extent = [(0.4590006, 15.87891, 21.100443), (4.500618, 19.863008, 22.204794)]
        double[] knots = [0, 0, 0, 0, 1, 2, 2, 2, 2]
        int[] order = [4]
        point3f[] points = [(0.9590006, 19.363008, 21.704794), (2.0239558, 18.71259, 21.600443), (2.9791386, 17.85487, 21.600443), (3.590688, 16.970034, 21.600443), (4.000618, 16.37891, 21.600443)]
        double2[] ranges = [(0, 2)]
...
   

The ArnoldUSD then reads as CurvesPrimitive - CatmullRom in Gaffer, the MayaUSD is read as CurvesPrimitive -BSpline.

When both are exported to Arnold the ArnoldUSD curve is handed on like this:
 
curves
{
..
 num_points 5
 points 5 1 b85VECTOR
..
 basis "catmull-rom"
..
}

while the MayaUSD curve end up as
curves
{
..
 num_points 5
 points 5 1 b85VECTOR
..
 basis "b-spline"
..
}

Now I'm not sure what to do. I tried setting the basis in the .ass manually to catmull-rom, but the result was the same. I have a feeling that Arnold totally needs first and last point doubled and the Arnold-USD exporter takes that into account. I poked a bit at how the USD procedural translates curves

Exporting the animation caches from Maya with ArnoldUSD also opens up a can of worms since it seems to miss some essential features the MayaUSD (and Alembic) exporter have .. like removing namespaces and defining a custom root.
I tried without much success to duplicate first and last point coordinate in Gaffer, although that will probably mess up the width or texture placement assignment.

I also found that adding wrap_mode "pinned" to the curve primitive in the .ass seems to give me the correct result. But that may be a red herring. And I don't know how to add arbitrary attributes to arnold nodes in Gaffer. Is there a way? CustomAttributes somehow don't end up in the .ass.

I'll attach the 2 usds. One exported from the arnold plugin in Maya the other the MayaUSD.

Thanks,
Nico
curves_mayaUSD.usda
curves_ArnoldUSD.usda

Daniel Dresser

unread,
Dec 20, 2024, 1:54:02 PM12/20/24
to gaffer-dev
Oh, I'd forgotten about good old fashioned Nurbs curves, that explains things.

Nurbs are a very powerful curve type, where you can do things like creating mathematically perfect circles with minimal control points by editing the knot vector. But most artists these days would rather just move control points around rather than editing the knot vector as well, so Catmull-Rom is a much more popular representation.

We don't currently support Nurbs in Gaffer. Arnold doesn't support Nurbs either, which is why the Arnold-USD export converts the curves to CatmullRom ( a curve type supported by both Arnold and Gaffer ).

Long run, the ideal behaviour would likely be that we should convert nurbs to catmullRom when loading a USD. Though this is actually a bit complicated and potentially lossy, so it's not trivial to add.

Do you need the exact curve shape preserved? Or would you be OK with just taking the control points and reinterpreting them as catmullRom and doubling up the endpoints so that the interpolated curve hits the endpoints? ( Note that Arnold is doing something much more complex than this, it recomputes the positions of the all the control points in order to approximate the shape of the Nurbs as a catmullRom ). I'm trying to think if there's a simple hack here that would get things functional for you.

The ideal would be exporting catmullRom curves from Maya, but sounds like Maya can't do that without going through Arnold. It sounds like there is a command in Maya to convert Nurbs curves to Bezier - Bezier curves might come through OK, but Bezier isn't a very good representation for a hair system if you need to do any further processing.

-Daniel

Nico Rehberg

unread,
Dec 20, 2024, 4:40:52 PM12/20/24
to gaffer-dev
Hi Daniel,

a rough approximation is good enough I think. I guess doubling the endpoints would be fine, but so far I failed to do that. What I got by manually setting the wrap_mode to pinned in the .ass looked fine too. These curves are used for some individual rigged hairs on characters, like whiskers and "eyebrow whiskers" on a cat. It seemed straightforward to use the curves directly for rendering at the time we set the characters up, but if this just doesn't work I'll have them go through Yeti with the rest of the fur.

I tried converting the curves to bezier in Maya. And when I use MayaUSD they transfer better to Gaffer, as BasisCurves with a bezier basis in the USD. But now Arnold in Maya does not like them anymore. It neither renders them nor translates them to USD, thus preventing me from using them in lookdev.

Nico

dan...@image-engine.com

unread,
Dec 20, 2024, 6:21:00 PM12/20/24
to gaffer-dev
If you're happy with just duplicating the endpoints, this is theoretically an easy fix, but I'm not quite sure where it should live.

For the moment, I've cut-and-pasted a Gaffer box that you can filter to these curves to do what you want - it's implemented in an extremely hacky way using Python, and I would definitely not recommend running it on a full density production groom, but it should be fine on some whiskers. We might able to come up with some way of releasing a proper version of this, but it won't happen until after the Christmas break. ( Actually, I've just realized that the easiest way to do this would be to just do this by default when we get given Nurbs curves in the USD loader ... treating them as catmullRom with duped endpoints isn't correct ... but treating them as bspline isn't correct either, and this is more likely to be useful to someone. But changing the default behaviour is theoretically a compatibility break, so we probably couldn't release it right away anyway. )

For the moment, here's a hack to play with ( copy-and-paste ):

import Gaffer
import GafferScene
import IECore
import imath

Gaffer.Metadata.registerValue( parent, "serialiser:milestoneVersion", 1, persistent=False )
Gaffer.Metadata.registerValue( parent, "serialiser:majorVersion", 5, persistent=False )
Gaffer.Metadata.registerValue( parent, "serialiser:minorVersion", 1, persistent=False )
Gaffer.Metadata.registerValue( parent, "serialiser:patchVersion", 0, persistent=False )

__children = {}

__children["VeryHackilyFixBadNurbs"] = Gaffer.Box( "VeryHackilyFixBadNurbs" )
parent.addChild( __children["VeryHackilyFixBadNurbs"] )
__children["VeryHackilyFixBadNurbs"].addChild( GafferScene.ScenePlug( "in", flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["VeryHackilyFixBadNurbs"].addChild( GafferScene.MergeScenes( "MergeScenes" ) )
__children["VeryHackilyFixBadNurbs"]["MergeScenes"]["in"].resize( 3 )
__children["VeryHackilyFixBadNurbs"]["MergeScenes"].addChild( Gaffer.V2fPlug( "__uiPosition", defaultValue = imath.V2f( 0, 0 ), flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["VeryHackilyFixBadNurbs"].addChild( GafferScene.ScenePlug( "out", direction = Gaffer.Plug.Direction.Out, flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["VeryHackilyFixBadNurbs"].addChild( Gaffer.BoxIn( "BoxIn" ) )
__children["VeryHackilyFixBadNurbs"]["BoxIn"].setup( GafferScene.ScenePlug( "out", ) )
__children["VeryHackilyFixBadNurbs"]["BoxIn"].addChild( Gaffer.V2fPlug( "__uiPosition", defaultValue = imath.V2f( 0, 0 ), flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["VeryHackilyFixBadNurbs"].addChild( Gaffer.BoxOut( "BoxOut" ) )
__children["VeryHackilyFixBadNurbs"]["BoxOut"].setup( GafferScene.ScenePlug( "in", ) )
__children["VeryHackilyFixBadNurbs"]["BoxOut"].addChild( Gaffer.V2fPlug( "__uiPosition", defaultValue = imath.V2f( 0, 0 ), flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["VeryHackilyFixBadNurbs"].addChild( Gaffer.V2fPlug( "__uiPosition", defaultValue = imath.V2f( 0, 0 ), flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["VeryHackilyFixBadNurbs"].addChild( Gaffer.Expression( "Expression" ) )
__children["VeryHackilyFixBadNurbs"]["Expression"]["__in"].addChild( Gaffer.ObjectPlug( "p0", defaultValue = IECore.NullObject(), flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["VeryHackilyFixBadNurbs"]["Expression"]["__out"].addChild( Gaffer.ObjectPlug( "p0", direction = Gaffer.Plug.Direction.Out, defaultValue = IECore.NullObject(), flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["VeryHackilyFixBadNurbs"]["Expression"].addChild( Gaffer.V2fPlug( "__uiPosition", defaultValue = imath.V2f( 0, 0 ), flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["VeryHackilyFixBadNurbs"].addChild( GafferScene.Isolate( "Isolate" ) )
__children["VeryHackilyFixBadNurbs"]["Isolate"].addChild( Gaffer.V2fPlug( "__uiPosition", defaultValue = imath.V2f( 0, 0 ), flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["VeryHackilyFixBadNurbs"].addChild( Gaffer.BoxIn( "BoxIn1" ) )
__children["VeryHackilyFixBadNurbs"]["BoxIn1"].setup( GafferScene.FilterPlug( "out", defaultValue = 7, minValue = 0, maxValue = 7, ) )
__children["VeryHackilyFixBadNurbs"]["BoxIn1"].addChild( Gaffer.V2fPlug( "__uiPosition", defaultValue = imath.V2f( 0, 0 ), flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["VeryHackilyFixBadNurbs"].addChild( GafferScene.FilterPlug( "filter", defaultValue = 7, minValue = 0, maxValue = 7, flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["VeryHackilyFixBadNurbs"].addChild( Gaffer.BoolPlug( "enabled", defaultValue = True, flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
Gaffer.Metadata.registerValue( __children["VeryHackilyFixBadNurbs"]["in"], 'plugValueWidget:type', '' )
__children["VeryHackilyFixBadNurbs"]["MergeScenes"]["in"][0].setInput( __children["VeryHackilyFixBadNurbs"]["BoxIn"]["out"] )
__children["VeryHackilyFixBadNurbs"]["MergeScenes"]["in"][1].setInput( __children["VeryHackilyFixBadNurbs"]["Isolate"]["out"] )
__children["VeryHackilyFixBadNurbs"]["MergeScenes"]["objectMode"].setValue( 1 )
__children["VeryHackilyFixBadNurbs"]["MergeScenes"]["__uiPosition"].setValue( imath.V2f( 451.980682, -132.734924 ) )
__children["VeryHackilyFixBadNurbs"]["out"].setInput( __children["VeryHackilyFixBadNurbs"]["BoxOut"]["__out"] )
Gaffer.Metadata.registerValue( __children["VeryHackilyFixBadNurbs"]["out"], 'description', 'The processed output scene.' )
Gaffer.Metadata.registerValue( __children["VeryHackilyFixBadNurbs"]["out"], 'nodule:type', 'GafferUI::StandardNodule' )
__children["VeryHackilyFixBadNurbs"]["BoxIn"]["__in"].setInput( __children["VeryHackilyFixBadNurbs"]["in"] )
Gaffer.Metadata.registerValue( __children["VeryHackilyFixBadNurbs"]["BoxIn"]["__in"], 'plugValueWidget:type', '' )
__children["VeryHackilyFixBadNurbs"]["BoxIn"]["__uiPosition"].setValue( imath.V2f( 448.980682, -108.49192 ) )
__children["VeryHackilyFixBadNurbs"]["BoxOut"]["in"].setInput( __children["VeryHackilyFixBadNurbs"]["MergeScenes"]["out"] )
Gaffer.Metadata.registerValue( __children["VeryHackilyFixBadNurbs"]["BoxOut"]["__out"], 'description', 'The processed output scene.' )
Gaffer.Metadata.registerValue( __children["VeryHackilyFixBadNurbs"]["BoxOut"]["__out"], 'nodule:type', 'GafferUI::StandardNodule' )
__children["VeryHackilyFixBadNurbs"]["BoxOut"]["passThrough"].setInput( __children["VeryHackilyFixBadNurbs"]["BoxIn"]["out"] )
__children["VeryHackilyFixBadNurbs"]["BoxOut"]["enabled"].setInput( __children["VeryHackilyFixBadNurbs"]["enabled"] )
__children["VeryHackilyFixBadNurbs"]["BoxOut"]["__uiPosition"].setValue( imath.V2f( 453.479828, -141.066956 ) )
__children["VeryHackilyFixBadNurbs"]["__uiPosition"].setValue( imath.V2f( 450.247162, -129.708435 ) )
__children["VeryHackilyFixBadNurbs"]["Expression"]["__in"]["p0"].setInput( __children["VeryHackilyFixBadNurbs"]["BoxIn"]["out"]["object"] )
__children["VeryHackilyFixBadNurbs"]["Expression"]["__uiPosition"].setValue( imath.V2f( 463.193054, -120.06765 ) )
__children["VeryHackilyFixBadNurbs"]["Isolate"]["in"]["bound"].setInput( __children["VeryHackilyFixBadNurbs"]["BoxIn"]["out"]["bound"] )
__children["VeryHackilyFixBadNurbs"]["Isolate"]["in"]["transform"].setInput( __children["VeryHackilyFixBadNurbs"]["BoxIn"]["out"]["transform"] )
__children["VeryHackilyFixBadNurbs"]["Isolate"]["in"]["attributes"].setInput( __children["VeryHackilyFixBadNurbs"]["BoxIn"]["out"]["attributes"] )
__children["VeryHackilyFixBadNurbs"]["Isolate"]["in"]["object"].setInput( __children["VeryHackilyFixBadNurbs"]["Expression"]["__out"]["p0"] )
__children["VeryHackilyFixBadNurbs"]["Isolate"]["in"]["childNames"].setInput( __children["VeryHackilyFixBadNurbs"]["BoxIn"]["out"]["childNames"] )
__children["VeryHackilyFixBadNurbs"]["Isolate"]["in"]["globals"].setInput( __children["VeryHackilyFixBadNurbs"]["BoxIn"]["out"]["globals"] )
__children["VeryHackilyFixBadNurbs"]["Isolate"]["in"]["setNames"].setInput( __children["VeryHackilyFixBadNurbs"]["BoxIn"]["out"]["setNames"] )
__children["VeryHackilyFixBadNurbs"]["Isolate"]["in"]["set"].setInput( __children["VeryHackilyFixBadNurbs"]["BoxIn"]["out"]["set"] )
__children["VeryHackilyFixBadNurbs"]["Isolate"]["in"]["exists"].setInput( __children["VeryHackilyFixBadNurbs"]["BoxIn"]["out"]["exists"] )
__children["VeryHackilyFixBadNurbs"]["Isolate"]["in"]["childBounds"].setInput( __children["VeryHackilyFixBadNurbs"]["BoxIn"]["out"]["childBounds"] )
__children["VeryHackilyFixBadNurbs"]["Isolate"]["in"]["__sortedChildNames"].setInput( __children["VeryHackilyFixBadNurbs"]["BoxIn"]["out"]["__sortedChildNames"] )
__children["VeryHackilyFixBadNurbs"]["Isolate"]["filter"].setInput( __children["VeryHackilyFixBadNurbs"]["BoxIn1"]["out"] )
__children["VeryHackilyFixBadNurbs"]["Isolate"]["__uiPosition"].setValue( imath.V2f( 460.611237, -124.570862 ) )
__children["VeryHackilyFixBadNurbs"]["BoxIn1"]["name"].setValue( 'filter' )
__children["VeryHackilyFixBadNurbs"]["BoxIn1"]["__in"].setInput( __children["VeryHackilyFixBadNurbs"]["filter"] )
Gaffer.Metadata.registerValue( __children["VeryHackilyFixBadNurbs"]["BoxIn1"]["__in"], 'nodule:type', 'GafferUI::StandardNodule' )
Gaffer.Metadata.registerValue( __children["VeryHackilyFixBadNurbs"]["BoxIn1"]["__in"], 'description', 'The filter used to control which parts of the scene are\nprocessed. A Filter node should be connected here.' )
Gaffer.Metadata.registerValue( __children["VeryHackilyFixBadNurbs"]["BoxIn1"]["__in"], 'noduleLayout:section', 'right' )
Gaffer.Metadata.registerValue( __children["VeryHackilyFixBadNurbs"]["BoxIn1"]["__in"], 'plugValueWidget:type', 'GafferSceneUI.FilterPlugValueWidget' )
Gaffer.Metadata.registerValue( __children["VeryHackilyFixBadNurbs"]["BoxIn1"]["out"], 'noduleLayout:section', 'left' )
__children["VeryHackilyFixBadNurbs"]["BoxIn1"]["__uiPosition"].setValue( imath.V2f( 473.924438, -124.571579 ) )
Gaffer.Metadata.registerValue( __children["VeryHackilyFixBadNurbs"]["filter"], 'nodule:type', 'GafferUI::StandardNodule' )
Gaffer.Metadata.registerValue( __children["VeryHackilyFixBadNurbs"]["filter"], 'description', 'The filter used to control which parts of the scene are\nprocessed. A Filter node should be connected here.' )
Gaffer.Metadata.registerValue( __children["VeryHackilyFixBadNurbs"]["filter"], 'noduleLayout:section', 'right' )
Gaffer.Metadata.registerValue( __children["VeryHackilyFixBadNurbs"]["filter"], 'plugValueWidget:type', 'GafferSceneUI.FilterPlugValueWidget' )
__children["VeryHackilyFixBadNurbs"]["Expression"]["__engine"].setValue( 'python' )
__children["VeryHackilyFixBadNurbs"]["Expression"]["__expression"].setValue( 'import IECoreScene\n\ncurves = parent["__in"]["p0"]\nif type( curves ) == IECoreScene.CurvesPrimitive:\n\tcurves.setTopology( curves.verticesPerCurve(), IECore.CubicBasisf.linear(), curves.periodic() )\n\tcurves = IECoreScene.CurvesAlgo.updateEndpointMultiplicity( curves, IECore.CubicBasisf.catmullRom() )\nparent["__out"]["p0"] = curves' )


del __children

dan...@image-engine.com

unread,
Dec 20, 2024, 6:35:24 PM12/20/24
to gaffer-dev
Actually, one last thought on this: using a bSpline basis, but with duplicated end points, probably comes closer to matching the shape of the Nurbs. Here's a version of the hacky network that lets you pick the basis:


import Gaffer
import GafferScene
import IECore
import imath

Gaffer.Metadata.registerValue( parent, "serialiser:milestoneVersion", 1, persistent=False )
Gaffer.Metadata.registerValue( parent, "serialiser:majorVersion", 5, persistent=False )
Gaffer.Metadata.registerValue( parent, "serialiser:minorVersion", 1, persistent=False )
Gaffer.Metadata.registerValue( parent, "serialiser:patchVersion", 0, persistent=False )

__children = {}

__children["HackilyFixBadNurbs"] = Gaffer.Box( "HackilyFixBadNurbs" )
parent.addChild( __children["HackilyFixBadNurbs"] )
__children["HackilyFixBadNurbs"].addChild( GafferScene.ScenePlug( "in", flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["HackilyFixBadNurbs"].addChild( GafferScene.MergeScenes( "MergeScenes" ) )
__children["HackilyFixBadNurbs"]["MergeScenes"]["in"].resize( 3 )
__children["HackilyFixBadNurbs"]["MergeScenes"].addChild( Gaffer.V2fPlug( "__uiPosition", defaultValue = imath.V2f( 0, 0 ), flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["HackilyFixBadNurbs"].addChild( GafferScene.ScenePlug( "out", direction = Gaffer.Plug.Direction.Out, flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["HackilyFixBadNurbs"].addChild( Gaffer.BoxIn( "BoxIn" ) )
__children["HackilyFixBadNurbs"]["BoxIn"].setup( GafferScene.ScenePlug( "out", ) )
__children["HackilyFixBadNurbs"]["BoxIn"].addChild( Gaffer.V2fPlug( "__uiPosition", defaultValue = imath.V2f( 0, 0 ), flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["HackilyFixBadNurbs"].addChild( Gaffer.BoxOut( "BoxOut" ) )
__children["HackilyFixBadNurbs"]["BoxOut"].setup( GafferScene.ScenePlug( "in", ) )
__children["HackilyFixBadNurbs"]["BoxOut"].addChild( Gaffer.V2fPlug( "__uiPosition", defaultValue = imath.V2f( 0, 0 ), flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["HackilyFixBadNurbs"].addChild( Gaffer.V2fPlug( "__uiPosition", defaultValue = imath.V2f( 0, 0 ), flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["HackilyFixBadNurbs"].addChild( Gaffer.Expression( "Expression" ) )
__children["HackilyFixBadNurbs"]["Expression"]["__in"].addChild( Gaffer.ObjectPlug( "p0", defaultValue = IECore.NullObject(), flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["HackilyFixBadNurbs"]["Expression"]["__in"].addChild( Gaffer.StringPlug( "p1", defaultValue = '', flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["HackilyFixBadNurbs"]["Expression"]["__out"].addChild( Gaffer.ObjectPlug( "p0", direction = Gaffer.Plug.Direction.Out, defaultValue = IECore.NullObject(), flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["HackilyFixBadNurbs"]["Expression"].addChild( Gaffer.V2fPlug( "__uiPosition", defaultValue = imath.V2f( 0, 0 ), flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["HackilyFixBadNurbs"].addChild( GafferScene.Isolate( "Isolate" ) )
__children["HackilyFixBadNurbs"]["Isolate"].addChild( Gaffer.V2fPlug( "__uiPosition", defaultValue = imath.V2f( 0, 0 ), flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["HackilyFixBadNurbs"].addChild( Gaffer.BoxIn( "BoxIn1" ) )
__children["HackilyFixBadNurbs"]["BoxIn1"].setup( GafferScene.FilterPlug( "out", defaultValue = 7, minValue = 0, maxValue = 7, ) )
__children["HackilyFixBadNurbs"]["BoxIn1"].addChild( Gaffer.V2fPlug( "__uiPosition", defaultValue = imath.V2f( 0, 0 ), flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["HackilyFixBadNurbs"].addChild( GafferScene.FilterPlug( "filter", defaultValue = 7, minValue = 0, maxValue = 7, flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["HackilyFixBadNurbs"].addChild( Gaffer.BoolPlug( "enabled", defaultValue = True, flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["HackilyFixBadNurbs"].addChild( Gaffer.StringPlug( "basis", defaultValue = '', flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
Gaffer.Metadata.registerValue( __children["HackilyFixBadNurbs"], 'uiEditor:emptySections', IECore.StringVectorData( [  ] ) )
Gaffer.Metadata.registerValue( __children["HackilyFixBadNurbs"], 'uiEditor:emptySectionIndices', IECore.IntVectorData( [  ] ) )
Gaffer.Metadata.registerValue( __children["HackilyFixBadNurbs"]["in"], 'plugValueWidget:type', '' )
Gaffer.Metadata.registerValue( __children["HackilyFixBadNurbs"]["in"], 'layout:section', 'Settings' )
Gaffer.Metadata.registerValue( __children["HackilyFixBadNurbs"]["in"], 'layout:index', 1 )
__children["HackilyFixBadNurbs"]["MergeScenes"]["in"][0].setInput( __children["HackilyFixBadNurbs"]["BoxIn"]["out"] )
__children["HackilyFixBadNurbs"]["MergeScenes"]["in"][1].setInput( __children["HackilyFixBadNurbs"]["Isolate"]["out"] )
__children["HackilyFixBadNurbs"]["MergeScenes"]["objectMode"].setValue( 1 )
__children["HackilyFixBadNurbs"]["MergeScenes"]["__uiPosition"].setValue( imath.V2f( 451.980682, -132.734924 ) )
__children["HackilyFixBadNurbs"]["out"].setInput( __children["HackilyFixBadNurbs"]["BoxOut"]["__out"] )
Gaffer.Metadata.registerValue( __children["HackilyFixBadNurbs"]["out"], 'description', 'The processed output scene.' )
Gaffer.Metadata.registerValue( __children["HackilyFixBadNurbs"]["out"], 'nodule:type', 'GafferUI::StandardNodule' )
Gaffer.Metadata.registerValue( __children["HackilyFixBadNurbs"]["out"], 'layout:section', 'Settings' )
Gaffer.Metadata.registerValue( __children["HackilyFixBadNurbs"]["out"], 'layout:index', 2 )
__children["HackilyFixBadNurbs"]["BoxIn"]["__in"].setInput( __children["HackilyFixBadNurbs"]["in"] )
Gaffer.Metadata.registerValue( __children["HackilyFixBadNurbs"]["BoxIn"]["__in"], 'plugValueWidget:type', '' )
__children["HackilyFixBadNurbs"]["BoxIn"]["__uiPosition"].setValue( imath.V2f( 448.980682, -108.49192 ) )
__children["HackilyFixBadNurbs"]["BoxOut"]["in"].setInput( __children["HackilyFixBadNurbs"]["MergeScenes"]["out"] )
Gaffer.Metadata.registerValue( __children["HackilyFixBadNurbs"]["BoxOut"]["__out"], 'description', 'The processed output scene.' )
Gaffer.Metadata.registerValue( __children["HackilyFixBadNurbs"]["BoxOut"]["__out"], 'nodule:type', 'GafferUI::StandardNodule' )
__children["HackilyFixBadNurbs"]["BoxOut"]["passThrough"].setInput( __children["HackilyFixBadNurbs"]["BoxIn"]["out"] )
__children["HackilyFixBadNurbs"]["BoxOut"]["enabled"].setInput( __children["HackilyFixBadNurbs"]["enabled"] )
__children["HackilyFixBadNurbs"]["BoxOut"]["__uiPosition"].setValue( imath.V2f( 453.479828, -141.066956 ) )
__children["HackilyFixBadNurbs"]["__uiPosition"].setValue( imath.V2f( 451.345306, -129.621979 ) )
__children["HackilyFixBadNurbs"]["Expression"]["__in"]["p0"].setInput( __children["HackilyFixBadNurbs"]["BoxIn"]["out"]["object"] )
__children["HackilyFixBadNurbs"]["Expression"]["__in"]["p1"].setInput( __children["HackilyFixBadNurbs"]["basis"] )
__children["HackilyFixBadNurbs"]["Expression"]["__uiPosition"].setValue( imath.V2f( 463.193054, -120.06765 ) )
__children["HackilyFixBadNurbs"]["Isolate"]["in"]["bound"].setInput( __children["HackilyFixBadNurbs"]["BoxIn"]["out"]["bound"] )
__children["HackilyFixBadNurbs"]["Isolate"]["in"]["transform"].setInput( __children["HackilyFixBadNurbs"]["BoxIn"]["out"]["transform"] )
__children["HackilyFixBadNurbs"]["Isolate"]["in"]["attributes"].setInput( __children["HackilyFixBadNurbs"]["BoxIn"]["out"]["attributes"] )
__children["HackilyFixBadNurbs"]["Isolate"]["in"]["object"].setInput( __children["HackilyFixBadNurbs"]["Expression"]["__out"]["p0"] )
__children["HackilyFixBadNurbs"]["Isolate"]["in"]["childNames"].setInput( __children["HackilyFixBadNurbs"]["BoxIn"]["out"]["childNames"] )
__children["HackilyFixBadNurbs"]["Isolate"]["in"]["globals"].setInput( __children["HackilyFixBadNurbs"]["BoxIn"]["out"]["globals"] )
__children["HackilyFixBadNurbs"]["Isolate"]["in"]["setNames"].setInput( __children["HackilyFixBadNurbs"]["BoxIn"]["out"]["setNames"] )
__children["HackilyFixBadNurbs"]["Isolate"]["in"]["set"].setInput( __children["HackilyFixBadNurbs"]["BoxIn"]["out"]["set"] )
__children["HackilyFixBadNurbs"]["Isolate"]["in"]["exists"].setInput( __children["HackilyFixBadNurbs"]["BoxIn"]["out"]["exists"] )
__children["HackilyFixBadNurbs"]["Isolate"]["in"]["childBounds"].setInput( __children["HackilyFixBadNurbs"]["BoxIn"]["out"]["childBounds"] )
__children["HackilyFixBadNurbs"]["Isolate"]["in"]["__sortedChildNames"].setInput( __children["HackilyFixBadNurbs"]["BoxIn"]["out"]["__sortedChildNames"] )
__children["HackilyFixBadNurbs"]["Isolate"]["filter"].setInput( __children["HackilyFixBadNurbs"]["BoxIn1"]["out"] )
__children["HackilyFixBadNurbs"]["Isolate"]["__uiPosition"].setValue( imath.V2f( 460.611237, -124.570862 ) )
__children["HackilyFixBadNurbs"]["BoxIn1"]["name"].setValue( 'filter' )
__children["HackilyFixBadNurbs"]["BoxIn1"]["__in"].setInput( __children["HackilyFixBadNurbs"]["filter"] )
Gaffer.Metadata.registerValue( __children["HackilyFixBadNurbs"]["BoxIn1"]["__in"], 'nodule:type', 'GafferUI::StandardNodule' )
Gaffer.Metadata.registerValue( __children["HackilyFixBadNurbs"]["BoxIn1"]["__in"], 'description', 'The filter used to control which parts of the scene are\nprocessed. A Filter node should be connected here.' )
Gaffer.Metadata.registerValue( __children["HackilyFixBadNurbs"]["BoxIn1"]["__in"], 'noduleLayout:section', 'right' )
Gaffer.Metadata.registerValue( __children["HackilyFixBadNurbs"]["BoxIn1"]["__in"], 'plugValueWidget:type', 'GafferSceneUI.FilterPlugValueWidget' )
Gaffer.Metadata.registerValue( __children["HackilyFixBadNurbs"]["BoxIn1"]["out"], 'noduleLayout:section', 'left' )
__children["HackilyFixBadNurbs"]["BoxIn1"]["__uiPosition"].setValue( imath.V2f( 473.924438, -124.571579 ) )
Gaffer.Metadata.registerValue( __children["HackilyFixBadNurbs"]["filter"], 'nodule:type', 'GafferUI::StandardNodule' )
Gaffer.Metadata.registerValue( __children["HackilyFixBadNurbs"]["filter"], 'description', 'The filter used to control which parts of the scene are\nprocessed. A Filter node should be connected here.' )
Gaffer.Metadata.registerValue( __children["HackilyFixBadNurbs"]["filter"], 'noduleLayout:section', 'right' )
Gaffer.Metadata.registerValue( __children["HackilyFixBadNurbs"]["filter"], 'plugValueWidget:type', 'GafferSceneUI.FilterPlugValueWidget' )
Gaffer.Metadata.registerValue( __children["HackilyFixBadNurbs"]["filter"], 'layout:section', 'Settings' )
Gaffer.Metadata.registerValue( __children["HackilyFixBadNurbs"]["filter"], 'layout:index', 3 )
Gaffer.Metadata.registerValue( __children["HackilyFixBadNurbs"]["enabled"], 'layout:section', 'Node' )
Gaffer.Metadata.registerValue( __children["HackilyFixBadNurbs"]["enabled"], 'layout:index', 4 )
__children["HackilyFixBadNurbs"]["basis"].setValue( 'bSpline' )
Gaffer.Metadata.registerValue( __children["HackilyFixBadNurbs"]["basis"], 'nodule:type', '' )
Gaffer.Metadata.registerValue( __children["HackilyFixBadNurbs"]["basis"], 'plugValueWidget:type', 'GafferUI.PresetsPlugValueWidget' )
Gaffer.Metadata.registerValue( __children["HackilyFixBadNurbs"]["basis"], 'layout:section', 'Settings' )
Gaffer.Metadata.registerValue( __children["HackilyFixBadNurbs"]["basis"], 'layout:index', 0 )
Gaffer.Metadata.registerValue( __children["HackilyFixBadNurbs"]["basis"], 'preset:bSpline', 'bSpline' )
Gaffer.Metadata.registerValue( __children["HackilyFixBadNurbs"]["basis"], 'preset:catmullRom', 'catmullRom' )
Gaffer.Metadata.registerValue( __children["HackilyFixBadNurbs"]["basis"], 'preset:linear', 'linear' )
__children["HackilyFixBadNurbs"]["Expression"]["__engine"].setValue( 'python' )
__children["HackilyFixBadNurbs"]["Expression"]["__expression"].setValue( 'import IECoreScene\n\ncurves = parent["__in"]["p0"]\nif type( curves ) == IECoreScene.CurvesPrimitive:\n\tcurves.setTopology( curves.verticesPerCurve(), IECore.CubicBasisf.linear(), curves.periodic() )\n\t\n\ttargetBasis = IECore.CubicBasisf.linear()\n\tif parent["__in"]["p1"] == "bSpline":\n\t\ttargetBasis = IECore.CubicBasisf.bSpline()\n\telif parent["__in"]["p1"] == "catmullRom":\n\t\ttargetBasis = IECore.CubicBasisf.catmullRom()\n\tcurves = IECoreScene.CurvesAlgo.updateEndpointMultiplicity( curves, targetBasis )\nparent["__out"]["p0"] = curves\n\n' )


del __children

Nico Rehberg

unread,
Dec 21, 2024, 5:26:44 PM12/21/24
to gaffer-dev
Hi Daniel,

this seems to work nicely. I wasn't even aware that you could manipulate primitives like that in an expression.

One small hickup is that while updateEndpointMultiplicity duplicates the points, it does not take care of the varying primitive variables, so the new entries at the end seem to point at invalid memory adresses resulting in funky values. This doesn't matter much for me here, since the only varying value coming in is width, which is actually constant for these characters, so I resampled it to a uniform variable.

Anyway, thanks again for your help,
Nico

Daniel Dresser

unread,
Dec 21, 2024, 6:53:28 PM12/21/24
to gaffe...@googlegroups.com
You're welcome!

Do note that in general, the trick of using expressions to manipulate primitives is something we recommend against for performance reasons.

But glad if it's helping you for the moment in this case. I'll reply to this thread if at some point in the future we come up with a better solution.

-Daniel

--
You received this message because you are subscribed to a topic in the Google Groups "gaffer-dev" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/gaffer-dev/VvJCMZR0vzY/unsubscribe.
To unsubscribe from this group and all its topics, send an email to gaffer-dev+...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/gaffer-dev/c2aff9ec-48af-44fb-92a3-d334068944c6n%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages