1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
19 # ---------------------------- ADAPTIVE DUPLIFACES --------------------------- #
20 # ------------------------------- version 0.84 ------------------------------- #
22 # Creates duplicates of selected mesh to active morphing the shape according #
25 # (c) Alessandro Zomparelli #
28 # http://www.co-de-it.com/ #
30 # ############################################################################ #
34 from bpy
.types
import (
39 from bpy
.props
import (
47 from mathutils
import Vector
54 def anim_tessellate_active(self
, context
):
56 props
= ob
.tissue_tessellate
57 if not props
.bool_hold
:
61 bpy
.ops
.object.update_tessellate()
64 def anim_tessellate_object(ob
):
66 #bpy.context.view_layer.objects.active = ob
67 bpy
.ops
.object.update_tessellate()
71 #from bpy.app.handlers import persistent
74 def anim_tessellate(scene
):
75 # store selected objects
76 #scene = context.scene
77 try: active_object
= bpy
.context
.object
78 except: active_object
= None
79 try: selected_objects
= bpy
.context
.selected_objects
80 except: selected_objects
= []
81 if bpy
.context
.mode
in ('OBJECT', 'PAINT_WEIGHT'):
82 old_mode
= bpy
.context
.mode
83 if old_mode
== 'PAINT_WEIGHT': old_mode
= 'WEIGHT_PAINT'
84 for ob
in scene
.objects
:
85 if ob
.tissue_tessellate
.bool_run
:
86 hidden
= ob
.hide_viewport
87 ob
.hide_viewport
= False
88 for o
in scene
.objects
:
89 if not o
.hide_viewport
: ob
.select_set(False)
90 bpy
.context
.view_layer
.objects
.active
= ob
93 bpy
.ops
.object.update_tessellate()
95 ob
.hide_viewport
= hidden
96 # restore selected objects
97 for o
in scene
.objects
:
98 if not o
.hide_viewport
: o
.select_set(False)
99 for o
in selected_objects
:
100 if not o
.hide_viewport
: o
.select_set(True)
101 bpy
.context
.view_layer
.objects
.active
= active_object
102 try: bpy
.ops
.object.mode_set(mode
=old_mode
)
106 def set_tessellate_handler(self
, context
):
108 blender_handlers
= bpy
.app
.handlers
.frame_change_post
109 for h
in blender_handlers
:
110 if "anim_tessellate" in str(h
):
111 old_handlers
.append(h
)
112 for h
in old_handlers
: blender_handlers
.remove(h
)
113 for o
in context
.scene
.objects
:
114 if o
.tissue_tessellate
.bool_run
:
115 blender_handlers
.append(anim_tessellate
)
119 class tissue_tessellate_prop(PropertyGroup
):
120 bool_hold
: BoolProperty(
122 description
="Prevent automatic update while other properties are changed",
125 bool_run
: BoolProperty(
126 name
="Animatable Tessellation",
127 description
="Automatically recompute the tessellation when the frame is changed. Currently is not working during Render Animation",
129 update
= set_tessellate_handler
131 zscale
: FloatProperty(
132 name
="Scale", default
=1, soft_min
=0, soft_max
=10,
133 description
="Scale factor for the component thickness",
134 update
= anim_tessellate_active
136 scale_mode
: EnumProperty(
138 ('CONSTANT', "Constant", "Uniform thinkness"),
139 ('ADAPTIVE', "Proportional", "Preserve component's proportions")
142 name
="Z-Scale according to faces size",
143 update
= anim_tessellate_active
145 offset
: FloatProperty(
146 name
="Surface Offset",
152 description
="Surface offset",
153 update
= anim_tessellate_active
157 ('BOUNDS', "Bounds", "The component fits automatically the size of the target face"),
158 ('LOCAL', "Local", "Based on Local coordinates, from 0 to 1"),
159 ('GLOBAL', 'Global', "Based on Global coordinates, from 0 to 1")),
161 name
="Component Mode",
162 update
= anim_tessellate_active
164 rotation_mode
: EnumProperty(
165 items
=(('RANDOM', "Random", "Random faces rotation"),
166 ('UV', "Active UV", "Rotate according to UV coordinates"),
167 ('DEFAULT', "Default", "Default rotation")),
169 name
="Component Rotation",
170 update
= anim_tessellate_active
172 fill_mode
: EnumProperty(
174 ('QUAD', 'Quad', 'Regular quad tessellation. Uses only 3 or 4 vertices'),
175 ('FAN', 'Fan', 'Radial tessellation for polygonal faces'),
176 ('PATCH', 'Patch', 'Curved tessellation according to the last ' +
177 'Subsurf\n(or Multires) modifiers. Works only with 4 sides ' +
178 'patches.\nAfter the last Subsurf (or Multires) only ' +
179 'deformation\nmodifiers can be used')),
182 update
= anim_tessellate_active
184 combine_mode
: EnumProperty(
186 ('LAST', 'Last', 'Show only the last iteration'),
187 ('UNUSED', 'Unused', 'Combine each iteration with the unused faces of the previous iteration. Used for branching systems'),
188 ('ALL', 'All', 'Combine the result of all iterations')),
191 update
= anim_tessellate_active
193 gen_modifiers
: BoolProperty(
194 name
="Generator Modifiers",
196 description
="Apply Modifiers and Shape Keys to the base object",
197 update
= anim_tessellate_active
199 com_modifiers
: BoolProperty(
200 name
="Component Modifiers",
202 description
="Apply Modifiers and Shape Keys to the component object",
203 update
= anim_tessellate_active
205 merge
: BoolProperty(
208 description
="Merge vertices in adjacent duplicates",
209 update
= anim_tessellate_active
211 merge_thres
: FloatProperty(
216 description
="Limit below which to merge vertices",
217 update
= anim_tessellate_active
219 generator
: PointerProperty(
220 type=bpy
.types
.Object
,
222 description
="Base object for the tessellation",
223 update
= anim_tessellate_active
225 component
: PointerProperty(
226 type=bpy
.types
.Object
,
228 description
="Component object for the tessellation",
230 update
= anim_tessellate_active
232 bool_random
: BoolProperty(
235 description
="Randomize component rotation",
236 update
= anim_tessellate_active
238 random_seed
: IntProperty(
243 description
="Random seed",
244 update
= anim_tessellate_active
246 bool_vertex_group
: BoolProperty(
247 name
="Map Vertex Group",
249 description
="Transfer all Vertex Groups from Base object",
250 update
= anim_tessellate_active
252 bool_selection
: BoolProperty(
253 name
="On selected Faces",
255 description
="Create Tessellation only on selected faces",
256 update
= anim_tessellate_active
258 bool_shapekeys
: BoolProperty(
259 name
="Use Shape Keys",
261 description
="Transfer Component's Shape Keys. If the name of Vertex "
262 "Groups and Shape Keys are the same, they will be "
263 "automatically combined",
264 update
= anim_tessellate_active
266 bool_smooth
: BoolProperty(
267 name
="Smooth Shading",
269 description
="Output faces with smooth shading rather than flat shaded",
270 update
= anim_tessellate_active
272 bool_materials
: BoolProperty(
273 name
="Transfer Materials",
275 description
="Preserve component's materials",
276 update
= anim_tessellate_active
278 bool_material_id
: BoolProperty(
279 name
="Tessellation on Material ID",
281 description
="Apply the component only on the selected Material",
282 update
= anim_tessellate_active
284 material_id
: IntProperty(
288 description
="Material ID",
289 update
= anim_tessellate_active
291 bool_dissolve_seams
: BoolProperty(
292 name
="Dissolve Seams",
294 description
="Dissolve all seam edges",
295 update
= anim_tessellate_active
297 iterations
: IntProperty(
302 description
="Automatically repeat the Tessellation using the "
303 + "generated geometry as new base object.\nUsefull for "
304 + "for branching systems. Dangerous!",
305 update
= anim_tessellate_active
307 bool_combine
: BoolProperty(
308 name
="Combine unused",
310 description
="Combine the generated geometry with unused faces",
311 update
= anim_tessellate_active
313 bool_advanced
: BoolProperty(
314 name
="Advanced Settings",
316 description
="Show more settings"
318 normals_mode
: EnumProperty(
320 ('VERTS', 'Along Normals', 'Consistent direction based on vertices normal'),
321 ('FACES', 'Individual Faces', 'Based on individual faces normal')),
324 update
= anim_tessellate_active
326 bool_multi_components
: BoolProperty(
327 name
="Multi Components",
329 description
="Combine different components according to materials name",
330 update
= anim_tessellate_active
332 error_message
: StringProperty(
333 name
="Error Message",
336 warning_message
: StringProperty(
337 name
="Warning Message",
340 bounds_x
: EnumProperty(
342 ('EXTEND', 'Extend', 'Default X coordinates'),
343 ('CLIP', 'Clip', 'Trim out of bounds in X direction'),
344 ('CYCLIC', 'Cyclic', 'Cyclic components in X direction')),
347 update
= anim_tessellate_active
349 bounds_y
: EnumProperty(
351 ('EXTEND', 'Extend', 'Default Y coordinates'),
352 ('CLIP', 'Clip', 'Trim out of bounds in Y direction'),
353 ('CYCLIC', 'Cyclic', 'Cyclic components in Y direction')),
356 update
= anim_tessellate_active
358 cap_faces
: BoolProperty(
361 description
="Cap open edges loops",
362 update
= anim_tessellate_active
364 open_edges_crease
: FloatProperty(
365 name
="Open Edges Crease",
369 description
="Automatically set crease for open edges",
370 update
= anim_tessellate_active
373 def store_parameters(operator
, ob
):
374 ob
.tissue_tessellate
.bool_hold
= True
375 ob
.tissue_tessellate
.generator
= bpy
.data
.objects
[operator
.generator
]
376 ob
.tissue_tessellate
.component
= bpy
.data
.objects
[operator
.component
]
377 ob
.tissue_tessellate
.zscale
= operator
.zscale
378 ob
.tissue_tessellate
.offset
= operator
.offset
379 ob
.tissue_tessellate
.gen_modifiers
= operator
.gen_modifiers
380 ob
.tissue_tessellate
.com_modifiers
= operator
.com_modifiers
381 ob
.tissue_tessellate
.mode
= operator
.mode
382 ob
.tissue_tessellate
.rotation_mode
= operator
.rotation_mode
383 ob
.tissue_tessellate
.merge
= operator
.merge
384 ob
.tissue_tessellate
.merge_thres
= operator
.merge_thres
385 ob
.tissue_tessellate
.scale_mode
= operator
.scale_mode
386 ob
.tissue_tessellate
.bool_random
= operator
.bool_random
387 ob
.tissue_tessellate
.random_seed
= operator
.random_seed
388 ob
.tissue_tessellate
.fill_mode
= operator
.fill_mode
389 ob
.tissue_tessellate
.bool_vertex_group
= operator
.bool_vertex_group
390 ob
.tissue_tessellate
.bool_selection
= operator
.bool_selection
391 ob
.tissue_tessellate
.bool_shapekeys
= operator
.bool_shapekeys
392 ob
.tissue_tessellate
.bool_smooth
= operator
.bool_smooth
393 ob
.tissue_tessellate
.bool_materials
= operator
.bool_materials
394 ob
.tissue_tessellate
.bool_material_id
= operator
.bool_material_id
395 ob
.tissue_tessellate
.material_id
= operator
.material_id
396 ob
.tissue_tessellate
.bool_dissolve_seams
= operator
.bool_dissolve_seams
397 ob
.tissue_tessellate
.iterations
= operator
.iterations
398 ob
.tissue_tessellate
.bool_advanced
= operator
.bool_advanced
399 ob
.tissue_tessellate
.normals_mode
= operator
.normals_mode
400 ob
.tissue_tessellate
.bool_combine
= operator
.bool_combine
401 ob
.tissue_tessellate
.bool_multi_components
= operator
.bool_multi_components
402 ob
.tissue_tessellate
.combine_mode
= operator
.combine_mode
403 ob
.tissue_tessellate
.bounds_x
= operator
.bounds_x
404 ob
.tissue_tessellate
.bounds_y
= operator
.bounds_y
405 ob
.tissue_tessellate
.cap_faces
= operator
.cap_faces
406 ob
.tissue_tessellate
.bool_hold
= False
409 def tessellate_patch(_ob0
, _ob1
, offset
, zscale
, com_modifiers
, mode
,
410 scale_mode
, rotation_mode
, rand_seed
, bool_vertex_group
,
411 bool_selection
, bool_shapekeys
, bool_material_id
, material_id
,
413 random
.seed(rand_seed
)
415 ob0
= convert_object_to_mesh(_ob0
)
418 # Check if zero faces are selected
419 if _ob0
.type == 'MESH':
421 for p
in me0
.polygons
:
422 check_sel
= check_mat
= False
423 if not bool_selection
or p
.select
: check_sel
= True
424 if not bool_material_id
or p
.material_index
== material_id
: check_mat
= True
425 if check_sel
and check_mat
:
434 bool_multires
= False
436 not_allowed
= ['FLUID_SIMULATION', 'ARRAY', 'BEVEL', 'BOOLEAN', 'BUILD',
437 'DECIMATE', 'EDGE_SPLIT', 'MASK', 'MIRROR', 'REMESH',
438 'SCREW', 'SOLIDIFY', 'TRIANGULATE', 'WIREFRAME', 'SKIN',
439 'EXPLODE', 'PARTICLE_INSTANCE', 'PARTICLE_SYSTEM', 'SMOKE']
440 modifiers0
= list(_ob0
.modifiers
)#[m for m in ob0.modifiers]
441 show_modifiers
= [m
.show_viewport
for m
in _ob0
.modifiers
]
442 show_modifiers
.reverse()
445 visible
= m
.show_viewport
446 #m.show_viewport = False
447 if m
.type in ('SUBSURF', 'MULTIRES') and visible
:
449 multires_name
= m
.name
450 if m
.type == 'MULTIRES':
452 multires_name
= m
.name
453 sculpt_levels
= m
.sculpt_levels
454 render_levels
= m
.render_levels
455 else: bool_multires
= False
457 elif m
.type in not_allowed
:
459 #bpy.data.meshes.remove(me0)
460 return "modifiers_error"
463 #if ob0.type == 'MESH': before.data = me0
464 before_mod
= list(before
.modifiers
)
467 if m
.type in ('SUBSURF', 'MULTIRES') and m
.show_viewport
:
468 before
.modifiers
.remove(m
)
470 else: before
.modifiers
.remove(m
)
472 before_subsurf
= simple_to_mesh(before
)
474 before_bm
= bmesh
.new()
475 before_bm
.from_mesh(before_subsurf
)
476 before_bm
.faces
.ensure_lookup_table()
477 for f
in before_bm
.faces
:
478 if len(f
.loops
) != 4:
479 return "topology_error"
480 before_bm
.edges
.ensure_lookup_table()
481 for e
in before_bm
.edges
:
482 if len(e
.link_faces
) == 0:
484 before_bm
.verts
.ensure_lookup_table()
485 for v
in before_bm
.verts
:
486 if len(v
.link_faces
) == 0:
490 verts0
= me0
.vertices
# Collect generator vertices
492 if com_modifiers
or _ob1
.type != 'MESH': bool_shapekeys
= False
494 # set Shape Keys to zero
497 original_key_values
= []
498 for sk
in _ob1
.data
.shape_keys
.key_blocks
:
499 original_key_values
.append(sk
.value
)
502 bool_shapekeys
= False
504 if not com_modifiers
and not bool_shapekeys
:
506 for m
in _ob1
.modifiers
:
507 mod_visibility
.append(m
.show_viewport
)
508 m
.show_viewport
= False
511 ob1
= convert_object_to_mesh(_ob1
, com_modifiers
, False)
515 bpy
.context
.object.active_shape_key_index
= 0
517 if bounds_x
!= 'EXTEND':
519 planes_co
= ((0,0,0),(1,1,1))
522 planes_co
= (ob1
.matrix_world
@ Vector((0,0,0)), ob1
.matrix_world
@ Vector((1,0,0)))
523 plane_no
= planes_co
[0]-planes_co
[1]
524 bpy
.ops
.object.mode_set(mode
='EDIT')
526 bpy
.ops
.mesh
.select_all(action
='SELECT')
527 bpy
.ops
.mesh
.bisect(plane_co
=co
, plane_no
=plane_no
)
528 bpy
.ops
.mesh
.mark_seam()
529 bpy
.ops
.object.mode_set(mode
='OBJECT')
530 _faces
= ob1
.data
.polygons
532 for f
in [f
for f
in _faces
if (ob1
.matrix_world
@ f
.center
).x
> 1]:
534 for f
in [f
for f
in _faces
if (ob1
.matrix_world
@ f
.center
).x
< 0]:
537 for f
in [f
for f
in _faces
if f
.center
.x
> 1]:
539 for f
in [f
for f
in _faces
if f
.center
.x
< 0]:
541 bpy
.ops
.object.mode_set(mode
='EDIT')
542 bpy
.ops
.mesh
.select_mode(type='FACE')
543 if bounds_x
== 'CLIP':
544 bpy
.ops
.mesh
.delete(type='FACE')
545 bpy
.ops
.object.mode_set(mode
='OBJECT')
546 if bounds_x
== 'CYCLIC':
548 bpy
.ops
.object.mode_set(mode
='OBJECT')
550 if bounds_y
!= 'EXTEND':
552 planes_co
= ((0,0,0),(1,1,1))
555 planes_co
= (ob1
.matrix_world
@ Vector((0,0,0)), ob1
.matrix_world
@ Vector((0,1,0)))
556 plane_no
= planes_co
[0]-planes_co
[1]
557 bpy
.ops
.object.mode_set(mode
='EDIT')
559 bpy
.ops
.mesh
.select_all(action
='SELECT')
560 bpy
.ops
.mesh
.bisect(plane_co
=co
, plane_no
=plane_no
)
561 bpy
.ops
.mesh
.mark_seam()
562 bpy
.ops
.object.mode_set(mode
='OBJECT')
563 _faces
= ob1
.data
.polygons
565 for f
in [f
for f
in _faces
if (ob1
.matrix_world
@ f
.center
).y
> 1]:
567 for f
in [f
for f
in _faces
if (ob1
.matrix_world
@ f
.center
).y
< 0]:
570 for f
in [f
for f
in _faces
if f
.center
.y
> 1]:
572 for f
in [f
for f
in _faces
if f
.center
.y
< 0]:
575 bpy
.ops
.object.mode_set(mode
='EDIT')
576 bpy
.ops
.mesh
.select_mode(type='FACE')
577 if bounds_y
== 'CLIP':
578 bpy
.ops
.mesh
.delete(type='FACE')
579 bpy
.ops
.object.mode_set(mode
='OBJECT')
580 if bounds_y
== 'CYCLIC':
582 bpy
.ops
.object.mode_set(mode
='OBJECT')
583 bpy
.ops
.object.mode_set(mode
='OBJECT')
585 # Component statistics
586 n_verts
= len(me1
.vertices
)
592 new_verts_np
= np
.array(())
594 # Component bounding box
595 min_c
= Vector((0, 0, 0))
596 max_c
= Vector((0, 0, 0))
598 for v
in me1
.vertices
:
600 if vert
[0] < min_c
[0] or first
:
602 if vert
[1] < min_c
[1] or first
:
604 if vert
[2] < min_c
[2] or first
:
606 if vert
[0] > max_c
[0] or first
:
608 if vert
[1] > max_c
[1] or first
:
610 if vert
[2] > max_c
[2] or first
:
617 for v
in me1
.vertices
:
619 vert
= v
.co
- min_c
# (ob1.matrix_world * v.co) - min_c
620 vert
[0] = (vert
[0] / bb
[0] if bb
[0] != 0 else 0.5)
621 vert
[1] = (vert
[1] / bb
[1] if bb
[1] != 0 else 0.5)
622 vert
[2] = (vert
[2] + (-0.5 + offset
* 0.5) * bb
[2]) * zscale
623 elif mode
== 'LOCAL':
626 #vert[2] = (vert[2] - min_c[2] + (-0.5 + offset * 0.5) * bb[2]) * zscale
627 elif mode
== 'GLOBAL':
628 vert
= ob1
.matrix_world
@ v
.co
631 for sk
in me1
.shape_keys
.key_blocks
:
632 sk
.data
[v
.index
].co
= ob1
.matrix_world
@ sk
.data
[v
.index
].co
639 if bounds_x
== 'CYCLIC':
641 for f
in [f
for f
in me1
.polygons
if (f
.center
).x
> 1]:
643 if v
not in move_verts
: move_verts
.append(v
)
645 me1
.vertices
[v
].co
.x
-= 1
647 _ob1
.active_shape_key_index
= 0
648 for sk
in me1
.shape_keys
.key_blocks
:
652 for f
in [f
for f
in me1
.polygons
if (f
.center
).x
< 0]:
654 if v
not in move_verts
: move_verts
.append(v
)
656 me1
.vertices
[v
].co
.x
+= 1
658 _ob1
.active_shape_key_index
= 0
659 for sk
in me1
.shape_keys
.key_blocks
:
662 if bounds_y
== 'CYCLIC':
664 for f
in [f
for f
in me1
.polygons
if (f
.center
).y
> 1]:
666 if v
not in move_verts
: move_verts
.append(v
)
668 me1
.vertices
[v
].co
.y
-= 1
670 _ob1
.active_shape_key_index
= 0
671 for sk
in me1
.shape_keys
.key_blocks
:
675 for f
in [f
for f
in me1
.polygons
if (f
.center
).y
< 0]:
677 if v
not in move_verts
: move_verts
.append(v
)
679 me1
.vertices
[v
].co
.y
+= 1
681 _ob1
.active_shape_key_index
= 0
682 for sk
in me1
.shape_keys
.key_blocks
:
685 verts1
= [v
.co
for v
in me1
.vertices
]
687 patch_faces
= 4**levels
688 sides
= int(sqrt(patch_faces
))
690 patch_faces0
= int((sides
-2)**2)
691 n_patches
= int(len(me0
.polygons
)/patch_faces
)
692 if len(me0
.polygons
)%patch_faces
!= 0:
694 return "topology_error"
700 for o
in bpy
.context
.view_layer
.objects
: o
.select_set(False)
704 if bool_vertex_group
:
707 for vg
in ob0
.vertex_groups
:
709 for v
in me0
.vertices
:
711 _weight
.append(vg
.weight(v
.index
))
714 weight
.append(_weight
)
716 bool_vertex_group
= False
719 if scale_mode
== 'ADAPTIVE':
720 if mode
== 'BOUNDS': com_area
= (bb
[0]*bb
[1])
722 mult
= 1/com_area
*patch_faces
726 bm
.verts
.ensure_lookup_table()
731 area
+= f
.calc_area()
734 verts_area
.append(sqrt(area
))
736 random
.seed(rand_seed
)
739 _faces
= [[[0] for ii
in range(sides
)] for jj
in range(sides
)]
740 _verts
= [[[0] for ii
in range(sides
+1)] for jj
in range(sides
+1)]
742 for i
in range(n_patches
):
743 poly
= me0
.polygons
[i
*patch_faces
]
744 if bool_selection
and not poly
.select
: continue
745 if bool_material_id
and not poly
.material_index
== material_id
: continue
748 new_patch
= bpy
.data
.objects
.new("patch", me1
.copy())
749 bpy
.context
.collection
.objects
.link(new_patch
)
751 new_patch
.select_set(True)
752 bpy
.context
.view_layer
.objects
.active
= new_patch
754 for area
in bpy
.context
.screen
.areas
:
755 for space
in area
.spaces
:
756 try: new_patch
.local_view_set(space
, True)
760 if bool_vertex_group
:
761 for vg
in ob0
.vertex_groups
:
762 new_patch
.vertex_groups
.new(name
=vg
.name
)
765 faces
= _faces
.copy()
766 verts
= _verts
.copy()
770 for j
in range(patch_faces
):
778 elif j
< patch_faces0
+ shift1
:
781 elif j
< patch_faces0
+ shift2
:
783 v
= j
-(patch_faces0
+ sides
)+1
784 elif j
< patch_faces0
+ shift3
:
785 jj
= j
-(patch_faces0
+ shift2
)
789 jj
= j
-(patch_faces0
+ shift3
)
792 face
= me0
.polygons
[j
+i
*patch_faces
]
794 verts
[u
][v
] = verts0
[face
.vertices
[0]]
796 verts
[sides
][v
] = verts0
[face
.vertices
[1]]
798 verts
[u
][sides
] = verts0
[face
.vertices
[3]]
799 if u
== v
== sides
-1:
800 verts
[sides
][sides
] = verts0
[face
.vertices
[2]]
803 if rotation_mode
== 'RANDOM':
804 rand
= random
.randint(0, 3)
806 verts
= [[verts
[k
][w
] for w
in range(sides
,-1,-1)] for k
in range(sides
,-1,-1)]
808 verts
= [[verts
[w
][k
] for w
in range(sides
,-1,-1)] for k
in range(sides
+1)]
810 verts
= [[verts
[w
][k
] for w
in range(sides
+1)] for k
in range(sides
,-1,-1)]
813 elif rotation_mode
== 'UV' and ob0
.type == 'MESH':
814 if len(ob0
.data
.uv_layers
) > 0:
815 uv0
= me0
.uv_layers
.active
.data
[faces
[0][0].index
*4].uv
816 uv1
= me0
.uv_layers
.active
.data
[faces
[0][-1].index
*4 + 3].uv
817 uv2
= me0
.uv_layers
.active
.data
[faces
[-1][-1].index
*4 + 2].uv
818 uv3
= me0
.uv_layers
.active
.data
[faces
[-1][0].index
*4 + 1].uv
831 if(abs(dot1203
) < abs(dot0132
)):
835 verts
= [[verts
[k
][w
] for w
in range(sides
,-1,-1)] for k
in range(sides
,-1,-1)]
838 verts
= [[verts
[w
][k
] for w
in range(sides
,-1,-1)] for k
in range(sides
+1)]
840 verts
= [[verts
[w
][k
] for w
in range(sides
+1)] for k
in range(sides
,-1,-1)]
843 for vert
, patch_vert
in zip(verts1
, new_patch
.data
.vertices
):
845 u
= int(vert
[0]//step
)
846 v
= int(vert
[1]//step
)
867 fu
= (vert
[0]-u
*step
)/step
868 fv
= (vert
[1]-v
*step
)/step
870 # interpolate Z scaling factor
871 fvec2d
= Vector((fu
,fv
,0))
872 if scale_mode
== 'ADAPTIVE':
873 a00
= verts_area
[v00
.index
]
874 a10
= verts_area
[v10
.index
]
875 a01
= verts_area
[v01
.index
]
876 a11
= verts_area
[v11
.index
]
877 fw
*=lerp2(a00
,a10
,a01
,a11
,fvec2d
)
878 # build factor vector
879 fvec
= Vector((fu
,fv
,fw
))
880 # interpolate vertex on patch
881 patch_vert
.co
= lerp3(v00
, v10
, v01
, v11
, fvec
)
884 if bool_vertex_group
:
885 for _weight
, vg
in zip(weight
, new_patch
.vertex_groups
):
886 w00
= _weight
[v00
.index
]
887 w10
= _weight
[v10
.index
]
888 w01
= _weight
[v01
.index
]
889 w11
= _weight
[v11
.index
]
890 wuv
= lerp2(w00
,w10
,w01
,w11
, fvec2d
)
891 vg
.add([patch_vert
.index
], wuv
, "ADD")
894 for sk
in ob1
.data
.shape_keys
.key_blocks
:
896 for sk_v
, _v
in zip(source
, me1
.vertices
):
898 sk_vert
= sk_v
.co
- min_c
# (ob1.matrix_world * v.co) - min_c
899 sk_vert
[0] = (sk_vert
[0] / bb
[0] if bb
[0] != 0 else 0.5)
900 sk_vert
[1] = (sk_vert
[1] / bb
[1] if bb
[1] != 0 else 0.5)
901 sk_vert
[2] = (sk_vert
[2] + (-0.5 + offset
* 0.5) * bb
[2]) * zscale
902 elif mode
== 'LOCAL':
903 sk_vert
= sk_v
.co
#.xyzco
904 #sk_vert[2] *= zscale
905 #sk_vert[2] = (sk_vert[2] - min_c[2] + (-0.5 + offset * 0.5) * bb[2]) * zscale
906 elif mode
== 'GLOBAL':
907 #sk_vert = ob1.matrix_world @ sk_v.co
909 #sk_vert[2] *= zscale
912 u
= int(sk_vert
[0]//step
)
913 v
= int(sk_vert
[1]//step
)
934 fu
= (sk_vert
[0]-u
*step
)/step
935 fv
= (sk_vert
[1]-v
*step
)/step
938 if scale_mode
== 'ADAPTIVE':
939 a00
= verts_area
[v00
.index
]
940 a10
= verts_area
[v10
.index
]
941 a01
= verts_area
[v01
.index
]
942 a11
= verts_area
[v11
.index
]
943 fw
*=lerp2(a00
,a10
,a01
,a11
,Vector((fu
,fv
,0)))
945 fvec
= Vector((fu
,fv
,fw
))
946 sk_co
= lerp3(v00
, v10
, v01
, v11
, fvec
)
948 new_patch
.data
.shape_keys
.key_blocks
[sk
.name
].data
[_v
.index
].co
= sk_co
950 #if ob0.type == 'MESH': ob0.data = old_me0
951 if not bool_correct
: return 0
953 bpy
.ops
.object.join()
956 # set original values and combine Shape Keys and Vertex Groups
957 for sk
, val
in zip(_ob1
.data
.shape_keys
.key_blocks
, original_key_values
):
959 new_patch
.data
.shape_keys
.key_blocks
[sk
.name
].value
= val
960 if bool_vertex_group
:
961 for sk
in new_patch
.data
.shape_keys
.key_blocks
:
962 for vg
in new_patch
.vertex_groups
:
963 if sk
.name
== vg
.name
:
964 sk
.vertex_group
= vg
.name
966 new_name
= ob0
.name
+ "_" + ob1
.name
967 new_patch
.name
= "tessellate_temp"
970 for m
in ob0
.modifiers
:
971 if m
.type == 'MULTIRES' and m
.name
== multires_name
:
973 m
.sculpt_levels
= sculpt_levels
974 m
.render_levels
= render_levels
975 # restore original modifiers visibility for component object
977 for m
, vis
in zip(_ob1
.modifiers
, mod_visibility
):
978 m
.show_viewport
= vis
981 bpy
.data
.objects
.remove(before
)
982 bpy
.data
.objects
.remove(ob0
)
983 bpy
.data
.objects
.remove(ob1
)
986 def tessellate_original(_ob0
, _ob1
, offset
, zscale
, gen_modifiers
, com_modifiers
, mode
,
987 scale_mode
, rotation_mode
, rand_seed
, fill_mode
,
988 bool_vertex_group
, bool_selection
, bool_shapekeys
,
989 bool_material_id
, material_id
, normals_mode
, bounds_x
, bounds_y
):
991 if com_modifiers
or _ob1
.type != 'MESH': bool_shapekeys
= False
992 random
.seed(rand_seed
)
996 original_key_values
= []
997 for sk
in _ob1
.data
.shape_keys
.key_blocks
:
998 original_key_values
.append(sk
.value
)
1001 bool_shapekeys
= False
1003 ob0
= convert_object_to_mesh(_ob0
, gen_modifiers
, True)
1005 ob1
= convert_object_to_mesh(_ob1
, com_modifiers
, True)
1009 base_face_normals
= []
1011 n_faces0
= len(me0
.polygons
)
1013 # Check if zero faces are selected
1014 if (bool_selection
and ob0
.type == 'MESH') or bool_material_id
:
1015 for p
in me0
.polygons
:
1016 if (bool_selection
and ob0
.type == 'MESH'):
1019 if bool_material_id
:
1020 is_mat
= p
.material_index
== material_id
1022 if is_sel
and is_mat
:
1023 base_polygons
.append(p
)
1024 base_face_normals
.append(p
.normal
)
1026 base_polygons
= me0
.polygons
1027 base_face_normals
= [p
.normal
for p
in me0
.polygons
]
1029 # numpy test: slower
1030 #base_face_normals = np.zeros(n_faces0*3)
1031 #me0.polygons.foreach_get("normal", base_face_normals)
1032 #base_face_normals = base_face_normals.reshape((n_faces0,3))
1034 if len(base_polygons
) == 0:
1037 if mode
!= 'BOUNDS':
1039 bpy
.ops
.object.select_all(action
='DESELECT')
1040 for o
in bpy
.context
.view_layer
.objects
: o
.select_set(False)
1041 bpy
.context
.view_layer
.objects
.active
= ob1
1042 ob1
.select_set(True)
1043 bpy
.context
.object.active_shape_key_index
= 0
1045 if bounds_x
!= 'EXTEND':
1046 if mode
== 'GLOBAL':
1047 planes_co
= ((0,0,0),(1,1,1))
1050 planes_co
= (ob1
.matrix_world
@ Vector((0,0,0)), ob1
.matrix_world
@ Vector((1,0,0)))
1051 plane_no
= planes_co
[0]-planes_co
[1]
1052 bpy
.ops
.object.mode_set(mode
='EDIT')
1053 for co
in planes_co
:
1054 bpy
.ops
.mesh
.select_all(action
='SELECT')
1055 bpy
.ops
.mesh
.bisect(plane_co
=co
, plane_no
=plane_no
)
1056 bpy
.ops
.mesh
.mark_seam()
1057 bpy
.ops
.object.mode_set(mode
='OBJECT')
1058 _faces
= ob1
.data
.polygons
1059 if mode
== 'GLOBAL':
1060 for f
in [f
for f
in _faces
if (ob1
.matrix_world
@ f
.center
).x
> 1]:
1062 for f
in [f
for f
in _faces
if (ob1
.matrix_world
@ f
.center
).x
< 0]:
1065 for f
in [f
for f
in _faces
if f
.center
.x
> 1]:
1067 for f
in [f
for f
in _faces
if f
.center
.x
< 0]:
1069 bpy
.ops
.object.mode_set(mode
='EDIT')
1070 bpy
.ops
.mesh
.select_mode(type='FACE')
1071 if bounds_x
== 'CLIP':
1072 bpy
.ops
.mesh
.delete(type='FACE')
1073 bpy
.ops
.object.mode_set(mode
='OBJECT')
1074 if bounds_x
== 'CYCLIC':
1075 bpy
.ops
.mesh
.split()
1076 bpy
.ops
.object.mode_set(mode
='OBJECT')
1078 if bounds_y
!= 'EXTEND':
1079 if mode
== 'GLOBAL':
1080 planes_co
= ((0,0,0),(1,1,1))
1083 planes_co
= (ob1
.matrix_world
@ Vector((0,0,0)), ob1
.matrix_world
@ Vector((0,1,0)))
1084 plane_no
= planes_co
[0]-planes_co
[1]
1085 bpy
.ops
.object.mode_set(mode
='EDIT')
1086 for co
in planes_co
:
1087 bpy
.ops
.mesh
.select_all(action
='SELECT')
1088 bpy
.ops
.mesh
.bisect(plane_co
=co
, plane_no
=plane_no
)
1089 bpy
.ops
.mesh
.mark_seam()
1090 bpy
.ops
.object.mode_set(mode
='OBJECT')
1091 _faces
= ob1
.data
.polygons
1092 if mode
== 'GLOBAL':
1093 for f
in [f
for f
in _faces
if (ob1
.matrix_world
@ f
.center
).y
> 1]:
1095 for f
in [f
for f
in _faces
if (ob1
.matrix_world
@ f
.center
).y
< 0]:
1098 for f
in [f
for f
in _faces
if f
.center
.y
> 1]:
1100 for f
in [f
for f
in _faces
if f
.center
.y
< 0]:
1103 bpy
.ops
.object.mode_set(mode
='EDIT')
1104 bpy
.ops
.mesh
.select_mode(type='FACE')
1105 if bounds_y
== 'CLIP':
1106 bpy
.ops
.mesh
.delete(type='FACE')
1107 bpy
.ops
.object.mode_set(mode
='OBJECT')
1108 if bounds_y
== 'CYCLIC':
1109 bpy
.ops
.mesh
.split()
1110 bpy
.ops
.object.mode_set(mode
='OBJECT')
1111 bpy
.ops
.object.mode_set(mode
='OBJECT')
1116 verts0
= me0
.vertices
# Collect generator vertices
1118 # Component statistics
1119 n_verts1
= len(me1
.vertices
)
1120 n_edges1
= len(me1
.edges
)
1121 n_faces1
= len(me1
.polygons
)
1123 # Create empty lists
1127 new_verts_np
= np
.array(())
1129 # Component Coordinates
1130 co1
= [0]*n_verts1
*3
1132 if mode
== 'GLOBAL':
1133 for v
in me1
.vertices
:
1134 v
.co
= ob1
.matrix_world
@ v
.co
1136 for sk
in me1
.shape_keys
.key_blocks
:
1137 sk
.data
[v
.index
].co
= ob1
.matrix_world
@ sk
.data
[v
.index
].co
1139 if mode
!= 'BOUNDS':
1140 if bounds_x
== 'CYCLIC':
1142 for f
in [f
for f
in me1
.polygons
if (f
.center
).x
> 1]:
1143 for v
in f
.vertices
:
1144 if v
not in move_verts
: move_verts
.append(v
)
1145 for v
in move_verts
:
1146 me1
.vertices
[v
].co
.x
-= 1
1148 _ob1
.active_shape_key_index
= 0
1149 for sk
in me1
.shape_keys
.key_blocks
:
1150 sk
.data
[v
].co
.x
-= 1
1153 for f
in [f
for f
in me1
.polygons
if (f
.center
).x
< 0]:
1154 for v
in f
.vertices
:
1155 if v
not in move_verts
: move_verts
.append(v
)
1156 for v
in move_verts
:
1157 me1
.vertices
[v
].co
.x
+= 1
1159 _ob1
.active_shape_key_index
= 0
1160 for sk
in me1
.shape_keys
.key_blocks
:
1161 sk
.data
[v
].co
.x
+= 1
1163 if bounds_y
== 'CYCLIC':
1165 for f
in [f
for f
in me1
.polygons
if (f
.center
).y
> 1]:
1166 for v
in f
.vertices
:
1167 if v
not in move_verts
: move_verts
.append(v
)
1168 for v
in move_verts
:
1169 me1
.vertices
[v
].co
.y
-= 1
1171 #new_ob1.active_shape_key_index = 0
1172 for sk
in me1
.shape_keys
.key_blocks
:
1173 sk
.data
[v
].co
.y
-= 1
1176 for f
in [f
for f
in me1
.polygons
if (f
.center
).y
< 0]:
1177 for v
in f
.vertices
:
1178 if v
not in move_verts
: move_verts
.append(v
)
1179 for v
in move_verts
:
1180 me1
.vertices
[v
].co
.y
+= 1
1182 #new_ob1.active_shape_key_index = 0
1183 for sk
in me1
.shape_keys
.key_blocks
:
1184 sk
.data
[v
].co
.y
+= 1
1188 me1
.vertices
.foreach_get("co", co1
)
1190 vx
= co1
[0::3].reshape((n_verts1
,1))
1191 vy
= co1
[1::3].reshape((n_verts1
,1))
1192 vz
= co1
[2::3].reshape((n_verts1
,1))
1193 min_c
= Vector((vx
.min(), vy
.min(), vz
.min())) # Min BB Corner
1194 max_c
= Vector((vx
.max(), vy
.max(), vz
.max())) # Max BB Corner
1195 bb
= max_c
- min_c
# Bounding Box
1197 # Component Coordinates
1198 if mode
== 'BOUNDS':
1199 vx
= (vx
- min_c
[0]) / bb
[0] if bb
[0] != 0 else 0.5
1200 vy
= (vy
- min_c
[1]) / bb
[1] if bb
[1] != 0 else 0.5
1201 vz
= ((vz
- min_c
[2]) + (-0.5 + offset
* 0.5) * bb
[2]) * zscale
1205 # Component polygons
1206 fs1
= [[i
for i
in p
.vertices
] for p
in me1
.polygons
]
1210 es1
= np
.array([[i
for i
in e
.vertices
] for e
in me1
.edges
])
1211 #es1 = [[i for i in e.vertices] for e in me1.edges if e.is_loose]
1216 basis
= True #com_modifiers
1221 for sk
in ob1
.data
.shape_keys
.key_blocks
:
1224 for _sk
in ob1
.data
.shape_keys
.key_blocks
: _sk
.value
= 0
1231 # Apply component modifiers
1233 sk_ob
= convert_object_to_mesh(_ob1
)
1234 sk_data
= sk_ob
.data
1235 source
= sk_data
.vertices
1241 if mode
== 'BOUNDS':
1243 vert
[0] = vert
[0] / bb
[0]
1244 vert
[1] = vert
[1] / bb
[1]
1245 vert
[2] = (vert
[2] + (-0.5 + offset
* 0.5) * bb
[2]) * zscale
1246 elif mode
== 'LOCAL':
1249 #vert[2] = (vert[2] - min_c[2] + (-0.5 + offset * 0.5) * bb[2]) * \
1251 elif mode
== 'GLOBAL':
1253 #vert = ob1.matrix_world @ v.co
1255 shapekeys
.append(vert
)
1257 # Component vertices
1258 key1
= np
.array([v
for v
in shapekeys
]).reshape(len(shapekeys
), 3, 1)
1259 vx_key
.append(key1
[:, 0])
1260 vy_key
.append(key1
[:, 1])
1261 vz_key
.append(key1
[:, 2])
1265 if bool_vertex_group
:
1268 vertex_groups
= ob0
.vertex_groups
1269 for vg
in vertex_groups
:
1271 for v
in me0
.vertices
:
1273 _weight
.append(vg
.weight(v
.index
))
1276 weight
.append(_weight
)
1278 bool_vertex_group
= False
1281 if scale_mode
== 'ADAPTIVE':
1282 if mode
== 'BOUNDS': com_area
= (bb
[0]*bb
[1])
1284 if com_area
== 0: mult
= 1
1285 else: mult
= 1/com_area
1289 bm
.verts
.ensure_lookup_table()
1292 faces
= v
.link_faces
1294 area
+= f
.calc_area()
1298 verts_area
.append(sqrt(area
))
1300 verts_area
.append(1)
1302 # FAN tessellation mode
1303 if fill_mode
== 'FAN':
1304 fan_verts
= [v
.co
.to_tuple() for v
in me0
.vertices
]
1309 # selected_faces = []
1310 for p
in base_polygons
:
1311 fan_center
= Vector((0, 0, 0))
1313 for v
in p
.vertices
:
1314 fan_center
+= me0
.vertices
[v
].co
1315 if scale_mode
== 'ADAPTIVE':
1316 center_area
+= verts_area
[v
]
1317 fan_center
/= len(p
.vertices
)
1318 center_area
/= len(p
.vertices
)
1320 last_vert
= len(fan_verts
)
1321 fan_verts
.append(fan_center
.to_tuple())
1322 #fan_verts.append(fan_center)
1323 if scale_mode
== 'ADAPTIVE':
1324 verts_area
.append(center_area
)
1327 if bool_vertex_group
:
1329 center_weight
= sum([w
[i
] for i
in p
.vertices
]) / len(p
.vertices
)
1330 w
.append(center_weight
)
1332 for i
in range(len(p
.vertices
)):
1333 fan_polygons
.append((p
.vertices
[i
],
1334 p
.vertices
[(i
+ 1) % len(p
.vertices
)],
1335 last_vert
, last_vert
))
1337 if bool_material_id
: fan_material
.append(p
.material_index
)
1338 if bool_selection
: fan_select
.append(p
.select
)
1339 if normals_mode
== 'FACES':
1340 fan_normals
.append(p
.normal
)
1342 fan_me
= bpy
.data
.meshes
.new('Fan.Mesh')
1343 fan_me
.from_pydata(tuple(fan_verts
), [], tuple(fan_polygons
))
1345 bpy
.data
.meshes
.remove(fan_me
)
1346 verts0
= me0
.vertices
1347 base_polygons
= me0
.polygons
1348 if normals_mode
== 'FACES': base_face_normals
= fan_normals
1350 count
= 0 # necessary for UV calculation
1355 bool_correct
= False
1358 n_faces
= len(base_polygons
)
1362 _w0
= [[0]*n_faces
]*len(ob0
.vertex_groups
)
1363 np_faces
= [np
.array(p
) for p
in fs1
]
1364 new_faces
= [0]*n_faces
*n_faces1
1367 for p
in base_polygons
:
1370 if rotation_mode
== 'UV' and ob0
.type != 'MESH':
1371 rotation_mode
= 'DEFAULT'
1374 if rotation_mode
== 'RANDOM':
1375 shifted_vertices
= []
1376 n_poly_verts
= len(p
.vertices
)
1377 rand
= random
.randint(0, n_poly_verts
)
1378 for i
in range(n_poly_verts
):
1379 shifted_vertices
.append(p
.vertices
[(i
+ rand
) % n_poly_verts
])
1380 if scale_mode
== 'ADAPTIVE':
1381 verts_area0
= np
.array([verts_area
[i
] for i
in shifted_vertices
])
1382 vs0
= np
.array([verts0
[i
].co
for i
in shifted_vertices
])
1383 nvs0
= np
.array([verts0
[i
].normal
for i
in shifted_vertices
])
1384 if normals_mode
== 'VERTS':
1385 nvs0
= np
.array([verts0
[i
].normal
for i
in shifted_vertices
])
1387 if bool_vertex_group
:
1391 for i
in shifted_vertices
:
1396 ws0
.append(np
.array(_ws0
))
1399 elif rotation_mode
== 'UV':
1400 if len(ob0
.data
.uv_layers
) > 0 and fill_mode
!= 'FAN':
1402 if bool_material_id
:
1403 count
= sum([len(p
.vertices
) for p
in me0
.polygons
[:i
]])
1404 #if i == 0: count = 0
1405 v01
= (me0
.uv_layers
.active
.data
[count
].uv
+
1406 me0
.uv_layers
.active
.data
[count
+ 1].uv
)
1407 if len(p
.vertices
) > 3:
1408 v32
= (me0
.uv_layers
.active
.data
[count
+ 3].uv
+
1409 me0
.uv_layers
.active
.data
[count
+ 2].uv
)
1411 v32
= (me0
.uv_layers
.active
.data
[count
].uv
+
1412 me0
.uv_layers
.active
.data
[count
+ 2].uv
)
1416 v12
= (me0
.uv_layers
.active
.data
[count
+ 1].uv
+
1417 me0
.uv_layers
.active
.data
[count
+ 2].uv
)
1418 if len(p
.vertices
) > 3:
1419 v03
= (me0
.uv_layers
.active
.data
[count
].uv
+
1420 me0
.uv_layers
.active
.data
[count
+ 3].uv
)
1422 v03
= (me0
.uv_layers
.active
.data
[count
].uv
+
1423 me0
.uv_layers
.active
.data
[count
].uv
)
1430 if(abs(dot1203
) < abs(dot0132
)):
1432 vertUV
= p
.vertices
[1:] + p
.vertices
[:1]
1434 vertUV
= p
.vertices
[3:] + p
.vertices
[:3]
1437 vertUV
= p
.vertices
[:]
1439 vertUV
= p
.vertices
[2:] + p
.vertices
[:2]
1440 vs0
= np
.array([verts0
[i
].co
for i
in vertUV
])
1441 nvs0
= np
.array([verts0
[i
].normal
for i
in vertUV
])
1444 if bool_vertex_group
:
1453 ws0
.append(np
.array(_ws0
))
1455 count
+= len(p
.vertices
)
1456 else: rotation_mode
= 'DEFAULT'
1459 if rotation_mode
== 'DEFAULT':
1460 vs0
= np
.array([verts0
[i
].co
for i
in p
.vertices
])
1461 nvs0
= np
.array([verts0
[i
].normal
for i
in p
.vertices
])
1463 if bool_vertex_group
:
1467 for i
in p
.vertices
:
1472 ws0
.append(np
.array(_ws0
))
1475 _vs0
[j
] = (vs0
[0], vs0
[1], vs0
[2], vs0
[-1])
1476 if normals_mode
== 'VERTS':
1477 _nvs0
[j
] = (nvs0
[0], nvs0
[1], nvs0
[2], nvs0
[-1])
1479 # _nvs0[j] = base_face_normals[j]
1482 # vertex z to normal
1483 if scale_mode
== 'ADAPTIVE':
1484 poly_faces
= (p
.vertices
[0], p
.vertices
[1], p
.vertices
[2], p
.vertices
[-1])
1485 if rotation_mode
== 'RANDOM': sz
= verts_area0
1486 else: sz
= np
.array([verts_area
[i
] for i
in poly_faces
])
1490 if bool_vertex_group
:
1493 _w0
[vg_count
][j
] = (_ws0
[0], _ws0
[1], _ws0
[2], _ws0
[-1])
1497 new_faces
[face1_count
] = [i
+ n_verts1
* j
for i
in p
]
1503 n_edges1
= new_edges
.shape
[0]
1504 new_edges
= new_edges
.reshape((1, n_edges1
, 2))
1505 new_edges
= new_edges
.repeat(n_faces
,axis
=0)
1506 new_edges
= new_edges
.reshape((n_edges1
*n_faces
, 2))
1507 increment
= np
.arange(n_faces
)*n_verts1
1508 increment
= increment
.repeat(n_edges1
, axis
=0)
1509 increment
= increment
.reshape((n_faces
*n_edges1
,1))
1510 new_edges
= new_edges
+ increment
1513 _vs0
= np
.array(_vs0
)
1516 _vs0_0
= _vs0
[:,0].reshape((n_faces
,1,3))
1517 _vs0_1
= _vs0
[:,1].reshape((n_faces
,1,3))
1518 _vs0_2
= _vs0
[:,2].reshape((n_faces
,1,3))
1519 _vs0_3
= _vs0
[:,3].reshape((n_faces
,1,3))
1521 # remapped vertex coordinates
1522 v0
= _vs0_0
+ (_vs0_1
- _vs0_0
) * vx
1523 v1
= _vs0_3
+ (_vs0_2
- _vs0_3
) * vx
1524 v2
= v0
+ (v1
- v0
) * vy
1526 # remapped vertex normal
1527 if normals_mode
== 'VERTS':
1528 _nvs0
= np
.array(_nvs0
)
1529 _nvs0_0
= _nvs0
[:,0].reshape((n_faces
,1,3))
1530 _nvs0_1
= _nvs0
[:,1].reshape((n_faces
,1,3))
1531 _nvs0_2
= _nvs0
[:,2].reshape((n_faces
,1,3))
1532 _nvs0_3
= _nvs0
[:,3].reshape((n_faces
,1,3))
1533 nv0
= _nvs0_0
+ (_nvs0_1
- _nvs0_0
) * vx
1534 nv1
= _nvs0_3
+ (_nvs0_2
- _nvs0_3
) * vx
1535 nv2
= nv0
+ (nv1
- nv0
) * vy
1537 nv2
= np
.array(base_face_normals
).reshape((n_faces
,1,3))
1539 if bool_vertex_group
:
1544 w_0
= w
[:,:,0].reshape((n_vg
, n_faces
,1,1))
1545 w_1
= w
[:,:,1].reshape((n_vg
, n_faces
,1,1))
1546 w_2
= w
[:,:,2].reshape((n_vg
, n_faces
,1,1))
1547 w_3
= w
[:,:,3].reshape((n_vg
, n_faces
,1,1))
1549 w0
= w_0
+ (w_1
- w_0
) * vx
1550 w1
= w_3
+ (w_2
- w_3
) * vx
1551 w
= w0
+ (w1
- w0
) * vy
1552 w
= w
.reshape((n_vg
, n_faces
*n_verts1
))
1555 if scale_mode
== 'ADAPTIVE':
1556 _sz_0
= _sz
[:,0].reshape((n_faces
,1,1))
1557 _sz_1
= _sz
[:,1].reshape((n_faces
,1,1))
1558 _sz_2
= _sz
[:,2].reshape((n_faces
,1,1))
1559 _sz_3
= _sz
[:,3].reshape((n_faces
,1,1))
1561 sz0
= _sz_0
+ (_sz_1
- _sz_0
) * vx
1562 sz1
= _sz_3
+ (_sz_2
- _sz_3
) * vx
1563 sz2
= sz0
+ (sz1
- sz0
) * vy
1564 v3
= v2
+ nv2
* vz
* sz2
1568 new_verts_np
= v3
.reshape((n_faces
*n_verts1
,3))
1573 for i
in range(n_sk
):
1574 vx
= np
.array(vx_key
)
1575 vy
= np
.array(vy_key
)
1576 vz
= np
.array(vz_key
)
1578 # remapped vertex coordinates
1579 v0
= _vs0_0
+ (_vs0_1
- _vs0_0
) * vx
1580 v1
= _vs0_3
+ (_vs0_2
- _vs0_3
) * vx
1581 v2
= v0
+ (v1
- v0
) * vy
1583 # remapped vertex normal
1584 if normals_mode
== 'VERTS':
1585 nv0
= _nvs0_0
+ (_nvs0_1
- _nvs0_0
) * vx
1586 nv1
= _nvs0_3
+ (_nvs0_2
- _nvs0_3
) * vx
1587 nv2
= nv0
+ (nv1
- nv0
) * vy
1589 nv2
= np
.array(base_face_normals
).reshape((n_faces
,1,3))
1591 if scale_mode
== 'ADAPTIVE':
1593 sz0
= _sz_0
+ (_sz_1
- _sz_0
) * vx
1594 sz1
= _sz_3
+ (_sz_2
- _sz_3
) * vx
1595 sz2
= sz0
+ (sz1
- sz0
) * vy
1596 v3
= v2
+ nv2
* vz
* sz2
1600 sk_np
[i
] = v3
.reshape((n_faces
*n_verts1
,3))
1602 #if ob0.type == 'MESH': ob0.data = old_me0
1604 if not bool_correct
: return 0
1606 new_verts
= new_verts_np
.tolist()
1607 new_name
= ob0
.name
+ "_" + ob1
.name
1608 new_me
= bpy
.data
.meshes
.new(new_name
)
1609 new_me
.from_pydata(new_verts
, new_edges
.tolist(), new_faces
)
1610 new_me
.update(calc_edges
=True)
1611 new_ob
= bpy
.data
.objects
.new("tessellate_temp", new_me
)
1614 if bool_vertex_group
and False:
1615 for vg
in ob0
.vertex_groups
:
1616 new_ob
.vertex_groups
.new(name
=vg
.name
)
1617 for i
in range(len(vg_np
[vg
.index
])):
1618 new_ob
.vertex_groups
[vg
.name
].add([i
], vg_np
[vg
.index
][i
],"ADD")
1620 if bool_vertex_group
:
1621 for vg
in ob0
.vertex_groups
:
1622 new_ob
.vertex_groups
.new(name
=vg
.name
)
1623 for i
in range(len(w
[vg
.index
])):
1624 new_ob
.vertex_groups
[vg
.name
].add([i
], w
[vg
.index
,i
],"ADD")
1627 basis
= com_modifiers
1629 for sk
, val
in zip(_ob1
.data
.shape_keys
.key_blocks
, original_key_values
):
1631 new_ob
.shape_key_add(name
=sk
.name
)
1632 new_ob
.data
.shape_keys
.key_blocks
[sk
.name
].value
= val
1633 # set shape keys vertices
1634 sk_data
= new_ob
.data
.shape_keys
.key_blocks
[sk
.name
].data
1638 for id in range(len(sk_data
)):
1639 sk_data
[id].co
= sk_np
[sk_count
-1][id]
1641 if bool_vertex_group
:
1642 for sk
in new_ob
.data
.shape_keys
.key_blocks
:
1643 for vg
in new_ob
.vertex_groups
:
1644 if sk
.name
== vg
.name
:
1645 sk
.vertex_group
= vg
.name
1648 edge_data
= [0]*n_edges1
1649 me1
.edges
.foreach_get("use_seam",edge_data
)
1651 edge_data
= edge_data
*n_faces
1652 new_ob
.data
.edges
.foreach_set("use_seam",edge_data
)
1655 edge_data
= [0]*n_edges1
1656 me1
.edges
.foreach_get("use_edge_sharp",edge_data
)
1658 edge_data
= edge_data
*n_faces
1659 new_ob
.data
.edges
.foreach_set("use_edge_sharp",edge_data
)
1661 bpy
.ops
.object.select_all(action
='DESELECT')
1662 bpy
.context
.collection
.objects
.link(new_ob
)
1663 new_ob
.select_set(True)
1664 bpy
.context
.view_layer
.objects
.active
= new_ob
1667 edge_data
= [0]*n_edges1
1668 me1
.edges
.foreach_get("bevel_weight",edge_data
)
1670 bpy
.ops
.object.mode_set(mode
='EDIT')
1671 bpy
.ops
.mesh
.select_all(action
='SELECT')
1672 bpy
.ops
.transform
.edge_bevelweight(value
=1)
1673 bpy
.ops
.object.mode_set(mode
='OBJECT')
1674 edge_data
= edge_data
*n_faces
1675 new_ob
.data
.edges
.foreach_set("bevel_weight",edge_data
)
1678 edge_data
= [0]*n_edges1
1679 me1
.edges
.foreach_get("crease",edge_data
)
1681 bpy
.ops
.object.mode_set(mode
='EDIT')
1682 bpy
.ops
.mesh
.select_all(action
='SELECT')
1683 bpy
.ops
.transform
.edge_crease(value
=1)
1684 bpy
.ops
.object.mode_set(mode
='OBJECT')
1685 edge_data
= edge_data
*n_faces
1686 new_ob
.data
.edges
.foreach_set('crease', edge_data
)
1689 for slot
in ob1
.material_slots
: new_ob
.data
.materials
.append(slot
.material
)
1692 polygon_materials
= [0]*n_faces1
1693 me1
.polygons
.foreach_get("material_index", polygon_materials
)
1694 polygon_materials
*= n_faces
1695 new_ob
.data
.polygons
.foreach_set("material_index", polygon_materials
)
1696 new_ob
.data
.update() ###
1699 bpy
.data
.objects
.remove(new_ob1
)
1702 bpy
.data
.objects
.remove(ob0
)
1703 bpy
.data
.meshes
.remove(me0
)
1704 bpy
.data
.objects
.remove(ob1
)
1705 bpy
.data
.meshes
.remove(me1
)
1709 class tessellate(Operator
):
1710 bl_idname
= "object.tessellate"
1711 bl_label
= "Tessellate"
1712 bl_description
= ("Create a copy of selected object on the active object's "
1713 "faces, adapting the shape to the different faces")
1714 bl_options
= {'REGISTER', 'UNDO'}
1717 object_name
: StringProperty(
1719 description
="Name of the generated object"
1721 zscale
: FloatProperty(
1726 description
="Scale factor for the component thickness"
1728 scale_mode
: EnumProperty(
1730 ('CONSTANT', "Constant", "Uniform thickness"),
1731 ('ADAPTIVE', "Proportional", "Preserve component's proportions")
1734 name
="Z-Scale according to faces size"
1736 offset
: FloatProperty(
1737 name
="Surface Offset",
1742 description
="Surface offset"
1744 mode
: EnumProperty(
1746 ('BOUNDS', "Bounds", "The component fits automatically the size of the target face"),
1747 ('LOCAL', "Local", "Based on Local coordinates, from 0 to 1"),
1748 ('GLOBAL', 'Global', "Based on Global coordinates, from 0 to 1")),
1750 name
="Component Mode"
1752 rotation_mode
: EnumProperty(
1753 items
=(('RANDOM', "Random", "Random faces rotation"),
1754 ('UV', "Active UV", "Face rotation is based on UV coordinates"),
1755 ('DEFAULT', "Default", "Default rotation")),
1757 name
="Component Rotation"
1759 fill_mode
: EnumProperty(
1761 ('QUAD', 'Quad', 'Regular quad tessellation. Uses only 3 or 4 vertices'),
1762 ('FAN', 'Fan', 'Radial tessellation for polygonal faces'),
1763 ('PATCH', 'Patch', 'Curved tessellation according to the last ' +
1764 'Subsurf\n(or Multires) modifiers. Works only with 4 sides ' +
1765 'patches.\nAfter the last Subsurf (or Multires) only ' +
1766 'deformation\nmodifiers can be used')),
1770 combine_mode
: EnumProperty(
1772 ('LAST', 'Last', 'Show only the last iteration'),
1773 ('UNUSED', 'Unused', 'Combine each iteration with the unused faces of the previous iteration. Used for branching systems'),
1774 ('ALL', 'All', 'Combine the result of all iterations')),
1776 name
="Combine Mode",
1778 gen_modifiers
: BoolProperty(
1779 name
="Generator Modifiers",
1781 description
="Apply Modifiers and Shape Keys to the base object"
1783 com_modifiers
: BoolProperty(
1784 name
="Component Modifiers",
1786 description
="Apply Modifiers and Shape Keys to the component object"
1788 merge
: BoolProperty(
1791 description
="Merge vertices in adjacent duplicates"
1793 merge_thres
: FloatProperty(
1798 description
="Limit below which to merge vertices"
1800 bool_random
: BoolProperty(
1803 description
="Randomize component rotation"
1805 random_seed
: IntProperty(
1810 description
="Random seed"
1812 bool_vertex_group
: BoolProperty(
1813 name
="Map Vertex Groups",
1815 description
="Transfer all Vertex Groups from Base object"
1817 bool_selection
: BoolProperty(
1818 name
="On selected Faces",
1820 description
="Create Tessellation only on selected faces"
1822 bool_shapekeys
: BoolProperty(
1823 name
="Use Shape Keys",
1825 description
="Transfer Component's Shape Keys. If the name of Vertex "
1826 "Groups and Shape Keys are the same, they will be "
1827 "automatically combined"
1829 bool_smooth
: BoolProperty(
1830 name
="Smooth Shading",
1832 description
="Output faces with smooth shading rather than flat shaded"
1834 bool_materials
: BoolProperty(
1835 name
="Transfer Materials",
1837 description
="Preserve component's materials"
1839 generator
: StringProperty(
1841 description
="Base object for the tessellation",
1844 component
: StringProperty(
1846 description
="Component object for the tessellation",
1849 bool_material_id
: BoolProperty(
1850 name
="Tessellation on Material ID",
1852 description
="Apply the component only on the selected Material"
1854 bool_dissolve_seams
: BoolProperty(
1855 name
="Dissolve Seams",
1857 description
="Dissolve all seam edges"
1859 material_id
: IntProperty(
1863 description
="Material ID"
1865 iterations
: IntProperty(
1870 description
="Automatically repeat the Tessellation using the "
1871 + "generated geometry as new base object.\nUsefull for "
1872 + "for branching systems. Dangerous!"
1874 bool_combine
: BoolProperty(
1875 name
="Combine unused",
1877 description
="Combine the generated geometry with unused faces"
1879 bool_advanced
: BoolProperty(
1880 name
="Advanced Settings",
1882 description
="Show more settings"
1884 normals_mode
: EnumProperty(
1886 ('VERTS', 'Along Normals', 'Consistent direction based on vertices normal'),
1887 ('FACES', 'Individual Faces', 'Based on individual faces normal')),
1891 bool_multi_components
: BoolProperty(
1892 name
="Multi Components",
1894 description
="Combine different components according to materials name"
1896 bounds_x
: EnumProperty(
1898 ('EXTEND', 'Extend', 'Default X coordinates'),
1899 ('CLIP', 'Clip', 'Trim out of bounds in X direction'),
1900 ('CYCLIC', 'Cyclic', 'Cyclic components in X direction')),
1904 bounds_y
: EnumProperty(
1906 ('EXTEND', 'Extend', 'Default Y coordinates'),
1907 ('CLIP', 'Clip', 'Trim out of bounds in Y direction'),
1908 ('CYCLIC', 'Cyclic', 'Cyclic components in Y direction')),
1912 cap_faces
: BoolProperty(
1915 description
="Cap open edges loops"
1917 open_edges_crease
: FloatProperty(
1918 name
="Open Edges Crease",
1922 description
="Automatically set crease for open edges"
1926 def draw(self
, context
):
1927 allowed_obj
= ('MESH', 'CURVE', 'SURFACE', 'FONT', 'META')
1930 bool_working = self.working_on == self.object_name and \
1931 self.working_on != ""
1933 bool_working = False
1936 bool_working
= False
1937 bool_allowed
= False
1941 sel
= bpy
.context
.selected_objects
1944 ob0
= sel
[0].tissue_tessellate
.generator
1945 ob1
= sel
[0].tissue_tessellate
.component
1946 self
.generator
= ob0
.name
1947 self
.component
= ob1
.name
1956 if o
.type not in allowed_obj
:
1957 bool_allowed
= False
1959 if len(sel
) != 2 and not bool_working
:
1960 layout
= self
.layout
1961 layout
.label(icon
='INFO')
1962 layout
.label(text
="Please, select two different objects")
1963 layout
.label(text
="Select first the Component object, then select")
1964 layout
.label(text
="the Base object.")
1965 elif not bool_allowed
and not bool_working
:
1966 layout
= self
.layout
1967 layout
.label(icon
='INFO')
1968 layout
.label(text
="Only Mesh, Curve, Surface or Text objects are allowed")
1970 if ob0
== ob1
== None:
1971 ob0
= bpy
.context
.active_object
1972 self
.generator
= ob0
.name
1976 self
.component
= o
.name
1977 self
.no_component
= False
1981 if self
.object_name
== "":
1982 if self
.generator
== "":
1983 self
.object_name
= "Tessellation"
1985 #self.object_name = self.generator + "_Tessellation"
1986 self
.object_name
= "Tessellation"
1988 layout
= self
.layout
1989 # Base and Component
1990 col
= layout
.column(align
=True)
1991 row
= col
.row(align
=True)
1992 row
.label(text
="BASE : " + self
.generator
)
1993 row
.label(text
="COMPONENT : " + self
.component
)
1996 row
= col
.row(align
=True)
1997 col2
= row
.column(align
=True)
1998 col2
.prop(self
, "gen_modifiers", text
="Use Modifiers", icon
='MODIFIER')
1999 base
= bpy
.data
.objects
[self
.generator
]
2001 if not (base
.modifiers
or base
.data
.shape_keys
):
2002 col2
.enabled
= False
2003 self
.gen_modifiers
= False
2005 col2
.enabled
= False
2006 self
.gen_modifiers
= False
2008 # Component Modifiers
2010 col3
= row
.column(align
=True)
2011 col3
.prop(self
, "com_modifiers", text
="Use Modifiers", icon
='MODIFIER')
2012 component
= bpy
.data
.objects
[self
.component
]
2014 if not (component
.modifiers
or component
.data
.shape_keys
):
2015 col3
.enabled
= False
2016 self
.com_modifiers
= False
2018 col3
.enabled
= False
2019 self
.com_modifiers
= False
2022 row
= col
.row(align
=True)
2023 row
.label(text
="Fill Mode:")
2024 row
.label(text
="Rotation:")
2025 row
= col
.row(align
=True)
2026 #col2 = row.column(align=True)
2028 self
, "fill_mode", text
="", icon
='NONE', expand
=False,
2029 slider
=True, toggle
=False, icon_only
=False, event
=False,
2030 full_event
=False, emboss
=True, index
=-1)
2034 col2
= row
.column(align
=True)
2036 self
, "rotation_mode", text
="", icon
='NONE', expand
=False,
2037 slider
=True, toggle
=False, icon_only
=False, event
=False,
2038 full_event
=False, emboss
=True, index
=-1)
2039 if self
.rotation_mode
== 'RANDOM':
2040 col2
.prop(self
, "random_seed")
2042 if self
.rotation_mode
== 'UV':
2044 if self
.fill_mode
== 'FAN':
2045 row
= col
.row(align
=True)
2046 row
.label(text
="UV rotation doesn't work in FAN mode",
2050 if ob0
.type != 'MESH':
2051 row
= col
.row(align
=True)
2053 text
="UV rotation supported only for Mesh objects",
2057 if len(ob0
.data
.uv_layers
) == 0:
2058 row
= col
.row(align
=True)
2059 check_name
= self
.generator
2060 row
.label(text
="'" + check_name
+
2061 "' doesn't have UV Maps", icon
='ERROR')
2064 row
= col
.row(align
=True)
2065 row
.label(text
="Default rotation will be used instead",
2069 row
= col
.row(align
=True)
2070 row
.label(text
="Component Coordinates:")
2071 row
= col
.row(align
=True)
2073 self
, "mode", text
="Component XY", icon
='NONE', expand
=True,
2074 slider
=False, toggle
=False, icon_only
=False, event
=False,
2075 full_event
=False, emboss
=True, index
=-1)
2077 if self
.mode
!= 'BOUNDS':
2079 row
= col
.row(align
=True)
2080 row
.label(text
="X:")
2082 self
, "bounds_x", text
="Bounds X", icon
='NONE', expand
=True,
2083 slider
=False, toggle
=False, icon_only
=False, event
=False,
2084 full_event
=False, emboss
=True, index
=-1)
2086 row
= col
.row(align
=True)
2087 row
.label(text
="Y:")
2089 self
, "bounds_y", text
="Bounds X", icon
='NONE', expand
=True,
2090 slider
=False, toggle
=False, icon_only
=False, event
=False,
2091 full_event
=False, emboss
=True, index
=-1)
2094 col
.label(text
="Thickness:")
2095 row
= col
.row(align
=True)
2097 self
, "scale_mode", text
="Scale Mode", icon
='NONE', expand
=True,
2098 slider
=False, toggle
=False, icon_only
=False, event
=False,
2099 full_event
=False, emboss
=True, index
=-1)
2101 self
, "zscale", text
="Scale", icon
='NONE', expand
=False,
2102 slider
=True, toggle
=False, icon_only
=False, event
=False,
2103 full_event
=False, emboss
=True, index
=-1)
2104 if self
.mode
== 'BOUNDS':
2106 self
, "offset", text
="Offset", icon
='NONE', expand
=False,
2107 slider
=True, toggle
=False, icon_only
=False, event
=False,
2108 full_event
=False, emboss
=True, index
=-1)
2111 row
= col
.row(align
=True)
2112 row
.label(text
="Direction:")
2113 row
= col
.row(align
=True)
2115 self
, "normals_mode", text
="Direction", icon
='NONE', expand
=True,
2116 slider
=False, toggle
=False, icon_only
=False, event
=False,
2117 full_event
=False, emboss
=True, index
=-1)
2118 row
.enabled
= self
.fill_mode
!= 'PATCH'
2121 col
= layout
.column(align
=True)
2122 row
= col
.row(align
=True)
2123 row
.prop(self
, "merge")
2125 row
.prop(self
, "merge_thres")
2126 row
= col
.row(align
=True)
2128 row
= col
.row(align
=True)
2129 row
.prop(self
, "bool_smooth")
2131 col2
= row
.column(align
=True)
2132 col2
.prop(self
, "bool_dissolve_seams")
2133 #if ob1.type != 'MESH': col2.enabled = False
2135 row
= col
.row(align
=True)
2136 row
.prop(self
, "cap_faces")
2138 col2
= row
.column(align
=True)
2139 col2
.prop(self
, "open_edges_crease", text
="Crease")
2142 col
= layout
.column(align
=True)
2145 row
= col
.row(align
=True)
2146 row
.prop(self
, "bool_advanced", icon
='SETTINGS')
2147 if self
.bool_advanced
:
2149 allow_shapekeys
= not self
.com_modifiers
2150 for m
in ob0
.data
.materials
:
2152 o
= bpy
.data
.objects
[m
.name
]
2155 if o
.data
.shape_keys
is None: continue
2156 elif len(o
.data
.shape_keys
.key_blocks
) < 2: continue
2157 else: allow_shapekeys
= not self
.com_modifiers
2161 col
= layout
.column(align
=True)
2162 col
.label(text
="Morphing:")
2163 # vertex group + shape keys
2164 row
= col
.row(align
=True)
2165 col2
= row
.column(align
=True)
2166 col2
.prop(self
, "bool_vertex_group", icon
='GROUP_VERTEX')
2167 #col2.prop_search(props, "vertex_group", props.generator, "vertex_groups")
2169 if len(ob0
.vertex_groups
) == 0:
2170 col2
.enabled
= False
2172 col2
.enabled
= False
2174 col2
= row
.column(align
=True)
2175 row2
= col2
.row(align
=True)
2176 row2
.prop(self
, "bool_shapekeys", text
="Use Shape Keys", icon
='SHAPEKEY_DATA')
2177 row2
.enabled
= allow_shapekeys
2179 # LIMITED TESSELLATION
2180 col
= layout
.column(align
=True)
2181 col
.label(text
="Limited Tessellation:")
2182 row
= col
.row(align
=True)
2183 col2
= row
.column(align
=True)
2184 col2
.prop(self
, "bool_multi_components", icon
='MOD_TINT')
2186 col2
.enabled
= False
2187 self
.bool_multi_components
= False
2189 row
= col
.row(align
=True)
2190 col2
= row
.column(align
=True)
2191 col2
.prop(self
, "bool_selection", text
="On selected Faces", icon
='RESTRICT_SELECT_OFF')
2192 #if self.bool_material_id or self.bool_selection or self.bool_multi_components:
2193 #col2 = row.column(align=True)
2194 # col2.prop(self, "bool_combine")
2196 if ob0
.type != 'MESH':
2197 col2
.enabled
= False
2198 col2
= row
.column(align
=True)
2199 col2
.prop(self
, "bool_material_id", icon
='MATERIAL_DATA', text
="Material ID")
2200 if self
.bool_material_id
and not self
.bool_multi_components
:
2201 #col2 = row.column(align=True)
2202 col2
.prop(self
, "material_id")
2203 col2
.enabled
= not self
.bool_multi_components
2206 row
= col
.row(align
=True)
2207 row
.label(text
='Reiterate Tessellation:', icon
='FILE_REFRESH')
2208 row
.prop(self
, 'iterations', text
='Repeat', icon
='SETTINGS')
2211 row
= col
.row(align
=True)
2212 row
.label(text
='Combine Iterations:')
2213 row
= col
.row(align
=True)
2215 self
, "combine_mode", icon
='NONE', expand
=True,
2216 slider
=False, toggle
=False, icon_only
=False, event
=False,
2217 full_event
=False, emboss
=True, index
=-1)
2219 def execute(self
, context
):
2220 allowed_obj
= ('MESH', 'CURVE', 'META', 'SURFACE', 'FONT')
2222 ob0
= bpy
.data
.objects
[self
.generator
]
2223 ob1
= bpy
.data
.objects
[self
.component
]
2225 return {'CANCELLED'}
2227 self
.object_name
= "Tessellation"
2228 # Check if existing object with same name
2229 names
= [o
.name
for o
in bpy
.data
.objects
]
2230 if self
.object_name
in names
:
2233 test_name
= self
.object_name
+ '.{:03d}'.format(count_name
)
2234 if not (test_name
in names
):
2235 self
.object_name
= test_name
2239 if ob1
.type not in allowed_obj
:
2240 message
= "Component must be Mesh, Curve, Surface, Text or Meta object!"
2241 self
.report({'ERROR'}, message
)
2242 self
.component
= None
2244 if ob0
.type not in allowed_obj
:
2245 message
= "Generator must be Mesh, Curve, Surface, Text or Meta object!"
2246 self
.report({'ERROR'}, message
)
2249 if True:#self.component not in ("",None) and self.generator not in ("",None):
2250 if bpy
.ops
.object.select_all
.poll():
2251 bpy
.ops
.object.select_all(action
='TOGGLE')
2252 bpy
.ops
.object.mode_set(mode
='OBJECT')
2254 #data0 = ob0.to_mesh(False)
2255 #data0 = ob0.data.copy()
2257 if bpy
.context
.object == ob0
:
2258 auto_layer_collection()
2259 #new_ob = bpy.data.objects.new(self.object_name, data0)
2260 new_ob
= convert_object_to_mesh(ob0
,False,False)
2261 new_ob
.data
.name
= self
.object_name
2262 #bpy.context.collection.objects.link(new_ob)
2263 #bpy.context.view_layer.objects.active = new_ob
2264 new_ob
.name
= self
.object_name
2265 #new_ob.select_set(True)
2267 new_ob
= bpy
.context
.object
2269 new_ob
= store_parameters(self
, new_ob
)
2270 try: bpy
.ops
.object.update_tessellate()
2271 except RuntimeError as e
:
2272 bpy
.data
.objects
.remove(new_ob
)
2273 self
.report({'ERROR'}, str(e
))
2274 return {'CANCELLED'}
2276 self
.object_name
= new_ob
.name
2277 #self.working_on = self.object_name
2278 new_ob
.location
= ob0
.location
2279 new_ob
.matrix_world
= ob0
.matrix_world
2283 def invoke(self
, context
, event
):
2284 return context
.window_manager
.invoke_props_dialog(self
)
2287 class update_tessellate(Operator
):
2288 bl_idname
= "object.update_tessellate"
2289 bl_label
= "Refresh"
2290 bl_description
= ("Fast update the tessellated mesh according to base and "
2291 "component changes")
2292 bl_options
= {'REGISTER', 'UNDO'}
2297 def poll(cls
, context
):
2299 try: #context.object == None: return False
2300 return context
.object.tissue_tessellate
.generator
!= None and \
2301 context
.object.tissue_tessellate
.component
!= None
2306 def check_gen_comp(checking
):
2307 # note pass the stored name key in here to check it out
2308 return checking
in bpy
.data
.objects
.keys()
2310 def execute(self
, context
):
2311 start_time
= time
.time()
2313 ob
= bpy
.context
.object
2315 generator
= ob
.tissue_tessellate
.generator
2316 component
= ob
.tissue_tessellate
.component
2317 zscale
= ob
.tissue_tessellate
.zscale
2318 scale_mode
= ob
.tissue_tessellate
.scale_mode
2319 rotation_mode
= ob
.tissue_tessellate
.rotation_mode
2320 offset
= ob
.tissue_tessellate
.offset
2321 merge
= ob
.tissue_tessellate
.merge
2322 merge_thres
= ob
.tissue_tessellate
.merge_thres
2323 gen_modifiers
= ob
.tissue_tessellate
.gen_modifiers
2324 com_modifiers
= ob
.tissue_tessellate
.com_modifiers
2325 bool_random
= ob
.tissue_tessellate
.bool_random
2326 random_seed
= ob
.tissue_tessellate
.random_seed
2327 fill_mode
= ob
.tissue_tessellate
.fill_mode
2328 bool_vertex_group
= ob
.tissue_tessellate
.bool_vertex_group
2329 bool_selection
= ob
.tissue_tessellate
.bool_selection
2330 bool_shapekeys
= ob
.tissue_tessellate
.bool_shapekeys
2331 mode
= ob
.tissue_tessellate
.mode
2332 bool_smooth
= ob
.tissue_tessellate
.bool_smooth
2333 bool_materials
= ob
.tissue_tessellate
.bool_materials
2334 bool_dissolve_seams
= ob
.tissue_tessellate
.bool_dissolve_seams
2335 bool_material_id
= ob
.tissue_tessellate
.bool_material_id
2336 material_id
= ob
.tissue_tessellate
.material_id
2337 iterations
= ob
.tissue_tessellate
.iterations
2338 bool_combine
= ob
.tissue_tessellate
.bool_combine
2339 normals_mode
= ob
.tissue_tessellate
.normals_mode
2340 bool_advanced
= ob
.tissue_tessellate
.bool_advanced
2341 bool_multi_components
= ob
.tissue_tessellate
.bool_multi_components
2342 combine_mode
= ob
.tissue_tessellate
.combine_mode
2343 bounds_x
= ob
.tissue_tessellate
.bounds_x
2344 bounds_y
= ob
.tissue_tessellate
.bounds_y
2345 cap_faces
= ob
.tissue_tessellate
.cap_faces
2346 open_edges_crease
= ob
.tissue_tessellate
.open_edges_crease
2352 self
.report({'ERROR'},
2353 "Active object must be Tessellate before Update")
2354 return {'CANCELLED'}
2356 # Solve Local View issues
2360 for area
in bpy
.context
.screen
.areas
:
2361 for space
in area
.spaces
:
2363 if ob
.local_view_get(space
):
2364 local_spaces
.append(space
)
2365 local_ob0
= ob0
.local_view_get(space
)
2366 ob0
.local_view_set(space
, True)
2367 local_ob1
= ob1
.local_view_get(space
)
2368 ob1
.local_view_set(space
, True)
2372 starting_mode
= bpy
.context
.object.mode
2373 #if starting_mode == 'PAINT_WEIGHT': starting_mode = 'WEIGHT_PAINT'
2374 bpy
.ops
.object.mode_set(mode
='OBJECT')
2378 auto_layer_collection()
2380 ob0_hide
= ob0
.hide_get()
2381 ob0_hidev
= ob0
.hide_viewport
2382 ob0_hider
= ob0
.hide_render
2383 ob1_hide
= ob1
.hide_get()
2384 ob1_hidev
= ob1
.hide_viewport
2385 ob1_hider
= ob1
.hide_render
2387 ob0
.hide_viewport
= False
2388 ob0
.hide_render
= False
2390 ob1
.hide_viewport
= False
2391 ob1
.hide_render
= False
2393 if ob0
.type == 'META':
2394 base_ob
= convert_object_to_mesh(ob0
, False, True)
2396 base_ob
= ob0
.copy()
2397 base_ob
.data
= ob0
.data
.copy()
2398 bpy
.context
.collection
.objects
.link(base_ob
)
2400 # In Blender 2.80 cache of copied objects is lost, must be re-baked
2401 bool_update_cloth
= False
2402 for m
in base_ob
.modifiers
:
2403 if m
.type == 'CLOTH':
2404 m
.point_cache
.frame_end
= bpy
.context
.scene
.frame_current
2405 bool_update_cloth
= True
2406 if bool_update_cloth
:
2407 bpy
.ops
.ptcache
.free_bake_all()
2408 bpy
.ops
.ptcache
.bake_all()
2410 #new_ob.location = ob.location
2411 #new_ob.matrix_world = ob.matrix_world
2412 base_ob
.modifiers
.update()
2413 bpy
.ops
.object.select_all(action
='DESELECT')
2414 iter_objects
= [base_ob
]
2415 #base_ob = new_ob#.copy()
2417 for iter in range(iterations
):
2419 matched_materials
= []
2420 if bool_multi_components
: mat_iter
= len(base_ob
.material_slots
)
2422 for m_id
in range(mat_iter
):
2423 if bool_multi_components
:
2425 mat
= base_ob
.material_slots
[m_id
].material
2426 ob1
= bpy
.data
.objects
[mat
.name
]
2428 matched_materials
.append(m_id
)
2429 bool_material_id
= True
2433 data1
= simple_to_mesh(ob1
)
2434 else: data1
= ob1
.data
.copy()
2435 n_edges1
= len(data1
.edges
)
2437 if iter != 0: gen_modifiers
= True
2438 if fill_mode
== 'PATCH':
2439 new_ob
= tessellate_patch(
2440 base_ob
, ob1
, offset
, zscale
, com_modifiers
, mode
, scale_mode
,
2441 rotation_mode
, random_seed
, bool_vertex_group
,
2442 bool_selection
, bool_shapekeys
, bool_material_id
, material_id
,
2446 new_ob
= tessellate_original(
2447 base_ob
, ob1
, offset
, zscale
, gen_modifiers
,
2448 com_modifiers
, mode
, scale_mode
, rotation_mode
,
2449 random_seed
, fill_mode
, bool_vertex_group
,
2450 bool_selection
, bool_shapekeys
, bool_material_id
,
2451 material_id
, normals_mode
, bounds_x
, bounds_y
2453 if type(new_ob
) is bpy
.types
.Object
:
2454 bpy
.context
.view_layer
.objects
.active
= new_ob
2457 n_components
= int(len(new_ob
.data
.edges
) / n_edges1
)
2461 # create selection list
2462 polygon_selection
= [p
.select
for p
in ob1
.data
.polygons
] * int(
2463 len(new_ob
.data
.polygons
) / len(ob1
.data
.polygons
))
2464 new_ob
.data
.polygons
.foreach_set("select", polygon_selection
)
2468 if type(new_ob
) == str: break
2470 if bool_multi_components
and type(new_ob
) not in (int,str):
2471 same_iteration
.append(new_ob
)
2472 new_ob
.select_set(True)
2473 bpy
.context
.view_layer
.objects
.active
= new_ob
2475 if type(new_ob
) == str: break
2477 #bpy.data.objects.remove(base_ob)
2478 if bool_multi_components
:
2479 bpy
.context
.view_layer
.update()
2480 bpy
.context
.view_layer
.objects
.active
.select_set(True)
2481 for o
in bpy
.data
.objects
:
2482 if o
in same_iteration
:
2484 o
.location
= ob
.location
2489 bpy
.ops
.object.join()
2490 new_ob
= bpy
.context
.view_layer
.objects
.active
2491 new_ob
.select_set(True)
2492 new_ob
.data
.update()
2496 if (bool_selection
or bool_material_id
) and combine_mode
== 'UNUSED':
2497 # remove faces from last mesh
2500 last_mesh
= iter_objects
[-1].data
.copy()
2502 bm
.from_mesh(last_mesh
)
2503 bm
.faces
.ensure_lookup_table()
2504 if bool_multi_components
:
2505 remove_materials
= matched_materials
2506 elif bool_material_id
:
2507 remove_materials
= [material_id
]
2508 else: remove_materials
= []
2510 remove_faces
= [f
for f
in bm
.faces
if f
.material_index
in remove_materials
and f
.select
]
2512 remove_faces
= [f
for f
in bm
.faces
if f
.material_index
in remove_materials
]
2513 bmesh
.ops
.delete(bm
, geom
=remove_faces
, context
='FACES')
2514 bm
.to_mesh(last_mesh
)
2517 if len(last_mesh
.vertices
) > 0:
2518 iter_objects
[-1].data
= last_mesh
.copy()
2519 iter_objects
[-1].data
.update()
2521 bpy
.data
.objects
.remove(iter_objects
[-1])
2522 iter_objects
= iter_objects
[:-1]
2524 base_ob
= convert_object_to_mesh(new_ob
,True,True)
2525 #bpy.context.collection.objects.unlink(base_ob)
2526 if iter < iterations
-1: new_ob
.data
= base_ob
.data
2528 iter_objects
.append(new_ob
)
2529 new_ob
.location
= ob
.location
2530 new_ob
.matrix_world
= ob
.matrix_world
2532 bpy
.data
.objects
.remove(bpy
.data
.objects
['_Tessellation_Base'])
2534 base_ob
.name
= "_Tessellation_Base"
2535 elif combine_mode
== 'ALL':
2536 base_ob
= new_ob
.copy()
2537 iter_objects
.append(new_ob
)
2538 new_ob
.location
= ob
.location
2539 new_ob
.matrix_world
= ob
.matrix_world
2541 if base_ob
!= new_ob
:
2542 bpy
.data
.objects
.remove(base_ob
)
2544 iter_objects
= [new_ob
]
2547 #for m, vis in zip(ob.modifiers, mod_visibility): m.show_viewport = vis
2548 message
= "Zero faces selected in the Base mesh!"
2549 bpy
.ops
.object.mode_set(mode
=starting_mode
)
2550 self
.report({'ERROR'}, message
)
2551 return {'CANCELLED'}
2553 errors
["modifiers_error"] = "Modifiers that change the topology of the mesh \n" \
2554 "after the last Subsurf (or Multires) are not allowed."
2555 errors
["topology_error"] = "Make sure that the topology of the mesh before \n" \
2556 "the last Subsurf (or Multires) is quads only."
2557 errors
["wires_error"] = "Please remove all wire edges in the base object."
2558 errors
["verts_error"] = "Please remove all floating vertices in the base object"
2559 if new_ob
in errors
:
2560 for o
in iter_objects
: bpy
.data
.objects
.remove(o
)
2561 bpy
.context
.view_layer
.objects
.active
= ob
2563 message
= errors
[new_ob
]
2564 ob
.tissue_tessellate
.error_message
= message
2565 bpy
.ops
.object.mode_set(mode
=starting_mode
)
2566 self
.report({'ERROR'}, message
)
2567 return {'CANCELLED'}
2569 new_ob
.location
= ob
.location
2570 new_ob
.matrix_world
= ob
.matrix_world
2573 if combine_mode
!= 'LAST' and len(iter_objects
)>0:
2574 if base_ob
not in iter_objects
: bpy
.data
.objects
.remove(base_ob
)
2575 for o
in iter_objects
:
2576 o
.location
= ob
.location
2578 bpy
.ops
.object.join()
2579 new_ob
.data
.update()
2581 # update data and preserve name
2582 if ob
.type != 'MESH':
2583 loc
, matr
= ob
.location
, ob
.matrix_world
2584 ob
= convert_object_to_mesh(ob
,False,True)
2585 ob
.location
, ob
.matrix_world
= loc
, matr
2586 data_name
= ob
.data
.name
2588 ob
.data
= new_ob
.data
2589 bpy
.data
.meshes
.remove(old_data
)
2590 ob
.data
.name
= data_name
2593 if bool_vertex_group
:
2594 for vg
in new_ob
.vertex_groups
:
2595 if not vg
.name
in ob
.vertex_groups
.keys():
2596 ob
.vertex_groups
.new(name
=vg
.name
)
2597 new_vg
= ob
.vertex_groups
[vg
.name
]
2598 for i
in range(len(ob
.data
.vertices
)):
2600 weight
= vg
.weight(i
)
2603 new_vg
.add([i
], weight
, 'REPLACE')
2605 selected_objects
= [o
for o
in bpy
.context
.selected_objects
]
2606 for o
in selected_objects
: o
.select_set(False)
2609 bpy
.context
.view_layer
.objects
.active
= ob
2610 bpy
.data
.objects
.remove(new_ob
)
2613 bpy
.ops
.object.mode_set(mode
='EDIT')
2614 bpy
.ops
.mesh
.select_mode(
2615 use_extend
=False, use_expand
=False, type='VERT')
2616 bpy
.ops
.mesh
.select_non_manifold(
2617 extend
=False, use_wire
=False, use_boundary
=True,
2618 use_multi_face
=False, use_non_contiguous
=False, use_verts
=False)
2620 bpy
.ops
.mesh
.remove_doubles(
2621 threshold
=merge_thres
, use_unselected
=False)
2623 bpy
.ops
.object.mode_set(mode
='OBJECT')
2624 if bool_dissolve_seams
:
2625 bpy
.ops
.object.mode_set(mode
='EDIT')
2626 bpy
.ops
.mesh
.select_mode(type='EDGE')
2627 bpy
.ops
.mesh
.select_all(action
='DESELECT')
2628 bpy
.ops
.object.mode_set(mode
='OBJECT')
2629 for e
in ob
.data
.edges
:
2630 e
.select
= e
.use_seam
2631 bpy
.ops
.object.mode_set(mode
='EDIT')
2632 bpy
.ops
.mesh
.dissolve_edges()
2634 bpy
.ops
.object.mode_set(mode
='EDIT')
2635 bpy
.ops
.mesh
.select_mode(
2636 use_extend
=False, use_expand
=False, type='EDGE')
2637 bpy
.ops
.mesh
.select_non_manifold(
2638 extend
=False, use_wire
=False, use_boundary
=True,
2639 use_multi_face
=False, use_non_contiguous
=False, use_verts
=False)
2640 bpy
.ops
.mesh
.edge_face_add()
2641 if open_edges_crease
!= 0:
2642 bpy
.ops
.transform
.edge_crease(value
=open_edges_crease
)
2644 bpy
.ops
.object.mode_set(mode
='EDIT')
2645 bpy
.ops
.object.mode_set(mode
='OBJECT')
2647 if bool_smooth
: bpy
.ops
.object.shade_smooth()
2648 ####values = [True] * len(ob.data.polygons)
2649 ####ob.data.polygons.foreach_set("use_smooth", values)
2651 #for m, vis in zip(ob.modifiers, mod_visibility): m.show_viewport = vis
2653 end_time
= time
.time()
2654 print('Tissue: object "{}" tessellated in {:.4f} sec'.format(ob
.name
, end_time
-start_time
))
2656 for mesh
in bpy
.data
.meshes
:
2657 if not mesh
.users
: bpy
.data
.meshes
.remove(mesh
)
2659 for o
in selected_objects
:
2660 try: o
.select_set(True)
2663 bpy
.ops
.object.mode_set(mode
=starting_mode
)
2666 for o
in bpy
.data
.objects
:
2667 if o
.name
not in context
.view_layer
.objects
and "temp" in o
.name
:
2668 bpy
.data
.objects
.remove(o
)
2670 ob
.tissue_tessellate
.error_message
= ""
2672 # Restore Base visibility
2673 ob0
.hide_set(ob0_hide
)
2674 ob0
.hide_viewport
= ob0_hidev
2675 ob0
.hide_render
= ob0_hider
2676 # Restore Component visibility
2677 ob1
.hide_set(ob1_hide
)
2678 ob1
.hide_viewport
= ob1_hidev
2679 ob1
.hide_render
= ob1_hider
2680 # Restore Local visibility
2681 for space
, local0
, local1
in zip(local_spaces
, local_ob0
, local_ob1
):
2682 ob0
.local_view_set(space
, local0
)
2683 ob1
.local_view_set(space
, local1
)
2687 def check(self
, context
):
2690 class TISSUE_PT_tessellate(Panel
):
2691 bl_label
= "Tissue Tools"
2692 bl_category
= "Edit"
2693 bl_space_type
= "VIEW_3D"
2694 bl_region_type
= "UI"
2695 bl_options
= {'DEFAULT_CLOSED'}
2698 def poll(cls
, context
):
2699 return context
.mode
in {'OBJECT', 'EDIT_MESH'}
2701 def draw(self
, context
):
2702 layout
= self
.layout
2704 col
= layout
.column(align
=True)
2705 col
.label(text
="Tessellate:")
2706 col
.operator("object.tessellate")
2707 col
.operator("object.dual_mesh_tessellated")
2709 #col = layout.column(align=True)
2710 #col.label(text="Tessellate Edit:")
2711 #col.operator("object.settings_tessellate")
2712 col
.operator("object.update_tessellate", icon
='FILE_REFRESH')
2714 #col = layout.column(align=True)
2715 col
.operator("mesh.rotate_face", icon
='NDOF_TURN')
2718 col
.label(text
="Other:")
2719 col
.operator("object.dual_mesh")
2720 col
.operator("object.lattice_along_surface", icon
="OUTLINER_OB_LATTICE")
2722 act
= context
.active_object
2723 if act
and act
.type == 'MESH':
2724 col
.operator("object.uv_to_mesh", icon
="UV")
2727 class TISSUE_PT_tessellate_object(Panel
):
2728 bl_space_type
= 'PROPERTIES'
2729 bl_region_type
= 'WINDOW'
2731 bl_label
= "Tissue - Tessellate"
2732 bl_options
= {'DEFAULT_CLOSED'}
2735 def poll(cls
, context
):
2736 try: return context
.object.type == 'MESH'
2737 except: return False
2739 def draw(self
, context
):
2741 props
= ob
.tissue_tessellate
2742 allowed_obj
= ('MESH','CURVE','SURFACE','FONT', 'META')
2745 bool_tessellated
= props
.generator
or props
.component
!= None
2746 ob0
= props
.generator
2747 ob1
= props
.component
2748 except: bool_tessellated
= False
2749 layout
= self
.layout
2750 if not bool_tessellated
:
2751 layout
.label(text
="The selected object is not a Tessellated object",
2754 if props
.error_message
!= "":
2755 layout
.label(text
=props
.error_message
,
2757 col
= layout
.column(align
=True)
2758 row
= col
.row(align
=True)
2760 set_tessellate_handler(self
,context
)
2761 set_animatable_fix_handler(self
,context
)
2762 row
.prop(props
, "bool_run", text
="Animatable")
2763 row
.operator("object.update_tessellate", icon
='FILE_REFRESH')
2765 col
= layout
.column(align
=True)
2766 row
= col
.row(align
=True)
2767 row
.label(text
="BASE :")
2768 row
.label(text
="COMPONENT :")
2769 row
= col
.row(align
=True)
2771 col2
= row
.column(align
=True)
2772 col2
.prop_search(props
, "generator", context
.scene
, "objects")
2774 col2
= row
.column(align
=True)
2775 col2
.prop_search(props
, "component", context
.scene
, "objects")
2776 row
= col
.row(align
=True)
2777 col2
= row
.column(align
=True)
2778 col2
.prop(props
, "gen_modifiers", text
="Use Modifiers", icon
='MODIFIER')
2781 if not (ob0
.modifiers
or ob0
.data
.shape_keys
) or props
.fill_mode
== 'PATCH':
2782 col2
.enabled
= False
2784 col2
.enabled
= False
2785 col2
= row
.column(align
=True)
2786 col2
.prop(props
, "com_modifiers", text
="Use Modifiers", icon
='MODIFIER')
2788 if not (props
.component
.modifiers
or props
.component
.data
.shape_keys
):
2789 col2
.enabled
= False
2791 col2
.enabled
= False
2795 row
= col
.row(align
=True)
2796 row
.label(text
="Fill Mode:")
2798 row
.label(text
="Rotation:")
2799 row
= col
.row(align
=True)
2802 row
.prop(props
, "fill_mode", text
="", icon
='NONE', expand
=False,
2803 slider
=True, toggle
=False, icon_only
=False, event
=False,
2804 full_event
=False, emboss
=True, index
=-1)
2808 col2
= row
.column(align
=True)
2809 col2
.prop(props
, "rotation_mode", text
="", icon
='NONE', expand
=False,
2810 slider
=True, toggle
=False, icon_only
=False, event
=False,
2811 full_event
=False, emboss
=True, index
=-1)
2813 if props
.rotation_mode
== 'RANDOM':
2814 #row = col.row(align=True)
2815 col2
.prop(props
, "random_seed")
2817 if props
.rotation_mode
== 'UV':
2819 if props
.fill_mode
== 'FAN':
2820 row
= col
.row(align
=True)
2821 row
.label(text
="UV rotation doesn't work in FAN mode",
2824 if props
.generator
.type != 'MESH':
2825 row
= col
.row(align
=True)
2827 text
="UV rotation supported only for Mesh objects",
2831 if len(props
.generator
.data
.uv_layers
) == 0:
2832 row
= col
.row(align
=True)
2833 row
.label(text
="'" + props
.generator
.name
+
2834 " doesn't have UV Maps", icon
='ERROR')
2837 row
= col
.row(align
=True)
2838 row
.label(text
="Default rotation will be used instead",
2842 row
= col
.row(align
=True)
2843 row
.label(text
="Component Coordinates:")
2844 row
= col
.row(align
=True)
2845 row
.prop(props
, "mode", expand
=True)
2847 if props
.mode
!= 'BOUNDS':
2849 row
= col
.row(align
=True)
2850 row
.label(text
="X:")
2852 props
, "bounds_x", text
="Bounds X", icon
='NONE', expand
=True,
2853 slider
=False, toggle
=False, icon_only
=False, event
=False,
2854 full_event
=False, emboss
=True, index
=-1)
2856 row
= col
.row(align
=True)
2857 row
.label(text
="Y:")
2859 props
, "bounds_y", text
="Bounds X", icon
='NONE', expand
=True,
2860 slider
=False, toggle
=False, icon_only
=False, event
=False,
2861 full_event
=False, emboss
=True, index
=-1)
2864 col
.label(text
="Thickness:")
2865 row
= col
.row(align
=True)
2866 row
.prop(props
, "scale_mode", expand
=True)
2867 col
.prop(props
, "zscale", text
="Scale", icon
='NONE', expand
=False,
2868 slider
=True, toggle
=False, icon_only
=False, event
=False,
2869 full_event
=False, emboss
=True, index
=-1)
2870 if props
.mode
== 'BOUNDS':
2871 col
.prop(props
, "offset", text
="Offset", icon
='NONE', expand
=False,
2872 slider
=True, toggle
=False, icon_only
=False, event
=False,
2873 full_event
=False, emboss
=True, index
=-1)
2876 row
= col
.row(align
=True)
2877 row
.label(text
="Direction:")
2878 row
= col
.row(align
=True)
2880 props
, "normals_mode", text
="Direction", icon
='NONE', expand
=True,
2881 slider
=False, toggle
=False, icon_only
=False, event
=False,
2882 full_event
=False, emboss
=True, index
=-1)
2883 row
.enabled
= props
.fill_mode
!= 'PATCH'
2886 col
= layout
.column(align
=True)
2887 row
= col
.row(align
=True)
2888 row
.prop(props
, "merge")
2890 row
.prop(props
, "merge_thres")
2891 row
= col
.row(align
=True)
2892 row
.prop(props
, "bool_smooth")
2894 col2
= row
.column(align
=True)
2895 col2
.prop(props
, "bool_dissolve_seams")
2896 #if props.component.type != 'MESH': col2.enabled = False
2898 row
= col
.row(align
=True)
2899 row
.prop(props
, "cap_faces")
2901 col2
= row
.column(align
=True)
2902 col2
.prop(props
, "open_edges_crease", text
="Crease")
2905 col
= layout
.column(align
=True)
2908 row
= col
.row(align
=True)
2909 row
.prop(props
, "bool_advanced", icon
='SETTINGS')
2910 if props
.bool_advanced
:
2912 allow_shapekeys
= not props
.com_modifiers
2913 for m
in ob0
.data
.materials
:
2915 o
= bpy
.data
.objects
[m
.name
]
2918 if o
.data
.shape_keys
is None: continue
2919 elif len(o
.data
.shape_keys
.key_blocks
) < 2: continue
2920 else: allow_shapekeys
= not props
.com_modifiers
2924 col
= layout
.column(align
=True)
2925 col
.label(text
="Morphing:")
2926 row
= col
.row(align
=True)
2927 col2
= row
.column(align
=True)
2928 col2
.prop(props
, "bool_vertex_group", icon
='GROUP_VERTEX')
2929 #col2.prop_search(props, "vertex_group", props.generator, "vertex_groups")
2931 if len(props
.generator
.vertex_groups
) == 0:
2932 col2
.enabled
= False
2934 col2
.enabled
= False
2936 col2
= row
.column(align
=True)
2937 row2
= col2
.row(align
=True)
2938 row2
.prop(props
, "bool_shapekeys", text
="Use Shape Keys", icon
='SHAPEKEY_DATA')
2939 row2
.enabled
= allow_shapekeys
2941 # LIMITED TESSELLATION
2942 col
= layout
.column(align
=True)
2943 col
.label(text
="Limited Tessellation:")
2944 row
= col
.row(align
=True)
2945 col2
= row
.column(align
=True)
2946 col2
.prop(props
, "bool_multi_components", icon
='MOD_TINT')
2948 col2
.enabled
= False
2950 row
= col
.row(align
=True)
2951 col2
= row
.column(align
=True)
2952 col2
.prop(props
, "bool_selection", text
="On selected Faces", icon
='RESTRICT_SELECT_OFF')
2953 #if props.bool_material_id or props.bool_selection or props.bool_multi_components:
2954 #col2 = row.column(align=True)
2955 # col2.prop(props, "bool_combine")
2957 if props
.generator
.type != 'MESH':
2958 col2
.enabled
= False
2959 col2
= row
.column(align
=True)
2960 col2
.prop(props
, "bool_material_id", icon
='MATERIAL_DATA', text
="Material ID")
2961 if props
.bool_material_id
and not props
.bool_multi_components
:
2962 #col2 = row.column(align=True)
2963 col2
.prop(props
, "material_id")
2964 if props
.bool_multi_components
:
2965 col2
.enabled
= False
2967 # TRANSFER DATA ### OFF
2968 if props
.fill_mode
!= 'PATCH' and False:
2969 col
= layout
.column(align
=True)
2970 col
.label(text
="Component Data:")
2971 row
= col
.row(align
=True)
2972 col2
= row
.column(align
=True)
2973 col2
.prop(props
, "bool_materials", icon
='MATERIAL_DATA')
2975 col2
= row
.column(align
=True)
2976 if props
.fill_mode
== 'PATCH':
2978 col
.label(text
='Not needed in Patch mode', icon
='INFO')
2981 row
= col
.row(align
=True)
2982 row
.label(text
='Reiterate Tessellation:', icon
='FILE_REFRESH')
2983 row
.prop(props
, 'iterations', text
='Repeat', icon
='SETTINGS')
2985 row
= col
.row(align
=True)
2986 row
.label(text
='Combine Iterations:')
2987 row
= col
.row(align
=True)
2989 props
, "combine_mode", text
="Combine:",icon
='NONE', expand
=True,
2990 slider
=False, toggle
=False, icon_only
=False, event
=False,
2991 full_event
=False, emboss
=True, index
=-1)
2993 class rotate_face(Operator
):
2994 bl_idname
= "mesh.rotate_face"
2995 bl_label
= "Rotate Faces"
2996 bl_description
= "Rotate selected faces and update tessellated meshes"
2997 bl_options
= {'REGISTER', 'UNDO'}
3000 def poll(cls
, context
):
3001 return context
.mode
== 'EDIT_MESH'
3003 def execute(self
, context
):
3004 ob
= bpy
.context
.active_object
3007 bm
= bmesh
.from_edit_mesh(me
)
3008 mesh_select_mode
= [sm
for sm
in context
.tool_settings
.mesh_select_mode
]
3010 for face
in bm
.faces
:
3014 material_index
= face
.material_index
3015 bm
.faces
.remove(face
)
3016 f2
= bm
.faces
.new(vs2
)
3018 f2
.material_index
= material_index
3022 bmesh
.update_edit_mesh(me
)
3023 ob
.select_set(False)
3025 # update tessellated meshes
3026 bpy
.ops
.object.mode_set(mode
='OBJECT')
3027 for o
in [obj
for obj
in bpy
.data
.objects
if
3028 obj
.tissue_tessellate
.generator
== ob
and obj
.visible_get()]:
3029 bpy
.context
.view_layer
.objects
.active
= o
3030 bpy
.ops
.object.update_tessellate()
3033 bpy
.context
.view_layer
.objects
.active
= ob
3034 bpy
.ops
.object.mode_set(mode
='EDIT')
3035 context
.tool_settings
.mesh_select_mode
= mesh_select_mode