diff --git a/Changes.md b/Changes.md index b68762f8733..ad955184360 100644 --- a/Changes.md +++ b/Changes.md @@ -13,11 +13,13 @@ Improvements - Viewer : - Added visualisation of light filters for USD lights. - Added support for USD lights and shaders in the floating inspector panel. + - Improved support for looking through USD spot lights. - ShaderTweaks/ShaderQuery : Added presets for USD light and surface shaders. - Test app : - The `-category` argument now accepts a space-separated list of categories, optionally containing wildcards. - Added `-excludedCategories` and `-showCategories` arguments. - Added information about performance test timings to the output stream. +- LightToCamera : Added support for USD spot lights. Fixes ----- diff --git a/python/GafferUSDTest/USDLightTest.py b/python/GafferUSDTest/USDLightTest.py index 9cac76efe40..5977bb07f11 100644 --- a/python/GafferUSDTest/USDLightTest.py +++ b/python/GafferUSDTest/USDLightTest.py @@ -125,5 +125,74 @@ def testShapingAndShadowAPIs( self ) : light["parameters"]["shaping:focus"]["value"] ) + def testLightToCamera( self ) : + + g = GafferScene.Group() + + inputs = {} + for shader, name in [ + ("SphereLight", "sphere1"), + ("SphereLight", "sphere2"), + ("DistantLight", "distant1"), + ("DomeLight", "env1"), + ] : + light = GafferUSD.USDLight() + light.loadShader( shader ) + light["name"].setValue( name ) + inputs[name] = light + + inputs["camera"] = GafferScene.Camera() + for i in inputs.values() : + g["in"][-1].setInput( i["out"] ) + + f = GafferScene.PathFilter() + f["paths"].setValue( IECore.StringVectorData( [ "/group/sphere1", "/group/env1", "/group/distant1" ] ) ) + + lc = GafferScene.LightToCamera() + lc["in"].setInput( g["out"] ) + lc["filter"].setInput( f["out"] ) + + # Test light with no corresponding camera outputs default camera + self.assertEqual( + lc["out"].object( "/group/sphere1" ).parameters(), + IECore.CompoundData( { + "projection" : IECore.StringData( "perspective" ), + } ) + ) + + def assertSpotCamera( camera ) : + + calculatedFieldOfView = camera.calculateFieldOfView() + self.assertAlmostEqual( calculatedFieldOfView[0], 65, 4 ) + self.assertAlmostEqual( calculatedFieldOfView[1], 65, 4 ) + self.assertEqual( camera.getClippingPlanes(), imath.V2f( 0.01, 100000 ) ) + self.assertEqual( camera.getProjection(), "perspective" ) + self.assertEqual( camera.getFilmFit(), IECoreScene.Camera.FilmFit.Fit ) + self.assertEqual( camera.hasResolution(), False ) + + # Test adding a spot cone to sphere light will output a persp cam + inputs["sphere1"]["parameters"]["shaping:cone:angle"]["enabled"].setValue( True ) + inputs["sphere1"]["parameters"]["shaping:cone:angle"]["value"].setValue( 32.5 ) + assertSpotCamera( lc["out"].object( "/group/sphere1" ) ) + + # Test distant light outputs ortho cam + distantCam = lc["out"].object( "/group/distant1" ) + self.assertEqual( distantCam.getAperture(), imath.V2f( 2, 2 ) ) + self.assertEqual( distantCam.getClippingPlanes(), imath.V2f( -100000, 100000 ) ) + self.assertEqual( distantCam.getProjection(), "orthographic" ) + self.assertEqual( distantCam.getFilmFit(), IECoreScene.Camera.FilmFit.Fit ) + self.assertEqual( distantCam.hasResolution(), False ) + + # Test adding a cone to a distant light will output a persp cam + inputs["distant1"]["parameters"]["shaping:cone:angle"]["enabled"].setValue( True ) + inputs["distant1"]["parameters"]["shaping:cone:angle"]["value"].setValue( 32.5 ) + assertSpotCamera( lc["out"].object( "/group/distant1" ) ) + + self.assertEqual( lc["out"].set( "__lights" ).value.paths(), [ "/group/sphere2" ] ) + self.assertEqual( + set( lc["out"].set( "__cameras" ).value.paths() ), + set( [ "/group/camera", "/group/sphere1", "/group/distant1", "/group/env1" ] ) + ) + if __name__ == "__main__": unittest.main() diff --git a/src/GafferScene/LightToCamera.cpp b/src/GafferScene/LightToCamera.cpp index b94c82fd27b..9ff4ea4a597 100644 --- a/src/GafferScene/LightToCamera.cpp +++ b/src/GafferScene/LightToCamera.cpp @@ -134,6 +134,12 @@ const char *lightType( const IECore::CompoundData *shaderParameters, const std:: return ""; } + if( boost::starts_with( metadataTarget, "light:" ) && shaderParameters->member( "shaping:cone:angle" ) ) + { + // USD lights with a cone angle defined are treated as spot lights + return "spot"; + } + return type->readable().c_str(); } @@ -151,6 +157,14 @@ float lightOuterAngle( const IECore::CompoundData *shaderParameters, const std:: } } + if( ConstStringDataPtr coneAngleType = Metadata::value( metadataTarget, "coneAngleType" ) ) + { + if( coneAngleType->readable() == "half" ) + { + coneAngle *= 2.0f; + } + } + const std::string *penumbraType = nullptr; ConstStringDataPtr penumbraTypeData = Metadata::value( metadataTarget, "penumbraType" ); if( penumbraTypeData )