Compare commits
19 Commits
simulation
...
tmp-workbe
Author | SHA1 | Date | |
---|---|---|---|
bd6567b262 | |||
91dfe40ef0 | |||
c8407006aa | |||
a102b7ebe5 | |||
aae2e1326f | |||
606f1d80b3 | |||
f426e71e09 | |||
62a7ac8f17 | |||
73de98e6e0 | |||
d36e63f8db | |||
e34ce36ea4 | |||
76a7ea0b71 | |||
46f8367eac | |||
5759e2d6c4 | |||
e6e740f317 | |||
b6e083358e | |||
1e58e68c1a | |||
1545c37cdb | |||
616545b5cf |
@@ -49,7 +49,6 @@ if(NOT LLVM_ROOT_DIR)
|
||||
OUTPUT_VARIABLE LLVM_ROOT_DIR
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
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()
|
||||
if(NOT LLVM_LIBPATH)
|
||||
execute_process(COMMAND ${LLVM_CONFIG} --libdir
|
||||
|
@@ -6,6 +6,7 @@
|
||||
#
|
||||
|
||||
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_BOOST OFF CACHE BOOL "" FORCE)
|
||||
|
6
extern/mantaflow/CMakeLists.txt
vendored
6
extern/mantaflow/CMakeLists.txt
vendored
@@ -49,11 +49,6 @@ if(WITH_TBB)
|
||||
add_definitions(-DTBB=1)
|
||||
endif()
|
||||
|
||||
if(WITH_OPENVDB)
|
||||
add_definitions(-DOPENVDB=1)
|
||||
add_definitions(-DOPENVDB_STATICLIB)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
add_definitions(-D_USE_MATH_DEFINES)
|
||||
endif()
|
||||
@@ -83,6 +78,7 @@ if(WITH_TBB)
|
||||
endif()
|
||||
|
||||
if(WITH_OPENVDB)
|
||||
add_definitions(-DOPENVDB=1 ${OPENVDB_DEFINITIONS})
|
||||
list(APPEND INC_SYS
|
||||
${OPENVDB_INCLUDE_DIRS}
|
||||
)
|
||||
|
@@ -390,7 +390,7 @@ typedef enum PassType {
|
||||
PASS_TRANSMISSION_DIRECT,
|
||||
PASS_TRANSMISSION_INDIRECT,
|
||||
PASS_TRANSMISSION_COLOR,
|
||||
PASS_VOLUME_DIRECT = 50,
|
||||
PASS_VOLUME_DIRECT,
|
||||
PASS_VOLUME_INDIRECT,
|
||||
/* No Scatter color since it's tricky to define what it would even mean. */
|
||||
PASS_CATEGORY_LIGHT_END = 63,
|
||||
|
@@ -57,9 +57,7 @@ ccl_device void svm_node_vector_rotate(ShaderData *sd,
|
||||
break;
|
||||
}
|
||||
float angle = stack_load_float(stack, angle_stack_offset);
|
||||
result = (len_squared(axis) != 0.0f) ?
|
||||
rotate_around_axis(vector - center, axis, angle) + center :
|
||||
vector;
|
||||
result = len(axis) ? rotate_around_axis(vector - center, axis, angle) + center : vector;
|
||||
}
|
||||
|
||||
/* Output */
|
||||
|
@@ -21,11 +21,7 @@
|
||||
* incompatible with the system libraries that Blender is built on. To solve
|
||||
* this we add a few -ffast-math symbols that can be missing. */
|
||||
|
||||
#ifdef __linux__
|
||||
# include <features.h>
|
||||
# include <math.h>
|
||||
|
||||
# if defined(__GLIBC_PREREQ) && __GLIBC_PREREQ(2, 31)
|
||||
#include <math.h>
|
||||
|
||||
double __exp_finite(double x);
|
||||
double __acos_finite(double x);
|
||||
@@ -111,6 +107,3 @@ float __powf_finite(float x, float y)
|
||||
{
|
||||
return powf(x, y);
|
||||
}
|
||||
|
||||
# endif /* __GLIBC_PREREQ */
|
||||
#endif /* __linux__ */
|
||||
|
@@ -64,7 +64,6 @@ if(WITH_TBB)
|
||||
endif()
|
||||
|
||||
if(WITH_OPENVDB)
|
||||
add_definitions(-DWITH_OPENVDB ${OPENVDB_DEFINITIONS})
|
||||
list(APPEND INC_SYS
|
||||
${OPENVDB_INCLUDE_DIRS}
|
||||
)
|
||||
|
@@ -95,8 +95,8 @@ _km_hierarchy = [
|
||||
('Weight Paint', 'EMPTY', 'WINDOW', [
|
||||
_km_expand_from_toolsystem('VIEW_3D', 'PAINT_WEIGHT'),
|
||||
]),
|
||||
('Paint Vertex Selection (Weight, Vertex)', 'EMPTY', 'WINDOW', []),
|
||||
('Paint Face Mask (Weight, Vertex, Texture)', 'EMPTY', 'WINDOW', []),
|
||||
('Weight Paint Vertex Selection', 'EMPTY', 'WINDOW', []),
|
||||
('Face Mask', 'EMPTY', 'WINDOW', []),
|
||||
# image and view3d
|
||||
('Image Paint', 'EMPTY', 'WINDOW', [
|
||||
_km_expand_from_toolsystem('VIEW_3D', 'PAINT_TEXTURE'),
|
||||
|
@@ -3380,7 +3380,7 @@ def km_grease_pencil_stroke_weight_mode(_params):
|
||||
def km_face_mask(params):
|
||||
items = []
|
||||
keymap = (
|
||||
"Paint Face Mask (Weight, Vertex, Texture)",
|
||||
"Face Mask",
|
||||
{"space_type": 'EMPTY', "region_type": 'WINDOW'},
|
||||
{"items": items},
|
||||
)
|
||||
@@ -3405,7 +3405,7 @@ def km_face_mask(params):
|
||||
def km_weight_paint_vertex_selection(params):
|
||||
items = []
|
||||
keymap = (
|
||||
"Paint Vertex Selection (Weight, Vertex)",
|
||||
"Weight Paint Vertex Selection",
|
||||
{"space_type": 'EMPTY', "region_type": 'WINDOW'},
|
||||
{"items": items},
|
||||
)
|
||||
@@ -5408,8 +5408,8 @@ def km_3d_view_tool_edit_armature_bone_size(params):
|
||||
"3D View Tool: Edit Armature, Bone Size",
|
||||
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
|
||||
{"items": [
|
||||
("transform.transform", {"type": params.tool_tweak, "value": 'ANY'},
|
||||
{"properties": [("release_confirm", True), ("mode", 'BONE_ENVELOPE')]}),
|
||||
("transform.bbone_resize", {"type": params.tool_tweak, "value": 'ANY'},
|
||||
{"properties": [("release_confirm", True)]}),
|
||||
]},
|
||||
)
|
||||
|
||||
@@ -5418,10 +5418,9 @@ def km_3d_view_tool_edit_armature_bone_envelope(params):
|
||||
return (
|
||||
"3D View Tool: Edit Armature, Bone Envelope",
|
||||
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
|
||||
|
||||
{"items": [
|
||||
("transform.bbone_resize", {"type": params.tool_tweak, "value": 'ANY'},
|
||||
{"properties": [("release_confirm", True)]}),
|
||||
("transform.transform", {"type": params.tool_tweak, "value": 'ANY'},
|
||||
{"properties": [("release_confirm", True), ("mode", 'BONE_ENVELOPE')]}),
|
||||
]},
|
||||
)
|
||||
|
||||
|
@@ -2531,7 +2531,7 @@ def km_grease_pencil_stroke_weight_mode(params):
|
||||
def km_face_mask(params):
|
||||
items = []
|
||||
keymap = (
|
||||
"Paint Face Mask (Weight, Vertex, Texture)",
|
||||
"Face Mask",
|
||||
{"space_type": 'EMPTY', "region_type": 'WINDOW'},
|
||||
{"items": items},
|
||||
)
|
||||
@@ -2558,7 +2558,7 @@ def km_face_mask(params):
|
||||
def km_weight_paint_vertex_selection(params):
|
||||
items = []
|
||||
keymap = (
|
||||
"Paint Vertex Selection (Weight, Vertex)",
|
||||
"Weight Paint Vertex Selection",
|
||||
{"space_type": 'EMPTY', "region_type": 'WINDOW'},
|
||||
{"items": items},
|
||||
)
|
||||
|
@@ -33,7 +33,6 @@ _modules = [
|
||||
"file",
|
||||
"image",
|
||||
"mesh",
|
||||
"modifiers",
|
||||
"node",
|
||||
"object",
|
||||
"object_align",
|
||||
|
@@ -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,
|
||||
)
|
@@ -406,7 +406,7 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
|
||||
row.prop(md, "mid_level")
|
||||
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")
|
||||
|
||||
def EDGE_SPLIT(self, layout, _ob, md):
|
||||
@@ -494,9 +494,13 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
|
||||
|
||||
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.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')
|
||||
|
||||
layout.separator()
|
||||
@@ -1711,42 +1715,6 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
|
||||
col.prop(md, "thresh", text="Threshold")
|
||||
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):
|
||||
bl_label = "Modifiers"
|
||||
|
@@ -206,11 +206,6 @@ class NODE_MT_add(bpy.types.Menu):
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
from nodes.base import BaseTree
|
||||
tree = context.space_data.node_tree
|
||||
if isinstance(tree, BaseTree):
|
||||
return
|
||||
|
||||
layout.operator_context = 'INVOKE_DEFAULT'
|
||||
props = layout.operator("node.add_search", text="Search...", icon='VIEWZOOM')
|
||||
props.use_transform = True
|
||||
|
@@ -68,8 +68,8 @@ ToolDef = namedtuple(
|
||||
"idname",
|
||||
# The name to display in the interface.
|
||||
"label",
|
||||
# Description (for tool-tip), when not set, use the description of 'operator',
|
||||
# may be a string or a 'function(context, item, key-map) -> string'.
|
||||
# Description (for tooltip), when not set, use the description of 'operator',
|
||||
# may be a string or a 'function(context, item, keymap) -> string'.
|
||||
"description",
|
||||
# The name of the icon to use (found in ``release/datafiles/icons``) or None for no icon.
|
||||
"icon",
|
||||
@@ -77,34 +77,13 @@ ToolDef = namedtuple(
|
||||
"cursor",
|
||||
# An optional gizmo group to activate when the tool is set or None for no gizmo.
|
||||
"widget",
|
||||
# Optional key-map for tool, possible values are:
|
||||
#
|
||||
# - ``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.
|
||||
#
|
||||
# Optional keymap for tool, either:
|
||||
# - A function that populates a keymaps passed in as an argument.
|
||||
# - A tuple filled with triple's of:
|
||||
# ``(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,
|
||||
# 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.
|
||||
"keymap",
|
||||
# Optional data-block associated with this tool.
|
||||
|
@@ -18,9 +18,6 @@
|
||||
|
||||
# <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
|
||||
# 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):
|
||||
brush = context.tool_settings.weight_paint.brush
|
||||
if brush is not None:
|
||||
from bl_ui.properties_paint_common import UnifiedPaintPanel
|
||||
UnifiedPaintPanel.prop_unified(
|
||||
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
|
||||
)
|
||||
|
||||
layout.prop(brush, "weight", slider=True)
|
||||
layout.prop(brush, "strength", slider=True)
|
||||
props = tool.operator_properties("paint.weight_gradient")
|
||||
layout.prop(props, "type", expand=True)
|
||||
|
||||
|
@@ -1190,7 +1190,6 @@ class USERPREF_PT_file_paths_data(FilePathsPanel, Panel):
|
||||
col.prop(paths, "script_directory", text="Scripts")
|
||||
col.prop(paths, "sound_directory", text="Sounds")
|
||||
col.prop(paths, "temporary_directory", text="Temporary Files")
|
||||
col.prop(paths, "nodelib_directory", text="Nodelib Files")
|
||||
|
||||
|
||||
class USERPREF_PT_file_paths_render(FilePathsPanel, Panel):
|
||||
|
@@ -2145,10 +2145,6 @@ class VIEW3D_MT_add(Menu):
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("fn.new_particle_system", text="Particle Simulation", icon='MOD_PARTICLES')
|
||||
|
||||
layout.separator()
|
||||
|
||||
if VIEW3D_MT_camera_add.is_extended():
|
||||
layout.menu("VIEW3D_MT_camera_add", icon='OUTLINER_OB_CAMERA')
|
||||
else:
|
||||
|
@@ -1,3 +0,0 @@
|
||||
from . auto_load import init, register, unregister
|
||||
|
||||
init()
|
@@ -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
|
@@ -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)
|
@@ -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")
|
@@ -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")
|
@@ -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")
|
@@ -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
|
@@ -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
|
@@ -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")
|
@@ -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")
|
@@ -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")
|
@@ -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")
|
@@ -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")
|
@@ -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")
|
@@ -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")
|
@@ -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")
|
@@ -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,
|
||||
)
|
@@ -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
|
@@ -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'}
|
@@ -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'}
|
@@ -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")
|
@@ -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
|
@@ -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
|
@@ -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)
|
@@ -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")
|
@@ -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="")
|
@@ -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
|
@@ -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})
|
@@ -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")
|
@@ -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")
|
@@ -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)
|
@@ -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="")
|
@@ -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")
|
@@ -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()
|
@@ -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")
|
@@ -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")
|
@@ -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
|
@@ -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")
|
@@ -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()
|
@@ -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)
|
@@ -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
|
@@ -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)
|
@@ -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)
|
@@ -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
|
@@ -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'}
|
@@ -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'}
|
@@ -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)
|
@@ -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)
|
@@ -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()
|
@@ -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"
|
@@ -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
|
@@ -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"])
|
@@ -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
|
@@ -1,6 +0,0 @@
|
||||
import bpy
|
||||
|
||||
class NodeSidebarPanel:
|
||||
bl_space_type = 'NODE_EDITOR'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = "Node"
|
@@ -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
|
@@ -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())
|
@@ -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
|
@@ -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)
|
@@ -112,8 +112,6 @@ add_subdirectory(nodes)
|
||||
add_subdirectory(modifiers)
|
||||
add_subdirectory(gpencil_modifiers)
|
||||
add_subdirectory(shader_fx)
|
||||
add_subdirectory(functions)
|
||||
add_subdirectory(simulations)
|
||||
add_subdirectory(makesdna)
|
||||
add_subdirectory(makesrna)
|
||||
|
||||
|
@@ -31,7 +31,6 @@ extern "C" {
|
||||
|
||||
#include "BKE_curve.h"
|
||||
#include "BKE_mesh.h"
|
||||
#include "BKE_object.h"
|
||||
}
|
||||
|
||||
using Alembic::AbcGeom::OCompoundProperty;
|
||||
@@ -177,11 +176,10 @@ Mesh *AbcCurveMeshWriter::getEvaluatedMesh(Scene * /*scene_eval*/,
|
||||
Object *ob_eval,
|
||||
bool &r_needsfree)
|
||||
{
|
||||
Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval);
|
||||
if (mesh_eval != NULL) {
|
||||
if (ob_eval->runtime.mesh_eval != NULL) {
|
||||
/* Mesh_eval only exists when generative modifiers are in use. */
|
||||
r_needsfree = false;
|
||||
return mesh_eval;
|
||||
return ob_eval->runtime.mesh_eval;
|
||||
}
|
||||
|
||||
r_needsfree = true;
|
||||
|
@@ -30,7 +30,6 @@ extern "C" {
|
||||
#include "BKE_lib_id.h"
|
||||
#include "BKE_mball.h"
|
||||
#include "BKE_mesh.h"
|
||||
#include "BKE_object.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 *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval);
|
||||
if (mesh_eval != NULL) {
|
||||
if (ob_eval->runtime.mesh_eval != NULL) {
|
||||
/* Mesh_eval only exists when generative modifiers are in use. */
|
||||
r_needsfree = false;
|
||||
return mesh_eval;
|
||||
return ob_eval->runtime.mesh_eval;
|
||||
}
|
||||
r_needsfree = true;
|
||||
|
||||
|
@@ -461,7 +461,7 @@ void blf_glyph_render(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, fl
|
||||
return;
|
||||
}
|
||||
|
||||
if (g->glyph_cache == NULL) {
|
||||
if (!g->cached) {
|
||||
if (font->tex_size_max == -1) {
|
||||
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->glyphs_len_free--;
|
||||
g->glyph_cache = gc;
|
||||
g->cached = true;
|
||||
}
|
||||
|
||||
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) {
|
||||
blf_batch_draw();
|
||||
g_batch.glyph_cache = g->glyph_cache;
|
||||
}
|
||||
g_batch.glyph_cache = gc;
|
||||
BLI_assert(g->offset < gc->bitmap_len);
|
||||
|
||||
if (font->flags & BLF_SHADOW) {
|
||||
rctf rect_ofs;
|
||||
|
@@ -131,7 +131,7 @@ typedef struct GlyphBLF {
|
||||
float pos_x;
|
||||
float pos_y;
|
||||
|
||||
struct GlyphCacheBLF *glyph_cache;
|
||||
bool cached;
|
||||
} GlyphBLF;
|
||||
|
||||
typedef struct FontBufInfoBLF {
|
||||
|
@@ -26,10 +26,6 @@
|
||||
#include "BLI_bitmap.h"
|
||||
#include "BLI_kdopbvh.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* 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_free(BVHCache **cache_p);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@@ -257,8 +257,6 @@ struct FCurve *iter_step_fcurve(struct FCurve *fcu_iter, const char rna_path[]);
|
||||
struct FCurve *id_data_find_fcurve(
|
||||
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
|
||||
* indicated
|
||||
* e.g. numMatches = list_find_data_fcurves(matches, &act->curves, "pose.bones[", "MyFancyBone");
|
||||
|
@@ -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__ */
|
@@ -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__ */
|
@@ -38,10 +38,6 @@ struct Mesh;
|
||||
struct Object;
|
||||
struct Scene;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void BKE_mesh_runtime_reset(struct Mesh *mesh);
|
||||
void BKE_mesh_runtime_reset_on_copy(struct Mesh *mesh, const int flag);
|
||||
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);
|
||||
#endif /* NDEBUG */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __BKE_MESH_RUNTIME_H__ */
|
||||
|
@@ -24,10 +24,6 @@
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "BLI_compiler_compat.h"
|
||||
#include "BLI_ghash.h"
|
||||
|
||||
@@ -139,25 +135,12 @@ typedef struct bNodeSocketType {
|
||||
struct bNode *node,
|
||||
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 */
|
||||
ExtensionRNA ext_socket;
|
||||
ExtensionRNA ext_interface;
|
||||
|
||||
/* for standard socket types in C */
|
||||
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;
|
||||
|
||||
typedef void *(*NodeInitExecFunction)(struct bNodeExecContext *context,
|
||||
@@ -184,6 +167,7 @@ typedef int (*NodeGPUExecFunction)(struct GPUMaterial *mat,
|
||||
*/
|
||||
typedef struct bNodeType {
|
||||
void *next, *prev;
|
||||
short needs_free; /* set for allocated types that need to be freed */
|
||||
|
||||
char idname[64]; /* identifier name */
|
||||
int type;
|
||||
@@ -263,11 +247,6 @@ typedef struct bNodeType {
|
||||
/* Update the internal links list, for muting and disconnect operators. */
|
||||
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 **** */
|
||||
NodeInitExecFunction initexecfunc;
|
||||
NodeFreeExecFunction freeexecfunc;
|
||||
@@ -1315,8 +1294,4 @@ void BKE_nodetree_shading_params_eval(struct Depsgraph *depsgraph,
|
||||
extern struct bNodeType NodeTypeUndefined;
|
||||
extern struct bNodeSocketType NodeSocketTypeUndefined;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __BKE_NODE_H__ */
|
||||
|
@@ -287,7 +287,6 @@ void BKE_object_eval_uber_transform(struct Depsgraph *depsgraph, struct Object *
|
||||
void BKE_object_eval_uber_data(struct Depsgraph *depsgraph,
|
||||
struct Scene *scene,
|
||||
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_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_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_original_mesh(struct Object *object);
|
||||
|
||||
|
@@ -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__ */
|
@@ -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__ */
|
@@ -37,7 +37,6 @@ set(INC
|
||||
../nodes
|
||||
../physics
|
||||
../shader_fx
|
||||
../simulations
|
||||
../render/extern/include
|
||||
../../../intern/ghost
|
||||
../../../intern/glew-mx
|
||||
@@ -126,8 +125,6 @@ set(SRC
|
||||
intern/idcode.c
|
||||
intern/idprop.c
|
||||
intern/idprop_utils.c
|
||||
intern/id_data_cache.cc
|
||||
intern/id_handle.cc
|
||||
intern/image.c
|
||||
intern/image_gen.c
|
||||
intern/image_save.c
|
||||
@@ -222,7 +219,6 @@ set(SRC
|
||||
intern/subdiv_stats.c
|
||||
intern/subdiv_topology.c
|
||||
intern/subsurf_ccg.c
|
||||
intern/surface_hook.cc
|
||||
intern/text.c
|
||||
intern/text_suggestions.c
|
||||
intern/texture.c
|
||||
@@ -236,7 +232,6 @@ set(SRC
|
||||
intern/tracking_util.c
|
||||
intern/undo_system.c
|
||||
intern/unit.c
|
||||
intern/virtual_node_tree.cc
|
||||
intern/workspace.c
|
||||
intern/world.c
|
||||
intern/writeavi.c
|
||||
@@ -297,8 +292,6 @@ set(SRC
|
||||
BKE_icons.h
|
||||
BKE_idcode.h
|
||||
BKE_idprop.h
|
||||
BKE_id_data_cache.h
|
||||
BKE_id_handle.h
|
||||
BKE_image.h
|
||||
BKE_image_save.h
|
||||
BKE_ipo.h
|
||||
@@ -363,14 +356,12 @@ set(SRC
|
||||
BKE_subdiv_mesh.h
|
||||
BKE_subdiv_topology.h
|
||||
BKE_subsurf.h
|
||||
BKE_surface_hook.h
|
||||
BKE_text.h
|
||||
BKE_text_suggestions.h
|
||||
BKE_texture.h
|
||||
BKE_tracking.h
|
||||
BKE_undo_system.h
|
||||
BKE_unit.h
|
||||
BKE_virtual_node_tree.h
|
||||
BKE_workspace.h
|
||||
BKE_world.h
|
||||
BKE_writeavi.h
|
||||
|
@@ -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);
|
||||
|
||||
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
|
||||
|
||||
Mesh *mesh_eval = NULL, *mesh_deform_eval = NULL;
|
||||
mesh_calc_modifiers(depsgraph,
|
||||
scene,
|
||||
ob,
|
||||
@@ -1772,35 +1802,26 @@ static void mesh_build_data(struct Depsgraph *depsgraph,
|
||||
-1,
|
||||
true,
|
||||
true,
|
||||
&mesh_deform_eval,
|
||||
&mesh_eval);
|
||||
&ob->runtime.mesh_deform_eval,
|
||||
&ob->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). */
|
||||
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);
|
||||
BKE_object_boundbox_calc_from_mesh(ob, ob->runtime.mesh_eval);
|
||||
|
||||
assign_object_mesh_eval(ob);
|
||||
|
||||
ob->runtime.mesh_deform_eval = mesh_deform_eval;
|
||||
ob->runtime.last_data_mask = *dataMask;
|
||||
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 (DEG_is_active(depsgraph)) {
|
||||
BKE_sculpt_update_object_after_eval(depsgraph, ob);
|
||||
}
|
||||
}
|
||||
|
||||
if (mesh_eval != NULL) {
|
||||
mesh_runtime_check_normals_valid(mesh_eval);
|
||||
if (ob->runtime.mesh_eval != NULL) {
|
||||
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,
|
||||
@@ -1921,20 +1942,18 @@ Mesh *mesh_get_eval_final(struct Depsgraph *depsgraph,
|
||||
CustomData_MeshMasks cddata_masks = *dataMask;
|
||||
object_get_datamask(depsgraph, ob, &cddata_masks, &need_mapping);
|
||||
|
||||
Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob);
|
||||
if ((mesh_eval == NULL) ||
|
||||
if (!ob->runtime.mesh_eval ||
|
||||
!CustomData_MeshMasks_are_matching(&(ob->runtime.last_data_mask), &cddata_masks) ||
|
||||
(need_mapping && !ob->runtime.last_need_mapping)) {
|
||||
CustomData_MeshMasks_update(&cddata_masks, &ob->runtime.last_data_mask);
|
||||
mesh_build_data(
|
||||
depsgraph, scene, ob, &cddata_masks, need_mapping || ob->runtime.last_need_mapping);
|
||||
mesh_eval = BKE_object_get_evaluated_mesh(ob);
|
||||
}
|
||||
|
||||
if (mesh_eval != NULL) {
|
||||
BLI_assert(!(mesh_eval->runtime.cd_dirty_vert & CD_MASK_NORMAL));
|
||||
if (ob->runtime.mesh_eval) {
|
||||
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,
|
||||
|
@@ -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
|
||||
* 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);
|
||||
float plane[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];
|
||||
/* Simple matrix parenting. */
|
||||
if ((data->flag & CHILDOF_ALL) == CHILDOF_ALL) {
|
||||
copy_m4_m4(parmat, ct->matrix);
|
||||
|
||||
/* simple matrix parenting */
|
||||
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 {
|
||||
float invmat[4][4], tempmat[4][4];
|
||||
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 */
|
||||
copy_v3_v3(loc, ct->matrix[3]);
|
||||
mat4_to_eulO(eul, ct->rotOrder, 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 */
|
||||
if (!(data->flag & CHILDOF_LOCX)) {
|
||||
loc[0] = 0.0f;
|
||||
loc[0] = loco[0] = 0.0f;
|
||||
}
|
||||
if (!(data->flag & CHILDOF_LOCY)) {
|
||||
loc[1] = 0.0f;
|
||||
loc[1] = loco[1] = 0.0f;
|
||||
}
|
||||
if (!(data->flag & CHILDOF_LOCZ)) {
|
||||
loc[2] = 0.0f;
|
||||
loc[2] = loco[2] = 0.0f;
|
||||
}
|
||||
if (!(data->flag & CHILDOF_ROTX)) {
|
||||
eul[0] = 0.0f;
|
||||
eul[0] = eulo[0] = 0.0f;
|
||||
}
|
||||
if (!(data->flag & CHILDOF_ROTY)) {
|
||||
eul[1] = 0.0f;
|
||||
eul[1] = eulo[1] = 0.0f;
|
||||
}
|
||||
if (!(data->flag & CHILDOF_ROTZ)) {
|
||||
eul[2] = 0.0f;
|
||||
eul[2] = eulo[2] = 0.0f;
|
||||
}
|
||||
if (!(data->flag & CHILDOF_SIZEX)) {
|
||||
size[0] = 1.0f;
|
||||
size[0] = sizo[0] = 1.0f;
|
||||
}
|
||||
if (!(data->flag & CHILDOF_SIZEY)) {
|
||||
size[1] = 1.0f;
|
||||
size[1] = sizo[1] = 1.0f;
|
||||
}
|
||||
if (!(data->flag & CHILDOF_SIZEZ)) {
|
||||
size[2] = 1.0f;
|
||||
size[2] = sizo[2] = 1.0f;
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
if (data->flag & CHILDOF_SET_INVERSE) {
|
||||
invert_m4_m4(data->invmat, parmat);
|
||||
/* 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, 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. */
|
||||
bConstraint *orig_con = constraint_find_original_for_update(cob, con);
|
||||
|
||||
if (orig_con != NULL) {
|
||||
bChildOfConstraint *orig_data = orig_con->data;
|
||||
|
||||
copy_m4_m4(orig_data->invmat, data->invmat);
|
||||
orig_data->flag &= ~CHILDOF_SET_INVERSE;
|
||||
/* 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] = tempmat[3][0];
|
||||
}
|
||||
if (!(data->flag & CHILDOF_LOCY)) {
|
||||
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};
|
||||
|
||||
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);
|
||||
|
||||
@@ -4736,7 +4742,7 @@ static void followtrack_evaluate(bConstraint *con, bConstraintOb *cob, ListBase
|
||||
|
||||
if (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) {
|
||||
BVHTreeFromMesh treeData = NULL_BVHTreeFromMesh;
|
||||
BVHTreeRayHit hit;
|
||||
@@ -4885,35 +4891,23 @@ static void objectsolver_evaluate(bConstraint *con, bConstraintOb *cob, ListBase
|
||||
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 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);
|
||||
|
||||
invert_m4_m4(imat, mat);
|
||||
mul_m4_m4m4(parmat, camob->obmat, imat);
|
||||
invert_m4_m4(camimat, cammat);
|
||||
mul_m4_m4m4(parmat, cammat, data->invmat);
|
||||
|
||||
copy_m4_m4(cammat, camob->obmat);
|
||||
copy_m4_m4(obmat, cob->matrix);
|
||||
|
||||
/* Recalculate the inverse matrix if requested. */
|
||||
if (data->flag & OBJECTSOLVER_SET_INVERSE) {
|
||||
invert_m4_m4(data->invmat, parmat);
|
||||
invert_m4_m4(imat, mat);
|
||||
|
||||
data->flag &= ~OBJECTSOLVER_SET_INVERSE;
|
||||
|
||||
/* 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);
|
||||
mul_m4_series(cob->matrix, cammat, imat, camimat, parmat, obmat);
|
||||
}
|
||||
|
||||
static bConstraintTypeInfo CTI_OBJECTSOLVER = {
|
||||
|
@@ -338,8 +338,8 @@ static void crazyspace_init_object_for_eval(struct Depsgraph *depsgraph,
|
||||
{
|
||||
Object *object_eval = DEG_get_evaluated_object(depsgraph, object);
|
||||
*object_crazy = *object_eval;
|
||||
if (object_crazy->runtime.data_orig != NULL) {
|
||||
object_crazy->data = object_crazy->runtime.data_orig;
|
||||
if (object_crazy->runtime.mesh_orig != NULL) {
|
||||
object_crazy->data = object_crazy->runtime.mesh_orig;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1772,7 +1772,7 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1800,11 +1800,12 @@ void BKE_displist_make_curveTypes(
|
||||
|
||||
dispbase = &(ob->runtime.curve_cache->disp);
|
||||
|
||||
Mesh *mesh_eval = NULL;
|
||||
do_makeDispListCurveTypes(depsgraph, scene, ob, dispbase, for_render, for_orco, &mesh_eval);
|
||||
do_makeDispListCurveTypes(
|
||||
depsgraph, scene, ob, dispbase, for_render, for_orco, &ob->runtime.mesh_eval);
|
||||
|
||||
if (mesh_eval != NULL) {
|
||||
BKE_object_eval_assign_data(ob, &mesh_eval->id, true);
|
||||
if (ob->runtime.mesh_eval != NULL) {
|
||||
ob->runtime.mesh_eval->id.tag |= LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT;
|
||||
ob->runtime.is_mesh_eval_owned = true;
|
||||
}
|
||||
|
||||
boundbox_displist_object(ob);
|
||||
@@ -1860,9 +1861,8 @@ static void boundbox_displist_object(Object *ob)
|
||||
ob->runtime.bb = MEM_callocN(sizeof(BoundBox), "boundbox");
|
||||
}
|
||||
|
||||
Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob);
|
||||
if (mesh_eval) {
|
||||
BKE_object_boundbox_calc_from_mesh(ob, mesh_eval);
|
||||
if (ob->runtime.mesh_eval) {
|
||||
BKE_object_boundbox_calc_from_mesh(ob, ob->runtime.mesh_eval);
|
||||
}
|
||||
else {
|
||||
float min[3], max[3];
|
||||
|
@@ -315,7 +315,7 @@ ListBase *BKE_effectors_create(Depsgraph *depsgraph,
|
||||
else if (weights->weight[ob->pd->forcefield] == 0.0f) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -656,7 +656,7 @@ int get_effector_data(EffectorCache *eff,
|
||||
efd->size = 0.0f;
|
||||
}
|
||||
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) {
|
||||
copy_v3_v3(efd->loc, me_eval->mvert[*efd->index].co);
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
if (*tot && eff->pd->forcefield == PFIELD_HARMONIC && point->index >= 0) {
|
||||
|
@@ -1870,15 +1870,15 @@ static DriverVarTypeInfo dvar_types[MAX_DVAR_TYPES] = {
|
||||
BEGIN_DVAR_TYPEDEF(DVAR_TYPE_ROT_DIFF) dvar_eval_rotDiff, /* eval callback */
|
||||
2, /* number of targets used */
|
||||
{"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}
|
||||
/* flags */
|
||||
{DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY,
|
||||
DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */
|
||||
END_DVAR_TYPEDEF,
|
||||
|
||||
BEGIN_DVAR_TYPEDEF(DVAR_TYPE_LOC_DIFF) dvar_eval_locDiff, /* eval callback */
|
||||
2, /* number of targets used */
|
||||
{"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}
|
||||
/* flags */
|
||||
{DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY,
|
||||
DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */
|
||||
END_DVAR_TYPEDEF,
|
||||
|
||||
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)) {
|
||||
dtar->idtype = ID_OB;
|
||||
}
|
||||
|
||||
if (type == DVAR_TYPE_FUNCTION) {
|
||||
dtar->idtype = ID_NT;
|
||||
}
|
||||
}
|
||||
DRIVER_TARGETS_LOOPER_END;
|
||||
}
|
||||
|
@@ -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
Reference in New Issue
Block a user