Ejemplo n.º 1
0
    def testCanDeleteFaces(self):

        rectangleScene = self.makeRectangleFromTwoSquaresScene()

        deleteFaces = GafferScene.DeleteFaces()
        deleteFaces["in"].setInput(rectangleScene["out"])

        pathFilter = GafferScene.PathFilter("PathFilter")
        pathFilter["paths"].setValue(IECore.StringVectorData(['/object']))
        deleteFaces["filter"].setInput(pathFilter["out"])

        faceDeletedObject = deleteFaces["out"].object("/object")

        self.assertEqual(faceDeletedObject.verticesPerFace,
                         IECore.IntVectorData([4]))
        self.assertEqual(faceDeletedObject.vertexIds,
                         IECore.IntVectorData([0, 1, 3, 2]))
        self.assertEqual(faceDeletedObject.numFaces(), 1)
        self.assertEqual(
            faceDeletedObject["P"].data,
            IECore.V3fVectorData([
                IECore.V3f(0, 0, 0),
                IECore.V3f(1, 0, 0),
                IECore.V3f(0, 1, 0),
                IECore.V3f(1, 1, 0)
            ]))

        # verify the primvars are correct
        self.assertEqual(faceDeletedObject["uniform"].data,
                         IECore.IntVectorData([10]))
        self.assertEqual(faceDeletedObject["vertex"].data,
                         IECore.IntVectorData([100, 101, 103, 104]))
        self.assertEqual(faceDeletedObject["faceVarying"].data,
                         IECore.IntVectorData([20, 21, 22, 23]))
Ejemplo n.º 2
0
    def testBoundsOfChildObjects(self):

        rectangle = self.makeRectangleFromTwoSquaresScene()
        sphere = GafferScene.Sphere()
        sphere["radius"].setValue(10)  # Totally encloses the rectangle

        parent = GafferScene.Parent()
        parent["in"].setInput(rectangle["out"])
        parent["parent"].setValue("/object")
        parent["children"][0].setInput(sphere["out"])

        self.assertSceneValid(parent["out"])

        pathFilter = GafferScene.PathFilter("PathFilter")
        pathFilter["paths"].setValue(IECore.StringVectorData(["/object"]))

        deleteFaces = GafferScene.DeleteFaces()
        deleteFaces["in"].setInput(parent["out"])
        deleteFaces["filter"].setInput(pathFilter["out"])

        # The sphere should not have been modified
        self.assertEqual(deleteFaces["out"].object("/object/sphere"),
                         parent["out"].object("/object/sphere"))
        # And the bounding boxes should still enclose all the objects,
        # including the sphere.
        self.assertSceneValid(deleteFaces["out"])
Ejemplo n.º 3
0
    def testNoUnnecessaryDirtyPropagationCrossTalk(self):

        #           plane
        #             |
        #     primitiveVariables
        #            / \
        #            | deleteFaces
        #            | |
        #            | |
        #           switch

        plane = GafferScene.Plane()

        primitiveVariables = GafferScene.PrimitiveVariables()
        primitiveVariables["in"].setInput(plane["out"])
        pv = Gaffer.NameValuePlug("test", IECore.IntData(0))
        primitiveVariables["primitiveVariables"].addChild(pv)

        # DeleteFaces has a dependency between the object and the
        # bound, so dirtying the input object also dirties the
        # output bound. There is cross-talk between the plugs.
        deleteFaces = GafferScene.DeleteFaces()
        deleteFaces["in"].setInput(primitiveVariables["out"])

        switch = Gaffer.Switch()
        switch.setup(primitiveVariables["out"])
        switch["in"][0].setInput(primitiveVariables["out"])
        switch["in"][1].setInput(deleteFaces["out"])

        # When the Switch index is constant 0, we know that DeleteFaces
        # is not the active branch. So we don't expect dirtying the input
        # object to dirty the output bound.
        cs = GafferTest.CapturingSlot(switch.plugDirtiedSignal())
        pv["value"].setValue(1)
        self.assertIn(switch["out"]["object"], {x[0] for x in cs})
        self.assertNotIn(switch["out"]["bound"], {x[0] for x in cs})

        # When the Switch index is constant 1, we know that DeleteFaces
        # is the active branch, so we do expect crosstalk.
        switch["index"].setValue(1)
        del cs[:]
        pv["value"].setValue(2)
        self.assertIn(switch["out"]["object"], {x[0] for x in cs})
        self.assertIn(switch["out"]["bound"], {x[0] for x in cs})

        # And when the Switch index is computed (indeterminate during
        # dirty propagation) we also expect crosstalk.
        add = GafferTest.AddNode()
        switch["index"].setInput(add["sum"])
        del cs[:]
        pv["value"].setValue(3)
        self.assertIn(switch["out"]["object"], {x[0] for x in cs})
        self.assertIn(switch["out"]["bound"], {x[0] for x in cs})
Ejemplo n.º 4
0
	def testDeletingFacesUpdatesBounds( self ) :

		rectangleScene = self.makeRectangleFromTwoSquaresScene()

		expectedOriginalBound = rectangleScene["out"].bound( "/object" )
		self.assertEqual(expectedOriginalBound, imath.Box3f( imath.V3f( 0, 0, 0 ), imath.V3f( 2, 1, 0 ) ) )

		deleteFaces = GafferScene.DeleteFaces()
		deleteFaces["in"].setInput( rectangleScene["out"] )

		pathFilter = GafferScene.PathFilter( "PathFilter" )
		pathFilter["paths"].setValue( IECore.StringVectorData( [ '/object' ] ) )
		deleteFaces["filter"].setInput( pathFilter["out"] )

		actualFaceDeletedBounds = deleteFaces["out"].bound( "/object" )
		expectedBoundingBox = imath.Box3f( imath.V3f( 0, 0, 0 ), imath.V3f( 1, 1, 0 ) )

		self.assertEqual( actualFaceDeletedBounds, expectedBoundingBox )
Ejemplo n.º 5
0
    def testIgnoreMissing(self):

        rectangle = self.makeRectangleFromTwoSquaresScene()
        deleteFaces = GafferScene.DeleteFaces()
        deleteFaces["in"].setInput(rectangle["out"])
        pathFilter = GafferScene.PathFilter("PathFilter")
        pathFilter["paths"].setValue(IECore.StringVectorData(['/object']))
        deleteFaces["filter"].setInput(pathFilter["out"])

        self.assertNotEqual(deleteFaces["in"].object("/object"),
                            deleteFaces["out"].object("/object"))

        deleteFaces["faces"].setValue("doesNotExist")
        self.assertRaises(RuntimeError, deleteFaces["out"].object, "/object")

        deleteFaces["ignoreMissingVariable"].setValue(True)
        self.assertEqual(deleteFaces["in"].object("/object"),
                         deleteFaces["out"].object("/object"))
Ejemplo n.º 6
0
    def testMerging(self):

        allFilter = GafferScene.PathFilter()
        allFilter["paths"].setValue(IECore.StringVectorData(['/...']))

        plane = GafferScene.Plane()
        plane["divisions"].setValue(imath.V2i(20, 20))

        # Assign a basic gradient shader
        uvGradientCode = GafferOSL.OSLCode()
        uvGradientCode["out"].addChild(
            Gaffer.Color3fPlug("out", direction=Gaffer.Plug.Direction.Out))
        uvGradientCode["code"].setValue('out = color( u, v, 0.5 );')

        shaderAssignment = GafferScene.ShaderAssignment()
        shaderAssignment["in"].setInput(plane["out"])
        shaderAssignment["filter"].setInput(allFilter["out"])
        shaderAssignment["shader"].setInput(uvGradientCode["out"]["out"])

        # Set up a random id from 0 - 3 on each face

        randomCode = GafferOSL.OSLCode()
        randomCode["out"].addChild(
            Gaffer.IntPlug("randomId", direction=Gaffer.Plug.Direction.Out))
        randomCode["code"].setValue(
            'randomId = int(cellnoise( P * 100 ) * 4);')

        outInt = GafferOSL.OSLShader()
        outInt.loadShader("ObjectProcessing/OutInt")
        outInt["parameters"]["name"].setValue('randomId')
        outInt["parameters"]["value"].setInput(randomCode["out"]["randomId"])

        outObject = GafferOSL.OSLShader()
        outObject.loadShader("ObjectProcessing/OutObject")
        outObject["parameters"]["in0"].setInput(
            outInt["out"]["primitiveVariable"])

        oSLObject = GafferOSL.OSLObject()
        oSLObject["in"].setInput(shaderAssignment["out"])
        oSLObject["filter"].setInput(allFilter["out"])
        oSLObject["shader"].setInput(outObject["out"])
        oSLObject["interpolation"].setValue(2)

        # Create 4 meshes by picking each of the 4 ids

        deleteContextVariables = Gaffer.DeleteContextVariables()
        deleteContextVariables.setup(GafferScene.ScenePlug())
        deleteContextVariables["variables"].setValue('collect:rootName')
        deleteContextVariables["in"].setInput(oSLObject["out"])

        pickCode = GafferOSL.OSLCode()
        pickCode["parameters"].addChild(Gaffer.IntPlug("targetId"))
        pickCode["out"].addChild(
            Gaffer.IntPlug("cull", direction=Gaffer.Plug.Direction.Out))
        pickCode["code"].setValue(
            'int randomId; getattribute( "randomId", randomId ); cull = randomId != targetId;'
        )

        expression = Gaffer.Expression()
        pickCode.addChild(expression)
        expression.setExpression(
            'parent.parameters.targetId = stoi( context( "collect:rootName", "0" ) );',
            "OSL")

        outInt1 = GafferOSL.OSLShader()
        outInt1.loadShader("ObjectProcessing/OutInt")
        outInt1["parameters"]["name"].setValue('deleteFaces')
        outInt1["parameters"]["value"].setInput(pickCode["out"]["cull"])

        outObject1 = GafferOSL.OSLShader()
        outObject1.loadShader("ObjectProcessing/OutObject")
        outObject1["parameters"]["in0"].setInput(
            outInt1["out"]["primitiveVariable"])

        oSLObject1 = GafferOSL.OSLObject()
        oSLObject1["in"].setInput(deleteContextVariables["out"])
        oSLObject1["filter"].setInput(allFilter["out"])
        oSLObject1["shader"].setInput(outObject1["out"])
        oSLObject1["interpolation"].setValue(2)

        deleteFaces = GafferScene.DeleteFaces()
        deleteFaces["in"].setInput(oSLObject1["out"])
        deleteFaces["filter"].setInput(allFilter["out"])

        collectScenes = GafferScene.CollectScenes()
        collectScenes["in"].setInput(deleteFaces["out"])
        collectScenes["rootNames"].setValue(
            IECore.StringVectorData(['0', '1', '2', '3']))
        collectScenes["sourceRoot"].setValue('/plane')

        # First variant:  bake everything, covering the whole 1001 UDIM

        customAttributes1 = GafferScene.CustomAttributes()
        customAttributes1["attributes"].addMember(
            'bake:fileName',
            IECore.StringData(
                '${bakeDirectory}/complete/<AOV>/<AOV>.<UDIM>.exr'))
        customAttributes1["in"].setInput(collectScenes["out"])

        # Second vaiant: bake just 2 of the 4 meshes, leaving lots of holes that will need filling
        pruneFilter = GafferScene.PathFilter()
        pruneFilter["paths"].setValue(IECore.StringVectorData(['/2', '/3']))

        prune = GafferScene.Prune()
        prune["in"].setInput(collectScenes["out"])
        prune["filter"].setInput(pruneFilter["out"])

        customAttributes2 = GafferScene.CustomAttributes()
        customAttributes2["attributes"].addMember(
            'bake:fileName',
            IECore.StringData(
                '${bakeDirectory}/incomplete/<AOV>/<AOV>.<UDIM>.exr'))
        customAttributes2["in"].setInput(prune["out"])

        # Third variant: bake everything, but with one mesh at a higher resolution

        customAttributes3 = GafferScene.CustomAttributes()
        customAttributes3["attributes"].addMember(
            'bake:fileName',
            IECore.StringData(
                '${bakeDirectory}/mismatch/<AOV>/<AOV>.<UDIM>.exr'))
        customAttributes3["in"].setInput(collectScenes["out"])

        pathFilter2 = GafferScene.PathFilter()
        pathFilter2["paths"].setValue(IECore.StringVectorData(['/2']))

        customAttributes = GafferScene.CustomAttributes()
        customAttributes["attributes"].addMember('bake:resolution',
                                                 IECore.IntData(200))
        customAttributes["filter"].setInput(pathFilter2["out"])
        customAttributes["in"].setInput(customAttributes3["out"])

        # Merge the 3 variants
        mergeGroup = GafferScene.Group()
        mergeGroup["in"][-1].setInput(customAttributes["out"])
        mergeGroup["in"][-1].setInput(customAttributes1["out"])
        mergeGroup["in"][-1].setInput(customAttributes2["out"])

        arnoldTextureBake = GafferArnold.ArnoldTextureBake()
        arnoldTextureBake["in"].setInput(mergeGroup["out"])
        arnoldTextureBake["filter"].setInput(allFilter["out"])
        arnoldTextureBake["bakeDirectory"].setValue(self.temporaryDirectory() +
                                                    '/bakeMerge/')
        arnoldTextureBake["defaultResolution"].setValue(128)

        # We want to check the intermediate results
        arnoldTextureBake["cleanupIntermediateFiles"].setValue(False)

        # Dispatch the bake
        script = Gaffer.ScriptNode()
        script.addChild(arnoldTextureBake)
        dispatcher = GafferDispatch.LocalDispatcher()
        dispatcher["jobsDirectory"].setValue(self.temporaryDirectory())
        dispatcher.dispatch([arnoldTextureBake])

        # Check results
        imageReader = GafferImage.ImageReader()

        outLayer = GafferOSL.OSLShader()
        outLayer.loadShader("ImageProcessing/OutLayer")
        outLayer["parameters"]["layerColor"].setInput(
            uvGradientCode["out"]["out"])

        outImage = GafferOSL.OSLShader()
        outImage.loadShader("ImageProcessing/OutImage")
        outImage["parameters"]["in0"].setInput(outLayer["out"]["layer"])
        oSLImage = GafferOSL.OSLImage()
        oSLImage["in"].setInput(imageReader["out"])
        oSLImage["shader"].setInput(outImage["out"])

        merge3 = GafferImage.Merge()
        merge3["in"]["in0"].setInput(oSLImage["out"])
        merge3["in"]["in1"].setInput(imageReader["out"])
        merge3["operation"].setValue(10)

        edgeDetect = self.SimpleEdgeDetect()
        edgeDetect["in"].setInput(imageReader["out"])

        edgeStats = GafferImage.ImageStats()
        edgeStats["in"].setInput(edgeDetect["out"])

        refDiffStats = GafferImage.ImageStats()
        refDiffStats["in"].setInput(merge3["out"])

        oneLayerReader = GafferImage.ImageReader()

        grade = GafferImage.Grade()
        grade["in"].setInput(oneLayerReader["out"])
        grade["channels"].setValue('[A]')
        grade["blackPoint"].setValue(imath.Color4f(0, 0, 0, 0.999899983))

        copyChannels = GafferImage.CopyChannels()
        copyChannels["in"]["in0"].setInput(merge3["out"])
        copyChannels["in"]["in1"].setInput(grade["out"])
        copyChannels["channels"].setValue('[A]')

        premultiply = GafferImage.Premultiply()
        premultiply["in"].setInput(copyChannels["out"])

        refDiffCoveredStats = GafferImage.ImageStats()
        refDiffCoveredStats["in"].setInput(premultiply["out"])

        # We are testing 3 different cases:
        # complete : Should be an exact match.
        # incomplete : Expect some mild variance of slopes and some error, because we have to
        #              reconstruct a lot of missing data.
        # mismatch : We should get a larger image, sized to the highest override on any mesh.
        #            Match won't be as perfect, because we're combining source images at
        #            different resolutions
        for name, expectedSize, maxEdge, maxRefDiff, maxMaskedDiff in [
            ("complete", 128, 0.01, 0.000001, 0.000001),
            ("incomplete", 128, 0.05, 0.15, 0.000001),
            ("mismatch", 200, 0.01, 0.01, 0.01)
        ]:
            imageReader["fileName"].setValue(self.temporaryDirectory() +
                                             "/bakeMerge/" + name +
                                             "/beauty/beauty.1001.tx")
            oneLayerReader["fileName"].setValue(self.temporaryDirectory() +
                                                "/bakeMerge/" + name +
                                                "/beauty/beauty.1001.exr")

            self.assertEqual(imageReader["out"]["format"].getValue().width(),
                             expectedSize)
            self.assertEqual(imageReader["out"]["format"].getValue().height(),
                             expectedSize)

            edgeStats["area"].setValue(
                imath.Box2i(imath.V2i(1), imath.V2i(expectedSize - 1)))
            refDiffStats["area"].setValue(
                imath.Box2i(imath.V2i(1), imath.V2i(expectedSize - 1)))
            refDiffCoveredStats["area"].setValue(
                imath.Box2i(imath.V2i(0), imath.V2i(expectedSize)))

            # Blue channel is constant, so everything should line up perfectly
            self.assertEqual(0, edgeStats["max"].getValue()[2])
            self.assertEqual(0, refDiffStats["max"].getValue()[2])
            self.assertEqual(0, refDiffCoveredStats["max"].getValue()[2])

            for i in range(2):

                # Make sure we've got actual data, by checking that we have some error ( we're not expecting
                # to perfectly reconstruct the gradient when the input is incomplete )
                self.assertGreater(edgeStats["max"].getValue()[i], 0.005)
                if name == "incomplete":
                    self.assertGreater(edgeStats["max"].getValue()[i], 0.03)
                    self.assertGreater(refDiffStats["max"].getValue()[i], 0.06)

                self.assertLess(edgeStats["max"].getValue()[i], maxEdge)
                self.assertLess(refDiffStats["max"].getValue()[i], maxRefDiff)
                self.assertLess(refDiffCoveredStats["max"].getValue()[i],
                                maxMaskedDiff)