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
33 def create_mesh_object(context
, verts
, edges
, faces
, name
):
35 mesh
= bpy
.data
.meshes
.new(name
)
36 # Make a mesh from a list of verts/edges/faces.
37 mesh
.from_pydata(verts
, [], faces
)
38 # Update mesh geometry after adding stuff.
40 return object_utils
.object_data_add(context
, mesh
, operator
=None)
44 def grid_gen(sub_d_x
, sub_d_y
, tri
, meshsize_x
, meshsize_y
, props
, water_plane
, water_level
):
47 vappend
= verts
.append
48 fappend
= faces
.append
49 for i
in range(0, sub_d_x
):
50 x
= meshsize_x
* (i
/ (sub_d_x
- 1) - 1 / 2)
51 for j
in range(0, sub_d_y
):
52 y
= meshsize_y
* (j
/ (sub_d_y
- 1) - 1 / 2)
54 z
= noise_gen((x
, y
, 0), props
)
60 A
= i
* sub_d_y
+ (j
- 1)
62 C
= (i
- 1) * sub_d_y
+ j
63 D
= (i
- 1) * sub_d_y
+ (j
- 1)
74 def sphere_gen(sub_d_x
, sub_d_y
, tri
, meshsize
, props
, water_plane
, water_level
):
77 vappend
= verts
.append
78 fappend
= faces
.append
81 for i
in range(0, sub_d_x
):
82 for j
in range(0, sub_d_y
):
83 u
= sin(j
* pi
* 2 / (sub_d_y
- 1)) * cos(-pi
/ 2 + i
* pi
/ (sub_d_x
- 1)) * meshsize
/ 2
84 v
= cos(j
* pi
* 2 / (sub_d_y
- 1)) * cos(-pi
/ 2 + i
* pi
/ (sub_d_x
- 1)) * meshsize
/ 2
85 w
= sin(-pi
/ 2 + i
* pi
/ (sub_d_x
- 1)) * meshsize
/ 2
89 h
= noise_gen((u
, v
, w
), props
) / meshsize
90 vappend(((u
+ u
* h
), (v
+ v
* h
), (w
+ w
* h
)))
93 for i
in range(0, sub_d_y
* (sub_d_x
- 1)):
94 if count
< sub_d_y
- 1:
103 fappend((A
, B
, C
, D
))
111 # ------------------------------------------------------------
112 # Do refresh - redraw
113 class AntLandscapeRefresh(bpy
.types
.Operator
):
114 bl_idname
= "mesh.ant_landscape_refresh"
116 bl_description
= "Refresh landscape with current settings"
117 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 # ------------------------------------------------------------
163 class AntLandscapeRegenerate(bpy
.types
.Operator
):
164 bl_idname
= "mesh.ant_landscape_regenerate"
165 bl_label
= "Regenerate"
166 bl_description
= "Regenerate landscape with current settings"
167 bl_options
= {'REGISTER', 'UNDO'}
170 def poll(cls
, context
):
171 ob
= bpy
.context
.active_object
172 if ob
.mode
== 'EDIT':
174 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')
342 def invoke(self
, context
, event
):
343 wm
= context
.window_manager
344 return wm
.invoke_props_dialog(self
)
346 def execute(self
, context
):
347 message
= "Popup Values: %d, %f, %s, %s" % \
348 (self
.select_flat
, self
.select_range
, self
.group_name
, self
.z_method
)
349 self
.report({'INFO'}, message
)
351 bpy
.ops
.object.mode_set(mode
='OBJECT')
352 ob
= bpy
.context
.active_object
356 bpy
.ops
.object.mode_set(mode
='EDIT')
357 bpy
.ops
.mesh
.select_all(action
='DESELECT')
358 bpy
.context
.tool_settings
.mesh_select_mode
= [True, False, False]
359 bpy
.ops
.object.mode_set(mode
='OBJECT')
361 bpy
.ops
.object.vertex_group_add()
362 vg_normal
= ob
.vertex_groups
.active
364 for v
in ob
.data
.vertices
:
365 if self
.z_method
== 'SLOPE_XYZ':
366 zval
= (v
.co
.normalized() * v
.normal
.normalized()) * 2 - 1
370 vg_normal
.add([v
.index
], zval
, 'REPLACE')
373 if zval
>= (1.0 - self
.select_range
):
376 vg_normal
.name
= self
.group_name
378 bpy
.ops
.paint
.weight_paint_toggle()
382 # ------------------------------------------------------------
385 def draw_ant_refresh(self
, context
):
387 if self
.auto_refresh
is False:
389 elif self
.auto_refresh
is True:
391 row
= layout
.box().row()
394 split
.prop(self
, "auto_refresh", toggle
=True, icon_only
=True, icon
='AUTO')
395 split
.prop(self
, "refresh", toggle
=True, icon_only
=True, icon
='FILE_REFRESH')
398 def draw_ant_main(self
, context
, generate
=True):
401 box
.prop(self
, "show_main_settings", toggle
=True)
402 if self
.show_main_settings
:
404 row
= box
.row(align
=True)
405 split
= row
.split(align
=True)
407 split
.prop(self
, "at_cursor", toggle
=True, icon_only
=True, icon
='PIVOT_CURSOR')
409 split
.prop(self
, "smooth_mesh", toggle
=True, icon_only
=True, icon
='SHADING_SOLID')
410 split
.prop(self
, "tri_face", toggle
=True, text
="Triangulate", icon
='MESH_DATA')
412 if not self
.sphere_mesh
:
413 row
= box
.row(align
=True)
414 row
.prop(self
, "sphere_mesh", toggle
=True)
416 row
= box
.row(align
=True)
417 split
= row
.split(factor
=0.5, align
=True)
418 split
.prop(self
, "sphere_mesh", toggle
=True)
419 split
.prop(self
, "remove_double", toggle
=True)
421 box
.prop(self
, "ant_terrain_name")
422 box
.prop_search(self
, "land_material", bpy
.data
, "materials")
424 col
= box
.column(align
=True)
425 col
.prop(self
, "subdivision_x")
426 col
.prop(self
, "subdivision_y")
427 col
= box
.column(align
=True)
429 col
.prop(self
, "mesh_size")
431 col
.prop(self
, "mesh_size_x")
432 col
.prop(self
, "mesh_size_y")
435 def draw_ant_noise(self
, context
, generate
=True):
438 box
.prop(self
, "show_noise_settings", toggle
=True)
439 if self
.show_noise_settings
:
440 box
.prop(self
, "noise_type")
441 if self
.noise_type
== "blender_texture":
442 box
.prop_search(self
, "texture_block", bpy
.data
, "textures")
444 box
.prop(self
, "basis_type")
446 col
= box
.column(align
=True)
447 col
.prop(self
, "random_seed")
448 col
= box
.column(align
=True)
449 col
.prop(self
, "noise_offset_x")
450 col
.prop(self
, "noise_offset_y")
451 if self
.sphere_mesh
or generate
== False:
452 col
.prop(self
, "noise_offset_z")
453 col
.prop(self
, "noise_size_x")
454 col
.prop(self
, "noise_size_y")
455 if self
.sphere_mesh
or generate
== False:
456 col
.prop(self
, "noise_size_z")
458 col
= box
.column(align
=True)
459 col
.prop(self
, "noise_size")
461 col
= box
.column(align
=True)
462 if self
.noise_type
== "multi_fractal":
463 col
.prop(self
, "noise_depth")
464 col
.prop(self
, "dimension")
465 col
.prop(self
, "lacunarity")
466 elif self
.noise_type
== "ridged_multi_fractal":
467 col
.prop(self
, "noise_depth")
468 col
.prop(self
, "dimension")
469 col
.prop(self
, "lacunarity")
470 col
.prop(self
, "offset")
471 col
.prop(self
, "gain")
472 elif self
.noise_type
== "hybrid_multi_fractal":
473 col
.prop(self
, "noise_depth")
474 col
.prop(self
, "dimension")
475 col
.prop(self
, "lacunarity")
476 col
.prop(self
, "offset")
477 col
.prop(self
, "gain")
478 elif self
.noise_type
== "hetero_terrain":
479 col
.prop(self
, "noise_depth")
480 col
.prop(self
, "dimension")
481 col
.prop(self
, "lacunarity")
482 col
.prop(self
, "offset")
483 elif self
.noise_type
== "fractal":
484 col
.prop(self
, "noise_depth")
485 col
.prop(self
, "dimension")
486 col
.prop(self
, "lacunarity")
487 elif self
.noise_type
== "turbulence_vector":
488 col
.prop(self
, "noise_depth")
489 col
.prop(self
, "amplitude")
490 col
.prop(self
, "frequency")
492 row
= col
.row(align
=True)
493 row
.prop(self
, "hard_noise", expand
=True)
494 elif self
.noise_type
== "variable_lacunarity":
495 box
.prop(self
, "vl_basis_type")
496 box
.prop(self
, "distortion")
497 elif self
.noise_type
== "marble_noise":
498 box
.prop(self
, "marble_shape")
499 box
.prop(self
, "marble_bias")
500 box
.prop(self
, "marble_sharp")
501 col
= box
.column(align
=True)
502 col
.prop(self
, "distortion")
503 col
.prop(self
, "noise_depth")
505 row
= col
.row(align
=True)
506 row
.prop(self
, "hard_noise", expand
=True)
507 elif self
.noise_type
== "shattered_hterrain":
508 col
.prop(self
, "noise_depth")
509 col
.prop(self
, "dimension")
510 col
.prop(self
, "lacunarity")
511 col
.prop(self
, "offset")
512 col
.prop(self
, "distortion")
513 elif self
.noise_type
== "strata_hterrain":
514 col
.prop(self
, "noise_depth")
515 col
.prop(self
, "dimension")
516 col
.prop(self
, "lacunarity")
517 col
.prop(self
, "offset")
518 col
.prop(self
, "distortion", text
="Strata")
519 elif self
.noise_type
== "ant_turbulence":
520 col
.prop(self
, "noise_depth")
521 col
.prop(self
, "amplitude")
522 col
.prop(self
, "frequency")
523 col
.prop(self
, "distortion")
525 row
= col
.row(align
=True)
526 row
.prop(self
, "hard_noise", expand
=True)
527 elif self
.noise_type
== "vl_noise_turbulence":
528 col
.prop(self
, "noise_depth")
529 col
.prop(self
, "amplitude")
530 col
.prop(self
, "frequency")
531 col
.prop(self
, "distortion")
533 box
.prop(self
, "vl_basis_type")
535 row
= col
.row(align
=True)
536 row
.prop(self
, "hard_noise", expand
=True)
537 elif self
.noise_type
== "vl_hTerrain":
538 col
.prop(self
, "noise_depth")
539 col
.prop(self
, "dimension")
540 col
.prop(self
, "lacunarity")
541 col
.prop(self
, "offset")
542 col
.prop(self
, "distortion")
544 box
.prop(self
, "vl_basis_type")
545 elif self
.noise_type
== "distorted_heteroTerrain":
546 col
.prop(self
, "noise_depth")
547 col
.prop(self
, "dimension")
548 col
.prop(self
, "lacunarity")
549 col
.prop(self
, "offset")
550 col
.prop(self
, "distortion")
552 box
.prop(self
, "vl_basis_type")
553 elif self
.noise_type
== "double_multiFractal":
554 col
.prop(self
, "noise_depth")
555 col
.prop(self
, "dimension")
556 col
.prop(self
, "lacunarity")
557 col
.prop(self
, "offset")
558 col
.prop(self
, "gain")
560 box
.prop(self
, "vl_basis_type")
561 elif self
.noise_type
== "rocks_noise":
562 col
.prop(self
, "noise_depth")
563 col
.prop(self
, "distortion")
565 row
= col
.row(align
=True)
566 row
.prop(self
, "hard_noise", expand
=True)
567 elif self
.noise_type
== "slick_rock":
568 col
.prop(self
, "noise_depth")
569 col
.prop(self
, "dimension")
570 col
.prop(self
, "lacunarity")
571 col
.prop(self
, "gain")
572 col
.prop(self
, "offset")
573 col
.prop(self
, "distortion")
575 box
.prop(self
, "vl_basis_type")
576 elif self
.noise_type
== "planet_noise":
577 col
.prop(self
, "noise_depth")
579 row
= col
.row(align
=True)
580 row
.prop(self
, "hard_noise", expand
=True)
583 col
= box
.column(align
=False)
584 box
.prop(self
, "fx_type")
585 if self
.fx_type
!= "0":
586 if int(self
.fx_type
) <= 12:
587 box
.prop(self
, "fx_bias")
589 box
.prop(self
, "fx_mix_mode")
590 col
= box
.column(align
=True)
591 col
.prop(self
, "fx_mixfactor")
593 col
= box
.column(align
=True)
594 col
.prop(self
, "fx_loc_x")
595 col
.prop(self
, "fx_loc_y")
596 col
.prop(self
, "fx_size")
598 col
= box
.column(align
=True)
599 col
.prop(self
, "fx_depth")
600 if self
.fx_depth
!= 0:
601 col
.prop(self
, "fx_frequency")
602 col
.prop(self
, "fx_amplitude")
603 col
.prop(self
, "fx_turb")
605 col
= box
.column(align
=True)
606 row
= col
.row(align
=True).split(factor
=0.92, align
=True)
607 row
.prop(self
, "fx_height")
608 row
.prop(self
, "fx_invert", toggle
=True, text
="", icon
='ARROW_LEFTRIGHT')
609 col
.prop(self
, "fx_offset")
612 def draw_ant_displace(self
, context
, generate
=True):
615 box
.prop(self
, "show_displace_settings", toggle
=True)
616 if self
.show_displace_settings
:
618 col
= box
.column(align
=False)
619 col
.prop(self
, "direction", toggle
=True)
621 col
= box
.column(align
=True)
622 row
= col
.row(align
=True).split(factor
=0.92, align
=True)
623 row
.prop(self
, "height")
624 row
.prop(self
, "height_invert", toggle
=True, text
="", icon
='ARROW_LEFTRIGHT')
625 col
.prop(self
, "height_offset")
626 col
.prop(self
, "maximum")
627 col
.prop(self
, "minimum")
629 if not self
.sphere_mesh
:
631 col
.prop(self
, "edge_falloff")
632 if self
.edge_falloff
!= "0":
633 col
= box
.column(align
=True)
634 col
.prop(self
, "edge_level")
635 if self
.edge_falloff
in ["2", "3"]:
636 col
.prop(self
, "falloff_x")
637 if self
.edge_falloff
in ["1", "3"]:
638 col
.prop(self
, "falloff_y")
641 col
.prop(self
, "strata_type")
642 if self
.strata_type
!= "0":
644 col
.prop(self
, "strata")
647 col
= box
.column(align
=False)
648 col
.prop_search(self
, "vert_group", bpy
.context
.object, "vertex_groups")
651 def draw_ant_water(self
, context
):
655 col
.prop(self
, "water_plane", toggle
=True)
657 col
= box
.column(align
=True)
658 col
.prop_search(self
, "water_material", bpy
.data
, "materials")
660 col
.prop(self
, "water_level")
664 def store_properties(operator
, ob
):
665 ob
.ant_landscape
.ant_terrain_name
= operator
.ant_terrain_name
666 ob
.ant_landscape
.at_cursor
= operator
.at_cursor
667 ob
.ant_landscape
.smooth_mesh
= operator
.smooth_mesh
668 ob
.ant_landscape
.tri_face
= operator
.tri_face
669 ob
.ant_landscape
.sphere_mesh
= operator
.sphere_mesh
670 ob
.ant_landscape
.land_material
= operator
.land_material
671 ob
.ant_landscape
.water_material
= operator
.water_material
672 ob
.ant_landscape
.texture_block
= operator
.texture_block
673 ob
.ant_landscape
.subdivision_x
= operator
.subdivision_x
674 ob
.ant_landscape
.subdivision_y
= operator
.subdivision_y
675 ob
.ant_landscape
.mesh_size_x
= operator
.mesh_size_x
676 ob
.ant_landscape
.mesh_size_y
= operator
.mesh_size_y
677 ob
.ant_landscape
.mesh_size
= operator
.mesh_size
678 ob
.ant_landscape
.random_seed
= operator
.random_seed
679 ob
.ant_landscape
.noise_offset_x
= operator
.noise_offset_x
680 ob
.ant_landscape
.noise_offset_y
= operator
.noise_offset_y
681 ob
.ant_landscape
.noise_offset_z
= operator
.noise_offset_z
682 ob
.ant_landscape
.noise_size_x
= operator
.noise_size_x
683 ob
.ant_landscape
.noise_size_y
= operator
.noise_size_y
684 ob
.ant_landscape
.noise_size_z
= operator
.noise_size_z
685 ob
.ant_landscape
.noise_size
= operator
.noise_size
686 ob
.ant_landscape
.noise_type
= operator
.noise_type
687 ob
.ant_landscape
.basis_type
= operator
.basis_type
688 ob
.ant_landscape
.vl_basis_type
= operator
.vl_basis_type
689 ob
.ant_landscape
.distortion
= operator
.distortion
690 ob
.ant_landscape
.hard_noise
= operator
.hard_noise
691 ob
.ant_landscape
.noise_depth
= operator
.noise_depth
692 ob
.ant_landscape
.amplitude
= operator
.amplitude
693 ob
.ant_landscape
.frequency
= operator
.frequency
694 ob
.ant_landscape
.dimension
= operator
.dimension
695 ob
.ant_landscape
.lacunarity
= operator
.lacunarity
696 ob
.ant_landscape
.offset
= operator
.offset
697 ob
.ant_landscape
.gain
= operator
.gain
698 ob
.ant_landscape
.marble_bias
= operator
.marble_bias
699 ob
.ant_landscape
.marble_sharp
= operator
.marble_sharp
700 ob
.ant_landscape
.marble_shape
= operator
.marble_shape
701 ob
.ant_landscape
.height
= operator
.height
702 ob
.ant_landscape
.height_invert
= operator
.height_invert
703 ob
.ant_landscape
.height_offset
= operator
.height_offset
704 ob
.ant_landscape
.maximum
= operator
.maximum
705 ob
.ant_landscape
.minimum
= operator
.minimum
706 ob
.ant_landscape
.edge_falloff
= operator
.edge_falloff
707 ob
.ant_landscape
.edge_level
= operator
.edge_level
708 ob
.ant_landscape
.falloff_x
= operator
.falloff_x
709 ob
.ant_landscape
.falloff_y
= operator
.falloff_y
710 ob
.ant_landscape
.strata_type
= operator
.strata_type
711 ob
.ant_landscape
.strata
= operator
.strata
712 ob
.ant_landscape
.water_plane
= operator
.water_plane
713 ob
.ant_landscape
.water_level
= operator
.water_level
714 ob
.ant_landscape
.vert_group
= operator
.vert_group
715 ob
.ant_landscape
.remove_double
= operator
.remove_double
716 ob
.ant_landscape
.fx_mixfactor
= operator
.fx_mixfactor
717 ob
.ant_landscape
.fx_mix_mode
= operator
.fx_mix_mode
718 ob
.ant_landscape
.fx_type
= operator
.fx_type
719 ob
.ant_landscape
.fx_bias
= operator
.fx_bias
720 ob
.ant_landscape
.fx_turb
= operator
.fx_turb
721 ob
.ant_landscape
.fx_depth
= operator
.fx_depth
722 ob
.ant_landscape
.fx_frequency
= operator
.fx_frequency
723 ob
.ant_landscape
.fx_amplitude
= operator
.fx_amplitude
724 ob
.ant_landscape
.fx_size
= operator
.fx_size
725 ob
.ant_landscape
.fx_loc_x
= operator
.fx_loc_x
726 ob
.ant_landscape
.fx_loc_y
= operator
.fx_loc_y
727 ob
.ant_landscape
.fx_height
= operator
.fx_height
728 ob
.ant_landscape
.fx_offset
= operator
.fx_offset
729 ob
.ant_landscape
.fx_invert
= operator
.fx_invert
733 # ------------------------------------------------------------
735 # "author": "Michel Anders, Ian Huish"
737 from random
import random
as rand
738 from math
import tan
, radians
739 from .eroder
import Grid
740 from .stats
import Stats
741 from .utils
import numexpr_available
744 def availableVertexGroupsOrNone(self
, context
):
745 groups
= [('None', 'None', 'None', 1)]
746 return groups
+ [(name
, name
, name
, n
+ 1) for n
, name
in enumerate(context
.active_object
.vertex_groups
.keys())]
749 class Eroder(bpy
.types
.Operator
):
750 bl_idname
= "mesh.eroder"
751 bl_label
= "ErosionR"
752 bl_description
= "Apply various kinds of erosion to a square ANT-Landscape grid. Also available in Weight Paint mode > Weights menu"
753 bl_options
= {'REGISTER', 'UNDO', 'PRESET'}
755 Iterations
: IntProperty(
757 description
="Number of overall iterations",
762 IterRiver
: IntProperty(
763 name
="River Iterations",
764 description
="Number of river iterations",
769 IterAva
: IntProperty(
770 name
="Avalanche Iterations",
771 description
="Number of avalanche iterations",
776 IterDiffuse
: IntProperty(
777 name
="Diffuse Iterations",
778 description
="Number of diffuse iterations",
784 name
="Rain on Plains",
785 description
="1 gives equal rain across the terrain, 0 rains more at the mountain tops",
792 description
="Thermal diffusion rate (1.0 is a fairly high rate)",
799 description
="Maximum stable talus angle",
807 description
="Total Rain amount",
814 name
="Rain variance",
815 description
="Rain variance (0 is constant, 1 is uniform)",
820 userainmap
: BoolProperty(
822 description
="Use active vertex group as a rain map",
826 name
="Soil solubility",
827 description
="Soil solubility - how quickly water quickly reaches saturation point",
833 name
="Deposition rate",
834 description
="Sediment deposition rate - how quickly silt is laid down once water stops flowing quickly",
840 name
="Fluvial Erosion Rate",
841 description
="Amount of sediment moved each main iteration - if 0, then rivers are formed but the mesh is not changed",
846 name
="Carrying capacity",
847 description
="Base sediment carrying capacity",
853 name
="Slope dependence",
854 description
="Slope dependence of carrying capacity (not used)",
861 description
="Evaporation Rate per grid square in % - causes sediment to be dropped closer to the hills",
866 numexpr
: BoolProperty(
868 description
="Use numexpr module (if available)",
872 name
="Diffusion Amount",
873 description
="Diffusion probability",
879 name
="Avalanche Amount",
880 description
="Avalanche amount",
887 description
="Water erosion probability",
892 smooth
: BoolProperty(
894 description
="Set smooth shading",
897 showiterstats
: BoolProperty(
898 name
="Iteration Stats",
899 description
="Show iteraration statistics",
902 showmeshstats
: BoolProperty(
904 description
="Show mesh statistics",
911 'rainmap': lambda g
, r
, c
: g
.rainmap
[r
, c
],
912 'scree': lambda g
, r
, c
: g
.avalanced
[r
, c
],
913 'avalanced': lambda g
, r
, c
: -g
.avalanced
[r
, c
],
914 'water': lambda g
, r
, c
: g
.water
[r
, c
] / g
.watermax
,
915 'scour': lambda g
, r
, c
: g
.scour
[r
, c
] / max(g
.scourmax
, -g
.scourmin
),
916 'deposit': lambda g
, r
, c
: g
.scour
[r
, c
] / min(-g
.scourmax
, g
.scourmin
),
917 'flowrate': lambda g
, r
, c
: g
.flowrate
[r
, c
],
918 'sediment': lambda g
, r
, c
: g
.sediment
[r
, c
],
919 'sedimentpct': lambda g
, r
, c
: g
.sedimentpct
[r
, c
],
920 'capacity': lambda g
, r
, c
: g
.capacity
[r
, c
]
923 def execute(self
, context
):
925 ob
= context
.active_object
930 for name
in self
.maps
:
932 ob
.vertex_groups
[name
]
934 ob
.vertex_groups
.new(name
=name
)
935 # Save a mapping from index to name, in case,
936 # the next iteration is different.
937 index_to_name
[ob
.vertex_groups
[name
].index
] = name
939 g
= Grid
.fromBlenderMesh(oldMesh
, ob
.vertex_groups
['rainmap'], self
.Ef
)
941 self
.counts
['diffuse'] = 0
942 self
.counts
['avalanche'] = 0
943 self
.counts
['water'] = 0
944 for i
in range(self
.Iterations
):
945 if self
.IterRiver
> 0:
946 for i
in range(self
.IterRiver
):
964 for k
in range(self
.IterDiffuse
):
965 g
.diffuse(self
.Kd
/ 5, self
.IterDiffuse
, self
.numexpr
)
966 self
.counts
['diffuse'] += 1
968 if self
.Kt
< radians(90) and self
.Pa
> 0:
969 for k
in range(self
.IterAva
):
970 # since dx and dy are scaled to 1, tan(Kt) is the height for a given angle
971 g
.avalanche(tan(self
.Kt
), self
.IterAva
, self
.Pa
, self
.numexpr
)
972 self
.counts
['avalanche'] += 1
974 g
.fluvial_erosion(self
.Kr
, self
.Kv
, self
.userainmap
, self
.Kc
, self
.Ks
,
975 self
.Kz
* 50, self
.Ka
, 0, 0, 0, 0, self
.numexpr
)
976 self
.counts
['water'] += 1
978 newMesh
= bpy
.data
.meshes
.new(oldMesh
.name
)
979 g
.toBlenderMesh(newMesh
)
981 # This empties ob.vertex_groups.
984 # Copy vertex groups from the old mesh.
985 for name
in self
.maps
:
986 ob
.vertex_groups
.new(name
=name
)
987 for vert
in oldMesh
.vertices
:
988 for group
in vert
.groups
:
989 name
= index_to_name
[group
.group
]
991 ob
.vertex_groups
[name
].add([vert
.index
], group
.weight
, 'REPLACE')
994 for row
in range(g
.rainmap
.shape
[0]):
995 for col
in range(g
.rainmap
.shape
[1]):
996 i
= row
* g
.rainmap
.shape
[1] + col
997 for name
, fn
in self
.maps
.items():
998 ob
.vertex_groups
[name
].add([i
], fn(g
, row
, col
), 'ADD')
1000 ob
.vertex_groups
.active
= ob
.vertex_groups
['capacity']
1003 bpy
.ops
.object.shade_smooth()
1006 if self
.showmeshstats
:
1007 self
.stats
.meshstats
= g
.analyze()
1011 def draw(self
, context
):
1012 layout
= self
.layout
1014 layout
.operator('screen.repeat_last', text
="Repeat", icon
='FILE_REFRESH')
1016 layout
.prop(self
, 'Iterations')
1019 col
= box
.column(align
=True)
1020 col
.label(text
="Thermal (Diffusion)")
1021 col
.prop(self
, 'Kd')
1022 col
.prop(self
, 'IterDiffuse')
1025 col
= box
.column(align
=True)
1026 col
.label(text
="Avalanche (Talus)")
1027 col
.prop(self
, 'Pa')
1028 col
.prop(self
, 'IterAva')
1029 col
.prop(self
, 'Kt')
1032 col
= box
.column(align
=True)
1033 col
.label(text
="River erosion")
1034 col
.prop(self
, 'IterRiver')
1035 col
.prop(self
, 'Kz')
1036 col
.prop(self
, 'Ks')
1037 col
.prop(self
, 'Kc')
1038 col
.prop(self
, 'Kdep')
1039 col
.prop(self
, 'Kr')
1040 col
.prop(self
, 'Kv')
1041 col
.prop(self
, 'Kev')
1043 col
.prop(self
, 'Ef')
1045 layout
.prop(self
, 'smooth')