Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mesh custom attributes #1931

Merged
merged 16 commits into from
Dec 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

Expand Down
4 changes: 2 additions & 2 deletions animation_nodes/data_structures/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions animation_nodes/data_structures/attributes/attribute.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ cpdef enum AttributeDomain:

cpdef enum AttributeDataType:
INT,
INT32_2D,
FLOAT,
FLOAT2,
FLOAT_VECTOR,
Expand Down
3 changes: 3 additions & 0 deletions animation_nodes/data_structures/attributes/attribute.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -38,6 +40,7 @@ stringFromDomain = {

stringFromDataType = {
INT: "INT",
INT32_2D: "INT32_2D",
FLOAT: "FLOAT",
FLOAT2: "FLOAT2",
FLOAT_VECTOR: "FLOAT_VECTOR",
Expand Down
12 changes: 12 additions & 0 deletions animation_nodes/data_structures/lists/special_list_types.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
6 changes: 4 additions & 2 deletions animation_nodes/extend_bpy_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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":
Expand All @@ -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())
Expand Down
6 changes: 5 additions & 1 deletion animation_nodes/math/conversion.pxd
Original file line number Diff line number Diff line change
@@ -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 *
Expand All @@ -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)
Expand Down
14 changes: 14 additions & 0 deletions animation_nodes/math/conversion.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -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
##########################################################
Expand Down
3 changes: 3 additions & 0 deletions animation_nodes/math/vector.pxd
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
cdef struct Int2:
int x, y

cdef struct Vector2:
float x, y

Expand Down
22 changes: 21 additions & 1 deletion animation_nodes/nodes/mesh/c_utils.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ from ... data_structures cimport (
Mesh,
VirtualLongList,
VirtualDoubleList,
EdgeIndices
EdgeIndices,
Int2List
)

from ... math cimport (
Expand Down Expand Up @@ -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 = <int>values.data[i].v1
int2D.data[i].y = <int>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 = <unsigned int>values.data[i].x
edges.data[i].v2 = <unsigned int>values.data[i].y
return edges
5 changes: 4 additions & 1 deletion animation_nodes/nodes/mesh/get_custom_attribute.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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)
Expand Down
10 changes: 9 additions & 1 deletion animation_nodes/nodes/mesh/insert_custom_attribute.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
Attribute,
AttributeType,
AttributeDomain,
VirtualInt2List,
VirtualLongList,
VirtualColorList,
AttributeDataType,
Expand All @@ -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):
Expand Down Expand Up @@ -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")

Expand All @@ -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":
Expand Down
2 changes: 1 addition & 1 deletion animation_nodes/nodes/mesh/mesh_object_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
11 changes: 9 additions & 2 deletions animation_nodes/nodes/mesh/set_custom_attribute.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from ... data_structures import (
Color,
FloatList,
VirtualInt2List,
VirtualLongList,
VirtualColorList,
VirtualDoubleList,
Expand All @@ -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):
Expand Down Expand Up @@ -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")

Expand Down Expand Up @@ -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":
Expand All @@ -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())
Expand Down
60 changes: 60 additions & 0 deletions animation_nodes/sockets/integer2d.py
Original file line number Diff line number Diff line change
@@ -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)