1 # SPDX-FileCopyrightText: 2017-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 # Another Noise Tool - Functions
9 # Michel Anders, Ian Huish
13 from bpy
.props
import (
24 from .ant_noise
import noise_gen
26 # ------------------------------------------------------------
27 # Create a new mesh (object) from verts/edges/faces.
28 # verts/edges/faces ... List of vertices/edges/faces for the
29 # new mesh (as used in from_pydata).
30 # name ... Name of the new mesh (& object)
32 from bpy_extras
import object_utils
35 def create_mesh_object(context
, verts
, edges
, faces
, name
):
37 mesh
= bpy
.data
.meshes
.new(name
)
38 # Make a mesh from a list of verts/edges/faces.
39 mesh
.from_pydata(verts
, [], faces
)
40 # Update mesh geometry after adding stuff.
42 return object_utils
.object_data_add(context
, mesh
, operator
=None)
46 def grid_gen(sub_d_x
, sub_d_y
, tri
, meshsize_x
, meshsize_y
, props
, water_plane
, water_level
):
49 vappend
= verts
.append
50 fappend
= faces
.append
51 for i
in range(0, sub_d_x
):
52 x
= meshsize_x
* (i
/ (sub_d_x
- 1) - 1 / 2)
53 for j
in range(0, sub_d_y
):
54 y
= meshsize_y
* (j
/ (sub_d_y
- 1) - 1 / 2)
56 z
= noise_gen((x
, y
, 0), props
)
62 A
= i
* sub_d_y
+ (j
- 1)
64 C
= (i
- 1) * sub_d_y
+ j
65 D
= (i
- 1) * sub_d_y
+ (j
- 1)
76 def sphere_gen(sub_d_x
, sub_d_y
, tri
, meshsize
, props
, water_plane
, water_level
):
79 vappend
= verts
.append
80 fappend
= faces
.append
83 for i
in range(0, sub_d_x
):
84 for j
in range(0, sub_d_y
):
85 u
= sin(j
* pi
* 2 / (sub_d_y
- 1)) * cos(-pi
/ 2 + i
* pi
/ (sub_d_x
- 1)) * meshsize
/ 2
86 v
= cos(j
* pi
* 2 / (sub_d_y
- 1)) * cos(-pi
/ 2 + i
* pi
/ (sub_d_x
- 1)) * meshsize
/ 2
87 w
= sin(-pi
/ 2 + i
* pi
/ (sub_d_x
- 1)) * meshsize
/ 2
91 h
= noise_gen((u
, v
, w
), props
) / meshsize
92 vappend(((u
+ u
* h
), (v
+ v
* h
), (w
+ w
* h
)))
95 for i
in range(0, sub_d_y
* (sub_d_x
- 1)):
96 if count
< sub_d_y
- 1:
100 D
= (i
+ sub_d_y
) + 1
105 fappend((A
, B
, C
, D
))
113 # ------------------------------------------------------------
114 # Do refresh - redraw
115 class AntLandscapeRefresh(bpy
.types
.Operator
):
116 bl_idname
= "mesh.ant_landscape_refresh"
118 bl_description
= "Refresh landscape with current settings"
119 bl_options
= {'REGISTER', 'UNDO'}
122 def poll(cls
, context
):
123 ob
= bpy
.context
.active_object
124 return (ob
.ant_landscape
and not ob
.ant_landscape
.sphere_mesh
)
126 def execute(self
, context
):
128 obj
= bpy
.context
.active_object
130 bpy
.ops
.object.mode_set(mode
='EDIT')
131 bpy
.ops
.object.mode_set(mode
='OBJECT')
133 keys
= obj
.ant_landscape
.keys()
135 ob
= obj
.ant_landscape
138 prop
.append(getattr(ob
, key
))
143 if ob
['vert_group'] != "" and ob
['vert_group'] in obj
.vertex_groups
:
144 vertex_group
= obj
.vertex_groups
[ob
['vert_group']]
145 gi
= vertex_group
.index
146 for v
in mesh
.vertices
:
150 v
.co
[2] = vertex_group
.weight(v
.index
) * noise_gen(v
.co
, prop
)
152 for v
in mesh
.vertices
:
154 v
.co
[2] = noise_gen(v
.co
, prop
)
161 # ------------------------------------------------------------
165 class AntLandscapeRegenerate(bpy
.types
.Operator
):
166 bl_idname
= "mesh.ant_landscape_regenerate"
167 bl_label
= "Regenerate"
168 bl_description
= "Regenerate landscape with current settings"
169 bl_options
= {'REGISTER', 'UNDO'}
172 def poll(cls
, context
):
173 ob
= bpy
.context
.active_object
174 if ob
.mode
== 'EDIT':
176 return ob
.ant_landscape
178 def execute(self
, context
):
180 view_layer
= bpy
.context
.view_layer
182 obj
= bpy
.context
.active_object
184 keys
= obj
.ant_landscape
.keys()
186 ob
= obj
.ant_landscape
189 ant_props
.append(getattr(ob
, key
))
193 # Main function, create landscape mesh object
194 if ob
['sphere_mesh']:
196 verts
, faces
= sphere_gen(
205 new_ob
= create_mesh_object(context
, verts
, [], faces
, new_name
)
206 if ob
['remove_double']:
207 new_ob
.select_set(True)
208 bpy
.ops
.object.mode_set(mode
='EDIT')
209 bpy
.ops
.mesh
.remove_doubles(threshold
=0.0001, use_unselected
=False)
210 bpy
.ops
.object.mode_set(mode
='OBJECT')
213 verts
, faces
= grid_gen(
223 new_ob
= create_mesh_object(context
, verts
, [], faces
, new_name
)
225 new_ob
.select_set(True)
227 if ob
['smooth_mesh']:
228 bpy
.ops
.object.shade_smooth()
230 bpy
.ops
.object.shade_flat()
233 if ob
['land_material'] != "" and ob
['land_material'] in bpy
.data
.materials
:
234 mat
= bpy
.data
.materials
[ob
['land_material']]
235 bpy
.context
.object.data
.materials
.append(mat
)
238 if ob
['water_plane']:
239 if ob
['sphere_mesh']:
241 verts
, faces
= sphere_gen(
250 wobj
= create_mesh_object(context
, verts
, [], faces
, new_name
+ "_plane")
251 if ob
['remove_double']:
252 wobj
.select_set(True)
253 bpy
.ops
.object.mode_set(mode
='EDIT')
254 bpy
.ops
.mesh
.remove_doubles(threshold
=0.0001, use_unselected
=False)
255 bpy
.ops
.object.mode_set(mode
='OBJECT')
258 verts
, faces
= grid_gen(
268 wobj
= create_mesh_object(context
, verts
, [], faces
, new_name
+ "_plane")
270 wobj
.select_set(True)
272 if ob
['smooth_mesh']:
273 bpy
.ops
.object.shade_smooth()
275 bpy
.ops
.object.shade_flat()
278 if ob
['water_material'] != "" and ob
['water_material'] in bpy
.data
.materials
:
279 mat
= bpy
.data
.materials
[ob
['water_material']]
280 bpy
.context
.object.data
.materials
.append(mat
)
283 if ob
['water_plane']:
284 wobj
.location
= obj
.location
285 wobj
.rotation_euler
= obj
.rotation_euler
286 wobj
.scale
= obj
.scale
287 wobj
.select_set(False)
289 new_ob
.location
= obj
.location
290 new_ob
.rotation_euler
= obj
.rotation_euler
291 new_ob
.scale
= obj
.scale
294 new_ob
= store_properties(ob
, new_ob
)
297 new_ob
.select_set(False)
300 view_layer
.objects
.active
= obj
301 bpy
.ops
.object.delete(use_global
=False)
303 # Select landscape and make active
304 new_ob
.select_set(True)
305 view_layer
.objects
.active
= new_ob
310 # ------------------------------------------------------------
311 # Z normal value to vertex group (Slope map)
312 class AntVgSlopeMap(bpy
.types
.Operator
):
313 bl_idname
= "mesh.ant_slope_map"
314 bl_label
= "Weight from Slope"
315 bl_description
= "A.N.T. Slope Map - z normal value to vertex group weight"
316 bl_options
= {'REGISTER', 'UNDO'}
318 z_method
: EnumProperty(
322 ('SLOPE_Z', "Z Slope", "Slope for planar mesh"),
323 ('SLOPE_XYZ', "Sphere Slope", "Slope for spherical mesh")
325 group_name
: StringProperty(
326 name
="Vertex Group Name:",
330 select_flat
: BoolProperty(
333 description
="Select vertices on flat surface"
335 select_range
: FloatProperty(
336 name
="Vert Select Range:",
340 description
="Increase to select more vertices on slopes"
344 def poll(cls
, context
):
346 return (ob
and ob
.type == 'MESH')
348 def invoke(self
, context
, event
):
349 wm
= context
.window_manager
350 return wm
.invoke_props_dialog(self
)
352 def execute(self
, context
):
353 message
= "Popup Values: %d, %f, %s, %s" % \
354 (self
.select_flat
, self
.select_range
, self
.group_name
, self
.z_method
)
355 self
.report({'INFO'}, message
)
357 bpy
.ops
.object.mode_set(mode
='OBJECT')
358 ob
= bpy
.context
.active_object
362 bpy
.ops
.object.mode_set(mode
='EDIT')
363 bpy
.ops
.mesh
.select_all(action
='DESELECT')
364 bpy
.context
.tool_settings
.mesh_select_mode
= [True, False, False]
365 bpy
.ops
.object.mode_set(mode
='OBJECT')
367 bpy
.ops
.object.vertex_group_add()
368 vg_normal
= ob
.vertex_groups
.active
370 for v
in ob
.data
.vertices
:
371 if self
.z_method
== 'SLOPE_XYZ':
372 zval
= (v
.co
.normalized() * v
.normal
.normalized()) * 2 - 1
376 vg_normal
.add([v
.index
], zval
, 'REPLACE')
379 if zval
>= (1.0 - self
.select_range
):
382 vg_normal
.name
= self
.group_name
384 bpy
.ops
.paint
.weight_paint_toggle()
388 # ------------------------------------------------------------
391 def draw_ant_refresh(self
, context
):
393 if self
.auto_refresh
is False:
395 elif self
.auto_refresh
is True:
397 row
= layout
.box().row()
400 split
.prop(self
, "auto_refresh", toggle
=True, icon_only
=True, icon
='AUTO')
401 split
.prop(self
, "refresh", toggle
=True, icon_only
=True, icon
='FILE_REFRESH')
404 def draw_ant_main(self
, context
, generate
=True):
407 box
.prop(self
, "show_main_settings", toggle
=True)
408 if self
.show_main_settings
:
410 row
= box
.row(align
=True)
411 split
= row
.split(align
=True)
413 split
.prop(self
, "at_cursor", toggle
=True, icon_only
=True, icon
='PIVOT_CURSOR')
415 split
.prop(self
, "smooth_mesh", toggle
=True, icon_only
=True, icon
='SHADING_SOLID')
416 split
.prop(self
, "tri_face", toggle
=True, text
="Triangulate", icon
='MESH_DATA')
418 if not self
.sphere_mesh
:
419 row
= box
.row(align
=True)
420 row
.prop(self
, "sphere_mesh", toggle
=True)
422 row
= box
.row(align
=True)
423 split
= row
.split(factor
=0.5, align
=True)
424 split
.prop(self
, "sphere_mesh", toggle
=True)
425 split
.prop(self
, "remove_double", toggle
=True)
427 box
.prop(self
, "ant_terrain_name")
428 box
.prop_search(self
, "land_material", bpy
.data
, "materials")
430 col
= box
.column(align
=True)
431 col
.prop(self
, "subdivision_x")
432 col
.prop(self
, "subdivision_y")
433 col
= box
.column(align
=True)
435 col
.prop(self
, "mesh_size")
437 col
.prop(self
, "mesh_size_x")
438 col
.prop(self
, "mesh_size_y")
441 def draw_ant_noise(self
, context
, generate
=True):
444 box
.prop(self
, "show_noise_settings", toggle
=True)
445 if self
.show_noise_settings
:
446 box
.prop(self
, "noise_type")
447 if self
.noise_type
== "blender_texture":
448 box
.prop_search(self
, "texture_block", bpy
.data
, "textures")
450 box
.prop(self
, "basis_type")
452 col
= box
.column(align
=True)
453 col
.prop(self
, "random_seed")
454 col
= box
.column(align
=True)
455 col
.prop(self
, "noise_offset_x")
456 col
.prop(self
, "noise_offset_y")
457 if self
.sphere_mesh
or generate
== False:
458 col
.prop(self
, "noise_offset_z")
459 col
.prop(self
, "noise_size_x")
460 col
.prop(self
, "noise_size_y")
461 if self
.sphere_mesh
or generate
== False:
462 col
.prop(self
, "noise_size_z")
464 col
= box
.column(align
=True)
465 col
.prop(self
, "noise_size")
467 col
= box
.column(align
=True)
468 if self
.noise_type
== "multi_fractal":
469 col
.prop(self
, "noise_depth")
470 col
.prop(self
, "dimension")
471 col
.prop(self
, "lacunarity")
472 elif self
.noise_type
== "ridged_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
== "hybrid_multi_fractal":
479 col
.prop(self
, "noise_depth")
480 col
.prop(self
, "dimension")
481 col
.prop(self
, "lacunarity")
482 col
.prop(self
, "offset")
483 col
.prop(self
, "gain")
484 elif self
.noise_type
== "hetero_terrain":
485 col
.prop(self
, "noise_depth")
486 col
.prop(self
, "dimension")
487 col
.prop(self
, "lacunarity")
488 col
.prop(self
, "offset")
489 elif self
.noise_type
== "fractal":
490 col
.prop(self
, "noise_depth")
491 col
.prop(self
, "dimension")
492 col
.prop(self
, "lacunarity")
493 elif self
.noise_type
== "turbulence_vector":
494 col
.prop(self
, "noise_depth")
495 col
.prop(self
, "amplitude")
496 col
.prop(self
, "frequency")
498 row
= col
.row(align
=True)
499 row
.prop(self
, "hard_noise", expand
=True)
500 elif self
.noise_type
== "variable_lacunarity":
501 box
.prop(self
, "vl_basis_type")
502 box
.prop(self
, "distortion")
503 elif self
.noise_type
== "marble_noise":
504 box
.prop(self
, "marble_shape")
505 box
.prop(self
, "marble_bias")
506 box
.prop(self
, "marble_sharp")
507 col
= box
.column(align
=True)
508 col
.prop(self
, "distortion")
509 col
.prop(self
, "noise_depth")
511 row
= col
.row(align
=True)
512 row
.prop(self
, "hard_noise", expand
=True)
513 elif self
.noise_type
== "shattered_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")
519 elif self
.noise_type
== "strata_hterrain":
520 col
.prop(self
, "noise_depth")
521 col
.prop(self
, "dimension")
522 col
.prop(self
, "lacunarity")
523 col
.prop(self
, "offset")
524 col
.prop(self
, "distortion", text
="Strata")
525 elif self
.noise_type
== "ant_turbulence":
526 col
.prop(self
, "noise_depth")
527 col
.prop(self
, "amplitude")
528 col
.prop(self
, "frequency")
529 col
.prop(self
, "distortion")
531 row
= col
.row(align
=True)
532 row
.prop(self
, "hard_noise", expand
=True)
533 elif self
.noise_type
== "vl_noise_turbulence":
534 col
.prop(self
, "noise_depth")
535 col
.prop(self
, "amplitude")
536 col
.prop(self
, "frequency")
537 col
.prop(self
, "distortion")
539 box
.prop(self
, "vl_basis_type")
541 row
= col
.row(align
=True)
542 row
.prop(self
, "hard_noise", expand
=True)
543 elif self
.noise_type
== "vl_hTerrain":
544 col
.prop(self
, "noise_depth")
545 col
.prop(self
, "dimension")
546 col
.prop(self
, "lacunarity")
547 col
.prop(self
, "offset")
548 col
.prop(self
, "distortion")
550 box
.prop(self
, "vl_basis_type")
551 elif self
.noise_type
== "distorted_heteroTerrain":
552 col
.prop(self
, "noise_depth")
553 col
.prop(self
, "dimension")
554 col
.prop(self
, "lacunarity")
555 col
.prop(self
, "offset")
556 col
.prop(self
, "distortion")
558 box
.prop(self
, "vl_basis_type")
559 elif self
.noise_type
== "double_multiFractal":
560 col
.prop(self
, "noise_depth")
561 col
.prop(self
, "dimension")
562 col
.prop(self
, "lacunarity")
563 col
.prop(self
, "offset")
564 col
.prop(self
, "gain")
566 box
.prop(self
, "vl_basis_type")
567 elif self
.noise_type
== "rocks_noise":
568 col
.prop(self
, "noise_depth")
569 col
.prop(self
, "distortion")
571 row
= col
.row(align
=True)
572 row
.prop(self
, "hard_noise", expand
=True)
573 elif self
.noise_type
== "slick_rock":
574 col
.prop(self
, "noise_depth")
575 col
.prop(self
, "dimension")
576 col
.prop(self
, "lacunarity")
577 col
.prop(self
, "gain")
578 col
.prop(self
, "offset")
579 col
.prop(self
, "distortion")
581 box
.prop(self
, "vl_basis_type")
582 elif self
.noise_type
== "planet_noise":
583 col
.prop(self
, "noise_depth")
585 row
= col
.row(align
=True)
586 row
.prop(self
, "hard_noise", expand
=True)
589 col
= box
.column(align
=False)
590 box
.prop(self
, "fx_type")
591 if self
.fx_type
!= "0":
592 if int(self
.fx_type
) <= 12:
593 box
.prop(self
, "fx_bias")
595 box
.prop(self
, "fx_mix_mode")
596 col
= box
.column(align
=True)
597 col
.prop(self
, "fx_mixfactor")
599 col
= box
.column(align
=True)
600 col
.prop(self
, "fx_loc_x")
601 col
.prop(self
, "fx_loc_y")
602 col
.prop(self
, "fx_size")
604 col
= box
.column(align
=True)
605 col
.prop(self
, "fx_depth")
606 if self
.fx_depth
!= 0:
607 col
.prop(self
, "fx_frequency")
608 col
.prop(self
, "fx_amplitude")
609 col
.prop(self
, "fx_turb")
611 col
= box
.column(align
=True)
612 row
= col
.row(align
=True).split(factor
=0.92, align
=True)
613 row
.prop(self
, "fx_height")
614 row
.prop(self
, "fx_invert", toggle
=True, text
="", icon
='ARROW_LEFTRIGHT')
615 col
.prop(self
, "fx_offset")
618 def draw_ant_displace(self
, context
, generate
=True):
621 box
.prop(self
, "show_displace_settings", toggle
=True)
622 if self
.show_displace_settings
:
624 col
= box
.column(align
=False)
625 col
.prop(self
, "direction", toggle
=True)
627 col
= box
.column(align
=True)
628 row
= col
.row(align
=True).split(factor
=0.92, align
=True)
629 row
.prop(self
, "height")
630 row
.prop(self
, "height_invert", toggle
=True, text
="", icon
='ARROW_LEFTRIGHT')
631 col
.prop(self
, "height_offset")
632 col
.prop(self
, "maximum")
633 col
.prop(self
, "minimum")
635 if not self
.sphere_mesh
:
637 col
.prop(self
, "edge_falloff")
638 if self
.edge_falloff
!= "0":
639 col
= box
.column(align
=True)
640 col
.prop(self
, "edge_level")
641 if self
.edge_falloff
in ["2", "3"]:
642 col
.prop(self
, "falloff_x")
643 if self
.edge_falloff
in ["1", "3"]:
644 col
.prop(self
, "falloff_y")
647 col
.prop(self
, "strata_type")
648 if self
.strata_type
!= "0":
650 col
.prop(self
, "strata")
653 col
= box
.column(align
=False)
654 col
.prop_search(self
, "vert_group", bpy
.context
.object, "vertex_groups")
657 def draw_ant_water(self
, context
):
661 col
.prop(self
, "water_plane", toggle
=True)
663 col
= box
.column(align
=True)
664 col
.prop_search(self
, "water_material", bpy
.data
, "materials")
666 col
.prop(self
, "water_level")
670 def store_properties(operator
, ob
):
671 ob
.ant_landscape
.ant_terrain_name
= operator
.ant_terrain_name
672 ob
.ant_landscape
.at_cursor
= operator
.at_cursor
673 ob
.ant_landscape
.smooth_mesh
= operator
.smooth_mesh
674 ob
.ant_landscape
.tri_face
= operator
.tri_face
675 ob
.ant_landscape
.sphere_mesh
= operator
.sphere_mesh
676 ob
.ant_landscape
.land_material
= operator
.land_material
677 ob
.ant_landscape
.water_material
= operator
.water_material
678 ob
.ant_landscape
.texture_block
= operator
.texture_block
679 ob
.ant_landscape
.subdivision_x
= operator
.subdivision_x
680 ob
.ant_landscape
.subdivision_y
= operator
.subdivision_y
681 ob
.ant_landscape
.mesh_size_x
= operator
.mesh_size_x
682 ob
.ant_landscape
.mesh_size_y
= operator
.mesh_size_y
683 ob
.ant_landscape
.mesh_size
= operator
.mesh_size
684 ob
.ant_landscape
.random_seed
= operator
.random_seed
685 ob
.ant_landscape
.noise_offset_x
= operator
.noise_offset_x
686 ob
.ant_landscape
.noise_offset_y
= operator
.noise_offset_y
687 ob
.ant_landscape
.noise_offset_z
= operator
.noise_offset_z
688 ob
.ant_landscape
.noise_size_x
= operator
.noise_size_x
689 ob
.ant_landscape
.noise_size_y
= operator
.noise_size_y
690 ob
.ant_landscape
.noise_size_z
= operator
.noise_size_z
691 ob
.ant_landscape
.noise_size
= operator
.noise_size
692 ob
.ant_landscape
.noise_type
= operator
.noise_type
693 ob
.ant_landscape
.basis_type
= operator
.basis_type
694 ob
.ant_landscape
.vl_basis_type
= operator
.vl_basis_type
695 ob
.ant_landscape
.distortion
= operator
.distortion
696 ob
.ant_landscape
.hard_noise
= operator
.hard_noise
697 ob
.ant_landscape
.noise_depth
= operator
.noise_depth
698 ob
.ant_landscape
.amplitude
= operator
.amplitude
699 ob
.ant_landscape
.frequency
= operator
.frequency
700 ob
.ant_landscape
.dimension
= operator
.dimension
701 ob
.ant_landscape
.lacunarity
= operator
.lacunarity
702 ob
.ant_landscape
.offset
= operator
.offset
703 ob
.ant_landscape
.gain
= operator
.gain
704 ob
.ant_landscape
.marble_bias
= operator
.marble_bias
705 ob
.ant_landscape
.marble_sharp
= operator
.marble_sharp
706 ob
.ant_landscape
.marble_shape
= operator
.marble_shape
707 ob
.ant_landscape
.height
= operator
.height
708 ob
.ant_landscape
.height_invert
= operator
.height_invert
709 ob
.ant_landscape
.height_offset
= operator
.height_offset
710 ob
.ant_landscape
.maximum
= operator
.maximum
711 ob
.ant_landscape
.minimum
= operator
.minimum
712 ob
.ant_landscape
.edge_falloff
= operator
.edge_falloff
713 ob
.ant_landscape
.edge_level
= operator
.edge_level
714 ob
.ant_landscape
.falloff_x
= operator
.falloff_x
715 ob
.ant_landscape
.falloff_y
= operator
.falloff_y
716 ob
.ant_landscape
.strata_type
= operator
.strata_type
717 ob
.ant_landscape
.strata
= operator
.strata
718 ob
.ant_landscape
.water_plane
= operator
.water_plane
719 ob
.ant_landscape
.water_level
= operator
.water_level
720 ob
.ant_landscape
.vert_group
= operator
.vert_group
721 ob
.ant_landscape
.remove_double
= operator
.remove_double
722 ob
.ant_landscape
.fx_mixfactor
= operator
.fx_mixfactor
723 ob
.ant_landscape
.fx_mix_mode
= operator
.fx_mix_mode
724 ob
.ant_landscape
.fx_type
= operator
.fx_type
725 ob
.ant_landscape
.fx_bias
= operator
.fx_bias
726 ob
.ant_landscape
.fx_turb
= operator
.fx_turb
727 ob
.ant_landscape
.fx_depth
= operator
.fx_depth
728 ob
.ant_landscape
.fx_frequency
= operator
.fx_frequency
729 ob
.ant_landscape
.fx_amplitude
= operator
.fx_amplitude
730 ob
.ant_landscape
.fx_size
= operator
.fx_size
731 ob
.ant_landscape
.fx_loc_x
= operator
.fx_loc_x
732 ob
.ant_landscape
.fx_loc_y
= operator
.fx_loc_y
733 ob
.ant_landscape
.fx_height
= operator
.fx_height
734 ob
.ant_landscape
.fx_offset
= operator
.fx_offset
735 ob
.ant_landscape
.fx_invert
= operator
.fx_invert
739 # ------------------------------------------------------------
741 # "author": "Michel Anders, Ian Huish"
743 from random
import random
as rand
744 from math
import tan
, radians
745 from .eroder
import Grid
746 from .stats
import Stats
747 from .utils
import numexpr_available
750 def availableVertexGroupsOrNone(self
, context
):
751 groups
= [('None', 'None', 'None', 1)]
752 return groups
+ [(name
, name
, name
, n
+ 1) for n
, name
in enumerate(context
.active_object
.vertex_groups
.keys())]
755 class Eroder(bpy
.types
.Operator
):
756 bl_idname
= "mesh.eroder"
757 bl_label
= "ErosionR"
758 bl_description
= "Apply various kinds of erosion to a square ANT-Landscape grid. Also available in Weight Paint mode > Weights menu"
759 bl_options
= {'REGISTER', 'UNDO', 'PRESET'}
761 Iterations
: IntProperty(
763 description
="Number of overall iterations",
768 IterRiver
: IntProperty(
769 name
="River Iterations",
770 description
="Number of river iterations",
775 IterAva
: IntProperty(
776 name
="Avalanche Iterations",
777 description
="Number of avalanche iterations",
782 IterDiffuse
: IntProperty(
783 name
="Diffuse Iterations",
784 description
="Number of diffuse iterations",
790 name
="Rain on Plains",
791 description
="1 gives equal rain across the terrain, 0 rains more at the mountain tops",
798 description
="Thermal diffusion rate (1.0 is a fairly high rate)",
805 description
="Maximum stable talus angle",
813 description
="Total Rain amount",
820 name
="Rain variance",
821 description
="Rain variance (0 is constant, 1 is uniform)",
826 userainmap
: BoolProperty(
828 description
="Use active vertex group as a rain map",
832 name
="Soil solubility",
833 description
="Soil solubility - how quickly water quickly reaches saturation point",
839 name
="Deposition rate",
840 description
="Sediment deposition rate - how quickly silt is laid down once water stops flowing quickly",
846 name
="Fluvial Erosion Rate",
847 description
="Amount of sediment moved each main iteration - if 0, then rivers are formed but the mesh is not changed",
852 name
="Carrying capacity",
853 description
="Base sediment carrying capacity",
859 name
="Slope dependence",
860 description
="Slope dependence of carrying capacity (not used)",
867 description
="Evaporation Rate per grid square in % - causes sediment to be dropped closer to the hills",
872 numexpr
: BoolProperty(
874 description
="Use numexpr module (if available)",
878 name
="Diffusion Amount",
879 description
="Diffusion probability",
885 name
="Avalanche Amount",
886 description
="Avalanche amount",
893 description
="Water erosion probability",
898 smooth
: BoolProperty(
900 description
="Set smooth shading",
903 showiterstats
: BoolProperty(
904 name
="Iteration Stats",
905 description
="Show iteraration statistics",
908 showmeshstats
: BoolProperty(
910 description
="Show mesh statistics",
917 'rainmap': lambda g
, r
, c
: g
.rainmap
[r
, c
],
918 'scree': lambda g
, r
, c
: g
.avalanced
[r
, c
],
919 'avalanced': lambda g
, r
, c
: -g
.avalanced
[r
, c
],
920 'water': lambda g
, r
, c
: g
.water
[r
, c
] / g
.watermax
,
921 'scour': lambda g
, r
, c
: g
.scour
[r
, c
] / max(g
.scourmax
, -g
.scourmin
),
922 'deposit': lambda g
, r
, c
: g
.scour
[r
, c
] / min(-g
.scourmax
, g
.scourmin
),
923 'flowrate': lambda g
, r
, c
: g
.flowrate
[r
, c
],
924 'sediment': lambda g
, r
, c
: g
.sediment
[r
, c
],
925 'sedimentpct': lambda g
, r
, c
: g
.sedimentpct
[r
, c
],
926 'capacity': lambda g
, r
, c
: g
.capacity
[r
, c
]
929 def execute(self
, context
):
931 ob
= context
.active_object
936 for name
in self
.maps
:
938 ob
.vertex_groups
[name
]
940 ob
.vertex_groups
.new(name
=name
)
941 # Save a mapping from index to name, in case,
942 # the next iteration is different.
943 index_to_name
[ob
.vertex_groups
[name
].index
] = name
945 g
= Grid
.fromBlenderMesh(oldMesh
, ob
.vertex_groups
['rainmap'], self
.Ef
)
947 self
.counts
['diffuse'] = 0
948 self
.counts
['avalanche'] = 0
949 self
.counts
['water'] = 0
950 for i
in range(self
.Iterations
):
951 if self
.IterRiver
> 0:
952 for i
in range(self
.IterRiver
):
970 for k
in range(self
.IterDiffuse
):
971 g
.diffuse(self
.Kd
/ 5, self
.IterDiffuse
, self
.numexpr
)
972 self
.counts
['diffuse'] += 1
974 if self
.Kt
< radians(90) and self
.Pa
> 0:
975 for k
in range(self
.IterAva
):
976 # since dx and dy are scaled to 1, tan(Kt) is the height for a given angle
977 g
.avalanche(tan(self
.Kt
), self
.IterAva
, self
.Pa
, self
.numexpr
)
978 self
.counts
['avalanche'] += 1
980 g
.fluvial_erosion(self
.Kr
, self
.Kv
, self
.userainmap
, self
.Kc
, self
.Ks
,
981 self
.Kz
* 50, self
.Ka
, 0, 0, 0, 0, self
.numexpr
)
982 self
.counts
['water'] += 1
984 newMesh
= bpy
.data
.meshes
.new(oldMesh
.name
)
985 g
.toBlenderMesh(newMesh
)
987 # This empties ob.vertex_groups.
990 # Copy vertex groups from the old mesh.
991 for name
in self
.maps
:
992 ob
.vertex_groups
.new(name
=name
)
993 for vert
in oldMesh
.vertices
:
994 for group
in vert
.groups
:
995 name
= index_to_name
[group
.group
]
997 ob
.vertex_groups
[name
].add([vert
.index
], group
.weight
, 'REPLACE')
1000 for row
in range(g
.rainmap
.shape
[0]):
1001 for col
in range(g
.rainmap
.shape
[1]):
1002 i
= row
* g
.rainmap
.shape
[1] + col
1003 for name
, fn
in self
.maps
.items():
1004 ob
.vertex_groups
[name
].add([i
], fn(g
, row
, col
), 'ADD')
1006 ob
.vertex_groups
.active
= ob
.vertex_groups
['capacity']
1009 bpy
.ops
.object.shade_smooth()
1011 bpy
.ops
.object.shade_flat()
1014 if self
.showmeshstats
:
1015 self
.stats
.meshstats
= g
.analyze()
1019 def draw(self
, context
):
1020 layout
= self
.layout
1022 layout
.operator('screen.repeat_last', text
="Repeat", icon
='FILE_REFRESH')
1024 layout
.prop(self
, 'Iterations')
1027 col
= box
.column(align
=True)
1028 col
.label(text
="Thermal (Diffusion)")
1029 col
.prop(self
, 'Kd')
1030 col
.prop(self
, 'IterDiffuse')
1033 col
= box
.column(align
=True)
1034 col
.label(text
="Avalanche (Talus)")
1035 col
.prop(self
, 'Pa')
1036 col
.prop(self
, 'IterAva')
1037 col
.prop(self
, 'Kt')
1040 col
= box
.column(align
=True)
1041 col
.label(text
="River erosion")
1042 col
.prop(self
, 'IterRiver')
1043 col
.prop(self
, 'Kz')
1044 col
.prop(self
, 'Ks')
1045 col
.prop(self
, 'Kc')
1046 col
.prop(self
, 'Kdep')
1047 col
.prop(self
, 'Kr')
1048 col
.prop(self
, 'Kv')
1049 col
.prop(self
, 'Kev')
1051 col
.prop(self
, 'Ef')
1053 layout
.prop(self
, 'smooth')