From cb870cf3d9b419c8d4f4ec2528b77d31f9dafc1a Mon Sep 17 00:00:00 2001 From: Kuldeep Singh <30294746+3DSinghVFX@users.noreply.github.com> Date: Sat, 23 Dec 2023 19:10:49 +0530 Subject: [PATCH] Support Integer 2D custom attributes (#1931) --- CHANGELOG.md | 2 + animation_nodes/data_structures/__init__.py | 4 +- .../data_structures/attributes/attribute.pxd | 1 + .../data_structures/attributes/attribute.pyx | 3 + .../lists/special_list_types.json | 12 ++++ .../virtual_list/virtual_clist_types.json | 5 ++ animation_nodes/extend_bpy_types.py | 6 +- animation_nodes/math/conversion.pxd | 6 +- animation_nodes/math/conversion.pyx | 14 +++++ animation_nodes/math/vector.pxd | 3 + animation_nodes/nodes/mesh/c_utils.pyx | 22 ++++++- .../nodes/mesh/get_custom_attribute.py | 5 +- .../nodes/mesh/insert_custom_attribute.py | 10 +++- .../nodes/mesh/mesh_object_output.py | 2 +- .../nodes/mesh/set_custom_attribute.py | 11 +++- animation_nodes/sockets/integer2d.py | 60 +++++++++++++++++++ 16 files changed, 155 insertions(+), 11 deletions(-) create mode 100644 animation_nodes/sockets/integer2d.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 87db19502..eceb29a9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - Added Container Type advanced option to object instancer node. - Added Evaluate FCurves Transforms node. - Added *Lamp Input* and *Lamp Output* nodes. +- Added *Int2* list and socket type. ### Fixed @@ -32,6 +33,7 @@ - Fixed *Set Bevel Vertex Weight* and *Set Bevel Edge Weight* nodes for API changes. - Fixed *Set Keyframe* node failing when path contains a subscript. - Fixed symbol not found error on MacOS 10. +- Fixed Custom Attributes for new API changes. ### Changed diff --git a/animation_nodes/data_structures/__init__.py b/animation_nodes/data_structures/__init__.py index 1d5587652..d24e9f644 100644 --- a/animation_nodes/data_structures/__init__.py +++ b/animation_nodes/data_structures/__init__.py @@ -11,14 +11,14 @@ def importDataStructures(): from . lists.base_lists import ( Vector3DList, Vector2DList, Matrix4x4List, EdgeIndicesList, EulerList, ColorList, BooleanList, FloatList, DoubleList, LongList, IntegerList, UShortList, CharList, - QuaternionList, UIntegerList, ShortList, UShortList + QuaternionList, UIntegerList, ShortList, UShortList, Int2List ) from . virtual_list.virtual_list import VirtualList, VirtualPyList from . virtual_list.virtual_clists import ( VirtualVector3DList, VirtualMatrix4x4List, VirtualEulerList, VirtualBooleanList, VirtualFloatList, VirtualDoubleList, VirtualLongList, VirtualColorList, - VirtualVector2DList, VirtualQuaternionList + VirtualVector2DList, VirtualQuaternionList, VirtualInt2List ) from . splines.base_spline import Spline diff --git a/animation_nodes/data_structures/attributes/attribute.pxd b/animation_nodes/data_structures/attributes/attribute.pxd index a9779b7af..13ccfd47f 100644 --- a/animation_nodes/data_structures/attributes/attribute.pxd +++ b/animation_nodes/data_structures/attributes/attribute.pxd @@ -18,6 +18,7 @@ cpdef enum AttributeDomain: cpdef enum AttributeDataType: INT, + INT32_2D, FLOAT, FLOAT2, FLOAT_VECTOR, diff --git a/animation_nodes/data_structures/attributes/attribute.pyx b/animation_nodes/data_structures/attributes/attribute.pyx index 090b6e433..0c5de447e 100644 --- a/animation_nodes/data_structures/attributes/attribute.pyx +++ b/animation_nodes/data_structures/attributes/attribute.pyx @@ -6,10 +6,12 @@ from .. lists.base_lists cimport ( BooleanList, Vector2DList, Vector3DList, + Int2List, ) cListFromDataType = { INT: LongList, + INT32_2D: Int2List, FLOAT: FloatList, FLOAT2: Vector2DList, FLOAT_VECTOR: Vector3DList, @@ -38,6 +40,7 @@ stringFromDomain = { stringFromDataType = { INT: "INT", + INT32_2D: "INT32_2D", FLOAT: "FLOAT", FLOAT2: "FLOAT2", FLOAT_VECTOR: "FLOAT_VECTOR", diff --git a/animation_nodes/data_structures/lists/special_list_types.json b/animation_nodes/data_structures/lists/special_list_types.json index 4a3470dc9..8038a0191 100644 --- a/animation_nodes/data_structures/lists/special_list_types.json +++ b/animation_nodes/data_structures/lists/special_list_types.json @@ -91,5 +91,17 @@ "from ... math.color cimport Color", "from ... math.conversion cimport setColor, toPyColor, toColor" ] + }, + "Int2List" : { + "Type" : "Int2", + "Buffer Type" : "int", + "Equals" : "not memcmp(&(\\1), &(\\2), sizeof(Int2))", + "Try Conversion" : "setInt2(target, value)", + "To PyObject" : "toPyInt2(value)", + "Additional Methods" : "", + "Declarations" : [ + "from ... math.vector cimport Int2", + "from ... math.conversion cimport setInt2, toPyInt2" + ] } } diff --git a/animation_nodes/data_structures/virtual_list/virtual_clist_types.json b/animation_nodes/data_structures/virtual_list/virtual_clist_types.json index d88f27599..31bc8d33e 100644 --- a/animation_nodes/data_structures/virtual_list/virtual_clist_types.json +++ b/animation_nodes/data_structures/virtual_list/virtual_clist_types.json @@ -9,6 +9,11 @@ "Import" : "from ... math.vector cimport Vector2", "Return" : "Pointer" }, + "Int2List" : { + "Type" : "Int2", + "Import" : "from ... math.vector cimport Int2", + "Return" : "Pointer" + }, "Matrix4x4List" : { "Type" : "Matrix4", "Import" : "from ... math.matrix cimport Matrix4", diff --git a/animation_nodes/extend_bpy_types.py b/animation_nodes/extend_bpy_types.py index 566da47c2..2761f057a 100644 --- a/animation_nodes/extend_bpy_types.py +++ b/animation_nodes/extend_bpy_types.py @@ -5,7 +5,7 @@ from . utils.depsgraph import getActiveDepsgraph from . data_structures import (Vector3DList, EdgeIndicesList, PolygonIndicesList, FloatList, UShortList, UIntegerList, Vector2DList, - ColorList, DoubleList, LongList, BooleanList) + ColorList, DoubleList, LongList, BooleanList, Int2List) def register(): bpy.types.Context.getActiveAnimationNodeTree = getActiveAnimationNodeTree @@ -125,6 +125,8 @@ def getCustomAttribute(self, name): data = DoubleList(length = amount) elif attribute.data_type == "INT": data = LongList(length = amount) + elif attribute.data_type == "INT32_2D": + data = Int2List(length = amount) elif attribute.data_type == "FLOAT2": data = Vector2DList(length = amount) elif attribute.data_type == "FLOAT_VECTOR": @@ -134,7 +136,7 @@ def getCustomAttribute(self, name): else: data = BooleanList(length = amount) - if attribute.data_type in ("FLOAT", "INT", "BOOLEAN"): + if attribute.data_type in ("FLOAT", "INT", "INT32_2D", "BOOLEAN"): attribute.data.foreach_get("value", data.asNumpyArray()) elif attribute.data_type in ("FLOAT2", "FLOAT_VECTOR"): attribute.data.foreach_get("vector", data.asNumpyArray()) diff --git a/animation_nodes/math/conversion.pxd b/animation_nodes/math/conversion.pxd index 70771910b..df36678f8 100644 --- a/animation_nodes/math/conversion.pxd +++ b/animation_nodes/math/conversion.pxd @@ -1,7 +1,7 @@ from . color cimport Color from . euler cimport Euler3 from . quaternion cimport Quaternion -from . vector cimport Vector2, Vector3, Vector4 +from . vector cimport Vector2, Vector3, Vector4, Int2 from . matrix cimport Matrix3, Matrix4, Matrix3_or_Matrix4 cdef Matrix4 toMatrix4(value) except * @@ -22,6 +22,10 @@ cdef Vector4 toVector4(value) except * cdef setVector4(Vector4* v, value) cdef toPyVector4(Vector4* v) +cdef Int2 toInt2(value) except * +cdef setInt2(Int2* v, value) +cdef toPyInt2(Int2* v) + cdef Euler3 toEuler3(value) except * cdef setEuler3(Euler3* e, value) cdef toPyEuler3(Euler3* e) diff --git a/animation_nodes/math/conversion.pyx b/animation_nodes/math/conversion.pyx index b8a45c640..c8cb7b64a 100644 --- a/animation_nodes/math/conversion.pyx +++ b/animation_nodes/math/conversion.pyx @@ -52,6 +52,20 @@ cdef setVector4(Vector4* v, value): cdef toPyVector4(Vector4* v): return Vector((v.x, v.y, v.z, v.w)) +cdef Int2 toInt2(value) except *: + cdef Int2 v + setInt2(&v, value) + return v + +cdef setInt2(Int2* v, value): + if len(value) != 2: + raise TypeError("element is not a 2D integer vector") + v.x = value[0] + v.y = value[1] + +cdef toPyInt2(Int2* v): + return (v.x, v.y) + # Matrices ########################################################## diff --git a/animation_nodes/math/vector.pxd b/animation_nodes/math/vector.pxd index 4263ad423..32e373c0b 100644 --- a/animation_nodes/math/vector.pxd +++ b/animation_nodes/math/vector.pxd @@ -1,3 +1,6 @@ +cdef struct Int2: + int x, y + cdef struct Vector2: float x, y diff --git a/animation_nodes/nodes/mesh/c_utils.pyx b/animation_nodes/nodes/mesh/c_utils.pyx index 00f1ac3ab..c91d0fc0e 100644 --- a/animation_nodes/nodes/mesh/c_utils.pyx +++ b/animation_nodes/nodes/mesh/c_utils.pyx @@ -17,7 +17,8 @@ from ... data_structures cimport ( Mesh, VirtualLongList, VirtualDoubleList, - EdgeIndices + EdgeIndices, + Int2List ) from ... math cimport ( @@ -707,3 +708,22 @@ def getReplicatedLoopEdges(UIntegerList loopEdges, Py_ssize_t amount, Py_ssize_t _newLoopEdges[index] = _loopEdges[j] + offset index += 1 return newLoopEdges + +# Conversion +################################### + +def convert_EdgeIndicesList_to_Int2List(EdgeIndicesList values): + cdef Py_ssize_t i + cdef Int2List int2D = Int2List(length = len(values)) + for i in range(len(values)): + int2D.data[i].x = values.data[i].v1 + int2D.data[i].y = values.data[i].v2 + return int2D + +def convert_Int2List_to_EdgeIndicesList(Int2List values): + cdef Py_ssize_t i + cdef EdgeIndicesList edges = EdgeIndicesList(length = len(values)) + for i in range(len(values)): + edges.data[i].v1 = values.data[i].x + edges.data[i].v2 = values.data[i].y + return edges diff --git a/animation_nodes/nodes/mesh/get_custom_attribute.py b/animation_nodes/nodes/mesh/get_custom_attribute.py index 3fd88b7f1..a2cc9d5bd 100644 --- a/animation_nodes/nodes/mesh/get_custom_attribute.py +++ b/animation_nodes/nodes/mesh/get_custom_attribute.py @@ -11,6 +11,7 @@ ("FLOAT_COLOR", "Color", "", "NONE", 4), ("BYTE_COLOR", "Byte Color", "", "NONE", 5), ("BOOLEAN", "Boolean", "", "NONE", 6), + ("INT32_2D", "Integer 2D", "", "NONE", 7), ] class GetCustomAttributeNode(AnimationNode, bpy.types.Node): @@ -35,8 +36,10 @@ def create(self): self.newOutput("Vector List", "Vectors", "data") elif self.dataType in ("FLOAT_COLOR", "BYTE_COLOR"): self.newOutput("Color List", "Colors", "data") - else: + elif self.dataType == "BOOLEAN": self.newOutput("Boolean List", "Values", "data") + else: + self.newOutput("Integer 2D List", "Values", "data") self.newOutput("Text", "Type", "type", hide = True) self.newOutput("Text", "Domain ", "domain", hide = True) self.newOutput("Text", "Data Type ", "dataType", hide = True) diff --git a/animation_nodes/nodes/mesh/insert_custom_attribute.py b/animation_nodes/nodes/mesh/insert_custom_attribute.py index 1057bc981..7dc9ef1ab 100644 --- a/animation_nodes/nodes/mesh/insert_custom_attribute.py +++ b/animation_nodes/nodes/mesh/insert_custom_attribute.py @@ -8,6 +8,7 @@ Attribute, AttributeType, AttributeDomain, + VirtualInt2List, VirtualLongList, VirtualColorList, AttributeDataType, @@ -32,6 +33,7 @@ ("FLOAT_COLOR", "Color", "", "NONE", 4), ("BYTE_COLOR", "Byte Color", "", "NONE", 5), ("BOOLEAN", "Boolean", "", "NONE", 6), + ("INT32_2D", "Integer 2D", "", "NONE", 7), ] class InsertCustomAttributeNode(AnimationNode, bpy.types.Node): @@ -65,9 +67,13 @@ def create(self): elif self.dataType in ("FLOAT_COLOR", "BYTE_COLOR"): self.newInput(VectorizedSocket("Color", "useDataList", ("Color", "data"), ("Colors", "data"))) - else: + elif self.dataType == "BOOLEAN": self.newInput(VectorizedSocket("Boolean", "useDataList", ("Value", "data"), ("Values", "data"))) + else: + self.newInput(VectorizedSocket("Integer 2D", "useDataList", + ("Value", "data"), ("Values", "data"))) + self.newOutput("Mesh", "Mesh", "mesh") @@ -89,6 +95,8 @@ def execute(self, mesh, customAttributeName, data): if self.dataType == "INT": _data = VirtualLongList.create(data, 0).materialize(amount) + elif self.dataType == "INT32_2D": + _data = VirtualInt2List.create(data, (0, 0)).materialize(amount) elif self.dataType == "FLOAT": _data = FloatList.fromValues(VirtualDoubleList.create(data, 0).materialize(amount)) elif self.dataType == "FLOAT2": diff --git a/animation_nodes/nodes/mesh/mesh_object_output.py b/animation_nodes/nodes/mesh/mesh_object_output.py index f31cb3d2c..a09926e78 100644 --- a/animation_nodes/nodes/mesh/mesh_object_output.py +++ b/animation_nodes/nodes/mesh/mesh_object_output.py @@ -171,7 +171,7 @@ def setMesh(self, outMesh, mesh, object): attributeOut = outMesh.attributes.new(attribute.name, dataType, domain) - if dataType in ("FLOAT", "INT", "BOOLEAN"): + if dataType in ("FLOAT", "INT", "INT32_2D", "BOOLEAN"): attributeOut.data.foreach_set("value", data.asMemoryView()) elif dataType in ("FLOAT2", "FLOAT_VECTOR"): attributeOut.data.foreach_set("vector", data.asMemoryView()) diff --git a/animation_nodes/nodes/mesh/set_custom_attribute.py b/animation_nodes/nodes/mesh/set_custom_attribute.py index f419ff4a1..8e3388fe1 100644 --- a/animation_nodes/nodes/mesh/set_custom_attribute.py +++ b/animation_nodes/nodes/mesh/set_custom_attribute.py @@ -5,6 +5,7 @@ from ... data_structures import ( Color, FloatList, + VirtualInt2List, VirtualLongList, VirtualColorList, VirtualDoubleList, @@ -28,6 +29,7 @@ ("FLOAT_COLOR", "Color", "", "NONE", 4), ("BYTE_COLOR", "Byte Color", "", "NONE", 5), ("BOOLEAN", "Boolean", "", "NONE", 6), + ("INT32_2D", "Integer 2D", "", "NONE", 7), ] class SetCustomAttributeNode(AnimationNode, bpy.types.Node): @@ -61,9 +63,12 @@ def create(self): elif self.dataType in ("FLOAT_COLOR", "BYTE_COLOR"): self.newInput(VectorizedSocket("Color", "useDataList", ("Color", "data"), ("Colors", "data"))) - else: + elif self.dataType == "BOOLEAN": self.newInput(VectorizedSocket("Boolean", "useDataList", ("Value", "data"), ("Values", "data"))) + else: + self.newInput(VectorizedSocket("Integer 2D", "useDataList", + ("Value", "data"), ("Values", "data"))) self.newOutput("Object", "Object", "object") @@ -94,6 +99,8 @@ def execute(self, object, customAttributeName, data): if self.dataType == "INT": _data = VirtualLongList.create(data, 0).materialize(amount) + elif self.dataType == "INT32_2D": + _data = VirtualInt2List.create(data, (0, 0)).materialize(amount) elif self.dataType == "FLOAT": _data = FloatList.fromValues(VirtualDoubleList.create(data, 0).materialize(amount)) elif attribute.data_type == "FLOAT2": @@ -105,7 +112,7 @@ def execute(self, object, customAttributeName, data): else: _data = VirtualBooleanList.create(data, False).materialize(amount) - if self.dataType in ("FLOAT", "INT", "BOOLEAN"): + if self.dataType in ("FLOAT", "INT", "INT32_2D", "BOOLEAN"): attribute.data.foreach_set("value", _data.asMemoryView()) elif self.dataType in ("FLOAT2", "FLOAT_VECTOR"): attribute.data.foreach_set("vector", _data.asMemoryView()) diff --git a/animation_nodes/sockets/integer2d.py b/animation_nodes/sockets/integer2d.py new file mode 100644 index 000000000..3725099ee --- /dev/null +++ b/animation_nodes/sockets/integer2d.py @@ -0,0 +1,60 @@ +import bpy +from bpy.props import * +from .. events import propertyChanged +from .. data_structures import Int2List +from .. base_types import AnimationNodeSocket, CListSocket +from . implicit_conversion import registerImplicitConversion + + +class Integer2DSocket(bpy.types.NodeSocket, AnimationNodeSocket): + bl_idname = "an_Integer2DSocket" + bl_label = "Integer 2D Socket" + dataType = "Integer 2D" + drawColor = (0.35, 0.7, 1.0, 1) + comparable = True + storable = True + + value: IntVectorProperty(default = [0, 0], size = 2, update = propertyChanged, subtype = "XYZ") + + def drawProperty(self, layout, text, node): + col = layout.column(align = True) + if text != "": col.label(text = text) + col.prop(self, "value", index = 0, text = "X") + col.prop(self, "value", index = 1, text = "Y") + + def getValue(self): + return tuple(self.value) + + def setProperty(self, data): + self.value = data + + @classmethod + def getDefaultValue(cls): + return (0, 0) + + @classmethod + def getDefaultValueCode(cls): + return "(0, 0)" + + @classmethod + def correctValue(cls, value): + if isinstance(value, tuple) and len(value) == 2: return value, 0 + elif isinstance(value, (list, set)) and len(value) == 2: return tuple(value), 1 + else: return cls.getDefaultValue(), 2 + +registerImplicitConversion("Edge Indices", "Integer 2D", "value") +registerImplicitConversion("Integer 2D", "Edge Indices", "value") + +class Integer2DListSocket(bpy.types.NodeSocket, CListSocket): + bl_idname = "an_Integer2DListSocket" + bl_label = "Integer 2D List Socket" + dataType = "Integer 2D List" + baseType = Integer2DSocket + drawColor = (0.35, 0.7, 1.0, 0.5) + storable = True + comparable = False + listClass = Int2List + +from .. nodes.mesh.c_utils import convert_EdgeIndicesList_to_Int2List, convert_Int2List_to_EdgeIndicesList +registerImplicitConversion("Edge Indices List", "Integer 2D List", convert_EdgeIndicesList_to_Int2List) +registerImplicitConversion("Integer 2D List", "Edge Indices List", convert_Int2List_to_EdgeIndicesList)