Merge branch 'master' into blender2.8
[blender-addons.git] / ant_landscape / ant_functions.py
blob3c0f2c34f3cf5e5f75d8b2855fc126d44af3baed
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 # Another Noise Tool - Functions
20 # Jimmy Hazevoet
22 # ErosionR:
23 # Michel Anders, Ian Huish
25 # import modules
26 import bpy
27 from bpy.props import (
28 BoolProperty,
29 FloatProperty,
30 StringProperty,
31 EnumProperty,
32 IntProperty,
33 PointerProperty,
35 from math import (
36 sin, cos, pi,
38 from .ant_noise import noise_gen
40 # ------------------------------------------------------------
41 # Create a new mesh (object) from verts/edges/faces.
42 # verts/edges/faces ... List of vertices/edges/faces for the
43 # new mesh (as used in from_pydata).
44 # name ... Name of the new mesh (& object)
46 from bpy_extras import object_utils
48 def create_mesh_object(context, verts, edges, faces, name):
49 # Create new mesh
50 mesh = bpy.data.meshes.new(name)
51 # Make a mesh from a list of verts/edges/faces.
52 mesh.from_pydata(verts, [], faces)
53 # Update mesh geometry after adding stuff.
54 mesh.update()
55 return object_utils.object_data_add(context, mesh, operator=None)
58 # Generate XY Grid
59 def grid_gen(sub_d_x, sub_d_y, tri, meshsize_x, meshsize_y, props, water_plane, water_level):
60 verts = []
61 faces = []
62 for i in range (0, sub_d_x):
63 x = meshsize_x * (i / (sub_d_x - 1) - 1 / 2)
64 for j in range(0, sub_d_y):
65 y = meshsize_y * (j / (sub_d_y - 1) - 1 / 2)
66 if water_plane:
67 z = water_level
68 else:
69 z = noise_gen((x, y, 0), props)
71 verts.append((x,y,z))
73 count = 0
74 for i in range (0, sub_d_y * (sub_d_x - 1)):
75 if count < sub_d_y - 1 :
76 A = i + 1
77 B = i
78 C = (i + sub_d_y)
79 D = (i + sub_d_y) + 1
80 if tri:
81 faces.append((A, B, D))
82 faces.append((B, C, D))
83 else:
84 faces.append((A, B, C, D))
85 count = count + 1
86 else:
87 count = 0
89 return verts, faces
92 # Generate UV Sphere
93 def sphere_gen(sub_d_x, sub_d_y, tri, meshsize, props, water_plane, water_level):
94 verts = []
95 faces = []
96 sub_d_x += 1
97 sub_d_y += 1
98 for i in range(0, sub_d_x):
99 for j in range(0, sub_d_y):
100 u = sin(j * pi * 2 / (sub_d_y - 1)) * cos(-pi / 2 + i * pi / (sub_d_x - 1)) * meshsize / 2
101 v = cos(j * pi * 2 / (sub_d_y - 1)) * cos(-pi / 2 + i * pi / (sub_d_x - 1)) * meshsize / 2
102 w = sin(-pi / 2 + i * pi / (sub_d_x - 1)) * meshsize / 2
103 if water_plane:
104 h = water_level
105 else:
106 h = noise_gen((u, v, w), props) / meshsize
107 verts.append(((u + u * h), (v + v * h), (w + w * h)))
109 count = 0
110 for i in range (0, sub_d_y * (sub_d_x - 1)):
111 if count < sub_d_y - 1 :
112 A = i + 1
113 B = i
114 C = (i + sub_d_y)
115 D = (i + sub_d_y) + 1
116 if tri:
117 faces.append((A, B, D))
118 faces.append((B, C, D))
119 else:
120 faces.append((A, B, C, D))
121 count = count + 1
122 else:
123 count = 0
125 return verts, faces
128 # ------------------------------------------------------------
129 # Do refresh - redraw
130 class AntLandscapeRefresh(bpy.types.Operator):
131 bl_idname = "mesh.ant_landscape_refresh"
132 bl_label = "Refresh"
133 bl_description = "Refresh landscape with current settings"
134 bl_options = {'REGISTER', 'UNDO'}
137 @classmethod
138 def poll(cls, context):
139 ob = bpy.context.active_object
140 return (ob.ant_landscape and not ob.ant_landscape['sphere_mesh'])
143 def execute(self, context):
144 # turn off undo
145 undo = bpy.context.user_preferences.edit.use_global_undo
146 bpy.context.user_preferences.edit.use_global_undo = False
148 # ant object items
149 obj = bpy.context.active_object
151 bpy.ops.object.mode_set(mode = 'EDIT')
152 bpy.ops.object.mode_set(mode = 'OBJECT')
154 if obj and obj.ant_landscape.keys():
155 ob = obj.ant_landscape
156 obi = ob.items()
157 prop = []
158 for i in range(len(obi)):
159 prop.append(obi[i][1])
161 # redraw verts
162 mesh = obj.data
164 if ob['vert_group'] != "" and ob['vert_group'] in obj.vertex_groups:
165 vertex_group = obj.vertex_groups[ob['vert_group']]
166 gi = vertex_group.index
167 for v in mesh.vertices:
168 for g in v.groups:
169 if g.group == gi:
170 v.co[2] = 0.0
171 v.co[2] = vertex_group.weight(v.index) * noise_gen(v.co, prop)
172 else:
173 for v in mesh.vertices:
174 v.co[2] = 0.0
175 v.co[2] = noise_gen(v.co, prop)
176 mesh.update()
177 else:
178 pass
180 # restore pre operator undo state
181 context.user_preferences.edit.use_global_undo = undo
183 return {'FINISHED'}
185 # ------------------------------------------------------------
186 # Do regenerate
187 class AntLandscapeRegenerate(bpy.types.Operator):
188 bl_idname = "mesh.ant_landscape_regenerate"
189 bl_label = "Regenerate"
190 bl_description = "Regenerate landscape with current settings"
191 bl_options = {'REGISTER', 'UNDO'}
194 @classmethod
195 def poll(cls, context):
196 ob = bpy.context.active_object
197 if ob.mode == 'EDIT':
198 return False
199 return ob.ant_landscape
202 def execute(self, context):
204 # turn off undo
205 undo = bpy.context.user_preferences.edit.use_global_undo
206 bpy.context.user_preferences.edit.use_global_undo = False
208 scene = bpy.context.scene
209 # ant object items
210 obj = bpy.context.active_object
212 if obj and obj.ant_landscape.keys():
213 ob = obj.ant_landscape
214 obi = ob.items()
215 ant_props = []
216 for i in range(len(obi)):
217 ant_props.append(obi[i][1])
219 new_name = obj.name
221 # Main function, create landscape mesh object
222 if ob['sphere_mesh']:
223 # sphere
224 verts, faces = sphere_gen(
225 ob['subdivision_y'],
226 ob['subdivision_x'],
227 ob['tri_face'],
228 ob['mesh_size'],
229 ant_props,
230 False,
233 new_ob = create_mesh_object(context, verts, [], faces, new_name).object
234 if ob['remove_double']:
235 new_ob.select = True
236 bpy.ops.object.mode_set(mode = 'EDIT')
237 bpy.ops.mesh.remove_doubles(threshold=0.0001, use_unselected=False)
238 bpy.ops.object.mode_set(mode = 'OBJECT')
239 else:
240 # grid
241 verts, faces = grid_gen(
242 ob['subdivision_x'],
243 ob['subdivision_y'],
244 ob['tri_face'],
245 ob['mesh_size_x'],
246 ob['mesh_size_y'],
247 ant_props,
248 False,
251 new_ob = create_mesh_object(context, verts, [], faces, new_name).object
253 new_ob.select = True
255 if ob['smooth_mesh']:
256 bpy.ops.object.shade_smooth()
258 # Landscape Material
259 if ob['land_material'] != "" and ob['land_material'] in bpy.data.materials:
260 mat = bpy.data.materials[ob['land_material']]
261 bpy.context.object.data.materials.append(mat)
263 # Water plane
264 if ob['water_plane']:
265 if ob['sphere_mesh']:
266 # sphere
267 verts, faces = sphere_gen(
268 ob['subdivision_y'],
269 ob['subdivision_x'],
270 ob['tri_face'],
271 ob['mesh_size'],
272 ant_props,
273 ob['water_plane'],
274 ob['water_level']
276 wobj = create_mesh_object(context, verts, [], faces, new_name+"_plane").object
277 if ob['remove_double']:
278 wobj.select = True
279 bpy.ops.object.mode_set(mode = 'EDIT')
280 bpy.ops.mesh.remove_doubles(threshold=0.0001, use_unselected=False)
281 bpy.ops.object.mode_set(mode = 'OBJECT')
282 else:
283 # grid
284 verts, faces = grid_gen(
287 ob['tri_face'],
288 ob['mesh_size_x'],
289 ob['mesh_size_y'],
290 ant_props,
291 ob['water_plane'],
292 ob['water_level']
294 wobj = create_mesh_object(context, verts, [], faces, new_name+"_plane").object
296 wobj.select = True
298 if ob['smooth_mesh']:
299 bpy.ops.object.shade_smooth()
301 # Water Material
302 if ob['water_material'] != "" and ob['water_material'] in bpy.data.materials:
303 mat = bpy.data.materials[ob['water_material']]
304 bpy.context.object.data.materials.append(mat)
306 # Loc Rot Scale
307 if ob['water_plane']:
308 wobj.location = obj.location
309 wobj.rotation_euler = obj.rotation_euler
310 wobj.scale = obj.scale
311 wobj.select = False
313 new_ob.location = obj.location
314 new_ob.rotation_euler = obj.rotation_euler
315 new_ob.scale = obj.scale
317 # Store props
318 new_ob = store_properties(ob, new_ob)
320 # Delete old object
321 new_ob.select = False
323 obj.select = True
324 scene.objects.active = obj
325 bpy.ops.object.delete(use_global=False)
327 # Select landscape and make active
328 new_ob.select = True
329 scene.objects.active = new_ob
331 # restore pre operator undo state
332 context.user_preferences.edit.use_global_undo = undo
334 return {'FINISHED'}
337 # ------------------------------------------------------------
338 # Z normal value to vertex group (Slope map)
339 class AntVgSlopeMap(bpy.types.Operator):
340 bl_idname = "mesh.ant_slope_map"
341 bl_label = "Weight from Slope"
342 bl_description = "A.N.T. Slope Map - z normal value to vertex group weight"
343 bl_options = {'REGISTER', 'UNDO'}
345 z_method = EnumProperty(
346 name="Method:",
347 default='SLOPE_Z',
348 items=[
349 ('SLOPE_Z', "Z Slope", "Slope for planar mesh"),
350 ('SLOPE_XYZ', "Sphere Slope", "Slope for spherical mesh")
352 group_name = StringProperty(
353 name="Vertex Group Name:",
354 default="Slope",
355 description="Name"
357 select_flat = BoolProperty(
358 name="Vert Select:",
359 default=True,
360 description="Select vertices on flat surface"
362 select_range = FloatProperty(
363 name="Vert Select Range:",
364 default=0.0,
365 min=0.0,
366 max=1.0,
367 description="Increase to select more vertices on slopes"
370 @classmethod
371 def poll(cls, context):
372 ob = context.object
373 return (ob and ob.type == 'MESH')
376 def invoke(self, context, event):
377 wm = context.window_manager
378 return wm.invoke_props_dialog(self)
381 def execute(self, context):
382 message = "Popup Values: %d, %f, %s, %s" % \
383 (self.select_flat, self.select_range, self.group_name, self.z_method)
384 self.report({'INFO'}, message)
386 bpy.ops.object.mode_set(mode='OBJECT')
387 ob = bpy.context.active_object
388 dim = ob.dimensions
390 if self.select_flat:
391 bpy.ops.object.mode_set(mode='EDIT')
392 bpy.ops.mesh.select_all(action='DESELECT')
393 bpy.context.tool_settings.mesh_select_mode = [True, False, False]
394 bpy.ops.object.mode_set(mode='OBJECT')
396 bpy.ops.object.vertex_group_add()
397 vg_normal = ob.vertex_groups.active
399 for v in ob.data.vertices:
400 if self.z_method == 'SLOPE_XYZ':
401 zval = (v.co.normalized() * v.normal.normalized()) * 2 - 1
402 else:
403 zval = v.normal[2]
405 vg_normal.add([v.index], zval, 'REPLACE')
407 if self.select_flat:
408 if zval >= (1.0 - self.select_range):
409 v.select = True
411 vg_normal.name = self.group_name
413 bpy.ops.paint.weight_paint_toggle()
414 return {'FINISHED'}
417 # ------------------------------------------------------------
418 # draw properties
420 def draw_ant_refresh(self, context):
421 layout = self.layout
422 if self.auto_refresh is False:
423 self.refresh = False
424 elif self.auto_refresh is True:
425 self.refresh = True
426 row = layout.box().row()
427 split = row.split()
428 split.scale_y = 1.5
429 split.prop(self, "auto_refresh", toggle=True, icon_only=True, icon='AUTO')
430 split.prop(self, "refresh", toggle=True, icon_only=True, icon='FILE_REFRESH')
433 def draw_ant_main(self, context, generate=True):
434 layout = self.layout
435 box = layout.box()
436 box.prop(self, "show_main_settings", toggle=True)
437 if self.show_main_settings:
438 if generate:
439 row = box.row(align=True)
440 split = row.split(align=True)
441 split.prop(self, "at_cursor", toggle=True, icon_only=True, icon='CURSOR')
442 split.prop(self, "smooth_mesh", toggle=True, icon_only=True, icon='SOLID')
443 split.prop(self, "tri_face", toggle=True, icon_only=True, icon='MESH_DATA')
445 if not self.sphere_mesh:
446 row = box.row(align=True)
447 row.prop(self, "sphere_mesh", toggle=True)
448 else:
449 row = box.row(align=True)
450 split = row.split(0.5, align=True)
451 split.prop(self, "sphere_mesh", toggle=True)
452 split.prop(self, "remove_double", toggle=True)
454 box.prop(self, "ant_terrain_name")
455 box.prop_search(self, "land_material", bpy.data, "materials")
457 col = box.column(align=True)
458 col.prop(self, "subdivision_x")
459 col.prop(self, "subdivision_y")
460 col = box.column(align=True)
461 if self.sphere_mesh:
462 col.prop(self, "mesh_size")
463 else:
464 col.prop(self, "mesh_size_x")
465 col.prop(self, "mesh_size_y")
468 def draw_ant_noise(self, context, generate=True):
469 layout = self.layout
470 box = layout.box()
471 box.prop(self, "show_noise_settings", toggle=True)
472 if self.show_noise_settings:
473 box.prop(self, "noise_type")
474 if self.noise_type == "blender_texture":
475 box.prop_search(self, "texture_block", bpy.data, "textures")
476 else:
477 box.prop(self, "basis_type")
479 col = box.column(align=True)
480 col.prop(self, "random_seed")
481 col = box.column(align=True)
482 col.prop(self, "noise_offset_x")
483 col.prop(self, "noise_offset_y")
484 if self.sphere_mesh == True or generate == False:
485 col.prop(self, "noise_offset_z")
486 col.prop(self, "noise_size_x")
487 col.prop(self, "noise_size_y")
488 if self.sphere_mesh == True or generate == False:
489 col.prop(self, "noise_size_z")
491 col = box.column(align=True)
492 col.prop(self, "noise_size")
494 col = box.column(align=True)
495 if self.noise_type == "multi_fractal":
496 col.prop(self, "noise_depth")
497 col.prop(self, "dimension")
498 col.prop(self, "lacunarity")
499 elif self.noise_type == "ridged_multi_fractal":
500 col.prop(self, "noise_depth")
501 col.prop(self, "dimension")
502 col.prop(self, "lacunarity")
503 col.prop(self, "offset")
504 col.prop(self, "gain")
505 elif self.noise_type == "hybrid_multi_fractal":
506 col.prop(self, "noise_depth")
507 col.prop(self, "dimension")
508 col.prop(self, "lacunarity")
509 col.prop(self, "offset")
510 col.prop(self, "gain")
511 elif self.noise_type == "hetero_terrain":
512 col.prop(self, "noise_depth")
513 col.prop(self, "dimension")
514 col.prop(self, "lacunarity")
515 col.prop(self, "offset")
516 elif self.noise_type == "fractal":
517 col.prop(self, "noise_depth")
518 col.prop(self, "dimension")
519 col.prop(self, "lacunarity")
520 elif self.noise_type == "turbulence_vector":
521 col.prop(self, "noise_depth")
522 col.prop(self, "amplitude")
523 col.prop(self, "frequency")
524 col.separator()
525 row = col.row(align=True)
526 row.prop(self, "hard_noise", expand=True)
527 elif self.noise_type == "variable_lacunarity":
528 box.prop(self, "vl_basis_type")
529 box.prop(self, "distortion")
530 elif self.noise_type == "marble_noise":
531 box.prop(self, "marble_shape")
532 box.prop(self, "marble_bias")
533 box.prop(self, "marble_sharp")
534 col = box.column(align=True)
535 col.prop(self, "distortion")
536 col.prop(self, "noise_depth")
537 col.separator()
538 row = col.row(align=True)
539 row.prop(self, "hard_noise", expand=True)
540 elif self.noise_type == "shattered_hterrain":
541 col.prop(self, "noise_depth")
542 col.prop(self, "dimension")
543 col.prop(self, "lacunarity")
544 col.prop(self, "offset")
545 col.prop(self, "distortion")
546 elif self.noise_type == "strata_hterrain":
547 col.prop(self, "noise_depth")
548 col.prop(self, "dimension")
549 col.prop(self, "lacunarity")
550 col.prop(self, "offset")
551 col.prop(self, "distortion", text="Strata")
552 elif self.noise_type == "ant_turbulence":
553 col.prop(self, "noise_depth")
554 col.prop(self, "amplitude")
555 col.prop(self, "frequency")
556 col.prop(self, "distortion")
557 col.separator()
558 row = col.row(align=True)
559 row.prop(self, "hard_noise", expand=True)
560 elif self.noise_type == "vl_noise_turbulence":
561 col.prop(self, "noise_depth")
562 col.prop(self, "amplitude")
563 col.prop(self, "frequency")
564 col.prop(self, "distortion")
565 col.separator()
566 box.prop(self, "vl_basis_type")
567 col.separator()
568 row = col.row(align=True)
569 row.prop(self, "hard_noise", expand=True)
570 elif self.noise_type == "vl_hTerrain":
571 col.prop(self, "noise_depth")
572 col.prop(self, "dimension")
573 col.prop(self, "lacunarity")
574 col.prop(self, "offset")
575 col.prop(self, "distortion")
576 col.separator()
577 box.prop(self, "vl_basis_type")
578 elif self.noise_type == "distorted_heteroTerrain":
579 col.prop(self, "noise_depth")
580 col.prop(self, "dimension")
581 col.prop(self, "lacunarity")
582 col.prop(self, "offset")
583 col.prop(self, "distortion")
584 col.separator()
585 box.prop(self, "vl_basis_type")
586 elif self.noise_type == "double_multiFractal":
587 col.prop(self, "noise_depth")
588 col.prop(self, "dimension")
589 col.prop(self, "lacunarity")
590 col.prop(self, "offset")
591 col.prop(self, "gain")
592 col.separator()
593 box.prop(self, "vl_basis_type")
594 elif self.noise_type == "rocks_noise":
595 col.prop(self, "noise_depth")
596 col.prop(self, "distortion")
597 col.separator()
598 row = col.row(align=True)
599 row.prop(self, "hard_noise", expand=True)
600 elif self.noise_type == "slick_rock":
601 col.prop(self, "noise_depth")
602 col.prop(self, "dimension")
603 col.prop(self, "lacunarity")
604 col.prop(self, "gain")
605 col.prop(self, "offset")
606 col.prop(self, "distortion")
607 col.separator()
608 box.prop(self, "vl_basis_type")
609 elif self.noise_type == "planet_noise":
610 col.prop(self, "noise_depth")
611 col.separator()
612 row = col.row(align=True)
613 row.prop(self, "hard_noise", expand=True)
615 # Effects mix
616 col = box.column(align=False)
617 box.prop(self, "fx_type")
618 if self.fx_type != "0":
619 if int(self.fx_type) <= 12:
620 box.prop(self, "fx_bias")
622 box.prop(self, "fx_mix_mode")
623 col = box.column(align=True)
624 col.prop(self, "fx_mixfactor")
626 col = box.column(align=True)
627 col.prop(self, "fx_loc_x")
628 col.prop(self, "fx_loc_y")
629 col.prop(self, "fx_size")
631 col = box.column(align=True)
632 col.prop(self, "fx_depth")
633 if self.fx_depth != 0:
634 col.prop(self, "fx_frequency")
635 col.prop(self, "fx_amplitude")
636 col.prop(self, "fx_turb")
638 col = box.column(align=True)
639 row = col.row(align=True).split(0.92, align=True)
640 row.prop(self, "fx_height")
641 row.prop(self, "fx_invert", toggle=True, text="", icon='ARROW_LEFTRIGHT')
642 col.prop(self, "fx_offset")
645 def draw_ant_displace(self, context, generate=True):
646 layout = self.layout
647 box = layout.box()
648 box.prop(self, "show_displace_settings", toggle=True)
649 if self.show_displace_settings:
650 if not generate:
651 col = box.column(align=False)
652 col.prop(self, "direction", toggle=True)
654 col = box.column(align=True)
655 row = col.row(align=True).split(0.92, align=True)
656 row.prop(self, "height")
657 row.prop(self, "height_invert", toggle=True, text="", icon='ARROW_LEFTRIGHT')
658 col.prop(self, "height_offset")
659 col.prop(self, "maximum")
660 col.prop(self, "minimum")
661 if generate:
662 if not self.sphere_mesh:
663 col = box.column()
664 col.prop(self, "edge_falloff")
665 if self.edge_falloff is not "0":
666 col = box.column(align=True)
667 col.prop(self, "edge_level")
668 if self.edge_falloff in ["2", "3"]:
669 col.prop(self, "falloff_x")
670 if self.edge_falloff in ["1", "3"]:
671 col.prop(self, "falloff_y")
673 col = box.column()
674 col.prop(self, "strata_type")
675 if self.strata_type is not "0":
676 col = box.column()
677 col.prop(self, "strata")
679 if not generate:
680 col = box.column(align=False)
681 col.prop_search(self, "vert_group", bpy.context.object, "vertex_groups")
684 def draw_ant_water(self, context):
685 layout = self.layout
686 box = layout.box()
687 col = box.column()
688 col.prop(self, "water_plane", toggle=True)
689 if self.water_plane:
690 col = box.column(align=True)
691 col.prop_search(self, "water_material", bpy.data, "materials")
692 col = box.column()
693 col.prop(self, "water_level")
696 # Store propereties
697 def store_properties(operator, ob):
698 ob.ant_landscape.ant_terrain_name = operator.ant_terrain_name
699 ob.ant_landscape.at_cursor = operator.at_cursor
700 ob.ant_landscape.smooth_mesh = operator.smooth_mesh
701 ob.ant_landscape.tri_face = operator.tri_face
702 ob.ant_landscape.sphere_mesh = operator.sphere_mesh
703 ob.ant_landscape.land_material = operator.land_material
704 ob.ant_landscape.water_material = operator.water_material
705 ob.ant_landscape.texture_block = operator.texture_block
706 ob.ant_landscape.subdivision_x = operator.subdivision_x
707 ob.ant_landscape.subdivision_y = operator.subdivision_y
708 ob.ant_landscape.mesh_size_x = operator.mesh_size_x
709 ob.ant_landscape.mesh_size_y = operator.mesh_size_y
710 ob.ant_landscape.mesh_size = operator.mesh_size
711 ob.ant_landscape.random_seed = operator.random_seed
712 ob.ant_landscape.noise_offset_x = operator.noise_offset_x
713 ob.ant_landscape.noise_offset_y = operator.noise_offset_y
714 ob.ant_landscape.noise_offset_z = operator.noise_offset_z
715 ob.ant_landscape.noise_size_x = operator.noise_size_x
716 ob.ant_landscape.noise_size_y = operator.noise_size_y
717 ob.ant_landscape.noise_size_z = operator.noise_size_z
718 ob.ant_landscape.noise_size = operator.noise_size
719 ob.ant_landscape.noise_type = operator.noise_type
720 ob.ant_landscape.basis_type = operator.basis_type
721 ob.ant_landscape.vl_basis_type = operator.vl_basis_type
722 ob.ant_landscape.distortion = operator.distortion
723 ob.ant_landscape.hard_noise = operator.hard_noise
724 ob.ant_landscape.noise_depth = operator.noise_depth
725 ob.ant_landscape.amplitude = operator.amplitude
726 ob.ant_landscape.frequency = operator.frequency
727 ob.ant_landscape.dimension = operator.dimension
728 ob.ant_landscape.lacunarity = operator.lacunarity
729 ob.ant_landscape.offset = operator.offset
730 ob.ant_landscape.gain = operator.gain
731 ob.ant_landscape.marble_bias = operator.marble_bias
732 ob.ant_landscape.marble_sharp = operator.marble_sharp
733 ob.ant_landscape.marble_shape = operator.marble_shape
734 ob.ant_landscape.height = operator.height
735 ob.ant_landscape.height_invert = operator.height_invert
736 ob.ant_landscape.height_offset = operator.height_offset
737 ob.ant_landscape.maximum = operator.maximum
738 ob.ant_landscape.minimum = operator.minimum
739 ob.ant_landscape.edge_falloff = operator.edge_falloff
740 ob.ant_landscape.edge_level = operator.edge_level
741 ob.ant_landscape.falloff_x = operator.falloff_x
742 ob.ant_landscape.falloff_y = operator.falloff_y
743 ob.ant_landscape.strata_type = operator.strata_type
744 ob.ant_landscape.strata = operator.strata
745 ob.ant_landscape.water_plane = operator.water_plane
746 ob.ant_landscape.water_level = operator.water_level
747 ob.ant_landscape.vert_group = operator.vert_group
748 ob.ant_landscape.remove_double = operator.remove_double
749 ob.ant_landscape.fx_mixfactor = operator.fx_mixfactor
750 ob.ant_landscape.fx_mix_mode = operator.fx_mix_mode
751 ob.ant_landscape.fx_type = operator.fx_type
752 ob.ant_landscape.fx_bias = operator.fx_bias
753 ob.ant_landscape.fx_turb = operator.fx_turb
754 ob.ant_landscape.fx_depth = operator.fx_depth
755 ob.ant_landscape.fx_frequency = operator.fx_frequency
756 ob.ant_landscape.fx_amplitude = operator.fx_amplitude
757 ob.ant_landscape.fx_size = operator.fx_size
758 ob.ant_landscape.fx_loc_x = operator.fx_loc_x
759 ob.ant_landscape.fx_loc_y = operator.fx_loc_y
760 ob.ant_landscape.fx_height = operator.fx_height
761 ob.ant_landscape.fx_offset = operator.fx_offset
762 ob.ant_landscape.fx_invert = operator.fx_invert
763 return ob
766 # ------------------------------------------------------------
767 # "name": "ErosionR"
768 # "author": "Michel Anders, Ian Huish"
770 from random import random as rand
771 from math import tan, radians
772 from .eroder import Grid
773 from .stats import Stats
774 from .utils import numexpr_available
777 def availableVertexGroupsOrNone(self, context):
778 groups = [ ('None', 'None', 'None', 1) ]
779 return groups + [(name, name, name, n+1) for n,name in enumerate(context.active_object.vertex_groups.keys())]
782 class Eroder(bpy.types.Operator):
783 bl_idname = "mesh.eroder"
784 bl_label = "ErosionR"
785 bl_description = "Apply various kinds of erosion to a square ANT-Landscape grid. Also available in Weight Paint mode > Weights menu"
786 bl_options = {'REGISTER', 'UNDO', 'PRESET'}
788 Iterations = IntProperty(
789 name="Iterations",
790 description="Number of overall iterations",
791 default=1,
792 min=1,
793 soft_max=100
795 IterRiver = IntProperty(
796 name="River Iterations",
797 description="Number of river iterations",
798 default=30,
799 min=1,
800 soft_max=1000
802 IterAva = IntProperty(
803 name="Avalanche Iterations",
804 description="Number of avalanche iterations",
805 default=5,
806 min=1,
807 soft_max=10
809 IterDiffuse = IntProperty(
810 name="Diffuse Iterations",
811 description="Number of diffuse iterations",
812 default=5,
813 min=1,
814 soft_max=10
816 Ef = FloatProperty(
817 name="Rain on Plains",
818 description="1 gives equal rain across the terrain, 0 rains more at the mountain tops",
819 default=0.0,
820 min=0,
821 max=1
823 Kd = FloatProperty(
824 name="Kd",
825 description="Thermal diffusion rate (1.0 is a fairly high rate)",
826 default=0.1,
827 min=0,
828 soft_max=100
830 Kt = FloatProperty(
831 name="Kt",
832 description="Maximum stable talus angle",
833 default=radians(60),
834 min=0,
835 max=radians(90),
836 subtype='ANGLE'
838 Kr = FloatProperty(
839 name="Rain amount",
840 description="Total Rain amount",
841 default=.01,
842 min=0,
843 soft_max=1,
844 precision=3
846 Kv = FloatProperty(
847 name="Rain variance",
848 description="Rain variance (0 is constant, 1 is uniform)",
849 default=0,
850 min=0,
851 max=1
853 userainmap = BoolProperty(
854 name="Use rain map",
855 description="Use active vertex group as a rain map",
856 default=True
858 Ks = FloatProperty(
859 name="Soil solubility",
860 description="Soil solubility - how quickly water quickly reaches saturation point",
861 default=0.5,
862 min=0,
863 soft_max=1
865 Kdep = FloatProperty(
866 name="Deposition rate",
867 description="Sediment deposition rate - how quickly silt is laid down once water stops flowing quickly",
868 default=0.1,
869 min=0,
870 soft_max=1
872 Kz = FloatProperty(name="Fluvial Erosion Rate",
873 description="Amount of sediment moved each main iteration - if 0, then rivers are formed but the mesh is not changed",
874 default=0.3,
875 min=0,
876 soft_max=20
878 Kc = FloatProperty(
879 name="Carrying capacity",
880 description="Base sediment carrying capacity",
881 default=0.9,
882 min=0,
883 soft_max=1
885 Ka = FloatProperty(
886 name="Slope dependence",
887 description="Slope dependence of carrying capacity (not used)",
888 default=1.0,
889 min=0,
890 soft_max=2
892 Kev = FloatProperty(
893 name="Evaporation",
894 description="Evaporation Rate per grid square in % - causes sediment to be dropped closer to the hills",
895 default=.5,
896 min=0,
897 soft_max=2
899 numexpr = BoolProperty(
900 name="Numexpr",
901 description="Use numexpr module (if available)",
902 default=True
904 Pd = FloatProperty(
905 name="Diffusion Amount",
906 description="Diffusion probability",
907 default=0.2,
908 min=0,
909 max=1
911 Pa = FloatProperty(
912 name="Avalanche Amount",
913 description="Avalanche amount",
914 default=0.5,
915 min=0,
916 max=1
918 Pw = FloatProperty(
919 name="River Amount",
920 description="Water erosion probability",
921 default=1,
922 min=0,
923 max=1
925 smooth = BoolProperty(
926 name="Smooth",
927 description="Set smooth shading",
928 default=True
930 showiterstats = BoolProperty(
931 name="Iteration Stats",
932 description="Show iteraration statistics",
933 default=False
935 showmeshstats = BoolProperty(name="Mesh Stats",
936 description="Show mesh statistics",
937 default=False
940 stats = Stats()
941 counts= {}
943 def execute(self, context):
945 ob = context.active_object
946 me = ob.data
947 self.stats.reset()
948 try:
949 vgActive = ob.vertex_groups.active.name
950 except:
951 vgActive = "capacity"
952 print("ActiveGroup", vgActive)
953 try:
954 vg=ob.vertex_groups["rainmap"]
955 except:
956 vg=ob.vertex_groups.new("rainmap")
957 try:
958 vgscree=ob.vertex_groups["scree"]
959 except:
960 vgscree=ob.vertex_groups.new("scree")
961 try:
962 vgavalanced=ob.vertex_groups["avalanced"]
963 except:
964 vgavalanced=ob.vertex_groups.new("avalanced")
965 try:
966 vgw=ob.vertex_groups["water"]
967 except:
968 vgw=ob.vertex_groups.new("water")
969 try:
970 vgscour=ob.vertex_groups["scour"]
971 except:
972 vgscour=ob.vertex_groups.new("scour")
973 try:
974 vgdeposit=ob.vertex_groups["deposit"]
975 except:
976 vgdeposit=ob.vertex_groups.new("deposit")
977 try:
978 vgflowrate=ob.vertex_groups["flowrate"]
979 except:
980 vgflowrate=ob.vertex_groups.new("flowrate")
981 try:
982 vgsediment=ob.vertex_groups["sediment"]
983 except:
984 vgsediment=ob.vertex_groups.new("sediment")
985 try:
986 vgsedimentpct=ob.vertex_groups["sedimentpct"]
987 except:
988 vgsedimentpct=ob.vertex_groups.new("sedimentpct")
989 try:
990 vgcapacity=ob.vertex_groups["capacity"]
991 except:
992 vgcapacity=ob.vertex_groups.new("capacity")
994 g = Grid.fromBlenderMesh(me, vg, self.Ef)
996 me = bpy.data.meshes.new(me.name)
998 self.counts['diffuse'] = 0
999 self.counts['avalanche'] = 0
1000 self.counts['water'] = 0
1001 for i in range(self.Iterations):
1002 if self.IterRiver > 0:
1003 for i in range(self.IterRiver):
1004 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)
1006 if self.Kd > 0.0:
1007 for k in range(self.IterDiffuse):
1008 g.diffuse(self.Kd / 5, self.IterDiffuse, self.numexpr)
1009 self.counts['diffuse']+=1
1011 if self.Kt < radians(90) and self.Pa > 0:
1012 for k in range(self.IterAva):
1013 # since dx and dy are scaled to 1, tan(Kt) is the height for a given angle
1014 g.avalanche(tan(self.Kt), self.IterAva, self.Pa, self.numexpr)
1015 self.counts['avalanche']+=1
1016 if self.Kz > 0:
1017 g.fluvial_erosion(self.Kr, self.Kv, self.userainmap, self.Kc, self.Ks, self.Kz*50, self.Ka, 0,0,0,0, self.numexpr)
1018 self.counts['water']+=1
1020 g.toBlenderMesh(me)
1021 ob.data = me
1023 if vg:
1024 for row in range(g.rainmap.shape[0]):
1025 for col in range(g.rainmap.shape[1]):
1026 i = row * g.rainmap.shape[1] + col
1027 vg.add([i],g.rainmap[row,col],'ADD')
1028 if vgscree:
1029 for row in range(g.rainmap.shape[0]):
1030 for col in range(g.rainmap.shape[1]):
1031 i = row * g.rainmap.shape[1] + col
1032 vgscree.add([i],g.avalanced[row,col],'ADD')
1033 if vgavalanced:
1034 for row in range(g.rainmap.shape[0]):
1035 for col in range(g.rainmap.shape[1]):
1036 i = row * g.rainmap.shape[1] + col
1037 vgavalanced.add([i],-g.avalanced[row,col],'ADD')
1038 if vgw:
1039 for row in range(g.rainmap.shape[0]):
1040 for col in range(g.rainmap.shape[1]):
1041 i = row * g.rainmap.shape[1] + col
1042 vgw.add([i],g.water[row,col]/g.watermax,'ADD')
1043 if vgscour:
1044 for row in range(g.rainmap.shape[0]):
1045 for col in range(g.rainmap.shape[1]):
1046 i = row * g.rainmap.shape[1] + col
1047 vgscour.add([i],g.scour[row,col]/max(g.scourmax, -g.scourmin),'ADD')
1048 if vgdeposit:
1049 for row in range(g.rainmap.shape[0]):
1050 for col in range(g.rainmap.shape[1]):
1051 i = row * g.rainmap.shape[1] + col
1052 vgdeposit.add([i],g.scour[row,col]/min(-g.scourmax, g.scourmin),'ADD')
1053 if vgflowrate:
1054 for row in range(g.rainmap.shape[0]):
1055 for col in range(g.rainmap.shape[1]):
1056 i = row * g.rainmap.shape[1] + col
1057 vgflowrate.add([i],g.flowrate[row,col],'ADD')
1058 if vgsediment:
1059 for row in range(g.rainmap.shape[0]):
1060 for col in range(g.rainmap.shape[1]):
1061 i = row * g.rainmap.shape[1] + col
1062 vgsediment.add([i],g.sediment[row,col],'ADD')
1063 if vgsedimentpct:
1064 for row in range(g.rainmap.shape[0]):
1065 for col in range(g.rainmap.shape[1]):
1066 i = row * g.rainmap.shape[1] + col
1067 vgsedimentpct.add([i],g.sedimentpct[row,col],'ADD')
1068 if vgcapacity:
1069 for row in range(g.rainmap.shape[0]):
1070 for col in range(g.rainmap.shape[1]):
1071 i = row * g.rainmap.shape[1] + col
1072 vgcapacity.add([i],g.capacity[row,col],'ADD')
1073 try:
1074 vg = ob.vertex_groups["vgActive"]
1075 except:
1076 vg = vgcapacity
1077 ob.vertex_groups.active = vg
1079 if self.smooth:
1080 bpy.ops.object.shade_smooth()
1081 self.stats.time()
1082 self.stats.memory()
1083 if self.showmeshstats:
1084 self.stats.meshstats = g.analyze()
1086 return {'FINISHED'}
1089 def draw(self,context):
1090 layout = self.layout
1092 layout.operator('screen.repeat_last', text="Repeat", icon='FILE_REFRESH' )
1094 layout.prop(self, 'Iterations')
1096 box = layout.box()
1097 col = box.column(align=True)
1098 col.label("Thermal (Diffusion)")
1099 col.prop(self, 'Kd')
1100 col.prop(self, 'IterDiffuse')
1102 box = layout.box()
1103 col = box.column(align=True)
1104 col.label("Avalanche (Talus)")
1105 col.prop(self, 'Pa')
1106 col.prop(self, 'IterAva')
1107 col.prop(self, 'Kt')
1109 box = layout.box()
1110 col = box.column(align=True)
1111 col.label("River erosion")
1112 col.prop(self, 'IterRiver')
1113 col.prop(self, 'Kz')
1114 col.prop(self, 'Ks')
1115 col.prop(self, 'Kc')
1116 col.prop(self, 'Kdep')
1117 col.prop(self, 'Kr')
1118 col.prop(self, 'Kv')
1119 col.prop(self, 'Kev')
1121 col.prop(self, 'Ef')
1123 layout.prop(self,'smooth')