tissue updated to v0.3.70 #105180
@ -1,21 +1,15 @@
|
||||
# Tissue
|
||||
![cover](http://www.co-de-it.com/wordpress/wp-content/uploads/2015/07/tissue_graphics.jpg)
|
||||
Tissue - Blender's add-on for computational design by Co-de-iT
|
||||
http://www.co-de-it.com/wordpress/code/blender-tissue
|
||||
https://www.co-de-it.com/code/blender-tissue
|
||||
|
||||
Tissue is already shipped with both Blender. However I recommend to update the default version downloading manually the most recent one, for more updated features and more stability.
|
||||
|
||||
### Blender 2.93
|
||||
### Blender 4.0.2
|
||||
|
||||
Tissue v0.3.52 for Blender 2.93 (latest stable release): https://github.com/alessandro-zomparelli/tissue/releases/tag/v0-3-52
|
||||
Download the latest release: https://github.com/alessandro-zomparelli/tissue/releases
|
||||
|
||||
Development branch (usually the most updated version): https://github.com/alessandro-zomparelli/tissue/tree/b290-dev
|
||||
|
||||
### Blender 2.79 (unsupported)
|
||||
|
||||
Tissue v0.3.4 for Blender 2.79b (latest stable release): https://github.com/alessandro-zomparelli/tissue/releases/tag/v0-3-4
|
||||
|
||||
Development branch (most updated version): https://github.com/alessandro-zomparelli/tissue/tree/dev1
|
||||
Current development branch (usually the most updated version): https://github.com/alessandro-zomparelli/tissue/tree/b401-dev
|
||||
|
||||
|
||||
### Installation:
|
||||
@ -27,21 +21,18 @@ Development branch (most updated version): https://github.com/alessandro-zompare
|
||||
|
||||
### Documentation
|
||||
|
||||
Tissue documentation for Blender 2.80: https://github.com/alessandro-zomparelli/tissue/wiki
|
||||
|
||||
Tissue documentation for Blender's latest version: https://docs.blender.org/manual/en/latest/addons/mesh/tissue.html
|
||||
|
||||
### Issues
|
||||
Please help me keeping Tissue stable and updated, report any issues or feedback here: https://github.com/alessandro-zomparelli/tissue/issues
|
||||
Please help me keep Tissue stable and updated, report any issues or feedback here: https://github.com/alessandro-zomparelli/tissue/issues
|
||||
|
||||
### Contribute
|
||||
Tissue is free and open-source. I really think that this is the power of Blender and I wanted to give my small contribution to it.
|
||||
Tissue is free and open-source. I think that this is the power of Blender and I wanted to give my small contribution to it.
|
||||
|
||||
If you like my work and you want to help me, please consider to support me on **Patreon**, where I share some tips about Blender, Tissue and scripting: https://www.patreon.com/alessandrozomparelli
|
||||
If you like my work and you want to help me, please consider supporting me on **Patreon**, where I share some tips about Blender, Tissue and scripting: https://www.patreon.com/alessandrozomparelli
|
||||
|
||||
[![Patreon](http://alessandrozomparelli.com/wp-content/uploads/2020/04/patreon-transparent-vector-small.png)](https://www.patreon.com/alessandrozomparelli)
|
||||
|
||||
A special thanks to all my patrons, in particular to my **Tissue Supporters**: *TomaLaboratory*, *Scott Shorter*, *Garrett Post*, *Kairomon*, *Art Evans*, *Justin Davis*, *John Wise*, *Avi Bryant*, *Ahmed Saber*, *SlimeSound Production*, *Steffen Meier*.
|
||||
|
||||
Many thanks,
|
||||
|
||||
Alessandro
|
||||
|
@ -1,6 +1,21 @@
|
||||
# SPDX-FileCopyrightText: 2017-2023 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# --------------------------------- TISSUE ----------------------------------- #
|
||||
# ------------------------------- version 0.3 -------------------------------- #
|
||||
@ -12,15 +27,15 @@
|
||||
# (2017) #
|
||||
# #
|
||||
# http://www.co-de-it.com/ #
|
||||
# http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/Mesh/Tissue #
|
||||
# https://docs.blender.org/manual/en/latest/addons/mesh/tissue.html #
|
||||
# #
|
||||
# ############################################################################ #
|
||||
|
||||
bl_info = {
|
||||
"name": "Tissue",
|
||||
"author": "Alessandro Zomparelli (Co-de-iT)",
|
||||
"version": (0, 3, 54),
|
||||
"blender": (2, 93, 0),
|
||||
"author": "Alessandro Zomparelli",
|
||||
"version": (0, 3, 70),
|
||||
"blender": (4, 0, 2),
|
||||
"location": "",
|
||||
"description": "Tools for Computational Design",
|
||||
"warning": "",
|
||||
@ -35,6 +50,7 @@ if "bpy" in locals():
|
||||
importlib.reload(tessellate_numpy)
|
||||
importlib.reload(tissue_properties)
|
||||
importlib.reload(weight_tools)
|
||||
importlib.reload(weight_reaction_diffusion)
|
||||
importlib.reload(dual_mesh)
|
||||
importlib.reload(lattice)
|
||||
importlib.reload(uv_to_mesh)
|
||||
@ -43,11 +59,14 @@ if "bpy" in locals():
|
||||
importlib.reload(material_tools)
|
||||
importlib.reload(curves_tools)
|
||||
importlib.reload(polyhedra)
|
||||
importlib.reload(texture_reaction_diffusion)
|
||||
importlib.reload(contour_curves)
|
||||
|
||||
else:
|
||||
from . import tessellate_numpy
|
||||
from . import tissue_properties
|
||||
from . import weight_tools
|
||||
from . import weight_reaction_diffusion
|
||||
from . import dual_mesh
|
||||
from . import lattice
|
||||
from . import uv_to_mesh
|
||||
@ -56,6 +75,8 @@ else:
|
||||
from . import material_tools
|
||||
from . import curves_tools
|
||||
from . import polyhedra
|
||||
from . import texture_reaction_diffusion
|
||||
from . import contour_curves
|
||||
|
||||
import bpy
|
||||
from bpy.props import PointerProperty, CollectionProperty, BoolProperty
|
||||
@ -86,6 +107,7 @@ classes = (
|
||||
tessellate_numpy.TISSUE_PT_tessellate_morphing,
|
||||
tessellate_numpy.TISSUE_PT_tessellate_iterations,
|
||||
tessellate_numpy.tissue_render_animation,
|
||||
tessellate_numpy.tissue_remove,
|
||||
|
||||
weight_tools.face_area_to_vertex_groups,
|
||||
weight_tools.vertex_colors_to_vertex_groups,
|
||||
@ -93,31 +115,36 @@ classes = (
|
||||
weight_tools.vertex_group_to_uv,
|
||||
weight_tools.TISSUE_PT_weight,
|
||||
weight_tools.TISSUE_PT_color,
|
||||
weight_tools.weight_contour_curves,
|
||||
weight_tools.tissue_weight_contour_curves_pattern,
|
||||
weight_tools.weight_contour_mask,
|
||||
weight_tools.weight_contour_displace,
|
||||
weight_tools.harmonic_weight,
|
||||
weight_tools.edges_deformation,
|
||||
weight_tools.edges_bending,
|
||||
weight_tools.weight_laplacian,
|
||||
weight_tools.reaction_diffusion,
|
||||
weight_tools.start_reaction_diffusion,
|
||||
weight_tools.TISSUE_PT_reaction_diffusion,
|
||||
weight_tools.TISSUE_PT_reaction_diffusion_weight,
|
||||
weight_tools.reset_reaction_diffusion_weight,
|
||||
weight_reaction_diffusion.start_reaction_diffusion,
|
||||
weight_reaction_diffusion.TISSUE_PT_reaction_diffusion,
|
||||
weight_reaction_diffusion.TISSUE_PT_reaction_diffusion_performance,
|
||||
weight_reaction_diffusion.TISSUE_PT_reaction_diffusion_vector_field,
|
||||
weight_reaction_diffusion.TISSUE_PT_reaction_diffusion_weight,
|
||||
weight_reaction_diffusion.TISSUE_PT_reaction_diffusion_cache,
|
||||
weight_reaction_diffusion.reset_reaction_diffusion_weight,
|
||||
weight_tools.formula_prop,
|
||||
weight_tools.reaction_diffusion_prop,
|
||||
weight_reaction_diffusion.reaction_diffusion_prop,
|
||||
weight_tools.weight_formula,
|
||||
weight_tools.update_weight_formula,
|
||||
weight_tools.curvature_to_vertex_groups,
|
||||
weight_tools.weight_formula_wiki,
|
||||
weight_tools.tissue_weight_distance,
|
||||
weight_tools.random_weight,
|
||||
weight_tools.bake_reaction_diffusion,
|
||||
weight_tools.reaction_diffusion_free_data,
|
||||
weight_reaction_diffusion.bake_reaction_diffusion,
|
||||
weight_reaction_diffusion.reaction_diffusion_free_data,
|
||||
weight_tools.tissue_weight_streamlines,
|
||||
|
||||
contour_curves.tissue_weight_contour_curves_pattern,
|
||||
contour_curves.tissue_update_contour_curves,
|
||||
contour_curves.tissue_contour_curves_prop,
|
||||
contour_curves.TISSUE_PT_contour_curves,
|
||||
|
||||
dual_mesh.dual_mesh,
|
||||
dual_mesh.dual_mesh_tessellated,
|
||||
|
||||
@ -128,12 +155,21 @@ classes = (
|
||||
|
||||
curves_tools.tissue_to_curve_prop,
|
||||
curves_tools.tissue_convert_to_curve,
|
||||
curves_tools.tissue_convert_to_curve_update,
|
||||
curves_tools.tissue_update_convert_to_curve,
|
||||
curves_tools.TISSUE_PT_convert_to_curve,
|
||||
|
||||
uv_to_mesh.uv_to_mesh,
|
||||
|
||||
polyhedra.polyhedra_wireframe
|
||||
polyhedra.polyhedral_wireframe,
|
||||
polyhedra.tissue_update_polyhedra,
|
||||
polyhedra.tissue_polyhedra_prop,
|
||||
polyhedra.TISSUE_PT_polyhedra_object,
|
||||
|
||||
texture_reaction_diffusion.tex_reaction_diffusion_prop,
|
||||
texture_reaction_diffusion.start_tex_reaction_diffusion,
|
||||
texture_reaction_diffusion.reset_tex_reaction_diffusion,
|
||||
texture_reaction_diffusion.TISSUE_PT_tex_reaction_diffusion,
|
||||
texture_reaction_diffusion.TISSUE_PT_tex_reaction_diffusion_images
|
||||
)
|
||||
|
||||
def register():
|
||||
@ -147,18 +183,27 @@ def register():
|
||||
bpy.types.Object.tissue_tessellate = PointerProperty(
|
||||
type=tissue_properties.tissue_tessellate_prop
|
||||
)
|
||||
bpy.types.Object.tissue_polyhedra = PointerProperty(
|
||||
type=polyhedra.tissue_polyhedra_prop
|
||||
)
|
||||
bpy.types.Object.tissue_to_curve = PointerProperty(
|
||||
type=curves_tools.tissue_to_curve_prop
|
||||
)
|
||||
bpy.types.Object.tissue_contour_curves = PointerProperty(
|
||||
type=contour_curves.tissue_contour_curves_prop
|
||||
)
|
||||
bpy.types.Object.formula_settings = CollectionProperty(
|
||||
type=weight_tools.formula_prop
|
||||
)
|
||||
bpy.types.Object.reaction_diffusion_settings = PointerProperty(
|
||||
type=weight_tools.reaction_diffusion_prop
|
||||
type=weight_reaction_diffusion.reaction_diffusion_prop
|
||||
)
|
||||
bpy.types.Object.tex_reaction_diffusion_settings = PointerProperty(
|
||||
type=texture_reaction_diffusion.tex_reaction_diffusion_prop
|
||||
)
|
||||
# weight_tools
|
||||
bpy.app.handlers.frame_change_post.append(weight_tools.reaction_diffusion_def)
|
||||
#bpy.app.handlers.frame_change_post.append(tessellate_numpy.anim_tessellate)
|
||||
bpy.app.handlers.frame_change_post.append(weight_reaction_diffusion.reaction_diffusion_def)
|
||||
bpy.app.handlers.frame_change_post.append(texture_reaction_diffusion.tex_reaction_diffusion_def)
|
||||
|
||||
def unregister():
|
||||
from bpy.utils import unregister_class
|
||||
|
@ -1,5 +1,3 @@
|
||||
# SPDX-FileCopyrightText: 2022-2023 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import bpy
|
||||
@ -15,7 +13,7 @@ class tissuePreferences(bpy.types.AddonPreferences):
|
||||
|
||||
print_stats : IntProperty(
|
||||
name="Print Stats",
|
||||
description="Print in the console all details about the computing time",
|
||||
description="Print in the console all details about the computing time.",
|
||||
default=1,
|
||||
min=0,
|
||||
max=4
|
||||
@ -37,9 +35,13 @@ class tissuePreferences(bpy.types.AddonPreferences):
|
||||
numba_spec = importlib.util.find_spec('numba')
|
||||
found = numba_spec is not None
|
||||
if found:
|
||||
try:
|
||||
import numba
|
||||
layout.label(text='Numba module installed correctly!', icon='INFO')
|
||||
layout.prop(self, "use_numba_tess")
|
||||
else:
|
||||
except:
|
||||
found = False
|
||||
if not found:
|
||||
layout.label(text='Numba module not installed!', icon='ERROR')
|
||||
layout.label(text='Installing Numba will make Tissue faster', icon='INFO')
|
||||
row = layout.row()
|
||||
@ -56,6 +58,8 @@ class tissue_install_numba(bpy.types.Operator):
|
||||
try:
|
||||
from .utils_pip import Pip
|
||||
#Pip.upgrade_pip()
|
||||
Pip.uninstall('llvmlite')
|
||||
Pip.uninstall('numba')
|
||||
Pip.install('llvmlite')
|
||||
Pip.install('numba')
|
||||
from numba import jit, njit, guvectorize, float64, int32, prange
|
||||
|
1189
mesh_tissue/contour_curves.py
Normal file
1189
mesh_tissue/contour_curves.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,23 @@
|
||||
# SPDX-FileCopyrightText: 2022-2023 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# #
|
||||
# (c) Alessandro Zomparelli #
|
||||
# (2017) #
|
||||
@ -37,7 +53,8 @@ from .utils import (
|
||||
convert_object_to_mesh,
|
||||
get_weight_numpy,
|
||||
loops_from_bmesh,
|
||||
get_mesh_before_subs
|
||||
get_mesh_before_subs,
|
||||
tissue_time
|
||||
)
|
||||
import time
|
||||
|
||||
@ -48,7 +65,7 @@ def anim_curve_active(self, context):
|
||||
try:
|
||||
props.object.name
|
||||
if not ob.tissue.bool_lock:
|
||||
bpy.ops.object.tissue_convert_to_curve_update()
|
||||
bpy.ops.object.tissue_update_convert_to_curve()
|
||||
except: pass
|
||||
|
||||
|
||||
@ -67,7 +84,7 @@ class tissue_to_curve_prop(PropertyGroup):
|
||||
)
|
||||
bool_lock : BoolProperty(
|
||||
name="Lock",
|
||||
description="Prevent automatic update on settings changes or if other objects have it in the hierarchy",
|
||||
description="Prevent automatic update on settings changes or if other objects have it in the hierarchy.",
|
||||
default=False,
|
||||
update = anim_curve_active
|
||||
)
|
||||
@ -79,7 +96,7 @@ class tissue_to_curve_prop(PropertyGroup):
|
||||
)
|
||||
bool_run : BoolProperty(
|
||||
name="Animatable Curve",
|
||||
description="Automatically recompute the conversion when the frame is changed",
|
||||
description="Automatically recompute the conversion when the frame is changed.",
|
||||
default = False
|
||||
)
|
||||
use_modifiers : BoolProperty(
|
||||
@ -480,12 +497,12 @@ class tissue_convert_to_curve(Operator):
|
||||
|
||||
new_ob.tissue.bool_lock = False
|
||||
|
||||
bpy.ops.object.tissue_convert_to_curve_update()
|
||||
bpy.ops.object.tissue_update_convert_to_curve()
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
class tissue_convert_to_curve_update(Operator):
|
||||
bl_idname = "object.tissue_convert_to_curve_update"
|
||||
class tissue_update_convert_to_curve(Operator):
|
||||
bl_idname = "object.tissue_update_convert_to_curve"
|
||||
bl_label = "Tissue Update Curve"
|
||||
bl_description = "Update Curve object"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
@ -500,9 +517,10 @@ class tissue_convert_to_curve_update(Operator):
|
||||
return False
|
||||
|
||||
def execute(self, context):
|
||||
ob = context.object
|
||||
tissue_time(None,'Tissue: Convert to Curve of "{}"...'.format(ob.name), levels=0)
|
||||
start_time = time.time()
|
||||
|
||||
ob = context.object
|
||||
props = ob.tissue_to_curve
|
||||
ob0 = props.object
|
||||
if props.mode == 'PARTICLES':
|
||||
@ -669,8 +687,7 @@ class tissue_convert_to_curve_update(Operator):
|
||||
ob.data.splines.update()
|
||||
if not props.bool_smooth: bpy.ops.object.shade_flat()
|
||||
|
||||
end_time = time.time()
|
||||
print('Tissue: object "{}" converted to Curve in {:.4f} sec'.format(ob.name, end_time-start_time))
|
||||
tissue_time(start_time,'Convert to Curve',levels=0)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
@ -700,7 +717,7 @@ class TISSUE_PT_convert_to_curve(Panel):
|
||||
#layout.use_property_decorate = False
|
||||
col = layout.column(align=True)
|
||||
row = col.row(align=True)
|
||||
#col.operator("object.tissue_convert_to_curve_update", icon='FILE_REFRESH', text='Refresh')
|
||||
#col.operator("object.tissue_update_convert_to_curve", icon='FILE_REFRESH', text='Refresh')
|
||||
row.operator("object.tissue_update_tessellate_deps", icon='FILE_REFRESH', text='Refresh') ####
|
||||
lock_icon = 'LOCKED' if ob.tissue.bool_lock else 'UNLOCKED'
|
||||
#lock_icon = 'PINNED' if props.bool_lock else 'UNPINNED'
|
||||
@ -710,6 +727,8 @@ class TISSUE_PT_convert_to_curve(Panel):
|
||||
col2 = row.column(align=True)
|
||||
col2.prop(ob.tissue, "bool_run", text="",icon='TIME')
|
||||
col2.enabled = not ob.tissue.bool_lock
|
||||
col2 = row.column(align=True)
|
||||
col2.operator("mesh.tissue_remove", text="", icon='X')
|
||||
|
||||
col.separator()
|
||||
row = col.row(align=True)
|
||||
|
@ -1,7 +1,23 @@
|
||||
# SPDX-FileCopyrightText: 2017-2023 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# --------------------------------- DUAL MESH -------------------------------- #
|
||||
# -------------------------------- version 0.3 ------------------------------- #
|
||||
# #
|
||||
@ -230,10 +246,14 @@ class dual_mesh(Operator):
|
||||
)
|
||||
bpy.ops.mesh.select_all(action='DESELECT')
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
subsurf_modifier = context.object.modifiers.new("dual_mesh_subsurf", 'SUBSURF')
|
||||
context.object.modifiers.move(len(context.object.modifiers)-1, 0)
|
||||
bpy.ops.object.modifier_add(type='SUBSURF')
|
||||
ob.modifiers[-1].name = "dual_mesh_subsurf"
|
||||
while True:
|
||||
bpy.ops.object.modifier_move_up(modifier="dual_mesh_subsurf")
|
||||
if ob.modifiers[0].name == "dual_mesh_subsurf":
|
||||
break
|
||||
|
||||
bpy.ops.object.modifier_apply(modifier=subsurf_modifier.name)
|
||||
bpy.ops.object.modifier_apply(modifier='dual_mesh_subsurf')
|
||||
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.ops.mesh.select_all(action='DESELECT')
|
||||
@ -246,7 +266,7 @@ class dual_mesh(Operator):
|
||||
bpy.ops.mesh.select_more(use_face_step=False)
|
||||
|
||||
bpy.ops.mesh.select_similar(
|
||||
type='EDGE', compare='EQUAL', threshold=0.01)
|
||||
type='VERT_EDGES', compare='EQUAL', threshold=0.01)
|
||||
bpy.ops.mesh.select_all(action='INVERT')
|
||||
|
||||
bpy.ops.mesh.dissolve_verts()
|
||||
|
@ -1,7 +1,20 @@
|
||||
# SPDX-FileCopyrightText: 2017-2023 Blender Foundation
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
# --------------------------- LATTICE ALONG SURFACE -------------------------- #
|
||||
# -------------------------------- version 0.3 ------------------------------- #
|
||||
# #
|
||||
@ -327,6 +340,7 @@ class lattice_along_surface(Operator):
|
||||
grid_mesh = temp_grid_obj.data
|
||||
for v in grid_mesh.vertices:
|
||||
v.co = grid_obj.matrix_world @ v.co
|
||||
#grid_mesh.calc_normals()
|
||||
|
||||
if len(grid_mesh.polygons) > 64 * 64:
|
||||
bpy.data.objects.remove(temp_grid_obj)
|
||||
@ -372,13 +386,16 @@ class lattice_along_surface(Operator):
|
||||
lattice.scale.z = 1
|
||||
|
||||
context.view_layer.objects.active = obj
|
||||
lattice_modifier = context.object.modifiers.new("", 'LATTICE')
|
||||
lattice_modifier.object = lattice
|
||||
bpy.ops.object.modifier_add(type='LATTICE')
|
||||
obj.modifiers[-1].object = lattice
|
||||
|
||||
# set as parent
|
||||
if self.set_parent:
|
||||
override = {'active_object': obj, 'selected_objects' : [lattice,obj]}
|
||||
bpy.ops.object.parent_set(override, type='OBJECT', keep_transform=False)
|
||||
override = context.copy()
|
||||
override['active_object'] = obj
|
||||
override['selected_objects'] = [lattice,obj]
|
||||
with context.temp_override(**override):
|
||||
bpy.ops.object.parent_set(type='OBJECT', keep_transform=False)
|
||||
|
||||
# reading grid structure
|
||||
verts_grid, edges_grid, faces_grid = grid_from_mesh(
|
||||
@ -434,7 +451,7 @@ class lattice_along_surface(Operator):
|
||||
bpy.ops.object.delete(use_global=False)
|
||||
context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
bpy.ops.object.modifier_remove(modifier=lattice_modifier.name)
|
||||
bpy.ops.object.modifier_remove(modifier=obj.modifiers[-1].name)
|
||||
if nu > 64 or nv > 64:
|
||||
self.report({'ERROR'}, "Maximum resolution allowed for Lattice is 64")
|
||||
return {'CANCELLED'}
|
||||
|
@ -1,6 +1,20 @@
|
||||
# SPDX-FileCopyrightText: 2022 Blender Foundation
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# #
|
||||
# (c) Alessandro Zomparelli #
|
||||
@ -210,8 +224,8 @@ class weight_to_materials(Operator):
|
||||
faces_weight.append(w)
|
||||
faces_weight = np.array(faces_weight)
|
||||
faces_weight = faces_weight * count
|
||||
faces_weight.astype('int')
|
||||
ob.data.polygons.foreach_set('material_index',list(faces_weight))
|
||||
faces_weight = list(faces_weight.astype('int'))
|
||||
ob.data.polygons.foreach_set('material_index', faces_weight)
|
||||
ob.data.update()
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
return {'FINISHED'}
|
||||
|
@ -1,6 +1,20 @@
|
||||
# SPDX-FileCopyrightText: 2019-2022 Blender Foundation
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
import numpy as np
|
||||
import time
|
||||
@ -32,11 +46,87 @@ except:
|
||||
if bool_numba:
|
||||
#from numba import jit, njit, guvectorize, float64, int32, prange
|
||||
|
||||
|
||||
@njit(parallel=True)
|
||||
#@cuda.jit('void(float32[:,:], float32[:,:])')
|
||||
def tex_laplacian(lap, arr):
|
||||
arr2 = arr*2
|
||||
diag = sqrt(2)/2
|
||||
nx = arr.shape[0]
|
||||
ny = arr.shape[1]
|
||||
for i in prange(nx):
|
||||
for j in prange(ny):
|
||||
i0 = (i-1)%nx
|
||||
j0 = (j-1)%ny
|
||||
i1 = (i+1)%nx
|
||||
j1 = (j+1)%ny
|
||||
#lap[i+1,j+1] = arr[i, j+1] + arr[i+2, j+1] + arr[i+1, j] + arr[i+1, j+2] - 4*arr[i+1,j+1]
|
||||
|
||||
lap[i,j] = ((arr[i0, j] + arr[i1, j] - arr2[i,j]) + \
|
||||
(arr[i, j0] + arr[i, j1] - arr2[i,j]) + \
|
||||
(arr[i0, j0] + arr[i1, j1] - arr2[i,j])*diag + \
|
||||
(arr[i1, j0] + arr[i0, j1] - arr2[i,j])*diag)*0.75
|
||||
|
||||
@njit(parallel=True)
|
||||
def tex_laplacian_ani(lap, arr, VF):
|
||||
arr2 = arr*2
|
||||
nx = arr.shape[0]
|
||||
ny = arr.shape[1]
|
||||
i0 = np.arange(nx)-1
|
||||
i0[0] = 1
|
||||
i1 = np.arange(nx)+1
|
||||
i1[nx-1] = nx-2
|
||||
j0 = np.arange(ny)-1
|
||||
j0[0] = 1
|
||||
j1 = np.arange(ny)+1
|
||||
j1[ny-1] = ny-2
|
||||
for i in prange(nx):
|
||||
for j in prange(ny):
|
||||
lap[i,j] = (arr[i0[i], j] + arr[i1[i], j] - arr2[i,j])*VF[0,i,j] + \
|
||||
(arr[i, j0[j]] + arr[i, j1[j]] - arr2[i,j])*VF[1,i,j] + \
|
||||
(arr[i0[i], j0[j]] + arr[i1[i], j1[j]] - arr2[i,j])*VF[2,i,j] + \
|
||||
(arr[i1[i], j0[j]] + arr[i0[i], j1[j]] - arr2[i,j])*VF[3,i,j]
|
||||
#lap[0,:] = lap[1,:]
|
||||
#lap[:,0] = lap[:,1]
|
||||
#lap[-1,:] = lap[-2,:]
|
||||
#lap[:,-1] = lap[:,-2]
|
||||
|
||||
#@cuda.jit(parallel=True)
|
||||
@njit(parallel=True)
|
||||
def run_tex_rd(A, B, lap_A, lap_B, diff_A, diff_B, f, k, dt, steps, brush):
|
||||
for t in range(steps):
|
||||
tex_laplacian(lap_A, A)
|
||||
tex_laplacian(lap_B, B)
|
||||
nx = A.shape[0]
|
||||
ny = A.shape[1]
|
||||
for i in prange(nx):
|
||||
for j in prange(ny):
|
||||
B[i,j] += brush[i,j]
|
||||
ab2 = A[i,j]*B[i,j]**2
|
||||
A[i,j] += (lap_A[i,j]*diff_A - ab2 + f*(1-A[i,j]))*dt
|
||||
B[i,j] += (lap_B[i,j]*diff_B + ab2 - (k+f)*B[i,j])*dt
|
||||
|
||||
@njit(parallel=True)
|
||||
def run_tex_rd_ani(A, B, lap_A, lap_B, diff_A, diff_B, f, k, dt, steps, vf1, vf2, brush):
|
||||
for t in range(steps):
|
||||
tex_laplacian_ani(lap_A, A, vf2)
|
||||
#laplacian(lap_A, A)
|
||||
tex_laplacian_ani(lap_B, B, vf1)
|
||||
nx = A.shape[0]
|
||||
ny = A.shape[1]
|
||||
for i in prange(nx):
|
||||
for j in prange(ny):
|
||||
B[i,j] += brush[i,j]
|
||||
ab2 = A[i ,j]*B[i,j]**2
|
||||
A[i,j] += (lap_A[i,j]*diff_A[i,j] - ab2 + f[i,j]*(1-A[i,j]))*dt
|
||||
B[i,j] += (lap_B[i,j]*diff_B[i,j] + ab2 - (k[i,j]+f[i,j])*B[i,j])*dt
|
||||
|
||||
|
||||
@njit(parallel=True)
|
||||
def numba_reaction_diffusion(n_verts, n_edges, edge_verts, a, b, brush, diff_a, diff_b, f, k, dt, time_steps):
|
||||
arr = np.arange(n_edges)*2
|
||||
id0 = edge_verts[arr]
|
||||
id1 = edge_verts[arr+1]
|
||||
arr = np.arange(n_edges)
|
||||
id0 = edge_verts[arr*2]
|
||||
id1 = edge_verts[arr*2+1]
|
||||
for i in range(time_steps):
|
||||
lap_a, lap_b = rd_init_laplacian(n_verts)
|
||||
numba_rd_laplacian(id0, id1, a, b, lap_a, lap_b)
|
||||
@ -62,17 +152,14 @@ if bool_numba:
|
||||
return values
|
||||
|
||||
@njit(parallel=True)
|
||||
def numba_reaction_diffusion_anisotropic(n_verts, n_edges, edge_verts, a, b, brush, diff_a, diff_b, f, k, dt, time_steps, grad):
|
||||
arr = np.arange(n_edges)*2
|
||||
id0 = edge_verts[arr]
|
||||
id1 = edge_verts[arr+1]
|
||||
#grad = weight_grad[id0] - weight_grad[id1]
|
||||
#grad = np.abs(grad)
|
||||
#grad /= abs(np.max(grad))
|
||||
#grad = grad*0.98 + 0.02
|
||||
def numba_reaction_diffusion_anisotropic(n_verts, n_edges, edge_verts, a, b, brush, diff_a, diff_b, f, k, dt, time_steps, field_mult):
|
||||
arr = np.arange(n_edges)
|
||||
id0 = edge_verts[arr*2]
|
||||
id1 = edge_verts[arr*2+1]
|
||||
mult = field_mult[arr]
|
||||
for i in range(time_steps):
|
||||
lap_a, lap_b = rd_init_laplacian(n_verts)
|
||||
numba_rd_laplacian_anisotropic(id0, id1, a, b, lap_a, lap_b, grad)
|
||||
numba_rd_laplacian_anisotropic(id0, id1, a, b, lap_a, lap_b, mult)
|
||||
numba_rd_core(a, b, lap_a, lap_b, diff_a, diff_b, f, k, dt)
|
||||
numba_set_ab(a,b,brush)
|
||||
return a,b
|
||||
@ -112,28 +199,27 @@ if bool_numba:
|
||||
if a[i] < 0: a[i] = 0
|
||||
elif a[i] > 1: a[i] = 1
|
||||
|
||||
|
||||
#@guvectorize(['(float64[:] ,float64[:] ,float64[:] , float64[:], float64[:], float64[:])'],'(m),(m),(n),(n),(n),(n)',target='parallel')
|
||||
@njit(parallel=True)
|
||||
def numba_rd_laplacian(id0, id1, a, b, lap_a, lap_b):
|
||||
for i in prange(len(id0)):
|
||||
v0 = id0[i]
|
||||
v1 = id1[i]
|
||||
lap_a[v0] += a[v1] - a[v0]
|
||||
lap_a[v1] += a[v0] - a[v1]
|
||||
lap_b[v0] += b[v1] - b[v0]
|
||||
lap_b[v1] += b[v0] - b[v1]
|
||||
lap_a[v0] += (a[v1] - a[v0])
|
||||
lap_a[v1] += (a[v0] - a[v1])
|
||||
lap_b[v0] += (b[v1] - b[v0])
|
||||
lap_b[v1] += (b[v0] - b[v1])
|
||||
#return lap_a, lap_b
|
||||
|
||||
@njit(parallel=True)
|
||||
def numba_rd_laplacian_anisotropic(id0, id1, a, b, lap_a, lap_b, grad):
|
||||
def numba_rd_laplacian_anisotropic(id0, id1, a, b, lap_a, lap_b, mult):
|
||||
for i in prange(len(id0)):
|
||||
v0 = id0[i]
|
||||
v1 = id1[i]
|
||||
lap_a[v0] += (a[v1] - a[v0])
|
||||
lap_a[v1] += (a[v0] - a[v1])
|
||||
lap_b[v0] -= (b[v1] - b[v0])*grad[i]
|
||||
lap_b[v1] += (b[v0] - b[v1])*grad[i]
|
||||
multiplier = mult[i]
|
||||
lap_a[v0] += (a[v1] - a[v0])# * multiplier
|
||||
lap_a[v1] += (a[v0] - a[v1])# * multiplier
|
||||
lap_b[v0] += (b[v1] - b[v0]) * multiplier
|
||||
lap_b[v1] += (b[v0] - b[v1]) * multiplier
|
||||
#return lap_a, lap_b
|
||||
|
||||
@njit(parallel=True)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,20 @@
|
||||
# SPDX-FileCopyrightText: 2017-2023 Blender Foundation
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# ---------------------------- ADAPTIVE DUPLIFACES --------------------------- #
|
||||
# ------------------------------- version 0.84 ------------------------------- #
|
||||
@ -47,26 +61,19 @@ from . import config
|
||||
def allowed_objects():
|
||||
return ('MESH', 'CURVE', 'SURFACE', 'FONT', 'META')
|
||||
|
||||
def remove_temp_objects():
|
||||
# clean objects
|
||||
for o in bpy.data.objects:
|
||||
if "_tissue_tmp" in o.name:
|
||||
bpy.data.objects.remove(o)
|
||||
return
|
||||
|
||||
def tessellated(ob):
|
||||
tess_props = ob.tissue_tessellate
|
||||
if tess_props.generator not in list(bpy.data.objects):
|
||||
props = ob.tissue_tessellate
|
||||
if props.generator not in list(bpy.data.objects):
|
||||
return False
|
||||
elif tess_props.component_mode == 'OBJECT':
|
||||
return tess_props.component in list(bpy.data.objects)
|
||||
elif tess_props.component_mode == 'COLLECTION':
|
||||
if tess_props.component_coll in list(bpy.data.collections):
|
||||
for o in list(tess_props.component_coll.objects):
|
||||
elif props.component_mode == 'OBJECT':
|
||||
return props.component in list(bpy.data.objects)
|
||||
elif props.component_mode == 'COLLECTION':
|
||||
if props.component_coll in list(bpy.data.collections):
|
||||
for o in list(props.component_coll.objects):
|
||||
if o.type in allowed_objects():
|
||||
return True
|
||||
else:
|
||||
for mat in tess_props.generator.material_slots.keys():
|
||||
for mat in props.generator.material_slots.keys():
|
||||
if mat in bpy.data.objects.keys():
|
||||
if bpy.data.objects[mat].type in allowed_objects():
|
||||
return True
|
||||
@ -101,6 +108,10 @@ def tessellate_patch(props):
|
||||
vertex_group_thickness = props['vertex_group_thickness']
|
||||
invert_vertex_group_thickness = props['invert_vertex_group_thickness']
|
||||
vertex_group_thickness_factor = props['vertex_group_thickness_factor']
|
||||
vertex_group_frame_thickness = props['vertex_group_frame_thickness']
|
||||
invert_vertex_group_frame_thickness = props['invert_vertex_group_frame_thickness']
|
||||
vertex_group_frame_thickness_factor = props['vertex_group_frame_thickness_factor']
|
||||
face_weight_frame = props['face_weight_frame']
|
||||
vertex_group_distribution = props['vertex_group_distribution']
|
||||
invert_vertex_group_distribution = props['invert_vertex_group_distribution']
|
||||
vertex_group_distribution_factor = props['vertex_group_distribution_factor']
|
||||
@ -128,6 +139,7 @@ def tessellate_patch(props):
|
||||
vertex_group_scale_normals = props['vertex_group_scale_normals']
|
||||
invert_vertex_group_scale_normals = props['invert_vertex_group_scale_normals']
|
||||
boundary_mat_offset = props['boundary_mat_offset']
|
||||
preserve_quads = props['preserve_quads']
|
||||
|
||||
_props = props.copy()
|
||||
|
||||
@ -154,7 +166,7 @@ def tessellate_patch(props):
|
||||
# Target mesh used for normals
|
||||
if normals_mode in ('SHAPEKEYS', 'OBJECT'):
|
||||
if fill_mode == 'PATCH':
|
||||
ob0_sk = convert_object_to_mesh(target, True, True)
|
||||
ob0_sk = convert_object_to_mesh(target, True, rotation_mode!='UV')
|
||||
else:
|
||||
use_modifiers = gen_modifiers
|
||||
if normals_mode == 'SHAPEKEYS' and not gen_modifiers:
|
||||
@ -175,7 +187,7 @@ def tessellate_patch(props):
|
||||
for sk in _ob0.data.shape_keys.key_blocks: sk.value = 0
|
||||
# Base mesh
|
||||
if fill_mode == 'PATCH':
|
||||
ob0 = convert_object_to_mesh(_ob0)
|
||||
ob0 = convert_object_to_mesh(_ob0, True, True, rotation_mode!='UV')
|
||||
|
||||
if boundary_mat_offset != 0:
|
||||
bm=bmesh.new()
|
||||
@ -230,7 +242,7 @@ def tessellate_patch(props):
|
||||
first_component = True
|
||||
for com in components:
|
||||
if com:
|
||||
com = convert_object_to_mesh(com, com_modifiers, False)
|
||||
com = convert_object_to_mesh(com, com_modifiers, False, False)
|
||||
com, com_area = tessellate_prepare_component(com, props)
|
||||
com_verts = get_vertices_numpy(com.data)
|
||||
bpy.data.objects.remove(com)
|
||||
@ -319,6 +331,9 @@ def tessellate_patch(props):
|
||||
break
|
||||
else: before.modifiers.remove(m)
|
||||
|
||||
if rotation_mode!='UV':
|
||||
before_subsurf = simple_to_mesh_mirror(before)
|
||||
else:
|
||||
before_subsurf = simple_to_mesh(before)
|
||||
|
||||
if boundary_mat_offset != 0:
|
||||
@ -360,6 +375,7 @@ def tessellate_patch(props):
|
||||
if not vertex_group_rotation in ob0.vertex_groups.keys():
|
||||
rotation_mode = 'DEFAULT'
|
||||
|
||||
bool_vertex_group = bool_vertex_group and len(ob0.vertex_groups.keys()) > 0
|
||||
bool_weight_smooth_normals = vertex_group_smooth_normals in ob0.vertex_groups.keys()
|
||||
bool_weight_thickness = vertex_group_thickness in ob0.vertex_groups.keys()
|
||||
bool_weight_distribution = vertex_group_distribution in ob0.vertex_groups.keys()
|
||||
@ -422,7 +438,10 @@ def tessellate_patch(props):
|
||||
v01 = all_verts[:,0,-1]
|
||||
v10 = all_verts[:,-1,0]
|
||||
v11 = all_verts[:,-1,-1]
|
||||
face_weight = (weight_distribution[v00] + weight_distribution[v01] + weight_distribution[v10] + weight_distribution[v11])/4 * len(components)
|
||||
# Average method
|
||||
face_weight = np.average(weight_distribution[all_verts.reshape((all_verts.shape[0], -1))], axis=1) * len(components)
|
||||
# Corners Method
|
||||
#face_weight = (weight_distribution[v00] + weight_distribution[v01] + weight_distribution[v10] + weight_distribution[v11])/4 * len(components)
|
||||
if fill_mode == 'FAN' and consistent_wedges:
|
||||
for i in range(n_original_faces):
|
||||
face_mask = faces_id == i
|
||||
@ -578,7 +597,7 @@ def tessellate_patch(props):
|
||||
for mat_id, _ob1 in enumerate(components):
|
||||
if _ob1 == None: continue
|
||||
|
||||
# Set original values (for next components)
|
||||
# Set original values (for next commponents)
|
||||
com_modifiers = _com_modifiers
|
||||
bool_shapekeys = _bool_shapekeys
|
||||
|
||||
@ -620,7 +639,7 @@ def tessellate_patch(props):
|
||||
mod_visibility.append(m.show_viewport)
|
||||
m.show_viewport = False
|
||||
com_modifiers = True
|
||||
ob1 = convert_object_to_mesh(_ob1, com_modifiers, False)
|
||||
ob1 = convert_object_to_mesh(_ob1, com_modifiers, False, False)
|
||||
ob1, com_area = tessellate_prepare_component(ob1, props)
|
||||
ob1.name = "_tissue_tmp_ob1"
|
||||
|
||||
@ -753,6 +772,11 @@ def tessellate_patch(props):
|
||||
if vertex_group_thickness in ob0.vertex_groups.keys():
|
||||
vg_id = ob0.vertex_groups[vertex_group_thickness].index
|
||||
weight_thickness = store_weight[vg_id,:,:]
|
||||
if invert_vertex_group_thickness:
|
||||
weight_thickness = 1-weight_thickness
|
||||
fact = vertex_group_thickness_factor
|
||||
if fact > 0:
|
||||
weight_thickness = weight_thickness*(1-fact) + fact
|
||||
if vertex_group_smooth_normals in ob0.vertex_groups.keys():
|
||||
vg_id = ob0.vertex_groups[vertex_group_smooth_normals].index
|
||||
weight_smooth_normals = store_weight[vg_id,:,:]
|
||||
@ -802,6 +826,7 @@ def tessellate_patch(props):
|
||||
n2 = n2[masked_faces][:,None,:]
|
||||
else:
|
||||
if normals_mode == 'CUSTOM':
|
||||
me0.calc_normals_split()
|
||||
normals_split = [0]*len(me0.loops)*3
|
||||
vertex_indexes = [0]*len(me0.loops)
|
||||
me0.loops.foreach_get('normal', normals_split)
|
||||
@ -863,7 +888,7 @@ def tessellate_patch(props):
|
||||
# thickness variation
|
||||
mean_area = []
|
||||
a2 = None
|
||||
if scale_mode == 'ADAPTIVE' and normals_mode not in ('SHAPEKEYS','OBJECT'):
|
||||
if scale_mode == 'ADAPTIVE':# and normals_mode not in ('SHAPEKEYS','OBJECT'):
|
||||
#com_area = bb[0]*bb[1]
|
||||
if mode != 'BOUNDS' or com_area == 0: com_area = 1
|
||||
if normals_mode == 'FACES':
|
||||
@ -878,6 +903,9 @@ def tessellate_patch(props):
|
||||
verts_area = verts_area[masked_verts]
|
||||
verts_area = verts_area.mean(axis=(1,2)).reshape((n_patches,1,1))
|
||||
a2 = verts_area
|
||||
if normals_mode in ('SHAPEKEYS','OBJECT'):
|
||||
verts_area = np.ones(n_verts0)
|
||||
verts_area = verts_area[masked_verts]
|
||||
else:
|
||||
areas = calc_verts_area_bmesh(me0)
|
||||
verts_area = np.sqrt(areas*patch_faces/com_area)
|
||||
@ -900,7 +928,6 @@ def tessellate_patch(props):
|
||||
np_v = np.clip(sk_uv_quads[:,:,1], 0, sides).astype('int')[:,None,:]
|
||||
np_u1 = np.clip(sk_uv_quads[:,:,2], 0, sides).astype('int')[:,None,:]
|
||||
np_v1 = np.clip(sk_uv_quads[:,:,3], 0, sides).astype('int')[:,None,:]
|
||||
print(np_v1)
|
||||
# face corners for each vertex (n_patches, n_sk, n_verts1, 4)
|
||||
v00 = verts_xyz[:,np_u,np_v].reshape((n_patches,n_sk,n_verts1,3))#.swapaxes(0,1)
|
||||
v10 = verts_xyz[:,np_u1,np_v].reshape((n_patches,n_sk,n_verts1,3))#.swapaxes(0,1)
|
||||
@ -973,12 +1000,18 @@ def tessellate_patch(props):
|
||||
except: pass
|
||||
tt = tissue_time(tt, "Inject coordinates", levels=2)
|
||||
|
||||
|
||||
# Vertex Group
|
||||
for vg in ob1.vertex_groups:
|
||||
vg_name = vg.name
|
||||
if vg_name in ob0.vertex_groups.keys():
|
||||
vg_name = '_{}_'.format(vg_name)
|
||||
new_patch.vertex_groups.new(name=vg_name)
|
||||
if bool_vertex_group:
|
||||
vg_name = '{} (Component)'.format(vg_name)
|
||||
else:
|
||||
vg_name = vg_name
|
||||
#new_patch.vertex_groups.new(name=vg_name)
|
||||
new_patch.vertex_groups[vg.name].name = vg_name
|
||||
|
||||
if bool_vertex_group:
|
||||
new_groups = []
|
||||
for vg in ob0.vertex_groups:
|
||||
@ -1253,7 +1286,7 @@ class tissue_tessellate(Operator):
|
||||
min=1,
|
||||
soft_max=5,
|
||||
description="Automatically repeat the Tessellation using the "
|
||||
+ "generated geometry as new base object.\nUseful for "
|
||||
+ "generated geometry as new base object.\nUsefull for "
|
||||
+ "for branching systems. Dangerous!"
|
||||
)
|
||||
bool_combine : BoolProperty(
|
||||
@ -1350,13 +1383,21 @@ class tissue_tessellate(Operator):
|
||||
name="Frame Thickness",
|
||||
default=0.2,
|
||||
min=0,
|
||||
soft_max=2,
|
||||
soft_max=1,
|
||||
description="Frame Thickness"
|
||||
)
|
||||
frame_boundary_thickness : FloatProperty(
|
||||
name="Frame Boundary Thickness",
|
||||
default=0,
|
||||
min=0,
|
||||
soft_max=1,
|
||||
description="Frame Boundary Thickness (if zero, it uses the Frame Thickness instead)"
|
||||
)
|
||||
frame_mode : EnumProperty(
|
||||
items=(
|
||||
('CONSTANT', 'Constant', 'Even thickness'),
|
||||
('RELATIVE', 'Relative', 'Frame offset depends on face areas')),
|
||||
('RELATIVE', 'Relative', 'Frame offset depends on face areas'),
|
||||
('CENTER', 'Center', 'Toward the center of the face (uses Incenter for Triangles)')),
|
||||
default='CONSTANT',
|
||||
name="Offset"
|
||||
)
|
||||
@ -1387,7 +1428,7 @@ class tissue_tessellate(Operator):
|
||||
)
|
||||
use_origin_offset : BoolProperty(
|
||||
name="Align to Origins",
|
||||
default=False,
|
||||
default=True,
|
||||
description="Define offset according to components origin and local Z coordinate"
|
||||
)
|
||||
|
||||
@ -1407,6 +1448,27 @@ class tissue_tessellate(Operator):
|
||||
description="Thickness factor to use for zero vertex group influence"
|
||||
)
|
||||
|
||||
vertex_group_frame_thickness : StringProperty(
|
||||
name="Frame thickness weight", default='',
|
||||
description="Vertex Group used for frame thickness"
|
||||
)
|
||||
invert_vertex_group_frame_thickness : BoolProperty(
|
||||
name="Invert", default=False,
|
||||
description="Invert the vertex group influence"
|
||||
)
|
||||
vertex_group_frame_thickness_factor : FloatProperty(
|
||||
name="Factor",
|
||||
default=0,
|
||||
min=0,
|
||||
max=1,
|
||||
description="Thickness factor to use for zero vertex group influence"
|
||||
)
|
||||
face_weight_frame : BoolProperty(
|
||||
name="Face Weight",
|
||||
default=True,
|
||||
description="Uniform weight for individual faces"
|
||||
)
|
||||
|
||||
vertex_group_distribution : StringProperty(
|
||||
name="Distribution weight", default='',
|
||||
description="Vertex Group used for gradient distribution"
|
||||
@ -1520,6 +1582,11 @@ class tissue_tessellate(Operator):
|
||||
name="Automatic Rotation", default=False,
|
||||
description="Automatically rotate the boundary faces"
|
||||
)
|
||||
preserve_quads : BoolProperty(
|
||||
name="Preserve Quads",
|
||||
default=False,
|
||||
description="Quad faces are tessellated using QUAD mode"
|
||||
)
|
||||
|
||||
working_on = ""
|
||||
|
||||
@ -1568,7 +1635,7 @@ class tissue_tessellate(Operator):
|
||||
if no_components:
|
||||
layout = self.layout
|
||||
layout.label(icon='OUTLINER_COLLECTION', text='Components from Active Collection')
|
||||
layout.label(icon='INFO', text="The Active Collection does not contain any Mesh,")
|
||||
layout.label(icon='INFO', text="The Active Collection does not containt any Mesh,")
|
||||
layout.label(text="Curve, Surface, Meta or Text object.")
|
||||
return
|
||||
elif self.component_mode == 'MATERIALS':
|
||||
@ -1646,9 +1713,21 @@ class tissue_tessellate(Operator):
|
||||
if self.fill_mode == 'FRAME':
|
||||
col.separator()
|
||||
col.label(text="Frame Settings:")
|
||||
col.prop(self, "preserve_quads", expand=True)
|
||||
col.separator()
|
||||
row = col.row(align=True)
|
||||
row.prop(self, "frame_mode", expand=True)
|
||||
col.prop(self, "frame_thickness", text='Thickness', icon='NONE')
|
||||
# Vertex Group Frame Thickness
|
||||
row = col.row(align=True)
|
||||
row.prop_search(self, 'vertex_group_frame_thickness',
|
||||
ob0, "vertex_groups", text='')
|
||||
col2 = row.column(align=True)
|
||||
row2 = col2.row(align=True)
|
||||
row2.prop(self, "invert_vertex_group_frame_thickness", text="",
|
||||
toggle=True, icon='ARROW_LEFTRIGHT')
|
||||
row2.prop(self, "vertex_group_frame_thickness_factor")
|
||||
row2.enabled = self.vertex_group_frame_thickness in ob0.vertex_groups.keys()
|
||||
col.separator()
|
||||
row = col.row(align=True)
|
||||
row.prop(self, "fill_frame", icon='NONE')
|
||||
@ -1661,6 +1740,10 @@ class tissue_tessellate(Operator):
|
||||
col2 = row.column(align=True)
|
||||
col2.prop(self, "boundary_mat_offset", icon='NONE')
|
||||
col2.enabled = self.frame_boundary and show_frame_mat
|
||||
if self.frame_boundary:
|
||||
col.separator()
|
||||
row = col.row(align=True)
|
||||
col.prop(self, "frame_boundary_thickness", icon='NONE')
|
||||
|
||||
if self.rotation_mode == 'UV':
|
||||
uv_error = False
|
||||
@ -1742,7 +1825,7 @@ class tissue_tessellate(Operator):
|
||||
bool_update = False
|
||||
if context.object == ob0:
|
||||
auto_layer_collection()
|
||||
new_ob = convert_object_to_mesh(ob0,False,False)
|
||||
new_ob = convert_object_to_mesh(ob0, False, False, self.rotation_mode!='UV') #///
|
||||
new_ob.data.name = self.object_name
|
||||
new_ob.name = self.object_name
|
||||
else:
|
||||
@ -1781,7 +1864,7 @@ class tissue_update_tessellate_deps(Operator):
|
||||
bl_idname = "object.tissue_update_tessellate_deps"
|
||||
bl_label = "Tissue Refresh"
|
||||
bl_description = ("Fast update the tessellated mesh according to base and "
|
||||
"component changes")
|
||||
"component changes.")
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
go = False
|
||||
@ -1809,20 +1892,30 @@ class tissue_update_tessellate_deps(Operator):
|
||||
update_objects = list(reversed(update_dependencies(ob, update_objects)))
|
||||
#update_objects = list(reversed(update_dependencies(ob, [ob])))
|
||||
for o in update_objects:
|
||||
override = {
|
||||
'object': o,
|
||||
'selected_objects' : [o]
|
||||
}
|
||||
override = {'object': o, 'selected_objects': [o]}
|
||||
with context.temp_override(**override):
|
||||
if o.type == 'MESH':
|
||||
if o.tissue.tissue_type == 'TESSELLATE':
|
||||
try:
|
||||
bpy.ops.object.tissue_update_tessellate(override)
|
||||
bpy.ops.object.tissue_update_tessellate()
|
||||
except:
|
||||
self.report({'ERROR'}, "Can't Tessellate :-(")
|
||||
else:
|
||||
if o.tissue.tissue_type == 'POLYHEDRA':
|
||||
try:
|
||||
bpy.ops.object.tissue_convert_to_curve_update(override)
|
||||
bpy.ops.object.tissue_update_polyhedra()
|
||||
except:
|
||||
self.report({'ERROR'}, "Can't compute Polyhedra :-(")
|
||||
else:
|
||||
if o.tissue.tissue_type == 'TO_CURVE':
|
||||
try:
|
||||
bpy.ops.object.tissue_update_convert_to_curve()
|
||||
except:
|
||||
self.report({'ERROR'}, "Can't compute Curve :-(")
|
||||
if o.tissue.tissue_type == 'CONTOUR_CURVES':
|
||||
try:
|
||||
bpy.ops.object.tissue_update_contour_curves()
|
||||
except:
|
||||
self.report({'ERROR'}, "Can't compute Contour Curves :-(")
|
||||
|
||||
context.view_layer.objects.active = active_ob
|
||||
for o in context.view_layer.objects:
|
||||
@ -1849,13 +1942,11 @@ class tissue_update_tessellate(Operator):
|
||||
return False
|
||||
|
||||
def execute(self, context):
|
||||
|
||||
tissue_time(None,'Tissue: Tessellating...', levels=0)
|
||||
ob = context.object
|
||||
tissue_time(None,'Tissue: Tessellate of "{}"...'.format(ob.name), levels=0)
|
||||
start_time = time.time()
|
||||
|
||||
|
||||
ob = context.object
|
||||
tess_props = props_to_dict(ob)
|
||||
props = props_to_dict(ob)
|
||||
if not self.go:
|
||||
generator = ob.tissue_tessellate.generator
|
||||
component = ob.tissue_tessellate.component
|
||||
@ -1897,6 +1988,7 @@ class tissue_update_tessellate(Operator):
|
||||
bridge_edges_crease = ob.tissue_tessellate.bridge_edges_crease
|
||||
bridge_smoothness = ob.tissue_tessellate.bridge_smoothness
|
||||
frame_thickness = ob.tissue_tessellate.frame_thickness
|
||||
frame_boundary_thickness = ob.tissue_tessellate.frame_boundary_thickness
|
||||
frame_mode = ob.tissue_tessellate.frame_mode
|
||||
frame_boundary = ob.tissue_tessellate.frame_boundary
|
||||
fill_frame = ob.tissue_tessellate.fill_frame
|
||||
@ -1910,6 +2002,10 @@ class tissue_update_tessellate(Operator):
|
||||
vertex_group_thickness = ob.tissue_tessellate.vertex_group_thickness
|
||||
invert_vertex_group_thickness = ob.tissue_tessellate.invert_vertex_group_thickness
|
||||
vertex_group_thickness_factor = ob.tissue_tessellate.vertex_group_thickness_factor
|
||||
vertex_group_frame_thickness = ob.tissue_tessellate.vertex_group_frame_thickness
|
||||
invert_vertex_group_frame_thickness = ob.tissue_tessellate.invert_vertex_group_frame_thickness
|
||||
vertex_group_frame_thickness_factor = ob.tissue_tessellate.vertex_group_frame_thickness_factor
|
||||
face_weight_frame = ob.tissue_tessellate.face_weight_frame
|
||||
vertex_group_distribution = ob.tissue_tessellate.vertex_group_distribution
|
||||
invert_vertex_group_distribution = ob.tissue_tessellate.invert_vertex_group_distribution
|
||||
vertex_group_distribution_factor = ob.tissue_tessellate.vertex_group_distribution_factor
|
||||
@ -1941,7 +2037,7 @@ class tissue_update_tessellate(Operator):
|
||||
# reset messages
|
||||
ob.tissue_tessellate.warning_message_merge = ''
|
||||
|
||||
tess_props = props_to_dict(ob)
|
||||
props = props_to_dict(ob)
|
||||
|
||||
# Solve Local View issues
|
||||
local_spaces = []
|
||||
@ -2000,7 +2096,7 @@ class tissue_update_tessellate(Operator):
|
||||
components.append(ob1)
|
||||
|
||||
if ob0.type == 'META':
|
||||
base_ob = convert_object_to_mesh(ob0, False, True)
|
||||
base_ob = convert_object_to_mesh(ob0, False, True, props['rotation_mode']!='UV')
|
||||
else:
|
||||
base_ob = ob0.copy()
|
||||
base_ob.data = ob0.data
|
||||
@ -2018,7 +2114,8 @@ class tissue_update_tessellate(Operator):
|
||||
for mod in base_ob.modifiers:
|
||||
if mod.type == 'CLOTH':
|
||||
override = {'scene': scene, 'active_object': base_ob, 'point_cache': mod.point_cache}
|
||||
bpy.ops.ptcache.bake(override, bake=True)
|
||||
with context.temp_override(**override):
|
||||
bpy.ops.ptcache.bake(bake=True)
|
||||
break
|
||||
base_ob.modifiers.update()
|
||||
|
||||
@ -2047,11 +2144,11 @@ class tissue_update_tessellate(Operator):
|
||||
ob.data.clear_geometry() # Faster with heavy geometries (from previous tessellations)
|
||||
|
||||
for iter in range(iterations):
|
||||
tess_props['generator'] = base_ob
|
||||
props['generator'] = base_ob
|
||||
|
||||
if iter > 0 and len(iter_objects) == 0: break
|
||||
if iter > 0 and normals_mode in ('SHAPEKEYS','OBJECT'):
|
||||
tess_props['normals_mode'] = 'VERTS'
|
||||
props['normals_mode'] = 'VERTS'
|
||||
same_iteration = []
|
||||
matched_materials = []
|
||||
|
||||
@ -2069,7 +2166,7 @@ class tissue_update_tessellate(Operator):
|
||||
components.append(None)
|
||||
else:
|
||||
components.append(None)
|
||||
tess_props['component'] = components
|
||||
props['component'] = components
|
||||
# patch subdivisions for additional iterations
|
||||
if iter > 0 and fill_mode == 'PATCH':
|
||||
temp_mod = base_ob.modifiers.new('Tissue_Subsurf', type='SUBSURF')
|
||||
@ -2078,9 +2175,8 @@ class tissue_update_tessellate(Operator):
|
||||
# patch tessellation
|
||||
tissue_time(None,"Tessellate iteration...",levels=1)
|
||||
tt = time.time()
|
||||
same_iteration = tessellate_patch(tess_props)
|
||||
same_iteration = tessellate_patch(props)
|
||||
tissue_time(tt, "Tessellate iteration",levels=1)
|
||||
|
||||
tt = time.time()
|
||||
|
||||
# if empty or error, continue
|
||||
@ -2121,6 +2217,10 @@ class tissue_update_tessellate(Operator):
|
||||
# remove faces from last mesh
|
||||
bm = bmesh.new()
|
||||
if (fill_mode == 'PATCH' or gen_modifiers) and iter == 0:
|
||||
|
||||
if props['rotation_mode']!='UV':
|
||||
last_mesh = simple_to_mesh_mirror(base_ob)#(ob0)
|
||||
else:
|
||||
last_mesh = simple_to_mesh(base_ob)#(ob0)
|
||||
else:
|
||||
last_mesh = iter_objects[-1].data.copy()
|
||||
@ -2152,7 +2252,7 @@ class tissue_update_tessellate(Operator):
|
||||
bpy.data.objects.remove(iter_objects[-1])
|
||||
iter_objects = iter_objects[:-1]
|
||||
# set new base object for next iteration
|
||||
base_ob = convert_object_to_mesh(new_ob,True,True)
|
||||
base_ob = convert_object_to_mesh(new_ob,True,True, props['rotation_mode']!='UV')
|
||||
if iter < iterations-1: new_ob.data = base_ob.data
|
||||
# store new iteration and set transformations
|
||||
iter_objects.append(new_ob)
|
||||
@ -2208,8 +2308,8 @@ class tissue_update_tessellate(Operator):
|
||||
for o in iter_objects:
|
||||
try: bpy.data.objects.remove(o)
|
||||
except: pass
|
||||
try: bpy.data.meshes.remove(data1)
|
||||
except: pass
|
||||
#try: bpy.data.meshes.remove(data1)
|
||||
#except: pass
|
||||
context.view_layer.objects.active = ob
|
||||
ob.select_set(True)
|
||||
message = errors[new_ob]
|
||||
@ -2221,7 +2321,7 @@ class tissue_update_tessellate(Operator):
|
||||
# update data and preserve name
|
||||
if ob.type != 'MESH':
|
||||
loc, matr = ob.location, ob.matrix_world
|
||||
ob = convert_object_to_mesh(ob,False,True)
|
||||
ob = convert_object_to_mesh(ob,False,True,props['rotation_mode']!='UV')
|
||||
ob.location, ob.matrix_world = loc, matr
|
||||
data_name = ob.data.name
|
||||
old_data = ob.data
|
||||
@ -2238,10 +2338,12 @@ class tissue_update_tessellate(Operator):
|
||||
ob.data.name = data_name
|
||||
bpy.data.meshes.remove(old_data)
|
||||
|
||||
'''
|
||||
# copy vertex group
|
||||
for vg in new_ob.vertex_groups:
|
||||
if not vg.name in ob.vertex_groups.keys():
|
||||
ob.vertex_groups.new(name=vg.name)
|
||||
'''
|
||||
|
||||
selected_objects = [o for o in context.selected_objects]
|
||||
for o in selected_objects: o.select_set(False)
|
||||
@ -2254,10 +2356,7 @@ class tissue_update_tessellate(Operator):
|
||||
use_bmesh = not (bool_shapekeys and fill_mode == 'PATCH' and component_mode != 'OBJECT')
|
||||
merge_components(new_ob, ob.tissue_tessellate, use_bmesh)
|
||||
|
||||
if bool_smooth:
|
||||
bpy.ops.object.shade_smooth()
|
||||
else:
|
||||
bpy.ops.object.shade_flat()
|
||||
if bool_smooth: bpy.ops.object.shade_smooth()
|
||||
|
||||
for mesh in bpy.data.meshes:
|
||||
if not mesh.users: bpy.data.meshes.remove(mesh)
|
||||
@ -2288,7 +2387,7 @@ class tissue_update_tessellate(Operator):
|
||||
|
||||
tissue_time(tt, "Closing tessellation", levels=1)
|
||||
|
||||
tissue_time(start_time,'Tessellation of "{}"'.format(ob.name),levels=0)
|
||||
tissue_time(start_time,'Tessellate',levels=0)
|
||||
return {'FINISHED'}
|
||||
|
||||
def check(self, context):
|
||||
@ -2320,9 +2419,17 @@ class TISSUE_PT_tessellate(Panel):
|
||||
col.operator("object.dual_mesh_tessellated", text='Dual Mesh', icon='SEQ_CHROMA_SCOPE')
|
||||
col.separator()
|
||||
|
||||
op = col.operator("object.polyhedral_wireframe", icon='MESH_CUBE', text='Polyhedral Decomposition')
|
||||
op.mode = 'POLYHEDRA'
|
||||
op = col.operator("object.polyhedral_wireframe", icon='MOD_WIREFRAME', text='Polyhedral Wireframe')
|
||||
op.mode = 'WIREFRAME'
|
||||
col.separator()
|
||||
|
||||
#col.label(text="Curves:")
|
||||
col.operator("object.tissue_convert_to_curve", icon='OUTLINER_OB_CURVE', text="Convert to Curve")
|
||||
#row.operator("object.tissue_convert_to_curve_update", icon='FILE_REFRESH', text='')
|
||||
col.operator("object.tissue_weight_contour_curves_pattern", icon='FORCE_TURBULENCE', text="Contour Curves")
|
||||
|
||||
#row.operator("object.tissue_update_convert_to_curve", icon='FILE_REFRESH', text='')
|
||||
|
||||
col.separator()
|
||||
col.operator("object.tissue_update_tessellate_deps", icon='FILE_REFRESH', text='Refresh') #####
|
||||
@ -2337,7 +2444,6 @@ class TISSUE_PT_tessellate(Panel):
|
||||
col.separator()
|
||||
col.label(text="Other:")
|
||||
col.operator("object.dual_mesh", icon='SEQ_CHROMA_SCOPE')
|
||||
col.operator("object.polyhedra_wireframe", icon='MOD_WIREFRAME', text='Polyhedra Wireframe')
|
||||
col.operator("object.lattice_along_surface", icon="OUTLINER_OB_LATTICE")
|
||||
|
||||
act = context.object
|
||||
@ -2389,7 +2495,7 @@ class TISSUE_PT_tessellate_object(Panel):
|
||||
col = layout.column(align=True)
|
||||
row = col.row(align=True)
|
||||
|
||||
set_tessellate_handler(self,context)
|
||||
set_tissue_handler(self,context)
|
||||
###### set_animatable_fix_handler(self,context)
|
||||
row.operator("object.tissue_update_tessellate_deps", icon='FILE_REFRESH', text='Refresh') ####
|
||||
lock_icon = 'LOCKED' if tissue_props.bool_lock else 'UNLOCKED'
|
||||
@ -2398,8 +2504,10 @@ class TISSUE_PT_tessellate_object(Panel):
|
||||
row.prop(tissue_props, "bool_dependencies", text="", icon=deps_icon)
|
||||
row.prop(tissue_props, "bool_lock", text="", icon=lock_icon)
|
||||
col2 = row.column(align=True)
|
||||
col2.prop(tissue_props, "bool_run", text="",icon='TIME')
|
||||
col2.prop(tissue_props, "bool_run", text="", icon='TIME')
|
||||
col2.enabled = not tissue_props.bool_lock
|
||||
col2 = row.column(align=True)
|
||||
col2.operator("mesh.tissue_remove", text="", icon='X')
|
||||
#layout.use_property_split = True
|
||||
#layout.use_property_decorate = False # No animation.
|
||||
col = layout.column(align=True)
|
||||
@ -2408,14 +2516,6 @@ class TISSUE_PT_tessellate_object(Panel):
|
||||
row.prop_search(props, "generator", context.scene, "objects")
|
||||
col2 = row.column(align=True)
|
||||
col2.prop(props, "gen_modifiers", text='Use Modifiers',icon='MODIFIER')
|
||||
'''
|
||||
try:
|
||||
if not (props.generator.modifiers or props.generator.data.shape_keys):
|
||||
col2.enabled = False
|
||||
except:
|
||||
col2.enabled = False
|
||||
'''
|
||||
#col.separator()
|
||||
|
||||
layout.use_property_split = False
|
||||
# Fill
|
||||
@ -2446,7 +2546,7 @@ class TISSUE_PT_tessellate_frame(Panel):
|
||||
try:
|
||||
bool_frame = context.object.tissue_tessellate.fill_mode == 'FRAME'
|
||||
bool_tessellated = context.object.tissue_tessellate.generator != None
|
||||
return context.object.type == 'MESH' and bool_frame and bool_tessellated
|
||||
return context.object.type == 'MESH' and bool_frame and bool_tessellated and context.object.tissue.tissue_type == 'TESSELLATE'
|
||||
except:
|
||||
return False
|
||||
|
||||
@ -2455,10 +2555,28 @@ class TISSUE_PT_tessellate_frame(Panel):
|
||||
props = ob.tissue_tessellate
|
||||
layout = self.layout
|
||||
col = layout.column(align=True)
|
||||
col.prop(props, "preserve_quads")
|
||||
col.separator()
|
||||
row = col.row(align=True)
|
||||
row.prop(props, "frame_mode", expand=True)
|
||||
row = col.row(align=True)
|
||||
row.prop(props, "frame_thickness", icon='NONE', expand=True)
|
||||
|
||||
# Vertex Group Frame Thickness
|
||||
row = col.row(align=True)
|
||||
ob0 = props.generator
|
||||
row.prop_search(props, 'vertex_group_frame_thickness',
|
||||
ob0, "vertex_groups", text='')
|
||||
col2 = row.column(align=True)
|
||||
row2 = col2.row(align=True)
|
||||
row2.prop(props, "invert_vertex_group_frame_thickness", text="",
|
||||
toggle=True, icon='ARROW_LEFTRIGHT')
|
||||
row2.prop(props, "vertex_group_frame_thickness_factor")
|
||||
row2.enabled = props.vertex_group_frame_thickness in ob0.vertex_groups.keys()
|
||||
row = col.row(align=True)
|
||||
row.prop(props, "face_weight_frame")
|
||||
row.enabled = props.vertex_group_frame_thickness in ob0.vertex_groups.keys()
|
||||
|
||||
col.separator()
|
||||
row = col.row(align=True)
|
||||
row.prop(props, "fill_frame", icon='NONE')
|
||||
@ -2471,6 +2589,10 @@ class TISSUE_PT_tessellate_frame(Panel):
|
||||
col2 = row.column(align=True)
|
||||
col2.prop(props, "boundary_mat_offset", icon='NONE')
|
||||
col2.enabled = props.frame_boundary and show_frame_mat
|
||||
if props.frame_boundary:
|
||||
col.separator()
|
||||
row = col.row(align=True)
|
||||
col.prop(props, "frame_boundary_thickness", icon='NONE')
|
||||
|
||||
|
||||
class TISSUE_PT_tessellate_component(Panel):
|
||||
@ -2507,13 +2629,6 @@ class TISSUE_PT_tessellate_component(Panel):
|
||||
row.prop_search(props, "component", context.scene, "objects")
|
||||
col2 = row.column(align=True)
|
||||
col2.prop(props, "com_modifiers", text='Use Modifiers',icon='MODIFIER')
|
||||
'''
|
||||
try:
|
||||
if not (props.component.modifiers or props.component.data.shape_keys):
|
||||
col2.enabled = False
|
||||
except:
|
||||
col2.enabled = False
|
||||
'''
|
||||
elif props.component_mode == 'COLLECTION':
|
||||
col.separator()
|
||||
|
||||
@ -2653,6 +2768,7 @@ class TISSUE_PT_tessellate_rotation(Panel):
|
||||
row.separator()
|
||||
row.separator()
|
||||
row.separator()
|
||||
ob0 = props['generator']
|
||||
row.prop_search(props, 'vertex_group_rotation',
|
||||
ob0, "vertex_groups", text='Vertex Group')
|
||||
col2 = row.column(align=True)
|
||||
@ -2820,6 +2936,8 @@ class TISSUE_PT_tessellate_options(Panel):
|
||||
def draw(self, context):
|
||||
ob = context.object
|
||||
props = ob.tissue_tessellate
|
||||
ob0 = props.generator
|
||||
ob1 = props.component
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False # No animation.
|
||||
@ -3026,6 +3144,26 @@ class TISSUE_PT_tessellate_iterations(Panel):
|
||||
slider=False, toggle=False, icon_only=False, event=False,
|
||||
full_event=False, emboss=True, index=-1)
|
||||
|
||||
class tissue_remove(Operator):
|
||||
bl_idname = "mesh.tissue_remove"
|
||||
bl_label = "Tissue Remove"
|
||||
bl_description = "Remove Tissue properties"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
def draw(self, context):
|
||||
ob = context.object
|
||||
layout = self.layout
|
||||
col = layout.column(align=True)
|
||||
col.label(text='This is a destructive operation! Are you sure?', icon='ERROR')
|
||||
|
||||
def execute(self, context):
|
||||
ob = context.active_object
|
||||
ob.tissue.tissue_type = 'NONE'
|
||||
return {'FINISHED'}
|
||||
|
||||
class tissue_rotate_face_right(Operator):
|
||||
bl_idname = "mesh.tissue_rotate_face_right"
|
||||
bl_label = "Tissue Rotate Faces Right"
|
||||
@ -3190,9 +3328,8 @@ class tissue_rotate_face_left(Operator):
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
def convert_to_frame(ob, props, use_modifiers):
|
||||
new_ob = convert_object_to_mesh(ob, use_modifiers, True)
|
||||
def convert_to_frame(ob, props, use_modifiers=True):
|
||||
new_ob = convert_object_to_mesh(ob, use_modifiers, True,props['rotation_mode']!='UV')
|
||||
|
||||
# create bmesh
|
||||
bm = bmesh.new()
|
||||
@ -3202,14 +3339,18 @@ def convert_to_frame(ob, props, use_modifiers):
|
||||
bm.faces.ensure_lookup_table()
|
||||
if props['bool_selection']:
|
||||
original_faces = [f for f in bm.faces if f.select]
|
||||
elif props['preserve_quads']:
|
||||
original_faces = [f for f in bm.faces if len(f.verts)!=4]
|
||||
else:
|
||||
original_faces = list(bm.faces)
|
||||
|
||||
# detect edge loops
|
||||
|
||||
loops = []
|
||||
boundaries_mat = []
|
||||
neigh_face_center = []
|
||||
face_normals = []
|
||||
|
||||
# append boundary loops
|
||||
if props['frame_boundary']:
|
||||
#selected_edges = [e for e in bm.edges if e.select]
|
||||
@ -3223,7 +3364,7 @@ def convert_to_frame(ob, props, use_modifiers):
|
||||
face_center = [face.calc_center_median()]
|
||||
loop_normals = [face.normal]
|
||||
selected_edges = selected_edges[1:]
|
||||
if props['bool_vertex_group']:
|
||||
if props['bool_vertex_group'] or True:
|
||||
n_verts = len(new_ob.data.vertices)
|
||||
base_vg = [get_weight(vg,n_verts) for vg in new_ob.vertex_groups]
|
||||
while True:
|
||||
@ -3265,7 +3406,7 @@ def convert_to_frame(ob, props, use_modifiers):
|
||||
vert_ids = []
|
||||
|
||||
# append regular faces
|
||||
for f in original_faces:#bm.faces:
|
||||
for f in original_faces:
|
||||
loop = list(f.verts)
|
||||
loops.append(loop)
|
||||
boundaries_mat.append([f.material_index for v in loop])
|
||||
@ -3282,6 +3423,21 @@ def convert_to_frame(ob, props, use_modifiers):
|
||||
else: area = 0
|
||||
verts_area.append(area)
|
||||
|
||||
bool_weight_thick = props['vertex_group_frame_thickness'] in new_ob.vertex_groups.keys()
|
||||
if bool_weight_thick:
|
||||
vg = new_ob.vertex_groups[props['vertex_group_frame_thickness']]
|
||||
weight_frame = get_weight_numpy(vg, len(bm.verts))
|
||||
if props['invert_vertex_group_frame_thickness']:
|
||||
weight_frame = 1-weight_frame
|
||||
fact = props['vertex_group_frame_thickness_factor']
|
||||
if fact > 0:
|
||||
weight_frame = weight_frame*(1-fact) + fact
|
||||
else:
|
||||
weight_frame = np.ones((len(bm.verts)))
|
||||
|
||||
centers_neigh = []
|
||||
centers_id = []
|
||||
verts_count = len(bm.verts)-1
|
||||
for loop_index, loop in enumerate(loops):
|
||||
is_boundary = loop_index < len(neigh_face_center)
|
||||
materials = boundaries_mat[loop_index]
|
||||
@ -3305,7 +3461,11 @@ def convert_to_frame(ob, props, use_modifiers):
|
||||
normal = face_normals[loop_index][i]
|
||||
tan0 = normal.cross(vec0)
|
||||
tan1 = normal.cross(vec1)
|
||||
tangent = (tan0 + tan1).normalized()/sin(ang)*props['frame_thickness']
|
||||
if is_boundary and props['frame_boundary_thickness'] != 0:
|
||||
thickness = props['frame_boundary_thickness']
|
||||
else:
|
||||
thickness = props['frame_thickness']
|
||||
tangent = (tan0 + tan1).normalized()/sin(ang)*thickness
|
||||
tangents.append(tangent)
|
||||
|
||||
# calc correct direction for boundaries
|
||||
@ -3319,16 +3479,54 @@ def convert_to_frame(ob, props, use_modifiers):
|
||||
dir_val += tangent.dot(vert.co - surf_point)
|
||||
if dir_val > 0: mult = 1
|
||||
|
||||
if props['frame_mode'] == 'CENTER':
|
||||
# uses incenter for triangular loops and average point for generic polygons
|
||||
polygon_loop = list(dict.fromkeys(loop_ext))
|
||||
if len(polygon_loop) == 3:
|
||||
loop_center = incenter([v.co for v in polygon_loop])
|
||||
else:
|
||||
loop_center = Vector((0,0,0))
|
||||
for v in polygon_loop:
|
||||
loop_center += v.co
|
||||
loop_center /= len(polygon_loop)
|
||||
|
||||
# add vertices
|
||||
central_vertex = None
|
||||
skip_vertex = False
|
||||
for i in range(len(loop)):
|
||||
vert = loop_ext[i+1]
|
||||
if props['frame_mode'] == 'RELATIVE': area = verts_area[vert.index]
|
||||
else: area = 1
|
||||
new_co = vert.co + tangents[i] * mult * area
|
||||
if props['face_weight_frame']:
|
||||
weight_factor = [weight_frame[v.index] for v in loop_ext]
|
||||
weight_factor = sum(weight_factor)/len(weight_factor)
|
||||
else:
|
||||
weight_factor = weight_frame[vert.index]
|
||||
if props['frame_mode'] == 'CENTER':
|
||||
if is_boundary:
|
||||
new_co = vert.co + tangents[i] * mult * weight_factor
|
||||
else:
|
||||
factor = weight_factor*props['frame_thickness']
|
||||
if factor == 1 and props['frame_thickness']:
|
||||
skip_vertex = True
|
||||
else:
|
||||
new_co = vert.co + (loop_center-vert.co)*factor
|
||||
else:
|
||||
new_co = vert.co + tangents[i] * mult * area * weight_factor
|
||||
# add vertex
|
||||
new_vert = bm.verts.new(new_co)
|
||||
new_loop.append(new_vert)
|
||||
if skip_vertex:
|
||||
# prevents dublicates in the center of the loop
|
||||
if central_vertex:
|
||||
new_vert = central_vertex
|
||||
else:
|
||||
central_vertex = bm.verts.new(loop_center)
|
||||
new_vert = central_vertex
|
||||
vert_ids.append(vert.index)
|
||||
skip_vertex = False
|
||||
else:
|
||||
new_vert = bm.verts.new(new_co)
|
||||
vert_ids.append(vert.index)
|
||||
new_loop.append(new_vert)
|
||||
new_loop.append(new_loop[0])
|
||||
|
||||
# add faces
|
||||
@ -3340,31 +3538,40 @@ def convert_to_frame(ob, props, use_modifiers):
|
||||
v3 = new_loop[i]
|
||||
face_verts = [v1,v0,v3,v2]
|
||||
if mult == -1: face_verts = [v0,v1,v2,v3]
|
||||
face_verts = list(dict.fromkeys(face_verts))
|
||||
new_face = bm.faces.new(face_verts)
|
||||
new_face.material_index = materials[i+1]
|
||||
new_face.select = True
|
||||
new_faces.append(new_face)
|
||||
# fill frame
|
||||
if props['fill_frame'] and not is_boundary:
|
||||
center_neigh = []
|
||||
n_verts = len(new_loop)-1
|
||||
loop_center = Vector((0,0,0))
|
||||
for v in new_loop[1:]: loop_center += v.co
|
||||
for v in new_loop[1:]:
|
||||
loop_center += v.co
|
||||
verts_count += 1
|
||||
center_neigh.append(verts_count)
|
||||
centers_neigh.append(center_neigh)
|
||||
loop_center /= n_verts
|
||||
center = bm.verts.new(loop_center)
|
||||
verts_count += 1
|
||||
vert_ids.append(center.index)
|
||||
centers_id.append(verts_count)
|
||||
for i in range(n_verts):
|
||||
v0 = new_loop[i+1]
|
||||
v1 = new_loop[i]
|
||||
face_verts = [v1,v0,center]
|
||||
face_verts = list(dict.fromkeys(face_verts))
|
||||
if len(face_verts) < 3: continue
|
||||
new_face = bm.faces.new(face_verts)
|
||||
new_face.material_index = materials[i] + props['fill_frame_mat']
|
||||
new_face.select = True
|
||||
new_faces.append(new_face)
|
||||
#bpy.ops.object.mode_set(mode='OBJECT')
|
||||
#for f in bm.faces: f.select_set(f not in new_faces)
|
||||
for f in original_faces: bm.faces.remove(f)
|
||||
bm.to_mesh(new_ob.data)
|
||||
# propagate vertex groups
|
||||
if props['bool_vertex_group']:
|
||||
if props['bool_vertex_group'] or bool_weight_thick:
|
||||
base_vg = []
|
||||
for vg in new_ob.vertex_groups:
|
||||
vertex_group = []
|
||||
@ -3378,6 +3585,13 @@ def convert_to_frame(ob, props, use_modifiers):
|
||||
for vg_id, vg in enumerate(new_ob.vertex_groups):
|
||||
for ii, jj in zip(vert_ids, new_vert_ids):
|
||||
vg.add([jj], base_vg[vg_id][ii], 'REPLACE')
|
||||
# set weight for the central points
|
||||
if props['fill_frame']:
|
||||
for cn, ii in zip(centers_neigh, centers_id):
|
||||
cw = [vg.weight(cni) for cni in cn]
|
||||
cw = sum(cw)/len(cw)
|
||||
vg.add([ii], cw, 'REPLACE')
|
||||
|
||||
new_ob.data.update()
|
||||
bm.free()
|
||||
return new_ob
|
||||
@ -3386,7 +3600,7 @@ def reduce_to_quads(ob, props):
|
||||
'''
|
||||
Convert an input object to a mesh with polygons that have maximum 4 vertices
|
||||
'''
|
||||
new_ob = convert_object_to_mesh(ob, props['gen_modifiers'], True)
|
||||
new_ob = convert_object_to_mesh(ob, props['gen_modifiers'], True, props['rotation_mode']!='UV')
|
||||
me = new_ob.data
|
||||
|
||||
# Check if there are polygons with more than 4 sides
|
||||
@ -3445,7 +3659,7 @@ def reduce_to_quads(ob, props):
|
||||
return new_ob
|
||||
|
||||
def convert_to_fan(ob, props, add_id_layer=False):
|
||||
new_ob = convert_object_to_mesh(ob, props['gen_modifiers'], True)
|
||||
new_ob = convert_object_to_mesh(ob, props['gen_modifiers'], True, props['rotation_mode']!='UV')
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(new_ob.data)
|
||||
if add_id_layer:
|
||||
@ -3464,7 +3678,7 @@ def convert_to_fan(ob, props, add_id_layer=False):
|
||||
return new_ob
|
||||
|
||||
def convert_to_triangles(ob, props):
|
||||
new_ob = convert_object_to_mesh(ob, props['gen_modifiers'], True)
|
||||
new_ob = convert_object_to_mesh(ob, props['gen_modifiers'], True, props['rotation_mode']!='UV')
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(new_ob.data)
|
||||
bmesh.ops.triangulate(bm, faces=bm.faces, quad_method='FIXED', ngon_method='BEAUTY')
|
||||
@ -3588,6 +3802,8 @@ def merge_components(ob, props, use_bmesh):
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
except: pass
|
||||
else:
|
||||
if(props.bridge_edges_crease>0 or props.open_edges_crease>0):
|
||||
ob.data.edge_creases_ensure()
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(ob.data.copy())
|
||||
if props.merge_open_edges_only:
|
||||
@ -3602,14 +3818,17 @@ def merge_components(ob, props, use_bmesh):
|
||||
if props.close_mesh != 'NONE':
|
||||
bm.edges.ensure_lookup_table()
|
||||
# set crease
|
||||
crease_layer = bm.edges.layers.float.new("crease_edge")
|
||||
crease_layer = bm.edges.layers.float.new('crease_edge')
|
||||
boundary_edges = [e for e in bm.edges if e.is_boundary or e.is_wire]
|
||||
n_materials = len(ob.material_slots)-1
|
||||
if props.close_mesh == 'BRIDGE':
|
||||
try:
|
||||
for e in boundary_edges:
|
||||
e[crease_layer] = props.bridge_edges_crease
|
||||
closed = bmesh.ops.bridge_loops(bm, edges=boundary_edges, use_pairs=True)
|
||||
for f in closed['faces']: f.material_index += props.bridge_material_offset
|
||||
if n_materials >= 0:
|
||||
for f in closed['faces']:
|
||||
f.material_index = min(f.material_index + props.bridge_material_offset, n_materials)
|
||||
except:
|
||||
bm.to_mesh(ob.data)
|
||||
return 'bridge_error'
|
||||
@ -3617,7 +3836,9 @@ def merge_components(ob, props, use_bmesh):
|
||||
for e in boundary_edges:
|
||||
e[crease_layer] = props.open_edges_crease
|
||||
closed = bmesh.ops.holes_fill(bm, edges=boundary_edges)
|
||||
for f in closed['faces']: f.material_index += props.cap_material_offset
|
||||
if n_materials >= 0:
|
||||
for f in closed['faces']:
|
||||
f.material_index = min(f.material_index + props.cap_material_offset, n_materials)
|
||||
elif props.close_mesh == 'BRIDGE_CAP':
|
||||
# BRIDGE
|
||||
dvert_lay = bm.verts.layers.deform.active
|
||||
@ -3630,7 +3851,9 @@ def merge_components(ob, props, use_bmesh):
|
||||
for e in bridge_edges:
|
||||
e[crease_layer] = props.bridge_edges_crease
|
||||
closed = bmesh.ops.bridge_loops(bm, edges=bridge_edges, use_pairs=True)
|
||||
for f in closed['faces']: f.material_index += props.bridge_material_offset
|
||||
if n_materials >= 0:
|
||||
for f in closed['faces']:
|
||||
f.material_index = min(f.material_index + props.bridge_material_offset, n_materials)
|
||||
boundary_edges = [e for e in bm.edges if e.is_boundary]
|
||||
except: pass
|
||||
# CAP
|
||||
@ -3643,7 +3866,9 @@ def merge_components(ob, props, use_bmesh):
|
||||
for e in cap_edges:
|
||||
e[crease_layer] = props.open_edges_crease
|
||||
closed = bmesh.ops.holes_fill(bm, edges=cap_edges)
|
||||
for f in closed['faces']: f.material_index += props.cap_material_offset
|
||||
if n_materials >= 0:
|
||||
for f in closed['faces']:
|
||||
f.material_index = min(f.material_index + props.bridge_material_offset, n_materials)
|
||||
except: pass
|
||||
bm.to_mesh(ob.data)
|
||||
|
||||
@ -3676,13 +3901,13 @@ class tissue_render_animation(Operator):
|
||||
self.report({'ERROR'}, message)
|
||||
return {'CANCELLED'}
|
||||
'''
|
||||
remove_tessellate_handler()
|
||||
remove_tissue_handler()
|
||||
scene = context.scene
|
||||
if event.type == 'ESC' or scene.frame_current >= scene.frame_end:
|
||||
scene.render.filepath = self.path
|
||||
# set again the handler
|
||||
blender_handlers = bpy.app.handlers.frame_change_post
|
||||
blender_handlers.append(anim_tessellate)
|
||||
blender_handlers.append(anim_tissue)
|
||||
blender_handlers.append(reaction_diffusion_scene)
|
||||
context.window_manager.event_timer_remove(self.timer)
|
||||
if event.type == 'ESC':
|
||||
@ -3705,7 +3930,7 @@ class tissue_render_animation(Operator):
|
||||
|
||||
scene = context.scene
|
||||
if self.start:
|
||||
remove_tessellate_handler()
|
||||
remove_tissue_handler()
|
||||
reaction_diffusion_remove_handler(self, context)
|
||||
scene = context.scene
|
||||
scene.frame_current = scene.frame_start
|
||||
@ -3715,7 +3940,7 @@ class tissue_render_animation(Operator):
|
||||
self.start = False
|
||||
else:
|
||||
scene.frame_current += scene.frame_step
|
||||
anim_tessellate(scene)
|
||||
anim_tissue(scene)
|
||||
reaction_diffusion_scene(scene)
|
||||
scene.render.filepath = "{}{:04d}".format(self.path,scene.frame_current)
|
||||
bpy.ops.render.render(write_still=True)
|
||||
@ -3729,7 +3954,7 @@ def offset_boundary_materials(bm, boundary_mat_offset=0, boundary_variable_offse
|
||||
bound_verts_value = [0]*len(bm.faces)
|
||||
bound_edges_value = [0]*len(bm.faces)
|
||||
shift_faces = [0]*len(bm.faces)
|
||||
# store boundaries information
|
||||
# store boundaries informations
|
||||
for v in bm.verts:
|
||||
if v.is_boundary:
|
||||
for f in v.link_faces:
|
||||
|
694
mesh_tissue/texture_reaction_diffusion.py
Normal file
694
mesh_tissue/texture_reaction_diffusion.py
Normal file
@ -0,0 +1,694 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
#-------------------------- COLORS / GROUPS EXCHANGER -------------------------#
|
||||
# #
|
||||
# Vertex Color to Vertex Group allow you to convert colors channles to weight #
|
||||
# maps. #
|
||||
# The main purpose is to use vertex colors to store information when importing #
|
||||
# files from other softwares. The script works with the active vertex color #
|
||||
# slot. #
|
||||
# For use the command "Vertex Clors to Vertex Groups" use the search bar #
|
||||
# (space bar). #
|
||||
# #
|
||||
# (c) Alessandro Zomparelli #
|
||||
# (2017) #
|
||||
# #
|
||||
# http://www.co-de-it.com/ #
|
||||
# #
|
||||
################################################################################
|
||||
|
||||
import bpy, bmesh, os
|
||||
import numpy as np
|
||||
import math, timeit, time
|
||||
from math import pi
|
||||
from statistics import mean, stdev
|
||||
from mathutils import Vector
|
||||
from mathutils.kdtree import KDTree
|
||||
from numpy import *
|
||||
try: from .numba_functions import run_tex_rd, run_tex_rd_ani
|
||||
except: pass
|
||||
#from .numba_functions import integrate_field
|
||||
#from .numba_functions import numba_reaction_diffusion
|
||||
try: import numexpr as ne
|
||||
except: pass
|
||||
|
||||
# Reaction-Diffusion cache
|
||||
from pathlib import Path
|
||||
import random as rnd
|
||||
import string
|
||||
|
||||
from bpy.types import (
|
||||
Operator,
|
||||
Panel,
|
||||
PropertyGroup,
|
||||
)
|
||||
|
||||
from bpy.props import (
|
||||
BoolProperty,
|
||||
EnumProperty,
|
||||
FloatProperty,
|
||||
IntProperty,
|
||||
StringProperty,
|
||||
FloatVectorProperty,
|
||||
IntVectorProperty
|
||||
)
|
||||
|
||||
from .utils import *
|
||||
|
||||
|
||||
def tex_reaction_diffusion_add_handler(self, context):
|
||||
# remove existing handlers
|
||||
tex_reaction_diffusion_remove_handler(self, context)
|
||||
# add new handler
|
||||
bpy.app.handlers.frame_change_post.append(tex_rd_scene)
|
||||
|
||||
|
||||
def tex_reaction_diffusion_remove_handler(self, context):
|
||||
# remove existing handlers
|
||||
old_handlers = []
|
||||
for h in bpy.app.handlers.frame_change_post:
|
||||
if "tex_rd" in str(h):
|
||||
old_handlers.append(h)
|
||||
for h in old_handlers: bpy.app.handlers.frame_change_post.remove(h)
|
||||
|
||||
|
||||
class tex_reaction_diffusion_prop(PropertyGroup):
|
||||
run : BoolProperty(default=False, update = tex_reaction_diffusion_add_handler,
|
||||
description='Compute a new iteration on frame changes. Currently is not working during Render Animation')
|
||||
|
||||
res_x : IntProperty(
|
||||
name="Resolution X", default=512, min=2, soft_max=1000,
|
||||
description="Resolution of the simulation")
|
||||
|
||||
res_y : IntProperty(
|
||||
name="Resolution Y", default=512, min=2, soft_max=1000,
|
||||
description="Resolution of the simulation")
|
||||
|
||||
time_steps : IntProperty(
|
||||
name="Steps", default=10, min=0, soft_max=50,
|
||||
description="Number of Steps")
|
||||
|
||||
dt : FloatProperty(
|
||||
name="dt", default=1, min=0, soft_max=0.2,
|
||||
description="Time Step")
|
||||
|
||||
diff_a : FloatProperty(
|
||||
name="Diff A", default=0.14, min=0, soft_max=2, precision=3,
|
||||
description="Diffusion A")
|
||||
|
||||
diff_b : FloatProperty(
|
||||
name="Diff B", default=0.07, min=0, soft_max=2, precision=3,
|
||||
description="Diffusion B")
|
||||
|
||||
f : FloatProperty(
|
||||
name="f", default=0.055, soft_min=0.01, soft_max=0.06, precision=4, step=0.05,
|
||||
description="Feed Rate")
|
||||
|
||||
k : FloatProperty(
|
||||
name="k", default=0.062, soft_min=0.035, soft_max=0.065, precision=4, step=0.05,
|
||||
description="Kill Rate")
|
||||
|
||||
diff_mult : FloatProperty(
|
||||
name="Scale", default=1, min=0, soft_max=1, max=10, precision=2,
|
||||
description="Multiplier for the diffusion of both substances")
|
||||
|
||||
anisotropy : FloatProperty(
|
||||
name="Anisotropy", default=0.5, min=0, max=1, precision=2,
|
||||
description="Influence of the Vector Field")
|
||||
|
||||
img_vector_field : StringProperty(
|
||||
name="Vector Field", default='',
|
||||
description="Image used for the Vector Field. RGB to XY")
|
||||
|
||||
img_a : StringProperty(
|
||||
name="A", default='',
|
||||
description="Image used for the chemical A")
|
||||
|
||||
img_b : StringProperty(
|
||||
name="B", default='',
|
||||
description="Image used for the chemical B")
|
||||
|
||||
img_diff_a : StringProperty(
|
||||
name="Diff A", default='',
|
||||
description="Image used for A diffusion")
|
||||
|
||||
img_diff_b : StringProperty(
|
||||
name="Diff B", default='',
|
||||
description="Image used for B diffusion")
|
||||
|
||||
img_scale : StringProperty(
|
||||
name="Scale", default='',
|
||||
description="Image used for Scale value")
|
||||
|
||||
img_f : StringProperty(
|
||||
name="f", default='',
|
||||
description="Image used for Feed value (f)")
|
||||
|
||||
img_k : StringProperty(
|
||||
name="k", default='',
|
||||
description="Image used for Kill value (k)")
|
||||
|
||||
img_brush : StringProperty(
|
||||
name="Brush", default='',
|
||||
description="Image used for adding/removing B")
|
||||
|
||||
invert_img_diff_a : BoolProperty(default=False,
|
||||
description='Invert the value of the Vertex Group Diff A')
|
||||
|
||||
invert_img_diff_b : BoolProperty(default=False,
|
||||
description='Invert the value of the Vertex Group Diff B')
|
||||
|
||||
invert_img_scale : BoolProperty(default=False,
|
||||
description='Invert the value of the Vertex Group Scale')
|
||||
|
||||
invert_img_f : BoolProperty(default=False,
|
||||
description='Invert the value of the Vertex Group f')
|
||||
|
||||
invert_img_k : BoolProperty(default=False,
|
||||
description='Invert the value of the Vertex Group k')
|
||||
|
||||
invert_img_vector_field : BoolProperty(default=False,
|
||||
description='Use the perpendicular direction')
|
||||
|
||||
min_diff_a : FloatProperty(
|
||||
name="Min Diff A", default=0.1, min=0, soft_max=2, precision=3,
|
||||
description="Min Diff A")
|
||||
|
||||
max_diff_a : FloatProperty(
|
||||
name="Max Diff A", default=0.1, min=0, soft_max=2, precision=3,
|
||||
description="Max Diff A")
|
||||
|
||||
min_diff_b : FloatProperty(
|
||||
name="Min Diff B", default=0.1, min=0, soft_max=2, precision=3,
|
||||
description="Min Diff B")
|
||||
|
||||
max_diff_b : FloatProperty(
|
||||
name="Max Diff B", default=0.1, min=0, soft_max=2, precision=3,
|
||||
description="Max Diff B")
|
||||
|
||||
min_scale : FloatProperty(
|
||||
name="Scale", default=0.35, min=0, soft_max=1, max=10, precision=2,
|
||||
description="Min Scale Value")
|
||||
|
||||
max_scale : FloatProperty(
|
||||
name="Scale", default=1, min=0, soft_max=1, max=10, precision=2,
|
||||
description="Max Scale value")
|
||||
|
||||
min_f : FloatProperty(
|
||||
name="Min f", default=0.02, min=0, soft_min=0.01, soft_max=0.06, max=0.2, precision=4, step=0.05,
|
||||
description="Min Feed Rate")
|
||||
|
||||
max_f : FloatProperty(
|
||||
name="Max f", default=0.055, min=0, soft_min=0.01, soft_max=0.06, max=0.2, precision=4, step=0.05,
|
||||
description="Max Feed Rate")
|
||||
|
||||
min_k : FloatProperty(
|
||||
name="Min k", default=0.035, min=0, soft_min=0.035, soft_max=0.065, max=0.2, precision=4, step=0.05,
|
||||
description="Min Kill Rate")
|
||||
|
||||
max_k : FloatProperty(
|
||||
name="Max k", default=0.062, min=0, soft_min=0.035, soft_max=0.065, max=0.2, precision=4, step=0.05,
|
||||
description="Max Kill Rate")
|
||||
|
||||
brush_mult : FloatProperty(
|
||||
name="Mult", default=0.5, min=-1, max=1, precision=3, step=0.05,
|
||||
description="Multiplier for brush value")
|
||||
|
||||
bool_cache : BoolProperty(
|
||||
name="Use Cache", default=False,
|
||||
description="Read modifiers affect the vertex groups")
|
||||
|
||||
cache_frame_start : IntProperty(
|
||||
name="Start", default=1,
|
||||
description="Frame on which the simulation starts")
|
||||
|
||||
cache_frame_end : IntProperty(
|
||||
name="End", default=250,
|
||||
description="Frame on which the simulation ends")
|
||||
|
||||
cache_dir : StringProperty(
|
||||
name="Cache directory", default="", subtype='FILE_PATH',
|
||||
description = 'Directory that contains Reaction-Diffusion cache files'
|
||||
)
|
||||
|
||||
normalize : BoolProperty(
|
||||
name="Normalize values", default=False,
|
||||
description="Normalize values from 0 to 1")
|
||||
|
||||
def tex_rd_scene(scene, bake=False):
|
||||
for ob in bpy.context.scene.objects:
|
||||
if ob.tex_reaction_diffusion_settings.run:
|
||||
tex_reaction_diffusion_def(ob)
|
||||
|
||||
def tex_reaction_diffusion_def(ob, bake=False):
|
||||
try:
|
||||
props = ob.tex_reaction_diffusion_settings
|
||||
except:
|
||||
return
|
||||
scene = bpy.context.scene
|
||||
print("Texture Reaction Diffusion: " + str(scene.frame_current))
|
||||
start_time = timeit.default_timer()
|
||||
img_a = bpy.data.images[props.img_a]
|
||||
img_b = bpy.data.images[props.img_b]
|
||||
diff_a = props.diff_a
|
||||
diff_b = props.diff_b
|
||||
diff_a_min = props.min_diff_a
|
||||
diff_a_max = props.max_diff_a
|
||||
diff_b_min = props.min_diff_b
|
||||
diff_b_max = props.max_diff_b
|
||||
f_min = props.min_f
|
||||
f_max = props.max_f
|
||||
k_min = props.min_k
|
||||
k_max = props.max_k
|
||||
ani = props.anisotropy
|
||||
dt = props.dt
|
||||
time_steps = props.time_steps
|
||||
res_x = props.res_x #int(img_b.size[0])
|
||||
res_y = props.res_y #int(img_b.size[1])
|
||||
|
||||
min_scale = props.min_scale
|
||||
max_scale = props.max_scale
|
||||
|
||||
images = bpy.data.images.keys()
|
||||
rd_images = [img_a, img_b]
|
||||
img_diff_a = None
|
||||
img_diff_b = None
|
||||
img_vector_field = None
|
||||
img_f = None
|
||||
img_k = None
|
||||
img_scale = None
|
||||
img_brush = None
|
||||
if props.img_vector_field in images:
|
||||
img_vector_field = bpy.data.images[props.img_vector_field]
|
||||
rd_images.append(img_vector_field)
|
||||
if props.img_diff_a in images:
|
||||
img_diff_a = bpy.data.images[props.img_diff_a]
|
||||
rd_images.append(img_diff_a)
|
||||
if props.img_diff_b in images:
|
||||
img_diff_b = bpy.data.images[props.img_diff_b]
|
||||
rd_images.append(img_diff_b)
|
||||
if props.img_f in images:
|
||||
img_f = bpy.data.images[props.img_f]
|
||||
rd_images.append(img_f)
|
||||
if props.img_k in images:
|
||||
img_k = bpy.data.images[props.img_k]
|
||||
rd_images.append(img_k)
|
||||
if props.img_scale in images:
|
||||
img_scale = bpy.data.images[props.img_scale]
|
||||
rd_images.append(img_scale)
|
||||
if props.img_brush in images:
|
||||
img_brush = bpy.data.images[props.img_brush]
|
||||
rd_images.append(img_brush)
|
||||
for im in rd_images:
|
||||
im.scale(res_x ,res_y)
|
||||
im.pixels.update()
|
||||
nx = res_y
|
||||
ny = res_x
|
||||
|
||||
a_px = np.float32(np.zeros(nx*ny*4))
|
||||
img_a.pixels.foreach_get(a_px)
|
||||
b_px = np.float32(np.zeros(nx*ny*4))
|
||||
img_b.pixels.foreach_get(b_px)
|
||||
if img_vector_field:
|
||||
vf_px = np.float32(np.zeros(nx*ny*4))
|
||||
img_vector_field.pixels.foreach_get(vf_px)
|
||||
vf_px = np.array(vf_px).reshape((-1,4))
|
||||
vf_x = vf_px[:,1]*2-1
|
||||
vf_x = vf_x.reshape((nx,ny))
|
||||
vf_y = vf_px[:,0]*2-1
|
||||
vf_y = vf_y.reshape((nx,ny))
|
||||
|
||||
# original field
|
||||
vf_x_ = sqrt(2)/2*vf_x
|
||||
vf_y_ = sqrt(2)/2*vf_y
|
||||
vf_xy1_ = abs(vf_x_ + vf_y_)
|
||||
vf_xy2_ = abs(vf_x_ - vf_y_)
|
||||
vf_xy1 = (vf_xy1_*ani + (1-ani))*sqrt(2)/2
|
||||
vf_xy2 = (vf_xy2_*ani + (1-ani))*sqrt(2)/2
|
||||
vf_x_ = abs(vf_x)*ani + (1-ani)
|
||||
vf_y_ = abs(vf_y)*ani + (1-ani)
|
||||
vf1 = np.concatenate((vf_x_[np.newaxis,:,:], vf_y_[np.newaxis,:,:], vf_xy1[np.newaxis,:,:], vf_xy2[np.newaxis,:,:]), axis=0)
|
||||
|
||||
# perpendicular field
|
||||
vf_x, vf_y = -vf_y, vf_x
|
||||
vf_x_ = sqrt(2)/2*vf_x
|
||||
vf_y_ = sqrt(2)/2*vf_y
|
||||
vf_xy1_ = abs(vf_x_ + vf_y_)
|
||||
vf_xy2_ = abs(vf_x_ - vf_y_)
|
||||
vf_xy1 = (vf_xy1_*ani + (1-ani))*sqrt(2)/2
|
||||
vf_xy2 = (vf_xy2_*ani + (1-ani))*sqrt(2)/2
|
||||
vf_x = abs(vf_x)*ani + (1-ani)
|
||||
vf_y = abs(vf_y)*ani + (1-ani)
|
||||
vf2 = np.concatenate((vf_x[np.newaxis,:,:], vf_y[np.newaxis,:,:], vf_xy1[np.newaxis,:,:], vf_xy2[np.newaxis,:,:]), axis=0)
|
||||
if props.invert_img_vector_field:
|
||||
vf1, vf2 = vf2, vf1
|
||||
else:
|
||||
vf = np.ones((1,nx,ny))
|
||||
vf_diag = np.ones((1,nx,ny))*sqrt(2)/2
|
||||
vf1 = np.concatenate((vf, vf, vf_diag, vf_diag), axis=0)
|
||||
vf2 = vf1
|
||||
|
||||
|
||||
if img_diff_a:
|
||||
diff_a = np_remap_image_values(img_diff_a, channel=0, min=diff_a_min, max=diff_a_max, invert=props.invert_img_diff_a)
|
||||
else:
|
||||
diff_a = np.ones((nx,ny))*props.diff_a
|
||||
|
||||
if img_diff_b:
|
||||
diff_b = np_remap_image_values(img_diff_b, channel=0, min=diff_b_min, max=diff_b_max, invert=props.invert_img_diff_b)
|
||||
else:
|
||||
diff_b = np.ones((nx,ny))*props.diff_b
|
||||
|
||||
if img_scale:
|
||||
scale = np_remap_image_values(img_scale, channel=0, min=min_scale, max=max_scale, invert=props.invert_img_scale)
|
||||
diff_a *= scale
|
||||
diff_b *= scale
|
||||
else:
|
||||
diff_a *= props.diff_mult
|
||||
diff_b *= props.diff_mult
|
||||
|
||||
if img_f:
|
||||
f = np_remap_image_values(img_f, channel=0, min=f_min, max=f_max, invert=props.invert_img_f)
|
||||
else:
|
||||
f = np.ones((nx,ny))*props.f
|
||||
|
||||
if img_k:
|
||||
k = np_remap_image_values(img_k, channel=0, min=k_min, max=k_max, invert=props.invert_img_k)
|
||||
else:
|
||||
k = np.ones((nx,ny))*props.k
|
||||
|
||||
if img_brush:
|
||||
brush = np_remap_image_values(img_brush)*props.brush_mult
|
||||
else:
|
||||
brush = np.zeros((nx,ny))
|
||||
|
||||
print("Load images: " + str(timeit.default_timer() - start_time) + " sec")
|
||||
|
||||
start_time = timeit.default_timer()
|
||||
|
||||
a_px = np.array(a_px).reshape((-1,4))
|
||||
a = a_px[:,0]
|
||||
a = a.reshape((nx,ny))
|
||||
lap_a = np.zeros((nx,ny))
|
||||
|
||||
b_px = np.array(b_px).reshape((-1,4))
|
||||
b = b_px[:,0]
|
||||
b = b.reshape((nx,ny))
|
||||
lap_b = np.zeros((nx,ny))
|
||||
|
||||
print("Reshape data time: " + str(timeit.default_timer() - start_time) + " sec")
|
||||
|
||||
start_time = timeit.default_timer()
|
||||
run_tex_rd_ani(a, b, lap_a, lap_b, diff_a, diff_b, f, k, dt, time_steps, vf1, vf2, brush)
|
||||
print("Simulation time: " + str(timeit.default_timer() - start_time) + " sec")
|
||||
|
||||
start_time = timeit.default_timer()
|
||||
np.clip(a,0,1,out=a)
|
||||
np.clip(b,0,1,out=b)
|
||||
a = a.flatten()
|
||||
b = b.flatten()
|
||||
a_px[:,0] = a
|
||||
a_px[:,1] = a
|
||||
a_px[:,2] = a
|
||||
b_px[:,0] = b
|
||||
b_px[:,1] = b
|
||||
b_px[:,2] = b
|
||||
img_a.pixels.foreach_set(np.float32(a_px.flatten()))
|
||||
img_b.pixels.foreach_set(np.float32(b_px.flatten()))
|
||||
img_a.pixels.update()
|
||||
img_b.pixels.update()
|
||||
img_a.update()
|
||||
img_b.update()
|
||||
print("Stored Images: " + str(timeit.default_timer() - start_time) + " sec")
|
||||
|
||||
class reset_tex_reaction_diffusion(Operator):
|
||||
bl_idname = "object.reset_tex_reaction_diffusion"
|
||||
bl_label = "Reset Texture Reaction Diffusion"
|
||||
bl_description = ("Run a Reaction-Diffusion based on images: A and B")
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
run : BoolProperty(
|
||||
name="Run Reaction-Diffusion", default=True, description="Compute a new iteration on frame changes")
|
||||
|
||||
time_steps : IntProperty(
|
||||
name="Steps", default=10, min=0, soft_max=50,
|
||||
description="Number of Steps")
|
||||
|
||||
dt : FloatProperty(
|
||||
name="dt", default=1, min=0, soft_max=0.2,
|
||||
description="Time Step")
|
||||
|
||||
diff_a : FloatProperty(
|
||||
name="Diff A", default=0.14, min=0, soft_max=2,
|
||||
description="Diffusion A")
|
||||
|
||||
diff_b : FloatProperty(
|
||||
name="Diff B", default=0.07, min=0, soft_max=2,
|
||||
description="Diffusion B")
|
||||
|
||||
f : FloatProperty(
|
||||
name="f", default=0.055, min=0, soft_min=0.01, soft_max=0.06, max=0.1, precision=4,
|
||||
description="Feed Rate")
|
||||
|
||||
k : FloatProperty(
|
||||
name="k", default=0.062, min=0, soft_min=0.035, soft_max=0.065, max=0.1, precision=4,
|
||||
description="Kill Rate")
|
||||
|
||||
def execute(self, context):
|
||||
props = context.object.tex_reaction_diffusion_settings
|
||||
props.dt = self.dt
|
||||
props.time_steps = self.time_steps
|
||||
props.f = self.f
|
||||
props.k = self.k
|
||||
props.diff_a = self.diff_a
|
||||
props.diff_b = self.diff_b
|
||||
res_x = props.res_x
|
||||
res_y = props.res_y
|
||||
img_a = bpy.data.images[props.img_a]
|
||||
img_b = bpy.data.images[props.img_b]
|
||||
img_a.scale(width=res_x, height=res_y)
|
||||
img_b.scale(width=res_x, height=res_y)
|
||||
img_a.pixels.foreach_set([1]*res_x*res_y*4)
|
||||
img_b.pixels.foreach_set([0,0,0,1]*res_x*res_y)
|
||||
img_a.pixels.update()
|
||||
img_b.pixels.update()
|
||||
img_a.update()
|
||||
img_b.update()
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
class start_tex_reaction_diffusion(Operator):
|
||||
bl_idname = "object.start_tex_reaction_diffusion"
|
||||
bl_label = "Start Texture Reaction Diffusion"
|
||||
bl_description = ("Run a Reaction-Diffusion based on images: A and B")
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
#res_x : IntProperty(
|
||||
# name="Resolution X", default=512, min=2, soft_max=1000,
|
||||
# description="Resolution of the simulation")
|
||||
#res_y : IntProperty(
|
||||
# name="Resolution Y", default=512, min=2, soft_max=1000,
|
||||
# description="Resolution of the simulation")
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return True
|
||||
|
||||
#def invoke(self, context, event):
|
||||
# return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
def execute(self, context):
|
||||
tex_reaction_diffusion_add_handler(self, context)
|
||||
set_animatable_fix_handler(self, context)
|
||||
|
||||
ob = context.object
|
||||
props = ob.tex_reaction_diffusion_settings
|
||||
if props.img_a in bpy.data.images.keys():
|
||||
img_a = bpy.data.images[props.img_a]
|
||||
img_a.scale(props.res_x, props.res_y)
|
||||
else:
|
||||
img_a = bpy.data.images.new(name="A", width=props.res_x, height=props.res_y)
|
||||
if props.img_b in bpy.data.images.keys():
|
||||
img_b = bpy.data.images[props.img_b]
|
||||
img_b.scale(props.res_x, props.res_y)
|
||||
else:
|
||||
img_b = bpy.data.images.new(name="B", width=props.res_x, height=props.res_y)
|
||||
props.run = True
|
||||
#props.res_x = self.res_x
|
||||
#props.res_y = self.res_y
|
||||
props.img_a = img_a.name
|
||||
props.img_b = img_b.name
|
||||
|
||||
#props.run = self.run
|
||||
#props.dt = self.dt
|
||||
#props.time_steps = self.time_steps
|
||||
#props.f = self.f
|
||||
#props.k = self.k
|
||||
#props.diff_a = self.diff_a
|
||||
#props.diff_b = self.diff_b
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class TISSUE_PT_tex_reaction_diffusion(Panel):
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "object"
|
||||
bl_label = "Tissue Texture Reaction-Diffusion"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
#@classmethod
|
||||
#def poll(cls, context):
|
||||
# return True
|
||||
|
||||
def draw(self, context):
|
||||
tex_reaction_diffusion_add_handler(self, context)
|
||||
ob = bpy.context.object
|
||||
props = ob.tex_reaction_diffusion_settings
|
||||
img_a = props.img_a
|
||||
img_b = props.img_b
|
||||
layout = self.layout
|
||||
col = layout.column(align=True)
|
||||
row = col.row(align=True)
|
||||
if not (img_a and img_b in bpy.data.images):
|
||||
row.operator("object.start_tex_reaction_diffusion",
|
||||
icon="EXPERIMENTAL")
|
||||
col = layout.column(align=True)
|
||||
row = col.row(align=True)
|
||||
row.prop(props, 'res_x')
|
||||
row.prop(props, 'res_y')
|
||||
col.separator()
|
||||
col.prop_search(props, 'img_a', bpy.data, "images")
|
||||
col.prop_search(props, 'img_b', bpy.data, "images")
|
||||
else:
|
||||
row.operator("object.reset_tex_reaction_diffusion",
|
||||
icon="EXPERIMENTAL")
|
||||
row = col.row(align=True)
|
||||
row.prop(props, "run", text="Run Reaction-Diffusion")
|
||||
col = layout.column(align=True)
|
||||
row = col.row(align=True)
|
||||
row.prop(props, 'res_x')
|
||||
row.prop(props, 'res_y')
|
||||
col.separator()
|
||||
col.prop_search(props, 'img_a', bpy.data, "images")
|
||||
col.prop_search(props, 'img_b', bpy.data, "images")
|
||||
col.separator()
|
||||
row = col.row(align=True)
|
||||
row.prop(props, "time_steps")
|
||||
row.prop(props, "dt")
|
||||
row.enabled = not props.bool_cache
|
||||
col.separator()
|
||||
row = col.row(align=True)
|
||||
col1 = row.column(align=True)
|
||||
col1.prop(props, "diff_a")
|
||||
col1.enabled = props.img_diff_a == '' and not props.bool_cache
|
||||
col1 = row.column(align=True)
|
||||
col1.prop(props, "diff_b")
|
||||
col1.enabled = props.img_diff_b == '' and not props.bool_cache
|
||||
row = col.row(align=True)
|
||||
row.prop(props, "diff_mult")
|
||||
row.enabled = props.img_scale == '' and not props.bool_cache
|
||||
#col.separator()
|
||||
row = col.row(align=True)
|
||||
col1 = row.column(align=True)
|
||||
col1.prop(props, "f")
|
||||
col1.enabled = props.img_f == '' and not props.bool_cache
|
||||
col1 = row.column(align=True)
|
||||
col1.prop(props, "k")
|
||||
col1.enabled = props.img_k == '' and not props.bool_cache
|
||||
'''
|
||||
col.separator()
|
||||
col.label(text='Cache:')
|
||||
#col.prop(props, "bool_cache")
|
||||
col.prop(props, "cache_dir", text='')
|
||||
col.separator()
|
||||
row = col.row(align=True)
|
||||
row.prop(props, "cache_frame_start")
|
||||
row.prop(props, "cache_frame_end")
|
||||
col.separator()
|
||||
if props.bool_cache:
|
||||
col.operator("object.reaction_diffusion_free_data")
|
||||
else:
|
||||
row = col.row(align=True)
|
||||
row.operator("object.bake_reaction_diffusion")
|
||||
file = bpy.context.blend_data.filepath
|
||||
temp = bpy.context.preferences.filepaths.temporary_directory
|
||||
if file == temp == props.cache_dir == '':
|
||||
row.enabled = False
|
||||
col.label(text="Cannot use cache", icon='ERROR')
|
||||
col.label(text='please save the Blender or set a Cache directory')
|
||||
'''
|
||||
|
||||
class TISSUE_PT_tex_reaction_diffusion_images(Panel):
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "object"
|
||||
bl_parent_id = "TISSUE_PT_tex_reaction_diffusion"
|
||||
bl_label = "Image Maps"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.object.tex_reaction_diffusion_settings
|
||||
if props.img_a and props.img_b in bpy.data.images.keys():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def draw(self, context):
|
||||
ob = context.object
|
||||
props = ob.tex_reaction_diffusion_settings
|
||||
layout = self.layout
|
||||
#layout.use_property_split = True
|
||||
col = layout.column(align=True)
|
||||
insert_image_parameter(col, ob, 'brush', text='Brush:')
|
||||
insert_image_parameter(col, ob, 'diff_a', text='Diff A:')
|
||||
insert_image_parameter(col, ob, 'diff_b', text='Diff B:')
|
||||
insert_image_parameter(col, ob, 'scale', text='Scale:')
|
||||
insert_image_parameter(col, ob, 'f', text='f:')
|
||||
insert_image_parameter(col, ob, 'k', text='k:')
|
||||
insert_image_parameter(col, ob, 'vector_field', text='Vector Field:')
|
||||
col.enabled = not props.bool_cache
|
||||
|
||||
def insert_image_parameter(col, ob, name, text=''):
|
||||
props = ob.tex_reaction_diffusion_settings
|
||||
split = col.split(factor=0.25, align=True)
|
||||
col2 = split.column(align=True)
|
||||
col2.label(text=text)
|
||||
col2 = split.column(align=True)
|
||||
row2 = col2.row(align=True)
|
||||
row2.prop_search(props, 'img_' + name, bpy.data, "images", text='')
|
||||
if name not in ('brush'):
|
||||
if name == 'vector_field': icon = 'DRIVER_ROTATIONAL_DIFFERENCE'#'ORIENTATION_VIEW'
|
||||
else: icon = 'ARROW_LEFTRIGHT'
|
||||
row2.prop(props, "invert_img_" + name, text="", toggle=True, icon=icon)
|
||||
if 'img_' + name in props:
|
||||
if props['img_' + name] != '':
|
||||
if name == 'brush':
|
||||
col2.prop(props, "brush_mult")
|
||||
elif name == 'vector_field':
|
||||
col2.prop(props, "anisotropy")
|
||||
else:
|
||||
row2 = col2.row(align=True)
|
||||
row2.prop(props, "min_" + name, text="Min")
|
||||
row2 = col2.row(align=True)
|
||||
row2.prop(props, "max_" + name, text="Max")
|
||||
col.separator()
|
@ -1,7 +1,23 @@
|
||||
# SPDX-FileCopyrightText: 2022-2023 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# ---------------------------- ADAPTIVE DUPLIFACES --------------------------- #
|
||||
# ------------------------------- version 0.84 ------------------------------- #
|
||||
# #
|
||||
@ -29,7 +45,9 @@ from bpy.props import (
|
||||
StringProperty,
|
||||
PointerProperty
|
||||
)
|
||||
from .utils import tissue_time
|
||||
from . import config
|
||||
import time
|
||||
|
||||
|
||||
def update_dependencies(ob, objects):
|
||||
@ -50,6 +68,8 @@ def get_deps(ob):
|
||||
return [ob.tissue_tessellate.generator, ob.tissue_tessellate.component]
|
||||
elif type == 'TO_CURVE':
|
||||
return [ob.tissue_to_curve.object]
|
||||
elif type == 'POLYHEDRA':
|
||||
return [ob.tissue_polyhedra.object]
|
||||
else: return []
|
||||
|
||||
def anim_tessellate_active(self, context):
|
||||
@ -75,10 +95,9 @@ def anim_tessellate_object(ob):
|
||||
#from bpy.app.handlers import persistent
|
||||
|
||||
|
||||
def anim_tessellate(scene, depsgraph=None):
|
||||
print('Tissue: animating tessellations...')
|
||||
|
||||
#config.evaluatedDepsgraph = depsgraph
|
||||
def anim_tissue(scene, depsgraph=None):
|
||||
tissue_time(None,'Tissue: Animating Tissue objects at frame {}...'.format(scene.frame_current), levels=0)
|
||||
start_time = time.time()
|
||||
|
||||
try:
|
||||
active_object = bpy.context.object
|
||||
@ -109,10 +128,15 @@ def anim_tessellate(scene, depsgraph=None):
|
||||
override['mode'] = 'OBJECT'
|
||||
override['view_layer'] = scene.view_layers[0]
|
||||
break
|
||||
with bpy.context.temp_override(**override):
|
||||
if ob.tissue.tissue_type == 'TESSELLATE':
|
||||
bpy.ops.object.tissue_update_tessellate(override)
|
||||
bpy.ops.object.tissue_update_tessellate()
|
||||
elif ob.tissue.tissue_type == 'TO_CURVE':
|
||||
bpy.ops.object.tissue_convert_to_curve_update(override)
|
||||
bpy.ops.object.tissue_update_convert_to_curve()
|
||||
elif ob.tissue.tissue_type == 'POLYHEDRA':
|
||||
bpy.ops.object.tissue_update_polyhedra()
|
||||
elif ob.tissue.tissue_type == 'CONTOUR_CURVES':
|
||||
bpy.ops.object.tissue_update_contour_curves()
|
||||
|
||||
if old_mode != None:
|
||||
objects = bpy.context.view_layer.objects
|
||||
@ -121,68 +145,40 @@ def anim_tessellate(scene, depsgraph=None):
|
||||
bpy.ops.object.mode_set(mode=old_mode)
|
||||
|
||||
config.evaluatedDepsgraph = None
|
||||
print('end')
|
||||
tissue_time(start_time,'Animated Tissue objects at frame {}'.format(scene.frame_current), levels=0)
|
||||
return
|
||||
'''
|
||||
def OLD_anim_tessellate(scene, depsgraph):
|
||||
print('Tissue: animating tessellations...')
|
||||
|
||||
#global evaluatedDepsgraph
|
||||
#print(evaluatedDepsgraph)
|
||||
print(config.evaluatedDepsgraph)
|
||||
config.evaluatedDepsgraph = depsgraph
|
||||
print(config.evaluatedDepsgraph)
|
||||
|
||||
try:
|
||||
active_object = bpy.context.object
|
||||
old_mode = bpy.context.object.mode
|
||||
selected_objects = bpy.context.selected_objects
|
||||
except: active_object = old_mode = selected_objects = None
|
||||
|
||||
if old_mode in ('OBJECT', 'PAINT_WEIGHT') or True:
|
||||
update_objects = []
|
||||
for ob in scene.objects:
|
||||
if ob.tissue.bool_run and not ob.tissue.bool_lock:
|
||||
if ob not in update_objects: update_objects.append(ob)
|
||||
update_objects = list(reversed(update_dependencies(ob, update_objects)))
|
||||
for ob in update_objects:
|
||||
for window in bpy.context.window_manager.windows:
|
||||
screen = window.screen
|
||||
for area in screen.areas:
|
||||
if area.type == 'VIEW_3D':
|
||||
override = bpy.context.copy()
|
||||
override['window'] = window
|
||||
override['screen'] = screen
|
||||
override['area'] = area
|
||||
override['selected_objects'] = [ob]
|
||||
override['object'] = ob
|
||||
override['active_object'] = ob
|
||||
override['selected_editable_objects'] = [ob]
|
||||
override['mode'] = 'OBJECT'
|
||||
override['view_layer'] = scene.view_layers[0]
|
||||
break
|
||||
bpy.ops.object.tissue_update_tessellate(override)
|
||||
|
||||
config.evaluatedDepsgraph = None
|
||||
print('end')
|
||||
print(config.evaluatedDepsgraph)
|
||||
return
|
||||
'''
|
||||
def remove_tessellate_handler():
|
||||
def remove_tissue_handler():
|
||||
tissue_handlers = []
|
||||
blender_handlers = bpy.app.handlers.frame_change_post
|
||||
for h in blender_handlers:
|
||||
if "anim_tessellate" in str(h):
|
||||
if "anim_tissue" in str(h):
|
||||
tissue_handlers.append(h)
|
||||
for h in tissue_handlers: blender_handlers.remove(h)
|
||||
|
||||
def set_tessellate_handler(self, context):
|
||||
|
||||
remove_tessellate_handler()
|
||||
def set_tissue_handler(self, context):
|
||||
remove_tissue_handler()
|
||||
for o in context.scene.objects:
|
||||
if o.tissue.bool_run:
|
||||
blender_handlers = bpy.app.handlers.frame_change_post
|
||||
blender_handlers.append(anim_tessellate)
|
||||
blender_handlers.append(anim_tissue)
|
||||
break
|
||||
return
|
||||
|
||||
def remove_polyhedra_handler():
|
||||
tissue_handlers = []
|
||||
blender_handlers = bpy.app.handlers.frame_change_post
|
||||
for h in blender_handlers:
|
||||
if "anim_polyhedra" in str(h):
|
||||
tissue_handlers.append(h)
|
||||
for h in tissue_handlers: blender_handlers.remove(h)
|
||||
|
||||
def set_polyhedra_handler(self, context):
|
||||
remove_polyhedra_handler()
|
||||
for o in context.scene.objects:
|
||||
if o.tissue.bool_run:
|
||||
blender_handlers = bpy.app.handlers.frame_change_post
|
||||
blender_handlers.append(anim_polyhedra)
|
||||
break
|
||||
return
|
||||
|
||||
@ -190,7 +186,7 @@ def set_tessellate_handler(self, context):
|
||||
class tissue_prop(PropertyGroup):
|
||||
bool_lock : BoolProperty(
|
||||
name="Lock",
|
||||
description="Prevent automatic update on settings changes or if other objects have it in the hierarchy",
|
||||
description="Prevent automatic update on settings changes or if other objects have it in the hierarchy.",
|
||||
default=False
|
||||
)
|
||||
bool_dependencies : BoolProperty(
|
||||
@ -202,17 +198,24 @@ class tissue_prop(PropertyGroup):
|
||||
name="Animatable",
|
||||
description="Automatically recompute the geometry when the frame is changed. Tessellations may not work using the default Render Animation",
|
||||
default = False,
|
||||
update = set_tessellate_handler
|
||||
update = set_tissue_handler
|
||||
)
|
||||
tissue_type : EnumProperty(
|
||||
items=(
|
||||
('NONE', "None", ""),
|
||||
('TESSELLATE', "Tessellate", ""),
|
||||
('TO_CURVE', "To Curve", "")
|
||||
('TO_CURVE', "To Curve", ""),
|
||||
('POLYHEDRA', "Polyhedra", ""),
|
||||
('CONTOUR_CURVES', "Contour Curves", "")
|
||||
),
|
||||
default='NONE',
|
||||
name=""
|
||||
)
|
||||
bool_hold : BoolProperty(
|
||||
name="Hold",
|
||||
description="Wait...",
|
||||
default=False
|
||||
)
|
||||
|
||||
class tissue_tessellate_prop(PropertyGroup):
|
||||
bool_hold : BoolProperty(
|
||||
@ -561,13 +564,13 @@ class tissue_tessellate_prop(PropertyGroup):
|
||||
boundary_mat_offset : IntProperty(
|
||||
name="Material Offset",
|
||||
default=0,
|
||||
description="Material Offset for boundaries (with Multi Components or Material ID)",
|
||||
description="Material Offset for boundaries (with components based on Materials)",
|
||||
update = anim_tessellate_active
|
||||
)
|
||||
fill_frame_mat : IntProperty(
|
||||
name="Material Offset",
|
||||
default=0,
|
||||
description="Material Offset for inner faces (with Multi Components or Material ID)",
|
||||
description="Material Offset for inner faces (with components based on Materials)",
|
||||
update = anim_tessellate_active
|
||||
)
|
||||
open_edges_crease : FloatProperty(
|
||||
@ -598,14 +601,23 @@ class tissue_tessellate_prop(PropertyGroup):
|
||||
name="Frame Thickness",
|
||||
default=0.2,
|
||||
min=0,
|
||||
soft_max=2,
|
||||
soft_max=1,
|
||||
description="Frame Thickness",
|
||||
update = anim_tessellate_active
|
||||
)
|
||||
frame_boundary_thickness : FloatProperty(
|
||||
name="Frame Boundary Thickness",
|
||||
default=0,
|
||||
min=0,
|
||||
soft_max=1,
|
||||
description="Frame Boundary Thickness (when zero it uses the Frame Thickness instead)",
|
||||
update = anim_tessellate_active
|
||||
)
|
||||
frame_mode : EnumProperty(
|
||||
items=(
|
||||
('CONSTANT', 'Constant', 'Even thickness'),
|
||||
('RELATIVE', 'Relative', 'Frame offset depends on face areas')),
|
||||
('RELATIVE', 'Relative', 'Frame offset depends on face areas'),
|
||||
('CENTER', 'Center', 'Toward the center of the face (uses Incenter for Triangles)')),
|
||||
default='CONSTANT',
|
||||
name="Offset",
|
||||
update = anim_tessellate_active
|
||||
@ -641,7 +653,7 @@ class tissue_tessellate_prop(PropertyGroup):
|
||||
)
|
||||
use_origin_offset : BoolProperty(
|
||||
name="Align to Origins",
|
||||
default=False,
|
||||
default=True,
|
||||
description="Define offset according to components origin and local Z coordinate",
|
||||
update = anim_tessellate_active
|
||||
)
|
||||
@ -665,6 +677,31 @@ class tissue_tessellate_prop(PropertyGroup):
|
||||
update = anim_tessellate_active
|
||||
)
|
||||
|
||||
vertex_group_frame_thickness : StringProperty(
|
||||
name="Frame Thickness weight", default='',
|
||||
description="Vertex Group used for frame thickness",
|
||||
update = anim_tessellate_active
|
||||
)
|
||||
invert_vertex_group_frame_thickness : BoolProperty(
|
||||
name="Invert", default=False,
|
||||
description="Invert the vertex group influence",
|
||||
update = anim_tessellate_active
|
||||
)
|
||||
vertex_group_frame_thickness_factor : FloatProperty(
|
||||
name="Factor",
|
||||
default=0,
|
||||
min=0,
|
||||
max=1,
|
||||
description="Frame thickness factor to use for zero vertex group influence",
|
||||
update = anim_tessellate_active
|
||||
)
|
||||
face_weight_frame : BoolProperty(
|
||||
name="Face Weight",
|
||||
default=True,
|
||||
description="Uniform weight for individual faces",
|
||||
update = anim_tessellate_active
|
||||
)
|
||||
|
||||
vertex_group_cap_owner : EnumProperty(
|
||||
items=(
|
||||
('BASE', 'Base', 'Use base vertex group'),
|
||||
@ -802,6 +839,12 @@ class tissue_tessellate_prop(PropertyGroup):
|
||||
description="Automatically rotate the boundary faces",
|
||||
update = anim_tessellate_active
|
||||
)
|
||||
preserve_quads : BoolProperty(
|
||||
name="Preserve Quads",
|
||||
default=False,
|
||||
description="Quad faces are tessellated using QUAD mode",
|
||||
update = anim_tessellate_active
|
||||
)
|
||||
|
||||
def store_parameters(operator, ob):
|
||||
ob.tissue_tessellate.bool_hold = True
|
||||
@ -852,6 +895,7 @@ def store_parameters(operator, ob):
|
||||
ob.tissue_tessellate.bridge_cuts = operator.bridge_cuts
|
||||
ob.tissue_tessellate.bridge_smoothness = operator.bridge_smoothness
|
||||
ob.tissue_tessellate.frame_thickness = operator.frame_thickness
|
||||
ob.tissue_tessellate.frame_boundary_thickness = operator.frame_boundary_thickness
|
||||
ob.tissue_tessellate.frame_mode = operator.frame_mode
|
||||
ob.tissue_tessellate.frame_boundary = operator.frame_boundary
|
||||
ob.tissue_tessellate.fill_frame = operator.fill_frame
|
||||
@ -863,6 +907,10 @@ def store_parameters(operator, ob):
|
||||
ob.tissue_tessellate.vertex_group_thickness = operator.vertex_group_thickness
|
||||
ob.tissue_tessellate.invert_vertex_group_thickness = operator.invert_vertex_group_thickness
|
||||
ob.tissue_tessellate.vertex_group_thickness_factor = operator.vertex_group_thickness_factor
|
||||
ob.tissue_tessellate.vertex_group_frame_thickness = operator.vertex_group_frame_thickness
|
||||
ob.tissue_tessellate.invert_vertex_group_frame_thickness = operator.invert_vertex_group_frame_thickness
|
||||
ob.tissue_tessellate.vertex_group_frame_thickness_factor = operator.vertex_group_frame_thickness_factor
|
||||
ob.tissue_tessellate.face_weight_frame = operator.face_weight_frame
|
||||
ob.tissue_tessellate.vertex_group_distribution = operator.vertex_group_distribution
|
||||
ob.tissue_tessellate.invert_vertex_group_distribution = operator.invert_vertex_group_distribution
|
||||
ob.tissue_tessellate.vertex_group_distribution_factor = operator.vertex_group_distribution_factor
|
||||
@ -888,6 +936,7 @@ def store_parameters(operator, ob):
|
||||
ob.tissue_tessellate.invert_vertex_group_scale_normals = operator.invert_vertex_group_scale_normals
|
||||
ob.tissue_tessellate.boundary_variable_offset = operator.boundary_variable_offset
|
||||
ob.tissue_tessellate.auto_rotate_boundary = operator.auto_rotate_boundary
|
||||
ob.tissue_tessellate.preserve_quads = operator.preserve_quads
|
||||
ob.tissue_tessellate.bool_hold = False
|
||||
return ob
|
||||
|
||||
@ -938,11 +987,16 @@ def load_parameters(operator, ob):
|
||||
operator.boundary_mat_offset = ob.tissue_tessellate.boundary_mat_offset
|
||||
operator.fill_frame_mat = ob.tissue_tessellate.fill_frame_mat
|
||||
operator.frame_thickness = ob.tissue_tessellate.frame_thickness
|
||||
operator.frame_boundary_thickness = ob.tissue_tessellate.frame_boundary_thickness
|
||||
operator.frame_mode = ob.tissue_tessellate.frame_mode
|
||||
operator.use_origin_offset = ob.tissue_tessellate.use_origin_offset
|
||||
operator.vertex_group_thickness = ob.tissue_tessellate.vertex_group_thickness
|
||||
operator.invert_vertex_group_thickness = ob.tissue_tessellate.invert_vertex_group_thickness
|
||||
operator.vertex_group_thickness_factor = ob.tissue_tessellate.vertex_group_thickness_factor
|
||||
operator.vertex_group_frame_thickness = ob.tissue_tessellate.vertex_group_frame_thickness
|
||||
operator.invert_vertex_group_frame_thickness = ob.tissue_tessellate.invert_vertex_group_frame_thickness
|
||||
operator.vertex_group_frame_thickness_factor = ob.tissue_tessellate.vertex_group_frame_thickness_factor
|
||||
operator.face_weight_frame = ob.tissue_tessellate.face_weight_frame
|
||||
operator.vertex_group_distribution = ob.tissue_tessellate.vertex_group_distribution
|
||||
operator.invert_vertex_group_distribution = ob.tissue_tessellate.invert_vertex_group_distribution
|
||||
operator.vertex_group_distribution_factor = ob.tissue_tessellate.vertex_group_distribution_factor
|
||||
@ -968,6 +1022,7 @@ def load_parameters(operator, ob):
|
||||
operator.invert_vertex_group_scale_normals = ob.tissue_tessellate.invert_vertex_group_scale_normals
|
||||
operator.boundary_variable_offset = ob.tissue_tessellate.boundary_variable_offset
|
||||
operator.auto_rotate_boundary = ob.tissue_tessellate.auto_rotate_boundary
|
||||
operator.preserve_quads = ob.tissue_tessellate.preserve_quads
|
||||
return ob
|
||||
|
||||
def props_to_dict(ob):
|
||||
@ -1003,6 +1058,7 @@ def props_to_dict(ob):
|
||||
tessellate_dict['even_thickness'] = props.even_thickness
|
||||
tessellate_dict['even_thickness_iter'] = props.even_thickness_iter
|
||||
tessellate_dict['frame_thickness'] = props.frame_thickness
|
||||
tessellate_dict['frame_boundary_thickness'] = props.frame_boundary_thickness
|
||||
tessellate_dict['frame_mode'] = props.frame_mode
|
||||
tessellate_dict['frame_boundary'] = props.frame_boundary
|
||||
tessellate_dict['fill_frame'] = props.fill_frame
|
||||
@ -1011,6 +1067,10 @@ def props_to_dict(ob):
|
||||
tessellate_dict['vertex_group_thickness'] = props.vertex_group_thickness
|
||||
tessellate_dict['invert_vertex_group_thickness'] = props.invert_vertex_group_thickness
|
||||
tessellate_dict['vertex_group_thickness_factor'] = props.vertex_group_thickness_factor
|
||||
tessellate_dict['vertex_group_frame_thickness'] = props.vertex_group_frame_thickness
|
||||
tessellate_dict['invert_vertex_group_frame_thickness'] = props.invert_vertex_group_frame_thickness
|
||||
tessellate_dict['vertex_group_frame_thickness_factor'] = props.vertex_group_frame_thickness_factor
|
||||
tessellate_dict['face_weight_frame'] = props.face_weight_frame
|
||||
tessellate_dict['vertex_group_distribution'] = props.vertex_group_distribution
|
||||
tessellate_dict['invert_vertex_group_distribution'] = props.invert_vertex_group_distribution
|
||||
tessellate_dict['vertex_group_distribution_factor'] = props.vertex_group_distribution_factor
|
||||
@ -1036,6 +1096,10 @@ def props_to_dict(ob):
|
||||
tessellate_dict["invert_vertex_group_scale_normals"] = props.invert_vertex_group_scale_normals
|
||||
tessellate_dict["boundary_variable_offset"] = props.boundary_variable_offset
|
||||
tessellate_dict["auto_rotate_boundary"] = props.auto_rotate_boundary
|
||||
tessellate_dict["merge"] = props.merge
|
||||
tessellate_dict["merge_thres"] = props.merge_thres
|
||||
tessellate_dict["merge_open_edges_only"] = props.merge_open_edges_only
|
||||
tessellate_dict["preserve_quads"] = props.preserve_quads
|
||||
return tessellate_dict
|
||||
|
||||
def copy_tessellate_props(source_ob, target_ob):
|
||||
|
@ -1,5 +1,3 @@
|
||||
# SPDX-FileCopyrightText: 2019-2023 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import bpy, bmesh
|
||||
@ -183,6 +181,25 @@ def vector_rotation(vec):
|
||||
if ang < 0: ang = 2*pi + ang
|
||||
return ang
|
||||
|
||||
def signed_angle_with_axis(va, vb, axis):
|
||||
return atan2(va.cross(vb).dot(axis.normalized()), va.dot(vb))
|
||||
|
||||
def round_angle_with_axis(va, vb, axis):
|
||||
angle = signed_angle_with_axis(va, vb, axis)
|
||||
return 2*pi + angle if angle < 0 else angle
|
||||
|
||||
def incenter(vecs):
|
||||
lengths = x = y = z = 0
|
||||
mid = len(vecs)//2+1
|
||||
for vi, vj, vk in zip(vecs, vecs[1:]+vecs[:1], vecs[mid:]+vecs[:mid]):
|
||||
length = (vj-vi).length
|
||||
lengths += length
|
||||
x += length*vk.x
|
||||
y += length*vk.y
|
||||
z += length*vk.z
|
||||
inc = Vector((x/lengths, y/lengths, z/lengths))
|
||||
return inc
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# SCENE
|
||||
# ------------------------------------------------------------------
|
||||
@ -215,7 +232,15 @@ def turn_off_animatable(scene):
|
||||
# OBJECTS
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def convert_object_to_mesh(ob, apply_modifiers=True, preserve_status=True):
|
||||
def remove_temp_objects():
|
||||
# clean objects
|
||||
for o in bpy.data.objects:
|
||||
if "_tissue_tmp" in o.name:
|
||||
bpy.data.objects.remove(o)
|
||||
return
|
||||
|
||||
def convert_object_to_mesh(ob, apply_modifiers=True, preserve_status=True, mirror_correction = True):
|
||||
#mirror_correction = False
|
||||
try: ob.name
|
||||
except: return None
|
||||
if ob.type != 'MESH':
|
||||
@ -226,6 +251,9 @@ def convert_object_to_mesh(ob, apply_modifiers=True, preserve_status=True):
|
||||
#dg = bpy.context.evaluated_depsgraph_get()
|
||||
#ob_eval = ob.evaluated_get(dg)
|
||||
#me = bpy.data.meshes.new_from_object(ob_eval, preserve_all_data_layers=True, depsgraph=dg)
|
||||
if mirror_correction:
|
||||
me = simple_to_mesh_mirror(ob)
|
||||
else:
|
||||
me = simple_to_mesh(ob)
|
||||
new_ob = bpy.data.objects.new(ob.data.name, me)
|
||||
new_ob.location, new_ob.matrix_world = ob.location, ob.matrix_world
|
||||
@ -234,6 +262,9 @@ def convert_object_to_mesh(ob, apply_modifiers=True, preserve_status=True):
|
||||
else:
|
||||
if apply_modifiers:
|
||||
new_ob = ob.copy()
|
||||
if mirror_correction:
|
||||
new_me = simple_to_mesh_mirror(ob)
|
||||
else:
|
||||
new_me = simple_to_mesh(ob)
|
||||
new_ob.modifiers.clear()
|
||||
new_ob.data = new_me
|
||||
@ -250,6 +281,76 @@ def convert_object_to_mesh(ob, apply_modifiers=True, preserve_status=True):
|
||||
bpy.context.view_layer.objects.active = new_ob
|
||||
return new_ob
|
||||
|
||||
def simple_to_mesh_mirror(ob, depsgraph=None):
|
||||
'''
|
||||
Convert object to mesh applying Modifiers and Shape Keys.
|
||||
Automatically correct Faces rotation for Tessellations.
|
||||
'''
|
||||
if 'MIRROR' in [m.type for m in ob.modifiers]:
|
||||
|
||||
_ob = ob.copy()
|
||||
_ob.name = _ob.name + "_mirror"
|
||||
bpy.context.collection.objects.link(_ob)
|
||||
# Store modifiers
|
||||
mods = list(_ob.modifiers)
|
||||
# Store visibility setting
|
||||
mods_vis = [m.show_viewport for m in _ob.modifiers]
|
||||
# Turn modifiers off
|
||||
for m in _ob.modifiers:
|
||||
m.show_viewport = False
|
||||
while True:
|
||||
if len(mods) == 0: break
|
||||
remove_mods = []
|
||||
|
||||
for m, vis in zip(mods, mods_vis):
|
||||
m.show_viewport = vis
|
||||
remove_mods.append(m)
|
||||
if m.type == 'MIRROR' and vis:
|
||||
n_axis = m.use_axis[0] + m.use_axis[1] + m.use_axis[2]
|
||||
fraction = 2**n_axis
|
||||
me = simple_to_mesh(_ob, depsgraph)
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(me)
|
||||
bm.faces.ensure_lookup_table()
|
||||
n_faces = len(bm.faces)
|
||||
if n_axis > 0:
|
||||
bm.faces.ensure_lookup_table()
|
||||
rotate_faces = bm.faces
|
||||
rot_index = []
|
||||
if n_axis == 1: fraction_val = [0,1]
|
||||
elif n_axis == 2: fraction_val = [0,1,1,0]
|
||||
elif n_axis == 3: fraction_val = [0,1,1,0,1,0,0,1]
|
||||
for i in fraction_val:
|
||||
for j in range(n_faces//fraction):
|
||||
rot_index.append(i)
|
||||
for face, shift in zip(rotate_faces, rot_index):
|
||||
if shift == 0: continue
|
||||
vs = face.verts[:]
|
||||
vs2 = vs[-shift:]+vs[:-shift]
|
||||
material_index = face.material_index
|
||||
bm.faces.remove(face)
|
||||
f2 = bm.faces.new(vs2)
|
||||
f2.select = True
|
||||
f2.material_index = material_index
|
||||
bm.normal_update()
|
||||
bm.to_mesh(me)
|
||||
bm.free()
|
||||
for rm in remove_mods:
|
||||
_ob.modifiers.remove(rm)
|
||||
_ob.data = me
|
||||
mods = mods[1:]
|
||||
mods_vis = mods_vis[1:]
|
||||
remove_mods = []
|
||||
break
|
||||
if m == mods[-1]:
|
||||
mods = []
|
||||
me = simple_to_mesh(_ob, depsgraph)
|
||||
_ob.data = me
|
||||
_ob.modifiers.clear()
|
||||
else:
|
||||
me = simple_to_mesh(ob, depsgraph)
|
||||
return me
|
||||
|
||||
def simple_to_mesh(ob, depsgraph=None):
|
||||
'''
|
||||
Convert object to mesh applying Modifiers and Shape Keys
|
||||
@ -263,6 +364,7 @@ def simple_to_mesh(ob, depsgraph=None):
|
||||
dg = depsgraph
|
||||
ob_eval = ob.evaluated_get(dg)
|
||||
me = bpy.data.meshes.new_from_object(ob_eval, preserve_all_data_layers=True, depsgraph=dg)
|
||||
#me.calc_normals()
|
||||
return me
|
||||
|
||||
def _join_objects(context, objects, link_to_scene=True, make_active=True):
|
||||
@ -320,11 +422,10 @@ def join_objects(context, objects):
|
||||
return new_ob
|
||||
|
||||
def join_objects(objects):
|
||||
override = bpy.context.copy()
|
||||
new_ob = objects[0]
|
||||
override['active_object'] = new_ob
|
||||
override['selected_editable_objects'] = objects
|
||||
bpy.ops.object.join(override)
|
||||
override = {'active_object': new_ob, 'selected_editable_objects': objects}
|
||||
with bpy.context.temp_override(**override):
|
||||
bpy.ops.object.join()
|
||||
return new_ob
|
||||
|
||||
def repeat_mesh(me, n):
|
||||
@ -345,9 +446,8 @@ def array_mesh(ob, n):
|
||||
arr = ob.modifiers.new('Repeat','ARRAY')
|
||||
arr.relative_offset_displace[0] = 0
|
||||
arr.count = n
|
||||
# with bpy.context.temp_override(active_object=ob):
|
||||
# bpy.ops.object.modifier_apply(modifier='Repeat')
|
||||
# me = ob.data
|
||||
#bpy.ops.object.modifier_apply({'active_object':ob},modifier='Repeat')
|
||||
#me = ob.data
|
||||
ob.modifiers.update()
|
||||
|
||||
dg = bpy.context.evaluated_depsgraph_get()
|
||||
@ -366,7 +466,8 @@ def array_mesh_object(ob, n):
|
||||
override = bpy.context.copy()
|
||||
override['active_object'] = ob
|
||||
override = {'active_object': ob}
|
||||
bpy.ops.object.modifier_apply(override, modifier=arr.name)
|
||||
with bpy.context.temp_override(**override):
|
||||
bpy.ops.object.modifier_apply(modifier=arr.name)
|
||||
return ob
|
||||
|
||||
|
||||
@ -389,7 +490,7 @@ def get_mesh_before_subs(ob):
|
||||
hide_mods = []
|
||||
mods_visibility = []
|
||||
for m in hide_mods: m.show_viewport = False
|
||||
me = simple_to_mesh(ob)
|
||||
me = simple_to_mesh_mirror(ob)
|
||||
for m, vis in zip(hide_mods,mods_visibility): m.show_viewport = vis
|
||||
return me, subs
|
||||
|
||||
@ -542,9 +643,6 @@ def get_patches____(me_low, me_high, sides, subs, bool_selection, bool_material_
|
||||
# fill inners
|
||||
patches[:,1:-1,1:-1] = inners[None,:,:] + ips[:,None,None]
|
||||
|
||||
#end_time = time.time()
|
||||
#print('Tissue: Got Patches in {:.4f} sec'.format(end_time-start_time))
|
||||
|
||||
return patches, mask
|
||||
|
||||
def tessellate_prepare_component(ob1, props):
|
||||
@ -673,10 +771,11 @@ def tessellate_prepare_component(ob1, props):
|
||||
cut_edges = [g for g in bisect['geom_cut'] if type(g)==bmesh.types.BMEdge]
|
||||
cut_verts = [g for g in bisect['geom_cut'] if type(g)==bmesh.types.BMVert]
|
||||
|
||||
if bound!='CLIP':
|
||||
if True or bound!='CLIP':
|
||||
for e in cut_edges:
|
||||
seam = True
|
||||
# Prevent glitches
|
||||
'''
|
||||
for e1 in original_edges:
|
||||
match_00 = (e.verts[0].co-e1.verts[0].co).length < thres
|
||||
match_11 = (e.verts[1].co-e1.verts[1].co).length < thres
|
||||
@ -685,6 +784,7 @@ def tessellate_prepare_component(ob1, props):
|
||||
if (match_00 and match_11) or (match_01 and match_10):
|
||||
seam = False
|
||||
break
|
||||
'''
|
||||
e.seam = seam
|
||||
|
||||
if bound == 'CYCLIC':
|
||||
@ -912,6 +1012,26 @@ def get_edges_id_numpy(mesh):
|
||||
edges = np.concatenate((edges,indexes), axis=1)
|
||||
return edges
|
||||
|
||||
def get_edges_numpy_ex(mesh):
|
||||
'''
|
||||
Create a numpy array with the edges of a given mesh, or all the possible
|
||||
between the vertices of a same face
|
||||
'''
|
||||
edges_verts = get_edges_numpy(mesh)
|
||||
polygons_diag = []
|
||||
for f in mesh.polygons:
|
||||
sides = len(f.vertices)
|
||||
if sides < 4: continue
|
||||
for i in range(sides-2):
|
||||
v0 = f.vertices[i]
|
||||
for j in range(i+2, sides-1 if i == 0 else sides):
|
||||
v1 = f.vertices[j]
|
||||
polygons_diag.append((v0,v1))
|
||||
if len(polygons_diag) == 0:
|
||||
return edges_verts
|
||||
polygons_diag = np.array(polygons_diag,dtype=np.int32)
|
||||
return np.concatenate((edges_verts, polygons_diag), axis=0)
|
||||
|
||||
def get_polygons_select_numpy(mesh):
|
||||
n_polys = len(mesh.polygons)
|
||||
selections = [0]*n_polys*2
|
||||
@ -919,13 +1039,16 @@ def get_polygons_select_numpy(mesh):
|
||||
selections = np.array(selections)
|
||||
return selections
|
||||
|
||||
def get_attribute_numpy(elements_list, attribute='select', mult=1):
|
||||
def get_attribute_numpy(elements_list, attribute='select', mult=1, size=None):
|
||||
'''
|
||||
Generate a numpy array getting attribute from a list of element using
|
||||
the foreach_get() function.
|
||||
'''
|
||||
if size:
|
||||
n_elements = size
|
||||
else:
|
||||
n_elements = len(elements_list)
|
||||
values = [0]*n_elements*mult
|
||||
values = np.zeros(int(n_elements*mult))
|
||||
elements_list.foreach_get(attribute, values)
|
||||
values = np.array(values)
|
||||
if mult > 1: values = values.reshape((n_elements,mult))
|
||||
@ -1006,6 +1129,73 @@ def find_curves(edges, n_verts):
|
||||
curves.append(curve)
|
||||
return curves
|
||||
|
||||
def find_curves_attribute(edges, n_verts, attribute):
|
||||
# dictionary with a list for every point
|
||||
verts_dict = {key:[] for key in range(n_verts)}
|
||||
# get neighbors for every point
|
||||
for e in edges:
|
||||
verts_dict[e[0]].append(e[1])
|
||||
verts_dict[e[1]].append(e[0])
|
||||
curves = []
|
||||
ordered_attr = []
|
||||
while True:
|
||||
if len(verts_dict) == 0: break
|
||||
# next starting point
|
||||
v = list(verts_dict.keys())[0]
|
||||
# neighbors
|
||||
v01 = verts_dict[v]
|
||||
if len(v01) == 0:
|
||||
verts_dict.pop(v)
|
||||
continue
|
||||
curve = []
|
||||
attr = []
|
||||
if len(v01) > 1:
|
||||
curve.append(v01[1]) # add neighbors
|
||||
attr.append(attribute[v01[1]]) # add neighbors
|
||||
curve.append(v) # add starting point
|
||||
attr.append(attribute[v])
|
||||
curve.append(v01[0]) # add neighbors
|
||||
attr.append(attribute[v01[0]])
|
||||
verts_dict.pop(v)
|
||||
# start building curve
|
||||
while True:
|
||||
#last_point = curve[-1]
|
||||
#if last_point not in verts_dict: break
|
||||
|
||||
# try to change direction if needed
|
||||
if curve[-1] in verts_dict: pass
|
||||
elif curve[0] in verts_dict:
|
||||
curve.reverse()
|
||||
attr.reverse()
|
||||
else: break
|
||||
|
||||
# neighbors points
|
||||
last_point = curve[-1]
|
||||
v01 = verts_dict[last_point]
|
||||
|
||||
# curve end
|
||||
if len(v01) == 1:
|
||||
verts_dict.pop(last_point)
|
||||
if curve[0] in verts_dict: continue
|
||||
else: break
|
||||
|
||||
# chose next point
|
||||
new_point = None
|
||||
if v01[0] == curve[-2]: new_point = v01[1]
|
||||
elif v01[1] == curve[-2]: new_point = v01[0]
|
||||
#else: break
|
||||
|
||||
#if new_point != curve[1]:
|
||||
curve.append(new_point)
|
||||
ordered_attr.append(attr)
|
||||
verts_dict.pop(last_point)
|
||||
if curve[0] == curve[-1]:
|
||||
verts_dict.pop(new_point)
|
||||
break
|
||||
if(len(curve)>0):
|
||||
curves.append(curve)
|
||||
return curves, ordered_attr
|
||||
|
||||
def curve_from_points(points, name='Curve'):
|
||||
curve = bpy.data.curves.new(name,'CURVE')
|
||||
for c in points:
|
||||
@ -1015,10 +1205,56 @@ def curve_from_points(points, name='Curve'):
|
||||
ob_curve = bpy.data.objects.new(name,curve)
|
||||
return ob_curve
|
||||
|
||||
def curve_from_pydata(points, radii, indexes, name='Curve', skip_open=False, merge_distance=1, set_active=True, only_data=False):
|
||||
def curve_from_pydata(points, radii, indexes, name='Curve', skip_open=False, merge_distance=1, set_active=True, only_data=False, curve=None, spline_type='POLY'):
|
||||
if not curve:
|
||||
curve = bpy.data.curves.new(name,'CURVE')
|
||||
curve.dimensions = '3D'
|
||||
use_rad = True
|
||||
for c in indexes:
|
||||
bool_cyclic = c[0] == c[-1]
|
||||
if bool_cyclic: c.pop(-1)
|
||||
# cleanup
|
||||
pts = np.array([points[i] for i in c])
|
||||
try:
|
||||
rad = np.array([radii[i] for i in c])
|
||||
except:
|
||||
use_rad = False
|
||||
rad = 1
|
||||
if merge_distance > 0:
|
||||
pts1 = np.roll(pts,1,axis=0)
|
||||
dist = np.linalg.norm(np.array(pts1-pts, dtype=np.float64), axis=1)
|
||||
count = 0
|
||||
n = len(dist)
|
||||
mask = np.ones(n).astype('bool')
|
||||
for i in range(n):
|
||||
count += dist[i]
|
||||
if count > merge_distance: count = 0
|
||||
else: mask[i] = False
|
||||
pts = pts[mask]
|
||||
if use_rad: rad = rad[mask]
|
||||
|
||||
if skip_open and not bool_cyclic: continue
|
||||
s = curve.splines.new(spline_type)
|
||||
n_pts = len(pts)
|
||||
s.points.add(n_pts-1)
|
||||
w = np.ones(n_pts).reshape((n_pts,1))
|
||||
co = np.concatenate((pts,w),axis=1).reshape((n_pts*4))
|
||||
s.points.foreach_set('co',co)
|
||||
if use_rad: s.points.foreach_set('radius',rad)
|
||||
s.use_cyclic_u = bool_cyclic
|
||||
if only_data:
|
||||
return curve
|
||||
else:
|
||||
ob_curve = bpy.data.objects.new(name,curve)
|
||||
bpy.context.collection.objects.link(ob_curve)
|
||||
if set_active:
|
||||
bpy.context.view_layer.objects.active = ob_curve
|
||||
return ob_curve
|
||||
|
||||
def update_curve_from_pydata_simple(curve, points, radii, indexes, skip_open=False, merge_distance=1, set_active=True, only_data=False, spline_type='POLY'):
|
||||
curve.splines.clear()
|
||||
curve.dimensions = '3D'
|
||||
use_rad = True
|
||||
for c in indexes:
|
||||
bool_cyclic = c[0] == c[-1]
|
||||
if bool_cyclic: c.pop(-1)
|
||||
@ -1043,7 +1279,7 @@ def curve_from_pydata(points, radii, indexes, name='Curve', skip_open=False, mer
|
||||
if use_rad: rad = rad[mask]
|
||||
|
||||
if skip_open and not bool_cyclic: continue
|
||||
s = curve.splines.new('POLY')
|
||||
s = curve.splines.new(spline_type)
|
||||
n_pts = len(pts)
|
||||
s.points.add(n_pts-1)
|
||||
w = np.ones(n_pts).reshape((n_pts,1))
|
||||
@ -1091,13 +1327,14 @@ def update_curve_from_pydata(curve, points, normals, radii, indexes, merge_dista
|
||||
#if skip_open and not bool_cyclic: continue
|
||||
n_pts = len(pts)
|
||||
series = np.arange(n_pts)
|
||||
if pattern[0]*pattern[1] != 0:
|
||||
patt1 = series + (series-series%pattern[1])/pattern[1]*pattern[0]+pattern[0]
|
||||
patt1 = patt1[patt1<n_pts].astype('int')
|
||||
patt0 = series + (series-series%pattern[0])/pattern[0]*pattern[1]
|
||||
patt0 = patt0[patt0<n_pts].astype('int')
|
||||
nor[patt0] *= 0.5*depth*(1 + offset)
|
||||
nor[patt1] *= 0.5*depth*(-1 + offset)
|
||||
if pattern[0]*pattern[1] != 0: pts += nor
|
||||
pts += nor
|
||||
s = curve.splines.new('POLY')
|
||||
s.points.add(n_pts-1)
|
||||
w = np.ones(n_pts).reshape((n_pts,1))
|
||||
@ -1253,7 +1490,7 @@ def get_weight(vertex_group, n_verts):
|
||||
:type vertex_group: :class:'bpy.types.VertexGroup'
|
||||
:arg n_verts: Number of Vertices (output list size).
|
||||
:type n_verts: int
|
||||
:return: Read weight values.
|
||||
:return: Readed weight values.
|
||||
:rtype: list
|
||||
"""
|
||||
weight = [0]*n_verts
|
||||
@ -1269,22 +1506,24 @@ def get_weight_numpy(vertex_group, n_verts):
|
||||
:type vertex_group: :class:'bpy.types.VertexGroup'
|
||||
:arg n_verts: Number of Vertices (output list size).
|
||||
:type n_verts: int
|
||||
:return: Read weight values as numpy array.
|
||||
:return: Readed weight values as numpy array.
|
||||
:rtype: :class:'numpy.ndarray'
|
||||
"""
|
||||
weight = [0]*n_verts
|
||||
weight = np.zeros(n_verts)
|
||||
for i in range(n_verts):
|
||||
try: weight[i] = vertex_group.weight(i)
|
||||
except: pass
|
||||
return np.array(weight)
|
||||
return weight
|
||||
|
||||
def bmesh_get_weight_numpy(group_index, layer, verts):
|
||||
def bmesh_get_weight_numpy(group_index, layer, verts, normalized=False):
|
||||
weight = np.zeros(len(verts))
|
||||
for i, v in enumerate(verts):
|
||||
dvert = v[layer]
|
||||
if group_index in dvert:
|
||||
weight[i] = dvert[group_index]
|
||||
#dvert[group_index] = 0.5
|
||||
if normalized:
|
||||
weight = (weight - np.min(weight))/np.ptp(weight)
|
||||
return weight
|
||||
|
||||
def bmesh_set_weight_numpy(group_index, layer, verts, weight):
|
||||
@ -1410,6 +1649,22 @@ def mesh_diffusion_vector(me, vectors, iter, diff, uv_dir=0):
|
||||
vectors[:,2] = z
|
||||
return vectors
|
||||
|
||||
def fill_neighbors_attribute(verts,weight,attribute):
|
||||
neigh = {}
|
||||
for v0 in verts:
|
||||
for f in v0.link_faces:
|
||||
for v1 in f.verts:
|
||||
if attribute == 'GEODESIC':
|
||||
dist = weight[v0.index] + (v0.co-v1.co).length
|
||||
elif attribute == 'TOPOLOGY':
|
||||
dist = weight[v0.index] + 1.0
|
||||
w1 = weight[v1.index]
|
||||
if w1 == None or w1 > dist:
|
||||
weight[v1.index] = dist
|
||||
neigh[v1] = 0
|
||||
if len(neigh) == 0: return weight
|
||||
else: return fill_neighbors_attribute(neigh.keys(), weight, attribute)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# MODIFIERS
|
||||
# ------------------------------------------------------------------
|
||||
@ -1434,7 +1689,7 @@ def mod_preserve_shape(mod):
|
||||
|
||||
def recurLayerCollection(layerColl, collName):
|
||||
'''
|
||||
Recursively transverse layer_collection for a particular name.
|
||||
Recursivly transverse layer_collection for a particular name.
|
||||
'''
|
||||
found = None
|
||||
if (layerColl.name == collName):
|
||||
@ -1456,3 +1711,15 @@ def auto_layer_collection():
|
||||
lc = recurLayerCollection(layer_collection, c.name)
|
||||
if not c.hide_viewport and not lc.hide_viewport:
|
||||
bpy.context.view_layer.active_layer_collection = lc
|
||||
|
||||
def np_remap_image_values(img, channel=0, min=0, max=1, invert=False):
|
||||
nx = img.size[1]
|
||||
ny = img.size[0]
|
||||
px = np.float32(np.zeros(nx*ny*4))
|
||||
img.pixels.foreach_get(px)
|
||||
px = np.array(px).reshape((-1,4))
|
||||
values = px[:,channel]
|
||||
values = values.reshape((nx,ny))
|
||||
if invert:
|
||||
values = 1-values
|
||||
return min + values*(max-min)
|
||||
|
@ -1,7 +1,25 @@
|
||||
# SPDX-FileCopyrightText: 2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
# ----------------------------------------------------------
|
||||
# Author: Stephen Leger (s-leger)
|
||||
#
|
||||
@ -26,7 +44,7 @@ class Pip:
|
||||
import sys
|
||||
site_package = site.getusersitepackages()
|
||||
if not os.path.exists(site_package):
|
||||
site_package = bpy.utils.user_resource('SCRIPTS', "site_package", create=True)
|
||||
site_package = bpy.utils.user_resource('SCRIPTS', path="site_package", create=True)
|
||||
site.addsitedir(site_package)
|
||||
if site_package not in sys.path:
|
||||
sys.path.append(site_package)
|
||||
|
@ -1,7 +1,23 @@
|
||||
# SPDX-FileCopyrightText: 2017-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# --------------------------------- UV to MESH ------------------------------- #
|
||||
# -------------------------------- version 0.1.1 ----------------------------- #
|
||||
# #
|
||||
|
1180
mesh_tissue/weight_reaction_diffusion.py
Normal file
1180
mesh_tissue/weight_reaction_diffusion.py
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user