Skip to content

Commit

Permalink
Merge pull request #5647 from gkocov/3dl_procedural
Browse files Browse the repository at this point in the history
3Delight external procedural support
  • Loading branch information
johnhaddon authored Feb 1, 2024
2 parents 5e5808b + 8a0f67e commit 6927b9a
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 0 deletions.
1 change: 1 addition & 0 deletions Changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ Improvements
- Added support for reading `dl:` and `user:` attributes from shaders.
- Added `importanceSampleFilter` plug to DelightOptions, providing denoiser-compatible output.
- Matched DelightOptions default values for `oversampling` and `shadingSamples` to 3Delight's own default values.
- Added support for external procedurals.
- GraphEditor : Improved logic used to connect a newly created node to the selected nodes.
- ScenePlug, ImagePlug : Child plugs are now serialisable. Among other things, this enables them to be driven by expressions (#3986).
- Premultiply : Added `useDeepVisibility` plug, which weights samples according to their visibility based on the opacity of samples in front.
Expand Down
3 changes: 3 additions & 0 deletions include/IECoreDelight/ParameterList.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ class IECOREDELIGHT_API ParameterList

void add( const NSIParam_t &parameter );
void add( const char *name, const std::string &value );
// Prevent calls to `add()` with a temporary string, since that
// would cause a dangling pointer to be passed to 3Delight.
void add( const char *name, const std::string &&value ) = delete;
/// \deprecated Use the version below.
void add( const char *name, const IECore::Data *value );
void add( const char *name, const IECore::Data *value, bool isSingleArray );
Expand Down
37 changes: 37 additions & 0 deletions python/IECoreDelightTest/RendererTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,43 @@ def testSphere( self ) :
self.__assertInNSI( '"width" "v float" 1 4', nsi )
self.__assertNotInNSI( '"N"', nsi )

def testProcedural( self ) :

r = GafferScene.Private.IECoreScenePreview.Renderer.create(
"3Delight",
GafferScene.Private.IECoreScenePreview.Renderer.RenderType.SceneDescription,
str( self.temporaryDirectory() / "test.nsia" ),
)

r.object(
"testProcedural",
IECoreScene.ExternalProcedural(
str( self.temporaryDirectory() / "testProc.nsi" ),
imath.Box3f( imath.V3f( -0.5, -0.5, -0.5 ), imath.V3f( 0.5, 0.5, 0.5 ) ),
IECore.CompoundData( {
"customParamFloat" : 1.0,
} )
),
r.attributes( IECore.CompoundObject() ),
)

r.render()
del r

nsi = self.__parseDict( self.temporaryDirectory() / "test.nsia" )

self.assertIn( "testProcedural", nsi )
self.assertEqual( nsi["testProcedural"]["nodeType"], "transform" )

procedurals = { k: v for k, v in nsi.items() if nsi[k]["nodeType"] == "procedural" }
self.assertEqual( len( procedurals ), 1 )

procedural = procedurals[next( iter( procedurals ) )]

self.assertEqual( procedural["filename"], str( self.temporaryDirectory() / "testProc.nsi" ) )
self.assertEqual( procedural["type"], "apistream" )
self.assertEqual( procedural["customParamFloat"], 1.0 )

def testEnvironment( self ) :

r = GafferScene.Private.IECoreScenePreview.Renderer.create(
Expand Down
108 changes: 108 additions & 0 deletions src/IECoreDelight/ProceduralAlgo.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
//////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2024, Cinesite VFX Ltd. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above
// copyright notice, this list of conditions and the following
// disclaimer.
//
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided with
// the distribution.
//
// * Neither the name of John Haddon nor the names of
// any other contributors to this software may be used to endorse or
// promote products derived from this software without specific prior
// written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////

#include "IECoreDelight/NodeAlgo.h"
#include "IECoreDelight/ParameterList.h"

#include "IECoreScene/ExternalProcedural.h"

#include "IECore/SimpleTypedData.h"

#include "boost/algorithm/string.hpp"
#include "boost/algorithm/string/predicate.hpp"

#include "fmt/format.h"

#include <nsi.h>

using namespace std;
using namespace Imath;
using namespace IECore;
using namespace IECoreScene;
using namespace IECoreDelight;

namespace
{

bool convert( const IECoreScene::ExternalProcedural *object, NSIContext_t context, const char *handle )
{
NSICreate( context, handle, "procedural", 0, nullptr );

ParameterList procParameters;

const std::string &filename = object->getFileName();
std::string type;

if( boost::ends_with( filename, "lua" ) )
{
type = "lua";
}
else if ( boost::ends_with( filename, "nsi" ) or boost::ends_with( filename, "nsia" ) )
{
type = "apistream";
}
else
{
type = "dynamiclibrary";
}

// 3Delight seems to behave weirdly when passed the boundingbox parameter:
// doesn't render the procedural content when streaming the NSI scene initially,
// yet always renders the procedural content when reading a NSI scene from disk.
// Due to this commenting out the corresponding code for now.
//
// const Box3f &bbox = object->getBound();
//
// if ( bbox != Box3f( V3f( -0.5, -0.5, -0.5 ), V3f( 0.5, 0.5, 0.5 ) ) )
// {
// procParameters.add( { "boundingbox", bbox.min.getValue(), NSITypePoint, 2, 1, NSIParamIsArray } );
// }

procParameters.add( "type", type );
procParameters.add( "filename", filename );

for( const auto &parameter : object->parameters()->readable() )
{
procParameters.add( parameter.first.c_str(), parameter.second.get(), true );
}

NSISetAttribute( context, handle, procParameters.size(), procParameters.data() );
return true;
}

NodeAlgo::ConverterDescription<ExternalProcedural> g_description( convert );

} // namespace

0 comments on commit 6927b9a

Please sign in to comment.