Skip to content

Commit

Permalink
Merge branch '1.5_maintenance'
Browse files Browse the repository at this point in the history
johnhaddon committed Jan 27, 2025
2 parents 40ce482 + df6a6c4 commit 85c996e
Showing 24 changed files with 893 additions and 8 deletions.
13 changes: 13 additions & 0 deletions Changes.md
Original file line number Diff line number Diff line change
@@ -9,10 +9,17 @@ Breaking Changes
1.5.x.x (relative to 1.5.3.0)
=======

Features
--------

- HierarchyView : Added ability to store and recall the Visible Set in named bookmarks that are saved with the script.
- TensorToMesh : Added new ML node for converting a suitable tensor into a 3d mesh.

Improvements
------------

- AttributeEditor : Added "Select Affected Objects" menu item to the "Linked Lights" and Arnold "Shadow Group" columns.
- ScriptNode : Added support for serialising metadata registered on a ScriptNode.

Fixes
-----
@@ -22,12 +29,15 @@ Fixes
- Fixed bugs which prevented edits being made in "Source" scope when there was a downstream edit in an EditScope (#6172).
- Fixed warning messages when attempting to disable a non-existent edit.
- Fixed warning message which referred to "None" rather than the "Source" scope.
- PythonEditor : Fixed bug preventing values from being inserted when dragging most VectorData types into the PythonEditor.

API
---

- RenderPassEditor : Added optional `index` argument to `registerOption()` and `registerColumn()`. This can be used to specify the column's position in the UI.
- Metadata : Added `targetsWithMetadata()` function, returning all the string targets which match a pattern and have a specific metadata key.
- VisibleSetData : Implemented `save()` and `load()`.
- ScriptNodeAlgo : Added functions for managing VisibleSet bookmarks.

1.5.3.0 (relative to 1.5.2.0)
=======
@@ -437,7 +447,10 @@ Build
1.4.15.x (relative to 1.4.15.4)
========

Fixes
-----

- PythonEditor : Fixed bug preventing values from being inserted when dragging most VectorData types into the PythonEditor.

1.4.15.4 (relative to 1.4.15.3)
========
4 changes: 2 additions & 2 deletions SConstruct
Original file line number Diff line number Diff line change
@@ -1119,12 +1119,12 @@ libraries = {
"envAppends" : {
"CPPPATH" : [ "$ONNX_ROOT/include" ],
"LIBPATH" : [ "$ONNX_ROOT/lib" ],
"LIBS" : [ "Gaffer", "GafferImage", "onnxruntime" ],
"LIBS" : [ "Gaffer", "GafferImage", "onnxruntime", "GafferScene", "IECoreScene$CORTEX_LIB_SUFFIX" ],
},
"pythonEnvAppends" : {
"CPPPATH" : [ "$ONNX_ROOT/include" ],
"LIBPATH" : [ "$ONNX_ROOT/lib" ],
"LIBS" : [ "GafferBindings", "GafferImage", "GafferML", "onnxruntime" ],
"LIBS" : [ "GafferBindings", "GafferImage", "GafferML", "onnxruntime", "GafferScene", "IECoreScene$CORTEX_LIB_SUFFIX" ],
},
"requiredOptions" : [ "ONNX_ROOT" ],
},
80 changes: 80 additions & 0 deletions include/GafferML/TensorToMesh.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
//////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2024, Lucien Fostier. 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.
//
//////////////////////////////////////////////////////////////////////////

#pragma once

#include "GafferML/Export.h"
#include "GafferML/TensorPlug.h"

#include "GafferScene/ObjectSource.h"
#include "Gaffer/TransformPlug.h"

namespace GafferML
{

class GAFFERML_API TensorToMesh : public GafferScene::ObjectSource
{

public :

explicit TensorToMesh( const std::string &name=defaultName<TensorToMesh>() );
~TensorToMesh() override;

GAFFER_NODE_DECLARE_TYPE( GafferML::TensorToMesh, TensorToMeshTypeId, GafferScene::ObjectSource );

TensorPlug *positionTensorPlug();
const TensorPlug *positionTensorPlug() const;

TensorPlug *vertexIdsTensorPlug();
const TensorPlug *vertexIdsTensorPlug() const;

void affects( const Gaffer::Plug *input, Gaffer::DependencyNode::AffectedPlugsContainer &outputs ) const override;

protected :

void hashSource( const Gaffer::Context *context, IECore::MurmurHash &h ) const override;
IECore::ConstObjectPtr computeSource( const Gaffer::Context *context ) const override;

private :


static size_t g_firstPlugIndex;

};

IE_CORE_DECLAREPTR( TensorToMesh )

} // namespace GafferML
1 change: 1 addition & 0 deletions include/GafferML/TypeIds.h
Original file line number Diff line number Diff line change
@@ -48,6 +48,7 @@ enum TypeId
InferenceTypeId = 110455,
TensorReaderTypeId = 110456,
DataToTensorTypeId = 110457,
TensorToMeshTypeId = 110458,

LastTypeId = 110500
};
2 changes: 1 addition & 1 deletion include/GafferScene/VisibleSetData.h
Original file line number Diff line number Diff line change
@@ -43,7 +43,7 @@

namespace IECore
{
IECORE_DECLARE_TYPEDDATA( VisibleSetData, GafferScene::VisibleSet, void, IECore::SimpleDataHolder );
IECORE_DECLARE_TYPEDDATA( VisibleSetData, GafferScene::VisibleSet, void, IECore::SharedDataHolder );
} // namespace IECore

namespace GafferScene
16 changes: 16 additions & 0 deletions include/GafferSceneUI/ScriptNodeAlgo.h
Original file line number Diff line number Diff line change
@@ -123,6 +123,22 @@ GAFFERSCENEUI_API void setCurrentRenderPass( Gaffer::ScriptNode *script, std::st
/// Returns the current render pass for the script.
GAFFERSCENEUI_API std::string getCurrentRenderPass( const Gaffer::ScriptNode *script );

/// Visible Set Bookmarks
/// =====================

/// Visible Set bookmarks can be used to store named bookmarks containing a VisibleSet as
/// metadata on the ScriptNode, allowing users to bookmark important VisibleSets to be
/// recalled later.

/// Stores a VisibleSet as a named bookmark for the script.
GAFFERSCENEUI_API void addVisibleSetBookmark( Gaffer::ScriptNode *script, const std::string &name, const GafferScene::VisibleSet &visibleSet, bool persistent = true );
/// Returns the VisibleSet previously bookmarked as `name`.
GAFFERSCENEUI_API GafferScene::VisibleSet getVisibleSetBookmark( const Gaffer::ScriptNode *script, const std::string &name );
/// Removes the bookmark previously stored as `name`.
GAFFERSCENEUI_API void removeVisibleSetBookmark( Gaffer::ScriptNode *script, const std::string &name );
/// Returns the names of all VisibleSet bookmarks stored on `script`.
GAFFERSCENEUI_API std::vector<std::string> visibleSetBookmarks( const Gaffer::ScriptNode *script );

} // namespace ScriptNodeAlgo

} // namespace GafferSceneUI
1 change: 1 addition & 0 deletions python/GafferML/__init__.py
Original file line number Diff line number Diff line change
@@ -39,6 +39,7 @@

__import__( "Gaffer" )
__import__( "GafferImage" )
__import__( "GafferScene" )

if hasattr( os, "add_dll_directory" ) :
os.add_dll_directory( ( pathlib.Path( os.environ["ONNX_ROOT"] ) / "lib" ).resolve() )
147 changes: 147 additions & 0 deletions python/GafferMLTest/TensorToMeshTest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
##########################################################################
#
# Copyright (c) 2024, Lucien Fostier. 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.
#
##########################################################################

import unittest

import imath

import IECore

import Gaffer
import GafferTest
import GafferScene
import GafferML

class TensorToMeshTest( GafferTest.TestCase ) :
def testNoInput( self ) :

node = GafferML.TensorToMesh()
with self.assertRaisesRegex( Gaffer.ProcessException, "Empty Position tensor" ) :
node["out"].object("/tensorMesh"),

def test( self ) :

points = [ 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 2.0 ]
position = IECore.FloatVectorData( points )
positionTensor = GafferML.Tensor( position, [ 1, 3, 3 ] )
vertexIds = IECore.Int64VectorData( [ 0, 1, 2 ] )
vertexIdsTensor = GafferML.Tensor( vertexIds, [ 1, 3 ] )
tensorToMesh = GafferML.TensorToMesh()
tensorToMesh["position"].setValue( positionTensor )
tensorToMesh["vertexIds"].setValue( vertexIdsTensor )

self.assertEqual( tensorToMesh["out"].object( "/" ), IECore.NullObject() )
self.assertEqual( tensorToMesh["out"].transform( "/" ), imath.M44f() )
self.assertEqual( tensorToMesh["out"].bound( "/" ), imath.Box3f( imath.V3f( 0 ), imath.V3f( 1, 1, 2 ) ) )
self.assertEqual( tensorToMesh["out"].childNames( "/" ), IECore.InternedStringVectorData( [ "tensorMesh" ] ) )

self.assertEqual( tensorToMesh["out"].transform( "/tensorMesh" ), imath.M44f() )
self.assertEqual( tensorToMesh["out"].bound( "/tensorMesh" ), imath.Box3f( imath.V3f( 0 ), imath.V3f( 1, 1, 2 ) ) )
self.assertEqual( tensorToMesh["out"].childNames( "/tensorMesh" ), IECore.InternedStringVectorData() )

mesh = tensorToMesh["out"].object( "/tensorMesh" )
expectedValue = [component for vec in mesh["P"].data for component in (vec.x, vec.y, vec.z)]

self.assertEqual( expectedValue, points )
self.assertEqual( mesh.numFaces(), 1 )
self.assertEqual( mesh.verticesPerFace, IECore.IntVectorData( [ 3 ] ) )

def testWrongVertexTensorDimension( self ) :

points = [ 0.0, 0.0, 0.0 , 1.0, 1.0, 0.0, 1.0, 1.0, 2.0 ]
position = IECore.FloatVectorData( points )
positionTensor = GafferML.Tensor( position, [ 3, 3 ] )
vertexIds = IECore.Int64VectorData( [ 0, 1, 2 ] )
vertexIdsTensor = GafferML.Tensor( vertexIds, [ 1, 3 ] )
tensorToMesh = GafferML.TensorToMesh()
tensorToMesh["position"].setValue( positionTensor )
tensorToMesh["vertexIds"].setValue( vertexIdsTensor )

with self.assertRaisesRegex( Gaffer.ProcessException, "Invalid position tensor number of dimensions" ):
mesh = tensorToMesh["out"].object( "/tensorMesh" )

def testWrongVertexDimension( self ) :

points = [ 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 ]
position = IECore.FloatVectorData( points )
positionTensor = GafferML.Tensor( position, [ 1, 3, 2 ] )
vertexIds = IECore.Int64VectorData( [ 0, 1, 2 ] )
vertexIdsTensor = GafferML.Tensor( vertexIds, [ 1, 3 ] )
tensorToMesh = GafferML.TensorToMesh()
tensorToMesh["position"].setValue( positionTensor )
tensorToMesh["vertexIds"].setValue( vertexIdsTensor )

with self.assertRaisesRegex( Gaffer.ProcessException, "Invalid position dimensions" ):
mesh = tensorToMesh["out"].object( "/tensorMesh" )

def testEmptyFaces( self ):
points = [ 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 2.0 ]
position = IECore.FloatVectorData( points )
positionTensor = GafferML.Tensor( position, [ 1, 3, 3 ] )
tensorToMesh = GafferML.TensorToMesh()
tensorToMesh["position"].setValue( positionTensor )

with self.assertRaisesRegex( Gaffer.ProcessException, "Empty VertexIds tensor" ):
mesh = tensorToMesh["out"].object( "/tensorMesh" )

def testWrongDataTypeVertices( self ):
points = [ 0, 0, 0, 1, 1, 1, 1, 0, 2 ]
position = IECore.IntVectorData( points )
positionTensor = GafferML.Tensor( position, [ 1, 3, 3 ] )
vertexIds = IECore.Int64VectorData( [ 0, 1, 2 ] )
vertexIdsTensor = GafferML.Tensor( vertexIds, [ 1, 3 ] )
tensorToMesh = GafferML.TensorToMesh()
tensorToMesh["position"].setValue( positionTensor )
tensorToMesh["vertexIds"].setValue( vertexIdsTensor )

with self.assertRaisesRegex( Gaffer.ProcessException, "Invalid data type input for position tensor" ):
mesh = tensorToMesh["out"].object( "/tensorMesh" )

def testWrongDataTypeFaces( self ):
points = [ 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 2.0 ]
position = IECore.FloatVectorData( points )
positionTensor = GafferML.Tensor( position, [ 1, 3, 3 ] )
vertexIds = IECore.IntVectorData( [ 0, 1, 2 ] )
vertexIdsTensor = GafferML.Tensor( vertexIds, [ 1, 3 ] )
tensorToMesh = GafferML.TensorToMesh()
tensorToMesh["position"].setValue( positionTensor )
tensorToMesh["vertexIds"].setValue( vertexIdsTensor )

with self.assertRaisesRegex( Gaffer.ProcessException, "Invalid data type input for vertexIds tensor" ):
mesh = tensorToMesh["out"].object( "/tensorMesh" )

if __name__ == "__main__":
unittest.main()
1 change: 1 addition & 0 deletions python/GafferMLTest/__init__.py
Original file line number Diff line number Diff line change
@@ -40,6 +40,7 @@
from .InferenceTest import InferenceTest
from .ImageToTensorTest import ImageToTensorTest
from .TensorToImageTest import TensorToImageTest
from .TensorToMeshTest import TensorToMeshTest

if __name__ == "__main__":
import unittest
Loading

0 comments on commit 85c996e

Please sign in to comment.