Compare commits

..

19 Commits

Author SHA1 Message Date
bd6567b262 Workbench: Refactor: Antialiasing 2020-03-03 17:35:30 +01:00
91dfe40ef0 Workbench: Refactor: Add Dof Support 2020-03-03 16:45:13 +01:00
c8407006aa Overlay: Fix wireframe after shadow refactor 2020-03-03 02:37:22 +01:00
a102b7ebe5 Workbench: Refactor: Add outline support 2020-03-03 02:29:05 +01:00
aae2e1326f Workbench: Refactor: Add cavity support 2020-03-03 01:42:31 +01:00
606f1d80b3 Workbench: Refactor: Add Shadow support 2020-03-02 15:16:18 +01:00
f426e71e09 Workbench: Refactor: Add hair support + tweak the randomness of hairs color 2020-03-01 22:03:24 +01:00
62a7ac8f17 Workbench: Refactor: Add Infront support 2020-03-01 14:26:56 +01:00
73de98e6e0 Workbench: Refactor: Add transparent (xray) support 2020-03-01 13:41:39 +01:00
d36e63f8db Workbench: Refactor: Only allocate id buffer if needed 2020-02-29 23:53:29 +01:00
e34ce36ea4 Workbench: Refactor: Add specular toggle support 2020-02-29 18:29:13 +01:00
76a7ea0b71 Workbench: Refactor: Add matcap support 2020-02-29 18:28:58 +01:00
46f8367eac Workbench: Refactor: Add support for Flat shading 2020-02-29 17:22:04 +01:00
5759e2d6c4 Workbench: Refactor: Add support for texpaint & vertpaint 2020-02-29 16:42:47 +01:00
e6e740f317 Workbench: Refactor: Support Material data 2020-02-28 20:40:49 +01:00
b6e083358e BLI_memblock: Allow to use BLI_memblock_elem_get using only elem index 2020-02-28 17:29:48 +01:00
1e58e68c1a DRW: Manager: Expose resource handle getter 2020-02-28 17:28:58 +01:00
1545c37cdb Workbench: Refactor: Code reorganization 2020-02-28 02:38:47 +01:00
616545b5cf DRW: Shader: Add shader library manager
This new object makes it easier to resolve shader code dependency.
2020-02-27 15:06:27 +01:00
420 changed files with 5193 additions and 32134 deletions

View File

@@ -49,7 +49,6 @@ if(NOT LLVM_ROOT_DIR)
OUTPUT_VARIABLE LLVM_ROOT_DIR OUTPUT_VARIABLE LLVM_ROOT_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE) OUTPUT_STRIP_TRAILING_WHITESPACE)
set(LLVM_ROOT_DIR ${LLVM_ROOT_DIR} CACHE PATH "Path to the LLVM installation") set(LLVM_ROOT_DIR ${LLVM_ROOT_DIR} CACHE PATH "Path to the LLVM installation")
set(LLVM_INCLUDE_DIRS ${LLVM_ROOT_DIR}/include CACHE PATH "Path to the LLVM include directory")
endif() endif()
if(NOT LLVM_LIBPATH) if(NOT LLVM_LIBPATH)
execute_process(COMMAND ${LLVM_CONFIG} --libdir execute_process(COMMAND ${LLVM_CONFIG} --libdir

View File

@@ -6,6 +6,7 @@
# #
set(WITH_INSTALL_PORTABLE ON CACHE BOOL "" FORCE) set(WITH_INSTALL_PORTABLE ON CACHE BOOL "" FORCE)
set(WITH_SYSTEM_GLEW ON CACHE BOOL "" FORCE)
set(WITH_ALEMBIC OFF CACHE BOOL "" FORCE) set(WITH_ALEMBIC OFF CACHE BOOL "" FORCE)
set(WITH_BOOST OFF CACHE BOOL "" FORCE) set(WITH_BOOST OFF CACHE BOOL "" FORCE)

View File

@@ -49,11 +49,6 @@ if(WITH_TBB)
add_definitions(-DTBB=1) add_definitions(-DTBB=1)
endif() endif()
if(WITH_OPENVDB)
add_definitions(-DOPENVDB=1)
add_definitions(-DOPENVDB_STATICLIB)
endif()
if(WIN32) if(WIN32)
add_definitions(-D_USE_MATH_DEFINES) add_definitions(-D_USE_MATH_DEFINES)
endif() endif()
@@ -83,6 +78,7 @@ if(WITH_TBB)
endif() endif()
if(WITH_OPENVDB) if(WITH_OPENVDB)
add_definitions(-DOPENVDB=1 ${OPENVDB_DEFINITIONS})
list(APPEND INC_SYS list(APPEND INC_SYS
${OPENVDB_INCLUDE_DIRS} ${OPENVDB_INCLUDE_DIRS}
) )

View File

@@ -390,7 +390,7 @@ typedef enum PassType {
PASS_TRANSMISSION_DIRECT, PASS_TRANSMISSION_DIRECT,
PASS_TRANSMISSION_INDIRECT, PASS_TRANSMISSION_INDIRECT,
PASS_TRANSMISSION_COLOR, PASS_TRANSMISSION_COLOR,
PASS_VOLUME_DIRECT = 50, PASS_VOLUME_DIRECT,
PASS_VOLUME_INDIRECT, PASS_VOLUME_INDIRECT,
/* No Scatter color since it's tricky to define what it would even mean. */ /* No Scatter color since it's tricky to define what it would even mean. */
PASS_CATEGORY_LIGHT_END = 63, PASS_CATEGORY_LIGHT_END = 63,

View File

@@ -57,9 +57,7 @@ ccl_device void svm_node_vector_rotate(ShaderData *sd,
break; break;
} }
float angle = stack_load_float(stack, angle_stack_offset); float angle = stack_load_float(stack, angle_stack_offset);
result = (len_squared(axis) != 0.0f) ? result = len(axis) ? rotate_around_axis(vector - center, axis, angle) + center : vector;
rotate_around_axis(vector - center, axis, angle) + center :
vector;
} }
/* Output */ /* Output */

View File

@@ -21,11 +21,7 @@
* incompatible with the system libraries that Blender is built on. To solve * incompatible with the system libraries that Blender is built on. To solve
* this we add a few -ffast-math symbols that can be missing. */ * this we add a few -ffast-math symbols that can be missing. */
#ifdef __linux__ #include <math.h>
# include <features.h>
# include <math.h>
# if defined(__GLIBC_PREREQ) && __GLIBC_PREREQ(2, 31)
double __exp_finite(double x); double __exp_finite(double x);
double __acos_finite(double x); double __acos_finite(double x);
@@ -111,6 +107,3 @@ float __powf_finite(float x, float y)
{ {
return powf(x, y); return powf(x, y);
} }
# endif /* __GLIBC_PREREQ */
#endif /* __linux__ */

View File

@@ -64,7 +64,6 @@ if(WITH_TBB)
endif() endif()
if(WITH_OPENVDB) if(WITH_OPENVDB)
add_definitions(-DWITH_OPENVDB ${OPENVDB_DEFINITIONS})
list(APPEND INC_SYS list(APPEND INC_SYS
${OPENVDB_INCLUDE_DIRS} ${OPENVDB_INCLUDE_DIRS}
) )

View File

@@ -95,8 +95,8 @@ _km_hierarchy = [
('Weight Paint', 'EMPTY', 'WINDOW', [ ('Weight Paint', 'EMPTY', 'WINDOW', [
_km_expand_from_toolsystem('VIEW_3D', 'PAINT_WEIGHT'), _km_expand_from_toolsystem('VIEW_3D', 'PAINT_WEIGHT'),
]), ]),
('Paint Vertex Selection (Weight, Vertex)', 'EMPTY', 'WINDOW', []), ('Weight Paint Vertex Selection', 'EMPTY', 'WINDOW', []),
('Paint Face Mask (Weight, Vertex, Texture)', 'EMPTY', 'WINDOW', []), ('Face Mask', 'EMPTY', 'WINDOW', []),
# image and view3d # image and view3d
('Image Paint', 'EMPTY', 'WINDOW', [ ('Image Paint', 'EMPTY', 'WINDOW', [
_km_expand_from_toolsystem('VIEW_3D', 'PAINT_TEXTURE'), _km_expand_from_toolsystem('VIEW_3D', 'PAINT_TEXTURE'),

View File

@@ -3380,7 +3380,7 @@ def km_grease_pencil_stroke_weight_mode(_params):
def km_face_mask(params): def km_face_mask(params):
items = [] items = []
keymap = ( keymap = (
"Paint Face Mask (Weight, Vertex, Texture)", "Face Mask",
{"space_type": 'EMPTY', "region_type": 'WINDOW'}, {"space_type": 'EMPTY', "region_type": 'WINDOW'},
{"items": items}, {"items": items},
) )
@@ -3405,7 +3405,7 @@ def km_face_mask(params):
def km_weight_paint_vertex_selection(params): def km_weight_paint_vertex_selection(params):
items = [] items = []
keymap = ( keymap = (
"Paint Vertex Selection (Weight, Vertex)", "Weight Paint Vertex Selection",
{"space_type": 'EMPTY', "region_type": 'WINDOW'}, {"space_type": 'EMPTY', "region_type": 'WINDOW'},
{"items": items}, {"items": items},
) )
@@ -5408,8 +5408,8 @@ def km_3d_view_tool_edit_armature_bone_size(params):
"3D View Tool: Edit Armature, Bone Size", "3D View Tool: Edit Armature, Bone Size",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [ {"items": [
("transform.transform", {"type": params.tool_tweak, "value": 'ANY'}, ("transform.bbone_resize", {"type": params.tool_tweak, "value": 'ANY'},
{"properties": [("release_confirm", True), ("mode", 'BONE_ENVELOPE')]}), {"properties": [("release_confirm", True)]}),
]}, ]},
) )
@@ -5418,10 +5418,9 @@ def km_3d_view_tool_edit_armature_bone_envelope(params):
return ( return (
"3D View Tool: Edit Armature, Bone Envelope", "3D View Tool: Edit Armature, Bone Envelope",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [ {"items": [
("transform.bbone_resize", {"type": params.tool_tweak, "value": 'ANY'}, ("transform.transform", {"type": params.tool_tweak, "value": 'ANY'},
{"properties": [("release_confirm", True)]}), {"properties": [("release_confirm", True), ("mode", 'BONE_ENVELOPE')]}),
]}, ]},
) )

View File

@@ -2531,7 +2531,7 @@ def km_grease_pencil_stroke_weight_mode(params):
def km_face_mask(params): def km_face_mask(params):
items = [] items = []
keymap = ( keymap = (
"Paint Face Mask (Weight, Vertex, Texture)", "Face Mask",
{"space_type": 'EMPTY', "region_type": 'WINDOW'}, {"space_type": 'EMPTY', "region_type": 'WINDOW'},
{"items": items}, {"items": items},
) )
@@ -2558,7 +2558,7 @@ def km_face_mask(params):
def km_weight_paint_vertex_selection(params): def km_weight_paint_vertex_selection(params):
items = [] items = []
keymap = ( keymap = (
"Paint Vertex Selection (Weight, Vertex)", "Weight Paint Vertex Selection",
{"space_type": 'EMPTY', "region_type": 'WINDOW'}, {"space_type": 'EMPTY', "region_type": 'WINDOW'},
{"items": items}, {"items": items},
) )

View File

@@ -33,7 +33,6 @@ _modules = [
"file", "file",
"image", "image",
"mesh", "mesh",
"modifiers",
"node", "node",
"object", "object",
"object_align", "object_align",

View File

@@ -1,91 +0,0 @@
import bpy
from bpy.props import (
StringProperty,
)
class ModifierOperator:
object_name: StringProperty()
modifier_name: StringProperty()
def get_modifier(self):
ob = bpy.data.objects.get(self.object_name)
if ob is None:
return None
return ob.modifiers.get(self.modifier_name)
class NewDeformationFunction(bpy.types.Operator, ModifierOperator):
bl_idname = "fn.new_deformation_function"
bl_label = "New Deformation Function"
def execute(self, context):
mod = self.get_modifier()
if mod is None:
return {'CANCELLED'}
from nodes.node_operators import new_function_tree
tree = new_function_tree("Deformation Function", [
("Vector", "Old Position"),
("Float", "Control 1"),
("Integer", "Control 2")
], [
("Vector", "New Position"),
])
tree.new_link(
tree.get_input_nodes()[0].outputs[0],
tree.get_output_nodes()[0].inputs[0])
mod.function_tree = tree
return {'FINISHED'}
class NewPointGeneratorFunction(bpy.types.Operator, ModifierOperator):
bl_idname = "fn.new_point_generator_function"
bl_label = "New Point Generator Function"
def execute(self, context):
mod = self.get_modifier()
if mod is None:
return {'CANCELLED'}
from nodes.node_operators import new_function_tree
tree = new_function_tree("Point Generator", [
("Float", "Control 1"),
("Integer", "Control 2"),
], [
("Vector List", "Points"),
])
mod.function_tree = tree
return {'FINISHED'}
class NewParticleSimulationTree(bpy.types.Operator, ModifierOperator):
bl_idname = "fn.new_particle_simulation_tree"
bl_label = "New Particle Simulation Tree"
def execute(self, context):
mod = self.get_modifier()
if mod is None:
return {'CANCELLED'}
tree = bpy.data.node_groups.new("Particle Simulation", "FunctionTree")
type_node = tree.nodes.new("fn_ParticleSystemNode")
emitter_node = tree.nodes.new("fn_InitialGridEmitterNode")
emitter_node.location = (-250, 200)
gravity_node = tree.nodes.new("fn_ForceNode")
gravity_node.inputs[0].value = (0, 0, -1)
gravity_node.location = (-250, -100)
tree.links.new(emitter_node.outputs[0], type_node.inputs[0])
tree.links.new(gravity_node.outputs[0], type_node.inputs[0])
mod.node_tree = tree
return {'FINISHED'}
classes = (
NewDeformationFunction,
NewPointGeneratorFunction,
NewParticleSimulationTree,
)

View File

@@ -406,7 +406,7 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
row.prop(md, "mid_level") row.prop(md, "mid_level")
row.prop(md, "strength") row.prop(md, "strength")
def DYNAMIC_PAINT(self, layout, _ob, md): def DYNAMIC_PAINT(self, layout, _ob, _md):
layout.label(text="Settings are inside the Physics tab") layout.label(text="Settings are inside the Physics tab")
def EDGE_SPLIT(self, layout, _ob, md): def EDGE_SPLIT(self, layout, _ob, md):
@@ -494,9 +494,13 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
layout.prop(md, "iterations") layout.prop(md, "iterations")
row = layout.row(align=True) row = layout.row()
row.active = not is_bind
row.label(text="Anchors Vertex Group:")
row = layout.row()
row.enabled = not is_bind row.enabled = not is_bind
row.prop_search(md, "vertex_group", ob, "vertex_groups") row.prop_search(md, "vertex_group", ob, "vertex_groups", text="")
row.prop(md, "invert_vertex_group", text="", icon='ARROW_LEFTRIGHT') row.prop(md, "invert_vertex_group", text="", icon='ARROW_LEFTRIGHT')
layout.separator() layout.separator()
@@ -1711,42 +1715,6 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
col.prop(md, "thresh", text="Threshold") col.prop(md, "thresh", text="Threshold")
col.prop(md, "face_influence") col.prop(md, "face_influence")
def FUNCTION_DEFORM(self, layout, ob, md):
layout.prop(md, "control1")
layout.prop(md, "control2")
row = layout.row(align=True)
row.prop(md, "function_tree")
props = row.operator("fn.new_deformation_function", text="", icon="ADD")
props.object_name = ob.name
props.modifier_name = md.name
def FUNCTION_POINTS(self, layout, ob, md):
layout.prop(md, "control1")
layout.prop(md, "control2")
row = layout.row(align=True)
row.prop(md, "function_tree")
props = row.operator("fn.new_point_generator_function", text="", icon="ADD")
props.object_name = ob.name
props.modifier_name = md.name
def BPARTICLES(self, layout, ob, md):
row = layout.row(align=True)
row.prop(md, "node_tree")
props = row.operator("fn.new_particle_simulation_tree", text="", icon="ADD")
props.object_name = ob.name
props.modifier_name = md.name
layout.operator("object.bparticles_clear_cache", text="Clear Cache")
layout.prop(md, "output_type")
def BPARTICLES_OUTPUT(self, layout, ob, md):
layout.prop(md, "source_object")
layout.prop(md, "source_particle_system", icon="PHYSICS")
layout.prop(md, "output_type")
class DATA_PT_gpencil_modifiers(ModifierButtonsPanel, Panel): class DATA_PT_gpencil_modifiers(ModifierButtonsPanel, Panel):
bl_label = "Modifiers" bl_label = "Modifiers"

View File

@@ -206,11 +206,6 @@ class NODE_MT_add(bpy.types.Menu):
def draw(self, context): def draw(self, context):
layout = self.layout layout = self.layout
from nodes.base import BaseTree
tree = context.space_data.node_tree
if isinstance(tree, BaseTree):
return
layout.operator_context = 'INVOKE_DEFAULT' layout.operator_context = 'INVOKE_DEFAULT'
props = layout.operator("node.add_search", text="Search...", icon='VIEWZOOM') props = layout.operator("node.add_search", text="Search...", icon='VIEWZOOM')
props.use_transform = True props.use_transform = True

View File

@@ -68,8 +68,8 @@ ToolDef = namedtuple(
"idname", "idname",
# The name to display in the interface. # The name to display in the interface.
"label", "label",
# Description (for tool-tip), when not set, use the description of 'operator', # Description (for tooltip), when not set, use the description of 'operator',
# may be a string or a 'function(context, item, key-map) -> string'. # may be a string or a 'function(context, item, keymap) -> string'.
"description", "description",
# The name of the icon to use (found in ``release/datafiles/icons``) or None for no icon. # The name of the icon to use (found in ``release/datafiles/icons``) or None for no icon.
"icon", "icon",
@@ -77,34 +77,13 @@ ToolDef = namedtuple(
"cursor", "cursor",
# An optional gizmo group to activate when the tool is set or None for no gizmo. # An optional gizmo group to activate when the tool is set or None for no gizmo.
"widget", "widget",
# Optional key-map for tool, possible values are: # Optional keymap for tool, either:
# # - A function that populates a keymaps passed in as an argument.
# - ``None`` when the tool doesn't have a key-map.
# Also the default value when no key-map value is defined.
#
# - A string literal for the key-map name, the key-map items are located in the default key-map.
#
# - ``()`` an empty tuple for a default name.
# This is convenience functionality for generating a key-map name.
# So if a tool name is "Bone Size", in "Edit Armature" mode for the "3D View",
# All of these values are combined into an id, e.g:
# "3D View Tool: Edit Armature, Bone Envelope"
#
# Typically searching for a string ending with the tool name
# in the default key-map will lead you to the key-map for a tool.
#
# - A function that populates a key-maps passed in as an argument.
#
# - A tuple filled with triple's of: # - A tuple filled with triple's of:
# ``(operator_id, operator_properties, keymap_item_args)``. # ``(operator_id, operator_properties, keymap_item_args)``.
# #
# Use this to define the key-map in-line.
#
# Note that this isn't used for Blender's built in tools which use the built-in key-map.
# Keep this functionality since it's likely useful for add-on key-maps.
#
# Warning: currently 'from_dict' this is a list of one item, # Warning: currently 'from_dict' this is a list of one item,
# so internally we can swap the key-map function for the key-map it's self. # so internally we can swap the keymap function for the keymap it's self.
# This isn't very nice and may change, tool definitions shouldn't care about this. # This isn't very nice and may change, tool definitions shouldn't care about this.
"keymap", "keymap",
# Optional data-block associated with this tool. # Optional data-block associated with this tool.

View File

@@ -18,9 +18,6 @@
# <pep8 compliant> # <pep8 compliant>
# For documentation on tool definitions: see "bl_ui.space_toolsystem_common.ToolDef"
# where there are comments for each field and their use.
# For now group all tools together # For now group all tools together
# we may want to move these into per space-type files. # we may want to move these into per space-type files.
# #
@@ -1130,25 +1127,8 @@ class _defs_weight_paint:
def draw_settings(context, layout, tool): def draw_settings(context, layout, tool):
brush = context.tool_settings.weight_paint.brush brush = context.tool_settings.weight_paint.brush
if brush is not None: if brush is not None:
from bl_ui.properties_paint_common import UnifiedPaintPanel layout.prop(brush, "weight", slider=True)
UnifiedPaintPanel.prop_unified( layout.prop(brush, "strength", slider=True)
layout,
context,
brush,
"weight",
unified_name="use_unified_weight",
slider=True,
header=True
)
UnifiedPaintPanel.prop_unified(
layout,
context,
brush,
"strength",
unified_name="use_unified_strength",
header=True
)
props = tool.operator_properties("paint.weight_gradient") props = tool.operator_properties("paint.weight_gradient")
layout.prop(props, "type", expand=True) layout.prop(props, "type", expand=True)

View File

@@ -1190,7 +1190,6 @@ class USERPREF_PT_file_paths_data(FilePathsPanel, Panel):
col.prop(paths, "script_directory", text="Scripts") col.prop(paths, "script_directory", text="Scripts")
col.prop(paths, "sound_directory", text="Sounds") col.prop(paths, "sound_directory", text="Sounds")
col.prop(paths, "temporary_directory", text="Temporary Files") col.prop(paths, "temporary_directory", text="Temporary Files")
col.prop(paths, "nodelib_directory", text="Nodelib Files")
class USERPREF_PT_file_paths_render(FilePathsPanel, Panel): class USERPREF_PT_file_paths_render(FilePathsPanel, Panel):

View File

@@ -2145,10 +2145,6 @@ class VIEW3D_MT_add(Menu):
layout.separator() layout.separator()
layout.operator("fn.new_particle_system", text="Particle Simulation", icon='MOD_PARTICLES')
layout.separator()
if VIEW3D_MT_camera_add.is_extended(): if VIEW3D_MT_camera_add.is_extended():
layout.menu("VIEW3D_MT_camera_add", icon='OUTLINER_OB_CAMERA') layout.menu("VIEW3D_MT_camera_add", icon='OUTLINER_OB_CAMERA')
else: else:

View File

@@ -1,3 +0,0 @@
from . auto_load import init, register, unregister
init()

View File

@@ -1,138 +0,0 @@
import os
import bpy
import sys
import typing
import inspect
import pkgutil
import importlib
from pathlib import Path
__all__ = (
"init",
"register",
"unregister",
)
modules = None
ordered_classes = None
def init():
global modules
global ordered_classes
modules = get_all_submodules(Path(__file__).parent)
ordered_classes = get_ordered_classes_to_register(modules)
def register():
for cls in ordered_classes:
bpy.utils.register_class(cls)
for module in modules:
if module.__name__ == __name__:
continue
if hasattr(module, "register"):
module.register()
def unregister():
for cls in reversed(ordered_classes):
bpy.utils.unregister_class(cls)
for module in modules:
if module.__name__ == __name__:
continue
if hasattr(module, "unregister"):
module.unregister()
# Import modules
#################################################
def get_all_submodules(directory):
return list(iter_submodules(directory, directory.name))
def iter_submodules(path, package_name):
for name in sorted(iter_submodule_names(path)):
yield importlib.import_module("." + name, package_name)
def iter_submodule_names(path, root=""):
for _, module_name, is_package in pkgutil.iter_modules([str(path)]):
if is_package:
sub_path = path / module_name
sub_root = root + module_name + "."
yield from iter_submodule_names(sub_path, sub_root)
else:
yield root + module_name
# Find classes to register
#################################################
def get_ordered_classes_to_register(modules):
return toposort(get_register_deps_dict(modules))
def get_register_deps_dict(modules):
deps_dict = {}
classes_to_register = set(iter_classes_to_register(modules))
for cls in classes_to_register:
deps_dict[cls] = set(iter_own_register_deps(cls, classes_to_register))
return deps_dict
def iter_own_register_deps(cls, own_classes):
yield from (dep for dep in iter_register_deps(cls) if dep in own_classes)
def iter_register_deps(cls):
for value in typing.get_type_hints(cls, {}, {}).values():
dependency = get_dependency_from_annotation(value)
if dependency is not None:
yield dependency
def get_dependency_from_annotation(value):
if isinstance(value, tuple) and len(value) == 2:
if value[0] in (bpy.props.PointerProperty, bpy.props.CollectionProperty):
return value[1]["type"]
return None
def iter_classes_to_register(modules):
base_types = get_register_base_types()
for cls in get_classes_in_modules(modules):
if any(base in base_types for base in cls.__bases__):
if not getattr(cls, "is_registered", False):
yield cls
def get_classes_in_modules(modules):
classes = set()
for module in modules:
for cls in iter_classes_in_module(module):
classes.add(cls)
return classes
def iter_classes_in_module(module):
for value in module.__dict__.values():
if inspect.isclass(value):
yield value
def get_register_base_types():
return set(getattr(bpy.types, name) for name in [
"Panel", "Operator", "PropertyGroup",
"AddonPreferences", "Header", "Menu",
"Node", "NodeSocket", "NodeTree",
"UIList", "RenderEngine"
])
# Find order to register to solve dependencies
#################################################
def toposort(deps_dict):
sorted_list = []
sorted_values = set()
while len(deps_dict) > 0:
unsorted = []
for value, deps in deps_dict.items():
if len(deps) == 0:
sorted_list.append(value)
sorted_values.add(value)
else:
unsorted.append(value)
deps_dict = {value : deps_dict[value] - sorted_values for value in unsorted}
return sorted_list

View File

@@ -1,303 +0,0 @@
import bpy
from bpy.props import *
from . utils.generic import iter_subclasses_recursive
import itertools
from collections import defaultdict
class BaseTree:
def new_link(self, a, b):
if a.is_output:
self.links.new(a, b)
else:
self.links.new(b, a)
def update(self):
self.sync()
def sync(self):
from . sync import sync_trees_and_dependent_trees
sync_trees_and_dependent_trees({self})
class SocketValueStates:
def __init__(self, node):
self.node = node
self.input_value_storage = dict()
def store_current(self):
for socket in self.node.inputs:
if not isinstance(socket, DataSocket):
continue
storage_id = (socket.data_type, socket.identifier)
self.input_value_storage[storage_id] = socket.get_state()
def try_load(self):
for socket in self.node.inputs:
if not isinstance(socket, DataSocket):
continue
storage_id = (socket.data_type, socket.identifier)
if storage_id in self.input_value_storage:
socket.restore_state(self.input_value_storage[storage_id])
def get_new_node_identifier():
import uuid
return str(uuid.uuid4())
class BaseNode:
search_terms = tuple()
search_terms_only = False
identifier: StringProperty()
def init(self, context):
self.identifier = get_new_node_identifier()
from . sync import skip_syncing
with skip_syncing():
self.init_props()
builder = self.get_node_builder()
builder.initialize_decls()
builder.build()
def init_props(self):
pass
@classmethod
def get_search_terms(cls):
if not cls.search_terms_only:
yield (cls.bl_label, dict())
yield from cls.search_terms
def sync_tree(self, context=None):
self.tree.sync()
def rebuild(self):
from . sync import skip_syncing
with skip_syncing():
self.socket_value_states.store_current()
linkage_state = LinkageState(self)
self.rebuild_fast()
self.socket_value_states.try_load()
linkage_state.try_restore()
def rebuild_fast(self):
from . sync import skip_syncing
with skip_syncing():
builder = self.get_node_builder()
builder.build()
_decl_map_per_node[self] = builder.get_sockets_decl_map()
@property
def tree(self):
return self.id_data
def get_node_builder(self):
from . node_builder import NodeBuilder
builder = NodeBuilder(self)
self.declaration(builder)
return builder
def declaration(self, builder):
raise NotImplementedError()
def draw_buttons(self, context, layout):
self.draw(layout)
for decl in self.decl_map.iter_decls():
decl.draw_node(layout)
def draw_buttons_ext(self, context, layout):
self.draw_advanced(layout)
def draw(self, layout):
pass
def draw_advanced(self, layout):
pass
def draw_socket(self, layout, socket, text, decl, index_in_decl):
decl.draw_socket(layout, socket, index_in_decl)
def draw_label(self):
if self.hide:
return self.draw_closed_label()
else:
return self.bl_label
def draw_closed_label(self):
return self.bl_label
def iter_directly_used_trees(self):
return
yield
def invoke_function(self,
layout, function_name, text,
*, icon="NONE", settings=tuple()):
assert isinstance(settings, tuple)
props = layout.operator("fn.node_operator", text=text, icon=icon)
self._set_common_invoke_props(props, function_name, settings)
def invoke_type_selection(self,
layout, function_name, text,
*, mode="ALL", icon="NONE", settings=tuple()):
assert isinstance(settings, tuple)
props = layout.operator("fn.node_data_type_selector", text=text, icon=icon)
self._set_common_invoke_props(props, function_name, settings)
props.mode = mode
def invoke_group_selector(self,
layout, function_name, text,
*, icon="NONE", settings=tuple()):
assert isinstance(settings, tuple)
props = layout.operator("fn.node_group_selector", text=text, icon=icon)
self._set_common_invoke_props(props, function_name, settings)
def _set_common_invoke_props(self, props, function_name, settings):
props.tree_name = self.id_data.name
props.node_name = self.name
props.function_name = function_name
props.settings_repr = repr(settings)
@classmethod
def iter_final_subclasses(cls):
yield from filter(lambda x: issubclass(x, bpy.types.Node), iter_subclasses_recursive(cls))
def find_input(self, identifier):
for socket in self.inputs:
if socket.identifier == identifier:
return socket
else:
return None
def find_output(self, identifier):
for socket in self.outputs:
if socket.identifier == identifier:
return socket
else:
return None
def find_socket(self, identifier, is_output):
if is_output:
return self.find_output(identifier)
else:
return self.find_input(identifier)
def iter_sockets(self):
yield from self.inputs
yield from self.outputs
# Storage
#########################
@property
def decl_map(self):
if self not in _decl_map_per_node:
builder = self.get_node_builder()
_decl_map_per_node[self] = builder.get_sockets_decl_map()
return _decl_map_per_node[self]
@property
def socket_value_states(self):
if self not in _socket_value_states_per_node:
_socket_value_states_per_node[self] = SocketValueStates(self)
return _socket_value_states_per_node[self]
def free(self):
if self in _decl_map_per_node:
del _decl_map_per_node[self]
def copy(self, src_node):
self.identifier = get_new_node_identifier()
self.duplicate(src_node)
def duplicate(self, src_node):
pass
class BaseSocket:
color = (0, 0, 0, 0)
def draw_color(self, context, node):
return self.color
def draw(self, context, layout, node, text):
decl, index = self.get_decl_with_index(node)
node.draw_socket(layout, self, text, decl, index)
def draw_self(self, layout, node, text):
layout.label(text=text)
def get_index(self, node):
if self.is_output:
return tuple(node.outputs).index(self)
else:
return tuple(node.inputs).index(self)
def to_id(self, node):
return (node, self.is_output, self.identifier)
def get_decl(self, node):
return node.decl_map.get_decl_by_socket(self)
def get_decl_with_index(self, node):
decl_map = node.decl_map
decl = decl_map.get_decl_by_socket(self)
index = decl_map.get_socket_index_in_decl(self)
return decl, index
class FunctionNode(BaseNode):
pass
class SimulationNode(BaseNode):
pass
class DataSocket(BaseSocket):
def draw_self(self, layout, node, text):
if not (self.is_linked or self.is_output) and hasattr(self, "draw_property"):
self.draw_property(layout, node, text)
else:
layout.label(text=text)
def get_state(self):
return None
def restore_state(self, state):
pass
class LinkageState:
def __init__(self, node):
self.node = node
self.tree = node.tree
self.links_per_input = defaultdict(set)
self.links_per_output = defaultdict(set)
for link in self.tree.links:
if link.from_node == node:
self.links_per_output[link.from_socket.identifier].add(link.to_socket)
if link.to_node == node:
self.links_per_input[link.to_socket.identifier].add(link.from_socket)
def try_restore(self):
tree = self.tree
for socket in self.node.inputs:
for from_socket in self.links_per_input[socket.identifier]:
tree.links.new(socket, from_socket)
for socket in self.node.outputs:
for to_socket in self.links_per_output[socket.identifier]:
tree.links.new(to_socket, socket)
_decl_map_per_node = {}
_socket_value_states_per_node = {}
@bpy.app.handlers.persistent
def clear_cached_node_states(_):
_decl_map_per_node.clear()
_socket_value_states_per_node.clear()
def register():
bpy.app.handlers.load_pre.append(clear_cached_node_states)

View File

@@ -1,26 +0,0 @@
import bpy
from bpy.props import *
from .. base import SimulationNode
from .. node_builder import NodeBuilder
class AlwaysExecuteNode(bpy.types.Node, SimulationNode):
bl_idname = "fn_AlwaysExecuteNode"
bl_label = "Always Execute"
execute__prop: NodeBuilder.ExecuteInputProperty()
def declaration(self, builder: NodeBuilder):
builder.execute_input("execute", "Execute", "execute__prop")
builder.influences_output("influence", "Influence")
class MultiExecuteNode(bpy.types.Node, SimulationNode):
bl_idname = "fn_MultiExecuteNode"
bl_label = "Multi Execute"
execute__prop: NodeBuilder.ExecuteInputProperty()
def declaration(self, builder: NodeBuilder):
builder.execute_input("execute", "Execute", "execute__prop")
builder.execute_output("execute", "Execute")

View File

@@ -1,11 +0,0 @@
import bpy
from .. base import SimulationNode
from .. node_builder import NodeBuilder
class CombineInfluencesNode(bpy.types.Node, SimulationNode):
bl_idname = "fn_CombineInfluencesNode"
bl_label = "Combine Influences"
def declaration(self, builder: NodeBuilder):
builder.influences_input("influences", "Influences")
builder.influences_output("influences", "Influences")

View File

@@ -1,18 +0,0 @@
import bpy
from bpy.props import *
from .. base import SimulationNode
from .. node_builder import NodeBuilder
class ParticleConditionNode(bpy.types.Node, SimulationNode):
bl_idname = "fn_ParticleConditionNode"
bl_label = "Particle Condition"
execute_if_true__prop: NodeBuilder.ExecuteInputProperty()
execute_if_false__prop: NodeBuilder.ExecuteInputProperty()
def declaration(self, builder: NodeBuilder):
builder.fixed_input("condition", "Condition", "Boolean")
builder.execute_input("execute_if_true", "Execute If True", "execute_if_true__prop")
builder.execute_input("execute_if_false", "Execute If False", "execute_if_false__prop")
builder.execute_output("execute", "Execute")

View File

@@ -1,46 +0,0 @@
import bpy
from bpy.props import *
from .. base import SimulationNode, FunctionNode
from .. node_builder import NodeBuilder
class SetParticleAttributeNode(bpy.types.Node, SimulationNode):
bl_idname = "fn_SetParticleAttributeNode"
bl_label = "Set Attribute"
attribute_type: StringProperty(
name="Attribute Type",
default="Float",
update=SimulationNode.sync_tree,
)
def declaration(self, builder: NodeBuilder):
builder.fixed_input("name", "Name", "Text", default="My Attribute", display_name=False)
builder.fixed_input("value", "Value", self.attribute_type)
builder.execute_output("execute", "Execute")
def draw(self, layout):
self.invoke_type_selection(layout, "set_type", "Select Type", mode="BASE", icon="SETTINGS")
def set_type(self, data_type):
self.attribute_type = data_type
class GetParticleAttributeNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_GetParticleAttributeNode"
bl_label = "Get Attribute"
attribute_type: StringProperty(
name="Attribute Type",
default="Float",
update=SimulationNode.sync_tree,
)
def declaration(self, builder: NodeBuilder):
builder.fixed_input("name", "Name", "Text", default="My Attribute", display_name=False)
builder.fixed_output("value", "Value", self.attribute_type)
def draw(self, layout):
self.invoke_type_selection(layout, "set_type", "Select Type", mode="BASE", icon="SETTINGS")
def set_type(self, data_type):
self.attribute_type = data_type

View File

@@ -1,169 +0,0 @@
import bpy
import uuid
from bpy.props import *
from .. base import SimulationNode, DataSocket, FunctionNode
from .. node_builder import NodeBuilder
from .. types import type_infos
from .. sync import skip_syncing
class CustomEmitterAttribute(bpy.types.PropertyGroup):
def sync_tree(self, context):
self.id_data.sync()
attribute_name: StringProperty(update=sync_tree)
attribute_type: StringProperty(update=sync_tree)
identifier: StringProperty()
is_list: NodeBuilder.VectorizedProperty()
class CustomEmitter(bpy.types.Node, SimulationNode):
bl_idname = "fn_CustomEmitterNode"
bl_label = "Custom Emitter"
execute_on_birth__prop: NodeBuilder.ExecuteInputProperty()
attributes: CollectionProperty(
type=CustomEmitterAttribute,
)
birth_time_mode: EnumProperty(
name="Birth Time Mode",
items=[
("NONE", "None", "Manually specify birth times of every particle", "NONE", 0),
("BEGIN", "Begin", "Spawn particles at the beginning of each time step", "NONE", 1),
("END", "End", "Spawn particles at the end of each time step", "NONE", 2),
("RANDOM", "Random", "Spawn particles at random moments in the time step", "NONE", 3),
("LINEAR", "Linear", "Distribute particles linearly in each time step", "NONE", 4),
],
default="END",
)
def init_props(self):
self.add_attribute("Vector", "Position")
def declaration(self, builder: NodeBuilder):
for i, item in enumerate(self.attributes):
builder.vectorized_input(
item.identifier,
f"attributes[{i}].is_list",
item.attribute_name,
item.attribute_name,
item.attribute_type)
builder.execute_input("execute_on_birth", "Execute on Birth", "execute_on_birth__prop")
builder.influences_output("emitter", "Emitter")
def draw(self, layout):
layout.prop(self, "birth_time_mode", text="Birth")
self.invoke_type_selection(layout, "add_attribute", "Add Attribute", mode="BASE")
def draw_socket(self, layout, socket, text, decl, index_in_decl):
if isinstance(socket, DataSocket):
index = list(self.inputs).index(socket)
item = self.attributes[index]
col = layout.column(align=True)
row = col.row(align=True)
row.prop(item, "attribute_name", text="")
self.invoke_type_selection(row, "set_attribute_type", "",
icon="SETTINGS", mode="BASE", settings=(index, ))
self.invoke_function(row, "remove_attribute", "", icon="X", settings=(index, ))
if not socket.is_linked and hasattr(socket, "draw_property"):
socket.draw_property(col, self, "")
else:
decl.draw_socket(layout, socket, index_in_decl)
def add_attribute(self, data_type, name="My Attribute"):
with skip_syncing():
item = self.attributes.add()
item.identifier = str(uuid.uuid4())
item.attribute_type = data_type
item.attribute_name = name
self.sync_tree()
def remove_attribute(self, index):
self.attributes.remove(index)
self.sync_tree()
def set_attribute_type(self, data_type, index):
self.attributes[index].attribute_type = data_type
class EmitterTimeInfoNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_EmitterTimeInfoNode"
bl_label = "Emitter Time Info"
def declaration(self, builder: NodeBuilder):
builder.fixed_output("duration", "Duration", "Float")
builder.fixed_output("begin", "Begin", "Float")
builder.fixed_output("end", "End", "Float")
builder.fixed_output("step", "Step", "Integer")
class SpawnParticlesAttribute(bpy.types.PropertyGroup):
def sync_tree(self, context):
self.id_data.sync()
attribute_name: StringProperty(update=sync_tree)
attribute_type: StringProperty(update=sync_tree)
identifier: StringProperty()
is_list: NodeBuilder.VectorizedProperty()
class SpawnParticlesNode(bpy.types.Node, SimulationNode):
bl_idname = "fn_SpawnParticlesNode"
bl_label = "Spawn Particles"
execute_on_birth__prop: NodeBuilder.ExecuteInputProperty()
attributes: CollectionProperty(
type=SpawnParticlesAttribute,
)
def init_props(self):
self.add_attribute("Vector", "Position")
def declaration(self, builder: NodeBuilder):
for i, item in enumerate(self.attributes):
builder.vectorized_input(
item.identifier,
f"attributes[{i}].is_list",
item.attribute_name,
item.attribute_name,
item.attribute_type)
builder.execute_input("execute_on_birth", "Execute on Birth", "execute_on_birth__prop")
builder.execute_output("execute", "Execute")
builder.influences_output("spawn_system", "Spawn System")
def draw(self, layout):
self.invoke_type_selection(layout, "add_attribute", "Add Attribute", mode="BASE")
def draw_socket(self, layout, socket, text, decl, index_in_decl):
if isinstance(socket, DataSocket):
index = list(self.inputs).index(socket)
item = self.attributes[index]
col = layout.column(align=True)
row = col.row(align=True)
row.prop(item, "attribute_name", text="")
self.invoke_type_selection(row, "set_attribute_type", "",
icon="SETTINGS", mode="BASE", settings=(index, ))
self.invoke_function(row, "remove_attribute", "", icon="X", settings=(index, ))
if not socket.is_linked and hasattr(socket, "draw_property"):
socket.draw_property(col, self, "")
else:
decl.draw_socket(layout, socket, index_in_decl)
def add_attribute(self, data_type, name="My Attribute"):
with skip_syncing():
item = self.attributes.add()
item.identifier = str(uuid.uuid4())
item.attribute_type = data_type
item.attribute_name = name
self.sync_tree()
def remove_attribute(self, index):
self.attributes.remove(index)
self.sync_tree()
def set_attribute_type(self, data_type, index):
self.attributes[index].attribute_type = data_type

View File

@@ -1,60 +0,0 @@
import bpy
from bpy.props import *
from .. base import SimulationNode, FunctionNode
from .. node_builder import NodeBuilder
class AgeReachedEventNode(bpy.types.Node, SimulationNode):
bl_idname = "fn_AgeReachedEventNode"
bl_label = "Age Reached Event"
execute_on_event__prop: NodeBuilder.ExecuteInputProperty()
def declaration(self, builder: NodeBuilder):
builder.fixed_input("age", "Age", "Float", default=3)
builder.execute_input("execute_on_event", "Execute on Event", "execute_on_event__prop")
builder.influences_output("event", "Event")
class MeshCollisionEventNode(bpy.types.Node, SimulationNode):
bl_idname = "fn_MeshCollisionEventNode"
bl_label = "Mesh Collision Event"
execute_on_event__prop: NodeBuilder.ExecuteInputProperty()
def declaration(self, builder: NodeBuilder):
builder.fixed_input("object", "Object", "Object")
builder.execute_input("execute_on_event", "Execute on Event", "execute_on_event__prop")
builder.influences_output("event", "Event")
class CustomEventNode(bpy.types.Node, SimulationNode):
bl_idname = "fn_CustomEventNode"
bl_label = "Custom Event"
execute_on_event__prop: NodeBuilder.ExecuteInputProperty()
def declaration(self, builder: NodeBuilder):
builder.fixed_input("condition", "Condition", "Boolean")
builder.fixed_input("time_factor", "Time Factor", "Float", default=1.0)
builder.execute_input("execute_on_event", "Execute on Event", "execute_on_event__prop")
builder.influences_output("event", "Event")
class EventFilterEndTimeNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_EventFilterEndTimeNode"
bl_label = "Event Filter End Time"
def declaration(self, builder: NodeBuilder):
builder.fixed_output("end_time", "End Time", "Float")
class EventFilterDurationNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_EventFilterDurationNode"
bl_label = "Event Filter Duration"
def declaration(self, builder: NodeBuilder):
builder.fixed_output("duration", "Duration", "Float")

View File

@@ -1,13 +0,0 @@
import bpy
from bpy.props import *
from .. base import SimulationNode
from .. node_builder import NodeBuilder
class ForceNode(bpy.types.Node, SimulationNode):
bl_idname = "fn_ForceNode"
bl_label = "Force"
def declaration(self, builder: NodeBuilder):
builder.fixed_input("force", "Force", "Vector")
builder.influences_output("force", "Force")

View File

@@ -1,19 +0,0 @@
import bpy
from bpy.props import *
from .. base import SimulationNode
from .. node_builder import NodeBuilder
class InitialGridEmitterNode(bpy.types.Node, SimulationNode):
bl_idname = "fn_InitialGridEmitterNode"
bl_label = "Initial Grid Emitter"
execute_on_birth__prop: NodeBuilder.ExecuteInputProperty()
def declaration(self, builder: NodeBuilder):
builder.fixed_input("amount_x", "Amount X", "Integer", default=50)
builder.fixed_input("amount_y", "Amount Y", "Integer", default=50)
builder.fixed_input("step_x", "Step X", "Float", default=0.2)
builder.fixed_input("step_y", "Step Y", "Float", default=0.2)
builder.fixed_input("size", "Size", "Float", default=0.01)
builder.execute_input("execute_on_birth", "Execute on Birth", "execute_on_birth__prop")
builder.influences_output("emitter", "Emitter")

View File

@@ -1,32 +0,0 @@
import bpy
from bpy.props import *
from .. base import SimulationNode
from .. node_builder import NodeBuilder
class MeshEmitterNode(bpy.types.Node, SimulationNode):
bl_idname = "fn_MeshEmitterNode"
bl_label = "Mesh Emitter"
execute_on_birth__prop: NodeBuilder.ExecuteInputProperty()
density_mode: EnumProperty(
name="Density Mode",
items=[
('UNIFORM', "Uniform", "", 'NONE', 0),
('VERTEX_WEIGHTS', "Vertex Weights", "", 'NONE', 1),
],
update=SimulationNode.sync_tree,
)
def declaration(self, builder: NodeBuilder):
builder.fixed_input("object", "Object", "Object")
builder.fixed_input("rate", "Rate", "Float", default=10)
if self.density_mode == 'VERTEX_WEIGHTS':
builder.fixed_input("density_vertex_group", "Density Group", "Text")
builder.execute_input("execute_on_birth", "Execute on Birth", "execute_on_birth__prop")
builder.influences_output("emitter", "Emitter")
def draw(self, layout):
layout.prop(self, "density_mode")

View File

@@ -1,15 +0,0 @@
import bpy
from .. base import SimulationNode
from .. node_builder import NodeBuilder
class ParticleSystemNode(bpy.types.Node, SimulationNode):
bl_idname = "fn_ParticleSystemNode"
bl_label = "Particle System"
def declaration(self, builder: NodeBuilder):
builder.background_color((0.8, 0.5, 0.4))
builder.influences_input("influences", "Influences")
def draw(self, layout):
layout.prop(self, "name", text="", icon="PHYSICS")

View File

@@ -1,17 +0,0 @@
import bpy
from bpy.props import *
from .. base import SimulationNode
from .. node_builder import NodeBuilder
class PointEmitterNode(bpy.types.Node, SimulationNode):
bl_idname = "fn_PointEmitterNode"
bl_label = "Point Emitter"
execute_on_birth__prop: NodeBuilder.ExecuteInputProperty()
def declaration(self, builder: NodeBuilder):
builder.fixed_input("position", "Position", "Vector")
builder.fixed_input("velocity", "Velocity", "Vector", default=(1, 0, 0))
builder.fixed_input("size", "Size", "Float", default=0.01)
builder.execute_input("execute_on_birth", "Execute on Birth", "execute_on_birth__prop")
builder.influences_output("emitter", "Emitter")

View File

@@ -1,13 +0,0 @@
import bpy
from bpy.props import *
from .. base import SimulationNode
from .. node_builder import NodeBuilder
class SizeOverTimeNode(bpy.types.Node, SimulationNode):
bl_idname = "fn_SizeOverTimeNode"
bl_label = "Size Over Time"
def declaration(self, builder: NodeBuilder):
builder.fixed_input("final_size", "Final Size", "Float", default=0.0)
builder.fixed_input("final_age", "Final Age", "Float", default=3)
builder.influences_output("influence", "Influence")

View File

@@ -1,16 +0,0 @@
import bpy
from bpy.props import *
from .. base import SimulationNode
from .. node_builder import NodeBuilder
class ParticleTrailsNode(bpy.types.Node, SimulationNode):
bl_idname = "fn_ParticleTrailsNode"
bl_label = "Particle Trails"
execute_on_birth__prop: NodeBuilder.ExecuteInputProperty()
def declaration(self, builder: NodeBuilder):
builder.fixed_input("rate", "Rate", "Float", default=20)
builder.execute_input("execute_on_birth", "Execute on Birth", "execute_on_birth__prop")
builder.influences_output("main_system", "Main System")
builder.influences_output("trail_system", "Trail System")

View File

@@ -1,12 +0,0 @@
from . base import NoDefaultValue
from . fixed_type import FixedSocketDecl
from . dynamic_list import ListSocketDecl
from . base_list_variadic import BaseListVariadic
from . vectorized import VectorizedInputDecl, VectorizedOutputDecl
from . bparticles import (
InfluencesSocketDecl,
ExecuteOutputDecl,
ExecuteInputDecl,
ExecuteInputListDecl,
)

View File

@@ -1,38 +0,0 @@
NoDefaultValue = object()
class SocketDeclBase:
def init(self):
pass
def build(self, node_sockets):
raise NotImplementedError()
def init_default(self, node_sockets):
pass
def amount(self):
raise NotImplementedError()
def validate(self, sockets):
raise NotImplementedError()
def draw_node(self, layout):
pass
def draw_socket(self, layout, socket, index):
socket.draw_self(layout, self, socket.name)
def operator_socket_call(self, own_socket, other_socket):
pass
def _data_socket_test(self, socket, name, data_type, identifier):
from .. base import DataSocket
if not isinstance(socket, DataSocket):
return False
if socket.name != name:
return False
if socket.data_type != data_type:
return False
if socket.identifier != identifier:
return False
return True

View File

@@ -1,152 +0,0 @@
import bpy
import uuid
from bpy.props import *
from . base import SocketDeclBase
from .. base import DataSocket
from .. types import type_infos
from .. sockets import OperatorSocket
class BaseListVariadic(SocketDeclBase):
def __init__(self, node, identifier: str, prop_name: str, base_type: str, default_amount: int):
self.node = node
self.identifier_suffix = identifier
self.prop_name = prop_name
self.base_type = base_type
self.list_type = type_infos.to_list(base_type)
self.default_amount = default_amount
def init(self):
collection = self.get_collection()
for _ in range(self.default_amount):
item = collection.add()
item.state = "BASE"
item.identifier = str(uuid.uuid4())
def build(self, node_sockets):
return list(self._build(node_sockets))
def _build(self, node_sockets):
for item in self.get_collection():
data_type = self.base_type if item.state == "BASE" else self.list_type
yield type_infos.build(
data_type,
node_sockets,
"",
item.identifier)
yield node_sockets.new("fn_OperatorSocket", "Operator")
def validate(self, sockets):
collection = self.get_collection()
if len(sockets) != len(collection) + 1:
return False
for socket, item in zip(sockets[:-1], collection):
data_type = self.base_type if item.state == "BASE" else self.list_type
if not self._data_socket_test(socket, "", data_type, item.identifier):
return False
if sockets[-1].bl_idname != "fn_OperatorSocket":
return False
return True
def draw_socket(self, layout, socket, index):
if isinstance(socket, OperatorSocket):
props = layout.operator("fn.new_base_list_variadic_input", text="New Input", icon='ADD')
props.tree_name = self.node.tree.name
props.node_name = self.node.name
props.prop_name = self.prop_name
else:
row = layout.row(align=True)
socket.draw_self(row, self.node, str(index))
props = row.operator("fn.remove_base_list_variadic_input", text="", icon='X')
props.tree_name = self.node.tree.name
props.node_name = self.node.name
props.prop_name = self.prop_name
props.index = index
def operator_socket_call(self, own_socket, linked_socket, connected_sockets):
if len(connected_sockets) != 1:
return
connected_socket = next(iter(connected_sockets))
if not isinstance(connected_socket, DataSocket):
return
is_output = own_socket.is_output
origin_data_type = connected_socket.data_type
if type_infos.is_link_allowed(origin_data_type, self.base_type):
state = "BASE"
elif type_infos.is_link_allowed(origin_data_type, self.list_type):
state = "LIST"
else:
return
collection = self.get_collection()
item = collection.add()
item.state = state
item.identifier = str(uuid.uuid4())
self.node.rebuild()
new_socket = self.node.find_socket(item.identifier, is_output)
self.node.tree.new_link(linked_socket, new_socket)
def amount(self):
return len(self.get_collection()) + 1
def get_collection(self):
return getattr(self.node, self.prop_name)
@classmethod
def Property(cls):
return CollectionProperty(type=BaseListVariadicPropertyGroup)
class BaseListVariadicPropertyGroup(bpy.types.PropertyGroup):
bl_idname = "fn_BaseListVariadicPropertyGroup"
state: EnumProperty(
default="BASE",
items=[
("BASE", "Base", "", "NONE", 0),
("LIST", "Base", "", "NONE", 1)])
identifier: StringProperty()
class NewBaseListVariadicInputOperator(bpy.types.Operator):
bl_idname = "fn.new_base_list_variadic_input"
bl_label = "New Pack List Input"
bl_options = {'INTERNAL'}
tree_name: StringProperty()
node_name: StringProperty()
prop_name: StringProperty()
def execute(self, context):
tree = bpy.data.node_groups[self.tree_name]
node = tree.nodes[self.node_name]
collection = getattr(node, self.prop_name)
item = collection.add()
item.state = "BASE"
item.identifier = str(uuid.uuid4())
tree.sync()
return {'FINISHED'}
class RemoveBaseListVariadicInputOperator(bpy.types.Operator):
bl_idname = "fn.remove_base_list_variadic_input"
bl_label = "Remove Pack List Input"
bl_options = {'INTERNAL'}
tree_name: StringProperty()
node_name: StringProperty()
prop_name: StringProperty()
index: IntProperty()
def execute(self, context):
tree = bpy.data.node_groups[self.tree_name]
node = tree.nodes[self.node_name]
collection = getattr(node, self.prop_name)
collection.remove(self.index)
tree.sync()
return {'FINISHED'}

View File

@@ -1,180 +0,0 @@
import bpy
import uuid
from bpy.props import *
from . base import SocketDeclBase
from .. sockets import OperatorSocket, ExecuteSocket
MAX_LINK_LIMIT = 4095
class InfluencesSocketDecl(SocketDeclBase):
def __init__(self, node, identifier: str, display_name: str):
self.node = node
self.identifier = identifier
self.display_name = display_name
def build(self, node_sockets):
socket = node_sockets.new("fn_InfluencesSocket", self.display_name, identifier=self.identifier)
socket.link_limit = MAX_LINK_LIMIT
socket.display_shape = 'DIAMOND'
return [socket]
def validate(self, sockets):
if len(sockets) != 1:
return False
socket = sockets[0]
if socket.bl_idname != "fn_InfluencesSocket":
return False
if socket.name != self.display_name:
return False
if socket.link_limit != MAX_LINK_LIMIT:
return False
return True
def amount(self):
return 1
class ExecuteOutputDecl(SocketDeclBase):
def __init__(self, node, identifier: str, display_name: str):
self.node = node
self.identifier = identifier
self.display_name = display_name
def build(self, node_sockets):
socket = node_sockets.new("fn_ExecuteSocket", self.display_name, identifier=self.identifier)
socket.display_shape = 'SQUARE'
return [socket]
def amount(self):
return 1
def validate(self, sockets):
if len(sockets) != 1:
return False
socket = sockets[0]
if socket.name != self.display_name:
return False
elif socket.identifier != self.identifier:
return False
elif socket.bl_idname != "fn_ExecuteSocket":
return False
return True
class ExecuteInputDecl(SocketDeclBase):
def __init__(self, node, identifier: str, display_name: str):
self.node = node
self.identifier = identifier
self.display_name = display_name
def build(self, node_sockets):
socket = node_sockets.new("fn_ExecuteSocket", self.display_name, identifier=self.identifier)
socket.display_shape = "SQUARE"
return [socket]
def amount(self):
return 1
def validate(self, sockets):
if len(sockets) != 1:
return False
if sockets[0].bl_idname != "fn_ExecuteSocket":
return False
return True
class ExecuteInputListDecl(SocketDeclBase):
def __init__(self, node, identifier: str, prop_name: str, display_name: str):
self.node = node
self.identifier = identifier
self.display_name = display_name
self.prop_name = prop_name
def build(self, node_sockets):
return list(self._build(node_sockets))
def _build(self, node_sockets):
items = self.get_items()
for i, item in enumerate(items):
socket = node_sockets.new(
"fn_ExecuteSocket",
self.display_name if i == 0 else "Then",
identifier=item.identifier)
socket.display_shape = 'SQUARE'
yield socket
socket = node_sockets.new(
"fn_OperatorSocket",
self.display_name)
socket.display_shape = 'SQUARE'
yield socket
def amount(self):
return len(self.get_items()) + 1
def get_items(self):
return getattr(self.node, self.prop_name)
def validate(self, sockets):
if len(sockets) != self.amount():
return False
for socket, item in zip(sockets[:-1], self.get_items()):
if socket.identifier != item.identifier:
return False
elif socket.bl_idname != "fn_ExecuteSocket":
return False
if not isinstance(sockets[-1], OperatorSocket):
return False
if not sockets[-1].name == self.display_name:
return False
return True
def draw_socket(self, layout, socket, index):
row = layout.row(align=True)
if index == 0:
row.label(text=self.display_name)
else:
row.label(text="Then")
if isinstance(socket, ExecuteSocket):
props = row.operator("fn.remove_execute_socket", text="", icon="X")
props.tree_name = socket.id_data.name
props.node_name = self.node.name
props.prop_name = self.prop_name
props.index = index
def operator_socket_call(self, own_socket, linked_socket, connected_sockets):
item = self.get_items().add()
item.identifier = str(uuid.uuid4())
self.node.rebuild()
new_socket = self.node.find_socket(item.identifier, False)
self.node.tree.new_link(linked_socket, new_socket)
@classmethod
def Property(cls):
return CollectionProperty(type=ExecuteInputItem)
class ExecuteInputItem(bpy.types.PropertyGroup):
identifier: StringProperty()
class RemoveExecuteSocketOperator(bpy.types.Operator):
bl_idname = "fn.remove_execute_socket"
bl_label = "Remove Execute Socket"
bl_options = {'INTERNAL'}
tree_name: StringProperty()
node_name: StringProperty()
prop_name: StringProperty()
index: IntProperty()
def execute(self, context):
tree = bpy.data.node_groups[self.tree_name]
node = tree.nodes[self.node_name]
collection = getattr(node, self.prop_name)
collection.remove(self.index)
tree.sync()
return {'FINISHED'}

View File

@@ -1,41 +0,0 @@
from bpy.props import *
from . base import SocketDeclBase
from .. types import type_infos
class ListSocketDecl(SocketDeclBase):
def __init__(self, node, identifier: str, display_name: str, prop_name: str, list_or_base: str):
self.node = node
self.identifier = identifier
self.display_name = display_name
self.prop_name = prop_name
self.list_or_base = list_or_base
def build(self, node_sockets):
data_type = self.get_data_type()
return [type_infos.build(
data_type,
node_sockets,
self.display_name,
self.identifier)]
def validate(self, sockets):
if len(sockets) != 1:
return False
return self._data_socket_test(sockets[0],
self.display_name, self.get_data_type(), self.identifier)
def get_data_type(self):
base_type = getattr(self.node, self.prop_name)
if self.list_or_base == "BASE":
return base_type
elif self.list_or_base == "LIST":
return type_infos.to_list(base_type)
else:
assert False
def amount(self):
return 1
@classmethod
def Property(cls):
return StringProperty(default="Float")

View File

@@ -1,38 +0,0 @@
from . base import SocketDeclBase, NoDefaultValue
from .. types import type_infos
class FixedSocketDecl(SocketDeclBase):
def __init__(self, node, identifier: str, display_name: str, data_type: str, default, socket_settings: dict):
self.node = node
self.identifier = identifier
self.display_name = display_name
self.data_type = data_type
self.default = default
self.socket_settings = socket_settings
def build(self, node_sockets):
socket = type_infos.build(self.data_type,
node_sockets,
self.display_name,
self.identifier)
for name, value in self.socket_settings.items():
setattr(socket, name, value)
return [socket]
def init_default(self, node_sockets):
if self.default is not NoDefaultValue:
socket = node_sockets[0]
socket.restore_state(self.default)
def validate(self, sockets):
if len(sockets) != 1:
return False
socket = sockets[0]
for name, value in self.socket_settings.items():
if getattr(socket, name) != value:
return False
return self._data_socket_test(sockets[0],
self.display_name, self.data_type, self.identifier)
def amount(self):
return 1

View File

@@ -1,94 +0,0 @@
import bpy
from bpy.props import *
from . base import SocketDeclBase, NoDefaultValue
from .. types import type_infos
from .. utils.generic import getattr_recursive
class VectorizedDeclBase:
def build(self, node_sockets):
data_type, name = self.get_type_and_name()
socket = type_infos.build(
data_type,
node_sockets,
name,
self.identifier)
for prop_name, value in self.socket_settings.items():
setattr(socket, prop_name, value)
return [socket]
def validate(self, sockets):
if len(sockets) != 1:
return False
socket = sockets[0]
for prop_name, value in self.socket_settings.items():
if getattr(socket, prop_name) != value:
return False
data_type, name = self.get_type_and_name()
return self._data_socket_test(socket,
name, data_type, self.identifier)
def amount(self):
return 1
def get_type_and_name(self):
if self.is_vectorized():
return self.list_type, self.list_name
else:
return self.base_type, self.base_name
class VectorizedInputDecl(VectorizedDeclBase, SocketDeclBase):
def __init__(self,
node, identifier, prop_name,
base_name, list_name,
base_type, default, socket_settings):
self.node = node
self.identifier = identifier
self.prop_name = prop_name
self.base_name = base_name
self.list_name = list_name
self.base_type = base_type
self.list_type = type_infos.to_list(base_type)
self.default = default
self.socket_settings = socket_settings
def init_default(self, node_sockets):
if self.default is not NoDefaultValue:
socket = node_sockets[0]
socket.restore_state(self.default)
def is_vectorized(self):
stored = getattr_recursive(self.node, self.prop_name)
if stored == "BASE":
return False
elif stored == "LIST":
return True
else:
assert False
@staticmethod
def Property():
return StringProperty(default="BASE")
class VectorizedOutputDecl(VectorizedDeclBase, SocketDeclBase):
def __init__(self,
node, identifier, input_prop_names,
base_name, list_name,
base_type, socket_settings):
self.node = node
self.identifier = identifier
self.input_prop_names = input_prop_names
self.base_name = base_name
self.list_name = list_name
self.base_type = base_type
self.list_type = type_infos.to_list(base_type)
self.socket_settings = socket_settings
def is_vectorized(self):
for prop_name in self.input_prop_names:
if getattr_recursive(self.node, prop_name) == "LIST":
return True
return False

View File

@@ -1,14 +0,0 @@
import bpy
from bpy.app.handlers import persistent
@persistent
def file_load_handler(dummy):
from . sync import sync_trees_and_dependent_trees
node_trees = set(tree for tree in bpy.data.node_groups if tree.bl_idname == "FunctionTree")
sync_trees_and_dependent_trees(node_trees)
def register():
bpy.app.handlers.load_post.append(file_load_handler)
def unregister():
bpy.app.handlers.load_post.remove(file_load_handler)

View File

@@ -1,58 +0,0 @@
import bpy
from bpy.props import *
from .. base import FunctionNode
from .. node_builder import NodeBuilder
class SeparateColorNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_SeparateColorNode"
bl_label = "Separate Color"
use_list__color: NodeBuilder.VectorizedProperty()
def declaration(self, builder: NodeBuilder):
builder.vectorized_input(
"color", "use_list__color",
"Color", "Colors", "Color")
builder.vectorized_output(
"red", ["use_list__color"],
"Red", "Red", "Float")
builder.vectorized_output(
"green", ["use_list__color"],
"Green", "Green", "Float")
builder.vectorized_output(
"blue", ["use_list__color"],
"Blue", "Blue", "Float")
builder.vectorized_output(
"alpha", ["use_list__color"],
"Alpha", "Alpha", "Float")
class CombineColorNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_CombineColorNode"
bl_label = "Combine Color"
use_list__red: NodeBuilder.VectorizedProperty()
use_list__green: NodeBuilder.VectorizedProperty()
use_list__blue: NodeBuilder.VectorizedProperty()
use_list__alpha: NodeBuilder.VectorizedProperty()
def declaration(self, builder: NodeBuilder):
builder.vectorized_input(
"red", "use_list__red",
"Red", "Red", "Float")
builder.vectorized_input(
"green", "use_list__green",
"Green", "Green", "Float")
builder.vectorized_input(
"blue", "use_list__blue",
"Blue", "Blue", "Float")
builder.vectorized_input(
"alpha", "use_list__alpha",
"Alpha", "Alpha", "Float",
default=1.0)
builder.vectorized_output(
"color", ["use_list__red", "use_list__green", "use_list__blue", "use_list__alpha"],
"Color", "Colors", "Color")

View File

@@ -1,31 +0,0 @@
import bpy
from bpy.props import *
from .. base import FunctionNode
from .. node_builder import NodeBuilder
class FloatRangeNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_FloatRangeNode"
bl_label = "Float Range"
mode: EnumProperty(
name="Mode",
items=[
("AMOUNT_START_STEP", "Amount / Start / Step", "", "NONE", 0),
("AMOUNT_START_STOP", "Amount / Start / Stop", "", "NONE", 1),
],
default="AMOUNT_START_STOP",
update=FunctionNode.sync_tree,
)
def declaration(self, builder: NodeBuilder):
builder.fixed_input("amount", "Amount", "Integer", default=10)
builder.fixed_input("start", "Start", "Float")
if self.mode == "AMOUNT_START_STEP":
builder.fixed_input("step", "Step", "Float")
elif self.mode == "AMOUNT_START_STOP":
builder.fixed_input("stop", "Stop", "Float", default=1)
builder.fixed_output("list", "List", "Float List")
def draw(self, layout):
layout.prop(self, "mode", text="")

View File

@@ -1,524 +0,0 @@
import bpy
from bpy.props import *
from .. types import type_infos
from .. base import BaseNode, FunctionNode, DataSocket
from .. function_tree import FunctionTree
from .. node_builder import NodeBuilder
from .. ui import NodeSidebarPanel
from .. utils.pie_menu_helper import PieMenuHelper
from .. sync import skip_syncing
interface_type_items = [
("DATA", "Data", "Some data type like integer or vector", "NONE", 0),
("EXECUTE", "Control Flow", "", "NONE", 1),
("INFLUENCES", "Influences", "", "NONE", 2),
]
class GroupInputNode(bpy.types.Node, BaseNode):
bl_idname = "fn_GroupInputNode"
bl_label = "Group Input"
input_name: StringProperty(
default="Name",
update=BaseNode.sync_tree,
)
sort_index: IntProperty()
display_settings: BoolProperty(
name="Display Settings",
default=False,
)
interface_type: EnumProperty(
items=interface_type_items,
default="DATA",
update= BaseNode.sync_tree,
)
data_type: StringProperty(
default="Float",
update=BaseNode.sync_tree,
)
def declaration(self, builder: NodeBuilder):
if self.interface_type == "DATA":
builder.fixed_output("value", "Value", self.data_type)
elif self.interface_type == "EXECUTE":
builder.execute_output("execute", "Execute")
elif self.interface_type == "INFLUENCES":
builder.influences_output("influences", "Influences")
else:
assert False
def draw(self, layout):
if not self.display_settings:
return
layout.prop(self, "interface_type", text="")
if self.interface_type == "DATA":
if hasattr(self.outputs[0], "draw_property"):
self.outputs[0].draw_property(layout, self, "Default")
self.invoke_type_selection(layout, "set_data_type", "Select Type")
def draw_socket(self, layout, socket, text, decl, index_in_decl):
row = layout.row(align=True)
row.prop(self, "input_name", text="")
row.prop(self, "display_settings", text="", icon="SETTINGS")
def draw_closed_label(self):
return self.input_name + " (Input)"
def set_data_type(self, data_type):
self.data_type = data_type
class GroupOutputNode(bpy.types.Node, BaseNode):
bl_idname = "fn_GroupOutputNode"
bl_label = "Group Output"
sort_index: IntProperty()
display_settings: BoolProperty(
name="Display Settings",
default=False,
)
output_name: StringProperty(
default="Name",
update=BaseNode.sync_tree,
)
interface_type: EnumProperty(
items=interface_type_items,
default="DATA",
update=BaseNode.sync_tree,
)
data_type: StringProperty(
default="Float",
update=BaseNode.sync_tree,
)
def declaration(self, builder: NodeBuilder):
if self.interface_type == "DATA":
builder.fixed_input("value", "Value", self.data_type)
elif self.interface_type == "EXECUTE":
builder.single_execute_input("execute", "Execute")
elif self.interface_type == "INFLUENCES":
builder.influences_input("influences", "Influences")
def draw(self, layout):
if not self.display_settings:
return
layout.prop(self, "interface_type", text="")
if self.interface_type == "DATA":
self.invoke_type_selection(layout, "set_type_type", "Select Type")
def draw_socket(self, layout, socket, text, decl, index_in_decl):
row = layout.row(align=True)
row.prop(self, "output_name", text="")
row.prop(self, "display_settings", text="", icon="SETTINGS")
def draw_closed_label(self):
return self.output_name + " (Output)"
def set_type_type(self, data_type):
self.data_type = data_type
class GroupNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_GroupNode"
bl_label = "Group"
bl_icon = "NODETREE"
node_group: PointerProperty(
type=bpy.types.NodeTree,
update=FunctionNode.sync_tree,
)
def declaration(self, builder: NodeBuilder):
if not isinstance(self.node_group, FunctionTree):
return
for input_node in self.node_group.get_input_nodes():
if input_node.interface_type == "DATA":
builder.fixed_input(
input_node.identifier,
input_node.input_name,
input_node.data_type,
default=input_node.outputs[0].get_state())
elif input_node.interface_type == "EXECUTE":
builder.single_execute_input(input_node.identifier, input_node.input_name)
elif input_node.interface_type == "INFLUENCES":
builder.influences_input(input_node.identifier, input_node.input_name)
else:
assert False
for output_node in self.node_group.get_output_nodes():
if output_node.interface_type == "DATA":
builder.fixed_output(
output_node.identifier,
output_node.output_name,
output_node.data_type)
elif output_node.interface_type == "EXECUTE":
builder.execute_output(output_node.identifier, output_node.output_name)
elif output_node.interface_type == "INFLUENCES":
builder.influences_output(output_node.identifier, output_node.output_name)
else:
assert False
def draw(self, layout):
layout.scale_y = 1.3
if self.node_group is None:
self.invoke_group_selector(layout, "set_group", "Select Group", icon="NODETREE")
elif not isinstance(self.node_group, FunctionTree):
layout.label(text="Group not found!", icon="ERROR")
self.invoke_group_selector(layout, "set_group", "Change Group", icon="NODETREE")
def draw_advanced(self, layout):
col = layout.column()
text = "Select Group" if self.node_group is None else self.node_group.name
col.scale_y = 1.3
self.invoke_group_selector(col, "set_group", text, icon="NODETREE")
def draw_label(self):
if self.node_group is None:
return "(G) -"
else:
return "(G) " + self.node_group.name
def set_group(self, group):
self.node_group = group
def iter_directly_used_trees(self):
if self.node_group is not None:
yield self.node_group
class GroupInterfacePanel(bpy.types.Panel, NodeSidebarPanel):
bl_idname = "FN_PT_group_interface_panel"
bl_label = "Group Interface"
@classmethod
def poll(self, context):
try: return isinstance(context.space_data.edit_tree, FunctionTree)
except: return False
def draw(self, context):
layout = self.layout
tree = context.space_data.edit_tree
draw_group_interface_panel(layout, tree)
def draw_group_interface_panel(layout, tree):
col = layout.column(align=True)
col.label(text="Inputs:")
box = col.box().column(align=True)
for i, node in enumerate(tree.get_input_nodes()):
row = box.row(align=True)
row.prop(node, "input_name", text="")
props = row.operator("fn.move_group_interface", text="", icon="TRIA_UP")
props.is_input = True
props.from_index = i
props.offset = -1
props = row.operator("fn.move_group_interface", text="", icon="TRIA_DOWN")
props.is_input = True
props.from_index = i
props.offset = 1
col = layout.column(align=True)
col.label(text="Outputs:")
box = col.box().column(align=True)
for i, node in enumerate(tree.get_output_nodes()):
row = box.row(align=True)
row.prop(node, "output_name", text="")
props = row.operator("fn.move_group_interface", text="", icon="TRIA_UP")
props.is_input = False
props.from_index = i
props.offset = -1
props = row.operator("fn.move_group_interface", text="", icon="TRIA_DOWN")
props.is_input = False
props.from_index = i
props.offset = 1
class MoveGroupInterface(bpy.types.Operator):
bl_idname = "fn.move_group_interface"
bl_label = "Move Group Interface"
is_input: BoolProperty()
from_index: IntProperty()
offset: IntProperty()
def execute(self, context):
tree = context.space_data.node_tree
if self.is_input:
nodes = tree.get_input_nodes()
else:
nodes = tree.get_output_nodes()
from_index = self.from_index
to_index = min(max(self.from_index + self.offset, 0), len(nodes) - 1)
nodes[from_index], nodes[to_index] = nodes[to_index], nodes[from_index]
with skip_syncing():
for i, node in enumerate(nodes):
node.sort_index = i
tree.sync()
return {"FINISHED"}
def update_sort_indices(tree):
for i, node in enumerate(tree.get_input_nodes()):
node.sort_index = i
for i, node in enumerate(tree.get_output_nodes()):
node.sort_index = i
class ManageGroupPieMenu(bpy.types.Menu, PieMenuHelper):
bl_idname = "FN_MT_manage_group_pie"
bl_label = "Manage Group"
@classmethod
def poll(cls, context):
try:
return isinstance(context.space_data.node_tree, FunctionTree)
except:
return False
def draw_top(self, layout):
layout.operator("fn.open_group_management_popup", text="Group Management")
def draw_left(self, layout):
node = bpy.context.active_node
if node is None:
self.empty(layout)
return
possible_inputs = [(i, socket) for i, socket in enumerate(node.inputs)
if socket_can_become_group_input(socket)]
if len(possible_inputs) == 0:
self.empty(layout, "No inputs.")
elif len(possible_inputs) == 1:
props = layout.operator("fn.create_group_input_for_socket", text="New Group Input")
props.input_index = possible_inputs[0][0]
else:
layout.operator("fn.create_group_input_for_socket_invoker", text="New Group Input")
def draw_right(self, layout):
node = bpy.context.active_node
if node is None:
self.empty(layout)
return
possible_outputs = [(i, socket) for i, socket in enumerate(node.outputs)
if socket_can_become_group_output(socket)]
if len(possible_outputs) == 0:
self.empty(layout, "No outputs.")
elif len(possible_outputs) == 1:
props = layout.operator("fn.create_group_output_for_socket", text="New Group Output")
props.output_index = possible_outputs[0][0]
else:
layout.operator("fn.create_group_output_for_socket_invoker", text="New Group Output")
class OpenGroupManagementPopup(bpy.types.Operator):
bl_idname = "fn.open_group_management_popup"
bl_label = "Group Management"
def invoke(self, context, event):
return context.window_manager.invoke_props_dialog(self)
def draw(self, context):
draw_group_interface_panel(self.layout, context.space_data.node_tree)
def execute(self, context):
return {"INTERFACE"}
class CreateGroupInputForSocketInvoker(bpy.types.Operator):
bl_idname = "fn.create_group_input_for_socket_invoker"
bl_label = "Create Group Input for Socket Invoker"
def invoke(self, context, event):
context.window_manager.popup_menu(self.draw_menu)
return {"CANCELLED"}
@staticmethod
def draw_menu(menu, context):
node = bpy.context.active_node
if node is None:
return
layout = menu.layout.column()
layout.operator_context = "INVOKE_DEFAULT"
for i, socket in enumerate(node.inputs):
if socket_can_become_group_input(socket):
props = layout.operator("fn.create_group_input_for_socket", text=socket.name)
props.input_index = i
class CreateGroupOutputForSocketInvoker(bpy.types.Operator):
bl_idname = "fn.create_group_output_for_socket_invoker"
bl_label = "Create Group Output for Socket Invoker"
def invoke(self, context, event):
context.window_manager.popup_menu(self.draw_menu)
return {"CANCELLED"}
@staticmethod
def draw_menu(menu, context):
node = bpy.context.active_node
if node is None:
return
layout = menu.layout.column()
layout.operator_context = "INVOKE_DEFAULT"
for i, socket in enumerate(node.outputs):
if socket_can_become_group_output(socket):
props = layout.operator("fn.create_group_output_for_socket", text=socket.name)
props.output_index = i
class CreateGroupInputForSocket(bpy.types.Operator):
bl_idname = "fn.create_group_input_for_socket"
bl_label = "Create Group Input for Socket"
input_index: IntProperty()
def invoke(self, context, event):
tree = context.space_data.node_tree
node = context.active_node
socket = node.inputs[self.input_index]
node.select = False
with skip_syncing():
new_node = tree.nodes.new(type="fn_GroupInputNode")
new_node.sort_index = 1000
new_node.input_name = socket.name
update_sort_indices(tree)
if isinstance(socket, DataSocket):
new_node.interface_type = "DATA"
new_node.data_type = socket.data_type
elif socket.bl_idname == "fn_ExecuteSocket":
new_node.interface_type = "EXECUTE"
elif socket.bl_idname == "fn_InfluencesSocket":
new_node.interface_type = "INFLUENCES"
new_node.rebuild()
new_node.select = True
new_node.parent = node.parent
new_node.location = node.location
new_node.location.x -= 200
new_node.outputs[0].restore_state(socket.get_state())
tree.new_link(new_node.outputs[0], socket)
tree.sync()
bpy.ops.node.translate_attach("INVOKE_DEFAULT")
return {"FINISHED"}
class CreateGroupOutputForSocket(bpy.types.Operator):
bl_idname = "fn.create_group_output_for_socket"
bl_label = "Create Group Output for Socket"
output_index: IntProperty()
def invoke(self, context, event):
tree = context.space_data.node_tree
node = context.active_node
socket = node.outputs[self.output_index]
node.select = False
with skip_syncing():
new_node = tree.nodes.new(type="fn_GroupOutputNode")
new_node.sort_index = 1000
update_sort_indices(tree)
new_node.output_name = socket.name
if isinstance(socket, DataSocket):
new_node.interface_type = "DATA"
new_node.data_type = socket.data_type
elif socket.bl_idname == "fn_ExecuteSocket":
new_node.interface_type = "EXECUTE"
elif socket.bl_idname == "fn_InfluencesSocket":
new_node.interface_type = "INFLUENCES"
new_node.rebuild()
new_node.select = True
new_node.parent = node.parent
new_node.location = node.location
new_node.location.x += 200
tree.new_link(new_node.inputs[0], socket)
tree.sync()
bpy.ops.node.translate_attach("INVOKE_DEFAULT")
return {"FINISHED"}
class OpenCloseGroupOperator(bpy.types.Operator):
bl_idname = "fn.open_close_group"
bl_label = "Open/Close Group"
bl_options = {"INTERNAL"}
@classmethod
def poll(cls, context):
try: return context.space_data.node_tree.bl_idname == "FunctionTree"
except: return False
def invoke(self, context, event):
space_data = context.space_data
active_node = context.active_node
if isinstance(active_node, GroupNode) and active_node.node_group is not None and active_node.select:
space_data.path.append(active_node.node_group, node=active_node)
else:
space_data.path.pop()
return {"FINISHED"}
def socket_can_become_group_input(socket):
return socket.bl_idname != "fn_OperatorSocket" and not socket.is_linked
def socket_can_become_group_output(socket):
return socket.bl_idname != "fn_OperatorSocket"
keymap = None
def register():
global keymap
if not bpy.app.background:
keymap = bpy.context.window_manager.keyconfigs.addon.keymaps.new(
name="Node Editor", space_type="NODE_EDITOR")
kmi = keymap.keymap_items.new("wm.call_menu_pie", type="V", value="PRESS")
kmi.properties.name = "FN_MT_manage_group_pie"
keymap.keymap_items.new("fn.open_close_group", type="TAB", value="PRESS")
def unregister():
global keymap
if not bpy.app.background:
bpy.context.window_manager.keyconfigs.addon.keymaps.remove(keymap)
keymap = None

View File

@@ -1,69 +0,0 @@
import bpy
from bpy.props import *
from .. types import type_infos
from .. base import FunctionNode
from .. node_builder import NodeBuilder
class GetListElementNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_GetListElementNode"
bl_label = "Get List Element"
active_type: NodeBuilder.DynamicListProperty()
def declaration(self, builder: NodeBuilder):
builder.dynamic_list_input("list", "List", "active_type")
builder.fixed_input("index", "Index", "Integer")
builder.dynamic_base_input("fallback", "Fallback", "active_type")
builder.dynamic_base_output("value", "Value", "active_type")
class GetListElementsNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_GetListElementsNode"
bl_label = "Get List Elements"
active_type: NodeBuilder.DynamicListProperty()
def declaration(self, builder: NodeBuilder):
builder.dynamic_list_input("list", "List", "active_type")
builder.fixed_input("indices", "Indices", "Integer List")
builder.dynamic_base_input("fallback", "Fallback", "active_type")
builder.dynamic_list_output("values", "Values", "active_type")
class ListLengthNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_ListLengthNode"
bl_label = "List Length"
active_type: NodeBuilder.DynamicListProperty()
def declaration(self, builder: NodeBuilder):
builder.dynamic_list_input("list", "List", "active_type")
builder.fixed_output("length", "Length", "Integer")
class PackListNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_PackListNode"
bl_label = "Pack List"
active_type: StringProperty(
default="Float",
update=FunctionNode.sync_tree)
variadic: NodeBuilder.BaseListVariadicProperty()
def declaration(self, builder):
builder.base_list_variadic_input("inputs", "variadic", self.active_type)
builder.fixed_output("output", "List", type_infos.to_list(self.active_type))
def draw_advanced(self, layout):
self.invoke_type_selection(layout, "set_type", "Change Type", mode="BASE")
def set_type(self, data_type):
self.active_type = data_type
@classmethod
def get_search_terms(cls):
for list_type in type_infos.iter_list_types():
base_type = type_infos.to_base(list_type)
yield ("Pack " + list_type, {"active_type" : base_type})

View File

@@ -1,129 +0,0 @@
import bpy
from bpy.props import *
from .. base import FunctionNode
from .. node_builder import NodeBuilder
def create_variadic_math_node(data_type, idname, label):
class MathNode(bpy.types.Node, FunctionNode):
bl_idname = idname
bl_label = label
variadic: NodeBuilder.BaseListVariadicProperty()
def declaration(self, builder: NodeBuilder):
builder.base_list_variadic_input("inputs", "variadic", data_type)
if NodeBuilder.BaseListVariadicPropertyHasList(self.variadic):
builder.fixed_output("result", "Result", data_type + " List")
else:
builder.fixed_output("result", "Result", data_type)
return MathNode
def create_single_type_two_inputs_math_node(data_type, idname, label):
return create_two_inputs_math_node(data_type, data_type, data_type, idname, label)
def create_two_inputs_math_node(input_type1, input_type2, output_type, idname, label):
class MathNode(bpy.types.Node, FunctionNode):
bl_idname = idname
bl_label = label
use_list__a: NodeBuilder.VectorizedProperty()
use_list__b: NodeBuilder.VectorizedProperty()
def declaration(self, builder: NodeBuilder):
builder.vectorized_input("a", "use_list__a", "A", "A", input_type1)
builder.vectorized_input("b", "use_list__b", "B", "B", input_type2)
builder.vectorized_output("result", ["use_list__a", "use_list__b"], "Result", "Result", output_type)
return MathNode
def create_single_input_math_node(input_type, output_type, idname, label):
class MathNode(bpy.types.Node, FunctionNode):
bl_idname = idname
bl_label = label
use_list: NodeBuilder.VectorizedProperty()
def declaration(self, builder: NodeBuilder):
builder.vectorized_input("input", "use_list", "Value", "Values", input_type)
builder.vectorized_output("output", ["use_list"], "Result", "Result", output_type)
return MathNode
AddFloatsNode = create_variadic_math_node("Float", "fn_AddFloatsNode", "Add Floats")
MultiplyFloatsNode = create_variadic_math_node("Float", "fn_MultiplyFloatsNode", "Multiply Floats")
MinimumFloatsNode = create_variadic_math_node("Float", "fn_MinimumFloatsNode", "Minimum Floats")
MaximumFloatsNode = create_variadic_math_node("Float", "fn_MaximumFloatsNode", "Maximum Floats")
SubtractFloatsNode = create_single_type_two_inputs_math_node("Float", "fn_SubtractFloatsNode", "Subtract Floats")
DivideFloatsNode = create_single_type_two_inputs_math_node("Float", "fn_DivideFloatsNode", "Divide Floats")
PowerFloatsNode = create_single_type_two_inputs_math_node("Float", "fn_PowerFloatsNode", "Power Floats")
SqrtFloatNode = create_single_input_math_node("Float", "Float", "fn_SqrtFloatNode", "Sqrt Float")
AbsFloatNode = create_single_input_math_node("Float", "Float", "fn_AbsoluteFloatNode", "Absolute Float")
SineFloatNode = create_single_input_math_node("Float", "Float", "fn_SineFloatNode", "Sine")
CosineFloatNode = create_single_input_math_node("Float", "Float", "fn_CosineFloatNode", "Cosine")
CeilFloatNode = create_single_input_math_node("Float", "Float", "fn_CeilFloatNode", "Ceil Float")
FloorFloatNode = create_single_input_math_node("Float", "Float", "fn_FloorFloatNode", "Floor Float")
AddVectorsNode = create_variadic_math_node("Vector", "fn_AddVectorsNode", "Add Vectors")
SubtractVectorsNode = create_single_type_two_inputs_math_node("Vector", "fn_SubtractVectorsNode", "Subtract Vectors")
MultiplyVectorsNode = create_variadic_math_node("Vector", "fn_MultiplyVectorsNode", "Multiply Vectors")
DivideVectorsNode = create_single_type_two_inputs_math_node("Vector", "fn_DivideVectorsNode", "Divide Vectors")
MultiplyVectorWithFloatNode = create_two_inputs_math_node("Vector", "Float", "Vector", "fn_MultiplyVectorWithFloatNode", "Multiply Vector with Float")
VectorCrossProductNode = create_single_type_two_inputs_math_node("Vector", "fn_VectorCrossProductNode", "Cross Product")
VectorReflectNode = create_single_type_two_inputs_math_node("Vector", "fn_ReflectVectorNode", "Reflect Vector")
VectorProjectNode = create_single_type_two_inputs_math_node("Vector", "fn_ProjectVectorNode", "Project Vector")
VectorDotProductNode = create_two_inputs_math_node("Vector", "Vector", "Float", "fn_VectorDotProductNode", "Dot Product")
VectorDistanceNode = create_two_inputs_math_node("Vector", "Vector", "Float", "fn_VectorDistanceNode", "Vector Distance")
NormalizeVectorNode = create_single_input_math_node("Vector", "Vector", "fn_NormalizeVectorNode", "Normalize Vector")
VectorLengthNode = create_single_input_math_node("Vector", "Float", "fn_VectorLengthNode", "Vector Length")
BooleanAndNode = create_variadic_math_node("Boolean", "fn_BooleanAndNode", "And")
BooleanOrNode = create_variadic_math_node("Boolean", "fn_BooleanOrNode", "Or")
BooleanNotNode = create_single_input_math_node("Boolean", "Boolean", "fn_BooleanNotNode", "Not")
LessThanFloatNode = create_two_inputs_math_node("Float", "Float", "Boolean", "fn_LessThanFloatNode", "Less Than Float")
GreaterThanFloatNode = create_two_inputs_math_node("Float", "Float", "Boolean", "fn_GreaterThanFloatNode", "Greater Than Float")
class MapRangeNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_MapRangeNode"
bl_label = "Map Range"
clamp: BoolProperty(
name="Clamp",
default=True,
)
use_list__value: NodeBuilder.VectorizedProperty()
use_list__from_min: NodeBuilder.VectorizedProperty()
use_list__from_max: NodeBuilder.VectorizedProperty()
use_list__to_min: NodeBuilder.VectorizedProperty()
use_list__to_max: NodeBuilder.VectorizedProperty()
def declaration(self, builder: NodeBuilder):
builder.vectorized_input("value", "use_list__value", "Value", "Values", "Float")
builder.vectorized_input("from_min", "use_list__from_min", "From Min", "From Min", "Float")
builder.vectorized_input("from_max", "use_list__from_max", "From Max", "From Max", "Float")
builder.vectorized_input("to_min", "use_list__to_min", "To Min", "To Min", "Float")
builder.vectorized_input("to_max", "use_list__to_max", "To Max", "To Max", "Float")
builder.vectorized_output("value", [
"use_list__value", "use_list__from_min", "use_list__from_max",
"use_list__to_min", "use_list__to_max"], "Value", "Values", "Float")
def draw(self, layout):
layout.prop(self, "clamp")
class FloatClampNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_FloatClampNode"
bl_label = "Clamp"
def declaration(self, builder: NodeBuilder):
builder.fixed_input("value", "Value", "Float")
builder.fixed_input("min", "Min", "Float", default=0)
builder.fixed_input("max", "Max", "Float", default=1)
builder.fixed_output("value", "Value", "Float")

View File

@@ -1,12 +0,0 @@
import bpy
from bpy.props import *
from .. base import FunctionNode
from .. node_builder import NodeBuilder
class NodeInstanceIdentifierNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_NodeInstanceIdentifierNode"
bl_label = "Node Instance Identifier"
def declaration(self, builder):
builder.fixed_output("identifier", "Identifier", "Text")

View File

@@ -1,143 +0,0 @@
import bpy
import random
from bpy.props import *
from .. base import FunctionNode
from .. node_builder import NodeBuilder
class PerlinNoiseNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_PerlinNoiseNode"
bl_label = "Perlin Noise"
def declaration(self, builder: NodeBuilder):
builder.fixed_input("position", "Position", "Vector")
builder.fixed_input("amplitude", "Amplitude", "Float", default=1)
builder.fixed_input("scale", "Scale", "Float", default=1)
builder.fixed_output("noise_1d", "Noise", "Float")
builder.fixed_output("noise_3d", "Noise", "Vector")
class RandomFloatNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_RandomFloatNode"
bl_label = "Random Float"
node_seed: IntProperty(
name="Node Seed",
)
def init_props(self):
self.node_seed = new_node_seed()
def declaration(self, builder: NodeBuilder):
builder.fixed_input("min", "Min", "Float", default=0)
builder.fixed_input("max", "Max", "Float", default=1)
builder.fixed_input("seed", "Seed", "Integer")
builder.fixed_output("value", "Value", "Float")
def draw_advanced(self, layout):
layout.prop(self, "node_seed")
def duplicate(self, src_node):
self.node_seed = new_node_seed()
class RandomFloatsNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_RandomFloatsNode"
bl_label = "Random Floats"
node_seed: IntProperty(
name="Node Seed",
)
def init_props(self):
self.node_seed = new_node_seed()
def declaration(self, builder: NodeBuilder):
builder.fixed_input("amount", "Amount", "Integer", default=10)
builder.fixed_input("min", "Min", "Float")
builder.fixed_input("max", "Max", "Float", default=1)
builder.fixed_input("seed", "Seed", "Integer")
builder.fixed_output("values", "Values", "Float List")
def draw_advanced(self, layout):
layout.prop(self, "node_seed")
def duplicate(self, src_node):
self.node_seed = new_node_seed()
random_vector_mode_items = [
("UNIFORM_IN_CUBE", "Uniform in Cube", "Generate a vector that is somewhere in the volume of a cube", "NONE", 0),
("UNIFORM_ON_SPHERE", "Uniform on Sphere", "Generate a vector that is somewhere on the surface of a sphere", 1),
("UNIFORM_IN_SPHERE", "Uniform in Sphere", "Generate a vector that is somewhere in the volume of a sphere", 2),
]
class RandomVectorNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_RandomVectorNode"
bl_label = "Random Vector"
node_seed: IntProperty(
name="Node Seed",
)
mode: EnumProperty(
name="Mode",
items=random_vector_mode_items,
default="UNIFORM_IN_CUBE",
)
use_list__factor: NodeBuilder.VectorizedProperty()
use_list__seed: NodeBuilder.VectorizedProperty()
def init_props(self):
self.node_seed = new_node_seed()
def declaration(self, builder: NodeBuilder):
builder.vectorized_input("factor", "use_list__factor", "Factor", "Factors", "Vector", default=(1, 1, 1))
builder.vectorized_input("seed", "use_list__seed", "Seed", "Seeds", "Integer")
builder.vectorized_output("vector", ["use_list__factor", "use_list__seed"], "Vector", "Vectors", "Vector")
def draw(self, layout):
layout.prop(self, "mode", text="")
def draw_advanced(self, layout):
layout.prop(self, "node_seed")
def duplicate(self, src_node):
self.node_seed = new_node_seed()
class RandomVectorsNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_RandomVectorsNode"
bl_label = "Random Vectors"
node_seed: IntProperty(
name="Node Seed",
)
mode: EnumProperty(
name="Mode",
items=random_vector_mode_items,
default="UNIFORM_IN_CUBE",
)
def init_props(self):
self.node_seed = new_node_seed()
def declaration(self, builder: NodeBuilder):
builder.fixed_input("amount", "Amount", "Integer", default=10)
builder.fixed_input("factor", "Factor", "Vector", default=(1, 1, 1))
builder.fixed_input("seed", "Seed", "Integer")
builder.fixed_output("vectors", "Vectors", "Vector List")
def draw(self, layout):
layout.prop(self, "mode", text="")
def draw_advanced(self, layout):
layout.prop(self, "node_seed")
def duplicate(self, src_node):
self.node_seed = new_node_seed()
def new_node_seed():
return random.randint(0, 10000)

View File

@@ -1,113 +0,0 @@
import bpy
from bpy.props import *
from .. base import FunctionNode
from .. node_builder import NodeBuilder
class ObjectMeshNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_ObjectMeshNode"
bl_label = "Object Mesh"
def declaration(self, builder):
builder.fixed_input("object", "Object", "Object", display_name=False)
builder.fixed_output("vertex_locations", "Vertex Locations", "Vector List")
class VertexInfo(bpy.types.Node, FunctionNode):
bl_idname = "fn_VertexInfoNode"
bl_label = "Vertex Info"
def declaration(self, builder):
builder.fixed_output("position", "Position", "Vector")
class ClosestLocationOnObjectNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_ClosestLocationOnObjectNode"
bl_label = "Closest Location on Object"
use_list__object: NodeBuilder.VectorizedProperty()
use_list__position: NodeBuilder.VectorizedProperty()
def declaration(self, builder: NodeBuilder):
builder.vectorized_input("object", "use_list__object", "Object", "Objects", "Object", display_name=False)
builder.vectorized_input("position", "use_list__position", "Position", "Positions", "Vector")
vectorize_props = ["use_list__object", "use_list__position"]
builder.vectorized_output("closest_hook", vectorize_props, "Closest Hook", "Closest Hooks", "Surface Hook")
builder.vectorized_output("closest_position", vectorize_props, "Closest Position", "Closest Positions", "Vector")
builder.vectorized_output("closest_normal", vectorize_props, "Closest Normal", "Closest Normals", "Vector")
class GetPositionOnSurfaceNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_GetPositionOnSurfaceNode"
bl_label = "Get Position on Surface"
use_list__surface_hook: NodeBuilder.VectorizedProperty()
def declaration(self, builder: NodeBuilder):
builder.vectorized_input("surface_hook", "use_list__surface_hook", "Surface Hook", "Surface Hooks", "Surface Hook")
builder.vectorized_output("position", ["use_list__surface_hook"], "Position", "Positions", "Vector")
class GetNormalOnSurfaceNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_GetNormalOnSurfaceNode"
bl_label = "Get Normal on Surface"
use_list__surface_hook: NodeBuilder.VectorizedProperty()
def declaration(self, builder):
builder.vectorized_input("surface_hook", "use_list__surface_hook", "Surface Hook", "Surface Hooks", "Surface Hook")
builder.vectorized_output("normal", ["use_list__surface_hook"], "Normal", "Normals", "Vector")
class GetWeightOnSurfaceNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_GetWeightOnSurfaceNode"
bl_label = "Get Weight on Surface"
use_list__surface_hook: NodeBuilder.VectorizedProperty()
use_list__vertex_group_name: NodeBuilder.VectorizedProperty()
def declaration(self, builder: NodeBuilder):
builder.vectorized_input("surface_hook", "use_list__surface_hook", "Surface Hook", "Surface Hooks", "Surface Hook")
builder.vectorized_input("vertex_group_name", "use_list__vertex_group_name", "Name", "Names", "Text",
default="Group", display_name=False, display_icon="GROUP_VERTEX")
builder.vectorized_output("weight", ["use_list__surface_hook", "use_list__vertex_group_name"], "Weight", "Weights", "Float")
class GetImageColorOnSurfaceNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_GetImageColorOnSurfaceNode"
bl_label = "Get Image Color on Surface"
use_list__surface_hook: NodeBuilder.VectorizedProperty()
use_list__image: NodeBuilder.VectorizedProperty()
def declaration(self, builder: NodeBuilder):
builder.vectorized_input("surface_hook", "use_list__surface_hook", "Surface Hook", "Surface Hooks", "Surface Hook")
builder.vectorized_input("image", "use_list__image", "Image", "Images", "Image", display_name=False)
builder.vectorized_output("color", ["use_list__surface_hook", "use_list__image"], "Color", "Colors", "Color")
class SampleObjectSurfaceNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_SampleObjectSurfaceNode"
bl_label = "Sample Object Surface"
weight_mode: EnumProperty(
name="Weight Mode",
items=[
("UNIFORM", "Uniform", "", "NONE", 0),
("VERTEX_WEIGHTS", "Vertex Weights", "", "NONE", 1),
],
default="UNIFORM",
update=FunctionNode.sync_tree,
)
def declaration(self, builder: NodeBuilder):
builder.fixed_input("object", "Object", "Object", display_name=False)
builder.fixed_input("amount", "Amount", "Integer", default=10)
builder.fixed_input("seed", "Seed", "Integer")
if self.weight_mode == "VERTEX_WEIGHTS":
builder.fixed_input("vertex_group_name", "Vertex Group", "Text", default="Group")
builder.fixed_output("surface_hooks", "Surface Hooks", "Surface Hook List")
def draw(self, layout):
layout.prop(self, "weight_mode", text="")

View File

@@ -1,11 +0,0 @@
import bpy
from bpy.props import *
from .. base import FunctionNode
class ObjectTransformsNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_ObjectTransformsNode"
bl_label = "Object Transforms"
def declaration(self, builder):
builder.fixed_input("object", "Object", "Object")
builder.fixed_output("location", "Location", "Vector")

View File

@@ -1,79 +0,0 @@
import bpy
import uuid
from bpy.props import *
from .. base import FunctionNode
from .. node_builder import NodeBuilder
class SwitchNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_SwitchNode"
bl_label = "Switch"
data_type: StringProperty(
default="Float",
update=FunctionNode.sync_tree
)
def declaration(self, builder: NodeBuilder):
builder.fixed_input("condition", "Condition", "Boolean")
builder.fixed_input("true", "True", self.data_type)
builder.fixed_input("false", "False", self.data_type)
builder.fixed_output("result", "Result", self.data_type)
def draw(self, layout):
self.invoke_type_selection(layout, "set_type", "Change Type")
def set_type(self, data_type):
self.data_type = data_type
class SelectNodeItem(bpy.types.PropertyGroup):
identifier: StringProperty()
class SelectNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_SelectNode"
bl_label = "Select"
data_type: StringProperty(
default="Float",
update=FunctionNode.sync_tree,
)
input_items: CollectionProperty(
type=SelectNodeItem,
)
def init_props(self):
self.add_input()
self.add_input()
def declaration(self, builder: NodeBuilder):
builder.fixed_input("select", "Select", "Integer")
for i, item in enumerate(self.input_items):
builder.fixed_input(item.identifier, str(i), self.data_type)
builder.fixed_input("fallback", "Fallback", self.data_type)
builder.fixed_output("result", "Result", self.data_type)
def draw(self, layout):
self.invoke_type_selection(layout, "set_type", "Change Type")
self.invoke_function(layout, "add_input", "Add Input")
def draw_socket(self, layout, socket, text, decl, index_in_decl):
if len(socket.name) <= 3:
index = int(socket.name)
row = layout.row(align=True)
decl.draw_socket(row, socket, index_in_decl)
self.invoke_function(row, "remove_input", "", icon="X", settings=(index, ))
else:
decl.draw_socket(layout, socket, index_in_decl)
def set_type(self, data_type):
self.data_type = data_type
def add_input(self):
item = self.input_items.add()
item.identifier = str(uuid.uuid4())
self.sync_tree()
def remove_input(self, index):
self.input_items.remove(index)
self.sync_tree()

View File

@@ -1,20 +0,0 @@
import bpy
from .. node_builder import NodeBuilder
from .. base import FunctionNode
class TextLengthNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_TextLengthNode"
bl_label = "Text Length"
def declaration(self, builder: NodeBuilder):
builder.fixed_input("text", "Text", "Text")
builder.fixed_output("length", "Length", "Integer")
class JoinTextListNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_JoinTextListNode"
bl_label = "Join Text List"
def declaration(self, builder: NodeBuilder):
builder.fixed_input("texts", "Texts", "Text List")
builder.fixed_output("text", "Text", "Text")

View File

@@ -1,10 +0,0 @@
import bpy
from .. node_builder import NodeBuilder
from .. base import FunctionNode
class TimeInfoNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_TimeInfoNode"
bl_label = "Time Info"
def declaration(self, builder: NodeBuilder):
builder.fixed_output("frame", "Frame", "Float")

View File

@@ -1,29 +0,0 @@
import bpy
from bpy.props import *
from .. base import FunctionNode
from .. node_builder import NodeBuilder
class ValueNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_ValueNode"
bl_label = "Value"
data_type: StringProperty(
name="Data Type",
default="Float",
update=FunctionNode.sync_tree,
)
def declaration(self, builder: NodeBuilder):
builder.fixed_output("value", "Value", self.data_type)
def draw_socket(self, layout, socket, text, decl, index_in_decl):
row = layout.row(align=True)
if hasattr(socket, "draw_property"):
socket.draw_property(row, self, "")
else:
row.label(text=text)
self.invoke_type_selection(row, "set_type", text="", icon="SETTINGS")
def set_type(self, data_type):
self.data_type = data_type

View File

@@ -1,71 +0,0 @@
import bpy
from bpy.props import *
from .. base import FunctionNode
from .. node_builder import NodeBuilder
class VectorFromValueNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_VectorFromValueNode"
bl_label = "Vector from Value"
use_list__value: NodeBuilder.VectorizedProperty()
def declaration(self, builder: NodeBuilder):
builder.vectorized_input("value", "use_list__value", "Value", "Values", "Float")
builder.vectorized_output("vector", ["use_list__value"], "Vector", "Vectors", "Vector")
class CombineVectorNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_CombineVectorNode"
bl_label = "Combine Vector"
use_list__x: NodeBuilder.VectorizedProperty()
use_list__y: NodeBuilder.VectorizedProperty()
use_list__z: NodeBuilder.VectorizedProperty()
def declaration(self, builder):
builder.vectorized_input(
"x", "use_list__x",
"X", "X", "Float")
builder.vectorized_input(
"y", "use_list__y",
"Y", "Y", "Float")
builder.vectorized_input(
"z", "use_list__z",
"Z", "Z", "Float")
builder.vectorized_output(
"vector", ["use_list__x", "use_list__y", "use_list__z"],
"Vector", "Vectors", "Vector")
class SeparateVectorNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_SeparateVectorNode"
bl_label = "Separate Vector"
use_list__vector: NodeBuilder.VectorizedProperty()
def declaration(self, builder: NodeBuilder):
builder.vectorized_input(
"vector", "use_list__vector",
"Vector", "Vectors", "Vector")
builder.vectorized_output(
"x", ["use_list__vector"],
"X", "X", "Float")
builder.vectorized_output(
"y", ["use_list__vector"],
"Y", "Y", "Float")
builder.vectorized_output(
"z", ["use_list__vector"],
"Z", "Z", "Float")
class FindNonClosePointsNode(bpy.types.Node, FunctionNode):
bl_idname = "fn_FindNonClosePointsNode"
bl_label = "Find Non Close Point"
def declaration(self, builder: NodeBuilder):
builder.fixed_input("points", "Points", "Vector List")
builder.fixed_input("min_distance", "Min Distance", "Float", default=0.1)
builder.fixed_output("indices", "Indices", "Integer List")

View File

@@ -1,57 +0,0 @@
import bpy
from collections import namedtuple
from . base import BaseTree, BaseNode
from . graph import DirectedGraphBuilder, DirectedGraph
class FunctionTree(bpy.types.NodeTree, BaseTree):
bl_idname = "FunctionTree"
bl_icon = "MOD_DATA_TRANSFER"
bl_label = "Function Nodes"
def get_input_nodes(self):
input_nodes = [node for node in self.nodes if node.bl_idname == "fn_GroupInputNode"]
sorted_input_nodes = sorted(input_nodes, key=lambda node: (node.sort_index, node.name))
return sorted_input_nodes
def get_output_nodes(self):
output_nodes = [node for node in self.nodes if node.bl_idname == "fn_GroupOutputNode"]
sorted_output_nodes = sorted(output_nodes, key=lambda node: (node.sort_index, node.name))
return sorted_output_nodes
def get_directly_used_trees(self):
trees = set()
for node in self.nodes:
if isinstance(node, BaseNode):
trees.update(node.iter_directly_used_trees())
return trees
def find_callable_trees(self):
used_by_trees = FunctionTree.BuildInvertedCallGraph().reachable(self)
trees = [tree for tree in bpy.data.node_groups
if isinstance(tree, FunctionTree) and tree not in used_by_trees]
return trees
@staticmethod
def BuildTreeCallGraph() -> DirectedGraph:
'''
Every vertex is a tree.
Every edge (A, B) means: Tree A uses tree B.
'''
builder = DirectedGraphBuilder()
for tree in bpy.data.node_groups:
if isinstance(tree, FunctionTree):
builder.add_vertex(tree)
for dependency_tree in tree.get_directly_used_trees():
builder.add_directed_edge(
from_v=tree,
to_v=dependency_tree)
return builder.build()
@staticmethod
def BuildInvertedCallGraph() -> DirectedGraph:
'''
Builds a directed graph in which every tree is a vertex.
Every edge (A, B) means: Changes in A might affect B.
'''
return FunctionTree.BuildTreeCallGraph().inverted()

View File

@@ -1,87 +0,0 @@
from collections import namedtuple, defaultdict
class DirectedGraph:
def __init__(self, V, E):
assert all(isinstance(e, tuple) for e in E)
assert all(v1 in V and v2 in V for v1, v2 in E)
self.V = set(V)
self.E = set(E)
self.outgoing = defaultdict(set)
self.incoming = defaultdict(set)
self.neighbors = defaultdict(set)
for v1, v2 in E:
self.outgoing[v1].add(v2)
self.incoming[v2].add(v1)
self.neighbors[v1].add(v2)
self.neighbors[v2].add(v1)
def inverted(self):
return DirectedGraph(self.V, [(v2, v1) for v1, v2 in self.E])
def reachable(self, start_verts):
return self._reachable(start_verts, self.outgoing)
def reachable_inversed(self, start_verts):
return self._reachable(start_verts, self.incoming)
def connected(self, start_verts):
return self._reachable(start_verts, self.neighbors)
def _reachable(self, start_verts, next_map):
if start_verts in self.V:
start_verts = (start_verts, )
assert all(v in self.V for v in start_verts)
verts_to_check = set(start_verts)
found_verts = set()
while len(verts_to_check) > 0:
v = verts_to_check.pop()
found_verts.add(v)
for prev_v in next_map[v]:
if prev_v not in found_verts:
verts_to_check.add(prev_v)
return found_verts
def toposort(self):
return self.toposort_partial(self.V)
def toposort_partial(self, verts_to_sort):
verts_to_sort = set(verts_to_sort)
sorted_verts = list()
temp_marked_verts = set()
finished_verts = set()
def visit(v):
if v in finished_verts:
return
if v in temp_marked_verts:
raise Exception("not a DAG")
temp_marked_verts.add(v)
for prev_v in self.incoming[v]:
visit(prev_v)
finished_verts.add(v)
if v in verts_to_sort:
sorted_verts.append(v)
for v in verts_to_sort:
visit(v)
return tuple(sorted_verts)
class DirectedGraphBuilder:
def __init__(self):
self.V = set()
self.E = set()
def add_vertex(self, v):
self.V.add(v)
def add_directed_edge(self, from_v, to_v):
self.V.add(from_v)
self.V.add(to_v)
self.E.add((from_v, to_v))
def build(self):
return DirectedGraph(self.V, self.E)

View File

@@ -1,291 +0,0 @@
from collections import namedtuple, defaultdict
from . utils.graph import iter_connected_components
from . types import type_infos
from . tree_data import TreeData
from . declaration import (
FixedSocketDecl,
ListSocketDecl,
BaseListVariadic,
VectorizedInputDecl,
VectorizedOutputDecl,
)
DecisionID = namedtuple("DecisionID", ("node", "prop_name"))
def get_inferencing_decisions(tree_data: TreeData):
list_decisions = make_list_decisions(tree_data)
vector_decisions = make_vector_decisions(tree_data, list_decisions)
base_list_variadic_decisions = make_base_list_variadic_decisions(tree_data, list_decisions, vector_decisions)
decisions = dict()
decisions.update(list_decisions)
decisions.update(vector_decisions)
decisions.update(base_list_variadic_decisions)
return decisions
# Inference list type decisions
#################################################
def make_list_decisions(tree_data):
decision_users = get_list_decision_ids_with_users(tree_data)
decision_links = get_list_decision_links(tree_data)
decisions = dict()
for component in iter_connected_components(decision_users.keys(), decision_links):
possible_types = set(iter_possible_list_component_types(
component, decision_users, tree_data))
if len(possible_types) == 1:
base_type = next(iter(possible_types))
for decision_id in component:
decisions[decision_id] = base_type
return decisions
def get_list_decision_ids_with_users(tree_data):
decision_users = defaultdict(lambda: {"BASE": [], "LIST": []})
for node in tree_data.iter_nodes():
for decl, sockets in node.decl_map.iter_decl_with_sockets():
if isinstance(decl, ListSocketDecl):
decision_id = DecisionID(node, decl.prop_name)
decision_users[decision_id][decl.list_or_base].append(sockets[0])
return decision_users
def get_list_decision_links(tree_data):
linked_decisions = defaultdict(set)
for from_socket, to_socket in tree_data.iter_connections():
from_node = tree_data.get_node(from_socket)
to_node = tree_data.get_node(to_socket)
from_decl = from_socket.get_decl(from_node)
to_decl = to_socket.get_decl(to_node)
if isinstance(from_decl, ListSocketDecl) and isinstance(to_decl, ListSocketDecl):
if from_decl.list_or_base == to_decl.list_or_base:
from_decision_id = DecisionID(from_node, from_decl.prop_name)
to_decision_id = DecisionID(to_node, to_decl.prop_name)
linked_decisions[from_decision_id].add(to_decision_id)
linked_decisions[to_decision_id].add(from_decision_id)
return linked_decisions
def iter_possible_list_component_types(component, decision_users, tree_data):
for decision_id in component:
for socket in decision_users[decision_id]["LIST"]:
for other_node, other_socket in tree_data.iter_connected_sockets_with_nodes(socket):
other_decl = other_socket.get_decl(other_node)
if data_sockets_are_static(other_decl):
data_type = other_socket.data_type
if type_infos.is_list(data_type):
yield type_infos.to_base(data_type)
elif isinstance(other_decl, BaseListVariadic):
yield other_decl.base_type
elif isinstance(other_decl, VectorizedInputDecl):
yield other_decl.base_type
elif isinstance(other_decl, VectorizedOutputDecl):
yield other_decl.base_type
for socket in decision_users[decision_id]["BASE"]:
for other_node, other_socket in tree_data.iter_connected_sockets_with_nodes(socket):
other_decl = other_socket.get_decl(other_node)
if data_sockets_are_static(other_decl):
data_type = other_socket.data_type
if type_infos.is_base(data_type):
yield data_type
elif isinstance(other_decl, BaseListVariadic):
yield other_decl.base_type
elif isinstance(other_decl, VectorizedInputDecl):
yield other_decl.base_type
elif isinstance(other_decl, VectorizedOutputDecl):
yield other_decl.base_type
# Inference vectorization decisions
########################################
def make_vector_decisions(tree_data, list_decisions):
graph, input_sockets, output_sockets = get_vector_decisions_graph(tree_data)
decisions = dict()
decision_ids_with_collision = set()
for initial_decision_id, decision in iter_obligatory_vector_decisions(graph, input_sockets, output_sockets, tree_data, list_decisions):
for decision_id in graph.reachable(initial_decision_id):
if decision_id in decisions:
if decisions[decision_id] != decision:
decision_ids_with_collision.add(decision_id)
else:
decisions[decision_id] = decision
for decision_id in graph.V:
decisions.setdefault(decision_id, "BASE")
while len(decision_ids_with_collision) > 0:
collision_decision_id = decision_ids_with_collision.pop()
connected_decision_ids = graph.connected(collision_decision_id)
for decision_id in connected_decision_ids:
decisions.pop(decision_id, None)
decision_ids_with_collision.discard(decision_id)
return decisions
def get_vector_decisions_graph(tree_data):
'''
Builds a directed graph.
Vertices in that graph are decision IDs.
A directed edge (A, B) means: If A is a list, then B has to be a list.
'''
from . graph import DirectedGraphBuilder
builder = DirectedGraphBuilder()
input_sockets = set()
output_sockets = set()
for node in tree_data.iter_nodes():
for decl, sockets in node.decl_map.iter_decl_with_sockets():
if isinstance(decl, VectorizedInputDecl):
decision_id = DecisionID(node, decl.prop_name)
builder.add_vertex(decision_id)
input_sockets.add(sockets[0])
elif isinstance(decl, VectorizedOutputDecl):
output_sockets.add(sockets[0])
for from_socket, to_socket in tree_data.iter_connections():
from_node = tree_data.get_node(from_socket)
to_node = tree_data.get_node(to_socket)
from_decl = from_socket.get_decl(from_node)
to_decl = to_socket.get_decl(to_node)
if isinstance(from_decl, VectorizedOutputDecl) and isinstance(to_decl, VectorizedInputDecl):
for prop_name in from_decl.input_prop_names:
from_decision_id = DecisionID(from_node, prop_name)
to_decision_id = DecisionID(to_node, to_decl.prop_name)
builder.add_directed_edge(from_decision_id, to_decision_id)
return builder.build(), input_sockets, output_sockets
def iter_obligatory_vector_decisions(graph, input_sockets, output_sockets, tree_data, list_decisions):
for socket in input_sockets:
other_node, other_socket = tree_data.try_get_origin_with_node(socket)
if other_node is None:
continue
node = tree_data.get_node(socket)
decl = socket.get_decl(node)
decision_id = DecisionID(node, decl.prop_name)
other_decl = other_socket.get_decl(other_node)
if data_sockets_are_static(other_decl):
other_data_type = other_socket.data_type
if type_infos.is_list(other_data_type) and type_infos.is_link_allowed(other_data_type, decl.list_type):
yield decision_id, "LIST"
elif isinstance(other_decl, ListSocketDecl):
if other_decl.list_or_base == "LIST":
list_decision_id = DecisionID(other_node, other_decl.prop_name)
if list_decision_id in list_decisions:
other_base_type = list_decisions[list_decision_id]
if type_infos.is_link_allowed(other_base_type, decl.base_type):
yield decision_id, "LIST"
else:
old_data_type = other_socket.data_type
if type_infos.is_link_allowed(old_data_type, decl.list_type):
yield decision_id, "LIST"
for socket in output_sockets:
node = tree_data.get_node(socket)
decl = socket.get_decl(node)
decision_ids = [DecisionID(node, p) for p in decl.input_prop_names]
for other_node, other_socket in tree_data.iter_connected_sockets_with_nodes(socket):
other_decl = other_socket.get_decl(other_node)
if data_sockets_are_static(other_decl):
other_data_type = other_socket.data_type
if type_infos.is_base(other_data_type) and type_infos.is_link_allowed(other_data_type, decl.base_type):
for decision_id in decision_ids:
yield decision_id, "BASE"
elif isinstance(other_decl, ListSocketDecl):
if other_decl.list_or_base == "BASE":
list_decision_id = DecisionID(other_node, other_decl.prop_name)
if list_decision_id in list_decisions:
other_base_type = list_decisions[list_decision_id]
if type_infos.is_link_allowed(decl.base_type, other_base_type):
for decision_id in decision_ids:
yield decision_id, "BASE"
else:
old_data_type = other_socket.data_type
if type_infos.is_link_allowed(decl.base_type, old_data_type):
for decision_id in decision_ids:
yield decision_id, "BASE"
# Inference pack list decisions
########################################
def make_base_list_variadic_decisions(tree_data, list_decisions, vector_decisions):
decisions = dict()
for decision_id, decl, socket in iter_base_list_variadic_sockets(tree_data):
assert not socket.is_output
origin_node, origin_socket = tree_data.try_get_origin_with_node(socket)
if origin_socket is None:
decisions[decision_id] = "BASE"
continue
origin_decl = origin_socket.get_decl(origin_node)
if data_sockets_are_static(origin_decl):
data_type = origin_socket.data_type
if type_infos.is_link_allowed(data_type, decl.base_type):
decisions[decision_id] = "BASE"
elif type_infos.is_link_allowed(data_type, decl.list_type):
decisions[decision_id] = "LIST"
else:
decisions[decision_id] = "BASE"
elif isinstance(origin_decl, ListSocketDecl):
list_decision_id = DecisionID(origin_node, origin_decl.prop_name)
if list_decision_id in list_decisions:
other_base_type = list_decisions[list_decision_id]
if type_infos.is_link_allowed(other_base_type, decl.base_type):
decisions[decision_id] = origin_decl.list_or_base
else:
decisions[decision_id] = "BASE"
else:
old_origin_type = origin_socket.data_type
if type_infos.is_link_allowed(old_origin_type, decl.base_type):
decisions[decision_id] = "BASE"
elif type_infos.is_link_allowed(old_origin_type, decl.list_type):
decisions[decision_id] = "LIST"
else:
decisions[decision_id] = "BASE"
elif isinstance(origin_decl, VectorizedOutputDecl):
other_base_type = origin_decl.base_type
if type_infos.is_link_allowed(other_base_type, decl.base_type):
for input_prop_name in origin_decl.input_prop_names:
input_decision_id = DecisionID(origin_node, input_prop_name)
if input_decision_id in vector_decisions:
if vector_decisions[input_decision_id] == "LIST":
decisions[decision_id] = "LIST"
break
else:
decisions[decision_id] = "BASE"
else:
decisions[decision_id] = "BASE"
else:
decisions[decision_id] = "BASE"
return decisions
def data_sockets_are_static(decl):
return isinstance(decl, FixedSocketDecl)
def iter_base_list_variadic_sockets(tree_data):
for node in tree_data.iter_nodes():
for decl, sockets in node.decl_map.iter_decl_with_sockets():
if isinstance(decl, BaseListVariadic):
collection = decl.get_collection()
for i, socket in enumerate(sockets[:-1]):
decision_id = DecisionID(node, f"{decl.prop_name}[{i}].state")
yield decision_id, decl, socket

View File

@@ -1,16 +0,0 @@
import bpy
def register():
wm = bpy.context.window_manager
if wm.keyconfigs.addon is None:
return
km = wm.keyconfigs.addon.keymaps.new(
name="Node Editor",
space_type='NODE_EDITOR')
km.keymap_items.new(
"fn.node_search",
type='A',
value='PRESS',
ctrl=True)

View File

@@ -1,95 +0,0 @@
import bpy
from . function_tree import FunctionTree
def draw_menu(self, context):
tree = context.space_data.node_tree
if not isinstance(tree, FunctionTree):
return
layout = self.layout
layout.operator_context = 'INVOKE_DEFAULT'
layout.operator("fn.node_search", text="Search", icon='VIEWZOOM')
layout.separator()
layout.menu("FN_MT_function_nodes_menu", text="Function Nodes")
layout.separator()
insert_node(layout, "fn_ParticleSystemNode", "Particle System")
layout.menu("BP_MT_influences_nodes_menu", text="Influences")
layout.menu("BP_MT_action_nodes_menu", text="Actions")
class FunctionNodesMenu(bpy.types.Menu):
bl_idname = "FN_MT_function_nodes_menu"
bl_label = "Function Nodes Menu"
def draw(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_DEFAULT'
insert_node(layout, "fn_SwitchNode", "Switch")
layout.separator()
insert_node(layout, "fn_FloatRangeNode", "Float Range")
layout.separator()
insert_node(layout, "fn_CombineVectorNode", "Combine Vector")
insert_node(layout, "fn_SeparateVectorNode", "Separate Vector")
insert_node(layout, "fn_VectorDistanceNode", "Vector Distance")
layout.separator()
insert_node(layout, "fn_SeparateColorNode", "Separate Color")
insert_node(layout, "fn_CombineColorNode", "Combine Color")
layout.separator()
insert_node(layout, "fn_GetListElementNode", "Get List Element")
insert_node(layout, "fn_ListLengthNode", "List Length")
insert_node(layout, "fn_PackListNode", "Pack List")
layout.separator()
insert_node(layout, "fn_ObjectMeshNode", "Object Mesh")
insert_node(layout, "fn_ObjectTransformsNode", "Object Transforms")
insert_node(layout, "fn_TextLengthNode", "Text Length")
class InfluencesNodesMenu(bpy.types.Menu):
bl_idname = "BP_MT_influences_nodes_menu"
bl_label = "Influences Nodes Menu"
def draw(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_DEFAULT'
insert_node(layout, "fn_CombineInfluencesNode", "Combine Influences")
layout.separator()
insert_node(layout, "fn_InitialGridEmitterNode", "Initial Grid Emitter")
insert_node(layout, "fn_MeshEmitterNode", "Mesh Emitter")
insert_node(layout, "fn_PointEmitterNode", "Point Emitter")
layout.separator()
insert_node(layout, "fn_AgeReachedEventNode", "Age Reached Event")
insert_node(layout, "fn_MeshCollisionEventNode", "Mesh Collision Event")
insert_node(layout, "fn_CustomEventNode", "Custom Event")
layout.separator()
insert_node(layout, "fn_ForceNode", "Force")
layout.separator()
insert_node(layout, "fn_SizeOverTimeNode", "Size Over Time")
insert_node(layout, "fn_ParticleTrailsNode", "Trails")
insert_node(layout, "fn_AlwaysExecuteNode", "Always Execute")
class ActionNodesMenu(bpy.types.Menu):
bl_idname = "BP_MT_action_nodes_menu"
bl_label = "Action Nodes Menu"
def draw(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_DEFAULT'
insert_node(layout, "fn_ParticleConditionNode", "Condition")
def insert_node(layout, type, text, settings = {}, icon = "NONE"):
operator = layout.operator("node.add_node", text = text, icon = icon)
operator.type = type
operator.use_transform = True
for name, value in settings.items():
item = operator.settings.add()
item.name = name
item.value = value
return operator
def register():
bpy.types.NODE_MT_add.append(draw_menu)

View File

@@ -1,240 +0,0 @@
from . declaration import (
FixedSocketDecl,
ListSocketDecl,
BaseListVariadic,
VectorizedInputDecl,
VectorizedOutputDecl,
InfluencesSocketDecl,
ExecuteOutputDecl,
ExecuteInputListDecl,
ExecuteInputDecl,
NoDefaultValue,
)
class NodeBuilder:
def __init__(self, node):
self.node = node
self.input_declarations = []
self.output_declarations = []
self._background_color = None
def _add_input(self, decl):
self.input_declarations.append(decl)
def _add_output(self, decl):
self.output_declarations.append(decl)
def initialize_decls(self):
for decl in self.input_declarations:
decl.init()
for decl in self.output_declarations:
decl.init()
def build(self):
from . sync import skip_syncing
with skip_syncing():
self.node.inputs.clear()
self.node.outputs.clear()
for decl in self.input_declarations:
sockets = decl.build(self.node.inputs)
assert len(sockets) == decl.amount()
decl.init_default(sockets)
for decl in self.output_declarations:
sockets = decl.build(self.node.outputs)
assert len(sockets) == decl.amount()
decl.init_default(sockets)
if self._background_color is not None:
self.node.use_custom_color = True
self.node.color = self._background_color
def get_sockets_decl_map(self):
return SocketDeclMap(
self.node,
self.input_declarations,
self.output_declarations)
def matches_sockets(self):
if not self._declarations_matches_sockets(self.input_declarations, self.node.inputs):
return False
if not self._declarations_matches_sockets(self.output_declarations, self.node.outputs):
return False
return True
def _declarations_matches_sockets(self, declarations, all_sockets):
sockets_iter = iter(all_sockets)
for decl in declarations:
amount = decl.amount()
try: sockets = [next(sockets_iter) for _ in range(amount)]
except StopIteration: return False
if not decl.validate(sockets):
return False
if len(tuple(sockets_iter)) > 0:
return False
return True
# General Node Properties
###################################
def background_color(self, color):
assert len(color) == 3
self._background_color = color
# Fixed Data Types
###################################
def fixed_input(self, identifier, name, data_type,
*, default=NoDefaultValue, **kwargs):
decl = FixedSocketDecl(self.node, identifier, name, data_type, default, kwargs)
self._add_input(decl)
def fixed_output(self, identifier, name, data_type,
*, default=NoDefaultValue, **kwargs):
decl = FixedSocketDecl(self.node, identifier, name, data_type, default, kwargs)
self._add_output(decl)
def fixed_pass_through(self, identifier, name, data_type, *, default=NoDefaultValue):
self.fixed_input(identifier, name, data_type, default=default)
self.fixed_output(identifier, name, data_type, default=default)
# Packed List
###################################
@staticmethod
def BaseListVariadicProperty():
return BaseListVariadic.Property()
@staticmethod
def BaseListVariadicPropertyHasList(prop):
return any(v.state == "LIST" for v in prop)
def base_list_variadic_input(self, identifier, prop_name, base_type, default_amount=2):
decl = BaseListVariadic(self.node, identifier, prop_name, base_type, default_amount)
self._add_input(decl)
# Dynamic List
###################################
@staticmethod
def DynamicListProperty():
return ListSocketDecl.Property()
def dynamic_list_input(self, identifier, name, prop_name):
decl = ListSocketDecl(self.node, identifier, name, prop_name, "LIST")
self._add_input(decl)
def dynamic_list_output(self, identifier, name, prop_name):
decl = ListSocketDecl(self.node, identifier, name, prop_name, "LIST")
self._add_output(decl)
def dynamic_base_input(self, identifier, name, prop_name):
decl = ListSocketDecl(self.node, identifier, name, prop_name, "BASE")
self._add_input(decl)
def dynamic_base_output(self, identifier, name, prop_name):
decl = ListSocketDecl(self.node, identifier, name, prop_name, "BASE")
self._add_output(decl)
# Vectorized
##################################
@staticmethod
def VectorizedProperty():
return VectorizedInputDecl.Property()
def vectorized_input(self, identifier, prop_name, base_name, list_name, base_type,
*, default=NoDefaultValue, **kwargs):
decl = VectorizedInputDecl(
self.node, identifier, prop_name,
base_name, list_name, base_type,
default, kwargs)
self._add_input(decl)
def vectorized_output(self, identifier, input_prop_names, base_name, list_name, base_type,
**kwargs):
decl = VectorizedOutputDecl(
self.node, identifier, input_prop_names,
base_name, list_name, base_type, kwargs)
self._add_output(decl)
# BParticles
###################################
def influences_input(self, identifier, name):
decl = InfluencesSocketDecl(self.node, identifier, name)
self._add_input(decl)
def influences_output(self, identifier, name):
decl = InfluencesSocketDecl(self.node, identifier, name)
self._add_output(decl)
@staticmethod
def ExecuteInputProperty():
return ExecuteInputListDecl.Property()
def execute_input(self, identifier, display_name, prop_name):
decl = ExecuteInputListDecl(self.node, identifier, prop_name, display_name)
self._add_input(decl)
def single_execute_input(self, identifier, name):
decl = ExecuteInputDecl(self.node, identifier, name)
self._add_input(decl)
def execute_output(self, identifier, name):
decl = ExecuteOutputDecl(self.node, identifier, name)
self._add_output(decl)
class SocketDeclMap:
def __init__(self, node, input_declarations, output_declarations):
self.node = node
self._sockets_by_decl = dict()
self._decl_by_socket = dict()
self._socket_index_in_decl = dict()
for decl, sockets in iter_sockets_by_decl(node.inputs, input_declarations):
self._sockets_by_decl[decl] = sockets
for i, socket in enumerate(sockets):
self._decl_by_socket[socket] = decl
self._socket_index_in_decl[socket] = i
for decl, sockets in iter_sockets_by_decl(node.outputs, output_declarations):
self._sockets_by_decl[decl] = sockets
for i, socket in enumerate(sockets):
self._decl_by_socket[socket] = decl
self._socket_index_in_decl[socket] = i
def get_decl_by_socket(self, socket):
return self._decl_by_socket[socket]
def get_socket_index_in_decl(self, socket):
return self._socket_index_in_decl[socket]
def get_sockets_by_decl(self, decl):
return self._sockets_by_decl[decl]
def iter_decl_with_sockets(self):
yield from self._sockets_by_decl.items()
def iter_decls(self):
yield from self._sockets_by_decl.keys()
def iter_sockets_by_decl(node_sockets, declarations):
node_sockets_iter = iter(node_sockets)
for decl in declarations:
amount = decl.amount()
sockets_of_decl = tuple(next(node_sockets_iter) for _ in range(amount))
assert decl.validate(sockets_of_decl)
yield decl, sockets_of_decl

View File

@@ -1,151 +0,0 @@
import bpy
from bpy.props import *
from . types import type_infos
from . function_tree import FunctionTree
def try_find_node(tree_name, node_name):
tree = bpy.data.node_groups.get(tree_name)
if tree is not None:
return tree.nodes.get(node_name)
return None
class NodeOperatorBase:
tree_name: StringProperty()
node_name: StringProperty()
function_name: StringProperty()
settings_repr: StringProperty()
def call(self, *args):
node = try_find_node(self.tree_name, self.node_name)
if node is None:
return {'CANCELLED'}
function = getattr(node, self.function_name)
settings = eval(self.settings_repr)
function(*args, *settings)
return {'FINISHED'}
class NodeOperator(bpy.types.Operator, NodeOperatorBase):
bl_idname = "fn.node_operator"
bl_label = "Generic Node Operator"
bl_options = {'INTERNAL'}
def execute(self, context):
return self.call()
class NodeDataTypeSelector(bpy.types.Operator, NodeOperatorBase):
bl_idname = "fn.node_data_type_selector"
bl_label = "Generic Node Data Type Selector"
bl_options = {'INTERNAL'}
bl_property = "item"
mode: EnumProperty(
items=[
("ALL", "All", ""),
("BASE", "Base", ""),
])
def get_items(self, context):
if self.mode == "ALL":
return type_infos.get_data_type_items()
elif self.mode == "BASE":
return type_infos.get_base_type_items()
else:
assert False
item: EnumProperty(items=get_items)
def invoke(self, context, event):
context.window_manager.invoke_search_popup(self)
return {'CANCELLED'}
def execute(self, context):
return self.call(self.item)
class NodeGroupSelector(bpy.types.Operator, NodeOperatorBase):
bl_idname = "fn.node_group_selector"
bl_label = "Node Group Selector"
bl_options = {'INTERNAL'}
bl_property = "item"
def get_items(self, context):
tree = bpy.data.node_groups.get(self.tree_name)
possible_trees = tree.find_callable_trees()
items = []
for tree in possible_trees:
items.append((tree.name, tree.name, ""))
items.append(("NONE", "None", ""))
return items
item: EnumProperty(items=get_items)
def invoke(self, context, event):
context.window_manager.invoke_search_popup(self)
return {'CANCELLED'}
def execute(self, context):
if self.item == "NONE":
return self.call(None)
else:
return self.call(bpy.data.node_groups.get(self.item))
class MoveViewToNode(bpy.types.Operator):
bl_idname = "fn.move_view_to_node"
bl_label = "Move View to Node"
bl_options = {'INTERNAL'}
tree_name: StringProperty()
node_name: StringProperty()
def execute(self, context):
target_node = try_find_node(self.tree_name, self.node_name)
if target_node is None:
return {'CANCELLED'}
tree = target_node.tree
context.space_data.node_tree = tree
for node in tree.nodes:
node.select = False
target_node.select = True
tree.nodes.active = target_node
bpy.ops.node.view_selected('INVOKE_DEFAULT')
return {'FINISHED'}
def new_function_tree(name, inputs, outputs):
tree = bpy.data.node_groups.new(name, "FunctionTree")
for i, (data_type, input_name) in enumerate(inputs):
input_node = tree.nodes.new("fn_GroupInputNode")
input_node.sort_index = i
input_node.interface_type = "DATA"
input_node.input_name = input_name
input_node.data_type = data_type
input_node.location = (-200, -i * 130)
for i, (data_type, output_name) in enumerate(outputs):
output_node = tree.nodes.new("fn_GroupOutputNode")
output_node.sort_index = i
output_node.output_name = output_name
output_node.data_type = data_type
output_node.location = (200, -i * 130)
tree.sync()
return tree
class NewParticleSystem(bpy.types.Operator):
bl_idname = "fn.new_particle_system"
bl_label = "New Particle System"
def execute(self, context):
mesh = bpy.data.meshes.new("Particle Simulation")
ob = bpy.data.objects.new("Particle Simulation", mesh)
modifier = ob.modifiers.new("BParticles", 'BPARTICLES')
bpy.ops.fn.new_particle_simulation_tree(object_name=ob.name, modifier_name=modifier.name)
context.collection.objects.link(ob)
return {'FINISHED'}

View File

@@ -1,34 +0,0 @@
import bpy
from bpy.props import *
from . ui import NodeSidebarPanel
warnings = []
def report_warning(node, msg):
warnings.append((node, msg))
class ProblemsPanel(bpy.types.Panel, NodeSidebarPanel):
bl_idname = "FN_PT_problems"
bl_label = "Problems"
def draw(self, context):
layout = self.layout
for i, (node, msg) in enumerate(warnings):
row = layout.row(align=True)
row.label(text=msg)
props = row.operator("fn.move_view_to_node", text="Find")
props.tree_name = node.tree.name
props.node_name = node.name
props = row.operator("fn.remove_warning", text="", icon='X')
props.index = i
class RemoveWarning(bpy.types.Operator):
bl_idname = "fn.remove_warning"
bl_label = "Remove Warning"
bl_options = {'INTERNAL'}
index: IntProperty()
def execute(self, context):
del warnings[self.index]
return {'FINISHED'}

View File

@@ -1,94 +0,0 @@
import os
import bpy
from bpy.props import *
from pathlib import Path
from . base import BaseNode
from . utils.enum_items_cache import cache_enum_items
from functools import lru_cache
@lru_cache()
def get_node_group_names_in_file(path: str):
with bpy.data.libraries.load(path) as (data_from, data_to):
return list(data_from.node_groups)
class NodeSearch(bpy.types.Operator):
bl_idname = "fn.node_search"
bl_label = "Node Search"
bl_options = {'REGISTER', 'UNDO'}
bl_property = "item"
def get_search_items(self, context):
items = []
tree = context.space_data.edit_tree
for node_cls in BaseNode.iter_final_subclasses():
for search_term, settings in node_cls.get_search_terms():
item = encode_search_item(("BUILTIN", node_cls.bl_idname, settings), search_term)
items.append(item)
current_tree = context.space_data.node_tree
for tree in current_tree.find_callable_trees():
item = encode_search_item(("EXISTING_GROUP", tree.name), tree.name + " (G)")
items.append(item)
nodelibdir = context.preferences.filepaths.nodelib_directory
if len(nodelibdir) > 0 and os.path.exists(nodelibdir):
local_group_names = set(tree.name for tree in bpy.data.node_groups)
for path in Path(nodelibdir).glob("**/*.blend"):
if not path.is_file():
continue
for group_name in get_node_group_names_in_file(str(path)):
if group_name not in local_group_names:
item = encode_search_item(("LIB_GROUP", str(path), group_name), group_name + " (G)")
items.append(item)
sorted_items = list(sorted(items, key=lambda item: item[1]))
return sorted_items
item: EnumProperty(items=cache_enum_items(get_search_items))
@classmethod
def poll(cls, context):
try: return context.space_data.node_tree.bl_idname == "FunctionTree"
except: return False
def invoke(self, context, event):
context.window_manager.invoke_search_popup(self)
return {'CANCELLED'}
def execute(self, context):
tree = context.space_data.node_tree
for node in tree.nodes:
node.select = False
item_data = decode_search_item(self.item)
item_type = item_data[0]
if item_type == "BUILTIN":
idname, settings = item_data[1:]
bpy.ops.node.add_node('INVOKE_DEFAULT', type=idname)
new_node = context.active_node
for key, value in settings.items():
setattr(new_node, key, value)
elif item_type == "EXISTING_GROUP":
group_name = item_data[1]
bpy.ops.node.add_node('INVOKE_DEFAULT', type="fn_GroupNode")
new_node = context.active_node
new_node.node_group = bpy.data.node_groups[group_name]
elif item_type == "LIB_GROUP":
path, group_name = item_data[1:]
bpy.ops.node.add_node('INVOKE_DEFAULT', type="fn_GroupNode")
new_node = context.active_node
with bpy.data.libraries.load(path, link=True) as (data_from, data_to):
data_to.node_groups = [group_name]
new_node.node_group = bpy.data.node_groups[group_name]
bpy.ops.node.translate_attach("INVOKE_DEFAULT")
return {'FINISHED'}
def encode_search_item(data, search_term):
identifier = repr(data)
return (identifier, search_term, "")
def decode_search_item(identifier):
return eval(identifier)

View File

@@ -1,228 +0,0 @@
import bpy
from . base import DataSocket, BaseSocket
from bpy.props import *
class OperatorSocket(bpy.types.NodeSocket, BaseSocket):
bl_idname = "fn_OperatorSocket"
bl_label = "Operator Socket"
def draw_color(self, context, node):
return (0, 0, 0, 0)
class FloatSocket(bpy.types.NodeSocket, DataSocket):
bl_idname = "fn_FloatSocket"
bl_label = "Float Socket"
data_type = "Float"
color = (0, 0.3, 0.5, 1)
value: FloatProperty(
name="Value",
default=0.0,
)
def draw_property(self, layout, node, text):
layout.prop(self, "value", text=text)
def get_state(self):
return self.value
def restore_state(self, state):
self.value = state
class IntegerSocket(bpy.types.NodeSocket, DataSocket):
bl_idname = "fn_IntegerSocket"
bl_label = "Integer Socket"
data_type = "Integer"
color = (0.3, 0.7, 0.5, 1)
value: IntProperty(
name="Value",
default=0,
)
def draw_property(self, layout, node, text):
layout.prop(self, "value", text=text)
def get_state(self):
return self.value
def restore_state(self, state):
self.value = state
class VectorSocket(bpy.types.NodeSocket, DataSocket):
bl_idname = "fn_VectorSocket"
bl_label = "Vector Socket"
data_type = "Vector"
color = (0, 0, 0.5, 1)
value: FloatVectorProperty(
name="Value",
size=3,
default=(0.0, 0.0, 0.0),
)
def draw_property(self, layout, node, text):
layout.column().prop(self, "value", text=text)
def get_state(self):
return tuple(self.value)
def restore_state(self, state):
self.value = state
class BooleanSocket(bpy.types.NodeSocket, DataSocket):
bl_idname = "fn_BooleanSocket"
bl_label = "Boolean Socket"
data_type = "Boolean"
color = (0.3, 0.3, 0.3, 1)
value: BoolProperty(
name="Value",
default=False,
)
def draw_property(self, layout, node, text):
layout.prop(self, "value", text=text)
def get_state(self):
return self.value
def restore_state(self, state):
self.value = state
class ObjectSocket(bpy.types.NodeSocket, DataSocket):
bl_idname = "fn_ObjectSocket"
bl_label = "Object Socket"
data_type = "Object"
color = (0, 0, 0, 1)
value: PointerProperty(
name="Value",
type=bpy.types.Object,
)
display_name: BoolProperty(default=True)
def draw_property(self, layout, node, text):
if not self.display_name:
text = ""
layout.prop(self, "value", text=text)
def get_state(self):
return self.value
def restore_state(self, state):
self.value = state
class ImageSocket(bpy.types.NodeSocket, DataSocket):
bl_idname = "fn_ImageSocket"
bl_label = "Image Socket"
data_type = "Image"
color = (0.6, 0.6, 0.6, 1)
value: PointerProperty(
name="Value",
type=bpy.types.Image,
)
display_name: BoolProperty()
def draw_property(self, layout, node, text):
if not self.display_name:
text = ""
layout.prop(self, "value", text=text)
def get_state(self):
return self.value
def restore_state(self, state):
self.value = state
class ColorSocket(bpy.types.NodeSocket, DataSocket):
bl_idname = "fn_ColorSocket"
bl_label = "Color Socket"
data_type = "Color"
color = (0.8, 0.8, 0.2, 1)
value: FloatVectorProperty(
name="Value",
size=4,
default=(0.8, 0.8, 0.8, 1.0),
subtype='COLOR',
soft_min=0.0,
soft_max=0.0,
)
def draw_property(self, layout, node, text):
layout.prop(self, "value", text=text)
def get_state(self):
return tuple(self.value)
def restore_state(self, state):
self.value = state
class TextSocket(bpy.types.NodeSocket, DataSocket):
bl_idname = "fn_TextSocket"
bl_label = "Text Socket"
data_type = "Text"
color = (0.8, 0.8, 0.8, 1)
value: StringProperty(
name="Value",
default="",
)
display_name: BoolProperty(default=True)
display_icon: StringProperty(default="NONE")
def draw_property(self, layout, node, text):
if not self.display_name:
text = ""
layout.prop(self, "value", text=text, icon=self.display_icon)
def get_state(self):
return self.value
def restore_state(self, state):
self.value = state
def create_simple_data_socket(idname, data_type, color):
return type(idname, (bpy.types.NodeSocket, DataSocket),
{
"bl_idname" : idname,
"bl_label" : idname,
"data_type" : data_type,
"color" : color,
})
FloatListSocket = create_simple_data_socket(
"fn_FloatListSocket", "Float List", (0, 0.3, 0.5, 0.5))
VectorListSocket = create_simple_data_socket(
"fn_VectorListSocket", "Vector List", (0, 0, 0.5, 0.5))
IntegerListSocket = create_simple_data_socket(
"fn_IntegerListSocket", "Integer List", (0.3, 0.7, 0.5, 0.5))
BooleanListSocket = create_simple_data_socket(
"fn_BooleanListSocket", "Boolean List", (0.3, 0.3, 0.3, 0.5))
ObjectListSocket = create_simple_data_socket(
"fn_ObjectListSocket", "Object List", (0, 0, 0, 0.5))
ImageListSocket = create_simple_data_socket(
"fn_ImageListSocket", "Image List", (0.6, 0.6, 0.6, 0.5))
ColorListSocket = create_simple_data_socket(
"fn_ColorListSocket", "Color List", (0.8, 0.8, 0.2, 0.5))
TextListSocket = create_simple_data_socket(
"fn_TextListSocket", "Text List", (0.8, 0.8, 0.8, 0.5))
SurfaceHookSocket = create_simple_data_socket(
"fn_SurfaceHookSocket", "Surface Hook", (0.2, 0.8, 0.2, 1.0))
SurfaceHookListSocket = create_simple_data_socket(
"fn_SurfaceHookListSocket", "Surface Hook List", (0.2, 0.8, 0.2, 0.5))
class ExecuteSocket(bpy.types.NodeSocket, BaseSocket):
bl_idname = "fn_ExecuteSocket"
bl_label = "Control Flow Socket"
color = (0.8, 0.2, 0.2, 1)
class InfluencesSocket(bpy.types.NodeSocket, BaseSocket):
bl_idname = "fn_InfluencesSocket"
bl_label = "Influences Socket"
color = (0.8, 0.8, 0.2, 1)

View File

@@ -1,183 +0,0 @@
import bpy
from pprint import pprint
from contextlib import contextmanager
from . base import BaseNode
from . tree_data import TreeData
from . graph import DirectedGraphBuilder
from . function_tree import FunctionTree
from . utils.generic import getattr_recursive, setattr_recursive
_is_syncing = False
def sync_trees_and_dependent_trees(trees):
global _is_syncing
if _is_syncing:
return
if _skip_syncing:
return
_is_syncing = True
try:
for tree in iter_trees_to_sync_in_order(trees):
sync_tree(tree)
finally:
_is_syncing = False
def sync_tree(tree):
rebuild_currently_outdated_nodes(tree)
tree_data = TreeData(tree)
tree_changed = run_socket_operators(tree_data)
if tree_changed: tree_data = TreeData(tree)
tree_changed = do_inferencing_and_update_nodes(tree_data)
if tree_changed: tree_data = TreeData(tree)
tree_changed = remove_invalid_links(tree_data)
# Sync skipping
######################################
_skip_syncing = False
@contextmanager
def skip_syncing():
global _skip_syncing
last_state = _skip_syncing
_skip_syncing = True
try:
yield
finally:
_skip_syncing = last_state
# Tree sync ordering
############################################
def iter_trees_to_sync_in_order(trees):
stored_tree_ids = {id(tree) for tree in bpy.data.node_groups}
if any(id(tree) not in stored_tree_ids for tree in trees):
# can happen after undo or on load
return
dependency_graph = FunctionTree.BuildInvertedCallGraph()
all_trees_to_sync = dependency_graph.reachable(trees)
trees_in_sync_order = dependency_graph.toposort_partial(all_trees_to_sync)
yield from trees_in_sync_order
# Rebuild already outdated nodes
############################################
def rebuild_currently_outdated_nodes(tree):
outdated_nodes = list(iter_nodes_with_outdated_sockets(tree))
rebuild_nodes_and_try_keep_state(outdated_nodes)
def iter_nodes_with_outdated_sockets(tree):
for node in tree.nodes:
if isinstance(node, BaseNode):
if not node_matches_current_declaration(node):
yield node
def node_matches_current_declaration(node):
from . node_builder import NodeBuilder
builder = node.get_node_builder()
return builder.matches_sockets()
# Socket Operators
############################################
def run_socket_operators(tree_data):
from . sockets import OperatorSocket
tree_changed = False
while True:
for link in tree_data.iter_blinks():
if isinstance(link.to_socket, OperatorSocket):
own_node = link.to_node
own_socket = link.to_socket
linked_socket = link.from_socket
connected_sockets = list(tree_data.iter_connected_origins(own_socket))
elif isinstance(link.from_socket, OperatorSocket):
own_node = link.from_node
own_socket = link.from_socket
linked_socket = link.to_socket
connected_sockets = list(tree_data.iter_connected_targets(own_socket))
else:
continue
tree_data.tree.links.remove(link)
decl = own_socket.get_decl(own_node)
decl.operator_socket_call(own_socket, linked_socket, connected_sockets)
tree_changed = True
else:
return tree_changed
# Inferencing
####################################
def do_inferencing_and_update_nodes(tree_data):
from . inferencing import get_inferencing_decisions
decisions = get_inferencing_decisions(tree_data)
nodes_to_rebuild = set()
for decision_id, value in decisions.items():
if getattr_recursive(decision_id.node, decision_id.prop_name) != value:
setattr_recursive(decision_id.node, decision_id.prop_name, value)
nodes_to_rebuild.add(decision_id.node)
rebuild_nodes_and_try_keep_state(nodes_to_rebuild)
tree_changed = len(nodes_to_rebuild) > 0
return tree_changed
# Remove Invalid Links
####################################
def remove_invalid_links(tree_data):
links_to_remove = set()
for from_socket, to_socket in tree_data.iter_connections():
if not is_link_valid(tree_data, from_socket, to_socket):
links_to_remove.update(tree_data.iter_incident_links(to_socket))
tree_changed = len(links_to_remove) > 0
tree = tree_data.tree
for link in links_to_remove:
tree.links.remove(link)
return tree_changed
def is_link_valid(tree_data, from_socket, to_socket):
from . types import type_infos
from . base import DataSocket
is_data_src = isinstance(from_socket, DataSocket)
is_data_dst = isinstance(to_socket, DataSocket)
if is_data_src != is_data_dst:
return False
if is_data_src and is_data_dst:
from_type = from_socket.data_type
to_type = to_socket.data_type
return type_infos.is_link_allowed(from_type, to_type)
return True
# Utils
######################################
def rebuild_nodes_and_try_keep_state(nodes):
for node in nodes:
node.rebuild()

View File

@@ -1,133 +0,0 @@
from collections import defaultdict
from . base import BaseNode
class TreeData:
def __init__(self, tree):
self.tree = tree
self.links_mapping = find_direct_links_mapping(tree)
self.node_by_socket = get_node_by_socket_mapping(tree)
self.connections_mapping = find_links_following_reroutes(self.links_mapping, self.node_by_socket)
self.link_by_sockets = get_link_by_sockets_mapping(tree)
def iter_nodes(self):
for node in self.tree.nodes:
if isinstance(node, BaseNode):
yield node
def iter_blinks(self):
yield from self.tree.links
def iter_connections(self):
for socket, others in self.connections_mapping.items():
if socket.is_output:
continue
for other in others:
yield other, socket
def get_node(self, socket):
return self.node_by_socket[socket]
def iter_connected_origins(self, socket):
node = self.get_node(socket)
if is_reroute(node):
socket = node.inputs[0]
for other_socket in self.links_mapping[socket]:
yield from self.iter_connected_origins(other_socket)
else:
if socket.is_output:
yield socket
else:
yield from self.iter_connected_sockets(socket)
def iter_connected_targets(self, socket):
node = self.get_node(socket)
if is_reroute(node):
socket = node.outputs[0]
for other_socket in self.links_mapping[socket]:
yield from self.iter_connected_targets(other_socket)
else:
if socket.is_output:
yield from self.iter_connected_sockets(socket)
else:
yield socket
def iter_connected_sockets(self, socket):
yield from self.connections_mapping[socket]
def iter_connected_sockets_with_nodes(self, socket):
for other_socket in self.iter_connected_sockets(socket):
other_node = self.get_node(other_socket)
yield other_node, other_socket
def try_get_origin_with_node(self, socket):
linked_sockets = self.connections_mapping[socket]
amount = len(linked_sockets)
if amount == 0:
return None, None
elif amount == 1:
origin_socket = next(iter(linked_sockets))
origin_node = self.get_node(origin_socket)
return origin_node, origin_socket
else:
assert False
def iter_incident_links(self, socket):
if socket.is_output:
for other_socket in self.links_mapping[socket]:
yield self.link_by_sockets[(socket, other_socket)]
else:
for other_socket in self.links_mapping[socket]:
yield self.link_by_sockets[(other_socket, socket)]
def find_direct_links_mapping(tree):
direct_links = defaultdict(set)
for link in tree.links:
direct_links[link.from_socket].add(link.to_socket)
direct_links[link.to_socket].add(link.from_socket)
return dict(direct_links)
def get_node_by_socket_mapping(tree):
node_by_socket = dict()
for node in tree.nodes:
for socket in node.inputs:
node_by_socket[socket] = node
for socket in node.outputs:
node_by_socket[socket] = node
return node_by_socket
def get_link_by_sockets_mapping(tree):
link_by_sockets = dict()
for link in tree.links:
link_by_sockets[(link.from_socket, link.to_socket)] = link
return link_by_sockets
def find_links_following_reroutes(direct_links, node_by_socket):
links = defaultdict(set)
for socket, direct_linked_sockets in direct_links.items():
node = node_by_socket[socket]
if socket.is_output:
# handle every link only once
continue
if is_reroute(node):
continue
for other_socket in direct_linked_sockets:
for origin_socket in iter_non_reroute_outputs(direct_links, node_by_socket, other_socket):
links[socket].add(origin_socket)
links[origin_socket].add(socket)
return links
def iter_non_reroute_outputs(direct_links, node_by_socket, socket):
assert socket.is_output
node = node_by_socket[socket]
if is_reroute(node):
input_socket = node.inputs[0]
if input_socket in direct_links:
for origin_socket in direct_links[input_socket]:
yield from iter_non_reroute_outputs(direct_links, node_by_socket, origin_socket)
else:
yield socket
def is_reroute(node):
return node.bl_idname == "NodeReroute"

View File

@@ -1,17 +0,0 @@
import bpy
from . ui import NodeSidebarPanel
from . base import BaseTree
class TreePanel(bpy.types.Panel, NodeSidebarPanel):
bl_idname = "FN_PT_tree_panel"
bl_label = "Tree"
@classmethod
def poll(self, context):
try: return isinstance(context.space_data.edit_tree, BaseTree)
except: return False
def draw(self, context):
layout = self.layout
tree = context.space_data.edit_tree

View File

@@ -1,16 +0,0 @@
from . import sockets as s
from . types_base import DataTypesInfo
type_infos = DataTypesInfo()
type_infos.insert_data_type(s.FloatSocket, s.FloatListSocket)
type_infos.insert_data_type(s.VectorSocket, s.VectorListSocket)
type_infos.insert_data_type(s.IntegerSocket, s.IntegerListSocket)
type_infos.insert_data_type(s.BooleanSocket, s.BooleanListSocket)
type_infos.insert_data_type(s.ObjectSocket, s.ObjectListSocket)
type_infos.insert_data_type(s.ImageSocket, s.ImageListSocket)
type_infos.insert_data_type(s.ColorSocket, s.ColorListSocket)
type_infos.insert_data_type(s.TextSocket, s.TextListSocket)
type_infos.insert_data_type(s.SurfaceHookSocket, s.SurfaceHookListSocket)
type_infos.insert_conversion_group(["Boolean", "Integer", "Float"])

View File

@@ -1,164 +0,0 @@
import itertools
from collections import namedtuple
'''
Type Rules
==========
A -> B means, Type A can be converted to type B implicitely.
A -!> B means, Type A cannot be converted to type B implicitely.
A_List is the type that contains a list of elements of type A
Iff T1 -> T2, then T1_List -> T2_List.
T -> T_List.
T_List -!> T.
Types always come in pairs: T and T_List.
There are no lists of lists.
A type can be in zero or one conversion group.
Every type in this group can be converted to any other implicitely.
The types within a group are ordered by their "rank".
When two types with different rank are used in one expression,
the type with lower rank is converted to the other.
'''
# Type Info Container
#####################################
ImplicitConversion = namedtuple("ImplicitConversion", ("from_type", "to_type"))
class DataTypesInfo:
def __init__(self):
self.data_types = set()
self.cls_by_data_type = dict()
self.list_by_base = dict()
self.base_by_list = dict()
self.unidirectional_conversions = set()
self.conversion_groups = dict()
self.all_implicit_conversions = set()
# Insert New Information
#############################
def insert_data_type(self, base_socket_cls, list_socket_cls):
base_type = base_socket_cls.data_type
list_type = list_socket_cls.data_type
assert base_type not in self.data_types
assert list_type not in self.data_types
self.data_types.add(base_type)
self.data_types.add(list_type)
self.list_by_base[base_type] = list_type
self.base_by_list[list_type] = base_type
self.cls_by_data_type[base_type] = base_socket_cls
self.cls_by_data_type[list_type] = list_socket_cls
self.all_implicit_conversions.add(ImplicitConversion(base_type, list_type))
def insert_conversion_group(self, types_by_rank):
'''lowest rank comes first'''
for data_type in types_by_rank:
assert self.is_data_type(data_type)
assert self.is_base(data_type)
assert data_type not in self.conversion_groups
group = tuple(types_by_rank)
for data_type in types_by_rank:
self.conversion_groups[data_type] = group
for from_base_type, to_base_type in itertools.combinations(group, 2):
from_list_type = self.to_list(from_base_type)
to_list_type = self.to_list(to_base_type)
self.all_implicit_conversions.add(ImplicitConversion(from_base_type, to_base_type))
self.all_implicit_conversions.add(ImplicitConversion(to_base_type, from_base_type))
self.all_implicit_conversions.add(ImplicitConversion(from_list_type, to_list_type))
self.all_implicit_conversions.add(ImplicitConversion(to_list_type, from_list_type))
def insert_unidirectional_conversion(self, from_type, to_type):
assert self.is_data_type(from_type)
assert self.is_data_type(to_type)
assert self.is_base(from_type)
assert self.is_base(to_type)
base_conversion = ImplicitConversion(from_type, to_type)
assert base_conversion not in self.implicit_conversions
self.implicit_conversions.add(base_conversion)
self.all_implicit_conversions.add(base_conversion)
list_conversion = ImplicitConversion(
self.to_list(from_type), self.to_list(to_type))
assert list_conversion not in self.implicit_conversions
self.implicit_conversions.add(list_conversion)
self.all_implicit_conversions.add(list_conversion)
# Query Information
##########################
def is_data_type(self, data_type):
return data_type in self.data_types
def is_base(self, data_type):
return data_type in self.list_by_base
def is_list(self, data_type):
return data_type in self.base_by_list
def to_list(self, data_type):
assert self.is_base(data_type)
return self.list_by_base[data_type]
def to_base(self, data_type):
assert self.is_list(data_type)
return self.base_by_list[data_type]
def get_data_type_items(self):
items = []
for data_type in self.data_types:
items.append((data_type, data_type, ""))
return items
def get_base_type_items(self):
items = []
for data_type in self.iter_base_types():
items.append((data_type, data_type, ""))
return items
def get_data_type_items_cb(self):
def callback(_1, _2):
return self.get_data_type_items()
return callback
def get_socket_color(self, data_type):
builder = self.to_builder(data_type)
return builder.get_color()
def is_link_allowed(self, from_type, to_type):
assert self.is_data_type(from_type)
assert self.is_data_type(to_type)
if from_type == to_type:
return True
else:
return self.is_implicitly_convertable(from_type, to_type)
def is_implicitly_convertable(self, from_type, to_type):
return ImplicitConversion(from_type, to_type) in self.all_implicit_conversions
def iter_list_types(self):
yield from self.base_by_list.keys()
def iter_base_types(self):
yield from self.list_by_base.keys()
# Build
##########################
def build(self, data_type, node_sockets, name, identifier):
idname = self.cls_by_data_type[data_type].bl_idname
socket = node_sockets.new(idname, name, identifier=identifier)
return socket

View File

@@ -1,6 +0,0 @@
import bpy
class NodeSidebarPanel:
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'UI'
bl_category = "Node"

View File

@@ -1,20 +0,0 @@
import functools
from collections import defaultdict
cached_item_tuples_by_hash = defaultdict(list)
def cache_enum_items(items_cb):
@functools.wraps(items_cb)
def wrapper(self, context):
item_tuples = tuple(items_cb(self, context))
item_tuples_hash = hash(item_tuples)
for cached_item_tuple in cached_item_tuples_by_hash[item_tuples_hash]:
if cached_item_tuple == item_tuples:
return cached_item_tuple
else:
cached_item_tuples_by_hash[item_tuples_hash].append(item_tuples)
return item_tuples
return wrapper

View File

@@ -1,21 +0,0 @@
import string
import random
def iter_subclasses_recursive(cls):
for sub in cls.__subclasses__():
yield sub
yield from iter_subclasses_recursive(sub)
def getattr_recursive(obj, name: str):
if "." not in name and "[" not in name:
return getattr(obj, name)
else:
# TODO: implement without eval
return eval("obj." + name, globals(), locals())
def setattr_recursive(obj, name: str, value):
if "." not in name and "[" not in name:
setattr(obj, name, value)
else:
# TODO: implement without exec
exec("obj." + name + " = value", globals(), locals())

View File

@@ -1,19 +0,0 @@
def iter_connected_components(nodes: set, links: dict):
nodes = set(nodes)
while len(nodes) > 0:
start_node = next(iter(nodes))
component = depth_first_search(start_node, links)
yield component
nodes -= component
def depth_first_search(start_node, links):
result = set()
found = set()
found.add(start_node)
while len(found) > 0:
node = found.pop()
result.add(node)
for linked_node in links[node]:
if linked_node not in result:
found.add(linked_node)
return result

View File

@@ -1,38 +0,0 @@
class PieMenuHelper:
def draw(self, context):
pie = self.layout.menu_pie()
self.draw_left(pie)
self.draw_right(pie)
self.draw_bottom(pie)
self.draw_top(pie)
self.draw_top_left(pie)
self.draw_top_right(pie)
self.draw_bottom_left(pie)
self.draw_bottom_right(pie)
def draw_left(self, layout):
self.empty(layout)
def draw_right(self, layout):
self.empty(layout)
def draw_bottom(self, layout):
self.empty(layout)
def draw_top(self, layout):
self.empty(layout)
def draw_top_left(self, layout):
self.empty(layout)
def draw_top_right(self, layout):
self.empty(layout)
def draw_bottom_left(self, layout):
self.empty(layout)
def draw_bottom_right(self, layout):
self.empty(layout)
def empty(self, layout, text=""):
layout.row().label(text=text)

View File

@@ -112,8 +112,6 @@ add_subdirectory(nodes)
add_subdirectory(modifiers) add_subdirectory(modifiers)
add_subdirectory(gpencil_modifiers) add_subdirectory(gpencil_modifiers)
add_subdirectory(shader_fx) add_subdirectory(shader_fx)
add_subdirectory(functions)
add_subdirectory(simulations)
add_subdirectory(makesdna) add_subdirectory(makesdna)
add_subdirectory(makesrna) add_subdirectory(makesrna)

View File

@@ -31,7 +31,6 @@ extern "C" {
#include "BKE_curve.h" #include "BKE_curve.h"
#include "BKE_mesh.h" #include "BKE_mesh.h"
#include "BKE_object.h"
} }
using Alembic::AbcGeom::OCompoundProperty; using Alembic::AbcGeom::OCompoundProperty;
@@ -177,11 +176,10 @@ Mesh *AbcCurveMeshWriter::getEvaluatedMesh(Scene * /*scene_eval*/,
Object *ob_eval, Object *ob_eval,
bool &r_needsfree) bool &r_needsfree)
{ {
Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval); if (ob_eval->runtime.mesh_eval != NULL) {
if (mesh_eval != NULL) {
/* Mesh_eval only exists when generative modifiers are in use. */ /* Mesh_eval only exists when generative modifiers are in use. */
r_needsfree = false; r_needsfree = false;
return mesh_eval; return ob_eval->runtime.mesh_eval;
} }
r_needsfree = true; r_needsfree = true;

View File

@@ -30,7 +30,6 @@ extern "C" {
#include "BKE_lib_id.h" #include "BKE_lib_id.h"
#include "BKE_mball.h" #include "BKE_mball.h"
#include "BKE_mesh.h" #include "BKE_mesh.h"
#include "BKE_object.h"
#include "BLI_utildefines.h" #include "BLI_utildefines.h"
} }
@@ -56,11 +55,10 @@ bool AbcMBallWriter::isAnimated() const
Mesh *AbcMBallWriter::getEvaluatedMesh(Scene * /*scene_eval*/, Object *ob_eval, bool &r_needsfree) Mesh *AbcMBallWriter::getEvaluatedMesh(Scene * /*scene_eval*/, Object *ob_eval, bool &r_needsfree)
{ {
Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval); if (ob_eval->runtime.mesh_eval != NULL) {
if (mesh_eval != NULL) {
/* Mesh_eval only exists when generative modifiers are in use. */ /* Mesh_eval only exists when generative modifiers are in use. */
r_needsfree = false; r_needsfree = false;
return mesh_eval; return ob_eval->runtime.mesh_eval;
} }
r_needsfree = true; r_needsfree = true;

View File

@@ -461,7 +461,7 @@ void blf_glyph_render(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, fl
return; return;
} }
if (g->glyph_cache == NULL) { if (!g->cached) {
if (font->tex_size_max == -1) { if (font->tex_size_max == -1) {
font->tex_size_max = GPU_max_texture_size(); font->tex_size_max = GPU_max_texture_size();
} }
@@ -492,7 +492,7 @@ void blf_glyph_render(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, fl
gc->bitmap_len = bitmap_len; gc->bitmap_len = bitmap_len;
gc->glyphs_len_free--; gc->glyphs_len_free--;
g->glyph_cache = gc; g->cached = true;
} }
if (font->flags & BLF_CLIPPING) { if (font->flags & BLF_CLIPPING) {
@@ -505,10 +505,8 @@ void blf_glyph_render(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, fl
} }
} }
if (g_batch.glyph_cache != g->glyph_cache) { g_batch.glyph_cache = gc;
blf_batch_draw(); BLI_assert(g->offset < gc->bitmap_len);
g_batch.glyph_cache = g->glyph_cache;
}
if (font->flags & BLF_SHADOW) { if (font->flags & BLF_SHADOW) {
rctf rect_ofs; rctf rect_ofs;

View File

@@ -131,7 +131,7 @@ typedef struct GlyphBLF {
float pos_x; float pos_x;
float pos_y; float pos_y;
struct GlyphCacheBLF *glyph_cache; bool cached;
} GlyphBLF; } GlyphBLF;
typedef struct FontBufInfoBLF { typedef struct FontBufInfoBLF {

View File

@@ -26,10 +26,6 @@
#include "BLI_bitmap.h" #include "BLI_bitmap.h"
#include "BLI_kdopbvh.h" #include "BLI_kdopbvh.h"
#ifdef __cplusplus
extern "C" {
#endif
/** /**
* This header encapsulates necessary code to build a BVH * This header encapsulates necessary code to build a BVH
*/ */
@@ -248,8 +244,4 @@ bool bvhcache_has_tree(const BVHCache *cache, const BVHTree *tree);
void bvhcache_insert(BVHCache **cache_p, BVHTree *tree, int type); void bvhcache_insert(BVHCache **cache_p, BVHTree *tree, int type);
void bvhcache_free(BVHCache **cache_p); void bvhcache_free(BVHCache **cache_p);
#ifdef __cplusplus
}
#endif
#endif #endif

View File

@@ -257,8 +257,6 @@ struct FCurve *iter_step_fcurve(struct FCurve *fcu_iter, const char rna_path[]);
struct FCurve *id_data_find_fcurve( struct FCurve *id_data_find_fcurve(
ID *id, void *data, struct StructRNA *type, const char *prop_name, int index, bool *r_driven); ID *id, void *data, struct StructRNA *type, const char *prop_name, int index, bool *r_driven);
void *get_driver_variable_function(struct DriverVar *dvar);
/* Get list of LinkData's containing pointers to the F-Curves which control the types of data /* Get list of LinkData's containing pointers to the F-Curves which control the types of data
* indicated * indicated
* e.g. numMatches = list_find_data_fcurves(matches, &act->curves, "pose.bones[", "MyFancyBone"); * e.g. numMatches = list_find_data_fcurves(matches, &act->curves, "pose.bones[", "MyFancyBone");

View File

@@ -1,34 +0,0 @@
#ifndef __BKE_ID_DATA_CACHE_H__
#define __BKE_ID_DATA_CACHE_H__
#include <mutex>
#include "BLI_map.h"
#include "BLI_kdopbvh.h"
#include "BLI_kdtree.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "BKE_bvhutils.h"
namespace BKE {
using BLI::Map;
class IDDataCache {
private:
mutable Map<Object *, BVHTreeFromMesh *> m_bvh_trees;
mutable std::mutex m_bvt_trees_mutex;
public:
IDDataCache() = default;
~IDDataCache();
BVHTreeFromMesh *get_bvh_tree(Object *object) const;
};
} // namespace BKE
#endif /* __BKE_ID_DATA_CACHE_H__ */

View File

@@ -1,108 +0,0 @@
#ifndef __BKE_ID_HANDLE_H__
#define __BKE_ID_HANDLE_H__
#include "BLI_utildefines.h"
#include "BLI_map.h"
extern "C" {
struct ID;
struct Object;
struct Image;
}
namespace BKE {
using BLI::Map;
/**
* This is a weak reference to an ID data-block. It does not contain a pointer to the actual data.
* It can happen that the IDHandle references data, that does not exist anymore. The handle does
* not know that.
*/
class IDHandle {
private:
uint32_t m_identifier;
public:
IDHandle() : m_identifier((uint32_t)-1)
{
}
IDHandle(struct ID *id);
friend bool operator==(IDHandle a, IDHandle b)
{
return a.m_identifier == b.m_identifier;
}
friend bool operator!=(IDHandle a, IDHandle b)
{
return !(a == b);
}
uint32_t internal_identifier() const
{
return m_identifier;
}
};
class ObjectIDHandle : public IDHandle {
public:
ObjectIDHandle() : IDHandle()
{
}
ObjectIDHandle(struct Object *object);
};
class ImageIDHandle : public IDHandle {
public:
ImageIDHandle() : IDHandle()
{
}
ImageIDHandle(struct Image *image);
};
class IDHandleLookup {
private:
Map<IDHandle, ID *> m_handle_to_id_map;
public:
void add(ID &id)
{
IDHandle handle(&id);
m_handle_to_id_map.add(handle, &id);
}
ID *lookup(IDHandle handle) const
{
return m_handle_to_id_map.lookup_default(handle, nullptr);
}
struct Object *lookup(ObjectIDHandle handle) const
{
return reinterpret_cast<struct Object *>(this->lookup((IDHandle)handle));
}
struct Image *lookup(ImageIDHandle handle) const
{
return reinterpret_cast<struct Image *>(this->lookup((IDHandle)handle));
}
static const IDHandleLookup &Empty();
};
} // namespace BKE
namespace BLI {
template<> struct DefaultHash<BKE::IDHandle> {
uint32_t operator()(const BKE::IDHandle &value) const
{
return value.internal_identifier();
}
};
} // namespace BLI
#endif /* __BKE_ID_HANDLE_H__ */

View File

@@ -38,10 +38,6 @@ struct Mesh;
struct Object; struct Object;
struct Scene; struct Scene;
#ifdef __cplusplus
extern "C" {
#endif
void BKE_mesh_runtime_reset(struct Mesh *mesh); void BKE_mesh_runtime_reset(struct Mesh *mesh);
void BKE_mesh_runtime_reset_on_copy(struct Mesh *mesh, const int flag); void BKE_mesh_runtime_reset_on_copy(struct Mesh *mesh, const int flag);
int BKE_mesh_runtime_looptri_len(const struct Mesh *mesh); int BKE_mesh_runtime_looptri_len(const struct Mesh *mesh);
@@ -107,8 +103,4 @@ void BKE_mesh_runtime_debug_print_cdlayers(struct CustomData *data);
bool BKE_mesh_runtime_is_valid(struct Mesh *me_eval); bool BKE_mesh_runtime_is_valid(struct Mesh *me_eval);
#endif /* NDEBUG */ #endif /* NDEBUG */
#ifdef __cplusplus
}
#endif
#endif /* __BKE_MESH_RUNTIME_H__ */ #endif /* __BKE_MESH_RUNTIME_H__ */

View File

@@ -24,10 +24,6 @@
* \ingroup bke * \ingroup bke
*/ */
#ifdef __cplusplus
extern "C" {
#endif
#include "BLI_compiler_compat.h" #include "BLI_compiler_compat.h"
#include "BLI_ghash.h" #include "BLI_ghash.h"
@@ -139,25 +135,12 @@ typedef struct bNodeSocketType {
struct bNode *node, struct bNode *node,
struct bNodeSocket *sock); struct bNodeSocket *sock);
void (*init_fn)(struct bNodeTree *ntree, struct bNode *node, struct bNodeSocket *socket);
void (*copy_fn)(struct bNodeTree *dst_ntree,
struct bNode *dst_node,
struct bNodeSocket *dst_socket,
const struct bNodeSocket *src_socket);
void (*free_fn)(struct bNodeTree *ntree, struct bNode *node, struct bNodeSocket *socket);
/* RNA integration */ /* RNA integration */
ExtensionRNA ext_socket; ExtensionRNA ext_socket;
ExtensionRNA ext_interface; ExtensionRNA ext_interface;
/* for standard socket types in C */ /* for standard socket types in C */
int type, subtype; int type, subtype;
/* Custom data that can be passed into callbacks. */
void *userdata;
/* Callback to free the socket type. */
void (*free_self)(struct bNodeSocketType *stype);
} bNodeSocketType; } bNodeSocketType;
typedef void *(*NodeInitExecFunction)(struct bNodeExecContext *context, typedef void *(*NodeInitExecFunction)(struct bNodeExecContext *context,
@@ -184,6 +167,7 @@ typedef int (*NodeGPUExecFunction)(struct GPUMaterial *mat,
*/ */
typedef struct bNodeType { typedef struct bNodeType {
void *next, *prev; void *next, *prev;
short needs_free; /* set for allocated types that need to be freed */
char idname[64]; /* identifier name */ char idname[64]; /* identifier name */
int type; int type;
@@ -263,11 +247,6 @@ typedef struct bNodeType {
/* Update the internal links list, for muting and disconnect operators. */ /* Update the internal links list, for muting and disconnect operators. */
void (*update_internal_links)(struct bNodeTree *, struct bNode *node); void (*update_internal_links)(struct bNodeTree *, struct bNode *node);
/* Custom data that can be used in callbacks. */
void *userdata;
void (*free_self)(struct bNodeType *ntype);
/* **** execution callbacks **** */ /* **** execution callbacks **** */
NodeInitExecFunction initexecfunc; NodeInitExecFunction initexecfunc;
NodeFreeExecFunction freeexecfunc; NodeFreeExecFunction freeexecfunc;
@@ -1315,8 +1294,4 @@ void BKE_nodetree_shading_params_eval(struct Depsgraph *depsgraph,
extern struct bNodeType NodeTypeUndefined; extern struct bNodeType NodeTypeUndefined;
extern struct bNodeSocketType NodeSocketTypeUndefined; extern struct bNodeSocketType NodeSocketTypeUndefined;
#ifdef __cplusplus
}
#endif
#endif /* __BKE_NODE_H__ */ #endif /* __BKE_NODE_H__ */

View File

@@ -287,7 +287,6 @@ void BKE_object_eval_uber_transform(struct Depsgraph *depsgraph, struct Object *
void BKE_object_eval_uber_data(struct Depsgraph *depsgraph, void BKE_object_eval_uber_data(struct Depsgraph *depsgraph,
struct Scene *scene, struct Scene *scene,
struct Object *ob); struct Object *ob);
void BKE_object_eval_assign_data(struct Object *object, struct ID *data, bool is_owned);
void BKE_object_eval_boundbox(struct Depsgraph *depsgraph, struct Object *object); void BKE_object_eval_boundbox(struct Depsgraph *depsgraph, struct Object *object);
void BKE_object_synchronize_to_original(struct Depsgraph *depsgraph, struct Object *object); void BKE_object_synchronize_to_original(struct Depsgraph *depsgraph, struct Object *object);
@@ -327,7 +326,8 @@ int BKE_object_obdata_texspace_get(struct Object *ob,
float **r_loc, float **r_loc,
float **r_size); float **r_size);
struct Mesh *BKE_object_get_evaluated_mesh(struct Object *object); struct Mesh *BKE_object_get_evaluated_mesh(const struct Depsgraph *depsgraph, struct Object *ob);
struct Mesh *BKE_object_get_final_mesh(struct Object *object);
struct Mesh *BKE_object_get_pre_modified_mesh(struct Object *object); struct Mesh *BKE_object_get_pre_modified_mesh(struct Object *object);
struct Mesh *BKE_object_get_original_mesh(struct Object *object); struct Mesh *BKE_object_get_original_mesh(struct Object *object);

View File

@@ -1,97 +0,0 @@
#ifndef __BKE_SURFACE_HOOK_H__
#define __BKE_SURFACE_HOOK_H__
#include "BLI_utildefines.h"
#include "BLI_float3.h"
#include "BKE_id_handle.h"
namespace BKE {
using BLI::float3;
namespace SurfaceHookType {
enum Enum {
None,
MeshObject,
};
}
/**
* References a point on a surface. If the surface moves, the point moves with it.
*/
class SurfaceHook {
private:
SurfaceHookType::Enum m_type;
/**
* Used to identify the object if m_type is MeshObject.
*/
ObjectIDHandle m_object_handle;
/* Index of the triangle that contains the referenced location. */
uint32_t m_triangle_index;
/* Barycentric coordinates of the referenced location inside the triangle. */
float3 m_bary_coords;
public:
SurfaceHook() : m_type(SurfaceHookType::None)
{
}
SurfaceHook(ObjectIDHandle object_handle, uint32_t triangle_index, float3 bary_coords)
: m_type(SurfaceHookType::MeshObject),
m_object_handle(object_handle),
m_triangle_index(triangle_index),
m_bary_coords(bary_coords)
{
}
SurfaceHookType::Enum type() const
{
return m_type;
}
bool is_valid() const
{
return m_type != SurfaceHookType::None;
}
ObjectIDHandle object_handle() const
{
BLI_assert(m_type == SurfaceHookType::MeshObject);
return m_object_handle;
}
uint32_t triangle_index() const
{
BLI_assert(m_type == SurfaceHookType::MeshObject);
return m_triangle_index;
}
float3 bary_coords() const
{
BLI_assert(m_type == SurfaceHookType::MeshObject);
return m_bary_coords;
}
static bool on_same_surface(const SurfaceHook &a, const SurfaceHook &b)
{
if (a.type() != b.type()) {
return false;
}
switch (a.type()) {
case BKE::SurfaceHookType::None:
return true;
case BKE::SurfaceHookType::MeshObject:
return a.object_handle() == b.object_handle();
}
BLI_assert(false);
return false;
}
};
} // namespace BKE
#endif /* __BKE_SURFACE_HOOK_H__ */

View File

@@ -1,380 +0,0 @@
#ifndef __BKE_VIRTUAL_NODE_TREE_H__
#define __BKE_VIRTUAL_NODE_TREE_H__
#include "BLI_vector.h"
#include "BLI_utility_mixins.h"
#include "BLI_array_cxx.h"
#include "BLI_string_ref.h"
#include "BLI_string_map.h"
#include "BLI_resource_collector.h"
#include "BLI_string_multi_map.h"
#include "BLI_linear_allocated_vector.h"
#include "DNA_node_types.h"
#include "RNA_access.h"
namespace BKE {
using BLI::Array;
using BLI::ArrayRef;
using BLI::LinearAllocatedVector;
using BLI::ResourceCollector;
using BLI::StringMap;
using BLI::StringMultiMap;
using BLI::StringRef;
using BLI::StringRefNull;
using BLI::Vector;
class VSocket;
class VInputSocket;
class VOutputSocket;
class VNode;
class VirtualNodeTree;
/* Virtual Node Tree declarations
******************************************/
class VSocket : BLI::NonCopyable, BLI::NonMovable {
protected:
LinearAllocatedVector<VSocket *> m_linked_sockets;
LinearAllocatedVector<VSocket *> m_directly_linked_sockets;
LinearAllocatedVector<bNodeLink *> m_incident_links;
VNode *m_node;
bool m_is_input;
bNodeSocket *m_bsocket;
uint m_id;
PointerRNA m_rna;
uint m_index;
friend VirtualNodeTree;
public:
ArrayRef<const VSocket *> linked_sockets() const;
ArrayRef<const VSocket *> directly_linked_sockets() const;
ArrayRef<bNodeLink *> incident_links() const;
const VNode &node() const;
const VirtualNodeTree &tree() const;
uint id() const;
uint index() const;
bool is_input() const;
bool is_output() const;
const VSocket &as_base() const;
const VInputSocket &as_input() const;
const VOutputSocket &as_output() const;
PointerRNA *rna() const;
StringRefNull idname() const;
StringRefNull name() const;
StringRefNull identifier() const;
bool is_linked() const;
bNodeSocket *bsocket() const;
bNodeTree *btree() const;
};
class VInputSocket final : public VSocket {
public:
ArrayRef<const VOutputSocket *> linked_sockets() const;
ArrayRef<const VOutputSocket *> directly_linked_sockets() const;
};
class VOutputSocket final : public VSocket {
public:
ArrayRef<const VInputSocket *> linked_sockets() const;
ArrayRef<const VInputSocket *> directly_linked_sockets() const;
};
class VNode : BLI::NonCopyable, BLI::NonMovable {
private:
VirtualNodeTree *m_vtree;
LinearAllocatedVector<VInputSocket *> m_inputs;
LinearAllocatedVector<VOutputSocket *> m_outputs;
bNode *m_bnode;
uint m_id;
PointerRNA m_rna;
friend VirtualNodeTree;
public:
const VirtualNodeTree &tree() const;
ArrayRef<const VInputSocket *> inputs() const;
ArrayRef<const VOutputSocket *> outputs() const;
PointerRNA *rna() const;
StringRefNull idname() const;
StringRefNull name() const;
const VInputSocket &input(uint index) const;
const VOutputSocket &output(uint index) const;
const VInputSocket &input(uint index, StringRef expected_name) const;
const VOutputSocket &output(uint index, StringRef expected_name) const;
bNode *bnode() const;
bNodeTree *btree() const;
uint id() const;
};
class VirtualNodeTree : BLI::NonCopyable, BLI::NonMovable {
private:
BLI::LinearAllocator<> m_allocator;
bNodeTree *m_btree;
Vector<VNode *> m_nodes_by_id;
Vector<VSocket *> m_sockets_by_id;
Vector<VInputSocket *> m_input_sockets;
Vector<VOutputSocket *> m_output_sockets;
StringMultiMap<VNode *> m_nodes_by_idname;
public:
VirtualNodeTree(bNodeTree *btree);
~VirtualNodeTree();
ArrayRef<const VNode *> nodes() const;
ArrayRef<const VNode *> nodes_with_idname(StringRef idname) const;
uint socket_count() const;
ArrayRef<const VSocket *> all_sockets() const;
ArrayRef<const VInputSocket *> all_input_sockets() const;
ArrayRef<const VOutputSocket *> all_output_sockets() const;
const VSocket &socket_by_id(uint id) const;
bNodeTree *btree() const;
private:
void find_targets_skipping_reroutes(VOutputSocket &vsocket,
LinearAllocatedVector<VSocket *> &r_targets);
};
/* Virtual Node Tree inline functions
****************************************************/
inline ArrayRef<const VSocket *> VSocket::linked_sockets() const
{
return m_linked_sockets.as_ref();
}
inline ArrayRef<const VSocket *> VSocket::directly_linked_sockets() const
{
return m_directly_linked_sockets.as_ref();
}
inline ArrayRef<bNodeLink *> VSocket::incident_links() const
{
return m_incident_links;
}
inline const VirtualNodeTree &VSocket::tree() const
{
return m_node->tree();
}
inline const VNode &VSocket::node() const
{
return *m_node;
}
inline uint VSocket::id() const
{
return m_id;
}
inline uint VSocket::index() const
{
return m_index;
}
inline bool VSocket::is_input() const
{
return m_is_input;
}
inline bool VSocket::is_output() const
{
return !m_is_input;
}
inline bool VSocket::is_linked() const
{
return m_linked_sockets.size() > 0;
}
inline const VSocket &VSocket::as_base() const
{
return *this;
}
inline const VInputSocket &VSocket::as_input() const
{
BLI_assert(this->is_input());
return *(const VInputSocket *)this;
}
inline const VOutputSocket &VSocket::as_output() const
{
BLI_assert(this->is_output());
return *(const VOutputSocket *)this;
}
inline PointerRNA *VSocket::rna() const
{
return const_cast<PointerRNA *>(&m_rna);
}
inline StringRefNull VSocket::idname() const
{
return m_bsocket->idname;
}
inline StringRefNull VSocket::name() const
{
return m_bsocket->name;
}
inline StringRefNull VSocket::identifier() const
{
return m_bsocket->identifier;
}
inline bNodeSocket *VSocket::bsocket() const
{
return m_bsocket;
}
inline ArrayRef<const VOutputSocket *> VInputSocket::linked_sockets() const
{
return m_linked_sockets.as_ref().cast<const VOutputSocket *>();
;
}
inline ArrayRef<const VOutputSocket *> VInputSocket::directly_linked_sockets() const
{
return m_directly_linked_sockets.as_ref().cast<const VOutputSocket *>();
}
inline ArrayRef<const VInputSocket *> VOutputSocket::linked_sockets() const
{
return m_linked_sockets.as_ref().cast<const VInputSocket *>();
}
inline ArrayRef<const VInputSocket *> VOutputSocket::directly_linked_sockets() const
{
return m_directly_linked_sockets.as_ref().cast<const VInputSocket *>();
}
inline ArrayRef<const VInputSocket *> VNode::inputs() const
{
return m_inputs.as_ref();
}
inline ArrayRef<const VOutputSocket *> VNode::outputs() const
{
return m_outputs.as_ref();
}
inline const VirtualNodeTree &VNode::tree() const
{
return *m_vtree;
}
inline PointerRNA *VNode::rna() const
{
return const_cast<PointerRNA *>(&m_rna);
}
inline StringRefNull VNode::idname() const
{
return m_bnode->idname;
}
inline StringRefNull VNode::name() const
{
return m_bnode->name;
}
inline const VInputSocket &VNode::input(uint index) const
{
return *m_inputs[index];
}
inline const VOutputSocket &VNode::output(uint index) const
{
return *m_outputs[index];
}
inline const VInputSocket &VNode::input(uint index, StringRef expected_name) const
{
BLI_assert(m_inputs[index]->name() == expected_name);
UNUSED_VARS_NDEBUG(expected_name);
return *m_inputs[index];
}
inline const VOutputSocket &VNode::output(uint index, StringRef expected_name) const
{
BLI_assert(m_outputs[index]->name() == expected_name);
UNUSED_VARS_NDEBUG(expected_name);
return *m_outputs[index];
}
inline bNode *VNode::bnode() const
{
return m_bnode;
}
inline uint VNode::id() const
{
return m_id;
}
inline bNodeTree *VirtualNodeTree::btree() const
{
return m_btree;
}
inline ArrayRef<const VNode *> VirtualNodeTree::nodes() const
{
return m_nodes_by_id.as_ref();
}
inline ArrayRef<const VNode *> VirtualNodeTree::nodes_with_idname(StringRef idname) const
{
return m_nodes_by_idname.lookup_default(idname);
}
inline uint VirtualNodeTree::socket_count() const
{
return m_sockets_by_id.size();
}
inline ArrayRef<const VSocket *> VirtualNodeTree::all_sockets() const
{
return m_sockets_by_id.as_ref();
}
inline ArrayRef<const VInputSocket *> VirtualNodeTree::all_input_sockets() const
{
return m_input_sockets.as_ref();
}
inline ArrayRef<const VOutputSocket *> VirtualNodeTree::all_output_sockets() const
{
return m_output_sockets.as_ref();
}
inline const VSocket &VirtualNodeTree::socket_by_id(uint id) const
{
return *m_sockets_by_id[id];
}
} // namespace BKE
#endif /* __BKE_VIRTUAL_NODE_TREE_H__ */

View File

@@ -37,7 +37,6 @@ set(INC
../nodes ../nodes
../physics ../physics
../shader_fx ../shader_fx
../simulations
../render/extern/include ../render/extern/include
../../../intern/ghost ../../../intern/ghost
../../../intern/glew-mx ../../../intern/glew-mx
@@ -126,8 +125,6 @@ set(SRC
intern/idcode.c intern/idcode.c
intern/idprop.c intern/idprop.c
intern/idprop_utils.c intern/idprop_utils.c
intern/id_data_cache.cc
intern/id_handle.cc
intern/image.c intern/image.c
intern/image_gen.c intern/image_gen.c
intern/image_save.c intern/image_save.c
@@ -222,7 +219,6 @@ set(SRC
intern/subdiv_stats.c intern/subdiv_stats.c
intern/subdiv_topology.c intern/subdiv_topology.c
intern/subsurf_ccg.c intern/subsurf_ccg.c
intern/surface_hook.cc
intern/text.c intern/text.c
intern/text_suggestions.c intern/text_suggestions.c
intern/texture.c intern/texture.c
@@ -236,7 +232,6 @@ set(SRC
intern/tracking_util.c intern/tracking_util.c
intern/undo_system.c intern/undo_system.c
intern/unit.c intern/unit.c
intern/virtual_node_tree.cc
intern/workspace.c intern/workspace.c
intern/world.c intern/world.c
intern/writeavi.c intern/writeavi.c
@@ -297,8 +292,6 @@ set(SRC
BKE_icons.h BKE_icons.h
BKE_idcode.h BKE_idcode.h
BKE_idprop.h BKE_idprop.h
BKE_id_data_cache.h
BKE_id_handle.h
BKE_image.h BKE_image.h
BKE_image_save.h BKE_image_save.h
BKE_ipo.h BKE_ipo.h
@@ -363,14 +356,12 @@ set(SRC
BKE_subdiv_mesh.h BKE_subdiv_mesh.h
BKE_subdiv_topology.h BKE_subdiv_topology.h
BKE_subsurf.h BKE_subsurf.h
BKE_surface_hook.h
BKE_text.h BKE_text.h
BKE_text_suggestions.h BKE_text_suggestions.h
BKE_texture.h BKE_texture.h
BKE_tracking.h BKE_tracking.h
BKE_undo_system.h BKE_undo_system.h
BKE_unit.h BKE_unit.h
BKE_virtual_node_tree.h
BKE_workspace.h BKE_workspace.h
BKE_world.h BKE_world.h
BKE_writeavi.h BKE_writeavi.h

View File

@@ -1719,12 +1719,43 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
} }
} }
static void mesh_build_extra_data(struct Depsgraph *depsgraph, Object *ob, Mesh *mesh_eval) static void assign_object_mesh_eval(Object *object)
{
BLI_assert(object->id.tag & LIB_TAG_COPIED_ON_WRITE);
Mesh *mesh = (Mesh *)object->data;
Mesh *mesh_eval = object->runtime.mesh_eval;
/* The modifier stack evaluation is storing result in mesh->runtime.mesh_eval, but this result
* is not guaranteed to be owned by object.
*
* Check ownership now, since later on we can not go to a mesh owned by someone else via object's
* runtime: this could cause access freed data on depsgraph destruction (mesh who owns the final
* result might be freed prior to object). */
if (mesh_eval == mesh->runtime.mesh_eval) {
object->runtime.is_mesh_eval_owned = false;
}
else {
mesh_eval->id.tag |= LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT;
object->runtime.is_mesh_eval_owned = true;
}
/* NOTE: We are not supposed to invoke evaluation for original object, but some areas are still
* under process of being ported, so we play safe here. */
if (object->id.tag & LIB_TAG_COPIED_ON_WRITE) {
object->data = mesh_eval;
}
else {
/* evaluated will be available via: 'object->runtime.mesh_eval' */
}
}
static void mesh_build_extra_data(struct Depsgraph *depsgraph, Object *ob)
{ {
uint32_t eval_flags = DEG_get_eval_flags_for_id(depsgraph, &ob->id); uint32_t eval_flags = DEG_get_eval_flags_for_id(depsgraph, &ob->id);
if (eval_flags & DAG_EVAL_NEED_SHRINKWRAP_BOUNDARY) { if (eval_flags & DAG_EVAL_NEED_SHRINKWRAP_BOUNDARY) {
BKE_shrinkwrap_compute_boundary_data(mesh_eval); BKE_shrinkwrap_compute_boundary_data(ob->runtime.mesh_eval);
} }
} }
@@ -1762,7 +1793,6 @@ static void mesh_build_data(struct Depsgraph *depsgraph,
} }
#endif #endif
Mesh *mesh_eval = NULL, *mesh_deform_eval = NULL;
mesh_calc_modifiers(depsgraph, mesh_calc_modifiers(depsgraph,
scene, scene,
ob, ob,
@@ -1772,35 +1802,26 @@ static void mesh_build_data(struct Depsgraph *depsgraph,
-1, -1,
true, true,
true, true,
&mesh_deform_eval, &ob->runtime.mesh_deform_eval,
&mesh_eval); &ob->runtime.mesh_eval);
/* The modifier stack evaluation is storing result in mesh->runtime.mesh_eval, but this result BKE_object_boundbox_calc_from_mesh(ob, ob->runtime.mesh_eval);
* is not guaranteed to be owned by object.
* assign_object_mesh_eval(ob);
* Check ownership now, since later on we can not go to a mesh owned by someone else via
* object's runtime: this could cause access freed data on depsgraph destruction (mesh who owns
* the final result might be freed prior to object). */
Mesh *mesh = ob->data;
const bool is_mesh_eval_owned = (mesh_eval != mesh->runtime.mesh_eval);
BKE_object_eval_assign_data(ob, &mesh_eval->id, is_mesh_eval_owned);
ob->runtime.mesh_deform_eval = mesh_deform_eval;
ob->runtime.last_data_mask = *dataMask; ob->runtime.last_data_mask = *dataMask;
ob->runtime.last_need_mapping = need_mapping; ob->runtime.last_need_mapping = need_mapping;
BKE_object_boundbox_calc_from_mesh(ob, mesh_eval);
if ((ob->mode & OB_MODE_ALL_SCULPT) && ob->sculpt) { if ((ob->mode & OB_MODE_ALL_SCULPT) && ob->sculpt) {
if (DEG_is_active(depsgraph)) { if (DEG_is_active(depsgraph)) {
BKE_sculpt_update_object_after_eval(depsgraph, ob); BKE_sculpt_update_object_after_eval(depsgraph, ob);
} }
} }
if (mesh_eval != NULL) { if (ob->runtime.mesh_eval != NULL) {
mesh_runtime_check_normals_valid(mesh_eval); mesh_runtime_check_normals_valid(ob->runtime.mesh_eval);
} }
mesh_build_extra_data(depsgraph, ob, mesh_eval); mesh_build_extra_data(depsgraph, ob);
} }
static void editbmesh_build_data(struct Depsgraph *depsgraph, static void editbmesh_build_data(struct Depsgraph *depsgraph,
@@ -1921,20 +1942,18 @@ Mesh *mesh_get_eval_final(struct Depsgraph *depsgraph,
CustomData_MeshMasks cddata_masks = *dataMask; CustomData_MeshMasks cddata_masks = *dataMask;
object_get_datamask(depsgraph, ob, &cddata_masks, &need_mapping); object_get_datamask(depsgraph, ob, &cddata_masks, &need_mapping);
Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); if (!ob->runtime.mesh_eval ||
if ((mesh_eval == NULL) ||
!CustomData_MeshMasks_are_matching(&(ob->runtime.last_data_mask), &cddata_masks) || !CustomData_MeshMasks_are_matching(&(ob->runtime.last_data_mask), &cddata_masks) ||
(need_mapping && !ob->runtime.last_need_mapping)) { (need_mapping && !ob->runtime.last_need_mapping)) {
CustomData_MeshMasks_update(&cddata_masks, &ob->runtime.last_data_mask); CustomData_MeshMasks_update(&cddata_masks, &ob->runtime.last_data_mask);
mesh_build_data( mesh_build_data(
depsgraph, scene, ob, &cddata_masks, need_mapping || ob->runtime.last_need_mapping); depsgraph, scene, ob, &cddata_masks, need_mapping || ob->runtime.last_need_mapping);
mesh_eval = BKE_object_get_evaluated_mesh(ob);
} }
if (mesh_eval != NULL) { if (ob->runtime.mesh_eval) {
BLI_assert(!(mesh_eval->runtime.cd_dirty_vert & CD_MASK_NORMAL)); BLI_assert(!(ob->runtime.mesh_eval->runtime.cd_dirty_vert & CD_MASK_NORMAL));
} }
return mesh_eval; return ob->runtime.mesh_eval;
} }
Mesh *mesh_get_eval_deform(struct Depsgraph *depsgraph, Mesh *mesh_get_eval_deform(struct Depsgraph *depsgraph,

View File

@@ -368,7 +368,7 @@ static void contarget_get_mesh_mat(Object *ob, const char *substring, float mat[
/* when not in EditMode, use the 'final' evaluated mesh, depsgraph /* when not in EditMode, use the 'final' evaluated mesh, depsgraph
* ensures we build with CD_MDEFORMVERT layer * ensures we build with CD_MDEFORMVERT layer
*/ */
Mesh *me_eval = BKE_object_get_evaluated_mesh(ob); Mesh *me_eval = ob->runtime.mesh_eval;
BMEditMesh *em = BKE_editmesh_from_object(ob); BMEditMesh *em = BKE_editmesh_from_object(ob);
float plane[3]; float plane[3];
float imat[3][3], tmat[3][3]; float imat[3][3], tmat[3][3];
@@ -860,87 +860,93 @@ static void childof_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar
} }
float parmat[4][4]; float parmat[4][4];
/* Simple matrix parenting. */
if ((data->flag & CHILDOF_ALL) == CHILDOF_ALL) { /* simple matrix parenting */
copy_m4_m4(parmat, ct->matrix); if (data->flag == CHILDOF_ALL) {
/* multiply target (parent matrix) by offset (parent inverse) to get
* the effect of the parent that will be exerted on the owner
*/
mul_m4_m4m4(parmat, ct->matrix, data->invmat);
/* now multiply the parent matrix by the owner matrix to get the
* the effect of this constraint (i.e. owner is 'parented' to parent)
*/
mul_m4_m4m4(cob->matrix, parmat, cob->matrix);
} }
/* Filter the parent matrix by channel. */
else { else {
float invmat[4][4], tempmat[4][4];
float loc[3], eul[3], size[3]; float loc[3], eul[3], size[3];
float loco[3], eulo[3], sizo[3];
/* get offset (parent-inverse) matrix */
copy_m4_m4(invmat, data->invmat);
/* extract components of both matrices */ /* extract components of both matrices */
copy_v3_v3(loc, ct->matrix[3]); copy_v3_v3(loc, ct->matrix[3]);
mat4_to_eulO(eul, ct->rotOrder, ct->matrix); mat4_to_eulO(eul, ct->rotOrder, ct->matrix);
mat4_to_size(size, ct->matrix); mat4_to_size(size, ct->matrix);
copy_v3_v3(loco, invmat[3]);
mat4_to_eulO(eulo, cob->rotOrder, invmat);
mat4_to_size(sizo, invmat);
/* disable channels not enabled */ /* disable channels not enabled */
if (!(data->flag & CHILDOF_LOCX)) { if (!(data->flag & CHILDOF_LOCX)) {
loc[0] = 0.0f; loc[0] = loco[0] = 0.0f;
} }
if (!(data->flag & CHILDOF_LOCY)) { if (!(data->flag & CHILDOF_LOCY)) {
loc[1] = 0.0f; loc[1] = loco[1] = 0.0f;
} }
if (!(data->flag & CHILDOF_LOCZ)) { if (!(data->flag & CHILDOF_LOCZ)) {
loc[2] = 0.0f; loc[2] = loco[2] = 0.0f;
} }
if (!(data->flag & CHILDOF_ROTX)) { if (!(data->flag & CHILDOF_ROTX)) {
eul[0] = 0.0f; eul[0] = eulo[0] = 0.0f;
} }
if (!(data->flag & CHILDOF_ROTY)) { if (!(data->flag & CHILDOF_ROTY)) {
eul[1] = 0.0f; eul[1] = eulo[1] = 0.0f;
} }
if (!(data->flag & CHILDOF_ROTZ)) { if (!(data->flag & CHILDOF_ROTZ)) {
eul[2] = 0.0f; eul[2] = eulo[2] = 0.0f;
} }
if (!(data->flag & CHILDOF_SIZEX)) { if (!(data->flag & CHILDOF_SIZEX)) {
size[0] = 1.0f; size[0] = sizo[0] = 1.0f;
} }
if (!(data->flag & CHILDOF_SIZEY)) { if (!(data->flag & CHILDOF_SIZEY)) {
size[1] = 1.0f; size[1] = sizo[1] = 1.0f;
} }
if (!(data->flag & CHILDOF_SIZEZ)) { if (!(data->flag & CHILDOF_SIZEZ)) {
size[2] = 1.0f; size[2] = sizo[2] = 1.0f;
} }
/* make new target mat and offset mat */ /* make new target mat and offset mat */
loc_eulO_size_to_mat4(parmat, loc, eul, size, ct->rotOrder); loc_eulO_size_to_mat4(ct->matrix, loc, eul, size, ct->rotOrder);
} loc_eulO_size_to_mat4(invmat, loco, eulo, sizo, cob->rotOrder);
/* Compute the inverse matrix if requested. */ /* multiply target (parent matrix) by offset (parent inverse) to get
if (data->flag & CHILDOF_SET_INVERSE) { * the effect of the parent that will be exerted on the owner
invert_m4_m4(data->invmat, parmat); */
mul_m4_m4m4(parmat, ct->matrix, invmat);
data->flag &= ~CHILDOF_SET_INVERSE; /* now multiply the parent matrix by the owner matrix to get the
* the effect of this constraint (i.e. owner is 'parented' to parent)
*/
copy_m4_m4(tempmat, cob->matrix);
mul_m4_m4m4(cob->matrix, parmat, tempmat);
/* Write the computed matrix back to the master copy if in COW evaluation. */ /* without this, changes to scale and rotation can change location
bConstraint *orig_con = constraint_find_original_for_update(cob, con); * of a parentless bone or a disconnected bone. Even though its set
* to zero above. */
if (orig_con != NULL) { if (!(data->flag & CHILDOF_LOCX)) {
bChildOfConstraint *orig_data = orig_con->data; cob->matrix[3][0] = tempmat[3][0];
}
copy_m4_m4(orig_data->invmat, data->invmat); if (!(data->flag & CHILDOF_LOCY)) {
orig_data->flag &= ~CHILDOF_SET_INVERSE; cob->matrix[3][1] = tempmat[3][1];
}
if (!(data->flag & CHILDOF_LOCZ)) {
cob->matrix[3][2] = tempmat[3][2];
} }
}
/* Multiply together the target (parent) matrix, parent inverse,
* and the owner transform matrixto get the effect of this constraint
* (i.e. owner is 'parented' to parent). */
float orig_cob_matrix[4][4];
copy_m4_m4(orig_cob_matrix, cob->matrix);
mul_m4_series(cob->matrix, parmat, data->invmat, orig_cob_matrix);
/* Without this, changes to scale and rotation can change location
* of a parentless bone or a disconnected bone. Even though its set
* to zero above. */
if (!(data->flag & CHILDOF_LOCX)) {
cob->matrix[3][0] = orig_cob_matrix[3][0];
}
if (!(data->flag & CHILDOF_LOCY)) {
cob->matrix[3][1] = orig_cob_matrix[3][1];
}
if (!(data->flag & CHILDOF_LOCZ)) {
cob->matrix[3][2] = orig_cob_matrix[3][2];
} }
} }
@@ -3968,7 +3974,7 @@ static void shrinkwrap_get_tarmat(struct Depsgraph *UNUSED(depsgraph),
float track_no[3] = {0.0f, 0.0f, 0.0f}; float track_no[3] = {0.0f, 0.0f, 0.0f};
SpaceTransform transform; SpaceTransform transform;
Mesh *target_eval = BKE_object_get_evaluated_mesh(ct->tar); Mesh *target_eval = ct->tar->runtime.mesh_eval;
copy_m4_m4(ct->matrix, cob->matrix); copy_m4_m4(ct->matrix, cob->matrix);
@@ -4736,7 +4742,7 @@ static void followtrack_evaluate(bConstraint *con, bConstraintOb *cob, ListBase
if (data->depth_ob) { if (data->depth_ob) {
Object *depth_ob = data->depth_ob; Object *depth_ob = data->depth_ob;
Mesh *target_eval = BKE_object_get_evaluated_mesh(depth_ob); Mesh *target_eval = depth_ob->runtime.mesh_eval;
if (target_eval) { if (target_eval) {
BVHTreeFromMesh treeData = NULL_BVHTreeFromMesh; BVHTreeFromMesh treeData = NULL_BVHTreeFromMesh;
BVHTreeRayHit hit; BVHTreeRayHit hit;
@@ -4885,35 +4891,23 @@ static void objectsolver_evaluate(bConstraint *con, bConstraintOb *cob, ListBase
return; return;
} }
float mat[4][4], obmat[4][4], imat[4][4], parmat[4][4]; float mat[4][4], obmat[4][4], imat[4][4], cammat[4][4], camimat[4][4], parmat[4][4];
float ctime = DEG_get_ctime(depsgraph); float ctime = DEG_get_ctime(depsgraph);
float framenr = BKE_movieclip_remap_scene_to_clip_frame(clip, ctime); float framenr = BKE_movieclip_remap_scene_to_clip_frame(clip, ctime);
BKE_object_where_is_calc_mat4(camob, cammat);
BKE_tracking_camera_get_reconstructed_interpolate(tracking, object, framenr, mat); BKE_tracking_camera_get_reconstructed_interpolate(tracking, object, framenr, mat);
invert_m4_m4(imat, mat); invert_m4_m4(camimat, cammat);
mul_m4_m4m4(parmat, camob->obmat, imat); mul_m4_m4m4(parmat, cammat, data->invmat);
copy_m4_m4(cammat, camob->obmat);
copy_m4_m4(obmat, cob->matrix); copy_m4_m4(obmat, cob->matrix);
/* Recalculate the inverse matrix if requested. */ invert_m4_m4(imat, mat);
if (data->flag & OBJECTSOLVER_SET_INVERSE) {
invert_m4_m4(data->invmat, parmat);
data->flag &= ~OBJECTSOLVER_SET_INVERSE; mul_m4_series(cob->matrix, cammat, imat, camimat, parmat, obmat);
/* Write the computed matrix back to the master copy if in COW evaluation. */
bConstraint *orig_con = constraint_find_original_for_update(cob, con);
if (orig_con != NULL) {
bObjectSolverConstraint *orig_data = orig_con->data;
copy_m4_m4(orig_data->invmat, data->invmat);
orig_data->flag &= ~OBJECTSOLVER_SET_INVERSE;
}
}
mul_m4_series(cob->matrix, parmat, data->invmat, obmat);
} }
static bConstraintTypeInfo CTI_OBJECTSOLVER = { static bConstraintTypeInfo CTI_OBJECTSOLVER = {

View File

@@ -338,8 +338,8 @@ static void crazyspace_init_object_for_eval(struct Depsgraph *depsgraph,
{ {
Object *object_eval = DEG_get_evaluated_object(depsgraph, object); Object *object_eval = DEG_get_evaluated_object(depsgraph, object);
*object_crazy = *object_eval; *object_crazy = *object_eval;
if (object_crazy->runtime.data_orig != NULL) { if (object_crazy->runtime.mesh_orig != NULL) {
object_crazy->data = object_crazy->runtime.data_orig; object_crazy->data = object_crazy->runtime.mesh_orig;
} }
} }

View File

@@ -1772,7 +1772,7 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
curve_calc_modifiers_post(depsgraph, scene, ob, &nubase, dispbase, r_final, for_render); curve_calc_modifiers_post(depsgraph, scene, ob, &nubase, dispbase, r_final, for_render);
} }
if (cu->flag & CU_DEFORM_FILL && !ob->runtime.data_eval) { if (cu->flag & CU_DEFORM_FILL && !ob->runtime.mesh_eval) {
curve_to_filledpoly(cu, &nubase, dispbase); curve_to_filledpoly(cu, &nubase, dispbase);
} }
@@ -1800,11 +1800,12 @@ void BKE_displist_make_curveTypes(
dispbase = &(ob->runtime.curve_cache->disp); dispbase = &(ob->runtime.curve_cache->disp);
Mesh *mesh_eval = NULL; do_makeDispListCurveTypes(
do_makeDispListCurveTypes(depsgraph, scene, ob, dispbase, for_render, for_orco, &mesh_eval); depsgraph, scene, ob, dispbase, for_render, for_orco, &ob->runtime.mesh_eval);
if (mesh_eval != NULL) { if (ob->runtime.mesh_eval != NULL) {
BKE_object_eval_assign_data(ob, &mesh_eval->id, true); ob->runtime.mesh_eval->id.tag |= LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT;
ob->runtime.is_mesh_eval_owned = true;
} }
boundbox_displist_object(ob); boundbox_displist_object(ob);
@@ -1860,9 +1861,8 @@ static void boundbox_displist_object(Object *ob)
ob->runtime.bb = MEM_callocN(sizeof(BoundBox), "boundbox"); ob->runtime.bb = MEM_callocN(sizeof(BoundBox), "boundbox");
} }
Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); if (ob->runtime.mesh_eval) {
if (mesh_eval) { BKE_object_boundbox_calc_from_mesh(ob, ob->runtime.mesh_eval);
BKE_object_boundbox_calc_from_mesh(ob, mesh_eval);
} }
else { else {
float min[3], max[3]; float min[3], max[3];

View File

@@ -315,7 +315,7 @@ ListBase *BKE_effectors_create(Depsgraph *depsgraph,
else if (weights->weight[ob->pd->forcefield] == 0.0f) { else if (weights->weight[ob->pd->forcefield] == 0.0f) {
continue; continue;
} }
else if (ob->pd->shape == PFIELD_SHAPE_POINTS && BKE_object_get_evaluated_mesh(ob) == NULL) { else if (ob->pd->shape == PFIELD_SHAPE_POINTS && ob->runtime.mesh_eval == NULL) {
continue; continue;
} }
@@ -656,7 +656,7 @@ int get_effector_data(EffectorCache *eff,
efd->size = 0.0f; efd->size = 0.0f;
} }
else if (eff->pd && eff->pd->shape == PFIELD_SHAPE_POINTS) { else if (eff->pd && eff->pd->shape == PFIELD_SHAPE_POINTS) {
Mesh *me_eval = BKE_object_get_evaluated_mesh(eff->ob); Mesh *me_eval = eff->ob->runtime.mesh_eval;
if (me_eval != NULL) { if (me_eval != NULL) {
copy_v3_v3(efd->loc, me_eval->mvert[*efd->index].co); copy_v3_v3(efd->loc, me_eval->mvert[*efd->index].co);
normal_short_to_float_v3(efd->nor, me_eval->mvert[*efd->index].no); normal_short_to_float_v3(efd->nor, me_eval->mvert[*efd->index].no);
@@ -769,7 +769,7 @@ static void get_effector_tot(
efd->index = p; efd->index = p;
if (eff->pd->shape == PFIELD_SHAPE_POINTS) { if (eff->pd->shape == PFIELD_SHAPE_POINTS) {
Mesh *me_eval = BKE_object_get_evaluated_mesh(eff->ob); Mesh *me_eval = eff->ob->runtime.mesh_eval;
*tot = me_eval != NULL ? me_eval->totvert : 1; *tot = me_eval != NULL ? me_eval->totvert : 1;
if (*tot && eff->pd->forcefield == PFIELD_HARMONIC && point->index >= 0) { if (*tot && eff->pd->forcefield == PFIELD_HARMONIC && point->index >= 0) {

View File

@@ -1870,15 +1870,15 @@ static DriverVarTypeInfo dvar_types[MAX_DVAR_TYPES] = {
BEGIN_DVAR_TYPEDEF(DVAR_TYPE_ROT_DIFF) dvar_eval_rotDiff, /* eval callback */ BEGIN_DVAR_TYPEDEF(DVAR_TYPE_ROT_DIFF) dvar_eval_rotDiff, /* eval callback */
2, /* number of targets used */ 2, /* number of targets used */
{"Object/Bone 1", "Object/Bone 2"}, /* UI names for targets */ {"Object/Bone 1", "Object/Bone 2"}, /* UI names for targets */
{DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY, DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY,
/* flags */ DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */
END_DVAR_TYPEDEF, END_DVAR_TYPEDEF,
BEGIN_DVAR_TYPEDEF(DVAR_TYPE_LOC_DIFF) dvar_eval_locDiff, /* eval callback */ BEGIN_DVAR_TYPEDEF(DVAR_TYPE_LOC_DIFF) dvar_eval_locDiff, /* eval callback */
2, /* number of targets used */ 2, /* number of targets used */
{"Object/Bone 1", "Object/Bone 2"}, /* UI names for targets */ {"Object/Bone 1", "Object/Bone 2"}, /* UI names for targets */
{DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY, DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY,
/* flags */ DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */
END_DVAR_TYPEDEF, END_DVAR_TYPEDEF,
BEGIN_DVAR_TYPEDEF(DVAR_TYPE_TRANSFORM_CHAN) dvar_eval_transChan, /* eval callback */ BEGIN_DVAR_TYPEDEF(DVAR_TYPE_TRANSFORM_CHAN) dvar_eval_transChan, /* eval callback */
@@ -1982,10 +1982,6 @@ void driver_change_variable_type(DriverVar *dvar, int type)
if ((flags & DTAR_FLAG_ID_OB_ONLY) || (dtar->idtype == 0)) { if ((flags & DTAR_FLAG_ID_OB_ONLY) || (dtar->idtype == 0)) {
dtar->idtype = ID_OB; dtar->idtype = ID_OB;
} }
if (type == DVAR_TYPE_FUNCTION) {
dtar->idtype = ID_NT;
}
} }
DRIVER_TARGETS_LOOPER_END; DRIVER_TARGETS_LOOPER_END;
} }

View File

@@ -1,31 +0,0 @@
#include "BKE_id_data_cache.h"
namespace BKE {
IDDataCache::~IDDataCache()
{
for (auto bvhtree : m_bvh_trees.values()) {
if (bvhtree != nullptr) {
free_bvhtree_from_mesh(bvhtree);
delete bvhtree;
}
}
}
BVHTreeFromMesh *IDDataCache::get_bvh_tree(Object *object) const
{
BLI_assert(object != nullptr);
std::lock_guard<std::mutex> lock(m_bvt_trees_mutex);
return m_bvh_trees.lookup_or_add(object, [&]() -> BVHTreeFromMesh * {
if (object->type != OB_MESH) {
return nullptr;
}
BVHTreeFromMesh *bvhtree_data = new BVHTreeFromMesh();
BKE_bvhtree_from_mesh_get(bvhtree_data, (Mesh *)object->data, BVHTREE_FROM_LOOPTRI, 2);
return bvhtree_data;
});
}
} // namespace BKE

Some files were not shown because too many files have changed in this diff Show More