1 # SPDX-License-Identifier: GPL-2.0-or-later
3 # Another Noise Tool - Functions
7 # Michel Anders, Ian Huish
11 from bpy
.props
import (
22 from .ant_noise
import noise_gen
24 # ------------------------------------------------------------
25 # Create a new mesh (object) from verts/edges/faces.
26 # verts/edges/faces ... List of vertices/edges/faces for the
27 # new mesh (as used in from_pydata).
28 # name ... Name of the new mesh (& object)
30 from bpy_extras
import object_utils
32 def create_mesh_object(context
, verts
, edges
, faces
, name
):
34 mesh
= bpy
.data
.meshes
.new(name
)
35 # Make a mesh from a list of verts/edges/faces.
36 mesh
.from_pydata(verts
, [], faces
)
37 # Update mesh geometry after adding stuff.
39 return object_utils
.object_data_add(context
, mesh
, operator
=None)
43 def grid_gen(sub_d_x
, sub_d_y
, tri
, meshsize_x
, meshsize_y
, props
, water_plane
, water_level
):
46 vappend
= verts
.append
47 fappend
= faces
.append
48 for i
in range (0, sub_d_x
):
49 x
= meshsize_x
* (i
/ (sub_d_x
- 1) - 1 / 2)
50 for j
in range(0, sub_d_y
):
51 y
= meshsize_y
* (j
/ (sub_d_y
- 1) - 1 / 2)
53 z
= noise_gen((x
, y
, 0), props
)
59 A
= i
* sub_d_y
+ (j
- 1)
61 C
= (i
- 1) * sub_d_y
+ j
62 D
= (i
- 1) * sub_d_y
+ (j
- 1)
73 def sphere_gen(sub_d_x
, sub_d_y
, tri
, meshsize
, props
, water_plane
, water_level
):
76 vappend
= verts
.append
77 fappend
= faces
.append
80 for i
in range(0, sub_d_x
):
81 for j
in range(0, sub_d_y
):
82 u
= sin(j
* pi
* 2 / (sub_d_y
- 1)) * cos(-pi
/ 2 + i
* pi
/ (sub_d_x
- 1)) * meshsize
/ 2
83 v
= cos(j
* pi
* 2 / (sub_d_y
- 1)) * cos(-pi
/ 2 + i
* pi
/ (sub_d_x
- 1)) * meshsize
/ 2
84 w
= sin(-pi
/ 2 + i
* pi
/ (sub_d_x
- 1)) * meshsize
/ 2
88 h
= noise_gen((u
, v
, w
), props
) / meshsize
89 vappend(((u
+ u
* h
), (v
+ v
* h
), (w
+ w
* h
)))
92 for i
in range (0, sub_d_y
* (sub_d_x
- 1)):
93 if count
< sub_d_y
- 1 :
102 fappend((A
, B
, C
, D
))
110 # ------------------------------------------------------------
111 # Do refresh - redraw
112 class AntLandscapeRefresh(bpy
.types
.Operator
):
113 bl_idname
= "mesh.ant_landscape_refresh"
115 bl_description
= "Refresh landscape with current settings"
116 bl_options
= {'REGISTER', 'UNDO'}
120 def poll(cls
, context
):
121 ob
= bpy
.context
.active_object
122 return (ob
.ant_landscape
and not ob
.ant_landscape
.sphere_mesh
)
124 def execute(self
, context
):
126 obj
= bpy
.context
.active_object
128 bpy
.ops
.object.mode_set(mode
= 'EDIT')
129 bpy
.ops
.object.mode_set(mode
= 'OBJECT')
131 keys
= obj
.ant_landscape
.keys()
133 ob
= obj
.ant_landscape
136 prop
.append(getattr(ob
, key
))
141 if ob
['vert_group'] != "" and ob
['vert_group'] in obj
.vertex_groups
:
142 vertex_group
= obj
.vertex_groups
[ob
['vert_group']]
143 gi
= vertex_group
.index
144 for v
in mesh
.vertices
:
148 v
.co
[2] = vertex_group
.weight(v
.index
) * noise_gen(v
.co
, prop
)
150 for v
in mesh
.vertices
:
152 v
.co
[2] = noise_gen(v
.co
, prop
)
159 # ------------------------------------------------------------
161 class AntLandscapeRegenerate(bpy
.types
.Operator
):
162 bl_idname
= "mesh.ant_landscape_regenerate"
163 bl_label
= "Regenerate"
164 bl_description
= "Regenerate landscape with current settings"
165 bl_options
= {'REGISTER', 'UNDO'}
169 def poll(cls
, context
):
170 ob
= bpy
.context
.active_object
171 if ob
.mode
== 'EDIT':
173 return ob
.ant_landscape
176 def execute(self
, context
):
178 view_layer
= bpy
.context
.view_layer
180 obj
= bpy
.context
.active_object
182 keys
= obj
.ant_landscape
.keys()
184 ob
= obj
.ant_landscape
187 ant_props
.append(getattr(ob
, key
))
191 # Main function, create landscape mesh object
192 if ob
['sphere_mesh']:
194 verts
, faces
= sphere_gen(
203 new_ob
= create_mesh_object(context
, verts
, [], faces
, new_name
)
204 if ob
['remove_double']:
205 new_ob
.select_set(True)
206 bpy
.ops
.object.mode_set(mode
= 'EDIT')
207 bpy
.ops
.mesh
.remove_doubles(threshold
=0.0001, use_unselected
=False)
208 bpy
.ops
.object.mode_set(mode
= 'OBJECT')
211 verts
, faces
= grid_gen(
221 new_ob
= create_mesh_object(context
, verts
, [], faces
, new_name
)
223 new_ob
.select_set(True)
225 if ob
['smooth_mesh']:
226 bpy
.ops
.object.shade_smooth()
229 if ob
['land_material'] != "" and ob
['land_material'] in bpy
.data
.materials
:
230 mat
= bpy
.data
.materials
[ob
['land_material']]
231 bpy
.context
.object.data
.materials
.append(mat
)
234 if ob
['water_plane']:
235 if ob
['sphere_mesh']:
237 verts
, faces
= sphere_gen(
246 wobj
= create_mesh_object(context
, verts
, [], faces
, new_name
+"_plane")
247 if ob
['remove_double']:
248 wobj
.select_set(True)
249 bpy
.ops
.object.mode_set(mode
= 'EDIT')
250 bpy
.ops
.mesh
.remove_doubles(threshold
=0.0001, use_unselected
=False)
251 bpy
.ops
.object.mode_set(mode
= 'OBJECT')
254 verts
, faces
= grid_gen(
264 wobj
= create_mesh_object(context
, verts
, [], faces
, new_name
+"_plane")
266 wobj
.select_set(True)
268 if ob
['smooth_mesh']:
269 bpy
.ops
.object.shade_smooth()
272 if ob
['water_material'] != "" and ob
['water_material'] in bpy
.data
.materials
:
273 mat
= bpy
.data
.materials
[ob
['water_material']]
274 bpy
.context
.object.data
.materials
.append(mat
)
277 if ob
['water_plane']:
278 wobj
.location
= obj
.location
279 wobj
.rotation_euler
= obj
.rotation_euler
280 wobj
.scale
= obj
.scale
281 wobj
.select_set(False)
283 new_ob
.location
= obj
.location
284 new_ob
.rotation_euler
= obj
.rotation_euler
285 new_ob
.scale
= obj
.scale
288 new_ob
= store_properties(ob
, new_ob
)
291 new_ob
.select_set(False)
294 view_layer
.objects
.active
= obj
295 bpy
.ops
.object.delete(use_global
=False)
297 # Select landscape and make active
298 new_ob
.select_set(True)
299 view_layer
.objects
.active
= new_ob
304 # ------------------------------------------------------------
305 # Z normal value to vertex group (Slope map)
306 class AntVgSlopeMap(bpy
.types
.Operator
):
307 bl_idname
= "mesh.ant_slope_map"
308 bl_label
= "Weight from Slope"
309 bl_description
= "A.N.T. Slope Map - z normal value to vertex group weight"
310 bl_options
= {'REGISTER', 'UNDO'}
312 z_method
: EnumProperty(
316 ('SLOPE_Z', "Z Slope", "Slope for planar mesh"),
317 ('SLOPE_XYZ', "Sphere Slope", "Slope for spherical mesh")
319 group_name
: StringProperty(
320 name
="Vertex Group Name:",
324 select_flat
: BoolProperty(
327 description
="Select vertices on flat surface"
329 select_range
: FloatProperty(
330 name
="Vert Select Range:",
334 description
="Increase to select more vertices on slopes"
338 def poll(cls
, context
):
340 return (ob
and ob
.type == 'MESH')
343 def invoke(self
, context
, event
):
344 wm
= context
.window_manager
345 return wm
.invoke_props_dialog(self
)
348 def execute(self
, context
):
349 message
= "Popup Values: %d, %f, %s, %s" % \
350 (self
.select_flat
, self
.select_range
, self
.group_name
, self
.z_method
)
351 self
.report({'INFO'}, message
)
353 bpy
.ops
.object.mode_set(mode
='OBJECT')
354 ob
= bpy
.context
.active_object
358 bpy
.ops
.object.mode_set(mode
='EDIT')
359 bpy
.ops
.mesh
.select_all(action
='DESELECT')
360 bpy
.context
.tool_settings
.mesh_select_mode
= [True, False, False]
361 bpy
.ops
.object.mode_set(mode
='OBJECT')
363 bpy
.ops
.object.vertex_group_add()
364 vg_normal
= ob
.vertex_groups
.active
366 for v
in ob
.data
.vertices
:
367 if self
.z_method
== 'SLOPE_XYZ':
368 zval
= (v
.co
.normalized() * v
.normal
.normalized()) * 2 - 1
372 vg_normal
.add([v
.index
], zval
, 'REPLACE')
375 if zval
>= (1.0 - self
.select_range
):
378 vg_normal
.name
= self
.group_name
380 bpy
.ops
.paint
.weight_paint_toggle()
384 # ------------------------------------------------------------
387 def draw_ant_refresh(self
, context
):
389 if self
.auto_refresh
is False:
391 elif self
.auto_refresh
is True:
393 row
= layout
.box().row()
396 split
.prop(self
, "auto_refresh", toggle
=True, icon_only
=True, icon
='AUTO')
397 split
.prop(self
, "refresh", toggle
=True, icon_only
=True, icon
='FILE_REFRESH')
400 def draw_ant_main(self
, context
, generate
=True):
403 box
.prop(self
, "show_main_settings", toggle
=True)
404 if self
.show_main_settings
:
406 row
= box
.row(align
=True)
407 split
= row
.split(align
=True)
409 split
.prop(self
, "at_cursor", toggle
=True, icon_only
=True, icon
='PIVOT_CURSOR')
411 split
.prop(self
, "smooth_mesh", toggle
=True, icon_only
=True, icon
='SHADING_SOLID')
412 split
.prop(self
, "tri_face", toggle
=True, text
="Triangulate", icon
='MESH_DATA')
414 if not self
.sphere_mesh
:
415 row
= box
.row(align
=True)
416 row
.prop(self
, "sphere_mesh", toggle
=True)
418 row
= box
.row(align
=True)
419 split
= row
.split(factor
=0.5, align
=True)
420 split
.prop(self
, "sphere_mesh", toggle
=True)
421 split
.prop(self
, "remove_double", toggle
=True)
423 box
.prop(self
, "ant_terrain_name")
424 box
.prop_search(self
, "land_material", bpy
.data
, "materials")
426 col
= box
.column(align
=True)
427 col
.prop(self
, "subdivision_x")
428 col
.prop(self
, "subdivision_y")
429 col
= box
.column(align
=True)
431 col
.prop(self
, "mesh_size")
433 col
.prop(self
, "mesh_size_x")
434 col
.prop(self
, "mesh_size_y")
437 def draw_ant_noise(self
, context
, generate
=True):
440 box
.prop(self
, "show_noise_settings", toggle
=True)
441 if self
.show_noise_settings
:
442 box
.prop(self
, "noise_type")
443 if self
.noise_type
== "blender_texture":
444 box
.prop_search(self
, "texture_block", bpy
.data
, "textures")
446 box
.prop(self
, "basis_type")
448 col
= box
.column(align
=True)
449 col
.prop(self
, "random_seed")
450 col
= box
.column(align
=True)
451 col
.prop(self
, "noise_offset_x")
452 col
.prop(self
, "noise_offset_y")
453 if self
.sphere_mesh
== True or generate
== False:
454 col
.prop(self
, "noise_offset_z")
455 col
.prop(self
, "noise_size_x")
456 col
.prop(self
, "noise_size_y")
457 if self
.sphere_mesh
== True or generate
== False:
458 col
.prop(self
, "noise_size_z")
460 col
= box
.column(align
=True)
461 col
.prop(self
, "noise_size")
463 col
= box
.column(align
=True)
464 if self
.noise_type
== "multi_fractal":
465 col
.prop(self
, "noise_depth")
466 col
.prop(self
, "dimension")
467 col
.prop(self
, "lacunarity")
468 elif self
.noise_type
== "ridged_multi_fractal":
469 col
.prop(self
, "noise_depth")
470 col
.prop(self
, "dimension")
471 col
.prop(self
, "lacunarity")
472 col
.prop(self
, "offset")
473 col
.prop(self
, "gain")
474 elif self
.noise_type
== "hybrid_multi_fractal":
475 col
.prop(self
, "noise_depth")
476 col
.prop(self
, "dimension")
477 col
.prop(self
, "lacunarity")
478 col
.prop(self
, "offset")
479 col
.prop(self
, "gain")
480 elif self
.noise_type
== "hetero_terrain":
481 col
.prop(self
, "noise_depth")
482 col
.prop(self
, "dimension")
483 col
.prop(self
, "lacunarity")
484 col
.prop(self
, "offset")
485 elif self
.noise_type
== "fractal":
486 col
.prop(self
, "noise_depth")
487 col
.prop(self
, "dimension")
488 col
.prop(self
, "lacunarity")
489 elif self
.noise_type
== "turbulence_vector":
490 col
.prop(self
, "noise_depth")
491 col
.prop(self
, "amplitude")
492 col
.prop(self
, "frequency")
494 row
= col
.row(align
=True)
495 row
.prop(self
, "hard_noise", expand
=True)
496 elif self
.noise_type
== "variable_lacunarity":
497 box
.prop(self
, "vl_basis_type")
498 box
.prop(self
, "distortion")
499 elif self
.noise_type
== "marble_noise":
500 box
.prop(self
, "marble_shape")
501 box
.prop(self
, "marble_bias")
502 box
.prop(self
, "marble_sharp")
503 col
= box
.column(align
=True)
504 col
.prop(self
, "distortion")
505 col
.prop(self
, "noise_depth")
507 row
= col
.row(align
=True)
508 row
.prop(self
, "hard_noise", expand
=True)
509 elif self
.noise_type
== "shattered_hterrain":
510 col
.prop(self
, "noise_depth")
511 col
.prop(self
, "dimension")
512 col
.prop(self
, "lacunarity")
513 col
.prop(self
, "offset")
514 col
.prop(self
, "distortion")
515 elif self
.noise_type
== "strata_hterrain":
516 col
.prop(self
, "noise_depth")
517 col
.prop(self
, "dimension")
518 col
.prop(self
, "lacunarity")
519 col
.prop(self
, "offset")
520 col
.prop(self
, "distortion", text
="Strata")
521 elif self
.noise_type
== "ant_turbulence":
522 col
.prop(self
, "noise_depth")
523 col
.prop(self
, "amplitude")
524 col
.prop(self
, "frequency")
525 col
.prop(self
, "distortion")
527 row
= col
.row(align
=True)
528 row
.prop(self
, "hard_noise", expand
=True)
529 elif self
.noise_type
== "vl_noise_turbulence":
530 col
.prop(self
, "noise_depth")
531 col
.prop(self
, "amplitude")
532 col
.prop(self
, "frequency")
533 col
.prop(self
, "distortion")
535 box
.prop(self
, "vl_basis_type")
537 row
= col
.row(align
=True)
538 row
.prop(self
, "hard_noise", expand
=True)
539 elif self
.noise_type
== "vl_hTerrain":
540 col
.prop(self
, "noise_depth")
541 col
.prop(self
, "dimension")
542 col
.prop(self
, "lacunarity")
543 col
.prop(self
, "offset")
544 col
.prop(self
, "distortion")
546 box
.prop(self
, "vl_basis_type")
547 elif self
.noise_type
== "distorted_heteroTerrain":
548 col
.prop(self
, "noise_depth")
549 col
.prop(self
, "dimension")
550 col
.prop(self
, "lacunarity")
551 col
.prop(self
, "offset")
552 col
.prop(self
, "distortion")
554 box
.prop(self
, "vl_basis_type")
555 elif self
.noise_type
== "double_multiFractal":
556 col
.prop(self
, "noise_depth")
557 col
.prop(self
, "dimension")
558 col
.prop(self
, "lacunarity")
559 col
.prop(self
, "offset")
560 col
.prop(self
, "gain")
562 box
.prop(self
, "vl_basis_type")
563 elif self
.noise_type
== "rocks_noise":
564 col
.prop(self
, "noise_depth")
565 col
.prop(self
, "distortion")
567 row
= col
.row(align
=True)
568 row
.prop(self
, "hard_noise", expand
=True)
569 elif self
.noise_type
== "slick_rock":
570 col
.prop(self
, "noise_depth")
571 col
.prop(self
, "dimension")
572 col
.prop(self
, "lacunarity")
573 col
.prop(self
, "gain")
574 col
.prop(self
, "offset")
575 col
.prop(self
, "distortion")
577 box
.prop(self
, "vl_basis_type")
578 elif self
.noise_type
== "planet_noise":
579 col
.prop(self
, "noise_depth")
581 row
= col
.row(align
=True)
582 row
.prop(self
, "hard_noise", expand
=True)
585 col
= box
.column(align
=False)
586 box
.prop(self
, "fx_type")
587 if self
.fx_type
!= "0":
588 if int(self
.fx_type
) <= 12:
589 box
.prop(self
, "fx_bias")
591 box
.prop(self
, "fx_mix_mode")
592 col
= box
.column(align
=True)
593 col
.prop(self
, "fx_mixfactor")
595 col
= box
.column(align
=True)
596 col
.prop(self
, "fx_loc_x")
597 col
.prop(self
, "fx_loc_y")
598 col
.prop(self
, "fx_size")
600 col
= box
.column(align
=True)
601 col
.prop(self
, "fx_depth")
602 if self
.fx_depth
!= 0:
603 col
.prop(self
, "fx_frequency")
604 col
.prop(self
, "fx_amplitude")
605 col
.prop(self
, "fx_turb")
607 col
= box
.column(align
=True)
608 row
= col
.row(align
=True).split(factor
=0.92, align
=True)
609 row
.prop(self
, "fx_height")
610 row
.prop(self
, "fx_invert", toggle
=True, text
="", icon
='ARROW_LEFTRIGHT')
611 col
.prop(self
, "fx_offset")
614 def draw_ant_displace(self
, context
, generate
=True):
617 box
.prop(self
, "show_displace_settings", toggle
=True)
618 if self
.show_displace_settings
:
620 col
= box
.column(align
=False)
621 col
.prop(self
, "direction", toggle
=True)
623 col
= box
.column(align
=True)
624 row
= col
.row(align
=True).split(factor
=0.92, align
=True)
625 row
.prop(self
, "height")
626 row
.prop(self
, "height_invert", toggle
=True, text
="", icon
='ARROW_LEFTRIGHT')
627 col
.prop(self
, "height_offset")
628 col
.prop(self
, "maximum")
629 col
.prop(self
, "minimum")
631 if not self
.sphere_mesh
:
633 col
.prop(self
, "edge_falloff")
634 if self
.edge_falloff
!= "0":
635 col
= box
.column(align
=True)
636 col
.prop(self
, "edge_level")
637 if self
.edge_falloff
in ["2", "3"]:
638 col
.prop(self
, "falloff_x")
639 if self
.edge_falloff
in ["1", "3"]:
640 col
.prop(self
, "falloff_y")
643 col
.prop(self
, "strata_type")
644 if self
.strata_type
!= "0":
646 col
.prop(self
, "strata")
649 col
= box
.column(align
=False)
650 col
.prop_search(self
, "vert_group", bpy
.context
.object, "vertex_groups")
653 def draw_ant_water(self
, context
):
657 col
.prop(self
, "water_plane", toggle
=True)
659 col
= box
.column(align
=True)
660 col
.prop_search(self
, "water_material", bpy
.data
, "materials")
662 col
.prop(self
, "water_level")
666 def store_properties(operator
, ob
):
667 ob
.ant_landscape
.ant_terrain_name
= operator
.ant_terrain_name
668 ob
.ant_landscape
.at_cursor
= operator
.at_cursor
669 ob
.ant_landscape
.smooth_mesh
= operator
.smooth_mesh
670 ob
.ant_landscape
.tri_face
= operator
.tri_face
671 ob
.ant_landscape
.sphere_mesh
= operator
.sphere_mesh
672 ob
.ant_landscape
.land_material
= operator
.land_material
673 ob
.ant_landscape
.water_material
= operator
.water_material
674 ob
.ant_landscape
.texture_block
= operator
.texture_block
675 ob
.ant_landscape
.subdivision_x
= operator
.subdivision_x
676 ob
.ant_landscape
.subdivision_y
= operator
.subdivision_y
677 ob
.ant_landscape
.mesh_size_x
= operator
.mesh_size_x
678 ob
.ant_landscape
.mesh_size_y
= operator
.mesh_size_y
679 ob
.ant_landscape
.mesh_size
= operator
.mesh_size
680 ob
.ant_landscape
.random_seed
= operator
.random_seed
681 ob
.ant_landscape
.noise_offset_x
= operator
.noise_offset_x
682 ob
.ant_landscape
.noise_offset_y
= operator
.noise_offset_y
683 ob
.ant_landscape
.noise_offset_z
= operator
.noise_offset_z
684 ob
.ant_landscape
.noise_size_x
= operator
.noise_size_x
685 ob
.ant_landscape
.noise_size_y
= operator
.noise_size_y
686 ob
.ant_landscape
.noise_size_z
= operator
.noise_size_z
687 ob
.ant_landscape
.noise_size
= operator
.noise_size
688 ob
.ant_landscape
.noise_type
= operator
.noise_type
689 ob
.ant_landscape
.basis_type
= operator
.basis_type
690 ob
.ant_landscape
.vl_basis_type
= operator
.vl_basis_type
691 ob
.ant_landscape
.distortion
= operator
.distortion
692 ob
.ant_landscape
.hard_noise
= operator
.hard_noise
693 ob
.ant_landscape
.noise_depth
= operator
.noise_depth
694 ob
.ant_landscape
.amplitude
= operator
.amplitude
695 ob
.ant_landscape
.frequency
= operator
.frequency
696 ob
.ant_landscape
.dimension
= operator
.dimension
697 ob
.ant_landscape
.lacunarity
= operator
.lacunarity
698 ob
.ant_landscape
.offset
= operator
.offset
699 ob
.ant_landscape
.gain
= operator
.gain
700 ob
.ant_landscape
.marble_bias
= operator
.marble_bias
701 ob
.ant_landscape
.marble_sharp
= operator
.marble_sharp
702 ob
.ant_landscape
.marble_shape
= operator
.marble_shape
703 ob
.ant_landscape
.height
= operator
.height
704 ob
.ant_landscape
.height_invert
= operator
.height_invert
705 ob
.ant_landscape
.height_offset
= operator
.height_offset
706 ob
.ant_landscape
.maximum
= operator
.maximum
707 ob
.ant_landscape
.minimum
= operator
.minimum
708 ob
.ant_landscape
.edge_falloff
= operator
.edge_falloff
709 ob
.ant_landscape
.edge_level
= operator
.edge_level
710 ob
.ant_landscape
.falloff_x
= operator
.falloff_x
711 ob
.ant_landscape
.falloff_y
= operator
.falloff_y
712 ob
.ant_landscape
.strata_type
= operator
.strata_type
713 ob
.ant_landscape
.strata
= operator
.strata
714 ob
.ant_landscape
.water_plane
= operator
.water_plane
715 ob
.ant_landscape
.water_level
= operator
.water_level
716 ob
.ant_landscape
.vert_group
= operator
.vert_group
717 ob
.ant_landscape
.remove_double
= operator
.remove_double
718 ob
.ant_landscape
.fx_mixfactor
= operator
.fx_mixfactor
719 ob
.ant_landscape
.fx_mix_mode
= operator
.fx_mix_mode
720 ob
.ant_landscape
.fx_type
= operator
.fx_type
721 ob
.ant_landscape
.fx_bias
= operator
.fx_bias
722 ob
.ant_landscape
.fx_turb
= operator
.fx_turb
723 ob
.ant_landscape
.fx_depth
= operator
.fx_depth
724 ob
.ant_landscape
.fx_frequency
= operator
.fx_frequency
725 ob
.ant_landscape
.fx_amplitude
= operator
.fx_amplitude
726 ob
.ant_landscape
.fx_size
= operator
.fx_size
727 ob
.ant_landscape
.fx_loc_x
= operator
.fx_loc_x
728 ob
.ant_landscape
.fx_loc_y
= operator
.fx_loc_y
729 ob
.ant_landscape
.fx_height
= operator
.fx_height
730 ob
.ant_landscape
.fx_offset
= operator
.fx_offset
731 ob
.ant_landscape
.fx_invert
= operator
.fx_invert
735 # ------------------------------------------------------------
737 # "author": "Michel Anders, Ian Huish"
739 from random
import random
as rand
740 from math
import tan
, radians
741 from .eroder
import Grid
742 from .stats
import Stats
743 from .utils
import numexpr_available
746 def availableVertexGroupsOrNone(self
, context
):
747 groups
= [ ('None', 'None', 'None', 1) ]
748 return groups
+ [(name
, name
, name
, n
+1) for n
,name
in enumerate(context
.active_object
.vertex_groups
.keys())]
751 class Eroder(bpy
.types
.Operator
):
752 bl_idname
= "mesh.eroder"
753 bl_label
= "ErosionR"
754 bl_description
= "Apply various kinds of erosion to a square ANT-Landscape grid. Also available in Weight Paint mode > Weights menu"
755 bl_options
= {'REGISTER', 'UNDO', 'PRESET'}
757 Iterations
: IntProperty(
759 description
="Number of overall iterations",
764 IterRiver
: IntProperty(
765 name
="River Iterations",
766 description
="Number of river iterations",
771 IterAva
: IntProperty(
772 name
="Avalanche Iterations",
773 description
="Number of avalanche iterations",
778 IterDiffuse
: IntProperty(
779 name
="Diffuse Iterations",
780 description
="Number of diffuse iterations",
786 name
="Rain on Plains",
787 description
="1 gives equal rain across the terrain, 0 rains more at the mountain tops",
794 description
="Thermal diffusion rate (1.0 is a fairly high rate)",
801 description
="Maximum stable talus angle",
809 description
="Total Rain amount",
816 name
="Rain variance",
817 description
="Rain variance (0 is constant, 1 is uniform)",
822 userainmap
: BoolProperty(
824 description
="Use active vertex group as a rain map",
828 name
="Soil solubility",
829 description
="Soil solubility - how quickly water quickly reaches saturation point",
835 name
="Deposition rate",
836 description
="Sediment deposition rate - how quickly silt is laid down once water stops flowing quickly",
841 Kz
: FloatProperty(name
="Fluvial Erosion Rate",
842 description
="Amount of sediment moved each main iteration - if 0, then rivers are formed but the mesh is not changed",
848 name
="Carrying capacity",
849 description
="Base sediment carrying capacity",
855 name
="Slope dependence",
856 description
="Slope dependence of carrying capacity (not used)",
863 description
="Evaporation Rate per grid square in % - causes sediment to be dropped closer to the hills",
868 numexpr
: BoolProperty(
870 description
="Use numexpr module (if available)",
874 name
="Diffusion Amount",
875 description
="Diffusion probability",
881 name
="Avalanche Amount",
882 description
="Avalanche amount",
889 description
="Water erosion probability",
894 smooth
: BoolProperty(
896 description
="Set smooth shading",
899 showiterstats
: BoolProperty(
900 name
="Iteration Stats",
901 description
="Show iteraration statistics",
904 showmeshstats
: BoolProperty(name
="Mesh Stats",
905 description
="Show mesh statistics",
912 def execute(self
, context
):
914 ob
= context
.active_object
918 vgActive
= ob
.vertex_groups
.active
.name
920 vgActive
= "capacity"
921 print("ActiveGroup", vgActive
)
923 vg
=ob
.vertex_groups
["rainmap"]
925 vg
=ob
.vertex_groups
.new(name
="rainmap")
927 vgscree
=ob
.vertex_groups
["scree"]
929 vgscree
=ob
.vertex_groups
.new(name
="scree")
931 vgavalanced
=ob
.vertex_groups
["avalanced"]
933 vgavalanced
=ob
.vertex_groups
.new(name
="avalanced")
935 vgw
=ob
.vertex_groups
["water"]
937 vgw
=ob
.vertex_groups
.new(name
="water")
939 vgscour
=ob
.vertex_groups
["scour"]
941 vgscour
=ob
.vertex_groups
.new(name
="scour")
943 vgdeposit
=ob
.vertex_groups
["deposit"]
945 vgdeposit
=ob
.vertex_groups
.new(name
="deposit")
947 vgflowrate
=ob
.vertex_groups
["flowrate"]
949 vgflowrate
=ob
.vertex_groups
.new(name
="flowrate")
951 vgsediment
=ob
.vertex_groups
["sediment"]
953 vgsediment
=ob
.vertex_groups
.new(name
="sediment")
955 vgsedimentpct
=ob
.vertex_groups
["sedimentpct"]
957 vgsedimentpct
=ob
.vertex_groups
.new(name
="sedimentpct")
959 vgcapacity
=ob
.vertex_groups
["capacity"]
961 vgcapacity
=ob
.vertex_groups
.new(name
="capacity")
963 g
= Grid
.fromBlenderMesh(me
, vg
, self
.Ef
)
965 me
= bpy
.data
.meshes
.new(me
.name
)
967 self
.counts
['diffuse'] = 0
968 self
.counts
['avalanche'] = 0
969 self
.counts
['water'] = 0
970 for i
in range(self
.Iterations
):
971 if self
.IterRiver
> 0:
972 for i
in range(self
.IterRiver
):
973 g
.rivergeneration(self
.Kr
, self
.Kv
, self
.userainmap
, self
.Kc
, self
.Ks
, self
.Kdep
, self
.Ka
, self
.Kev
/100, 0,0,0,0, self
.numexpr
)
976 for k
in range(self
.IterDiffuse
):
977 g
.diffuse(self
.Kd
/ 5, self
.IterDiffuse
, self
.numexpr
)
978 self
.counts
['diffuse']+=1
980 if self
.Kt
< radians(90) and self
.Pa
> 0:
981 for k
in range(self
.IterAva
):
982 # since dx and dy are scaled to 1, tan(Kt) is the height for a given angle
983 g
.avalanche(tan(self
.Kt
), self
.IterAva
, self
.Pa
, self
.numexpr
)
984 self
.counts
['avalanche']+=1
986 g
.fluvial_erosion(self
.Kr
, self
.Kv
, self
.userainmap
, self
.Kc
, self
.Ks
, self
.Kz
*50, self
.Ka
, 0,0,0,0, self
.numexpr
)
987 self
.counts
['water']+=1
993 for row
in range(g
.rainmap
.shape
[0]):
994 for col
in range(g
.rainmap
.shape
[1]):
995 i
= row
* g
.rainmap
.shape
[1] + col
996 vg
.add([i
],g
.rainmap
[row
,col
],'ADD')
998 for row
in range(g
.rainmap
.shape
[0]):
999 for col
in range(g
.rainmap
.shape
[1]):
1000 i
= row
* g
.rainmap
.shape
[1] + col
1001 vgscree
.add([i
],g
.avalanced
[row
,col
],'ADD')
1003 for row
in range(g
.rainmap
.shape
[0]):
1004 for col
in range(g
.rainmap
.shape
[1]):
1005 i
= row
* g
.rainmap
.shape
[1] + col
1006 vgavalanced
.add([i
],-g
.avalanced
[row
,col
],'ADD')
1008 for row
in range(g
.rainmap
.shape
[0]):
1009 for col
in range(g
.rainmap
.shape
[1]):
1010 i
= row
* g
.rainmap
.shape
[1] + col
1011 vgw
.add([i
],g
.water
[row
,col
]/g
.watermax
,'ADD')
1013 for row
in range(g
.rainmap
.shape
[0]):
1014 for col
in range(g
.rainmap
.shape
[1]):
1015 i
= row
* g
.rainmap
.shape
[1] + col
1016 vgscour
.add([i
],g
.scour
[row
,col
]/max(g
.scourmax
, -g
.scourmin
),'ADD')
1018 for row
in range(g
.rainmap
.shape
[0]):
1019 for col
in range(g
.rainmap
.shape
[1]):
1020 i
= row
* g
.rainmap
.shape
[1] + col
1021 vgdeposit
.add([i
],g
.scour
[row
,col
]/min(-g
.scourmax
, g
.scourmin
),'ADD')
1023 for row
in range(g
.rainmap
.shape
[0]):
1024 for col
in range(g
.rainmap
.shape
[1]):
1025 i
= row
* g
.rainmap
.shape
[1] + col
1026 vgflowrate
.add([i
],g
.flowrate
[row
,col
],'ADD')
1028 for row
in range(g
.rainmap
.shape
[0]):
1029 for col
in range(g
.rainmap
.shape
[1]):
1030 i
= row
* g
.rainmap
.shape
[1] + col
1031 vgsediment
.add([i
],g
.sediment
[row
,col
],'ADD')
1033 for row
in range(g
.rainmap
.shape
[0]):
1034 for col
in range(g
.rainmap
.shape
[1]):
1035 i
= row
* g
.rainmap
.shape
[1] + col
1036 vgsedimentpct
.add([i
],g
.sedimentpct
[row
,col
],'ADD')
1038 for row
in range(g
.rainmap
.shape
[0]):
1039 for col
in range(g
.rainmap
.shape
[1]):
1040 i
= row
* g
.rainmap
.shape
[1] + col
1041 vgcapacity
.add([i
],g
.capacity
[row
,col
],'ADD')
1043 vg
= ob
.vertex_groups
["vgActive"]
1046 ob
.vertex_groups
.active
= vg
1049 bpy
.ops
.object.shade_smooth()
1052 if self
.showmeshstats
:
1053 self
.stats
.meshstats
= g
.analyze()
1058 def draw(self
,context
):
1059 layout
= self
.layout
1061 layout
.operator('screen.repeat_last', text
="Repeat", icon
='FILE_REFRESH' )
1063 layout
.prop(self
, 'Iterations')
1066 col
= box
.column(align
=True)
1067 col
.label(text
="Thermal (Diffusion)")
1068 col
.prop(self
, 'Kd')
1069 col
.prop(self
, 'IterDiffuse')
1072 col
= box
.column(align
=True)
1073 col
.label(text
="Avalanche (Talus)")
1074 col
.prop(self
, 'Pa')
1075 col
.prop(self
, 'IterAva')
1076 col
.prop(self
, 'Kt')
1079 col
= box
.column(align
=True)
1080 col
.label(text
="River erosion")
1081 col
.prop(self
, 'IterRiver')
1082 col
.prop(self
, 'Kz')
1083 col
.prop(self
, 'Ks')
1084 col
.prop(self
, 'Kc')
1085 col
.prop(self
, 'Kdep')
1086 col
.prop(self
, 'Kr')
1087 col
.prop(self
, 'Kv')
1088 col
.prop(self
, 'Kev')
1090 col
.prop(self
, 'Ef')
1092 layout
.prop(self
,'smooth')