Skip to content
Merged
2 changes: 1 addition & 1 deletion .github/workflows/main/installDependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@

# Determine default archive URL.

defaultURL = "https://github.com/ImageEngine/cortex/releases/download/10.7.0.0a9/cortex-10.7.0.0a9-{platform}-{vfxPlatform}.{extension}"
defaultURL = "https://github.com/GafferHQ/dependencies/releases/download/11.0.0a6/gafferDependencies-11.0.0a6-{platform}-{vfxPlatform}.{extension}"

# Parse command line arguments.

Expand Down
8 changes: 5 additions & 3 deletions Changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
Features
--------

- Cycles : Updated to version 5.0.0.
- Cycles : Updated to version 5.1.0.
- CurvesInterpolation : Added node for modifying CurvesPrimitive `basis` and `wrap`. This includes the ability to convert curves with `Pinned` wrap to `NonPeriodic`, adding the appropriate "phantom" points to maintain curve shape.

Improvements
Expand Down Expand Up @@ -38,8 +38,9 @@ Fixes
- DeleteCurves : Fixed deletion of periodic curves.
- ResamplePrimitiveVariables : Fixed resampling between Vertex and Varying for linear curves.
- Cycles :
- Reduced memory usage when rendering a single segment of deformation blur on CPU devices.
- Reduced memory usage when rendering a single segment of deformation blur.
- Fixed PointsPrimitive motion blur when rendering with even numbers of segments.
- Fixed translation of Uniform `N` primitive variables, these are now resampled to FaceVarying.
- USDShader : Fixed value of `type` plug after loading a USDLux light.
- CyclesLight, ArnoldLight, LightFilter : Fixed potential hang when loading shaders (GIL management bug in `loadShader()` binding).
- StandardNodeGadget : Fixed crash caused by the node emitting `errorSignal()` while the gadget is undergoing construction.
Expand Down Expand Up @@ -103,7 +104,7 @@ Build

- Boost : Updated to version 1.85.0.
- Cortex : Updated to version 10.7.0.0a9.
- Cycles : Updated to version 5.0.0.
- Cycles : Updated to version 5.1.0.
- Embree : Updated to version 4.4.0.
- Imath : Updated to version 3.1.12.
- Jemalloc : Removed when building on macOS.
Expand All @@ -117,6 +118,7 @@ Build
- PySide : Updated to version 6.5.8.
- Python : Updated to version 3.11.14.
- Qt : Updated to version 6.5.8.
- SSE2NEON : Added version 1.9.1 when building on macOS.
- TBB : Updated to version 2021.13.0.
- USD : Updated to version 26.03.

Expand Down
2 changes: 1 addition & 1 deletion SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -1390,7 +1390,7 @@ libraries = {
"LIBS" : [
"IECoreScene$CORTEX_LIB_SUFFIX", "IECoreImage$CORTEX_LIB_SUFFIX", "IECoreVDB$CORTEX_LIB_SUFFIX",
"Gaffer", "GafferScene", "GafferDispatch", "GafferOSL",
"cycles_device", "cycles_session", "cycles_scene", "cycles_graph", "cycles_bvh", "cycles_kernel", "cycles_kernel_osl",
"cycles_device", "cycles_session", "cycles_scene", "cycles_graph", "cycles_bvh", "cycles_kernel_cpu", "cycles_kernel_osl",
"cycles_integrator", "cycles_util", "cycles_subd", "extern_sky", "extern_cuew",
"OpenImageIO$OIIO_LIB_SUFFIX", "OpenImageIO_Util$OIIO_LIB_SUFFIX", "oslexec$OSL_LIB_SUFFIX", "oslcomp$OSL_LIB_SUFFIX", "oslquery$OSL_LIB_SUFFIX",
"openvdb$VDB_LIB_SUFFIX", "Alembic", "osdCPU", "OpenColorIO$OCIO_LIB_SUFFIX", "embree4", "openpgl", "zstd",
Expand Down
2 changes: 1 addition & 1 deletion include/GafferCycles/IECoreCyclesPreview/GeometryAlgo.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ namespace GeometryAlgo
{

/// Converts animated samples of an `IECore::Object` into an equivalent `ccl::Geometry` object.
IECORECYCLES_API ccl::Geometry *convert( const IECoreScenePreview::Renderer::ObjectSamples &samples, const IECoreScenePreview::Renderer::SampleTimes &times, ccl::Session *session );
IECORECYCLES_API ccl::Geometry *convert( const IECoreScenePreview::Renderer::ObjectSamples &samples, const IECoreScenePreview::Renderer::SampleTimes &times, ccl::Scene *scene );

/// Converts a primitive variable to a `ccl::Attribute` inside of a `ccl::AttributeSet`.
IECORECYCLES_API void convertPrimitiveVariable( const std::string &name, const IECoreScene::PrimitiveVariable &primitiveVariable, ccl::AttributeSet &attributes, ccl::AttributeElement attributeElement );
Expand Down
41 changes: 37 additions & 4 deletions python/GafferCyclesTest/IECoreCyclesPreviewTest/RendererTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -900,9 +900,7 @@ def testPointsWithNormals( self ) :
self.assertTrue( isinstance( image, IECoreImage.ImagePrimitive ) )

color = self.__colorAtUV( image, imath.V2f( 0.5 ) )
self.assertEqual( color.r, points["N"].data[0].x )
self.assertEqual( color.g, points["N"].data[0].y )
self.assertEqual( color.b, points["N"].data[0].z )
self.assertEqualWithAbsError( imath.Color3f( color.r, color.g, color.b ), points["N"].data[0].normalize(), 0.0001 )

def __testMeshSmoothing( self, cube, smoothingExpected ) :

Expand Down Expand Up @@ -970,7 +968,6 @@ def testNoMeshNormals( self ) :

def testFaceVaryingMeshNormals( self ) :

# These are treated like non-existent normals, since Cycles doesn't support them.
cube = IECoreScene.MeshPrimitive.createBox( imath.Box3f( imath.V3f( -0.5 ), imath.V3f( 0.5 ) ) )
self.__testMeshSmoothing( cube, smoothingExpected = False )

Expand All @@ -980,6 +977,42 @@ def testVertexMeshNormals( self ) :
cube["N"] = IECoreScene.MeshAlgo.calculateVertexNormals( cube, IECoreScene.MeshAlgo.NormalWeighting.Equal )
self.__testMeshSmoothing( cube, smoothingExpected = True )

def testUniformMeshNormals( self ) :

cube = IECoreScene.MeshPrimitive.createBox( imath.Box3f( imath.V3f( -0.5 ), imath.V3f( 0.5 ) ) )
cube["N"] = IECoreScene.MeshAlgo.calculateUniformNormals( cube )
self.__testMeshSmoothing( cube, smoothingExpected = False )

def testUnsupportedMeshNormals( self ) :

renderer = self.createRenderer()
attributes = renderer.attributes( IECore.CompoundObject() )

cube = IECoreScene.MeshPrimitive.createBox( imath.Box3f( imath.V3f( -0.5 ), imath.V3f( 0.5 ) ) )

for interpolation in (
IECoreScene.PrimitiveVariable.Interpolation.Uniform,
IECoreScene.PrimitiveVariable.Interpolation.Vertex,
IECoreScene.PrimitiveVariable.Interpolation.FaceVarying,
) :
with self.subTest( interpolation = interpolation ) :

cube = cube.copy()
cube["N"] = IECoreScene.PrimitiveVariable(
interpolation,
IECore.V3iVectorData( [ imath.V3i( 0, 1, 0 ) ] * cube.variableSize( interpolation ), IECore.GeometricData.Interpretation.Normal )
)

with IECore.CapturingMessageHandler() as mh :

renderer.object( str( interpolation ), cube, attributes )

self.assertEqual( len( mh.messages ), 1 )
self.assertEqual(
mh.messages[0].message,
"Primitive variable \"N\" has unsupported type \"V3iVectorData\" (expected V3fVectorData)."
)

def testUnsupportedPrimitiveVariables( self ) :

renderer = self.createRenderer()
Expand Down
66 changes: 37 additions & 29 deletions src/GafferCycles/IECoreCyclesPreview/GeometryAlgo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@

#include "IECoreScene/PrimitiveVariable.h"

#include "IECore/ObjectInterpolator.h"
#include "IECore/SimpleTypedData.h"

IECORE_PUSH_DEFAULT_VISIBILITY
Expand Down Expand Up @@ -142,6 +141,13 @@ ccl::Attribute *convertTypedPrimitiveVariable( const std::string &name, const Pr
data = static_cast<const T *>( primitiveVariable.data.get() );
}

// Special case for normals as they need to be octahedrally encoded.
const bool isNormal = typeDesc == ccl::TypeNormal;
if( isNormal )
{
attributeElement = attributeElement == ccl::ATTR_ELEMENT_CORNER ? ccl::ATTR_ELEMENT_CORNER_NORMAL : ccl::ATTR_ELEMENT_VERTEX_NORMAL;
}

// Create attribute. Cycles will allocate a buffer based on `attributeElement` and the information
// `attributes.geometry` contains.

Expand All @@ -167,7 +173,29 @@ ccl::Attribute *convertTypedPrimitiveVariable( const std::string &name, const Pr

// Copy data into buffer.

if constexpr( std::is_same_v<T, V3fVectorData> || std::is_same_v<T, Color3fVectorData> )
if( isNormal )
{
if constexpr( std::is_same_v<T, V3fVectorData> )
{
ccl::packed_normal *pn = attribute->data_normal();
for( const auto &v : data->readable() )
{
*pn++ = ccl::packed_normal( ccl::make_float3( v.x, v.y, v.z ) );
}
}
else
{
msg(
Msg::Warning, "IECoreCyles::GeometryAlgo::convertPrimitiveVariable",
fmt::format(
"Primitive variable \"{}\" has unsupported type \"{}\" (expected V3fVectorData).",
name, primitiveVariable.data->typeName()
)
);
return nullptr;
}
}
else if constexpr( std::is_same_v<T, V3fVectorData> || std::is_same_v<T, Color3fVectorData> )
{
// Special case for arrays of `float3`, where each element actually contains 4 floats for alignment purposes.
ccl::float3 *f3 = attribute->data_float3();
Expand Down Expand Up @@ -211,37 +239,13 @@ namespace IECoreCycles
namespace GeometryAlgo
{

ccl::Geometry *convert( const IECoreScenePreview::Renderer::ObjectSamples &samples, const IECoreScenePreview::Renderer::SampleTimes &times, ccl::Session *session )
ccl::Geometry *convert( const IECoreScenePreview::Renderer::ObjectSamples &samples, const IECoreScenePreview::Renderer::SampleTimes &times, ccl::Scene *scene )
{
if( samples.empty() )
{
return nullptr;
}

if( samples.size() % 2 == 0 && session->device->info.type != ccl::DeviceType::DEVICE_CPU )
{
// Cycles requires an odd number of motion samples for some reason, although
// experimentally this only seems to be the case when using GPU devices.
// Make memory-wasting redundant samples to work around this. Samples are
// expected to be spaced evenly in time, so we have to insert a redundant sample
// in every gap.
IECoreScenePreview::Renderer::ObjectSamples processedSamples;
processedSamples.reserve( samples.size() * 2 - 1 );
IECoreScenePreview::Renderer::SampleTimes processedTimes;
processedTimes.reserve( samples.size() * 2 - 1 );
for( size_t i = 0; i < samples.size(); ++i )
{
processedSamples.push_back( samples[i] );
processedTimes.push_back( times[i] );
if( i + 1 < samples.size() )
{
processedSamples.push_back( linearObjectInterpolation( samples[i].get(), samples[i+1].get(), 0.5f ) );
processedTimes.push_back( Imath::lerp( times[i], times[i+1], 0.5f ) );
}
}
return convert( processedSamples, processedTimes, session );
}

const IECore::Object *firstSample = samples.front().get();
const IECore::TypeId firstSampleTypeId = firstSample->typeId();
for( const auto &sample : samples )
Expand All @@ -261,7 +265,7 @@ ccl::Geometry *convert( const IECoreScenePreview::Renderer::ObjectSamples &sampl
// Cycles expects the middle sample (rounding down for even numbers of
// samples) to be specified as the main sample, and the other samples to
// be provided via ATTR_STD_MOTION_VERTEX_POSITION.
return it->second( samples, times, (samples.size() - 1) / 2, session->scene.get() );
return it->second( samples, times, (samples.size() - 1) / 2, scene );
}

void registerConverter( IECore::TypeId fromType, Converter converter )
Expand Down Expand Up @@ -374,10 +378,14 @@ void convertPrimitiveVariable( const std::string &name, const IECoreScene::Primi
/// use `convertPrimitiveVariable()` for most data, instead of having
/// custom code paths for `P`, `uv` etc?

if( name == "N" && attr->element == ccl::ATTR_ELEMENT_VERTEX && attr->type == ccl::TypeNormal )
if( name == "N" && attr->element == ccl::ATTR_ELEMENT_VERTEX_NORMAL && attr->type == ccl::TypeNormal )
{
attr->std = ccl::ATTR_STD_VERTEX_NORMAL;
}
else if( name == "N" && attr->element == ccl::ATTR_ELEMENT_CORNER_NORMAL && attr->type == ccl::TypeNormal )
{
attr->std = ccl::ATTR_STD_CORNER_NORMAL;
}
else if( name == "uv" && attr->type == ccl::TypeFloat2 )
{
attr->std = ccl::ATTR_STD_UV;
Expand Down
16 changes: 8 additions & 8 deletions src/GafferCycles/IECoreCyclesPreview/IECoreCycles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,24 +146,24 @@ IECore::CompoundDataPtr nodeData()
IECore::CompoundDataPtr result = new IECore::CompoundData();
IECore::CompoundDataMap &nodes = result->writable();

for( const auto& nodeType : ccl::NodeType::types() )
for( const auto& nodeType : ccl::NodeType::type_names() )
{
const ccl::NodeType *cNodeType = ccl::NodeType::find( nodeType.first );
const ccl::NodeType *cNodeType = ccl::NodeType::find( nodeType );
if( cNodeType )
{
// We skip "ShaderNode" types here
if( cNodeType->type == ccl::NodeType::SHADER )
continue;

// The shader node we skip
if( nodeType.first == "shader" )
if( nodeType == "shader" )
continue;

IECore::CompoundDataPtr node = new IECore::CompoundData();
IECore::CompoundDataMap &n = node->writable();
n["in"] = getSockets( cNodeType, false );
n["out"] = getSockets( cNodeType, true );
nodes[nodeType.first.c_str()] = std::move( node );
nodes[nodeType.c_str()] = std::move( node );
}
}
return result;
Expand All @@ -174,14 +174,14 @@ IECore::CompoundDataPtr shaderData()
IECore::CompoundDataPtr result = new IECore::CompoundData();
IECore::CompoundDataMap &shaders = result->writable();

for( const auto& nodeType : ccl::NodeType::types() )
for( const auto& nodeType : ccl::NodeType::type_names() )
{
// Skip over the "output" ShaderNode, as this is a part of the main
// "shader" node.
if( std::string( nodeType.first.c_str() ) == "output" )
if( std::string( nodeType.c_str() ) == "output" )
continue;

const ccl::NodeType *cNodeType = ccl::NodeType::find( nodeType.first );
const ccl::NodeType *cNodeType = ccl::NodeType::find( nodeType );
if( cNodeType )
{
if( cNodeType->type == ccl::NodeType::SHADER )
Expand All @@ -191,7 +191,7 @@ IECore::CompoundDataPtr shaderData()
s["in"] = getSockets( cNodeType, false );
s["out"] = getSockets( cNodeType, true );

std::string type( nodeType.first.c_str() );
std::string type( nodeType.c_str() );

if( boost::ends_with( type, "bsdf" ) )
{
Expand Down
29 changes: 15 additions & 14 deletions src/GafferCycles/IECoreCyclesPreview/MeshAlgo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,10 @@
#include "IECoreScene/MeshPrimitive.h"
#include "IECoreScene/MeshAlgo.h"

#include "IECore/Interpolator.h"
#include "IECore/SimpleTypedData.h"

// Cycles
#include "kernel/types.h"
#include "scene/geometry.h"
#include "scene/mesh.h"
#include "subd/dice.h"
#include "util/param.h"
#include "util/types.h"

#include "fmt/format.h"
Expand All @@ -66,14 +61,18 @@ namespace
// - Cycles meshes store vertex normals as ("N", ATTR_STD_VERTEX_NORMAL)
// - If we don't specify vertex normals, they are computed for us
// and added to the mesh by Cycles itself by `Mesh::add_vertex_normals()`
// - Face normals are computed on demand in the Cycles kernel.
// - Face normals are always computed on demand in the Cycles kernel, so we
// resample custom uniform normals to face-varying.
// - Which normal is actually used for shading is determined on a
// triangle-by-triangle basis using the `smooth` flag passed
// to `Mesh::add_triangle()`.
// - Cycles does not support facevarying normals.
// - Cycles as of 5.1 now supports face-varying normals as
// ("N", ATTR_STD_CORNER_NORMAL)
//
// Also see `GeometryAlgo::convertPrimitiveVariable()` where we handle the
// tagging of normal attributes with ATTR_STD_VERTEX_NORMAL.
// tagging of normal attributes with ATTR_STD_VERTEX_NORMAL or
// ATTR_STD_CORNER_NORMAL. This also handles octahedral packing of normals
// via the `ccl::packed_normal()` utility function.
bool hasSmoothNormals( const IECoreScene::MeshPrimitive *mesh )
{
auto it = mesh->variables.find( "N" );
Expand All @@ -88,12 +87,6 @@ bool hasSmoothNormals( const IECoreScene::MeshPrimitive *mesh )
case PrimitiveVariable::Uniform :
// These are definitely intended to be faceted.
return false;
case PrimitiveVariable::FaceVarying :
// Could be a mix of faceted and non-faceted triangles, including
// triangles with a mix of soft and hard edges, which aren't
// representable in Cycles. Plump for faceted, among other things
// because the native Cortex cube geometry has FaceVarying normals.
return false;
default :
return true;
}
Expand Down Expand Up @@ -220,6 +213,14 @@ ccl::Mesh *convertPrimary( const IECoreScene::MeshPrimitive *mesh, ccl::Scene *s
// Converted above already
continue;
}
if( name == "N" && variable.interpolation == PrimitiveVariable::Uniform )
{
// Resample "N" to FaceVarying as Cycles doesn't accept custom uniform normals.
PrimitiveVariable resampledN = variable;
IECoreScene::MeshAlgo::resamplePrimitiveVariable( mesh, resampledN, PrimitiveVariable::FaceVarying );
GeometryAlgo::convertPrimitiveVariable( name, resampledN, attributes, ccl::ATTR_ELEMENT_CORNER );
continue;
}
switch( variable.interpolation )
{
case PrimitiveVariable::Constant :
Expand Down
Loading
Loading