├── _setuputils ├── __init__.py ├── setup_info_files.py ├── compile_libraries.py ├── copy_addon.py └── cythonize.py ├── animation_nodes ├── libs │ ├── __init__.py │ ├── midiparser │ │ └── __init__.py │ └── FastNoiseSIMD │ │ ├── source │ │ ├── .gitignore │ │ ├── README.md │ │ ├── compile_linux.sh │ │ ├── compile_macos.sh │ │ ├── compile_windows.bat │ │ └── LICENSE │ │ ├── __init__.pxd │ │ ├── __init__.py │ │ └── __setup_info.py ├── nodes │ ├── __init__.py │ ├── list │ │ ├── __init__.py │ │ ├── get_list_length.py │ │ ├── mask_list.py │ │ ├── shift_list.py │ │ └── reverse_list.py │ ├── mesh │ │ ├── __init__.py │ │ ├── generation │ │ │ ├── __init__.py │ │ │ ├── cylinder.py │ │ │ └── unity_triangle.py │ │ ├── join_mesh_data_list.py │ │ ├── bmesh_recalculate_face_normals.py │ │ ├── edges_of_polygons.py │ │ ├── bmesh_remove_doubles.py │ │ ├── bmesh_invert_normals.py │ │ ├── create_edges.py │ │ ├── get_uv_map.py │ │ ├── separate_polygons.py │ │ ├── bmesh_from_object.py │ │ ├── get_linked_vertices.py │ │ ├── get_vertex_group.py │ │ ├── triangulate_mesh.py │ │ └── offset_vertices.py │ ├── text │ │ ├── __init__.py │ │ ├── convert_to_text.py │ │ ├── join_texts.py │ │ ├── text_block_reader.py │ │ ├── text_length.py │ │ ├── text_block_writer.py │ │ ├── timecode.py │ │ ├── reverse_text.py │ │ ├── find_replace.py │ │ ├── repeat_text.py │ │ ├── random_text.py │ │ └── characters_input.py │ ├── action │ │ ├── __init__.py │ │ └── delay_action.pyx │ ├── animation │ │ ├── __init__.py │ │ ├── c_utils.pyx │ │ └── repeat_time.py │ ├── boolean │ │ ├── __init__.py │ │ ├── invert_node.py │ │ └── switch_node.py │ ├── bvh_tree │ │ ├── __init__.py │ │ └── is_inside_volume.py │ ├── color │ │ ├── __init__.py │ │ └── choose_color.py │ ├── falloff │ │ ├── __init__.py │ │ ├── invert_falloff.py │ │ ├── constant_falloff.pyx │ │ ├── custom_falloff.pyx │ │ └── random_falloff.pyx │ ├── fcurve │ │ ├── __init__.py │ │ ├── fcurves_from_object.py │ │ ├── fcurve_info.py │ │ ├── fcurve_keyframes.py │ │ └── evaluate_fcurve.py │ ├── generic │ │ ├── __init__.py │ │ └── blend_data_by_name.py │ ├── kd_tree │ │ ├── __init__.py │ │ ├── construct.py │ │ ├── find_points_in_radius.py │ │ ├── find_nearest_n_points.py │ │ └── find_nearest_point.py │ ├── material │ │ ├── __init__.py │ │ └── object_material_input.py │ ├── matrix │ │ ├── __init__.py │ │ ├── combine_matrices.py │ │ └── invert_matrix.py │ ├── number │ │ ├── __init__.py │ │ ├── round.py │ │ └── sort.py │ ├── numpy │ │ └── __init__.py │ ├── object │ │ ├── __init__.py │ │ ├── utility_nodes │ │ │ ├── __init__.py │ │ │ ├── get_active_camera.py │ │ │ ├── reset_transformation.py │ │ │ ├── move_object.py │ │ │ ├── get_selected_objects.py │ │ │ └── transform_object.py │ │ ├── evaluate_object.py │ │ ├── collection_info.py │ │ └── object_color_output.py │ ├── particles │ │ ├── __init__.py │ │ ├── particle_systems_from_object.py │ │ └── c_utils.pyx │ ├── rotation │ │ ├── __init__.py │ │ └── quaternion_list_combine.py │ ├── sequences │ │ ├── __init__.py │ │ ├── get_all_sequences.py │ │ └── sequences_from_channel.py │ ├── shape_key │ │ ├── __init__.py │ │ └── shape_keys_from_object.py │ ├── sound │ │ ├── __init__.py │ │ ├── midi_track_info.py │ │ ├── sound_from_sequence.py │ │ └── midi_file_reader.py │ ├── spline │ │ ├── __init__.py │ │ ├── connect_splines.py │ │ ├── make_spline_cyclic.py │ │ ├── spline_evaluation_base.py │ │ ├── smooth_bezier_spline.py │ │ ├── bevel_spline.py │ │ └── spline_from_gp_stroke.py │ ├── struct │ │ └── __init__.py │ ├── vector │ │ ├── __init__.py │ │ ├── vector_length.py │ │ ├── vector_from_value.py │ │ ├── vector_list_math.py │ │ ├── separate_vector.py │ │ ├── vector_dot_product.py │ │ └── vector_angle.py │ ├── viewer │ │ ├── __init__.py │ │ ├── matrix_shader.glsl │ │ └── interpolation_viewer.py │ ├── geometry │ │ ├── __init__.py │ │ ├── bmesh_limited_dissolve.py │ │ └── point_list_normal.py │ ├── gpencil │ │ └── __init__.py │ ├── interpolation │ │ ├── __init__.py │ │ ├── from_fcurve.py │ │ ├── evaluate.py │ │ └── mirror.py │ ├── simulation │ │ └── __init__.py │ ├── subprogram │ │ ├── __init__.py │ │ └── loop_break.py │ ├── texture │ │ ├── __init__.py │ │ └── c_utils.pyx │ └── container_provider.py ├── ui │ ├── __init__.py │ ├── info_popups.py │ ├── advanced_node_settings_panel.py │ ├── node_editor_hud.py │ ├── selection_pie.py │ ├── header.py │ ├── node_popup.py │ └── auto_nodetree_selection.py ├── utils │ ├── __init__.py │ ├── limits.pxd │ ├── scene.py │ ├── pointers.pxd │ ├── clamp.pxd │ ├── objects.py │ ├── lists.pxd │ ├── unicode.py │ ├── pointers.pyx │ ├── depsgraph.py │ ├── limits.pyx │ ├── clamp.pyx │ ├── code.py │ ├── pretty_strings.py │ ├── data_blocks.py │ ├── animation.py │ ├── timing.py │ ├── sequence_editor.py │ ├── enum_items.py │ ├── recursion.py │ ├── layout.py │ ├── bvh.py │ ├── lists.pyx │ └── selection.py ├── algorithms │ ├── __init__.py │ ├── mesh │ │ └── __init__.py │ ├── spline │ │ └── __init__.py │ ├── mesh_generation │ │ └── __init__.py │ ├── lsystem │ │ ├── __init__.py │ │ ├── geometry.pxd │ │ ├── parsing.pxd │ │ ├── apply_rules.pxd │ │ └── py_interface.pyx │ ├── perlin_noise.pxd │ ├── hashing │ │ ├── __init__.py │ │ ├── murmurhash3.pxd │ │ ├── utils.pyx │ │ └── test_murmurhash3.py │ ├── interpolations │ │ └── __init__.py │ ├── matrices │ │ ├── __init__.py │ │ ├── scale.pxd │ │ ├── rotation.pxd │ │ └── translation.pxd │ ├── random_number_generators │ │ ├── utils.pxd │ │ ├── splitmix64.pxd │ │ ├── __init__.pxd │ │ ├── xoshiro256plus.pxd │ │ ├── splitmix64.pyx │ │ └── xoshiro256starstar.pxd │ ├── rotations │ │ ├── __init__.py │ │ └── rotation_and_direction.pxd │ └── lists │ │ ├── sort.py │ │ ├── __init__.py │ │ └── reverse.pyx ├── execution │ ├── __init__.py │ ├── cache.py │ ├── auto_execution.py │ └── compile_scripts.py ├── operators │ ├── __init__.py │ ├── node_creators │ │ ├── __init__.py │ │ ├── insert_invoke_node.py │ │ └── insert_loop_for_iteration.py │ ├── an_operator.py │ ├── tag_retry_execution.py │ ├── deactivate_auto_execution.py │ ├── copy_nodetree.py │ ├── path_chooser.py │ ├── replace_node_with_copies.py │ ├── switch_tree.py │ ├── remove_nodetree.py │ ├── execute_tree.py │ ├── generate_socket_data_csv.py │ ├── choose_id_key.py │ └── rename_datablock.py ├── sockets │ ├── __init__.py │ ├── kd_tree.py │ ├── bmesh.py │ ├── implicit_conversion.py │ ├── node_control.py │ ├── matrix.py │ ├── edge_indices.py │ └── generic.py ├── data_structures │ ├── midi │ │ └── __init__.py │ ├── falloffs │ │ ├── __init__.py │ │ ├── evaluation.pxd │ │ ├── falloff_base.pxd │ │ └── types.pxd │ ├── gpencils │ │ ├── __init__.py │ │ └── gp_frame_data.py │ ├── meshes │ │ ├── __init__.py │ │ └── mesh_data.pxd │ ├── sounds │ │ ├── __init__.py │ │ └── sound_data.py │ ├── attributes │ │ ├── __init__.py │ │ └── attribute.pxd │ ├── default_lists │ │ ├── __init__.py │ │ ├── default_list.pxd │ │ ├── c_default_list.pxd │ │ └── default_list.pyx │ ├── lists │ │ ├── __init__.py │ │ ├── __color_list_functions.src │ │ ├── __euler_list_functions.src │ │ ├── .gitignore │ │ ├── __setup_info.py │ │ ├── clist.pxd │ │ ├── __convert.src │ │ ├── numeric_list_types.json │ │ ├── utils.pxd │ │ ├── __boolean_list_functions.src │ │ ├── __matrix_list_functions.src │ │ ├── __indices_list_functions.src │ │ ├── test_utils.py │ │ ├── polygon_indices_list.pxd │ │ └── _generate_convert_code.py │ ├── virtual_list │ │ ├── __init__.py │ │ ├── .gitignore │ │ ├── __setup_info.py │ │ ├── virtual_list.pxd │ │ └── __virtual_clist_declaration.src │ ├── action │ │ ├── .gitignore │ │ ├── __setup_info.py │ │ ├── __pxd_imports.src │ │ ├── __pyx_imports.src │ │ ├── action_channels.pxd │ │ ├── action_base.pxd │ │ ├── __init__.pxd │ │ ├── __init__.py │ │ └── __types_declaration.src │ ├── splines │ │ ├── __init__.py │ │ ├── poly_spline.pxd │ │ └── bezier_spline.pxd │ ├── interpolation.pxd │ ├── struct.py │ └── __init__.pxd ├── graphics │ ├── __init__.py │ └── import_shader.py ├── base_types │ ├── effects │ │ └── __init__.py │ ├── sockets │ │ └── __init__.py │ ├── nodes │ │ └── __init__.py │ ├── socket_templates │ │ ├── __init__.py │ │ └── base.py │ └── __init__.py ├── math │ ├── __init__.py │ ├── euler.pxd │ ├── euler.pyx │ ├── __init__.pxd │ ├── geometry.pxd │ ├── list_operations.pxd │ ├── quaternion.pxd │ ├── color.pxd │ ├── rotation_conversion.pxd │ ├── conversion.pxd │ └── number.pxd ├── test_compile.pyx ├── id_keys │ ├── data_types │ │ ├── float_type.py │ │ ├── __init__.py │ │ └── text_type.py │ ├── __init__.py │ └── id_keys_on_objects.py ├── tests.py └── draw_handler.py ├── .gitattributes ├── conf.default.json ├── .gitignore ├── .github └── stale.yml ├── README.md ├── license.txt └── _export_c_setup.py /_setuputils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/libs/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/nodes/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/ui/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/utils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/algorithms/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/execution/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/nodes/list/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/nodes/mesh/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/nodes/text/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/operators/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/sockets/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/algorithms/mesh/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/libs/midiparser/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/nodes/action/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/nodes/animation/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/nodes/boolean/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/nodes/bvh_tree/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/nodes/color/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/nodes/falloff/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/nodes/fcurve/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/nodes/generic/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/nodes/kd_tree/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/nodes/material/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/nodes/matrix/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/nodes/number/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/nodes/numpy/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/nodes/object/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/nodes/particles/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/nodes/rotation/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/nodes/sequences/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/nodes/shape_key/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/nodes/sound/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/nodes/spline/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/nodes/struct/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/nodes/vector/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/nodes/viewer/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/algorithms/spline/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/midi/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/nodes/geometry/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /animation_nodes/nodes/gpencil/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /animation_nodes/nodes/interpolation/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/nodes/simulation/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/nodes/subprogram/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/nodes/texture/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/falloffs/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/gpencils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/meshes/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/sounds/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/nodes/mesh/generation/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/operators/node_creators/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/algorithms/mesh_generation/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/attributes/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/default_lists/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/lists/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/virtual_list/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/nodes/object/utility_nodes/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/lists/__color_list_functions.src: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/lists/__euler_list_functions.src: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /animation_nodes/libs/FastNoiseSIMD/source/.gitignore: -------------------------------------------------------------------------------- 1 | !*.h 2 | !*.cpp 3 | -------------------------------------------------------------------------------- /animation_nodes/graphics/__init__.py: -------------------------------------------------------------------------------- 1 | from . rectangle import Rectangle 2 | -------------------------------------------------------------------------------- /animation_nodes/libs/FastNoiseSIMD/__init__.pxd: -------------------------------------------------------------------------------- 1 | from . wrapper cimport PyNoise 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /animation_nodes/algorithms/lsystem/__init__.py: -------------------------------------------------------------------------------- 1 | from . py_interface import calculateLSystem -------------------------------------------------------------------------------- /animation_nodes/data_structures/action/.gitignore: -------------------------------------------------------------------------------- 1 | action_types.pyx 2 | action_types.pxd 3 | -------------------------------------------------------------------------------- /animation_nodes/utils/limits.pxd: -------------------------------------------------------------------------------- 1 | cdef int INT_MIN, INT_MAX 2 | cdef long LONG_MIN, LONG_MAX 3 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/lists/.gitignore: -------------------------------------------------------------------------------- 1 | base_lists.pxd 2 | base_lists.pyx 3 | convert.pyx 4 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/virtual_list/.gitignore: -------------------------------------------------------------------------------- 1 | virtual_clists.pyx 2 | virtual_clists.pxd 3 | -------------------------------------------------------------------------------- /animation_nodes/utils/scene.py: -------------------------------------------------------------------------------- 1 | def getFPS(scene): 2 | return scene.render.fps / scene.render.fps_base 3 | -------------------------------------------------------------------------------- /animation_nodes/algorithms/perlin_noise.pxd: -------------------------------------------------------------------------------- 1 | cpdef double perlinNoise1D(double x, double persistance, int octaves) 2 | -------------------------------------------------------------------------------- /animation_nodes/utils/pointers.pxd: -------------------------------------------------------------------------------- 1 | cdef void *intToPointer(number) 2 | cdef object pointerToInt(void *pointer) 3 | -------------------------------------------------------------------------------- /animation_nodes/algorithms/hashing/__init__.py: -------------------------------------------------------------------------------- 1 | from . murmurhash3 import strToInt 2 | from . utils import strToEnumItemID 3 | -------------------------------------------------------------------------------- /animation_nodes/libs/FastNoiseSIMD/__init__.py: -------------------------------------------------------------------------------- 1 | from . node_base import Noise3DNodeBase 2 | from . wrapper import PyNoise 3 | -------------------------------------------------------------------------------- /animation_nodes/execution/cache.py: -------------------------------------------------------------------------------- 1 | from .. utils import fcurve 2 | 3 | def clearExecutionCache(): 4 | fcurve.clearCache() 5 | -------------------------------------------------------------------------------- /animation_nodes/algorithms/interpolations/__init__.py: -------------------------------------------------------------------------------- 1 | from . implementations import * 2 | from . presets import getInterpolationPreset 3 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/default_lists/default_list.pxd: -------------------------------------------------------------------------------- 1 | cdef class DefaultList: 2 | cdef Py_ssize_t getRealLength(self) 3 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/splines/__init__.py: -------------------------------------------------------------------------------- 1 | from . poly_spline import PolySpline 2 | from . bezier_spline import BezierSpline 3 | -------------------------------------------------------------------------------- /conf.default.json: -------------------------------------------------------------------------------- 1 | { 2 | "Copy Target" : "C:\\Users\\USERNAME\\AppData\\Roaming\\Blender Foundation\\Blender\\2.79\\scripts\\addons" 3 | } 4 | -------------------------------------------------------------------------------- /animation_nodes/base_types/effects/__init__.py: -------------------------------------------------------------------------------- 1 | from . code_effects import VectorizeCodeEffect, PrependCodeEffect, ReturnDefaultsOnExceptionCodeEffect 2 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/action/__setup_info.py: -------------------------------------------------------------------------------- 1 | def getPyPreprocessorProviders(): 2 | return [ 3 | "_generate_action_types.py" 4 | ] 5 | -------------------------------------------------------------------------------- /animation_nodes/math/__init__.py: -------------------------------------------------------------------------------- 1 | from . geometry import * 2 | from . conversion import * 3 | from . list_operations import * 4 | from . rotation_conversion import * 5 | -------------------------------------------------------------------------------- /animation_nodes/base_types/sockets/__init__.py: -------------------------------------------------------------------------------- 1 | from . base_socket import AnimationNodeSocket 2 | from . list_sockets import ListSocket, PythonListSocket, CListSocket 3 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/virtual_list/__setup_info.py: -------------------------------------------------------------------------------- 1 | def getPyPreprocessorProviders(): 2 | return [ 3 | "_generate_virtual_clist_code.py", 4 | ] 5 | -------------------------------------------------------------------------------- /animation_nodes/algorithms/lsystem/geometry.pxd: -------------------------------------------------------------------------------- 1 | from . symbol_string cimport * 2 | 3 | cdef geometryFromSymbolString(SymbolString symbols, Py_ssize_t seed = ?, dict defaults = ?) -------------------------------------------------------------------------------- /animation_nodes/utils/clamp.pxd: -------------------------------------------------------------------------------- 1 | cdef int clampInt(object value) 2 | cdef long clampLong(object value) 3 | cdef double clamp(double value, double minValue, double maxValue) 4 | -------------------------------------------------------------------------------- /animation_nodes/algorithms/matrices/__init__.py: -------------------------------------------------------------------------------- 1 | from . translation import translateMatrixList 2 | from . rotation import getRotatedMatrixList 3 | from . scale import scaleMatrixList 4 | -------------------------------------------------------------------------------- /animation_nodes/libs/FastNoiseSIMD/__setup_info.py: -------------------------------------------------------------------------------- 1 | def getLibraryCompilationProviders(): 2 | return ["wrapper_setup_info.py"] 3 | 4 | def getIncludeDirs(): 5 | return ["source"] 6 | -------------------------------------------------------------------------------- /animation_nodes/test_compile.pyx: -------------------------------------------------------------------------------- 1 | ''' 2 | This module has no content. It just exists to have an easy way to check if 3 | the user uses an compiled or uncompiled version of the addon. 4 | ''' 5 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/lists/__setup_info.py: -------------------------------------------------------------------------------- 1 | def getPyPreprocessorProviders(): 2 | return [ 3 | "_generate_list_code.py", 4 | "_generate_convert_code.py" 5 | ] 6 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/sounds/sound_data.py: -------------------------------------------------------------------------------- 1 | class SoundData: 2 | def __init__(self, samples, sampleRate): 3 | self.samples = samples 4 | self.sampleRate = sampleRate 5 | -------------------------------------------------------------------------------- /animation_nodes/math/euler.pxd: -------------------------------------------------------------------------------- 1 | cdef struct Euler3: 2 | float x, y, z 3 | char order 4 | # char[3] pad 5 | 6 | cdef void mixEul3(Euler3* target, Euler3* a, Euler3* b, float factor) 7 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/virtual_list/virtual_list.pxd: -------------------------------------------------------------------------------- 1 | from .. lists.clist cimport CList 2 | 3 | cdef class VirtualList: 4 | pass 5 | 6 | cdef class VirtualPyList(VirtualList): 7 | pass 8 | -------------------------------------------------------------------------------- /animation_nodes/utils/objects.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | def enterObjectMode(): 4 | if getattr(bpy.context.active_object, "mode", "OBJECT") != "OBJECT": 5 | bpy.ops.object.mode_set(mode = "OBJECT") -------------------------------------------------------------------------------- /animation_nodes/algorithms/random_number_generators/utils.pxd: -------------------------------------------------------------------------------- 1 | from libc.stdint cimport uint64_t 2 | 3 | cdef inline uint64_t rotl(uint64_t x, uint64_t k): 4 | return (x << k) | (x >> (64 - k)) 5 | -------------------------------------------------------------------------------- /animation_nodes/algorithms/rotations/__init__.py: -------------------------------------------------------------------------------- 1 | from . rotation_and_direction import (eulerToDirection, eulersToDirections, 2 | directionToMatrix, directionsToMatrices) 3 | -------------------------------------------------------------------------------- /animation_nodes/utils/lists.pxd: -------------------------------------------------------------------------------- 1 | cpdef findListSegment(long amount, bint cyclic, float parameter) 2 | cdef void findListSegment_LowLevel(long amount, bint cyclic, float parameter, long* index, float* factor) 3 | -------------------------------------------------------------------------------- /animation_nodes/utils/unicode.py: -------------------------------------------------------------------------------- 1 | # only use these two functions together 2 | def toValidString(text): 3 | return str(text.encode()) 4 | 5 | def fromValidString(text): 6 | return eval(text).decode() 7 | -------------------------------------------------------------------------------- /animation_nodes/algorithms/hashing/murmurhash3.pxd: -------------------------------------------------------------------------------- 1 | from libc.stdint cimport uint8_t, uint32_t 2 | 3 | cpdef uint32_t strToInt(str text, uint32_t seed = ?) 4 | cdef uint32_t murmur3_32(char* key, uint32_t len, uint32_t seed) 5 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/lists/clist.pxd: -------------------------------------------------------------------------------- 1 | cdef class CList: 2 | cdef void *getPointer(self) 3 | cdef int getElementSize(self) 4 | cdef Py_ssize_t getLength(self) 5 | cdef Py_ssize_t getCapacity(self) 6 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/virtual_list/__virtual_clist_declaration.src: -------------------------------------------------------------------------------- 1 | cdef class VirtualLISTNAME(VirtualList): 2 | # Should only be called with a positiv index 3 | cdef TYPE OPTIONAL_STAR get(self, Py_ssize_t i) 4 | -------------------------------------------------------------------------------- /animation_nodes/libs/FastNoiseSIMD/source/README.md: -------------------------------------------------------------------------------- 1 | This is the FastNoiseSIMD library (with minor modifications) developed by Jordan Peck. 2 | 3 | The main repository of it can be found here: https://github.com/Auburns/FastNoiseSIMD 4 | -------------------------------------------------------------------------------- /animation_nodes/utils/pointers.pyx: -------------------------------------------------------------------------------- 1 | from libc.stdint cimport intptr_t 2 | 3 | cdef void *intToPointer(number): 4 | return number 5 | 6 | cdef object pointerToInt(void *pointer): 7 | return pointer 8 | -------------------------------------------------------------------------------- /animation_nodes/base_types/nodes/__init__.py: -------------------------------------------------------------------------------- 1 | from . base_node import AnimationNode 2 | 3 | from . node_ui_extension import ( 4 | NodeUIExtension, 5 | InterpolationUIExtension, 6 | ErrorUIExtension, 7 | TextUIExtension 8 | ) 9 | -------------------------------------------------------------------------------- /animation_nodes/algorithms/lists/sort.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | def naturalSortKey(text): 4 | return [_convert(c) for c in re.split('([0-9]+)', text)] 5 | 6 | def _convert(text): 7 | return text.zfill(10) if text.isdigit() else text.lower() 8 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/action/__pxd_imports.src: -------------------------------------------------------------------------------- 1 | from . action_base cimport * 2 | from .. lists.base_lists cimport FloatList, IntegerList 3 | 4 | ctypedef void (*EvaluateFunction)(void *self, float frame, Py_ssize_t index, float *target) 5 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/action/__pyx_imports.src: -------------------------------------------------------------------------------- 1 | from cpython cimport PyMem_Malloc, PyMem_Free 2 | from ... graphics import Rectangle 3 | 4 | cdef class EvaluateFunctionData: 5 | cdef EvaluateFunction function 6 | cdef list channels 7 | -------------------------------------------------------------------------------- /animation_nodes/algorithms/random_number_generators/splitmix64.pxd: -------------------------------------------------------------------------------- 1 | from libc.stdint cimport uint64_t 2 | 3 | # Based on http://prng.di.unimi.it/splitmix64.c 4 | 5 | cdef class SplitMix64: 6 | cdef uint64_t x 7 | 8 | cdef uint64_t nextUInt64(self) 9 | -------------------------------------------------------------------------------- /animation_nodes/algorithms/hashing/utils.pyx: -------------------------------------------------------------------------------- 1 | from . murmurhash3 cimport strToInt 2 | 3 | cpdef int strToEnumItemID(str text): 4 | # not sure why but Blender has problems 5 | # when the unique number is too large 6 | return strToInt(text) % 10000000 7 | -------------------------------------------------------------------------------- /animation_nodes/algorithms/lsystem/parsing.pxd: -------------------------------------------------------------------------------- 1 | from . rule cimport Rule, RuleSet 2 | from . symbol_string cimport SymbolString 3 | 4 | cdef SymbolString parseSymbolString(str source, dict defaults) except * 5 | cdef RuleSet parseRuleSet(ruleStrings, defaults) except * -------------------------------------------------------------------------------- /animation_nodes/operators/an_operator.py: -------------------------------------------------------------------------------- 1 | class AnimationNodeOperator: 2 | @classmethod 3 | def poll(cls, context): 4 | if not hasattr(context, "active_node"): return False 5 | return getattr(context.active_node, "isAnimationNode", False) 6 | -------------------------------------------------------------------------------- /animation_nodes/base_types/socket_templates/__init__.py: -------------------------------------------------------------------------------- 1 | from . base import SocketTemplate 2 | from . data_type_selector import DataTypeSelectorSocket 3 | from . list_type_selector import ListTypeSelectorSocket 4 | from . vectorization_selector import VectorizedSocket 5 | -------------------------------------------------------------------------------- /animation_nodes/algorithms/lsystem/apply_rules.pxd: -------------------------------------------------------------------------------- 1 | from . rule cimport * 2 | from . symbol_string cimport * 3 | 4 | cdef SymbolString applyGrammarRules(SymbolString axiom, RuleSet rules, float generations, 5 | int seed = ?, bint onlyPartialMoves = ?, symbolLimit = ?) except * -------------------------------------------------------------------------------- /animation_nodes/algorithms/random_number_generators/__init__.pxd: -------------------------------------------------------------------------------- 1 | from . splitmix64 cimport ( 2 | SplitMix64 3 | ) 4 | 5 | from . xoshiro256starstar cimport ( 6 | XoShiRo256StarStar 7 | ) 8 | 9 | from . xoshiro256plus cimport ( 10 | XoShiRo256Plus 11 | ) 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.pyd 3 | *.py~ 4 | *.c 5 | *.h 6 | *.cpp 7 | *.exp 8 | *.lib 9 | *.obj 10 | *.html 11 | *.so 12 | *.zip 13 | *.a 14 | *.o 15 | .vscode 16 | build/ 17 | venv/ 18 | conf.json 19 | animation_nodes/compilation_info.json 20 | setup_summary.json 21 | tags 22 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/lists/__convert.src: -------------------------------------------------------------------------------- 1 | def toTARGETLIST(NumericList sourceList): 2 | cdef TARGETLIST newList = TARGETLIST(sourceList.length) 3 | cdef Py_ssize_t i 4 | for i in range(sourceList.length): 5 | newList.data[i] = sourceList.data[i] 6 | return newList 7 | -------------------------------------------------------------------------------- /animation_nodes/math/euler.pyx: -------------------------------------------------------------------------------- 1 | from . number cimport lerpFloat 2 | 3 | cdef void mixEul3(Euler3* target, Euler3* a, Euler3* b, float factor): 4 | target.x = lerpFloat(a.x, b.x, factor) 5 | target.y = lerpFloat(a.y, b.y, factor) 6 | target.z = lerpFloat(a.z, b.z, factor) 7 | target.order = a.order 8 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/action/action_channels.pxd: -------------------------------------------------------------------------------- 1 | from . action_base cimport ActionChannel 2 | 3 | cdef class PathActionChannel(ActionChannel): 4 | cdef readonly str path 5 | 6 | cdef class PathIndexActionChannel(ActionChannel): 7 | cdef readonly str property 8 | cdef readonly Py_ssize_t index 9 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/interpolation.pxd: -------------------------------------------------------------------------------- 1 | from . lists.base_lists cimport DoubleList 2 | 3 | ctypedef double (*InterpolationFunction)(Interpolation, double) 4 | 5 | cdef class Interpolation: 6 | cdef bint clamped 7 | cdef double evaluate(self, double x) 8 | cdef double derivative(self, double x) 9 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/action/action_base.pxd: -------------------------------------------------------------------------------- 1 | cdef class Action: 2 | cdef set getChannelSet(self) 3 | 4 | cdef class ActionEvaluator: 5 | cdef Py_ssize_t channelAmount 6 | 7 | cdef void evaluate(self, float frame, Py_ssize_t index, float *target) 8 | 9 | cdef class ActionChannel: 10 | pass 11 | -------------------------------------------------------------------------------- /animation_nodes/utils/depsgraph.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from .. import events 3 | 4 | def getActiveDepsgraph(): 5 | if events.evaluatedDepsgraph: return events.evaluatedDepsgraph 6 | return bpy.context.evaluated_depsgraph_get() 7 | 8 | def getEvaluatedID(idObject): 9 | return idObject.evaluated_get(getActiveDepsgraph()) 10 | -------------------------------------------------------------------------------- /animation_nodes/math/__init__.pxd: -------------------------------------------------------------------------------- 1 | from . color cimport * 2 | from . euler cimport * 3 | from . vector cimport * 4 | from . matrix cimport * 5 | from . number cimport * 6 | from . geometry cimport * 7 | from . quaternion cimport * 8 | from . conversion cimport * 9 | from . list_operations cimport * 10 | from . rotation_conversion cimport * 11 | -------------------------------------------------------------------------------- /animation_nodes/utils/limits.pyx: -------------------------------------------------------------------------------- 1 | def calcMin(bytes): 2 | return - 2 ** (bytes * 8 - 1) 3 | 4 | def calcMax(bytes): 5 | return 2 ** (bytes * 8 - 1) - 1 6 | 7 | cdef int INT_MIN = calcMin(sizeof(int)) 8 | cdef int INT_MAX = calcMax(sizeof(int)) 9 | 10 | cdef long LONG_MIN = calcMin(sizeof(long)) 11 | cdef long LONG_MAX = calcMax(sizeof(long)) 12 | -------------------------------------------------------------------------------- /animation_nodes/ui/info_popups.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | def showTextPopup(text, title = "", icon = "NONE"): 4 | bpy.context.window_manager.popup_menu(getPopupDrawer(text), title = title, icon = icon) 5 | 6 | def getPopupDrawer(text): 7 | def drawPopup(menu, context): 8 | layout = menu.layout 9 | layout.label(text = text) 10 | return drawPopup 11 | -------------------------------------------------------------------------------- /animation_nodes/math/geometry.pxd: -------------------------------------------------------------------------------- 1 | from . vector cimport Vector3 2 | 3 | cdef float findNearestLineParameter(Vector3* lineStart, Vector3* lineDirection, Vector3* point) 4 | cdef double signedDistancePointToPlane_Normalized(Vector3* planePoint, Vector3* normalizedPlaneNormal, Vector3* point) 5 | cdef double distancePointToPlane(Vector3* planePoint, Vector3* planeNormal, Vector3* point) 6 | -------------------------------------------------------------------------------- /animation_nodes/algorithms/rotations/rotation_and_direction.pxd: -------------------------------------------------------------------------------- 1 | from ... math cimport Euler3, Vector3, Matrix4 2 | 3 | cdef eulerToDirection_LowLevel(Vector3* target, Euler3* rotation, char axis) 4 | 5 | cdef directionToMatrix_LowLevel(Matrix4* target, 6 | Vector3* direction, Vector3* guide, 7 | char trackAxis, char guideAxis) 8 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/splines/poly_spline.pxd: -------------------------------------------------------------------------------- 1 | from ... math cimport Vector3 2 | from . base_spline cimport Spline 3 | from .. lists.base_lists cimport FloatList, Vector3DList 4 | 5 | cdef class PolySpline(Spline): 6 | cdef: 7 | public Vector3DList points 8 | public FloatList radii 9 | public FloatList tilts 10 | Vector3DList normalsCache 11 | -------------------------------------------------------------------------------- /_setuputils/setup_info_files.py: -------------------------------------------------------------------------------- 1 | from . generic import * 2 | 3 | def getSetupInfoList(addonDirectory): 4 | setupInfoList = [] 5 | for path in iterSetupInfoPaths(addonDirectory): 6 | setupInfoList.append(executePythonFile(path)) 7 | return setupInfoList 8 | 9 | def iterSetupInfoPaths(addonDirectory): 10 | return iterPathsWithFileName(addonDirectory, "__setup_info.py") 11 | -------------------------------------------------------------------------------- /animation_nodes/graphics/import_shader.py: -------------------------------------------------------------------------------- 1 | import gpu 2 | 3 | def getShader(path): 4 | with open(path) as f: 5 | lines = f.readlines() 6 | for i, line in enumerate(lines): 7 | if "Fragment Shader" in line: 8 | index = i 9 | break 10 | return gpu.types.GPUShader("\n".join(lines[:i]), "\n".join(["\n"] * index + lines[i:])) 11 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/falloffs/evaluation.pxd: -------------------------------------------------------------------------------- 1 | cdef class FalloffEvaluator: 2 | cdef str sourceType 3 | 4 | cdef float evaluate(self, void *value, Py_ssize_t index) 5 | cdef pyEvaluate(self, object value, Py_ssize_t index) 6 | 7 | cdef void evaluateList_LowLevel(self, void *values, Py_ssize_t startIndex, 8 | Py_ssize_t amount, float *target) 9 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | daysUntilStale: 30 2 | 3 | daysUntilClose: 7 4 | 5 | exemptLabels: 6 | - bug 7 | - proposal 8 | - pending 9 | - to-do 10 | 11 | staleLabel: stale 12 | 13 | markComment: > 14 | This issue has been automatically marked as stale because it has not had 15 | recent activity. It will be closed if no further activity occurs. Thank you 16 | for your contributions. 17 | 18 | -------------------------------------------------------------------------------- /animation_nodes/utils/clamp.pyx: -------------------------------------------------------------------------------- 1 | from . limits cimport INT_MAX, INT_MIN, LONG_MAX, LONG_MIN 2 | 3 | cdef int clampInt(object value): 4 | return max(INT_MIN, min(INT_MAX, value)) 5 | 6 | cdef long clampLong(object value): 7 | return max(LONG_MIN, min(LONG_MAX, value)) 8 | 9 | cdef double clamp(double value, double minValue, double maxValue): 10 | return min(max(value, minValue), maxValue) 11 | -------------------------------------------------------------------------------- /animation_nodes/libs/FastNoiseSIMD/source/compile_linux.sh: -------------------------------------------------------------------------------- 1 | gcc -c FastNoiseSIMD.cpp -std=c++11 -fPIC -O3 2 | gcc -c FastNoiseSIMD_internal.cpp -std=c++11 -fPIC -O3 3 | gcc -c FastNoiseSIMD_sse2.cpp -std=c++11 -fPIC -O3 -msse2 4 | gcc -c FastNoiseSIMD_sse41.cpp -std=c++11 -fPIC -O3 -msse4.1 5 | gcc -c FastNoiseSIMD_avx2.cpp -std=c++11 -fPIC -O3 -march=core-avx2 6 | ar rcs libFastNoiseSIMD_linux.a *.o 7 | 8 | echo "Done." 9 | -------------------------------------------------------------------------------- /animation_nodes/nodes/animation/c_utils.pyx: -------------------------------------------------------------------------------- 1 | from ... data_structures cimport ( 2 | DoubleList, 3 | VirtualDoubleList 4 | ) 5 | 6 | def executeSubtract_A_B(VirtualDoubleList a, VirtualDoubleList b, long amount): 7 | cdef DoubleList result = DoubleList(length = amount) 8 | cdef Py_ssize_t i 9 | for i in range(amount): 10 | result.data[i] = a.get(i) - b.get(i) 11 | return result 12 | 13 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/action/__init__.pxd: -------------------------------------------------------------------------------- 1 | from . action_base cimport ( 2 | Action, ActionEvaluator, ActionChannel 3 | ) 4 | 5 | from . action_channels cimport ( 6 | PathActionChannel, PathIndexActionChannel 7 | ) 8 | 9 | from . action_types cimport ( 10 | BoundedAction, UnboundedAction, 11 | BoundedActionEvaluator, UnboundedActionEvaluator, 12 | SimpleBoundedAction, SimpleUnboundedAction 13 | ) 14 | -------------------------------------------------------------------------------- /animation_nodes/utils/code.py: -------------------------------------------------------------------------------- 1 | import re 2 | import ast 3 | import sys 4 | 5 | def isCodeValid(code): 6 | return getSyntaxError(code) is None 7 | 8 | def getSyntaxError(code): 9 | try: 10 | ast.parse(code) 11 | return None 12 | except SyntaxError as e: 13 | return e 14 | 15 | def containsStarImport(code): 16 | match = re.search("import\s*\*", code) 17 | return match is not None 18 | -------------------------------------------------------------------------------- /animation_nodes/utils/pretty_strings.py: -------------------------------------------------------------------------------- 1 | def formatVector(vector): 2 | return "V({:>7.3f}, {:>7.3f}, {:>7.3f})".format(*vector) 3 | 4 | def formatEuler(euler): 5 | return "E({:>7.3f}, {:>7.3f}, {:>7.3f})".format(*euler) 6 | 7 | def formatQuaternion(quaternion): 8 | return "Q({:>7.3f}, {:>7.3f}, {:>7.3f}, {:>7.3f})".format(*quaternion) 9 | 10 | def formatFloat(number): 11 | return "{:>8.3f}".format(number) 12 | -------------------------------------------------------------------------------- /animation_nodes/operators/tag_retry_execution.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from .. base_types.update_file import updateFile 3 | 4 | class TagRetryExecution(bpy.types.Operator): 5 | bl_idname = "an.tag_retry_execution" 6 | bl_label = "Tag retry execution" 7 | bl_description = "Rebuild internal node structures and check for problems again" 8 | 9 | def execute(self, context): 10 | updateFile() 11 | return {"FINISHED"} 12 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/action/__init__.py: -------------------------------------------------------------------------------- 1 | from . action_base import ( 2 | Action, ActionEvaluator, ActionChannel 3 | ) 4 | 5 | from . action_channels import ( 6 | PathActionChannel, PathIndexActionChannel 7 | ) 8 | 9 | from . action_types import ( 10 | BoundedAction, UnboundedAction, 11 | BoundedActionEvaluator, UnboundedActionEvaluator, 12 | SimpleBoundedAction, SimpleUnboundedAction, 13 | DelayAction 14 | ) 15 | -------------------------------------------------------------------------------- /animation_nodes/math/list_operations.pxd: -------------------------------------------------------------------------------- 1 | from . vector cimport Vector3 2 | from . matrix cimport Matrix4 3 | from .. data_structures.lists.base_lists cimport Vector3DList, EulerList, Matrix4x4List 4 | 5 | cpdef void transformVector3DList(Vector3DList vectors, matrix, bint ignoreTranslation = ?) 6 | cpdef double distanceSumOfVector3DList(Vector3DList vectors) 7 | 8 | cdef void mixVec3Arrays(Vector3* target, Vector3* a, Vector3* b, long arrayLength, float factor) 9 | -------------------------------------------------------------------------------- /animation_nodes/algorithms/lists/__init__.py: -------------------------------------------------------------------------------- 1 | from . shuffle import shuffle, getShuffleFunction 2 | from . reverse import reverse, getReverseFunction 3 | from . repeat import repeat, getRepeatFunction 4 | from . sample import sample, getSampleFunction 5 | from . fill import fill, getFillFunction 6 | from . mask import mask, getMaskFunction, mask_CList 7 | from . repeat_elements import repeatElements, getRepeatElementsFunction 8 | 9 | from . sort import naturalSortKey 10 | -------------------------------------------------------------------------------- /animation_nodes/id_keys/data_types/float_type.py: -------------------------------------------------------------------------------- 1 | from . base import SingleIDKeyDataType 2 | from ... data_structures import DoubleList 3 | 4 | class FloatDataType(SingleIDKeyDataType): 5 | identifier = "Float" 6 | default = 0.0 7 | 8 | @classmethod 9 | def getList(cls, objects, name): 10 | default = cls.default 11 | path = cls.getPath(name) 12 | return DoubleList.fromValues(getattr(object, path, default) for object in objects) 13 | -------------------------------------------------------------------------------- /animation_nodes/math/quaternion.pxd: -------------------------------------------------------------------------------- 1 | from . vector cimport Vector3 2 | 3 | ctypedef struct Quaternion: 4 | float x, y, z, w 5 | 6 | cdef void setUnitQuaternion(Quaternion *q) 7 | cdef void multQuat(Quaternion *target, Quaternion *q1, Quaternion *q2) 8 | cdef void rotVec3ByQuat(Vector3 *target, Vector3 *v, Quaternion *q) 9 | cdef void quaternionNormalize_InPlace(Quaternion *q) 10 | cdef void mixQuat(Quaternion* target, Quaternion* x, Quaternion* y, float factor) 11 | -------------------------------------------------------------------------------- /animation_nodes/nodes/text/convert_to_text.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode 3 | 4 | class ConvertToTextNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_ConvertToTextNode" 6 | bl_label = "Convert to Text" 7 | 8 | def create(self): 9 | self.newInput("Generic", "Data", "data") 10 | self.newOutput("Text", "Text", "text") 11 | 12 | def getExecutionCode(self, required): 13 | return "text = str(data)" 14 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/splines/bezier_spline.pxd: -------------------------------------------------------------------------------- 1 | from ... math cimport Vector3 2 | from . base_spline cimport Spline 3 | from .. lists.base_lists cimport Vector3DList, FloatList 4 | 5 | cdef class BezierSpline(Spline): 6 | cdef: 7 | public Vector3DList points 8 | public Vector3DList leftHandles 9 | public Vector3DList rightHandles 10 | public FloatList radii 11 | public FloatList tilts 12 | Vector3DList normalsCache 13 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/default_lists/c_default_list.pxd: -------------------------------------------------------------------------------- 1 | from . default_list cimport DefaultList 2 | from .. lists.clist cimport CList 3 | 4 | cdef class CDefaultList(DefaultList): 5 | cdef: 6 | object dataType 7 | CList realList 8 | CList defaultElementList 9 | 10 | char* arrayStart 11 | void* default 12 | Py_ssize_t realListLength 13 | Py_ssize_t elementSize 14 | 15 | cdef void* get(self, Py_ssize_t index) 16 | -------------------------------------------------------------------------------- /animation_nodes/algorithms/random_number_generators/xoshiro256plus.pxd: -------------------------------------------------------------------------------- 1 | from libc.stdint cimport uint64_t 2 | 3 | cdef class XoShiRo256Plus: 4 | cdef uint64_t s0 5 | cdef uint64_t s1 6 | cdef uint64_t s2 7 | cdef uint64_t s3 8 | 9 | cdef uint64_t nextUInt64(self) 10 | cdef double nextDouble(self) 11 | cdef double nextDoubleInRange(self, double start, double end) 12 | cdef float nextFloat(self) 13 | cdef float nextFloatInRange(self, float start, float end) 14 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/lists/numeric_list_types.json: -------------------------------------------------------------------------------- 1 | [ 2 | ["FloatList", "float"], 3 | ["DoubleList", "double"], 4 | ["CharList", "char"], ["UCharList", "unsigned char"], 5 | ["LongList", "long"], ["ULongList", "unsigned long"], 6 | ["IntegerList", "int"], ["UIntegerList", "unsigned int"], 7 | ["ShortList", "short"], ["UShortList", "unsigned short"], 8 | ["LongLongList", "long long"], ["ULongLongList", "unsigned long long"] 9 | ] 10 | -------------------------------------------------------------------------------- /animation_nodes/algorithms/matrices/scale.pxd: -------------------------------------------------------------------------------- 1 | from ... math cimport ( 2 | Matrix4, 3 | Vector3 4 | ) 5 | 6 | from ... data_structures cimport ( 7 | Matrix4x4List, 8 | FloatList, 9 | VirtualVector3DList 10 | ) 11 | 12 | ctypedef void (*ScaleFunction)(Matrix4 *m, Vector3 *v) 13 | 14 | cpdef scaleMatrixList(Matrix4x4List matrices, str type, 15 | VirtualVector3DList scales, FloatList influences) 16 | 17 | cdef ScaleFunction getScaleFunction(str type) except * 18 | -------------------------------------------------------------------------------- /animation_nodes/utils/data_blocks.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | collectionNameByType = { 4 | "MESH" : "meshes", 5 | "CAMERA" : "cameras", 6 | "FONT" : "curves", 7 | "CURVE" : "curves", 8 | "SURFACE" : "curves", 9 | "META" : "metaballs", 10 | "ARMATURE" : "armatures", 11 | "LATTICE" : "lattices", 12 | "LIGHT" : "lights", 13 | "SPEAKER" : "speakers" 14 | } 15 | 16 | def removeNotUsedDataBlock(data, type): 17 | getattr(bpy.data, collectionNameByType[type]).remove(data) 18 | -------------------------------------------------------------------------------- /animation_nodes/nodes/object/utility_nodes/get_active_camera.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from .... base_types import AnimationNode 3 | 4 | class GetActiveCameraNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_GetActiveCameraNode" 6 | bl_label = "Get Active Camera" 7 | 8 | def create(self): 9 | self.newInput("Scene", "Scene", "scene", hide = True) 10 | self.newOutput("Object", "Active Camera", "activeCamera") 11 | 12 | def execute(self, scene): 13 | return getattr(scene, "camera", None) 14 | -------------------------------------------------------------------------------- /animation_nodes/algorithms/matrices/rotation.pxd: -------------------------------------------------------------------------------- 1 | from ... math cimport ( 2 | Matrix4, 3 | Euler3 4 | ) 5 | 6 | from ... data_structures cimport ( 7 | Matrix4x4List, 8 | FloatList, 9 | VirtualEulerList 10 | ) 11 | 12 | ctypedef void (*RotateFunction)(Matrix4 *target, Matrix4 *m, Euler3 *v) 13 | 14 | cpdef getRotatedMatrixList(Matrix4x4List matrices, str type, 15 | VirtualEulerList rotations, FloatList influences) 16 | 17 | cdef RotateFunction getRotateFunction(str type) except * 18 | -------------------------------------------------------------------------------- /animation_nodes/nodes/text/join_texts.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode 3 | 4 | class JoinTextsNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_JoinTextsNode" 6 | bl_label = "Join Texts" 7 | 8 | def create(self): 9 | self.newInput("Text List", "Texts", "texts") 10 | self.newInput("Text", "Separator", "separator") 11 | self.newOutput("Text", "Text", "text") 12 | 13 | def getExecutionCode(self, required): 14 | return "text = separator.join(texts)" 15 | -------------------------------------------------------------------------------- /animation_nodes/algorithms/hashing/test_murmurhash3.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | from . murmurhash3 import strToInt 3 | 4 | class TestMurmurHash3(TestCase): 5 | def testNormal(self): 6 | self.assertEqual(strToInt(""), 0) 7 | self.assertEqual(strToInt("a"), 1009084850) 8 | self.assertEqual(strToInt("abc"), 3017643002) 9 | self.assertEqual(strToInt("This is a test."), 2040885872) 10 | 11 | def testSpecialCharacters(self): 12 | self.assertEqual(strToInt("äöü!§$%&/()[]?ß"), 4172822797) 13 | -------------------------------------------------------------------------------- /animation_nodes/algorithms/matrices/translation.pxd: -------------------------------------------------------------------------------- 1 | from ... math cimport ( 2 | Matrix4, 3 | Vector3 4 | ) 5 | 6 | from ... data_structures cimport ( 7 | Matrix4x4List, 8 | FloatList, 9 | VirtualVector3DList 10 | ) 11 | 12 | ctypedef void (*TranslationFunction)(Matrix4 *m, Vector3 *v) 13 | 14 | cpdef translateMatrixList(Matrix4x4List matrices, str type, 15 | VirtualVector3DList translations, FloatList influences) 16 | 17 | cdef TranslationFunction getTranslationFunction(str type) except * 18 | -------------------------------------------------------------------------------- /animation_nodes/operators/deactivate_auto_execution.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from .. utils.blender_ui import redrawAreaType 3 | from .. utils.nodes import getAnimationNodeTrees 4 | 5 | class DeactivateAutoExecution(bpy.types.Operator): 6 | bl_idname = "an.deactivate_auto_execution" 7 | bl_label = "Deactivate Auto Execution" 8 | 9 | def execute(self, context): 10 | for tree in getAnimationNodeTrees(): 11 | tree.autoExecution.enabled = False 12 | redrawAreaType("NODE_EDITOR") 13 | return {"FINISHED"} 14 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/default_lists/default_list.pyx: -------------------------------------------------------------------------------- 1 | cdef class DefaultList: 2 | cdef Py_ssize_t getRealLength(self): 3 | return 0 4 | 5 | @classmethod 6 | def getMaxLength(cls, *args): 7 | cdef: 8 | DefaultList defaultList 9 | Py_ssize_t maxLength = 0 10 | Py_ssize_t length 11 | 12 | for defaultList in args: 13 | length = defaultList.getRealLength() 14 | if length > maxLength: 15 | maxLength = length 16 | return maxLength 17 | -------------------------------------------------------------------------------- /animation_nodes/nodes/matrix/combine_matrices.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode 3 | from . c_utils import reduceMatrixList 4 | 5 | class MatrixCombineNode(AnimationNode, bpy.types.Node): 6 | bl_idname = "an_MatrixCombineNode" 7 | bl_label = "Combine Matrices" 8 | 9 | def create(self): 10 | self.newInput("Matrix List", "Matrices", "matrices") 11 | self.newOutput("Matrix", "Result", "result") 12 | 13 | def execute(self, matrices): 14 | return reduceMatrixList(matrices, reversed = True) 15 | -------------------------------------------------------------------------------- /animation_nodes/nodes/mesh/join_mesh_data_list.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... data_structures import Mesh 3 | from ... base_types import AnimationNode 4 | 5 | class JoinMeshListNode(AnimationNode, bpy.types.Node): 6 | bl_idname = "an_JoinMeshListNode" 7 | bl_label = "Join Mesh List" 8 | 9 | def create(self): 10 | self.newInput("Mesh List", "Mesh List", "meshDataList", dataIsModified = True) 11 | self.newOutput("Mesh", "Mesh", "meshData") 12 | 13 | def execute(self, meshDataList): 14 | return Mesh.join(*meshDataList) 15 | -------------------------------------------------------------------------------- /animation_nodes/nodes/text/text_block_reader.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode 3 | 4 | class TextBlockReaderNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_TextBlockReaderNode" 6 | bl_label = "Text Block Reader" 7 | 8 | def create(self): 9 | self.newInput("Text Block", "Text Block", "textBlock", defaultDrawType = "PROPERTY_ONLY") 10 | self.newOutput("Text", "Text", "text") 11 | 12 | def execute(self, textBlock): 13 | if textBlock is None: return "" 14 | else: return textBlock.as_string() 15 | -------------------------------------------------------------------------------- /animation_nodes/operators/copy_nodetree.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | class CopyNodeTree(bpy.types.Operator): 4 | bl_idname = "an.copy_node_tree" 5 | bl_label = "Copy Animation Node Tree" 6 | bl_description = "Copy the active animation node tree" 7 | 8 | @classmethod 9 | def poll(cls, context): 10 | return context.getActiveAnimationNodeTree() is not None 11 | 12 | def execute(self, context): 13 | tree = context.space_data.node_tree 14 | new = tree.copy() 15 | context.space_data.node_tree = new 16 | return {"FINISHED"} 17 | -------------------------------------------------------------------------------- /animation_nodes/operators/path_chooser.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.props import * 3 | 4 | class ChoosePath(bpy.types.Operator): 5 | bl_idname = "an.choose_path" 6 | bl_label = "Choose Path" 7 | 8 | filepath: StringProperty(subtype = "FILE_PATH") 9 | callback: StringProperty() 10 | 11 | def invoke(self, context, event): 12 | context.window_manager.fileselect_add(self) 13 | return {"RUNNING_MODAL"} 14 | 15 | def execute(self, context): 16 | self.an_executeCallback(self.callback, self.filepath) 17 | return {"FINISHED"} 18 | -------------------------------------------------------------------------------- /animation_nodes/algorithms/random_number_generators/splitmix64.pyx: -------------------------------------------------------------------------------- 1 | from libc.stdint cimport uint64_t 2 | 3 | # Based on http://prng.di.unimi.it/splitmix64.c 4 | 5 | cdef class SplitMix64: 6 | def __cinit__(self, uint64_t seed): 7 | self.x = seed 8 | 9 | cdef uint64_t nextUInt64(self): 10 | self.x += 0x9e3779b97f4a7c15 11 | cdef uint64_t z = self.x 12 | z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9 13 | z = (z ^ (z >> 27)) * 0x94d049bb133111eb 14 | return z ^ (z >> 31) 15 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/lists/utils.pxd: -------------------------------------------------------------------------------- 1 | cpdef Py_ssize_t predictSliceLength(Py_ssize_t start, Py_ssize_t end, Py_ssize_t step) 2 | cpdef makeStepPositive(Py_ssize_t start, Py_ssize_t stop, Py_ssize_t step) 3 | 4 | cdef removeValuesInSlice(char* arrayStart, Py_ssize_t arrayLength, Py_ssize_t elementSize, 5 | Py_ssize_t start, Py_ssize_t stop, Py_ssize_t step) 6 | 7 | cdef getValuesInSlice(void* source, Py_ssize_t elementAmount, int elementSize, 8 | void** target, Py_ssize_t* targetLength, 9 | sliceObject) 10 | -------------------------------------------------------------------------------- /animation_nodes/libs/FastNoiseSIMD/source/compile_macos.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | gcc -c FastNoiseSIMD.cpp -std=c++11 -fPIC -O3 5 | gcc -c FastNoiseSIMD_internal.cpp -std=c++11 -fPIC -O3 6 | 7 | if [ "$(arch)" == "arm64" ]; then 8 | gcc -c FastNoiseSIMD_neon.cpp -std=c++11 -fPIC -O3 9 | else 10 | gcc -c FastNoiseSIMD_sse2.cpp -std=c++11 -fPIC -O3 -msse2 11 | gcc -c FastNoiseSIMD_sse41.cpp -std=c++11 -fPIC -O3 -msse4.1 12 | gcc -c FastNoiseSIMD_avx2.cpp -std=c++11 -fPIC -O3 -march=core-avx2 13 | fi 14 | 15 | ar rcs libFastNoiseSIMD_macos.a *.o 16 | 17 | echo "Done." 18 | -------------------------------------------------------------------------------- /animation_nodes/operators/node_creators/insert_invoke_node.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.props import * 3 | from . node_creator import NodeCreator 4 | from ... tree_info import getNodeByIdentifier 5 | 6 | class InsertSubprogramInvokeNode(bpy.types.Operator, NodeCreator): 7 | bl_idname = "an.insert_invoke_subprogram_node" 8 | bl_label = "Insert Invoke Node" 9 | 10 | subprogramIdentifier: StringProperty() 11 | 12 | def insert(self): 13 | invokeNode = self.newNode("an_InvokeSubprogramNode") 14 | invokeNode.subprogramIdentifier = self.subprogramIdentifier 15 | -------------------------------------------------------------------------------- /animation_nodes/nodes/fcurve/fcurves_from_object.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode 3 | 4 | class FCurvesFromObjectNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_FCurvesFromObjectNode" 6 | bl_label = "FCurves from Object" 7 | 8 | def create(self): 9 | self.newInput("Object", "Object", "object", defaultDrawType = "PROPERTY_ONLY") 10 | self.newOutput("FCurve List", "FCurves", "fCurves") 11 | 12 | def execute(self, object): 13 | try: return list(object.animation_data.action.fcurves) 14 | except: return [] 15 | -------------------------------------------------------------------------------- /animation_nodes/nodes/spline/connect_splines.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode 3 | from ... data_structures.splines.connect import connectSplines 4 | 5 | class ConnectSplinesNode(AnimationNode, bpy.types.Node): 6 | bl_idname = "an_ConnectSplinesNode" 7 | bl_label = "Connect Splines" 8 | 9 | def create(self): 10 | self.newInput("Spline List", "Splines", "splines", defaultDrawType = "PROPERTY_ONLY") 11 | self.newOutput("Spline", "Spline", "spline") 12 | 13 | def execute(self, splines): 14 | return connectSplines(splines) 15 | -------------------------------------------------------------------------------- /animation_nodes/nodes/number/round.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode 3 | 4 | class RoundNumberNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_RoundNumberNode" 6 | bl_label = "Round Number" 7 | 8 | def create(self): 9 | self.newInput("Float", "Number", "number") 10 | self.newInput("Integer", "Decimals", "decimals") 11 | self.newOutput("Float", "Result", "result") 12 | 13 | def getExecutionCode(self, required): 14 | yield "result = int(round(number, decimals)) if decimals <= 0 else round(number, decimals)" 15 | -------------------------------------------------------------------------------- /animation_nodes/nodes/matrix/invert_matrix.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode 3 | 4 | class InvertMatrixNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_InvertMatrixNode" 6 | bl_label = "Invert Matrix" 7 | 8 | def create(self): 9 | self.newInput("Matrix", "Matrix", "matrix") 10 | self.newOutput("Matrix", "Inverted Matrix", "invertedMatrix") 11 | 12 | def draw(self, layout): 13 | layout.separator() 14 | 15 | def getExecutionCode(self, required): 16 | return "invertedMatrix = matrix.inverted(Matrix.Identity(4))" 17 | -------------------------------------------------------------------------------- /animation_nodes/utils/animation.py: -------------------------------------------------------------------------------- 1 | from . blender_ui import iterActiveScreens 2 | 3 | def isAnimationPlaying(): 4 | return any([screen.is_animation_playing for screen in iterActiveScreens()]) 5 | 6 | def isAnimated(idObject): 7 | if not idObject: 8 | return False 9 | 10 | animationData = idObject.animation_data 11 | if not animationData: 12 | return False 13 | 14 | if animationData.action and len(animationData.action.fcurves) != 0: 15 | return True 16 | 17 | return len(animationData.drivers) != 0 or len(animationData.nla_tracks) != 0 18 | -------------------------------------------------------------------------------- /animation_nodes/nodes/sound/midi_track_info.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode 3 | 4 | class MIDITrackInfoNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_MIDITrackInfoNode" 6 | bl_label = "MIDI Track Info" 7 | 8 | def create(self): 9 | self.newInput("MIDI Track", "Track", "track") 10 | 11 | self.newOutput("MIDI Note List", "Notes", "notes") 12 | self.newOutput("Text", "Name", "name") 13 | self.newOutput("Integer", "Index", "index") 14 | 15 | def execute(self, track): 16 | return track.notes, track.name, track.index 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Animation Nodes 2 | =============== 3 | 4 | ![Build And Deploy](https://github.com/JacquesLucke/animation_nodes/actions/workflows/build.yml/badge.svg) 5 | 6 | Animation Nodes is a node based visual scripting system designed for motion graphics in [Blender](https://blender.org). 7 | 8 | Download the latest version from the Animation Nodes website. https://animation-nodes.com/#download 9 | 10 | Get started with Animation Nodes by reading the documentation. https://docs.animation-nodes.com/ 11 | 12 | [![Showreel](https://img.youtube.com/vi/nCghhlMOwRg/0.jpg)](https://www.youtube.com/watch?v=nCghhlMOwRg) 13 | -------------------------------------------------------------------------------- /animation_nodes/execution/auto_execution.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from .. preferences import getPreferences 3 | from .. utils.blender_ui import redrawAll 4 | from .. utils.nodes import getAnimationNodeTrees 5 | 6 | def iterAutoExecutionNodeTrees(events): 7 | for nodeTree in getAnimationNodeTrees(): 8 | if nodeTree.canAutoExecute(events): 9 | yield nodeTree 10 | 11 | def executeNodeTrees(nodeTrees): 12 | for nodeTree in nodeTrees: 13 | nodeTree.autoExecute() 14 | 15 | def afterExecution(): 16 | from .. events import isRendering 17 | if not isRendering(): 18 | redrawAll() 19 | -------------------------------------------------------------------------------- /animation_nodes/nodes/sequences/get_all_sequences.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode 3 | 4 | class GetAllSequencesNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_GetAllSequencesNode" 6 | bl_label = "Get All Sequences" 7 | 8 | def create(self): 9 | self.newInput("Scene", "Scene", "scene", hide = True) 10 | self.newOutput("Sequence List", "Sequences", "sequences") 11 | 12 | def getExecutionCode(self, required): 13 | yield "editor = scene.sequence_editor if scene else None" 14 | yield "sequences = list(getattr(editor, 'sequences', []))" 15 | -------------------------------------------------------------------------------- /animation_nodes/ui/advanced_node_settings_panel.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | class NodeSettingsPanel(bpy.types.Panel): 4 | bl_idname = "AN_PT_node_settings_panel" 5 | bl_label = "Advanced Node Settings" 6 | bl_space_type = "NODE_EDITOR" 7 | bl_region_type = "UI" 8 | bl_category = "Node" 9 | bl_options = {"DEFAULT_CLOSED"} 10 | 11 | @classmethod 12 | def poll(cls, context): 13 | node = context.active_node 14 | return getattr(node, "isAnimationNode", False) 15 | 16 | def draw(self, context): 17 | node = bpy.context.active_node 18 | node.drawAdvanced(self.layout) 19 | -------------------------------------------------------------------------------- /animation_nodes/nodes/list/get_list_length.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode 3 | 4 | class GetListLengthNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_GetListLengthNode" 6 | bl_label = "Get List Length" 7 | dynamicLabelType = "HIDDEN_ONLY" 8 | 9 | def create(self): 10 | self.newInput("Generic", "List", "list") 11 | self.newOutput("Integer", "Length", "length") 12 | 13 | def drawLabel(self): 14 | return "Get Length" 15 | 16 | def getExecutionCode(self, required): 17 | yield "try: length = len(list)" 18 | yield "except: length = 0" 19 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/meshes/mesh_data.pxd: -------------------------------------------------------------------------------- 1 | from .. attributes.attribute cimport Attribute 2 | from .. lists.polygon_indices_list cimport PolygonIndicesList 3 | from .. lists.base_lists cimport Vector3DList, EdgeIndicesList, LongList 4 | 5 | cdef class Mesh: 6 | cdef: 7 | readonly Vector3DList vertices 8 | readonly EdgeIndicesList edges 9 | readonly PolygonIndicesList polygons 10 | dict derivedMeshDataCache 11 | object builtInAttributes 12 | object customAttributes 13 | object uvMapAttributes 14 | object vertexColorAttributes 15 | object vertexWeightAttributes 16 | -------------------------------------------------------------------------------- /animation_nodes/nodes/mesh/bmesh_recalculate_face_normals.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode 3 | 4 | class BMeshRecalculateFaceNormalsNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_BMeshRecalculateFaceNormalsNode" 6 | bl_label = "BMesh Recalculate Normals" 7 | 8 | def create(self): 9 | self.newInput("BMesh", "BMesh", "bm").dataIsModified = True 10 | self.newOutput("BMesh", "BMesh", "bm") 11 | 12 | def getExecutionCode(self, required): 13 | return "bmesh.ops.recalc_face_normals(bm, faces = bm.faces)" 14 | 15 | def getUsedModules(self): 16 | return ["bmesh"] 17 | -------------------------------------------------------------------------------- /animation_nodes/nodes/falloff/invert_falloff.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from . remap_falloff import RemapFalloff 3 | from ... base_types import AnimationNode 4 | 5 | class InvertFalloffNode(AnimationNode, bpy.types.Node): 6 | bl_idname = "an_InvertFalloffNode" 7 | bl_label = "Invert Falloff" 8 | 9 | def create(self): 10 | self.newInput("Falloff", "Falloff", "inFalloff") 11 | self.newOutput("Falloff", "Falloff", "outFalloff") 12 | 13 | def execute(self, falloff): 14 | return InvertFalloff(falloff) 15 | 16 | class InvertFalloff: 17 | def __new__(cls, falloff): 18 | return RemapFalloff(falloff, 0, 1, 1, 0) 19 | -------------------------------------------------------------------------------- /animation_nodes/nodes/mesh/edges_of_polygons.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode 3 | from ... data_structures import EdgeIndicesList 4 | from ... data_structures.meshes.validate import createValidEdgesList 5 | 6 | class EdgesOfPolygonsNode(AnimationNode, bpy.types.Node): 7 | bl_idname = "an_EdgesOfPolygonsNode" 8 | bl_label = "Edges of Polygons" 9 | 10 | def create(self): 11 | self.newInput("Polygon Indices List", "Polygons", "polygons") 12 | self.newOutput("Edge Indices List", "Edges", "edges") 13 | 14 | def execute(self, polygons): 15 | return createValidEdgesList(polygons = polygons) 16 | -------------------------------------------------------------------------------- /animation_nodes/operators/replace_node_with_copies.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | class ReplaceNodesWithCopies(bpy.types.Operator): 4 | bl_idname = "an.replace_nodes_with_copies" 5 | bl_label = "Replace Nodes with Copies" 6 | 7 | @classmethod 8 | def poll(cls, context): 9 | try: return context.space_data.node_tree.bl_idname == "an_AnimationNodeTree" 10 | except: return False 11 | 12 | def execute(self, context): 13 | bpy.ops.node.select_all(action = "SELECT") 14 | bpy.ops.node.clipboard_copy() 15 | bpy.ops.node.delete() 16 | bpy.ops.node.clipboard_paste() 17 | return {"FINISHED"} 18 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/gpencils/gp_frame_data.py: -------------------------------------------------------------------------------- 1 | import textwrap 2 | 3 | class GPFrame: 4 | def __init__(self, strokes = None, frameNumber = None): 5 | 6 | if strokes is None: strokes = [] 7 | if frameNumber is None: frameNumber = 0 8 | 9 | self.strokes = strokes 10 | self.frameNumber = frameNumber 11 | 12 | def __repr__(self): 13 | return textwrap.dedent( 14 | f"""AN Frame Object: 15 | Strokes: {len(self.strokes)} 16 | Frame Number: {self.frameNumber}""") 17 | 18 | def copy(self): 19 | return GPFrame([stroke.copy() for stroke in self.strokes], self.frameNumber) 20 | -------------------------------------------------------------------------------- /animation_nodes/nodes/material/object_material_input.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode 3 | 4 | class ObjectMaterialInputNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_ObjectMaterialInputNode" 6 | bl_label = "Object Material Input" 7 | 8 | def create(self): 9 | self.newInput("Object", "Object", "object", defaultDrawType = "PROPERTY_ONLY") 10 | self.newOutput("Material List", "Materials", "materials") 11 | 12 | def execute(self, object): 13 | if object is None or not hasattr(object.data, "materials"): return [] 14 | return [material for material in object.data.materials if material] 15 | 16 | -------------------------------------------------------------------------------- /animation_nodes/nodes/object/utility_nodes/reset_transformation.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from .... base_types import AnimationNode 3 | 4 | class ResetObjectTransformsNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_ResetObjectTransformsNode" 6 | bl_label = "Reset Object Transforms" 7 | 8 | def create(self): 9 | self.newInput("Object", "Object", "object").defaultDrawType = "PROPERTY_ONLY" 10 | self.newOutput("Object", "Object", "object") 11 | 12 | def getExecutionCode(self, required): 13 | return "if object: object.matrix_world = mathutils.Matrix.Identity(4)" 14 | 15 | def getUsedModules(self): 16 | return ["mathutils"] 17 | -------------------------------------------------------------------------------- /animation_nodes/nodes/rotation/quaternion_list_combine.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import operator 3 | import functools 4 | from mathutils import Quaternion 5 | from ... base_types import AnimationNode 6 | 7 | class QuaternionListCombineNode(AnimationNode, bpy.types.Node): 8 | bl_idname = "an_QuaternionListCombineNode" 9 | bl_label = "Combine Quaternion Rotations" 10 | 11 | def create(self): 12 | self.newInput("Quaternion List", "Quaternions", "quaternions") 13 | self.newOutput("Quaternion", "Result", "result") 14 | 15 | def execute(self, quaternions): 16 | return functools.reduce(operator.matmul, reversed(quaternions), Quaternion((1, 0, 0, 0))) 17 | -------------------------------------------------------------------------------- /animation_nodes/ui/node_editor_hud.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from .. utils.timing import prettyTime 3 | from .. draw_handler import drawHandler 4 | from .. graphics.drawing_2d import drawText 5 | from .. utils.blender_ui import getDpiFactor 6 | 7 | @drawHandler("SpaceNodeEditor", "WINDOW") 8 | def drawNodeEditorHud(): 9 | tree = bpy.context.getActiveAnimationNodeTree() 10 | if tree is None: 11 | return 12 | 13 | dpiFactor = getDpiFactor() 14 | 15 | top = bpy.context.region.height 16 | 17 | executionTime = prettyTime(tree.lastExecutionInfo.executionTime) 18 | drawText(executionTime, 10 * dpiFactor, top - 20 * dpiFactor, 19 | size = 11, color = (1, 1, 1, 0.5)) 20 | -------------------------------------------------------------------------------- /animation_nodes/id_keys/data_types/__init__.py: -------------------------------------------------------------------------------- 1 | from . text_type import TextDataType 2 | from . float_type import FloatDataType 3 | from . integer_type import IntegerDataType 4 | from . transforms_type import TransformDataType 5 | 6 | 7 | dataTypeByIdentifier = { 8 | "Text" : TextDataType, 9 | "Float" : FloatDataType, 10 | "Integer" : IntegerDataType, 11 | "Transforms" : TransformDataType 12 | } 13 | 14 | dataTypeIdentifiers = set(dataTypeByIdentifier.keys()) 15 | 16 | keyDataTypeItems = [ 17 | ("Transforms", "Transforms", "", "NONE", 0), 18 | ("Text", "Text", "", "NONE", 1), 19 | ("Integer", "Integer", "", "NONE", 2), 20 | ("Float", "Float", "", "NONE", 3) 21 | ] 22 | -------------------------------------------------------------------------------- /animation_nodes/libs/FastNoiseSIMD/source/compile_windows.bat: -------------------------------------------------------------------------------- 1 | :: Please run this file in the 'x64 Native Tools Command Prompt for VS 2017' 2 | :: If you don't have it, install Visual Studio 2017 3 | @echo off 4 | setlocal 5 | 6 | :: -c Create only .obj file 7 | :: /EHsc Set appropriate error handling 8 | :: /GL- Disable whole program optimization 9 | :: /Ox Enable full optimization 10 | set args=-c /EHsc /GL- /Ox /MD 11 | 12 | cl FastNoiseSIMD.cpp %args% 13 | cl FastNoiseSIMD_internal.cpp %args% 14 | cl FastNoiseSIMD_sse2.cpp %args% 15 | cl FastNoiseSIMD_sse41.cpp %args% 16 | cl FastNoiseSIMD_avx2.cpp %args% /arch:AVX2 17 | 18 | lib *.obj /OUT:FastNoiseSIMD_windows.lib 19 | 20 | @echo Done. 21 | -------------------------------------------------------------------------------- /animation_nodes/operators/switch_tree.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.props import * 3 | 4 | class SwitchTreeOperator(bpy.types.Operator): 5 | bl_idname = "an.switch_tree" 6 | bl_label = "Switch Tree" 7 | bl_description = "Switch to that tree and view all nodes" 8 | 9 | treeName: StringProperty() 10 | 11 | @classmethod 12 | def poll(cls, context): 13 | return context.area.type == "NODE_EDITOR" 14 | 15 | def execute(self, context): 16 | if self.treeName not in bpy.data.node_groups: 17 | return {"CANCELLED"} 18 | 19 | context.space_data.node_tree = bpy.data.node_groups[self.treeName] 20 | bpy.ops.node.view_all() 21 | return {"FINISHED"} 22 | -------------------------------------------------------------------------------- /animation_nodes/id_keys/__init__.py: -------------------------------------------------------------------------------- 1 | ''' 2 | The ID Key system allows to store custom data inside of ID objects (Objects, ...). 3 | Therefor it builds upon the normal ID properties Blender provides. 4 | 5 | The used ID properties always have one of the following structures: 6 | AN*Data_Type*Subproperty_Name*Property_Name 7 | AN*Data_Type*Property_Name 8 | 9 | The total length of this string is limited to 63 characters. 10 | 11 | Data_Type, Subproperty_Name and Property_Name must not be empty nor contain '*'. 12 | ''' 13 | 14 | from . data_types import keyDataTypeItems 15 | from . data_types import dataTypeByIdentifier as IDKeyTypes 16 | from . existing_keys import getAllIDKeys, updateIdKeysList, IDKey, findsIDKeys 17 | -------------------------------------------------------------------------------- /animation_nodes/nodes/kd_tree/construct.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode 3 | 4 | class ConstructKDTreeNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_ConstructKDTreeNode" 6 | bl_label = "Construct KDTree" 7 | 8 | def create(self): 9 | self.newInput("Vector List", "Vector List", "vectorList") 10 | self.newOutput("KDTree", "KDTree", "kdTree") 11 | 12 | def getExecutionCode(self, required): 13 | yield "kdTree = mathutils.kdtree.KDTree(len(vectorList))" 14 | yield "for i, vector in enumerate(vectorList): kdTree.insert(vector, i)" 15 | yield "kdTree.balance()" 16 | 17 | def getUsedModules(self): 18 | return ["mathutils"] 19 | -------------------------------------------------------------------------------- /animation_nodes/nodes/spline/make_spline_cyclic.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode 3 | 4 | class MakeSplineCyclicNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_MakeSplineCyclicNode" 6 | bl_label = "Make Spline Cyclic" 7 | 8 | def create(self): 9 | socket = self.newInput("Spline", "Spline", "spline") 10 | socket.dataIsModified = True 11 | socket.defaultDrawType = "PROPERTY_ONLY" 12 | self.newInput("Boolean", "Cyclic", "cylic", value = True) 13 | self.newOutput("Spline", "Spline", "outSpline") 14 | 15 | def execute(self, spline, cyclic): 16 | spline.cyclic = cyclic 17 | spline.markChanged() 18 | return spline 19 | -------------------------------------------------------------------------------- /animation_nodes/base_types/__init__.py: -------------------------------------------------------------------------------- 1 | from . node_tree import AnimationNodeTree 2 | 3 | from . nodes import ( 4 | AnimationNode, 5 | NodeUIExtension, 6 | InterpolationUIExtension, 7 | ErrorUIExtension, 8 | TextUIExtension 9 | ) 10 | 11 | from . sockets import (AnimationNodeSocket, 12 | ListSocket, 13 | PythonListSocket, 14 | CListSocket) 15 | 16 | from . effects import ( 17 | VectorizeCodeEffect, 18 | PrependCodeEffect, 19 | ReturnDefaultsOnExceptionCodeEffect 20 | ) 21 | 22 | from . socket_templates import ( 23 | SocketTemplate, 24 | VectorizedSocket, 25 | DataTypeSelectorSocket, 26 | ListTypeSelectorSocket 27 | ) 28 | -------------------------------------------------------------------------------- /animation_nodes/base_types/socket_templates/base.py: -------------------------------------------------------------------------------- 1 | class SocketTemplate: 2 | def createInput(self, node): 3 | return self.create(node, node.inputs) 4 | 5 | def createOutput(self, node): 6 | return self.create(node, node.outputs) 7 | 8 | def create(self, node, sockets): 9 | raise NotImplementedError() 10 | 11 | def getSocketIdentifiers(self): 12 | raise NotImplementedError() 13 | 14 | def getRelatedPropertyNames(self): 15 | raise NotImplementedError() 16 | 17 | def applyWithContext(self, node, socket, updatedProperties, fixedProperties): 18 | return self.apply(node, socket) 19 | 20 | def apply(self, node, socket): 21 | raise NotImplementedError() 22 | -------------------------------------------------------------------------------- /animation_nodes/nodes/mesh/bmesh_remove_doubles.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import bmesh 3 | from ... base_types import AnimationNode 4 | 5 | class BMeshRemoveDoublesNode(AnimationNode, bpy.types.Node): 6 | bl_idname = "an_BMeshRemoveDoublesNode" 7 | bl_label = "BMesh Remove Doubles" 8 | 9 | def create(self): 10 | self.newInput("BMesh", "BMesh", "bm").dataIsModified = True 11 | self.newInput("Float", "Distance", "distance", value = 0.0001, minValue = 0.0) 12 | self.newOutput("BMesh", "BMesh", "bm") 13 | 14 | def getExecutionCode(self, required): 15 | return "bmesh.ops.remove_doubles(bm, verts = bm.verts, dist = distance)" 16 | 17 | def getUsedModules(self): 18 | return ["bmesh"] 19 | -------------------------------------------------------------------------------- /animation_nodes/sockets/kd_tree.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from mathutils.kdtree import KDTree 3 | from .. base_types import AnimationNodeSocket 4 | 5 | class KDTreeSocket(bpy.types.NodeSocket, AnimationNodeSocket): 6 | bl_idname = "an_KDTreeSocket" 7 | bl_label = "KDTree Socket" 8 | dataType = "KDTree" 9 | drawColor = (0.32, 0.32, 0.18, 1) 10 | comparable = True 11 | storable = True 12 | 13 | @classmethod 14 | def getDefaultValue(cls): 15 | kdTree = KDTree(0) 16 | kdTree.balance() 17 | return kdTree 18 | 19 | @classmethod 20 | def correctValue(cls, value): 21 | if isinstance(value, KDTree): 22 | return value, 0 23 | return cls.getDefaultValue(), 2 24 | -------------------------------------------------------------------------------- /animation_nodes/nodes/object/utility_nodes/move_object.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from .... base_types import AnimationNode 3 | from .... utils.depsgraph import getEvaluatedID 4 | 5 | class MoveObjectNode(AnimationNode, bpy.types.Node): 6 | bl_idname = "an_MoveObjectNode" 7 | bl_label = "Move Object" 8 | 9 | def create(self): 10 | self.newInput("Object", "Object", "object").defaultDrawType = "PROPERTY_ONLY" 11 | self.newInput("Vector", "Translation", "translation").defaultDrawType = "PROPERTY_ONLY" 12 | self.newOutput("Object", "Object", "object") 13 | 14 | def getExecutionCode(self, required): 15 | return "if object: object.location = AN.utils.depsgraph.getEvaluatedID(object).location + translation" 16 | -------------------------------------------------------------------------------- /animation_nodes/nodes/text/text_length.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode, VectorizedSocket 3 | 4 | class TextLengthNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_TextLengthNode" 6 | bl_label = "Text Length" 7 | codeEffects = [VectorizedSocket.CodeEffect] 8 | 9 | useTextList: VectorizedSocket.newProperty() 10 | 11 | def create(self): 12 | self.newInput(VectorizedSocket("Text", "useTextList", 13 | ("Text", "text"), ("Texts", "texts"))) 14 | 15 | self.newOutput(VectorizedSocket("Integer", "useTextList", 16 | ("Length", "length"), ("Lengths", "lengths"))) 17 | 18 | def getExecutionCode(self, required): 19 | return "length = len(text)" 20 | -------------------------------------------------------------------------------- /animation_nodes/tests.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from . utils.handlers import eventHandler 3 | from . preferences import testsAreEnabled 4 | 5 | @eventHandler("ADDON_LOAD_POST") 6 | def autoRunTestsInDebugMode(): 7 | if testsAreEnabled(): 8 | runTests() 9 | 10 | def runTests(): 11 | print("\n" * 2) 12 | print("Start running the Animation Nodes test suite.") 13 | print("Can be disabled in the user preferences of the addon.\n") 14 | 15 | testLoader = unittest.TestLoader() 16 | testLoader.testMethodPrefix = "test" # <- change to only run selected tests 17 | allTests = testLoader.discover("animation_nodes", pattern = "test*") 18 | unittest.TextTestRunner(verbosity = 1).run(allTests) 19 | 20 | print("\n" * 2) 21 | -------------------------------------------------------------------------------- /animation_nodes/utils/timing.py: -------------------------------------------------------------------------------- 1 | import time 2 | import functools 3 | from .. preferences import debuggingIsEnabled 4 | 5 | def prettyTime(seconds): 6 | if seconds > 1.5: return "{:.2f} s".format(seconds) 7 | else: return "{:.4f} ms".format(seconds * 1000) 8 | 9 | def measureTime(function): 10 | @functools.wraps(function) 11 | def wrapper(*args, **kwargs): 12 | start = time.process_time() 13 | output = function(*args, **kwargs) 14 | end = time.process_time() 15 | duration = end - start 16 | if debuggingIsEnabled(): 17 | print("Time: {:.5f} - fps : {:.2f} - Function: {}".format(duration, 1 / max(duration, 1e-10), function.__name__)) 18 | return output 19 | return wrapper 20 | -------------------------------------------------------------------------------- /animation_nodes/sockets/bmesh.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import bmesh 3 | from .. base_types import AnimationNodeSocket 4 | 5 | class BMeshSocket(bpy.types.NodeSocket, AnimationNodeSocket): 6 | bl_idname = "an_BMeshSocket" 7 | bl_label = "BMesh Socket" 8 | dataType = "BMesh" 9 | drawColor = (0.1, 0.4, 0.1, 1) 10 | storable = False 11 | comparable = True 12 | 13 | @classmethod 14 | def getDefaultValue(cls): 15 | return bmesh.new() 16 | 17 | @classmethod 18 | def getCopyExpression(cls): 19 | return "value.copy()" 20 | 21 | @classmethod 22 | def correctValue(cls, value): 23 | if isinstance(value, bmesh.types.BMesh): 24 | return value, 0 25 | return cls.getDefaultValue(), 2 26 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/lists/__boolean_list_functions.src: -------------------------------------------------------------------------------- 1 | def allTrue(self): 2 | cdef Py_ssize_t i 3 | for i in range(self.length): 4 | if self.data[i] == 0: return False 5 | return True 6 | 7 | def allFalse(self): 8 | cdef Py_ssize_t i 9 | for i in range(self.length): 10 | if self.data[i] != 0: return False 11 | return True 12 | 13 | def countTrue(self): 14 | return self.length - self.countFalse() 15 | 16 | def countFalse(self): 17 | cdef Py_ssize_t counter = 0 18 | for i in range(self.length): 19 | if self.data[i] == 0: counter += 1 20 | return counter 21 | 22 | def invertAll(self): 23 | cdef Py_ssize_t i 24 | for i in range(self.length): 25 | self.data[i] = not self.data[i] 26 | -------------------------------------------------------------------------------- /animation_nodes/nodes/text/text_block_writer.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode 3 | 4 | class TextBlockWriterNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_TextBlockWriterNode" 6 | bl_label = "Text Block Writer" 7 | 8 | def create(self): 9 | self.newInput("Text Block", "Text Block", "textBlock", defaultDrawType = "PROPERTY_ONLY") 10 | self.newInput("Text", "Text", "text") 11 | self.newInput("Boolean", "Enabled", "enabled", hide = True) 12 | self.newOutput("Text Block", "Text Block", "textBlock") 13 | 14 | def execute(self, textBlock, text, enabled): 15 | if not enabled or textBlock is None: return textBlock 16 | textBlock.from_string(text) 17 | return textBlock 18 | -------------------------------------------------------------------------------- /animation_nodes/nodes/sequences/sequences_from_channel.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode 3 | 4 | class SequencesFromChannelNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_SequencesFromChannelNode" 6 | bl_label = "Sequences from Channel" 7 | 8 | def create(self): 9 | self.newInput("Integer", "Channel", "channel", value = 1).setRange(1, 32) 10 | self.newInput("Scene", "Scene", "scene", hide = True) 11 | self.newOutput("Sequence List", "Sequences", "sequences") 12 | 13 | def getExecutionCode(self, required): 14 | return ("editor = scene.sequence_editor if scene else None", 15 | "sequences = [sequence for sequence in getattr(editor, 'sequences', []) if sequence.channel == channel]") 16 | -------------------------------------------------------------------------------- /animation_nodes/nodes/spline/spline_evaluation_base.py: -------------------------------------------------------------------------------- 1 | from bpy.props import * 2 | from ... events import propertyChanged, executionCodeChanged 3 | 4 | parameterTypeItems = [ 5 | ("RESOLUTION", "Resolution", ""), 6 | ("UNIFORM", "Uniform", "")] 7 | 8 | class SplineEvaluationBase: 9 | 10 | def parameterTypeChanged(self, context): 11 | propertyChanged() 12 | executionCodeChanged() 13 | 14 | parameterType: EnumProperty(name = "Parameter Type", default = "UNIFORM", 15 | items = parameterTypeItems, update = parameterTypeChanged) 16 | 17 | resolution: IntProperty(name = "Resolution", min = 2, default = 20, 18 | description = "Increase to have a more accurate evaluation if the type is set to Uniform", 19 | update = propertyChanged) 20 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/struct.py: -------------------------------------------------------------------------------- 1 | from .. sockets.info import getCopyFunction 2 | 3 | class ANStruct(dict): 4 | 5 | def copyValues(self): 6 | s = ANStruct() 7 | for (dataType, name), value in self.items(): 8 | s[(dataType, name)] = getCopyFunction(dataType)(value) 9 | return s 10 | 11 | def findDataTypesWithName(self, name): 12 | return [dataType for dataType, _name in self.keys() if name == _name] 13 | 14 | def findNamesWithDataType(self, dataType): 15 | return [name for _dataType, name in self.keys() if dataType == _dataType] 16 | 17 | def __repr__(self): 18 | elements = [repr(name) + ": " + str(value) for (_, name), value in self.items()] 19 | return "".format(", ".join(elements)) 20 | -------------------------------------------------------------------------------- /animation_nodes/nodes/text/timecode.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.utils import smpte_from_frame 3 | from ... base_types import AnimationNode 4 | 5 | class TimecodeGeneratorNode(AnimationNode, bpy.types.Node): 6 | bl_idname = "an_TimecodeGeneratorNode" 7 | bl_label = "Timecode Generator" 8 | 9 | def create(self): 10 | self.newInput("Float", "Frame", "frame") 11 | socket = self.newInput("Float", "Frame Rate", "frameRate", minValue = 0) 12 | socket.value = bpy.context.scene.render.fps / bpy.context.scene.render.fps_base 13 | self.newOutput("Text", "Timecode", "timecode") 14 | 15 | def execute(self, frame, frameRate): 16 | if frameRate > 0: 17 | return smpte_from_frame(frame, fps = frameRate, fps_base = 1) 18 | return "00:00:00:00" 19 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2017 Jacques Lucke 2 | mail@jlucke.com 3 | 4 | Created by Jacques Lucke 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | -------------------------------------------------------------------------------- /animation_nodes/algorithms/lists/reverse.pyx: -------------------------------------------------------------------------------- 1 | from ... sockets.info import getSocketClass 2 | from ... data_structures cimport CList, PolygonIndicesList 3 | 4 | def reverse(str dataType, myList): 5 | return getReverseFunction(dataType)(myList) 6 | 7 | def getReverseFunction(str dataType): 8 | socketClass = getSocketClass(dataType) 9 | defaultValue = socketClass.getDefaultValue() 10 | 11 | if isinstance(defaultValue, list): 12 | return reverse_PythonList 13 | elif isinstance(defaultValue, (CList, PolygonIndicesList)): 14 | return reverse_ReversableList 15 | else: 16 | raise NotImplementedError() 17 | 18 | def reverse_PythonList(list myList): 19 | return list(reversed(myList)) 20 | 21 | def reverse_ReversableList(myList): 22 | return myList.reversed() 23 | -------------------------------------------------------------------------------- /animation_nodes/nodes/text/reverse_text.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode, VectorizedSocket 3 | 4 | class ReverseTextNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_ReverseTextNode" 6 | bl_label = "Reverse Text" 7 | 8 | useList: VectorizedSocket.newProperty() 9 | 10 | def create(self): 11 | self.newInput(VectorizedSocket("Text", "useList", 12 | ("Text", "inText"), ("Texts", "inTexts"))) 13 | 14 | self.newOutput(VectorizedSocket("Text", "useList", 15 | ("Text", "outText"), ("Texts", "outTexts"))) 16 | 17 | def getExecutionCode(self, required): 18 | if self.useList: 19 | return "outTexts = [text[::-1] for text in inTexts]" 20 | else: 21 | return "outText = inText[::-1]" 22 | -------------------------------------------------------------------------------- /animation_nodes/nodes/object/evaluate_object.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode, VectorizedSocket 3 | 4 | class EvaluateObjectNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_EvaluateObjectNode" 6 | bl_label = "Evaluate Object" 7 | 8 | useObjectList: VectorizedSocket.newProperty() 9 | 10 | def create(self): 11 | self.newInput(VectorizedSocket("Object", "useObjectList", 12 | ("Object", "object", dict(defaultDrawType = "PROPERTY_ONLY")), 13 | ("Objects", "objects"))) 14 | self.newOutput(VectorizedSocket("Object", "useObjectList", 15 | ("Object", "object"), 16 | ("Objects", "objects"))) 17 | 18 | def getExecutionCode(self, required): 19 | yield "AN.utils.depsgraph.getActiveDepsgraph().update()" 20 | -------------------------------------------------------------------------------- /animation_nodes/nodes/fcurve/fcurve_info.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode 3 | 4 | class FCurveInfoNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_FCurveInfoNode" 6 | bl_label = "FCurve Info" 7 | 8 | def create(self): 9 | self.newInput("FCurve", "FCurve", "fCurve") 10 | self.newOutput("Text", "Data Path", "dataPath") 11 | self.newOutput("Integer", "Array Index", "arrayIndex") 12 | 13 | def getExecutionCode(self, required): 14 | if len(required) == 0: 15 | return 16 | 17 | yield "if fCurve is not None:" 18 | if "dataPath" in required: yield " dataPath = fCurve.data_path" 19 | if "arrayIndex" in required: yield " arrayIndex = fCurve.array_index" 20 | yield "else: dataPath, arrayIndex = '', 0" 21 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/attributes/attribute.pxd: -------------------------------------------------------------------------------- 1 | from .. lists.clist cimport CList 2 | 3 | cpdef enum AttributeType: 4 | MATERIAL_INDEX, 5 | UV_MAP, 6 | VERTEX_COLOR, 7 | VERTEX_WEIGHT, 8 | BEVEL_VERTEX_WEIGHT, 9 | BEVEL_EDGE_WEIGHT, 10 | EDGE_CREASE, 11 | CUSTOM, 12 | 13 | cpdef enum AttributeDomain: 14 | POINT, 15 | EDGE, 16 | FACE, 17 | CORNER, 18 | 19 | cpdef enum AttributeDataType: 20 | INT, 21 | INT32_2D, 22 | FLOAT, 23 | FLOAT2, 24 | FLOAT_VECTOR, 25 | FLOAT_COLOR, 26 | BYTE_COLOR, 27 | BOOLEAN, 28 | 29 | cdef class Attribute: 30 | cdef: 31 | readonly str name 32 | readonly AttributeType type 33 | readonly AttributeDomain domain 34 | readonly AttributeDataType dataType 35 | readonly CList data 36 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/falloffs/falloff_base.pxd: -------------------------------------------------------------------------------- 1 | from . evaluation cimport FalloffEvaluator 2 | 3 | cdef class Falloff: 4 | cdef bint clamped 5 | cdef dict evaluators 6 | 7 | cpdef FalloffEvaluator getEvaluator(self, str sourceType, bint clamped = ?) 8 | 9 | cdef class BaseFalloff(Falloff): 10 | cdef str dataType 11 | cdef float evaluate(self, void *object, Py_ssize_t index) 12 | cdef void evaluateList(self, void *objects, Py_ssize_t startIndex, 13 | Py_ssize_t amount, float *target) 14 | 15 | cdef class CompoundFalloff(Falloff): 16 | cdef list getDependencies(self) 17 | cdef list getClampingRequirements(self) 18 | cdef float evaluate(self, float *dependencyResults) 19 | cdef void evaluateList(self, float **dependencyResults, Py_ssize_t amount, float *target) 20 | -------------------------------------------------------------------------------- /animation_nodes/nodes/mesh/bmesh_invert_normals.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.props import * 3 | from ... events import propertyChanged 4 | from ... base_types import AnimationNode 5 | 6 | class BMeshInvertNormalsNode(AnimationNode, bpy.types.Node): 7 | bl_idname = "an_BMeshInvertNormalsNode" 8 | bl_label = "BMesh Invert Normals" 9 | 10 | def create(self): 11 | self.newInput("BMesh", "BMesh", "bm").dataIsModified = True 12 | i = self.newInput("Boolean", "Flip Multires", "flipMultires") 13 | i.value = False 14 | i.hide = True 15 | self.newOutput("BMesh", "BMesh", "bm") 16 | 17 | def getExecutionCode(self, required): 18 | return "bmesh.ops.reverse_faces(bm, faces = bm.faces, flip_multires = flipMultires)" 19 | 20 | def getUsedModules(self): 21 | return ["bmesh"] 22 | -------------------------------------------------------------------------------- /animation_nodes/math/color.pxd: -------------------------------------------------------------------------------- 1 | cdef struct Color: 2 | float r, g, b, a 3 | 4 | cdef void addColor(Color* target, Color* a, Color* b) 5 | cdef void addColor_Inplace(Color* c, Color* a) 6 | cdef void scaleColor_Inplace(Color* c, float factor) 7 | cdef void mixColor(Color* target, Color* x, Color* y, float factor) 8 | 9 | cdef void hsla_to_rgba(Color* c, float h, float s, float l, float a) 10 | cdef void hsva_to_rgba(Color* c, float h, float s, float v, float a) 11 | cdef void yiqa_to_rgba(Color* c, float y, float i, float q, float a) 12 | cdef float hue_to_rgb(float m1, float m2, float h) 13 | 14 | cdef void rgba_to_hsla(Color* c, double* h, double* s, double* l, double* a) 15 | cdef void rgba_to_hsva(Color* c, double* h, double* s, double* v, double* a) 16 | cdef void rgba_to_yiqa(Color* c, double* y, double* i, double* q, double* a) 17 | -------------------------------------------------------------------------------- /animation_nodes/nodes/text/find_replace.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode, VectorizedSocket 3 | 4 | class ReplaceTextNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_ReplaceTextNode" 6 | bl_label = "Replace Text" 7 | codeEffects = [VectorizedSocket.CodeEffect] 8 | 9 | useTextList: VectorizedSocket.newProperty() 10 | 11 | def create(self): 12 | self.newInput(VectorizedSocket("Text", "useTextList", 13 | ("Text", "text"), ("Texts", "texts"))) 14 | self.newInput("Text", "Old", "old") 15 | self.newInput("Text", "New", "new") 16 | 17 | self.newOutput(VectorizedSocket("Text", "useTextList", 18 | ("Text", "newText"), ("Texts", "newTexts"))) 19 | 20 | def getExecutionCode(self, required): 21 | return "newText = text.replace(old, new)" 22 | -------------------------------------------------------------------------------- /animation_nodes/utils/sequence_editor.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from . path import toAbsolutePath 3 | 4 | def iterSoundSequencesInScene(scene): 5 | return (sequence for sequence in iterSequencesInScene(scene) if sequence.type == "SOUND") 6 | 7 | def iterSequencesInScene(scene): 8 | if scene is None: return [] 9 | if scene.sequence_editor is None: [] 10 | yield from scene.sequence_editor.sequences 11 | 12 | def getEmptyChannel(editor): 13 | channels = [False] * 32 14 | for sequence in editor.sequences: 15 | channels[sequence.channel - 1] = True 16 | for channel, isUsed in enumerate(channels): 17 | if not isUsed: return channel + 1 18 | return 32 19 | 20 | def getOrCreateSequencer(scene): 21 | if not scene.sequence_editor: 22 | scene.sequence_editor_create() 23 | return scene.sequence_editor 24 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/lists/__matrix_list_functions.src: -------------------------------------------------------------------------------- 1 | def toEulers(self, bint isNormalized = False): 2 | from ... math import matrix4x4ListToEulerList 3 | return matrix4x4ListToEulerList(self, isNormalized) 4 | 5 | def toQuaternions(self, bint isNormalized = False): 6 | from ... math import matrix4x4ListToQuaternionList 7 | return matrix4x4ListToQuaternionList(self, isNormalized) 8 | 9 | def transpose(self): 10 | cdef Py_ssize_t i 11 | for i in range(self.length): 12 | transposeMatrix_Inplace(self.data + i) 13 | 14 | def transform(self, matrix): 15 | cdef Matrix4 transformation 16 | cdef Matrix4 temp 17 | cdef Py_ssize_t i 18 | setMatrix4(&transformation, matrix) 19 | for i in range(self.length): 20 | multMatrix4(&temp, &transformation, self.data + i) 21 | self.data[i] = temp 22 | -------------------------------------------------------------------------------- /animation_nodes/nodes/sound/sound_from_sequence.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... data_structures import Sound, SoundSequence 3 | from ... base_types import AnimationNode, VectorizedSocket 4 | 5 | class SoundFromSequenceNode(AnimationNode, bpy.types.Node): 6 | bl_idname = "an_SoundFromSequenceNode" 7 | bl_label = "Sound From Sequence" 8 | 9 | useSequenceList: VectorizedSocket.newProperty() 10 | 11 | def create(self): 12 | self.newInput(VectorizedSocket("Sequence", "useSequenceList", 13 | ("Sequence", "sequence"), ("Sequences", "sequences"))) 14 | 15 | self.newOutput("Sound", "Sound", "sound") 16 | 17 | def execute(self, sequences): 18 | if not self.useSequenceList: sequences = [sequences] 19 | return Sound([SoundSequence.fromSequence(s) for s in sequences if s is not None and s.type == "SOUND"]) 20 | -------------------------------------------------------------------------------- /animation_nodes/ui/selection_pie.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from .. utils.blender_ui import PieMenuHelper 3 | 4 | class SelectionPie(bpy.types.Menu, PieMenuHelper): 5 | bl_idname = "AN_MT_selection_pie" 6 | bl_label = "Selection Pie" 7 | 8 | @classmethod 9 | def poll(cls, context): 10 | tree = context.getActiveAnimationNodeTree() 11 | if tree is None: return False 12 | if tree.nodes.active is None: return False 13 | return True 14 | 15 | def drawLeft(self, layout): 16 | layout.operator("an.select_dependencies") 17 | 18 | def drawRight(self, layout): 19 | layout.operator("an.select_dependent_nodes") 20 | 21 | def drawBottom(self, layout): 22 | layout.operator("an.select_network") 23 | 24 | def drawTop(self, layout): 25 | layout.operator("an.frame_active_network") 26 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/__init__.pxd: -------------------------------------------------------------------------------- 1 | from . lists.clist import CList 2 | from . lists.base_lists cimport * 3 | from . lists.polygon_indices_list cimport PolygonIndicesList 4 | 5 | from . virtual_list.virtual_list cimport VirtualList, VirtualPyList 6 | from . virtual_list.virtual_clists cimport * 7 | 8 | from . default_lists.c_default_list cimport CDefaultList 9 | from . meshes.mesh_data cimport Mesh 10 | 11 | from . splines.base_spline cimport Spline 12 | from . splines.poly_spline cimport PolySpline 13 | from . splines.bezier_spline cimport BezierSpline 14 | 15 | from . attributes.attribute cimport Attribute 16 | 17 | from . falloffs.evaluation cimport FalloffEvaluator 18 | from . falloffs.falloff_base cimport Falloff, BaseFalloff, CompoundFalloff 19 | from . interpolation cimport InterpolationFunction, Interpolation 20 | 21 | from . action cimport * 22 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/lists/__indices_list_functions.src: -------------------------------------------------------------------------------- 1 | def getMinIndex(self): 2 | if self.length == 0: 3 | raise ValueError("Cannot find a min value in a list with zero elements") 4 | 5 | cdef MEMVIEW* data = self.data 6 | cdef MEMVIEW minValue = data[0] 7 | for i in range(self.length * sizeof(TYPE) // sizeof(MEMVIEW)): 8 | if data[i] < minValue: 9 | minValue = data[i] 10 | return minValue 11 | 12 | def getMaxIndex(self): 13 | if self.length == 0: 14 | raise ValueError("Cannot find a max value in a list with zero elements") 15 | 16 | cdef MEMVIEW* data = self.data 17 | cdef MEMVIEW maxValue = data[0] 18 | for i in range(self.length * sizeof(TYPE) // sizeof(MEMVIEW)): 19 | if data[i] > maxValue: 20 | maxValue = data[i] 21 | return maxValue 22 | -------------------------------------------------------------------------------- /animation_nodes/nodes/object/collection_info.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode 3 | 4 | class CollectionInfoNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_CollectionInfoNode" 6 | bl_label = "Collection Info" 7 | 8 | def create(self): 9 | self.newInput("Collection", "Collection", "collection", defaultDrawType = "PROPERTY_ONLY") 10 | self.newOutput("Object List", "Objects", "objects") 11 | self.newOutput("Object List", "All Objects", "allObjects") 12 | self.newOutput("Collection List", "Children", "children") 13 | 14 | def execute(self, collection): 15 | objects = list(getattr(collection, "objects", [])) 16 | allObjects = list(getattr(collection, "all_objects", [])) 17 | children = list(getattr(collection, "children", [])) 18 | return objects, allObjects, children 19 | -------------------------------------------------------------------------------- /animation_nodes/math/rotation_conversion.pxd: -------------------------------------------------------------------------------- 1 | from .. data_structures.lists.base_lists cimport EulerList, Matrix4x4List, QuaternionList 2 | from . matrix cimport Matrix3_or_Matrix4, Matrix4 3 | from . euler cimport Euler3 4 | from . quaternion cimport Quaternion 5 | from . vector cimport Vector3 6 | 7 | cdef matrixToEuler(Euler3* target, Matrix3_or_Matrix4* m) 8 | cdef void normalizedAxisAngleToMatrix(Matrix3_or_Matrix4* m, Vector3* axis, float angle) 9 | cdef void normalizedAxisCosAngleToMatrix(Matrix3_or_Matrix4* m, Vector3* axis, float cosAngle) 10 | cdef void euler3ToQuaternion(Quaternion *q, Euler3 *e) 11 | cdef void quaternionToMatrix4(Matrix4 *m, Quaternion *q) 12 | cdef void quaternionToEuler3(Euler3 *e, Quaternion *q) 13 | cdef void quaternionFromAxisAngle(Quaternion *q, Vector3 *axis, float angle) 14 | cdef void quaternionToAxisAngle(Vector3 *v, float *a, Quaternion *q) 15 | -------------------------------------------------------------------------------- /animation_nodes/nodes/boolean/invert_node.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode, VectorizedSocket 3 | 4 | class InvertBooleanNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_InvertBooleanNode" 6 | bl_label = "Invert Boolean" 7 | 8 | useList: VectorizedSocket.newProperty() 9 | 10 | def create(self): 11 | self.newInput(VectorizedSocket("Boolean", "useList", 12 | ("Input", "input"), 13 | ("Input", "input", dict(dataIsModified = True)))) 14 | 15 | self.newOutput(VectorizedSocket("Boolean", "useList", 16 | ("Output", "output"), 17 | ("Output", "output"))) 18 | 19 | def getExecutionCode(self, required): 20 | if self.useList: 21 | yield "input.invertAll()" 22 | yield "output = input" 23 | else: 24 | yield "output = not input" 25 | -------------------------------------------------------------------------------- /animation_nodes/nodes/mesh/create_edges.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from . c_utils import createEdges 3 | from ... base_types import AnimationNode 4 | 5 | class CreateEdgesNode(AnimationNode, bpy.types.Node): 6 | bl_idname = "an_CreateEdgesNode" 7 | bl_label = "Create Edges" 8 | bl_width_default = 140 9 | errorHandlingType = "EXCEPTION" 10 | 11 | def create(self): 12 | self.newInput("Vector List", "Edge Starts", "edgeStarts") 13 | self.newInput("Vector List", "Edge Ends", "edgeEnds") 14 | 15 | self.newOutput("Vector List", "Vectors", "vectors") 16 | self.newOutput("Edge Indices List", "Edge Indices List", "edgeIndicesList") 17 | 18 | def execute(self, edgeStarts, edgeEnds): 19 | if len(edgeStarts) != len(edgeEnds): 20 | self.raiseErrorMessage("List lengths not equal") 21 | return createEdges(edgeStarts, edgeEnds) 22 | -------------------------------------------------------------------------------- /animation_nodes/operators/remove_nodetree.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | class RemoveNodeTree(bpy.types.Operator): 4 | bl_idname = "an.remove_node_tree" 5 | bl_label = "Remove Animation Node Tree" 6 | bl_description = "Remove the active animation node tree" 7 | 8 | @classmethod 9 | def poll(cls, context): 10 | return context.getActiveAnimationNodeTree() is not None 11 | 12 | def invoke(self, context, event): 13 | return context.window_manager.invoke_confirm(self, event) 14 | 15 | def execute(self, context): 16 | tree = context.space_data.node_tree 17 | tree.use_fake_user = False 18 | context.space_data.node_tree = None 19 | # the doc says this can maybe crash Blender 20 | # I didn't experienced this yet 21 | tree.user_clear() 22 | bpy.data.node_groups.remove(tree) 23 | return {"FINISHED"} 24 | -------------------------------------------------------------------------------- /animation_nodes/nodes/color/choose_color.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.props import * 3 | from ... data_structures import Color 4 | from ... events import propertyChanged 5 | from ... base_types import AnimationNode 6 | 7 | class ChooseColorNode(AnimationNode, bpy.types.Node): 8 | bl_idname = "an_ChooseColorNode" 9 | bl_label = "Choose Color" 10 | 11 | colorProperty: FloatVectorProperty( 12 | default = [0.5, 0.5, 0.5], subtype = "COLOR", 13 | soft_min = 0.0, soft_max = 1.0, update = propertyChanged) 14 | 15 | def create(self): 16 | self.newInput("Float", "Alpha", "alpha", value = 1.0) 17 | self.newOutput("Color", "Color", "color") 18 | 19 | def draw(self, layout): 20 | layout.template_color_picker(self, "colorProperty", value_slider = True) 21 | 22 | def execute(self, alpha): 23 | return Color((*self.colorProperty, alpha)) 24 | -------------------------------------------------------------------------------- /animation_nodes/nodes/particles/particle_systems_from_object.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode 3 | from ... utils.depsgraph import getEvaluatedID 4 | 5 | class ParticleSystemsInputNode(AnimationNode, bpy.types.Node): 6 | bl_idname = "an_ParticleSystemsFromObjectNode" 7 | bl_label = "Particle Systems from Object" 8 | bl_width_default = 160 9 | 10 | def create(self): 11 | self.newInput("Object", "Object", "object", defaultDrawType = "PROPERTY_ONLY") 12 | self.newOutput("Particle System", "Active", "active") 13 | self.newOutput("Particle System List", "Particle Systems", "particleSystems") 14 | 15 | def execute(self, object): 16 | if not object: return None, [] 17 | particleSystems = getEvaluatedID(object).particle_systems 18 | active = particleSystems.active 19 | return active, list(particleSystems) 20 | -------------------------------------------------------------------------------- /animation_nodes/nodes/action/delay_action.pyx: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.props import * 3 | from ... base_types import AnimationNode 4 | from ... data_structures import DelayAction 5 | 6 | class DelayActionNode(AnimationNode, bpy.types.Node): 7 | bl_idname = "an_DelayActionNode" 8 | bl_label = "Delay Action" 9 | 10 | __annotations__ = {} 11 | __annotations__["relative"] = BoolProperty(name = "Relative", default = True) 12 | 13 | def create(self): 14 | self.newInput("Action", "Action", "inAction") 15 | self.newInput("Float", "Delay", "delay", value = 5) 16 | self.newOutput("Action", "Action", "outAction") 17 | 18 | def draw(self, layout): 19 | layout.prop(self, "relative") 20 | 21 | def execute(self, action, delay): 22 | if action is None: 23 | return None 24 | 25 | return DelayAction(action, delay, self.relative) 26 | -------------------------------------------------------------------------------- /animation_nodes/nodes/mesh/get_uv_map.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode 3 | from ... data_structures import AttributeType 4 | 5 | class GetUVMapNode(AnimationNode, bpy.types.Node): 6 | bl_idname = "an_GetUVMapNode" 7 | bl_label = "Get UV Map" 8 | errorHandlingType = "EXCEPTION" 9 | 10 | def create(self): 11 | self.newInput("Mesh", "Mesh", "mesh") 12 | self.newInput("Text", "Name", "uvMapName") 13 | 14 | self.newOutput("Vector 2D List", "Positions", "positions") 15 | 16 | def execute(self, mesh, uvMapName): 17 | if uvMapName == "": 18 | self.raiseErrorMessage("UV map name can't be empty.") 19 | 20 | uvMap = mesh.getUVMapAttribute(uvMapName) 21 | if uvMap is None: 22 | self.raiseErrorMessage(f"Mesh doesn't have a uv map with the name '{uvMapName}'.") 23 | 24 | return uvMap.data 25 | -------------------------------------------------------------------------------- /animation_nodes/nodes/shape_key/shape_keys_from_object.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode 3 | 4 | class ShapeKeysFromObjectNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_ShapeKeysFromObjectNode" 6 | bl_label = "Shape Keys from Object" 7 | 8 | def create(self): 9 | self.newInput("Object", "Object", "object").defaultDrawType = "PROPERTY_ONLY" 10 | self.newOutput("Shape Key List", "Shape Keys", "shapeKeys") 11 | self.newOutput("Shape Key", "Reference Key", "referenceKey") 12 | 13 | def execute(self, object): 14 | if object is None: return [], None 15 | if object.type not in ("MESH", "CURVE", "LATTICE"): return [], None 16 | if object.data.shape_keys is None: return [], None 17 | 18 | reference = object.data.shape_keys.reference_key 19 | return list(object.data.shape_keys.key_blocks)[1:], reference 20 | -------------------------------------------------------------------------------- /animation_nodes/nodes/viewer/matrix_shader.glsl: -------------------------------------------------------------------------------- 1 | // Vertex Shader 2 | 3 | uniform int u_Count; 4 | uniform mat4 u_ViewProjectionMatrix; 5 | 6 | in vec3 pos; 7 | flat out vec4 v_Color; 8 | 9 | void main() 10 | { 11 | float s = (float(gl_VertexID / 4) / u_Count) * 0.5; 12 | switch(gl_VertexID % 4) 13 | { 14 | // case 0 ignored because of the flat interpolation qualifier of v_Color. 15 | case 1: 16 | v_Color = vec4(1.0, s, s, 1.0); 17 | break; 18 | case 2: 19 | v_Color = vec4(s, 1.0, s, 1.0); 20 | break; 21 | case 3: 22 | v_Color = vec4(s, s, 1.0, 1.0); 23 | break; 24 | } 25 | gl_Position = u_ViewProjectionMatrix * vec4(pos, 1.0); 26 | } 27 | 28 | // Fragment Shader 29 | 30 | flat in vec4 v_Color; 31 | out vec4 fragColor; 32 | 33 | void main() 34 | { 35 | fragColor = v_Color; 36 | } 37 | -------------------------------------------------------------------------------- /animation_nodes/nodes/interpolation/from_fcurve.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode 3 | from ... algorithms.interpolations import Linear, FCurveMapping 4 | 5 | class InterpolationFromFCurveNode(AnimationNode, bpy.types.Node): 6 | bl_idname = "an_InterpolationFromFCurveNode" 7 | bl_label = "Interpolation from FCurve" 8 | 9 | def create(self): 10 | self.newInput("FCurve", "FCurve", "fCurve") 11 | self.newOutput("Interpolation", "Interpolation", "interpolation") 12 | 13 | def execute(self, fCurve): 14 | if fCurve is None: return Linear() 15 | startX, endX = fCurve.range() 16 | if startX == endX: return Linear() 17 | startY = fCurve.evaluate(startX) 18 | endY = fCurve.evaluate(endX) 19 | if startY == endY: return Linear() 20 | 21 | return FCurveMapping(fCurve, startX, endX - startX, -startY, 1 / (endY - startY)) 22 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/falloffs/types.pxd: -------------------------------------------------------------------------------- 1 | from .. lists.clist cimport CList 2 | from . falloff_base cimport BaseFalloff 3 | 4 | ctypedef object (*PyConversionFunction)(object value, void *target) 5 | ctypedef float (*EvaluateBaseConverted)(BaseFalloff, void *value, Py_ssize_t index) 6 | ctypedef void (*ConvertList)(void *source, void *target, Py_ssize_t amount) 7 | 8 | cdef set getFalloffDataTypes() 9 | cdef bint falloffDataTypeExists(str dataType) 10 | cdef getSizeOfFalloffDataType(str dataType) 11 | cdef typeConversionRequired(str sourceType, str dataType) 12 | cdef EvaluateBaseConverted getCallConvertedFunction(str sourceType, str dataType) 13 | cdef PyConversionFunction getPyConversionFunction(str dataType) 14 | cdef ConvertList getConvertListFunction(str sourceType, str dataType) 15 | cdef bint isValidSourceForDataTypes(str sourceType, dataTypes) 16 | cdef bint isValidSourceForDataType(str sourceType, str dataType) 17 | -------------------------------------------------------------------------------- /animation_nodes/nodes/fcurve/fcurve_keyframes.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode 3 | from ... data_structures import FloatList, DoubleList 4 | 5 | class FCurveKeyframesNode(AnimationNode, bpy.types.Node): 6 | bl_idname = "an_FCurveKeyframesNode" 7 | bl_label = "FCurve Keyframes" 8 | 9 | def create(self): 10 | self.newInput("FCurve", "FCurve", "fCurve") 11 | self.newOutput("Float List", "Keyframes Frames", "keyframesFrames") 12 | self.newOutput("Float List", "Keyframes Values", "keyframesValues") 13 | 14 | def execute(self, fCurve): 15 | if fCurve is None: 16 | return DoubleList(), DoubleList() 17 | 18 | allValues = FloatList(len(fCurve.keyframe_points) * 2) 19 | fCurve.keyframe_points.foreach_get("co", allValues.asMemoryView()) 20 | return DoubleList.fromValues(allValues[0::2]), DoubleList.fromValues(allValues[1::2]) 21 | -------------------------------------------------------------------------------- /_setuputils/compile_libraries.py: -------------------------------------------------------------------------------- 1 | from . generic import * 2 | 3 | def execute_CompileLibraries(setupInfoList, addonDirectory): 4 | printHeader("Compile Libraries") 5 | 6 | for task in iterLibraryCompilationTasks(setupInfoList): 7 | task(Utils) 8 | 9 | def iterLibraryCompilationTasks(setupInfoList): 10 | for path in iterLibraryCompilationProviders(setupInfoList): 11 | obj = executePythonFile(path) 12 | fName = "getCompileLibraryTasks" 13 | if not fName in obj: 14 | raise Exception("File does not contain '{}' function:\n {}".formaT(fName, path)) 15 | yield from obj[fName](Utils) 16 | 17 | def iterLibraryCompilationProviders(setupInfoList): 18 | fName = "getLibraryCompilationProviders" 19 | for setupInfo in setupInfoList: 20 | if fName in setupInfo: 21 | for name in setupInfo[fName](): 22 | yield changeFileName(setupInfo["__file__"], name) 23 | -------------------------------------------------------------------------------- /animation_nodes/operators/execute_tree.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.props import * 3 | from .. problems import canExecute 4 | from .. utils.blender_ui import redrawAll 5 | 6 | class ExecuteNodeTree(bpy.types.Operator): 7 | bl_idname = "an.execute_tree" 8 | bl_label = "Execute Node Tree" 9 | bl_description = "Execute all main networks in the tree" 10 | 11 | name: StringProperty(name = "Node Tree Name") 12 | 13 | @classmethod 14 | def poll(cls, context): 15 | return canExecute() 16 | 17 | def execute(self, context): 18 | nodeTree = bpy.data.node_groups.get(self.name) 19 | if nodeTree is not None: 20 | if nodeTree.bl_idname == "an_AnimationNodeTree": 21 | nodeTree.execute() 22 | redrawAll() 23 | return {"FINISHED"} 24 | self.report({"ERROR"}, "{} is no animation nodes tree".format(repr(self.name))) 25 | return {"CANCELLED"} 26 | -------------------------------------------------------------------------------- /animation_nodes/nodes/vector/vector_length.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode, VectorizedSocket 3 | from . c_utils import calculateVectorLengths 4 | 5 | class VectorLengthNode(AnimationNode, bpy.types.Node): 6 | bl_idname = "an_VectorLengthNode" 7 | bl_label = "Vector Length" 8 | 9 | useList: VectorizedSocket.newProperty() 10 | 11 | def create(self): 12 | self.newInput(VectorizedSocket("Vector", "useList", 13 | ("Vector", "vector"), ("Vectors", "vectors"))) 14 | 15 | self.newOutput(VectorizedSocket("Float", "useList", 16 | ("Length", "length"), ("Lenghts", "lengths"))) 17 | 18 | def getExecutionCode(self, required): 19 | if self.useList: 20 | yield "lengths = self.calcLengths(vectors)" 21 | else: 22 | yield "length = vector.length" 23 | 24 | def calcLengths(self, vectors): 25 | return calculateVectorLengths(vectors) 26 | -------------------------------------------------------------------------------- /_setuputils/copy_addon.py: -------------------------------------------------------------------------------- 1 | import os 2 | from . generic import * 3 | from . addon_files import iterRelativeAddonFiles 4 | 5 | def execute_CopyAddon(addonDirectory, targetPath, addonName): 6 | targetPath = os.path.join(targetPath, addonName) 7 | printHeader("Copy Addon") 8 | try: 9 | changes = syncDirectories(addonDirectory, targetPath, iterRelativeAddonFiles) 10 | except PermissionError: 11 | raise Exception("\n\nGot a permission error.\nThis might happen when Blender is open while copying.") 12 | 13 | for path in changes["removed"]: 14 | print("Removed:", os.path.relpath(path, targetPath)) 15 | for path in changes["updated"]: 16 | print("Updated:", os.path.relpath(path, targetPath)) 17 | for path in changes["created"]: 18 | print("Created:", os.path.relpath(path, targetPath)) 19 | 20 | totalChanged = sum(len(l) for l in changes.values()) 21 | print("\nModified {} files.".format(totalChanged)) 22 | -------------------------------------------------------------------------------- /animation_nodes/nodes/mesh/generation/cylinder.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from .... base_types import AnimationNode 3 | from .... algorithms.mesh_generation.cylinder import getCylinderMesh 4 | 5 | class CylinderMeshNode(AnimationNode, bpy.types.Node): 6 | bl_idname = "an_CylinderMeshNode" 7 | bl_label = "Cylinder Mesh" 8 | 9 | def create(self): 10 | self.newInput("Float", "Radius", "radius", value = 1, minValue = 0) 11 | self.newInput("Float", "Height", "height", value = 2, minValue = 0) 12 | self.newInput("Integer", "Resolution", "resolution", value = 8, minValue = 2) 13 | self.newInput("Boolean", "Caps", "caps", value = True) 14 | 15 | self.newOutput("Mesh", "Mesh", "mesh") 16 | 17 | def execute(self, radius, height, resolution, caps): 18 | resolution = max(resolution, 2) 19 | radius = max(radius, 0) 20 | height = max(height, 0) 21 | return getCylinderMesh(radius, height, resolution, caps) -------------------------------------------------------------------------------- /animation_nodes/nodes/mesh/separate_polygons.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from . c_utils import separatePolygons 3 | from ... base_types import AnimationNode 4 | 5 | class SeparatePolygonsNode(AnimationNode, bpy.types.Node): 6 | bl_idname = "an_SeparatePolygonsNode" 7 | bl_label = "Separate Polygons" 8 | errorHandlingType = "EXCEPTION" 9 | 10 | def create(self): 11 | self.newInput("Vector List", "Vertex Locations", "inVertices") 12 | self.newInput("Polygon Indices List", "Polygon Indices", "inPolygonIndices") 13 | 14 | self.newOutput("Vector List", "Vertices", "outVertices") 15 | self.newOutput("Polygon Indices List", "Polygon Indices", "outPolygonIndices") 16 | 17 | def execute(self, vertices, polygons): 18 | if len(polygons) == 0 or polygons.getMaxIndex() < len(vertices): 19 | return separatePolygons(vertices, polygons) 20 | else: 21 | self.raiseErrorMessage("Invalid polygon indices") 22 | -------------------------------------------------------------------------------- /animation_nodes/nodes/mesh/bmesh_from_object.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import bmesh 3 | from ... base_types import AnimationNode 4 | from ... utils.depsgraph import getActiveDepsgraph, getEvaluatedID 5 | 6 | class BMeshFromObjectNode(AnimationNode, bpy.types.Node): 7 | bl_idname = "an_BMeshFromObjectNode" 8 | bl_label = "BMesh from Object" 9 | 10 | def create(self): 11 | self.newInput("Object", "Object", "object", defaultDrawType = "PROPERTY_ONLY") 12 | self.newInput("Boolean", "Use World Space", "useWorldSpace", value = True) 13 | self.newOutput("BMesh", "BMesh", "bm") 14 | 15 | def execute(self, object, useWorldSpace): 16 | bm = bmesh.new() 17 | if getattr(object, "type", "") != "MESH": return bm 18 | bm.from_object(object, getActiveDepsgraph()) 19 | if useWorldSpace: 20 | evaluatedObject = getEvaluatedID(object) 21 | bm.transform(evaluatedObject.matrix_world) 22 | return bm 23 | -------------------------------------------------------------------------------- /animation_nodes/algorithms/random_number_generators/xoshiro256starstar.pxd: -------------------------------------------------------------------------------- 1 | from libc.stdint cimport uint64_t 2 | 3 | cdef class XoShiRo256StarStar: 4 | cdef uint64_t s0 5 | cdef uint64_t s1 6 | cdef uint64_t s2 7 | cdef uint64_t s3 8 | 9 | cdef uint64_t nextUInt64(self) 10 | cdef long long nextLongLong(self) 11 | cdef long nextLong(self) 12 | cdef int nextInt(self) 13 | cdef bint nextBoolean(self) 14 | 15 | cdef uint64_t nextUInt64WithMax(self, uint64_t maximum) 16 | cdef long long nextLongLongWithMax(self, uint64_t maximum) 17 | cdef long nextLongWithMax(self, uint64_t maximum) 18 | cdef int nextIntWithMax(self, uint64_t maximum) 19 | 20 | cdef uint64_t nextUInt64InRange(self, uint64_t start, uint64_t end) 21 | cdef long long nextLongLongInRange(self, uint64_t start, uint64_t end) 22 | cdef long nextLongInRange(self, uint64_t start, uint64_t end) 23 | cdef int nextIntInRange(self, uint64_t start, uint64_t end) 24 | -------------------------------------------------------------------------------- /animation_nodes/nodes/vector/vector_from_value.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from . c_utils import vectorsFromValues 3 | from ... base_types import AnimationNode, VectorizedSocket 4 | 5 | class VectorFromValueNode(AnimationNode, bpy.types.Node): 6 | bl_idname = "an_VectorFromValueNode" 7 | bl_label = "Vector from Value" 8 | 9 | useList: VectorizedSocket.newProperty() 10 | 11 | def create(self): 12 | self.newInput(VectorizedSocket("Float", "useList", 13 | ("Value", "value"), ("Values", "values"))) 14 | 15 | self.newOutput(VectorizedSocket("Vector", "useList", 16 | ("Vector", "vector"), ("Vectors", "vectors"))) 17 | 18 | def getExecutionCode(self, required): 19 | if self.useList: 20 | return "vectors = self.vectorsFromValues(values)" 21 | else: 22 | return "vector = Vector((value, value, value))" 23 | 24 | def vectorsFromValues(self, values): 25 | return vectorsFromValues(values) 26 | -------------------------------------------------------------------------------- /animation_nodes/draw_handler.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from collections import defaultdict 3 | 4 | drawHandlers = [] 5 | registeredHandlersPerEditor = defaultdict(list) 6 | 7 | def drawHandler(editorName, regionName, drawType = "POST_PIXEL"): 8 | def drawHandlerDecorator(function): 9 | drawHandlers.append((function, editorName, regionName, drawType)) 10 | return function 11 | 12 | return drawHandlerDecorator 13 | 14 | def register(): 15 | for function, editorName, regionName, drawType in drawHandlers: 16 | editor = getattr(bpy.types, editorName) 17 | handler = editor.draw_handler_add(function, (), regionName, drawType) 18 | registeredHandlersPerEditor[editor].append((handler, regionName)) 19 | 20 | def unregister(): 21 | for editor, handlers in registeredHandlersPerEditor.items(): 22 | for handler, regionName in handlers: 23 | editor.draw_handler_remove(handler, regionName) 24 | registeredHandlersPerEditor.clear() 25 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/lists/test_utils.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | from . utils import predictSliceLength, makeStepPositive 3 | 4 | class TestPredictSliceLength(TestCase): 5 | def testSimple(self): 6 | self.check(0, 10, 1) 7 | self.check(0, 10, 2) 8 | self.check(0, 10, 11) 9 | 10 | self.check(1, 10, 1) 11 | self.check(2, 10, 1) 12 | self.check(11, 10, 1) 13 | 14 | self.check(10, 0, -1) 15 | self.check(10, 0, -2) 16 | self.check(10, 3, -3) 17 | self.check(3, 3, -3) 18 | 19 | def check(self, start, stop, step): 20 | real = len(tuple(range(start, stop, step))) 21 | calculated = predictSliceLength(start, stop, step) 22 | self.assertEqual(real, calculated) 23 | 24 | class TestMakeStepPositive(TestCase): 25 | def test(self): 26 | self.assertEqual(makeStepPositive(9, -1, -2), (1, 10, 2)) 27 | self.assertEqual(makeStepPositive(10, 5, -1), (6, 11, 1)) 28 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/action/__types_declaration.src: -------------------------------------------------------------------------------- 1 | cdef class BOUNDTYPEAction(Action): 2 | cdef BOUNDTYPEActionEvaluator getEvaluator_Limited(self, list channels) 3 | cpdef BOUNDTYPEActionEvaluator getEvaluator_Full(self, list channels, FloatList defaults) 4 | 5 | cdef class BOUNDTYPEActionEvaluator(ActionEvaluator): 6 | pass 7 | #IF "BOUNDTYPE" == "Bounded" 8 | cdef void evaluateBounded(self, float t, Py_ssize_t index, float *target) 9 | 10 | cpdef float getStart(self, Py_ssize_t index) 11 | cpdef float getEnd(self, Py_ssize_t index) 12 | cpdef float getLength(self, Py_ssize_t index) 13 | #ENDIF 14 | 15 | cdef class SimpleBOUNDTYPEAction(BOUNDTYPEAction): 16 | cdef list getEvaluateFunctions(self) 17 | cdef newFunction(self, void *function, list channels) 18 | 19 | #IF "BOUNDTYPE" == "Bounded" 20 | cdef float getStart(self, Py_ssize_t index) 21 | cdef float getEnd(self, Py_ssize_t index) 22 | cdef float getLength(self, Py_ssize_t index) 23 | #ENDIF 24 | -------------------------------------------------------------------------------- /animation_nodes/utils/enum_items.py: -------------------------------------------------------------------------------- 1 | import functools 2 | from collections import defaultdict 3 | from .. algorithms.hashing import strToEnumItemID 4 | 5 | def enumItemsFromList(itemData): 6 | items = [] 7 | for element in itemData: 8 | items.append((element, element, "", "NONE", strToEnumItemID(element))) 9 | if len(items) == 0: 10 | items = [("NONE", "NONE", "")] 11 | return items 12 | 13 | cachedItemTuplesByHash = defaultdict(list) 14 | 15 | def cacheEnumItems(itemsCB): 16 | 17 | @functools.wraps(itemsCB) 18 | def wrapper(self, context): 19 | itemTuples = tuple(itemsCB(self, context)) 20 | itemTuplesHash = hash(itemTuples) 21 | 22 | for cachedItemTuple in cachedItemTuplesByHash[itemTuplesHash]: 23 | if cachedItemTuple == itemTuples: 24 | return cachedItemTuple 25 | else: 26 | cachedItemTuplesByHash[itemTuplesHash].append(itemTuples) 27 | return itemTuples 28 | 29 | return wrapper 30 | -------------------------------------------------------------------------------- /animation_nodes/utils/recursion.py: -------------------------------------------------------------------------------- 1 | import functools 2 | 3 | activeFunctions = set() 4 | 5 | def noRecursion(function): 6 | """The decorated function should not return any values""" 7 | @functools.wraps(function) 8 | def wrapper(*args, **kwargs): 9 | identifier = id(function) 10 | if identifier not in activeFunctions: 11 | activeFunctions.add(identifier) 12 | result = function(*args, **kwargs) 13 | activeFunctions.remove(identifier) 14 | return result 15 | return wrapper 16 | 17 | def noCallbackRecursion(function): 18 | """The decorated function should not return any values""" 19 | @functools.wraps(function) 20 | def wrapper(self, context): 21 | identifier = id(function) 22 | if identifier not in activeFunctions: 23 | activeFunctions.add(identifier) 24 | result = function(self, context) 25 | activeFunctions.remove(identifier) 26 | return result 27 | return wrapper 28 | -------------------------------------------------------------------------------- /animation_nodes/nodes/bvh_tree/is_inside_volume.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode, VectorizedSocket 3 | 4 | class IsInsideVolumeBVHTreeNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_IsInsideVolumeBVHTreeNode" 6 | bl_label = "Is Inside Volume" 7 | codeEffects = [VectorizedSocket.CodeEffect] 8 | 9 | useVectorList: VectorizedSocket.newProperty() 10 | 11 | def create(self): 12 | self.newInput("BVHTree", "BVHTree", "bvhTree", defaultDrawType = "PROPERTY_ONLY") 13 | 14 | self.newInput(VectorizedSocket("Vector", "useVectorList", 15 | ("Vector", "vector", dict(defaultDrawType = "PROPERTY_ONLY")), 16 | ("Vectors", "vectors", dict(defaultDrawType = "PROPERTY_ONLY")))) 17 | 18 | self.newOutput(VectorizedSocket("Boolean", "useVectorList", 19 | ("Is Inside", "isInside"), ("Are Inside", "areInside"))) 20 | 21 | def getExecutionCode(self, required): 22 | return "isInside = AN.utils.bvh.isInsideVolume(bvhTree, vector)" 23 | -------------------------------------------------------------------------------- /animation_nodes/nodes/geometry/bmesh_limited_dissolve.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.props import * 3 | from math import radians 4 | from bmesh.ops import dissolve_limit 5 | from ... base_types import AnimationNode 6 | 7 | class BMeshLimitedDissolveNode(AnimationNode, bpy.types.Node): 8 | bl_idname = "an_BMeshLimitedDissolveNode" 9 | bl_label = "Limited Dissolve BMesh" 10 | bl_width_default = 160 11 | 12 | def create(self): 13 | self.newInput("BMesh", "BMesh", "bm", dataIsModified = True) 14 | self.newInput("Float", "Angle Limit", "angleLimit", value = 30.0) 15 | self.newInput("Boolean", "Dissolve Boundries", "dissolveBoundries", value = False) 16 | self.newOutput("BMesh", "BMesh", "bm") 17 | 18 | def execute(self, bm, angleLimit, dissolveBoundries): 19 | dissolve_limit(bm, verts = bm.verts, edges = bm.edges, 20 | angle_limit = radians(angleLimit), 21 | use_dissolve_boundaries = dissolveBoundries) 22 | return bm 23 | -------------------------------------------------------------------------------- /animation_nodes/nodes/mesh/get_linked_vertices.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode 3 | from ... data_structures import LongList 4 | 5 | class GetLinkedVerticesNode(AnimationNode, bpy.types.Node): 6 | bl_idname = "an_GetLinkedVerticesNode" 7 | bl_label = "Get Linked Vertices" 8 | errorHandlingType = "EXCEPTION" 9 | 10 | def create(self): 11 | self.newInput("Mesh", "Mesh", "mesh") 12 | self.newInput("Integer", "Vextex Index", "vertexIndex") 13 | 14 | self.newOutput("Integer List", "Vertices", "vertexIndices") 15 | self.newOutput("Integer List", "Edges", "edgeIndices") 16 | self.newOutput("Integer", "Amount", "amount") 17 | 18 | def execute(self, mesh, vertexIndex): 19 | if mesh is None: 20 | return LongList(), LongList(), 0 21 | if vertexIndex < 0 or vertexIndex >= len(mesh.vertices): 22 | self.raiseErrorMessage("Vertex Index is out of range.") 23 | 24 | return mesh.getVertexLinkedVertices(vertexIndex) 25 | -------------------------------------------------------------------------------- /animation_nodes/sockets/implicit_conversion.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | 3 | implicitConversions = defaultdict(dict) 4 | conversionFunctions = [] 5 | 6 | def registerImplicitConversion(fromType, toType, converter): 7 | if hasattr(converter, "__call__"): 8 | conversionFunctions.append(converter) 9 | path = "AN.sockets.implicit_conversion.conversionFunctions[{}](value)" 10 | path = path.format(len(conversionFunctions) - 1) 11 | implicitConversions[toType][fromType] = path 12 | elif isinstance(converter, str): 13 | implicitConversions[toType][fromType] = converter 14 | elif converter is None: 15 | implicitConversions[toType][fromType] = None 16 | else: 17 | raise Exception("expected str, function or None") 18 | 19 | def iterTypesThatCanConvertTo(toType): 20 | yield from implicitConversions[toType].keys() 21 | 22 | def getConversionCode(fromType, toType): 23 | try: return implicitConversions[toType][fromType] 24 | except: return None 25 | -------------------------------------------------------------------------------- /animation_nodes/nodes/list/mask_list.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... algorithms.lists import mask as maskList 3 | from ... base_types import AnimationNode, ListTypeSelectorSocket 4 | 5 | class MaskListNode(AnimationNode, bpy.types.Node): 6 | bl_idname = "an_MaskListNode" 7 | bl_label = "Mask List" 8 | 9 | assignedType: ListTypeSelectorSocket.newProperty(default = "Integer List") 10 | 11 | def create(self): 12 | prop = ("assignedType", "LIST") 13 | self.newInput(ListTypeSelectorSocket( 14 | "List", "inList", "LIST", prop)) 15 | self.newInput("Boolean List", "Mask", "mask") 16 | self.newOutput(ListTypeSelectorSocket( 17 | "List", "outList", "LIST", prop)) 18 | 19 | def execute(self, inList, mask): 20 | if len(inList) == len(mask): 21 | return maskList(self.assignedType, inList, mask) 22 | else: 23 | _mask = mask.repeated(length = len(inList), default = True) 24 | return maskList(self.assignedType, inList, _mask) 25 | -------------------------------------------------------------------------------- /animation_nodes/nodes/list/shift_list.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode, ListTypeSelectorSocket 3 | 4 | class ShiftListNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_ShiftListNode" 6 | bl_label = "Shift List" 7 | 8 | assignedType: ListTypeSelectorSocket.newProperty(default = "Float List") 9 | 10 | def create(self): 11 | prop = ("assignedType", "LIST") 12 | self.newInput(ListTypeSelectorSocket( 13 | "List", "inList", "LIST", prop, dataIsModified = True)) 14 | self.newInput("Integer", "Amount", "amount") 15 | self.newOutput(ListTypeSelectorSocket( 16 | "Shifted List", "shiftedList", "LIST", prop, dataIsModified = True)) 17 | 18 | def getExecutionCode(self, required): 19 | yield "if len(inList) == 0: shiftedList = self.outputs[0].getDefaultValue()" 20 | yield "else:" 21 | yield " shiftAmount = amount % len(inList)" 22 | yield " shiftedList = inList[-shiftAmount:] + inList[:-shiftAmount]" 23 | -------------------------------------------------------------------------------- /animation_nodes/operators/generate_socket_data_csv.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from .. sockets.info import getSocketClasses, isList, isBase 3 | 4 | class GenerateSocketDataCSV(bpy.types.Operator): 5 | bl_idname = "an.generate_socket_data_csv" 6 | bl_label = "Generate Socket Data CSV" 7 | 8 | def execute(self, context): 9 | sockets = [socket for socket in getSocketClasses() if not isList(socket.bl_idname)] 10 | sockets.sort(key = lambda x: x.dataType) 11 | output = [] 12 | for socket in sockets: 13 | data = [] 14 | data.append(socket.dataType) 15 | data.append("``{}``".format(socket.bl_idname)) 16 | data.append("Yes" if isBase(socket.bl_idname) else "No") 17 | output.append(", ".join(data)) 18 | 19 | text = bpy.data.texts.get("Socket Data.cvs") 20 | if text is None: 21 | text = bpy.data.texts.new("Socket Data.cvs") 22 | text.clear() 23 | text.write("\n".join(output)) 24 | return {"FINISHED"} 25 | -------------------------------------------------------------------------------- /animation_nodes/nodes/object/utility_nodes/get_selected_objects.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from .... events import isRendering 3 | from .... base_types import AnimationNode 4 | from .... utils.depsgraph import getActiveDepsgraph 5 | from .... utils.selection import getSelectedObjects, getActiveObject 6 | 7 | class GetSelectedObjectsNode(AnimationNode, bpy.types.Node): 8 | bl_idname = "an_GetSelectedObjectsNode" 9 | bl_label = "Get Selected Objects" 10 | searchTags = ["Get Active Object"] 11 | 12 | def create(self): 13 | self.newOutput("Object List", "Selected Objects", "selectedObjects") 14 | self.newOutput("Object", "Active Object", "activeObject") 15 | 16 | def execute(self): 17 | if isRendering(): 18 | return [], None 19 | else: 20 | viewLayer = getActiveDepsgraph().view_layer 21 | return getSelectedObjects(viewLayer), getActiveObject(viewLayer) 22 | 23 | def draw(self, layout): 24 | layout.label(text = "Disabled During Rendering", icon = "INFO") 25 | -------------------------------------------------------------------------------- /animation_nodes/nodes/spline/smooth_bezier_spline.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode, VectorizedSocket 3 | 4 | class SmoothBezierSplineNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_SmoothBezierSplineNode" 6 | bl_label = "Smooth Bezier Spline" 7 | codeEffects = [VectorizedSocket.CodeEffect] 8 | 9 | useSplineList: VectorizedSocket.newProperty() 10 | 11 | def create(self): 12 | socket = self.newInput(VectorizedSocket("Spline", "useSplineList", 13 | ("Spline", "spline"), ("Splines", "splines"))) 14 | socket.defaultDrawType = "TEXT_ONLY" 15 | socket.dataIsModified = True 16 | 17 | self.newInput("Float", "Smoothness", "smoothness", value = 0.3333) 18 | 19 | self.newOutput(VectorizedSocket("Spline", "useSplineList", 20 | ("Spline", "spline"), ("Splines", "splines"))) 21 | 22 | def getExecutionCode(self, required): 23 | yield "if spline.type == 'BEZIER':" 24 | yield " spline.smoothAllHandles(smoothness)" 25 | -------------------------------------------------------------------------------- /animation_nodes/nodes/container_provider.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from .. utils.names import getPossibleNodeName 3 | 4 | mainObjectContainerName = "Animation Nodes Object Container" 5 | helperMaterialName = "Helper Material for Animation Nodes" 6 | 7 | 8 | def getMainObjectContainer(scene): 9 | objectContainer = bpy.data.collections.get(mainObjectContainerName) 10 | if objectContainer is None: 11 | objectContainer = bpy.data.collections.new(mainObjectContainerName) 12 | if objectContainer.name not in scene.collection.children: 13 | scene.collection.children.link(objectContainer) 14 | return objectContainer 15 | 16 | def getHelperMaterial(): 17 | helperMaterial = bpy.data.materials.get(helperMaterialName) 18 | if helperMaterial is None: 19 | helperMaterial = newHelperMaterial() 20 | return helperMaterial 21 | 22 | def newHelperMaterial(): 23 | material = bpy.data.materials.new(helperMaterialName) 24 | material.use_nodes = True 25 | material.use_fake_user = True 26 | return material 27 | -------------------------------------------------------------------------------- /animation_nodes/nodes/geometry/point_list_normal.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode 3 | 4 | class PointListNormalNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_PointListNormalNode" 6 | bl_label = "Point List Normal" 7 | bl_width_default = 160 8 | errorHandlingType = "MESSAGE" 9 | searchTags = ["Points Normal", "Calculate Normal"] 10 | 11 | def create(self): 12 | self.newInput("Vector List", "Point List", "points") 13 | self.newOutput("Vector", "Normal", "normal") 14 | self.newOutput("Boolean", "Is Valid", "isValid") 15 | 16 | def getExecutionCode(self, required): 17 | yield "if len(points) >= 3:" 18 | yield " normal = mathutils.geometry.normal(points)" 19 | yield "else:" 20 | yield " normal = Vector((0, 0, 0))" 21 | yield " self.setErrorMessage('Expected min 3 different vectors')" 22 | 23 | yield "isValid = normal[:] != (0, 0, 0)" 24 | 25 | def getUsedModules(self): 26 | return ["mathutils"] 27 | -------------------------------------------------------------------------------- /animation_nodes/nodes/mesh/generation/unity_triangle.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from .... base_types import AnimationNode 3 | from .... data_structures import ( 4 | Mesh, 5 | LongList, 6 | Vector3DList, 7 | EdgeIndicesList, 8 | PolygonIndicesList, 9 | ) 10 | 11 | class UnityTriangleMeshNode(AnimationNode, bpy.types.Node): 12 | bl_idname = "an_UnityTriangleMeshNode" 13 | bl_label = "Unity Triangle Mesh" 14 | 15 | def create(self): 16 | self.newOutput("Mesh", "Mesh", "meshData") 17 | 18 | def execute(self): 19 | return mesh.copy() 20 | 21 | vertexLocations = Vector3DList.fromValues([ 22 | (-3**-0.25, -3**-0.75, 0), 23 | (3**-0.25, -3**-0.75, 0), 24 | (0, 2/3**0.75, 0) 25 | ]) 26 | edgeIndices = EdgeIndicesList.fromValues( 27 | [(0, 1), (1, 2), (2, 0)] 28 | ) 29 | polygonIndices = PolygonIndicesList.fromValues( 30 | [(0, 1, 2)] 31 | ) 32 | materialIndices = LongList.fromValues( 33 | [0] 34 | ) 35 | mesh = Mesh(vertexLocations, edgeIndices, polygonIndices, skipValidation = True) 36 | -------------------------------------------------------------------------------- /animation_nodes/utils/layout.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import textwrap 3 | from . blender_ui import getDpiFactor 4 | 5 | def splitAlignment(layout): 6 | row = layout.row() 7 | left = row.row() 8 | left.alignment = "LEFT" 9 | right = row.row() 10 | right.alignment = "RIGHT" 11 | return left, right 12 | 13 | def writeText(layout, text, width = 30, icon = "NONE", autoWidth = False): 14 | if autoWidth == True: 15 | try: width = bpy.context.region.width / getDpiFactor() / 7 16 | except: width = 30 17 | 18 | col = layout.column(align = True) 19 | col.scale_y = 0.85 20 | prefix = " " 21 | for paragraph in text.split("\n"): 22 | lines = textwrap.wrap(paragraph, int(width)) 23 | 24 | if len(lines) == 0: 25 | subcol = col.column() 26 | subcol.scale_y = 0.3 27 | subcol.label(text = "") 28 | 29 | for line in lines: 30 | col.label(text = prefix + line, icon = icon) 31 | if icon != "NONE": prefix = " " 32 | icon = "NONE" 33 | -------------------------------------------------------------------------------- /animation_nodes/execution/compile_scripts.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from .. problems import InvalidSyntax 3 | 4 | cache = {} 5 | 6 | def compileScript(script, name = ""): 7 | # clear the cache once in a while 8 | if len(cache) == 500: 9 | cache.clear() 10 | 11 | try: 12 | scriptHash = hash(script) 13 | if scriptHash not in cache: 14 | compiledCode = compile(script, name, "exec") 15 | cache[scriptHash] = compiledCode 16 | 17 | return cache[scriptHash] 18 | 19 | except SyntaxError: 20 | lines = script.split("\n") 21 | lineNumber = sys.exc_info()[1].lineno 22 | lineNumberWidth = len(str(len(lines))) 23 | 24 | print("\n"*5) 25 | for i, line in enumerate(lines): 26 | linePrefix = str(i + 1).rjust(lineNumberWidth) + ". " 27 | linesSuffix = " <-------------- Error happens here" if lineNumber == i + 1 else "" 28 | print(linePrefix + line + linesSuffix) 29 | print("\n"*5) 30 | 31 | InvalidSyntax().report() 32 | -------------------------------------------------------------------------------- /animation_nodes/nodes/list/reverse_list.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... sockets.info import isList 3 | from ... base_types import AnimationNode, ListTypeSelectorSocket 4 | 5 | class ReverseListNode(AnimationNode, bpy.types.Node): 6 | bl_idname = "an_ReverseListNode" 7 | bl_label = "Reverse List" 8 | 9 | assignedType: ListTypeSelectorSocket.newProperty(default = "Float List") 10 | 11 | def create(self): 12 | prop = ("assignedType", "LIST") 13 | self.newInput(ListTypeSelectorSocket( 14 | "List", "inList", "LIST", prop, dataIsModified = True)) 15 | self.newOutput(ListTypeSelectorSocket( 16 | "Reversed List", "reversedList", "LIST", prop)) 17 | 18 | def getExecutionCode(self, required): 19 | yield "reversedList = AN.algorithms.lists.reverse('%s', inList)" % self.assignedType 20 | 21 | def assignType(self, listDataType): 22 | if not isList(listDataType): return 23 | if listDataType == self.assignedType: return 24 | self.assignedType = listDataType 25 | self.refresh() 26 | -------------------------------------------------------------------------------- /animation_nodes/nodes/vector/vector_list_math.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.props import * 3 | from ... events import executionCodeChanged 4 | from ... base_types import AnimationNode 5 | 6 | operationItems = [ 7 | ("ADD", "Add", "", "", 0), 8 | ("AVERAGE", "Average", "", "", 1) ] 9 | 10 | class VectorListMathNode(AnimationNode, bpy.types.Node): 11 | bl_idname = "an_VectorListMathNode" 12 | bl_label = "Vector List Math" 13 | 14 | operation: EnumProperty(name = "Operation", default = "ADD", 15 | items = operationItems, update = executionCodeChanged) 16 | 17 | def create(self): 18 | self.newInput("Vector List", "Vector List", "vectors") 19 | self.newOutput("Vector", "Result", "result") 20 | 21 | def draw(self, layout): 22 | layout.prop(self, "operation", text = "") 23 | 24 | def getExecutionCode(self, required): 25 | if self.operation == "ADD": 26 | yield "result = vectors.getSumOfElements()" 27 | elif self.operation == "AVERAGE": 28 | yield "result = vectors.getAverageOfElements()" 29 | -------------------------------------------------------------------------------- /animation_nodes/nodes/spline/bevel_spline.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... data_structures import VirtualDoubleList 3 | from ... base_types import AnimationNode, VectorizedSocket 4 | from ... algorithms.spline.bevel_poly_spline import bevelPolySpline 5 | 6 | class BevelSplineNode(AnimationNode, bpy.types.Node): 7 | bl_idname = "an_BevelSplineNode" 8 | bl_label = "Bevel Spline" 9 | errorHandlingType = "EXCEPTION" 10 | 11 | useRadiusList: VectorizedSocket.newProperty() 12 | 13 | def create(self): 14 | self.newInput("Spline", "Spline", "spline", defaultDrawType = "PROPERTY_ONLY") 15 | 16 | self.newInput(VectorizedSocket("Float", "useRadiusList", 17 | ("Radius", "radius", dict(value = 0.5, minValue = 0)), 18 | ("Radii", "radii"))) 19 | 20 | self.newOutput("Spline", "Spline", "spline") 21 | 22 | def execute(self, spline, radii): 23 | if spline.type != "POLY": 24 | self.raiseErrorMessage("Bezier splines are not supported!") 25 | return bevelPolySpline(spline, VirtualDoubleList.create(radii, 0.5)) 26 | -------------------------------------------------------------------------------- /animation_nodes/nodes/interpolation/evaluate.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode, VectorizedSocket 3 | 4 | class EvaluateInterpolationNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_EvaluateInterpolationNode" 6 | bl_label = "Evaluate Interpolation" 7 | bl_width_default = 160 8 | 9 | useList: VectorizedSocket.newProperty() 10 | 11 | def create(self): 12 | self.newInput(VectorizedSocket("Float", "useList", 13 | ("Position", "position", dict(minValue = 0, maxValue = 1)), 14 | ("Positions", "positions"))) 15 | 16 | self.newInput("Interpolation", "Interpolation", "interpolation", 17 | defaultDrawType = "PROPERTY_ONLY") 18 | 19 | self.newOutput(VectorizedSocket("Float", "useList", 20 | ("Value", "value"), ("Values", "values"))) 21 | 22 | def getExecutionCode(self, required): 23 | if self.useList: 24 | return "values = interpolation.evaluateList(positions)" 25 | else: 26 | return "value = interpolation(max(min(position, 1.0), 0.0))" 27 | -------------------------------------------------------------------------------- /animation_nodes/nodes/particles/c_utils.pyx: -------------------------------------------------------------------------------- 1 | from libc.math cimport sqrt 2 | from ... data_structures cimport QuaternionList 3 | 4 | cdef float sin_45 = (1.0 / sqrt(2.0)) 5 | 6 | # The particle rotations returned by Blender don't align with the actual rotations 7 | # of the particles. This function corrects the rotations by shifting the quaternion 8 | # components and rotating 90 degrees along the negative z axis. 9 | def correctParticleRotations(QuaternionList rotations): 10 | cdef QuaternionList correctedRotations = QuaternionList(length = len(rotations)) 11 | cdef Py_ssize_t i 12 | cdef float ws, xs, ys, zs 13 | for i in range(len(rotations)): 14 | ws = rotations.data[i].w * sin_45 15 | xs = rotations.data[i].x * sin_45 16 | ys = rotations.data[i].y * sin_45 17 | zs = rotations.data[i].z * sin_45 18 | correctedRotations.data[i].w = xs + ws 19 | correctedRotations.data[i].x = ys - zs 20 | correctedRotations.data[i].y = ys + zs 21 | correctedRotations.data[i].z = ws - xs 22 | return correctedRotations 23 | 24 | -------------------------------------------------------------------------------- /_export_c_setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | currentDirectory = os.path.dirname(os.path.abspath(__file__)) 5 | 6 | if not os.path.samefile(currentDirectory, os.getcwd()): 7 | print("You are not in the correct directory.") 8 | print("Expected:", currentDirectory) 9 | print("Got: ", os.getcwd()) 10 | sys.exit() 11 | 12 | if currentDirectory not in sys.path: 13 | sys.path.append(currentDirectory) 14 | 15 | addonName = "animation_nodes" 16 | addonDirectory = os.path.join(currentDirectory, addonName) 17 | exportPath = os.path.join(currentDirectory, "{}.zip".format(addonName)) 18 | 19 | from _setuputils.export import execute_Export 20 | from _setuputils.compilation import execute_Compile 21 | from _setuputils.compile_libraries import execute_CompileLibraries 22 | from _setuputils.setup_info_files import getSetupInfoList 23 | 24 | setupInfoList = getSetupInfoList(addonDirectory) 25 | 26 | execute_CompileLibraries(setupInfoList, addonDirectory) 27 | execute_Compile(setupInfoList, addonDirectory) 28 | execute_Export(addonDirectory, exportPath, addonName) 29 | 30 | print("\nDone.") 31 | -------------------------------------------------------------------------------- /animation_nodes/ui/header.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | def subprograms_menu_callback(self, context): 4 | if context.space_data.tree_type != "an_AnimationNodeTree": return 5 | self.layout.menu("AN_MT_subprograms_menu", text="Subprograms") 6 | 7 | def remove_nodetree_callback(self, context): 8 | if context.space_data.tree_type != "an_AnimationNodeTree": return 9 | self.layout.operator("an.remove_node_tree", text="Remove", icon="CANCEL") 10 | 11 | def copy_nodetree_callback(self, context): 12 | if context.space_data.tree_type != "an_AnimationNodeTree": return 13 | self.layout.operator("an.copy_node_tree", text="Copy", icon="COPYDOWN") 14 | 15 | def register(): 16 | bpy.types.NODE_MT_editor_menus.append(subprograms_menu_callback) 17 | bpy.types.NODE_HT_header.append(copy_nodetree_callback) 18 | bpy.types.NODE_HT_header.append(remove_nodetree_callback) 19 | 20 | def unregister(): 21 | bpy.types.NODE_MT_editor_menus.remove(subprograms_menu_callback) 22 | bpy.types.NODE_HT_header.remove(copy_nodetree_callback) 23 | bpy.types.NODE_HT_header.remove(remove_nodetree_callback) 24 | -------------------------------------------------------------------------------- /animation_nodes/utils/bvh.py: -------------------------------------------------------------------------------- 1 | from random import random 2 | from mathutils import Vector 3 | 4 | # in some cases multiple tests have to done 5 | # to reduce the probability for errors 6 | direction1 = Vector((random(), random(), random())).normalized() 7 | direction2 = Vector((random(), random(), random())).normalized() 8 | direction3 = Vector((random(), random(), random())).normalized() 9 | 10 | def isInsideVolume(bvhTree, vector): 11 | hits1 = countHits(bvhTree, vector, direction1) 12 | if hits1 == 0: return False 13 | if hits1 == 1: return True 14 | 15 | hits2 = countHits(bvhTree, vector, direction2) 16 | if hits1 % 2 == hits2 % 2: 17 | return hits1 % 2 == 1 18 | 19 | hits3 = countHits(bvhTree, vector, direction3) 20 | return hits3 % 2 == 1 21 | 22 | def countHits(bvhTree, start, direction): 23 | hits = 0 24 | offset = direction * 0.0001 25 | location = bvhTree.ray_cast(start, direction)[0] 26 | 27 | while location is not None: 28 | hits += 1 29 | location = bvhTree.ray_cast(location + offset, direction)[0] 30 | 31 | return hits 32 | -------------------------------------------------------------------------------- /_setuputils/cythonize.py: -------------------------------------------------------------------------------- 1 | from . generic import * 2 | 3 | def execute_Cythonize(setupInfoList, addonDirectory, configs): 4 | printHeader("Run Cythonize") 5 | tasks = getCythonizeTasks(addonDirectory, configs) 6 | for i, task in enumerate(tasks, 1): 7 | print("{}/{}:".format(i, len(tasks))) 8 | task.execute() 9 | 10 | def getCythonizeTasks(addonDirectory, configs): 11 | includePaths = configs.get("Cython Include Paths", []) 12 | includePaths.append(".") 13 | 14 | tasks = [] 15 | for path in iterCythonFilePaths(addonDirectory): 16 | tasks.append(CythonizeTask(path, includePaths)) 17 | return tasks 18 | 19 | def iterCythonFilePaths(addonDirectory): 20 | yield from iterPathsWithExtension(addonDirectory, ".pyx") 21 | 22 | class CythonizeTask: 23 | def __init__(self, path, includePaths): 24 | self.path = path 25 | self.includePaths = includePaths 26 | 27 | def execute(self): 28 | from Cython.Build import cythonize 29 | cythonize(self.path, include_path = self.includePaths, compiler_directives = {"language_level" : "3"}) 30 | -------------------------------------------------------------------------------- /animation_nodes/nodes/number/sort.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode 3 | 4 | class SortNumbersNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_SortNumbersNode" 6 | bl_label = "Sort Numbers" 7 | 8 | def create(self): 9 | self.newInput("Float List", "Numbers", "numbers") 10 | self.newOutput("Float List", "Sorted Numbers", "sortedNumbers") 11 | self.newOutput("Integer List", "Indices", "indices") 12 | 13 | def getExecutionCode(self, required): 14 | if "sortedNumbers" in required: 15 | yield "if len(numbers):" 16 | yield " sortedNumbers = DoubleList.fromNumpyArray(numpy.sort(numbers.asMemoryView()))" 17 | yield "else:" 18 | yield " sortedNumbers = DoubleList()" 19 | if "indices" in required: 20 | yield "if len(numbers):" 21 | yield " indices = LongList.fromNumpyArray(numpy.argsort(numbers.asMemoryView()).astype(int))" 22 | yield "else:" 23 | yield " indices = LongList()" 24 | 25 | def getUsedModules(self): 26 | return ["numpy"] 27 | -------------------------------------------------------------------------------- /animation_nodes/nodes/texture/c_utils.pyx: -------------------------------------------------------------------------------- 1 | # cython: profile=True 2 | import cython 3 | from ... data_structures cimport ( 4 | Color, 5 | ColorList, 6 | DoubleList, 7 | Vector3DList 8 | ) 9 | 10 | @cython.cdivision(True) 11 | def getTextureColors(texture, Vector3DList locations, matrix): 12 | cdef long amount = locations.length 13 | cdef ColorList colors = ColorList(length = amount) 14 | cdef DoubleList reds = DoubleList(length = amount) 15 | cdef DoubleList greens = DoubleList(length = amount) 16 | cdef DoubleList blues = DoubleList(length = amount) 17 | cdef DoubleList alphas = DoubleList(length = amount) 18 | cdef DoubleList intensities = DoubleList(length = amount) 19 | cdef float r, g, b, a 20 | 21 | for i in range(amount): 22 | r, g, b, a = texture.evaluate(matrix @ locations[i]) 23 | reds.data[i] = r 24 | greens.data[i] = g 25 | blues.data[i] = b 26 | alphas.data[i] = a 27 | colors.data[i] = Color(r, g, b, a) 28 | intensities.data[i] = (r + g + b) / 3.0 29 | return colors, reds, greens, blues, alphas, intensities 30 | -------------------------------------------------------------------------------- /animation_nodes/utils/lists.pyx: -------------------------------------------------------------------------------- 1 | from libc.math cimport floor 2 | 3 | cpdef findListSegment(long amount, bint cyclic, float parameter): 4 | cdef long[2] indices 5 | cdef float factor 6 | findListSegment_LowLevel(amount, cyclic, parameter, indices, &factor) 7 | return [indices[0], indices[1]], factor 8 | 9 | cdef void findListSegment_LowLevel(long amount, bint cyclic, float parameter, long* index, float* factor): 10 | if not cyclic: 11 | if parameter < 1: 12 | index[0] = floor(parameter * (amount - 1)) 13 | index[1] = index[0] + 1 14 | factor[0] = parameter * (amount - 1) - index[0] 15 | else: 16 | index[0] = amount - 2 17 | index[1] = amount - 1 18 | factor[0] = 1 19 | else: 20 | if parameter < 1: 21 | index[0] = floor(parameter * amount) 22 | index[1] = index[0] + 1 if index[0] < (amount - 1) else 0 23 | factor[0] = parameter * amount - index[0] 24 | else: 25 | index[0] = amount - 1 26 | index[1] = 0 27 | factor[0] = 1 28 | -------------------------------------------------------------------------------- /animation_nodes/nodes/falloff/constant_falloff.pyx: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode 3 | from ... data_structures cimport BaseFalloff 4 | 5 | class ConstantFalloffNode(AnimationNode, bpy.types.Node): 6 | bl_idname = "an_ConstantFalloffNode" 7 | bl_label = "Constant Falloff" 8 | 9 | def create(self): 10 | self.newInput("Float", "Strength", "strength") 11 | self.newOutput("Falloff", "Falloff", "falloff") 12 | 13 | def execute(self, strength): 14 | return ConstantFalloff(strength) 15 | 16 | 17 | cdef class ConstantFalloff(BaseFalloff): 18 | cdef float value 19 | 20 | def __cinit__(self, float value): 21 | self.value = value 22 | self.clamped = 0 <= value <= 1 23 | self.dataType = "NONE" 24 | 25 | cdef float evaluate(self, void *object, Py_ssize_t index): 26 | return self.value 27 | 28 | cdef void evaluateList(self, void *values, Py_ssize_t startIndex, 29 | Py_ssize_t amount, float *target): 30 | cdef Py_ssize_t i 31 | for i in range(amount): 32 | target[i] = self.value 33 | -------------------------------------------------------------------------------- /animation_nodes/nodes/text/repeat_text.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.props import * 3 | from ... events import propertyChanged 4 | from ... base_types import AnimationNode 5 | 6 | modeItems = [ 7 | ("START", "at Start", "", "TRIA_LEFT", 0), 8 | ("END", "at End", "", "TRIA_RIGHT", 1) 9 | ] 10 | 11 | class RepeatTextNode(AnimationNode, bpy.types.Node): 12 | bl_idname = "an_RepeatTextNode" 13 | bl_label = "Repeat Text" 14 | 15 | mode: EnumProperty(name = "Mode", default = "END", 16 | items = modeItems, update = propertyChanged) 17 | 18 | def create(self): 19 | self.newInput("Integer", "Repeats", "repeats", minValue = 0) 20 | self.newInput("Text", "Text", "inText") 21 | self.newInput("Text", "Fill", "fill") 22 | 23 | self.newOutput("Text", "Text", "outText") 24 | 25 | def draw(self, layout): 26 | layout.prop(self, "mode", text = "") 27 | 28 | def execute(self, repeats, inText, fill): 29 | if self.mode == "START": 30 | return (fill * repeats) + inText 31 | elif self.mode == "END": 32 | return inText + (fill * repeats) 33 | -------------------------------------------------------------------------------- /animation_nodes/nodes/viewer/interpolation_viewer.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.props import * 3 | from ... base_types import AnimationNode, InterpolationUIExtension 4 | 5 | interpolationByNode = {} 6 | 7 | class InterpolationViewerNode(AnimationNode, bpy.types.Node): 8 | bl_idname = "an_InterpolationViewerNode" 9 | bl_label = "Interpolation Viewer" 10 | bl_width_default = 160 11 | 12 | resolution: IntProperty(name = "Resolution", min = 5, default = 100) 13 | 14 | def create(self): 15 | self.newInput("Interpolation", "Interpolation", "interpolation", defaultDrawType = "PROPERTY_ONLY") 16 | 17 | def drawAdvanced(self, layout): 18 | layout.prop(self, "resolution") 19 | 20 | def execute(self, interpolation): 21 | interpolationByNode[self.identifier] = interpolation 22 | 23 | def getUIExtensions(self): 24 | if self.hide: 25 | return [] 26 | 27 | interpolation = interpolationByNode.get(self.identifier, None) 28 | if interpolation is None: 29 | return [] 30 | 31 | return [InterpolationUIExtension(interpolation, self.resolution)] 32 | -------------------------------------------------------------------------------- /animation_nodes/nodes/mesh/get_vertex_group.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... data_structures import DoubleList 3 | from ... base_types import AnimationNode 4 | 5 | class GetVertexGroupNode(AnimationNode, bpy.types.Node): 6 | bl_idname = "an_GetVertexGroupNode" 7 | bl_label = "Get Vertex Group" 8 | errorHandlingType = "EXCEPTION" 9 | 10 | def create(self): 11 | self.newInput("Mesh", "Mesh", "mesh") 12 | self.newInput("Text", "Name", "vertexWeightName") 13 | self.newOutput("Float List", "Weights", "weights") 14 | 15 | def execute(self, mesh, vertexWeightName): 16 | if mesh is None: return None 17 | if vertexWeightName == "": self.raiseErrorMessage("Vertex group name can't be empty.") 18 | 19 | attribute = mesh.getVertexWeightAttribute(vertexWeightName) 20 | if attribute is None: 21 | self.raiseErrorMessage( 22 | f"Mesh does not have vertex group with name '{vertexWeightName}'." 23 | f"\nAvailable: {mesh.getAllVertexWeightAttributeNames()}" 24 | ) 25 | 26 | return DoubleList.fromValues(attribute.data) 27 | -------------------------------------------------------------------------------- /animation_nodes/sockets/node_control.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.props import * 3 | from .. base_types import AnimationNodeSocket 4 | 5 | class NodeControlSocket(bpy.types.NodeSocket, AnimationNodeSocket): 6 | bl_idname = "an_NodeControlSocket" 7 | bl_label = "Node Control Socket" 8 | dataType = "Node Control" 9 | allowedInputTypes = [] 10 | drawColor = (0.0, 0.0, 0.0, 0.0) 11 | storable = False 12 | comparable = False 13 | 14 | margin: FloatProperty(default = 0.0001, min = 0.0001) 15 | 16 | def draw(self, context, layout, node, text): 17 | col = layout.column() 18 | subcol = col.column() 19 | subcol.label(text = "") 20 | subcol.scale_y = self.margin 21 | 22 | node.drawControlSocket(col, self) 23 | 24 | subcol = col.column() 25 | subcol.label(text = "") 26 | subcol.scale_y = self.margin 27 | 28 | @classmethod 29 | def getDefaultValue(cls): 30 | return None 31 | 32 | @classmethod 33 | def getDefaultValueCode(cls): 34 | return "None" 35 | 36 | @classmethod 37 | def correctValue(cls, value): 38 | return value, 0 39 | -------------------------------------------------------------------------------- /animation_nodes/id_keys/data_types/text_type.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from . base import SingleIDKeyDataType 3 | from ... utils.operators import makeOperator 4 | 5 | class TextDataType(SingleIDKeyDataType): 6 | identifier = "Text" 7 | default = "" 8 | 9 | @classmethod 10 | def drawExtras(cls, layout, object, name): 11 | props = layout.operator("an.id_keys_from_text_body", icon = "OUTLINER_DATA_FONT") 12 | props.name = name 13 | 14 | @classmethod 15 | def drawCopyMenu(cls, layout, object, name): 16 | props = layout.operator("an.copy_id_key_to_attribute", text = "to Text Body") 17 | props.dataType = "Text" 18 | props.propertyName = name 19 | props.attribute = "data.body" 20 | 21 | @makeOperator("an.id_keys_from_text_body", "From Text Body", arguments = ["String"], 22 | description = "Assign text ID Keys based on text body") 23 | def idKeyFromTextBody(name): 24 | for object in bpy.context.selected_objects: 25 | if object.type == "FONT": 26 | object.id_keys.set("Text", name, object.data.body) 27 | else: 28 | object.id_keys.set("Text", name, "") 29 | -------------------------------------------------------------------------------- /animation_nodes/nodes/interpolation/mirror.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.props import * 3 | from ... events import propertyChanged 4 | from ... base_types import AnimationNode 5 | 6 | from ... algorithms.interpolations import ( 7 | MirroredInterpolation, 8 | MirroredAndChainedInterpolation 9 | ) 10 | 11 | class MirrorInterpolationNode(AnimationNode, bpy.types.Node): 12 | bl_idname = "an_MirrorInterpolationNode" 13 | bl_label = "Mirror Interpolation" 14 | 15 | chain: BoolProperty(name = "Chain", default = True, 16 | description = "Connect original and mirrored interpolation", 17 | update = propertyChanged) 18 | 19 | def create(self): 20 | self.newInput("Interpolation", "Interpolation", "interpolationIn", defaultDrawType = "PROPERTY_ONLY") 21 | self.newOutput("Interpolation", "Interpolation", "interpolationOut") 22 | 23 | def draw(self, layout): 24 | layout.prop(self, "chain") 25 | 26 | def execute(self, interpolation): 27 | if self.chain: 28 | return MirroredAndChainedInterpolation(interpolation) 29 | else: 30 | return MirroredInterpolation(interpolation) 31 | -------------------------------------------------------------------------------- /animation_nodes/nodes/object/object_color_output.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode, VectorizedSocket 3 | 4 | class ObjectColorOutputNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_ObjectColorOutputNode" 6 | bl_label = "Object Color Output" 7 | codeEffects = [VectorizedSocket.CodeEffect] 8 | 9 | useObjectList: VectorizedSocket.newProperty() 10 | useColorList: VectorizedSocket.newProperty() 11 | 12 | def create(self): 13 | self.newInput(VectorizedSocket("Object", "useObjectList", 14 | ("Object", "object", dict(defaultDrawType = "PROPERTY_ONLY")), 15 | ("Objects", "objects"), 16 | codeProperties = dict(allowListExtension = False))) 17 | 18 | self.newInput(VectorizedSocket("Color", "useColorList", 19 | ("Color", "color"), ("Colors", "colors"))) 20 | 21 | self.newOutput(VectorizedSocket("Object", "useObjectList", 22 | ("Object", "object", dict(defaultDrawType = "PROPERTY_ONLY")), 23 | ("Objects", "objects"))) 24 | 25 | def getExecutionCode(self, required): 26 | return "if object is not None: object.color = color" 27 | -------------------------------------------------------------------------------- /animation_nodes/nodes/vector/separate_vector.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.props import * 3 | from ... base_types import AnimationNode, VectorizedSocket 4 | from . c_utils import getAxisListOfVectorList 5 | 6 | class SeparateVectorNode(AnimationNode, bpy.types.Node): 7 | bl_idname = "an_SeparateVectorNode" 8 | bl_label = "Separate Vector" 9 | 10 | useList: VectorizedSocket.newProperty() 11 | 12 | def create(self): 13 | self.newInput(VectorizedSocket("Vector", "useList", 14 | ("Vector", "vector"), ("Vectors", "vectors"))) 15 | 16 | for axis in "XYZ": 17 | self.newOutput(VectorizedSocket("Float", "useList", 18 | (axis, axis.lower()), (axis, axis.lower()))) 19 | 20 | def getExecutionCode(self, required): 21 | for i, axis in enumerate("xyz"): 22 | if axis in required: 23 | if self.useList: 24 | yield "{0} = self.getAxisList(vectors, '{0}')".format(axis) 25 | else: 26 | yield "{} = vector[{}]".format(axis, i) 27 | 28 | def getAxisList(self, vectors, axis): 29 | return getAxisListOfVectorList(vectors, axis) 30 | -------------------------------------------------------------------------------- /animation_nodes/libs/FastNoiseSIMD/source/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Jordan Peck 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /animation_nodes/data_structures/lists/polygon_indices_list.pxd: -------------------------------------------------------------------------------- 1 | from . base_lists cimport UIntegerList, LongList, LongList 2 | 3 | cdef class PolygonIndicesList: 4 | cdef: 5 | readonly UIntegerList indices 6 | readonly UIntegerList polyStarts 7 | readonly UIntegerList polyLengths 8 | 9 | cdef long getLength(self) 10 | 11 | cpdef append(self, value) 12 | cpdef extend(self, values) 13 | cpdef copy(self) 14 | cpdef index(self, value) 15 | cpdef count(self, value) 16 | cpdef remove(self, value) 17 | 18 | cdef getElementAtIndex(self, long index) 19 | cdef getValuesInSlice(self, slice sliceObject) 20 | cdef getValuesInIndexList(self, keyList) 21 | 22 | cdef setElementAtIndex(self, long index, value) 23 | cdef setElementAtIndex_SameLength(self, long index, value) 24 | cdef setElementAtIndex_DifferentLength(self, long index, value) 25 | 26 | cdef removeElementAtIndex(self, long index) 27 | 28 | cpdef copyWithNewOrder(self, LongList newOrder, checkIndices = ?) 29 | cdef extend_SameType(self, PolygonIndicesList otherList) 30 | 31 | cdef isValueValid(self, value) 32 | cdef tryCorrectIndex(self, long index) 33 | -------------------------------------------------------------------------------- /animation_nodes/id_keys/id_keys_on_objects.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from . existing_keys import IDKey, findsIDKeys, removesIDKey 3 | from . data_types import dataTypeByIdentifier, dataTypeIdentifiers 4 | 5 | @findsIDKeys(removable = True) 6 | def getIDKeysOnObjects(): 7 | possibleKeys = set() 8 | for object in getAllObjects(): 9 | for key in object.keys(): 10 | if key.startswith("AN*"): possibleKeys.add(key) 11 | return filterRealIDKeys(possibleKeys) 12 | 13 | 14 | def filterRealIDKeys(possibleKeys): 15 | realIDKeys = set() 16 | for key in possibleKeys: 17 | parts = key.split("*") 18 | if len(parts) == 3: 19 | if parts[1] in dataTypeIdentifiers: 20 | realIDKeys.add(IDKey(parts[1], parts[2])) 21 | elif len(parts) == 4: 22 | if parts[1] in dataTypeIdentifiers: 23 | realIDKeys.add(IDKey(parts[1], parts[3])) 24 | return realIDKeys 25 | 26 | @removesIDKey 27 | def removeIDKey(idKey): 28 | typeClass = dataTypeByIdentifier[idKey.type] 29 | for object in getAllObjects(): 30 | typeClass.remove(object, idKey.name) 31 | 32 | def getAllObjects(): 33 | return bpy.data.objects 34 | -------------------------------------------------------------------------------- /animation_nodes/nodes/generic/blend_data_by_name.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.props import * 3 | from ... base_types import AnimationNode 4 | 5 | dataTypes = { 6 | "Object" : "objects", 7 | "Scene" : "scenes", 8 | "Collection" : "collections", 9 | "Text Block" : "texts", 10 | "Material" : "materials" } 11 | 12 | class BlendDataByNameNode(AnimationNode, bpy.types.Node): 13 | bl_idname = "an_BlendDataByNameNode" 14 | bl_label = "Data by Name" 15 | dynamicLabelType = "ALWAYS" 16 | 17 | onlySearchTags = True 18 | searchTags = [(name + " by Name", {"dataType" : repr(name)}) for name in dataTypes.keys()] 19 | 20 | # Should be set only on node creation 21 | dataType: StringProperty(name = "Data Type", default = "Object", 22 | update = AnimationNode.refresh) 23 | 24 | def create(self): 25 | self.newInput("Text", "Name", "name", defaultDrawType = "PROPERTY_ONLY") 26 | self.newOutput(self.dataType, self.dataType, "output") 27 | 28 | def drawLabel(self): 29 | return self.dataType + " by Name" 30 | 31 | def getExecutionCode(self, required): 32 | return "output = bpy.data.{}.get(name)".format(dataTypes[self.dataType]) 33 | -------------------------------------------------------------------------------- /animation_nodes/nodes/kd_tree/find_points_in_radius.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode 3 | 4 | class FindPointsInRadiusInKDTreeNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_FindPointsInRadiusInKDTreeNode" 6 | bl_label = "Find Points in Radius" 7 | 8 | def create(self): 9 | self.newInput("KDTree", "KDTree", "kdTree") 10 | self.newInput("Float", "Radius", "radius", value = 5, minValue = 0) 11 | self.newInput("Vector", "Vector", "searchVector", defaultDrawType = "PROPERTY_ONLY") 12 | 13 | self.newOutput("an_VectorListSocket", "Vectors", "nearestVectors") 14 | self.newOutput("an_FloatListSocket", "Distances", "distances") 15 | self.newOutput("an_IntegerListSocket", "Indices", "indices") 16 | 17 | def getExecutionCode(self, required): 18 | yield "nearestVectors = Vector3DList()" 19 | yield "distances = DoubleList()" 20 | yield "indices = LongList()" 21 | yield "for vector, index, distance in kdTree.find_range(searchVector, max(radius, 0)):" 22 | yield " nearestVectors.append(vector)" 23 | yield " indices.append(index)" 24 | yield " distances.append(distance)" 25 | -------------------------------------------------------------------------------- /animation_nodes/nodes/fcurve/evaluate_fcurve.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.props import * 3 | from ... base_types import AnimationNode 4 | from ... events import executionCodeChanged 5 | 6 | frameTypeItems = [ 7 | ("OFFSET", "Offset", ""), 8 | ("ABSOLUTE", "Absolute", "") ] 9 | 10 | class EvaluateFCurveNode(AnimationNode, bpy.types.Node): 11 | bl_idname = "an_EvaluateFCurveNode" 12 | bl_label = "Evaluate FCurve" 13 | 14 | frameType: EnumProperty( 15 | name = "Frame Type", default = "OFFSET", 16 | items = frameTypeItems, update = executionCodeChanged) 17 | 18 | def create(self): 19 | self.newInput("FCurve", "FCurve", "fCurve") 20 | self.newInput("Float", "Frame", "frame") 21 | self.newOutput("Float", "Value", "value") 22 | 23 | def draw(self, layout): 24 | layout.prop(self, "frameType", text = "Frame") 25 | 26 | def getExecutionCode(self, required): 27 | yield "evaluationFrame = frame" 28 | if self.frameType == "OFFSET": 29 | yield "evaluationFrame += self.nodeTree.scene.frame_current_final" 30 | 31 | yield "if fCurve is None: value = 0.0" 32 | yield "else: value = fCurve.evaluate(evaluationFrame)" 33 | -------------------------------------------------------------------------------- /animation_nodes/nodes/spline/spline_from_gp_stroke.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... data_structures import PolySpline 3 | from ... base_types import AnimationNode, VectorizedSocket 4 | 5 | class SplineFromGPStrokeNode(AnimationNode, bpy.types.Node): 6 | bl_idname = "an_SplineFromGPStrokeNode" 7 | bl_label = "Spline From GP Stroke" 8 | codeEffects = [VectorizedSocket.CodeEffect] 9 | 10 | useStrokeList: VectorizedSocket.newProperty() 11 | 12 | def create(self): 13 | self.newInput(VectorizedSocket("GPStroke", "useStrokeList", 14 | ("Stroke", "stroke"), ("Strokes", "strokes"))) 15 | 16 | self.newOutput(VectorizedSocket("Spline", "useStrokeList", 17 | ("Spline", "spline"), ("Splines", "splines"))) 18 | 19 | def getExecutionCode(self, required): 20 | return "spline = self.splinesFromStrokes(stroke)" 21 | 22 | def splinesFromStrokes(self, stroke): 23 | if stroke is None: return PolySpline() 24 | 25 | return PolySpline(points = stroke.vertices.copy(), 26 | radii = stroke.pressures.copy(), 27 | cyclic = stroke.useCyclic, 28 | materialIndex = stroke.materialIndex) 29 | -------------------------------------------------------------------------------- /animation_nodes/nodes/text/random_text.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import random 3 | from bpy.props import * 4 | from ... events import propertyChanged 5 | from ... base_types import AnimationNode 6 | 7 | class RandomTextNode(AnimationNode, bpy.types.Node): 8 | bl_idname = "an_RandomTextNode" 9 | bl_label = "Random Text" 10 | 11 | nodeSeed: IntProperty(name = "Node Seed", update = propertyChanged) 12 | 13 | def setup(self): 14 | self.randomizeNodeSeed() 15 | 16 | def create(self): 17 | self.newInput("Integer", "Seed", "seed") 18 | self.newInput("Integer", "Length", "length", value = 5) 19 | self.newInput("Text", "Characters", "characters", value = "abcdefghijklmnopqrstuvwxyz") 20 | self.newOutput("Text", "Text", "text") 21 | 22 | def draw(self, layout): 23 | layout.prop(self, "nodeSeed") 24 | 25 | def execute(self, seed, length, characters): 26 | random.seed(seed + 12334 * self.nodeSeed) 27 | return ''.join(random.choice(characters) for _ in range(length)) 28 | 29 | def duplicate(self, sourceNode): 30 | self.randomizeNodeSeed() 31 | 32 | def randomizeNodeSeed(self): 33 | self.nodeSeed = int(random.random() * 100) 34 | -------------------------------------------------------------------------------- /animation_nodes/nodes/text/characters_input.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import string 3 | from ... base_types import AnimationNode 4 | 5 | class CharactersNode(AnimationNode, bpy.types.Node): 6 | bl_idname = "an_CharactersNode" 7 | bl_label = "Characters" 8 | 9 | def create(self): 10 | self.newOutput("Text", "Lower Case", "lower") 11 | self.newOutput("Text", "Upper Case", "upper") 12 | self.newOutput("Text", "Digits", "digits") 13 | self.newOutput("Text", "Special", "special") 14 | self.newOutput("Text", "Line Break", "lineBreak") 15 | self.newOutput("Text", "Tab", "tab") 16 | 17 | def getExecutionCode(self, required): 18 | if "lower" in required: 19 | yield "lower = '{}'".format(string.ascii_lowercase) 20 | if "upper" in required: 21 | yield "upper = '{}'".format(string.ascii_uppercase) 22 | if "digits" in required: 23 | yield "digits = '{}'".format(string.digits) 24 | if "special" in required: 25 | yield "special = '!$%&/\\()=?*+#\\'-_.:,;\"'" 26 | if "lineBreak" in required: 27 | yield "lineBreak = '\\n'" 28 | if "tab" in required: 29 | yield "tab = '\\t'" 30 | -------------------------------------------------------------------------------- /animation_nodes/nodes/vector/vector_dot_product.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode, VectorizedSocket 3 | 4 | class VectorDotProductNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_VectorDotProductNode" 6 | bl_label = "Vector Dot Product" 7 | 8 | useListA: VectorizedSocket.newProperty() 9 | useListB: VectorizedSocket.newProperty() 10 | 11 | def create(self): 12 | self.newInput(VectorizedSocket("Vector", "useListA", 13 | ("A", "a"), ("A", "a"))) 14 | self.newInput(VectorizedSocket("Vector", "useListB", 15 | ("B", "b"), ("B", "b"))) 16 | 17 | self.newOutput(VectorizedSocket("Float", ["useListA", "useListB"], 18 | ("Dot Product", "dotProduct"), ("Dot Product", "dotProducts"))) 19 | 20 | def getExecutionCode(self, required): 21 | if self.useListA or self.useListB: 22 | yield "_vA, _vB = VirtualVector3DList.createMultiple((a, (0,0,0)), (b, (0,0,0)))" 23 | yield "amount = VirtualVector3DList.getMaxRealLength(_vA, _vB)" 24 | yield "dotProducts = AN.nodes.vector.c_utils.calculateVectorDotProducts(amount, _vA, _vB)" 25 | else: 26 | yield "dotProduct = a.dot(b)" 27 | -------------------------------------------------------------------------------- /animation_nodes/math/conversion.pxd: -------------------------------------------------------------------------------- 1 | from . color cimport Color 2 | from . euler cimport Euler3 3 | from . quaternion cimport Quaternion 4 | from . vector cimport Vector2, Vector3, Vector4, Int2 5 | from . matrix cimport Matrix3, Matrix4, Matrix3_or_Matrix4 6 | 7 | cdef Matrix4 toMatrix4(value) except * 8 | cdef setMatrix4(Matrix4* m, value) 9 | cdef toPyMatrix4(Matrix4* m) 10 | 11 | cdef toPyMatrix3(Matrix3* m) 12 | 13 | cdef Vector2 toVector2(value) except * 14 | cdef setVector2(Vector2* v, value) 15 | cdef toPyVector2(Vector2* v) 16 | 17 | cdef Vector3 toVector3(value) except * 18 | cdef setVector3(Vector3* v, value) 19 | cdef toPyVector3(Vector3* v) 20 | 21 | cdef Vector4 toVector4(value) except * 22 | cdef setVector4(Vector4* v, value) 23 | cdef toPyVector4(Vector4* v) 24 | 25 | cdef Int2 toInt2(value) except * 26 | cdef setInt2(Int2* v, value) 27 | cdef toPyInt2(Int2* v) 28 | 29 | cdef Euler3 toEuler3(value) except * 30 | cdef setEuler3(Euler3* e, value) 31 | cdef toPyEuler3(Euler3* e) 32 | 33 | cdef Quaternion toQuaternion(value) except * 34 | cdef setQuaternion(Quaternion* q, value) 35 | cdef toPyQuaternion(Quaternion* q) 36 | 37 | cdef Color toColor(value) except * 38 | cdef setColor(Color* c, value) 39 | cdef toPyColor(Color* c) 40 | -------------------------------------------------------------------------------- /animation_nodes/operators/node_creators/insert_loop_for_iteration.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.props import * 3 | from mathutils import Vector 4 | from ... sockets.info import isList 5 | from . node_creator import NodeCreator 6 | from ... tree_info import getNodeByIdentifier 7 | 8 | class InsertLoopForIterator(bpy.types.Operator, NodeCreator): 9 | bl_idname = "an.insert_loop_for_iterator" 10 | bl_label = "Insert Loop for Iterator" 11 | 12 | nodeIdentifier: StringProperty() 13 | socketIndex: IntProperty() 14 | 15 | def insert(self): 16 | try: 17 | sourceNode = getNodeByIdentifier(self.nodeIdentifier) 18 | socket = sourceNode.outputs[self.socketIndex] 19 | except: return 20 | if not isList(socket.bl_idname): return 21 | 22 | loopInputNode = self.newNode("an_LoopInputNode") 23 | loopInputNode.newIterator(socket.dataType) 24 | invokeNode = self.newNode("an_InvokeSubprogramNode", move = False, mouseOffset = False) 25 | invokeNode.location = sourceNode.viewLocation + Vector((250, 0)) 26 | 27 | invokeNode.subprogramIdentifier = loopInputNode.identifier 28 | self.updateSubprograms() 29 | 30 | socket.linkWith(invokeNode.inputs[0]) 31 | -------------------------------------------------------------------------------- /animation_nodes/nodes/subprogram/loop_break.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.props import * 3 | from ... base_types import AnimationNode 4 | from ... tree_info import getNodeByIdentifier 5 | from ... events import treeChanged, propertyChanged 6 | 7 | class LoopBreakNode(AnimationNode, bpy.types.Node): 8 | bl_idname = "an_LoopBreakNode" 9 | bl_label = "Loop Break" 10 | onlySearchTags = True 11 | 12 | loopInputIdentifier: StringProperty(update = treeChanged) 13 | 14 | def create(self): 15 | self.newInput("Boolean", "Continue", "continueCondition", value = True) 16 | 17 | def draw(self, layout): 18 | node = self.loopInputNode 19 | if node: layout.label(text = node.subprogramName, icon = "GROUP_VERTEX") 20 | 21 | def edit(self): 22 | network = self.network 23 | if network.type != "Invalid": return 24 | if network.loopInAmount != 1: return 25 | loopInput = network.getLoopInputNode() 26 | if self.loopInputIdentifier == loopInput.identifier: return 27 | self.loopInputIdentifier = loopInput.identifier 28 | 29 | @property 30 | def loopInputNode(self): 31 | try: return getNodeByIdentifier(self.loopInputIdentifier) 32 | except: return None 33 | -------------------------------------------------------------------------------- /animation_nodes/sockets/matrix.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from mathutils import Matrix 3 | from .. data_structures import Matrix4x4List 4 | from .. base_types import AnimationNodeSocket, CListSocket 5 | 6 | class MatrixSocket(bpy.types.NodeSocket, AnimationNodeSocket): 7 | bl_idname = "an_MatrixSocket" 8 | bl_label = "Matrix Socket" 9 | dataType = "Matrix" 10 | drawColor = (1, 0.56, 0.3, 1) 11 | storable = True 12 | comparable = False 13 | 14 | @classmethod 15 | def getDefaultValue(cls): 16 | return Matrix.Identity(4) 17 | 18 | @classmethod 19 | def getCopyExpression(cls): 20 | return "value.copy()" 21 | 22 | @classmethod 23 | def correctValue(cls, value): 24 | if isinstance(value, Matrix): 25 | return value, 0 26 | else: 27 | try: return Matrix(value), 1 28 | except: return cls.getDefaultValue(), 2 29 | 30 | 31 | class MatrixListSocket(bpy.types.NodeSocket, CListSocket): 32 | bl_idname = "an_MatrixListSocket" 33 | bl_label = "Matrix List Socket" 34 | dataType = "Matrix List" 35 | baseType = MatrixSocket 36 | drawColor = (1, 0.56, 0.3, 0.5) 37 | storable = True 38 | comparable = False 39 | listClass = Matrix4x4List 40 | -------------------------------------------------------------------------------- /animation_nodes/nodes/sound/midi_file_reader.py: -------------------------------------------------------------------------------- 1 | import os 2 | import bpy 3 | from ... utils.midi import readMIDIFile 4 | from ... base_types import AnimationNode 5 | 6 | class ReadMIDIFileNode(AnimationNode, bpy.types.Node): 7 | bl_idname = "an_ReadMIDIFileNode" 8 | bl_label = "Read MIDI File" 9 | errorHandlingType = "EXCEPTION" 10 | 11 | def create(self): 12 | self.newInput("Text", "Path", "path", showFileChooser = True) 13 | self.newOutput("MIDI Track List", "Tracks", "tracks") 14 | 15 | def draw(self, layout): 16 | if self.inputs[0].isUnlinked: 17 | name = os.path.basename(self.inputs[0].value) 18 | if name != "": 19 | layout.label(text = name, icon = "FILE_TEXT") 20 | 21 | def drawAdvanced(self, layout): 22 | self.invokeFunction(layout, "clearCache", text = "Clear Cache") 23 | 24 | def clearCache(self): 25 | readMIDIFile.cache_clear() 26 | 27 | def execute(self, path): 28 | if not os.path.exists(path): 29 | self.raiseErrorMessage("File does not exist.") 30 | elif not path.endswith(".mid"): 31 | self.raiseErrorMessage("File is not a MIDI file.") 32 | else: 33 | return readMIDIFile(path) 34 | -------------------------------------------------------------------------------- /animation_nodes/nodes/falloff/custom_falloff.pyx: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode 3 | from ... data_structures cimport BaseFalloff, FloatList 4 | 5 | class CustomFalloffNode(AnimationNode, bpy.types.Node): 6 | bl_idname = "an_CustomFalloffNode" 7 | bl_label = "Custom Falloff" 8 | 9 | def create(self): 10 | self.newInput("Float List", "Strengths", "strengths") 11 | self.newInput("Float", "Fallback", "fallback", hide = True).setRange(0, 1) 12 | self.newOutput("Falloff", "Falloff", "falloff") 13 | 14 | def execute(self, strengths, fallback): 15 | return CustomFalloff(FloatList.fromValues(strengths), fallback) 16 | 17 | 18 | cdef class CustomFalloff(BaseFalloff): 19 | cdef FloatList strengths 20 | cdef Py_ssize_t length 21 | cdef float fallback 22 | 23 | def __cinit__(self, FloatList strengths, float fallback): 24 | self.strengths = strengths 25 | self.length = strengths.length 26 | self.fallback = fallback 27 | self.clamped = False 28 | self.dataType = "NONE" 29 | 30 | cdef float evaluate(self, void *object, Py_ssize_t index): 31 | if index < self.length: 32 | return self.strengths[index] 33 | return self.fallback 34 | -------------------------------------------------------------------------------- /animation_nodes/nodes/kd_tree/find_nearest_n_points.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode 3 | 4 | class FindNearestNPointsInKDTreeNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_FindNearestNPointsInKDTreeNode" 6 | bl_label = "Find Nearest Points" 7 | 8 | def create(self): 9 | self.newInput("KDTree", "KDTree", "kdTree") 10 | self.newInput("Integer", "Amount", "amount", value = 5, minValue = 0) 11 | self.newInput("Vector", "Vector", "searchVector", defaultDrawType = "PROPERTY_ONLY") 12 | self.newOutput("Vector List", "Vectors", "nearestVectors") 13 | self.newOutput("Float List", "Distances", "distances") 14 | self.newOutput("Integer List", "Indices", "indices") 15 | 16 | def getExecutionCode(self, required): 17 | yield "_amount = max(amount, 0)" 18 | yield "nearestVectors = Vector3DList(capacity = _amount)" 19 | yield "distances = DoubleList(capacity = _amount)" 20 | yield "indices = LongList(capacity = _amount)" 21 | yield "for vector, index, distance in kdTree.find_n(searchVector, _amount):" 22 | yield " nearestVectors.append(vector)" 23 | yield " indices.append(index)" 24 | yield " distances.append(distance)" 25 | -------------------------------------------------------------------------------- /animation_nodes/operators/choose_id_key.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.props import * 3 | from .. id_keys import getAllIDKeys 4 | from .. utils.enum_items import cacheEnumItems 5 | from .. utils.unicode import toValidString, fromValidString 6 | 7 | class IDKeySearch(bpy.types.Operator): 8 | bl_idname = "an.choose_id_key" 9 | bl_label = "ID Key Search" 10 | bl_options = {"REGISTER"} 11 | bl_property = "item" 12 | 13 | def getSearchItems(self, context): 14 | items = [] 15 | for dataType, name in getAllIDKeys(): 16 | identifier = toValidString("{} * {}".format(dataType, name)) 17 | items.append((identifier, name, "")) 18 | return items 19 | 20 | item: EnumProperty(items = cacheEnumItems(getSearchItems)) 21 | callback: StringProperty() 22 | 23 | def invoke(self, context, event): 24 | bpy.ops.an.update_id_keys_list() 25 | context.window_manager.invoke_search_popup(self) 26 | return {"CANCELLED"} 27 | 28 | def execute(self, context): 29 | item = fromValidString(self.item) 30 | if item == "NONE": return {"CANCELLED"} 31 | dataType, name = item.split(" * ") 32 | self.an_executeCallback(self.callback, dataType, name) 33 | return {"FINISHED"} 34 | -------------------------------------------------------------------------------- /animation_nodes/utils/selection.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | sortedSelectionNames = [] 4 | 5 | def getSortedSelectedObjects(): 6 | objects = [] 7 | for name in getSortedSelectedObjectNames(): 8 | objects.append(bpy.data.objects.get(name)) 9 | return objects 10 | 11 | def getSortedSelectedObjectNames(): 12 | return sortedSelectionNames 13 | 14 | def updateSelectionSorting(viewLayer): 15 | global sortedSelectionNames 16 | 17 | selectedNames = getSelectedObjectNames(viewLayer) 18 | 19 | newSortedSelection = [] 20 | 21 | selectedNamesSet = set(selectedNames) 22 | for name in sortedSelectionNames: 23 | if name in selectedNamesSet: 24 | newSortedSelection.append(name) 25 | 26 | for name in selectedNames: 27 | if name not in newSortedSelection: 28 | newSortedSelection.append(name) 29 | 30 | sortedSelectionNames = newSortedSelection 31 | 32 | def getSelectedObjectNames(viewLayer): 33 | return [obj.name for obj in viewLayer.objects if obj.select_get(view_layer = viewLayer)] 34 | 35 | def getSelectedObjects(viewLayer): 36 | return [obj for obj in viewLayer.objects if obj.select_get(view_layer = viewLayer)] 37 | 38 | def getActiveObject(viewLayer): 39 | return viewLayer.objects.active 40 | -------------------------------------------------------------------------------- /animation_nodes/sockets/edge_indices.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from .. data_structures import EdgeIndicesList 3 | from .. base_types import AnimationNodeSocket, CListSocket 4 | 5 | class EdgeIndicesSocket(bpy.types.NodeSocket, AnimationNodeSocket): 6 | bl_idname = "an_EdgeIndicesSocket" 7 | bl_label = "Edge Indices Socket" 8 | dataType = "Edge Indices" 9 | drawColor = (0.4, 0.6, 0.6, 1) 10 | comparable = True 11 | storable = True 12 | 13 | @classmethod 14 | def getDefaultValue(cls): 15 | return (0, 1) 16 | 17 | @classmethod 18 | def getDefaultValueCode(cls): 19 | return "(0, 1)" 20 | 21 | @classmethod 22 | def correctValue(cls, value): 23 | if isinstance(value, tuple) and len(value) == 2: return value, 0 24 | elif isinstance(value, (list, set)) and len(value) == 2: return tuple(value), 1 25 | else: return cls.getDefaultValue(), 2 26 | 27 | 28 | class EdgeIndicesListSocket(bpy.types.NodeSocket, CListSocket): 29 | bl_idname = "an_EdgeIndicesListSocket" 30 | bl_label = "Edge Indices List Socket" 31 | dataType = "Edge Indices List" 32 | baseType = EdgeIndicesSocket 33 | drawColor = (0.4, 0.6, 0.6, 0.5) 34 | storable = True 35 | comparable = False 36 | listClass = EdgeIndicesList 37 | -------------------------------------------------------------------------------- /animation_nodes/nodes/vector/vector_angle.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode, VectorizedSocket 3 | 4 | class VectorAngleNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_VectorAngleNode" 6 | bl_label = "Vector Angle" 7 | codeEffects = [VectorizedSocket.CodeEffect] 8 | 9 | useListA: VectorizedSocket.newProperty() 10 | useListB: VectorizedSocket.newProperty() 11 | 12 | def create(self): 13 | self.newInput(VectorizedSocket("Vector", "useListA", 14 | ("A", "a", dict(value = (1, 0, 0))), ("A", "a"))) 15 | 16 | self.newInput(VectorizedSocket("Vector", "useListB", 17 | ("B", "b", dict(value = (0, 0, 1))), ("B", "b"))) 18 | 19 | self.newOutput(VectorizedSocket("Float", ["useListA", "useListB"], 20 | ("Angle", "angle"), ("Angles", "angles"))) 21 | 22 | self.newOutput(VectorizedSocket("Quaternion", ["useListA", "useListB"], 23 | ("Rotation Difference", "rotationDifference"), 24 | ("Rotation Differences", "rotationDifferences"))) 25 | 26 | def getExecutionCode(self, required): 27 | if "angle" in required: 28 | yield "angle = a.angle(b, 0.0)" 29 | if "rotationDifference" in required: 30 | yield "rotationDifference = a.rotation_difference(b)" 31 | -------------------------------------------------------------------------------- /animation_nodes/nodes/animation/repeat_time.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.props import * 3 | from ... events import executionCodeChanged 4 | from ... base_types import AnimationNode 5 | 6 | repetitionTypeItems = [ 7 | ("LOOP", "Loop", ""), 8 | ("PING_PONG", "Ping Pong", "")] 9 | 10 | class RepeatTimeNode(AnimationNode, bpy.types.Node): 11 | bl_idname = "an_RepeatTimeNode" 12 | bl_label = "Repeat Time" 13 | 14 | repetitionType: EnumProperty(name = "Repetition Type", default = "LOOP", 15 | items = repetitionTypeItems, update = executionCodeChanged) 16 | 17 | def create(self): 18 | self.newInput("Float", "Time", "time") 19 | self.newInput("Float", "Rate", "rate", value = 50, minValue = 0.0001) 20 | self.newOutput("Float", "Time", "outTime") 21 | 22 | def draw(self, layout): 23 | layout.prop(self, "repetitionType", text = "Type") 24 | 25 | def getExecutionCode(self, required): 26 | if self.repetitionType == "LOOP": 27 | yield "outTime = time % max(rate, 0.0001)" 28 | if self.repetitionType == "PING_PONG": 29 | yield "finalRate = max(rate, 0.0001)" 30 | yield "outTime = time % finalRate if (time // finalRate) % 2 == 0 else finalRate - time % finalRate" 31 | yield "outTime = time if time < 0 else outTime" 32 | -------------------------------------------------------------------------------- /animation_nodes/math/number.pxd: -------------------------------------------------------------------------------- 1 | from libc.math cimport M_PI as PI 2 | from libc.math cimport (sin, cos, tan, asin, acos, atan, atan2, hypot, 3 | pow, floor, ceil, sqrt, log) 4 | 5 | cdef double PI_HALF, PI_QUARTER 6 | 7 | cdef double add(double x, double y) 8 | cdef double subtract(double x, double y) 9 | cdef double multiply(double x, double y) 10 | cdef double divide_Save(double x, double y) 11 | cdef double modulo_Save(double x, double y) 12 | cdef double floorDivision_Save(double x, double y) 13 | 14 | cdef double asin_Save(double x) 15 | cdef double acos_Save(double x) 16 | 17 | cdef double power_Save(double base, double exponent) 18 | 19 | cdef double min(double x, double y) 20 | cdef double max(double x, double y) 21 | cdef double abs(double x) 22 | 23 | cdef double sqrt_Save(double x) 24 | cdef double invert(double x) 25 | cdef double reciprocal_Save(double x) 26 | 27 | cdef double snap_Save(double x, double step) 28 | cdef double copySign(double x, double y) 29 | 30 | cdef double logarithm_Save(double a, double base) 31 | 32 | cdef double clamp(double x, double minValue, double maxValue) 33 | 34 | cdef double lerp(double x, double y, double p) 35 | cdef float lerpFloat(float x, float y, float p) 36 | 37 | cdef double degreeToRadian(double value) 38 | cdef double radianToDegree(double value) 39 | -------------------------------------------------------------------------------- /animation_nodes/nodes/mesh/triangulate_mesh.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.props import * 3 | from ... events import propertyChanged 4 | from ... base_types import AnimationNode 5 | from ... data_structures.meshes.validate import checkMeshData 6 | 7 | class TriangulateMeshNode(AnimationNode, bpy.types.Node): 8 | bl_idname = "an_TriangulateMeshNode" 9 | bl_label = "Triangulate Mesh" 10 | errorHandlingType = "EXCEPTION" 11 | 12 | methodType: BoolProperty(name = "Use Advanced Method", default = False, 13 | description = "Use advanced method for Mesh Triangulation", 14 | update = propertyChanged) 15 | 16 | def create(self): 17 | self.newInput("Mesh", "Mesh", "mesh", dataIsModified = True) 18 | 19 | self.newOutput("Mesh", "Mesh", "meshOut") 20 | 21 | def draw(self, layout): 22 | layout.prop(self, "methodType", text = "Use Advanced Method") 23 | 24 | def execute(self, mesh): 25 | if self.methodType: 26 | mesh.triangulateMesh(method = "EAR") 27 | else: 28 | mesh.triangulateMesh(method = "FAN") 29 | 30 | try: 31 | checkMeshData(mesh.vertices, mesh.edges, mesh.polygons) 32 | return mesh 33 | except Exception as e: 34 | self.raiseErrorMessage(str(e)) 35 | -------------------------------------------------------------------------------- /animation_nodes/nodes/mesh/offset_vertices.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... data_structures import VirtualVector3DList 3 | from .. vector.offset_vector import offsetVector3DList 4 | from ... base_types import AnimationNode, VectorizedSocket 5 | 6 | class OffsetVerticesNode(AnimationNode, bpy.types.Node): 7 | bl_idname = "an_OffsetVerticesNode" 8 | bl_label = "Offset Vertices" 9 | errorHandlingType = "EXCEPTION" 10 | 11 | useVectorList: VectorizedSocket.newProperty() 12 | 13 | def create(self): 14 | self.newInput("Mesh", "Mesh", "mesh", dataIsModified = True) 15 | self.newInput("Falloff", "Falloff", "falloff") 16 | self.newInput(VectorizedSocket("Vector", "useVectorList", 17 | ("Offset", "offset", dict(value = (0, 0, 1))), 18 | ("Offsets", "offsets"))) 19 | self.newOutput("Mesh", "Mesh", "mesh") 20 | 21 | def execute(self, mesh, falloff, offsets): 22 | _offsets = VirtualVector3DList.create(offsets, (0, 0, 0)) 23 | offsetVector3DList(mesh.vertices, _offsets, self.getFalloffEvaluator(falloff)) 24 | mesh.verticesTransformed() 25 | return mesh 26 | 27 | def getFalloffEvaluator(self, falloff): 28 | try: return falloff.getEvaluator("LOCATION") 29 | except: self.raiseErrorMessage("This falloff cannot be evaluated for vectors") 30 | -------------------------------------------------------------------------------- /animation_nodes/ui/node_popup.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.props import * 3 | from .. tree_info import getNodeByIdentifier 4 | from .. utils.blender_ui import getDpiFactor 5 | 6 | class NodePopup(bpy.types.Operator): 7 | bl_idname = "an.node_popup" 8 | bl_label = "Node Popup" 9 | 10 | nodeIdentifier: StringProperty(default = "") 11 | 12 | width: IntProperty(default = 250) 13 | drawFunctionName: StringProperty(default = "") 14 | executeFunctionName: StringProperty(default = "") 15 | 16 | def invoke(self, context, event): 17 | return context.window_manager.invoke_props_dialog(self, width = int(self.width * getDpiFactor())) 18 | 19 | def draw(self, context): 20 | try: node = getNodeByIdentifier(self.nodeIdentifier) 21 | except: self.layout.label(text = "Node not found", icon = "INFO") 22 | 23 | drawFunction = getattr(node, self.drawFunctionName) 24 | drawFunction(self.layout) 25 | 26 | def check(self, context): 27 | return True 28 | 29 | def execute(self, context): 30 | if self.executeFunctionName != "": 31 | try: node = getNodeByIdentifier(self.nodeIdentifier) 32 | except: return {"CANCELLED"} 33 | 34 | executeFunction = getattr(node, self.executeFunctionName) 35 | executeFunction() 36 | return {"FINISHED"} 37 | -------------------------------------------------------------------------------- /animation_nodes/algorithms/lsystem/py_interface.pyx: -------------------------------------------------------------------------------- 1 | from math import radians 2 | from .. random cimport randomInteger 3 | 4 | from . rule cimport RuleSet, freeRuleSet 5 | from . symbol_string cimport SymbolString, freeSymbolString, LSystemSymbolString 6 | 7 | from . apply_rules cimport applyGrammarRules 8 | from . geometry cimport geometryFromSymbolString 9 | from . parsing cimport parseRuleSet, parseSymbolString 10 | 11 | 12 | def calculateLSystem(axiom, rules, generations, seed, defaults, onlyPartialMoves = True, limit = None): 13 | assert generations >= 0 14 | 15 | cdef SymbolString _axiom = parseSymbolString(axiom, defaults) 16 | cdef RuleSet _ruleSet = parseRuleSet(rules, defaults) 17 | 18 | cdef SymbolString symbols = applyGrammarRules( 19 | _axiom, _ruleSet, generations, randomInteger(seed), 20 | onlyPartialMoves, limit 21 | ) 22 | 23 | freeRuleSet(&_ruleSet) 24 | freeSymbolString(&_axiom) 25 | 26 | geometryDefaults = { 27 | "Angle" : radians(defaults["Angle"]) 28 | } 29 | 30 | vertices, edges, widths, statesJ, statesK, statesM = geometryFromSymbolString( 31 | symbols, randomInteger(seed + 1), geometryDefaults 32 | ) 33 | 34 | outputSymbols = LSystemSymbolString.fromSymbolString(symbols) 35 | 36 | return vertices, edges, widths, outputSymbols, statesJ, statesK, statesM -------------------------------------------------------------------------------- /animation_nodes/data_structures/lists/_generate_convert_code.py: -------------------------------------------------------------------------------- 1 | paths = {} 2 | 3 | def setup(utils): 4 | global paths 5 | paths = { 6 | "source" : utils.changeFileName(__file__, "__convert.src"), 7 | "numericLists" : utils.changeFileName(__file__, "numeric_list_types.json") 8 | } 9 | 10 | def getPyPreprocessTasks(PyPreprocessTask, utils): 11 | dependencies = [ 12 | __file__, 13 | paths["source"], 14 | paths["numericLists"] 15 | ] 16 | pyxTask = PyPreprocessTask( 17 | target = utils.changeFileName(__file__, "convert.pyx"), 18 | dependencies = dependencies, 19 | function = generate 20 | ) 21 | return [pyxTask] 22 | 23 | def generate(target, utils): 24 | source = utils.readTextFile(paths["source"]) 25 | numericLists = utils.readJsonFile(paths["numericLists"]) 26 | listNames = ", ".join(name for name, _ in numericLists) 27 | 28 | parts = [] 29 | parts.append("from . base_lists cimport NumericList") 30 | parts.append("from . base_lists cimport " + listNames) 31 | parts.append("") 32 | 33 | for listName, listType in numericLists: 34 | code = utils.multiReplace(source, 35 | TARGETLIST = listName, 36 | TYPE = listType) 37 | parts.append(code) 38 | 39 | utils.writeTextFile(target, "\n".join(parts)) 40 | -------------------------------------------------------------------------------- /animation_nodes/operators/rename_datablock.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.props import * 3 | from .. utils.blender_ui import getDpiFactor 4 | 5 | class RenameDatablockPopupOperator(bpy.types.Operator): 6 | bl_idname = "an.rename_datablock_popup" 7 | bl_label = "Rename Datablock" 8 | 9 | oldName: StringProperty() 10 | newName: StringProperty() 11 | 12 | icon: StringProperty(default = "NONE") 13 | path: StringProperty(default = "bpy.data.objects") 14 | 15 | def invoke(self, context, event): 16 | self.newName = self.oldName 17 | return context.window_manager.invoke_props_dialog(self, width = int(200 * getDpiFactor())) 18 | 19 | def check(self, context): 20 | return True 21 | 22 | def draw(self, context): 23 | dataBlock = self.getDatablock() 24 | if dataBlock is None: 25 | self.layout.label(text = "The datablock does not exist.", icon = "INFO") 26 | else: 27 | self.layout.prop(self, "newName", text = "", icon = self.icon) 28 | 29 | def execute(self, context): 30 | dataBlock = self.getDatablock() 31 | if dataBlock is not None: 32 | dataBlock.name = self.newName 33 | return {"FINISHED"} 34 | 35 | def getDatablock(self): 36 | try: return eval(self.path + "[{}]".format(repr(self.oldName))) 37 | except: return None 38 | -------------------------------------------------------------------------------- /animation_nodes/nodes/kd_tree/find_nearest_point.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... base_types import AnimationNode, VectorizedSocket 3 | 4 | class FindNearestPointInKDTreeNode(AnimationNode, bpy.types.Node): 5 | bl_idname = "an_FindNearestPointInKDTreeNode" 6 | bl_label = "Find Nearest Point" 7 | codeEffects = [VectorizedSocket.CodeEffect] 8 | 9 | useVectorList: VectorizedSocket.newProperty() 10 | 11 | def create(self): 12 | self.newInput("KDTree", "KDTree", "kdTree") 13 | self.newInput(VectorizedSocket("Vector", "useVectorList", 14 | ("Vector", "searchVector", dict(defaultDrawType = "PROPERTY_ONLY")), 15 | ("Vectors", "searchVectors"))) 16 | 17 | self.newOutput(VectorizedSocket("Vector", "useVectorList", 18 | ("Vector", "nearestVector"), ("Vectors", "nearestVectors"))) 19 | self.newOutput(VectorizedSocket("Float", "useVectorList", 20 | ("Distance", "distance"), ("Distances", "distances"))) 21 | self.newOutput(VectorizedSocket("Integer", "useVectorList", 22 | ("Index", "index"), ("Indices", "indices"))) 23 | 24 | def getExecutionCode(self, required): 25 | yield "nearestVector, index, distance = kdTree.find(searchVector)" 26 | yield "if nearestVector is None:" 27 | yield " nearestVector, index, distance = Vector((0, 0, 0)), 0.0, -1" 28 | -------------------------------------------------------------------------------- /animation_nodes/nodes/object/utility_nodes/transform_object.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.props import * 3 | from mathutils import Matrix 4 | from .... events import propertyChanged 5 | from .... base_types import AnimationNode 6 | from .... utils.depsgraph import getEvaluatedID 7 | 8 | class TransformObjectNode(AnimationNode, bpy.types.Node): 9 | bl_idname = "an_TransformObjectNode" 10 | bl_label = "Transform Object" 11 | 12 | useCenter: BoolProperty(name = "Use Center", default = True, 13 | description = "Use the object location as origin", update = propertyChanged) 14 | 15 | def create(self): 16 | self.newInput("Object", "Object", "object").defaultDrawType = "PROPERTY_ONLY" 17 | self.newInput("Matrix", "Matrix", "matrix") 18 | self.newOutput("Object", "Object", "object") 19 | 20 | def draw(self, layout): 21 | layout.prop(self, "useCenter") 22 | 23 | def execute(self, object, matrix): 24 | if object is None: return None 25 | evaluatedObject = getEvaluatedID(object) 26 | if self.useCenter: 27 | offset = Matrix.Translation(evaluatedObject.location) 28 | transformation = offset @ matrix @ offset.inverted() 29 | else: 30 | transformation = matrix 31 | object.matrix_world = transformation @ evaluatedObject.matrix_world 32 | return object 33 | -------------------------------------------------------------------------------- /animation_nodes/sockets/generic.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from .. base_types import AnimationNodeSocket, PythonListSocket 3 | 4 | class GenericSocket(bpy.types.NodeSocket, AnimationNodeSocket): 5 | bl_idname = "an_GenericSocket" 6 | bl_label = "Generic Socket" 7 | dataType = "Generic" 8 | allowedInputTypes = ["All"] 9 | drawColor = (0.6, 0.3, 0.3, 1.0) 10 | storable = True 11 | comparable = False 12 | 13 | def getCurrentDataType(self): 14 | linkedDataTypes = tuple(self.linkedDataTypes) 15 | if len(linkedDataTypes) == 0: 16 | return "Generic" 17 | else: 18 | return linkedDataTypes[0] 19 | 20 | @classmethod 21 | def getDefaultValue(cls): 22 | return None 23 | 24 | @classmethod 25 | def getDefaultValueCode(cls): 26 | return "None" 27 | 28 | @classmethod 29 | def correctValue(cls, value): 30 | return value, 0 31 | 32 | 33 | class GenericListSocket(bpy.types.NodeSocket, PythonListSocket): 34 | bl_idname = "an_GenericListSocket" 35 | bl_label = "GenericListSocket" 36 | dataType = "Generic List" 37 | baseType = GenericSocket 38 | allowedInputTypes = ["All"] 39 | drawColor = (0.6, 0.3, 0.3, 0.5) 40 | storable = True 41 | comparable = False 42 | 43 | @classmethod 44 | def correctValue(cls, value): 45 | return value, 0 46 | -------------------------------------------------------------------------------- /animation_nodes/ui/auto_nodetree_selection.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from .. utils.handlers import eventHandler 3 | from .. utils.nodes import getAnimationNodeTrees 4 | 5 | treeNameBySpace = {} 6 | 7 | @eventHandler("ALWAYS") 8 | def updateAutoNodeTreeSelection(): 9 | nodeTrees = getAnimationNodeTrees() 10 | if len(nodeTrees) == 0: return 11 | 12 | for space in getAnimationNodeEditorSpaces(): 13 | spaceHash = str(hash(space)) 14 | 15 | if space.node_tree is None: 16 | if len(nodeTrees) == 1: 17 | space.node_tree = nodeTrees[0] 18 | else: 19 | lastUsedTree = bpy.data.node_groups.get(treeNameBySpace.get(spaceHash, "")) 20 | if lastUsedTree is not None: 21 | space.node_tree = lastUsedTree 22 | else: 23 | space.node_tree = nodeTrees[0] 24 | 25 | treeName = getattr(space.node_tree, "name", None) 26 | if treeName is not None: 27 | treeNameBySpace[spaceHash] = treeName 28 | 29 | def getAnimationNodeEditorSpaces(): 30 | spaces = [] 31 | for area in getattr(bpy.context.screen, "areas", []): 32 | if area.type == "NODE_EDITOR": 33 | space = area.spaces.active 34 | if space.tree_type == "an_AnimationNodeTree": 35 | spaces.append(space) 36 | return spaces 37 | -------------------------------------------------------------------------------- /animation_nodes/nodes/falloff/random_falloff.pyx: -------------------------------------------------------------------------------- 1 | import bpy 2 | from ... data_structures cimport BaseFalloff 3 | from ... base_types import AnimationNode 4 | from ... algorithms.random cimport randomDouble_Range 5 | 6 | class RandomFalloffNode(AnimationNode, bpy.types.Node): 7 | bl_idname = "an_RandomFalloffNode" 8 | bl_label = "Random Falloff" 9 | 10 | def create(self): 11 | self.newInput("Integer", "Seed", "seed") 12 | self.newInput("Float", "Min", "minValue", value = 0) 13 | self.newInput("Float", "Max", "maxValue", value = 1) 14 | self.newOutput("Falloff", "Falloff", "falloff") 15 | 16 | def execute(self, seed, minValue, maxValue): 17 | return RandomFalloff(seed % 0x7fffffff, minValue, maxValue) 18 | 19 | 20 | cdef class RandomFalloff(BaseFalloff): 21 | cdef: 22 | int seed 23 | float minValue, maxValue 24 | 25 | def __cinit__(self, int seed, float minValue, float maxValue): 26 | self.seed = (seed * 534523) % 0x7fffffff 27 | self.minValue = minValue 28 | self.maxValue = maxValue 29 | self.dataType = "NONE" 30 | self.clamped = 0 <= min(minValue, maxValue) <= max(minValue, maxValue) <= 1 31 | 32 | cdef float evaluate(self, void *object, Py_ssize_t index): 33 | return randomDouble_Range((self.seed + index) % 0x7fffffff, self.minValue, self.maxValue) 34 | -------------------------------------------------------------------------------- /animation_nodes/nodes/boolean/switch_node.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.props import * 3 | from ... base_types import AnimationNode, DataTypeSelectorSocket 4 | 5 | class SwitchNode(AnimationNode, bpy.types.Node): 6 | bl_idname = "an_SwitchNode" 7 | bl_label = "Switch" 8 | 9 | assignedType: DataTypeSelectorSocket.newProperty(default = "Float") 10 | 11 | def create(self): 12 | self.newInput("an_BooleanSocket", "Condition", "condition") 13 | 14 | self.newInput(DataTypeSelectorSocket("If True", "ifTrue", "assignedType")) 15 | self.newInput(DataTypeSelectorSocket("If False", "ifFalse", "assignedType")) 16 | 17 | self.newOutput(DataTypeSelectorSocket("Output", "output", "assignedType")) 18 | self.newOutput(DataTypeSelectorSocket("Other", "other", "assignedType"), hide = True) 19 | 20 | def drawAdvanced(self, layout): 21 | self.invokeSelector(layout, "DATA_TYPE", "assignType", 22 | text = "Change Type", icon = "TRIA_RIGHT") 23 | 24 | def assignType(self, dataType): 25 | if self.assignedType != dataType: 26 | self.assignedType = dataType 27 | self.refresh() 28 | 29 | def getExecutionCode(self, required): 30 | if "output" in required: yield "output = ifTrue if condition else ifFalse" 31 | if "other" in required: yield "other = ifFalse if condition else ifTrue" 32 | --------------------------------------------------------------------------------