Newer
Older
# SPDX-License-Identifier: GPL-2.0-or-later
# Another Noise Tool - Functions
# import modules
import bpy
from bpy.props import (
BoolProperty,
FloatProperty,
StringProperty,
EnumProperty,
IntProperty,
)
from math import (
sin, cos, pi,
)
# ------------------------------------------------------------
# Create a new mesh (object) from verts/edges/faces.
# verts/edges/faces ... List of vertices/edges/faces for the
# new mesh (as used in from_pydata).
# name ... Name of the new mesh (& object)
from bpy_extras import object_utils
def create_mesh_object(context, verts, edges, faces, name):
# Create new mesh
mesh = bpy.data.meshes.new(name)
# Make a mesh from a list of verts/edges/faces.
mesh.from_pydata(verts, [], faces)
# Update mesh geometry after adding stuff.
mesh.update()
return object_utils.object_data_add(context, mesh, operator=None)
# Generate XY Grid
def grid_gen(sub_d_x, sub_d_y, tri, meshsize_x, meshsize_y, props, water_plane, water_level):
verts = []
faces = []
vappend = verts.append
fappend = faces.append
for i in range (0, sub_d_x):
x = meshsize_x * (i / (sub_d_x - 1) - 1 / 2)
for j in range(0, sub_d_y):
y = meshsize_y * (j / (sub_d_y - 1) - 1 / 2)
z = noise_gen((x, y, 0), props)
else:
z = water_level
vappend((x,y,z))
if i > 0 and j > 0:
A = i * sub_d_y + (j - 1)
B = i * sub_d_y + j
C = (i - 1) * sub_d_y + j
D = (i - 1) * sub_d_y + (j - 1)
if not tri:
fappend((A, B, C, D))
else:
fappend((A, B, D))
fappend((B, C, D))
return verts, faces
# Generate UV Sphere
def sphere_gen(sub_d_x, sub_d_y, tri, meshsize, props, water_plane, water_level):
verts = []
faces = []
vappend = verts.append
fappend = faces.append
sub_d_x += 1
sub_d_y += 1
for i in range(0, sub_d_x):
for j in range(0, sub_d_y):
u = sin(j * pi * 2 / (sub_d_y - 1)) * cos(-pi / 2 + i * pi / (sub_d_x - 1)) * meshsize / 2
v = cos(j * pi * 2 / (sub_d_y - 1)) * cos(-pi / 2 + i * pi / (sub_d_x - 1)) * meshsize / 2
w = sin(-pi / 2 + i * pi / (sub_d_x - 1)) * meshsize / 2
if water_plane:
h = water_level
else:
h = noise_gen((u, v, w), props) / meshsize
vappend(((u + u * h), (v + v * h), (w + w * h)))
count = 0
for i in range (0, sub_d_y * (sub_d_x - 1)):
if count < sub_d_y - 1 :
A = i + 1
B = i
C = (i + sub_d_y)
D = (i + sub_d_y) + 1
if tri:
fappend((A, B, D))
fappend((B, C, D))
count = count + 1
else:
count = 0
return verts, faces
# ------------------------------------------------------------
# Do refresh - redraw
class AntLandscapeRefresh(bpy.types.Operator):
bl_idname = "mesh.ant_landscape_refresh"
bl_label = "Refresh"
bl_description = "Refresh landscape with current settings"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
ob = bpy.context.active_object
return (ob.ant_landscape and not ob.ant_landscape.sphere_mesh)
def execute(self, context):
# ant object items
obj = bpy.context.active_object
bpy.ops.object.mode_set(mode = 'EDIT')
bpy.ops.object.mode_set(mode = 'OBJECT')
keys = obj.ant_landscape.keys()
if keys:
for key in keys:
prop.append(getattr(ob, key))
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# redraw verts
mesh = obj.data
if ob['vert_group'] != "" and ob['vert_group'] in obj.vertex_groups:
vertex_group = obj.vertex_groups[ob['vert_group']]
gi = vertex_group.index
for v in mesh.vertices:
for g in v.groups:
if g.group == gi:
v.co[2] = 0.0
v.co[2] = vertex_group.weight(v.index) * noise_gen(v.co, prop)
else:
for v in mesh.vertices:
v.co[2] = 0.0
v.co[2] = noise_gen(v.co, prop)
mesh.update()
else:
pass
return {'FINISHED'}
# ------------------------------------------------------------
# Do regenerate
class AntLandscapeRegenerate(bpy.types.Operator):
bl_idname = "mesh.ant_landscape_regenerate"
bl_label = "Regenerate"
bl_description = "Regenerate landscape with current settings"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
ob = bpy.context.active_object
if ob.mode == 'EDIT':
return False
return ob.ant_landscape
# ant object items
obj = bpy.context.active_object
keys = obj.ant_landscape.keys()
if keys:
ob = obj.ant_landscape
ant_props = []
for key in keys:
ant_props.append(getattr(ob, key))
new_name = obj.name
# Main function, create landscape mesh object
if ob['sphere_mesh']:
# sphere
verts, faces = sphere_gen(
ob['subdivision_y'],
ob['subdivision_x'],
ob['tri_face'],
ob['mesh_size'],
ant_props,
False,
0.0
)
new_ob = create_mesh_object(context, verts, [], faces, new_name)
bpy.ops.object.mode_set(mode = 'EDIT')
bpy.ops.mesh.remove_doubles(threshold=0.0001, use_unselected=False)
bpy.ops.object.mode_set(mode = 'OBJECT')
else:
# grid
verts, faces = grid_gen(
ob['subdivision_x'],
ob['subdivision_y'],
ob['tri_face'],
ob['mesh_size_x'],
ob['mesh_size_y'],
ant_props,
False,
0.0
)
new_ob = create_mesh_object(context, verts, [], faces, new_name)
if ob['smooth_mesh']:
bpy.ops.object.shade_smooth()
# Landscape Material
if ob['land_material'] != "" and ob['land_material'] in bpy.data.materials:
mat = bpy.data.materials[ob['land_material']]
bpy.context.object.data.materials.append(mat)
# Water plane
if ob['water_plane']:
if ob['sphere_mesh']:
# sphere
verts, faces = sphere_gen(
ob['subdivision_y'],
ob['subdivision_x'],
ob['tri_face'],
ob['mesh_size'],
ant_props,
ob['water_plane'],
ob['water_level']
)
wobj = create_mesh_object(context, verts, [], faces, new_name+"_plane")
bpy.ops.object.mode_set(mode = 'EDIT')
bpy.ops.mesh.remove_doubles(threshold=0.0001, use_unselected=False)
bpy.ops.object.mode_set(mode = 'OBJECT')
else:
# grid
verts, faces = grid_gen(
2,
2,
ob['tri_face'],
ob['mesh_size_x'],
ob['mesh_size_y'],
ant_props,
ob['water_plane'],
ob['water_level']
)
wobj = create_mesh_object(context, verts, [], faces, new_name+"_plane")
if ob['smooth_mesh']:
bpy.ops.object.shade_smooth()
# Water Material
if ob['water_material'] != "" and ob['water_material'] in bpy.data.materials:
mat = bpy.data.materials[ob['water_material']]
bpy.context.object.data.materials.append(mat)
# Loc Rot Scale
if ob['water_plane']:
wobj.location = obj.location
wobj.rotation_euler = obj.rotation_euler
wobj.scale = obj.scale
new_ob.location = obj.location
new_ob.rotation_euler = obj.rotation_euler
new_ob.scale = obj.scale
# Store props
new_ob = store_properties(ob, new_ob)
# Delete old object
bpy.ops.object.delete(use_global=False)
# Select landscape and make active
return {'FINISHED'}
# ------------------------------------------------------------
# Z normal value to vertex group (Slope map)
class AntVgSlopeMap(bpy.types.Operator):
bl_idname = "mesh.ant_slope_map"
bl_label = "Weight from Slope"
bl_description = "A.N.T. Slope Map - z normal value to vertex group weight"
bl_options = {'REGISTER', 'UNDO'}
name="Method:",
default='SLOPE_Z',
items=[
('SLOPE_Z', "Z Slope", "Slope for planar mesh"),
('SLOPE_XYZ', "Sphere Slope", "Slope for spherical mesh")
name="Vertex Group Name:",
default="Slope",
description="Name"
)
name="Vert Select:",
default=True,
description="Select vertices on flat surface"
)
select_range: FloatProperty(
name="Vert Select Range:",
default=0.0,
min=0.0,
max=1.0,
description="Increase to select more vertices on slopes"
@classmethod
def poll(cls, context):
ob = context.object
return (ob and ob.type == 'MESH')
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self)
def execute(self, context):
message = "Popup Values: %d, %f, %s, %s" % \
(self.select_flat, self.select_range, self.group_name, self.z_method)
self.report({'INFO'}, message)
bpy.ops.object.mode_set(mode='OBJECT')
ob = bpy.context.active_object
dim = ob.dimensions
if self.select_flat:
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='DESELECT')
bpy.context.tool_settings.mesh_select_mode = [True, False, False]
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.vertex_group_add()
vg_normal = ob.vertex_groups.active
for v in ob.data.vertices:
if self.z_method == 'SLOPE_XYZ':
zval = (v.co.normalized() * v.normal.normalized()) * 2 - 1
else:
zval = v.normal[2]
vg_normal.add([v.index], zval, 'REPLACE')
if self.select_flat:
if zval >= (1.0 - self.select_range):
vg_normal.name = self.group_name
bpy.ops.paint.weight_paint_toggle()
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
return {'FINISHED'}
# ------------------------------------------------------------
# draw properties
def draw_ant_refresh(self, context):
layout = self.layout
if self.auto_refresh is False:
self.refresh = False
elif self.auto_refresh is True:
self.refresh = True
row = layout.box().row()
split = row.split()
split.scale_y = 1.5
split.prop(self, "auto_refresh", toggle=True, icon_only=True, icon='AUTO')
split.prop(self, "refresh", toggle=True, icon_only=True, icon='FILE_REFRESH')
def draw_ant_main(self, context, generate=True):
layout = self.layout
box = layout.box()
box.prop(self, "show_main_settings", toggle=True)
if self.show_main_settings:
if generate:
row = box.row(align=True)
split = row.split(align=True)
split.prop(self, "at_cursor", toggle=True, icon_only=True, icon='PIVOT_CURSOR')
split.prop(self, "smooth_mesh", toggle=True, icon_only=True, icon='SHADING_SOLID')
split.prop(self, "tri_face", toggle=True, text="Triangulate", icon='MESH_DATA')
if not self.sphere_mesh:
row = box.row(align=True)
row.prop(self, "sphere_mesh", toggle=True)
else:
row = box.row(align=True)
split = row.split(factor=0.5, align=True)
split.prop(self, "sphere_mesh", toggle=True)
split.prop(self, "remove_double", toggle=True)
box.prop(self, "ant_terrain_name")
box.prop_search(self, "land_material", bpy.data, "materials")
col = box.column(align=True)
col.prop(self, "subdivision_x")
col.prop(self, "subdivision_y")
col = box.column(align=True)
if self.sphere_mesh:
col.prop(self, "mesh_size")
else:
col.prop(self, "mesh_size_x")
col.prop(self, "mesh_size_y")
def draw_ant_noise(self, context, generate=True):
layout = self.layout
box = layout.box()
box.prop(self, "show_noise_settings", toggle=True)
if self.show_noise_settings:
box.prop(self, "noise_type")
if self.noise_type == "blender_texture":
box.prop_search(self, "texture_block", bpy.data, "textures")
else:
box.prop(self, "basis_type")
col = box.column(align=True)
col.prop(self, "random_seed")
col = box.column(align=True)
col.prop(self, "noise_offset_x")
col.prop(self, "noise_offset_y")
if self.sphere_mesh == True or generate == False:
col.prop(self, "noise_offset_z")
col.prop(self, "noise_size_x")
col.prop(self, "noise_size_y")
if self.sphere_mesh == True or generate == False:
col.prop(self, "noise_size_z")
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
col = box.column(align=True)
col.prop(self, "noise_size")
col = box.column(align=True)
if self.noise_type == "multi_fractal":
col.prop(self, "noise_depth")
col.prop(self, "dimension")
col.prop(self, "lacunarity")
elif self.noise_type == "ridged_multi_fractal":
col.prop(self, "noise_depth")
col.prop(self, "dimension")
col.prop(self, "lacunarity")
col.prop(self, "offset")
col.prop(self, "gain")
elif self.noise_type == "hybrid_multi_fractal":
col.prop(self, "noise_depth")
col.prop(self, "dimension")
col.prop(self, "lacunarity")
col.prop(self, "offset")
col.prop(self, "gain")
elif self.noise_type == "hetero_terrain":
col.prop(self, "noise_depth")
col.prop(self, "dimension")
col.prop(self, "lacunarity")
col.prop(self, "offset")
elif self.noise_type == "fractal":
col.prop(self, "noise_depth")
col.prop(self, "dimension")
col.prop(self, "lacunarity")
elif self.noise_type == "turbulence_vector":
col.prop(self, "noise_depth")
col.prop(self, "amplitude")
col.prop(self, "frequency")
col.separator()
row = col.row(align=True)
row.prop(self, "hard_noise", expand=True)
elif self.noise_type == "variable_lacunarity":
box.prop(self, "vl_basis_type")
box.prop(self, "distortion")
elif self.noise_type == "marble_noise":
box.prop(self, "marble_shape")
box.prop(self, "marble_bias")
box.prop(self, "marble_sharp")
col = box.column(align=True)
col.prop(self, "distortion")
col.prop(self, "noise_depth")
col.separator()
row = col.row(align=True)
row.prop(self, "hard_noise", expand=True)
elif self.noise_type == "shattered_hterrain":
col.prop(self, "noise_depth")
col.prop(self, "dimension")
col.prop(self, "lacunarity")
col.prop(self, "offset")
col.prop(self, "distortion")
elif self.noise_type == "strata_hterrain":
col.prop(self, "noise_depth")
col.prop(self, "dimension")
col.prop(self, "lacunarity")
col.prop(self, "offset")
col.prop(self, "distortion", text="Strata")
elif self.noise_type == "ant_turbulence":
col.prop(self, "noise_depth")
col.prop(self, "amplitude")
col.prop(self, "frequency")
col.prop(self, "distortion")
col.separator()
row = col.row(align=True)
row.prop(self, "hard_noise", expand=True)
elif self.noise_type == "vl_noise_turbulence":
col.prop(self, "noise_depth")
col.prop(self, "amplitude")
col.prop(self, "frequency")
col.prop(self, "distortion")
col.separator()
col.separator()
row = col.row(align=True)
row.prop(self, "hard_noise", expand=True)
elif self.noise_type == "vl_hTerrain":
col.prop(self, "noise_depth")
col.prop(self, "dimension")
col.prop(self, "lacunarity")
col.prop(self, "offset")
col.prop(self, "distortion")
col.separator()
elif self.noise_type == "distorted_heteroTerrain":
col.prop(self, "noise_depth")
col.prop(self, "dimension")
col.prop(self, "lacunarity")
col.prop(self, "offset")
col.prop(self, "distortion")
col.separator()
elif self.noise_type == "double_multiFractal":
col.prop(self, "noise_depth")
col.prop(self, "dimension")
col.prop(self, "lacunarity")
col.prop(self, "offset")
col.prop(self, "gain")
col.separator()
elif self.noise_type == "rocks_noise":
col.prop(self, "noise_depth")
col.prop(self, "distortion")
col.separator()
row = col.row(align=True)
row.prop(self, "hard_noise", expand=True)
elif self.noise_type == "slick_rock":
col.prop(self, "noise_depth")
col.prop(self, "dimension")
col.prop(self, "lacunarity")
col.prop(self, "gain")
col.prop(self, "offset")
col.prop(self, "distortion")
col.separator()
elif self.noise_type == "planet_noise":
col.prop(self, "noise_depth")
col.separator()
row = col.row(align=True)
row.prop(self, "hard_noise", expand=True)
# Effects mix
col = box.column(align=False)
box.prop(self, "fx_type")
if self.fx_type != "0":
if int(self.fx_type) <= 12:
box.prop(self, "fx_bias")
box.prop(self, "fx_mix_mode")
col = box.column(align=True)
col.prop(self, "fx_mixfactor")
col = box.column(align=True)
col.prop(self, "fx_loc_x")
col.prop(self, "fx_loc_y")
col.prop(self, "fx_size")
col = box.column(align=True)
col.prop(self, "fx_depth")
if self.fx_depth != 0:
col.prop(self, "fx_frequency")
col.prop(self, "fx_amplitude")
col.prop(self, "fx_turb")
col = box.column(align=True)
row = col.row(align=True).split(factor=0.92, align=True)
row.prop(self, "fx_height")
row.prop(self, "fx_invert", toggle=True, text="", icon='ARROW_LEFTRIGHT')
col.prop(self, "fx_offset")
def draw_ant_displace(self, context, generate=True):
layout = self.layout
box = layout.box()
box.prop(self, "show_displace_settings", toggle=True)
if self.show_displace_settings:
col.prop(self, "direction", toggle=True)
col = box.column(align=True)
row = col.row(align=True).split(factor=0.92, align=True)
row.prop(self, "height")
row.prop(self, "height_invert", toggle=True, text="", icon='ARROW_LEFTRIGHT')
col.prop(self, "height_offset")
col.prop(self, "maximum")
col.prop(self, "minimum")
if generate:
if not self.sphere_mesh:
col = box.column()
col.prop(self, "edge_falloff")
col = box.column(align=True)
col.prop(self, "edge_level")
if self.edge_falloff in ["2", "3"]:
col.prop(self, "falloff_x")
if self.edge_falloff in ["1", "3"]:
col.prop(self, "falloff_y")
col = box.column()
col.prop(self, "strata_type")
col = box.column()
col.prop(self, "strata")
if not generate:
col = box.column(align=False)
col.prop_search(self, "vert_group", bpy.context.object, "vertex_groups")
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
def draw_ant_water(self, context):
layout = self.layout
box = layout.box()
col = box.column()
col.prop(self, "water_plane", toggle=True)
if self.water_plane:
col = box.column(align=True)
col.prop_search(self, "water_material", bpy.data, "materials")
col = box.column()
col.prop(self, "water_level")
# Store propereties
def store_properties(operator, ob):
ob.ant_landscape.ant_terrain_name = operator.ant_terrain_name
ob.ant_landscape.at_cursor = operator.at_cursor
ob.ant_landscape.smooth_mesh = operator.smooth_mesh
ob.ant_landscape.tri_face = operator.tri_face
ob.ant_landscape.sphere_mesh = operator.sphere_mesh
ob.ant_landscape.land_material = operator.land_material
ob.ant_landscape.water_material = operator.water_material
ob.ant_landscape.texture_block = operator.texture_block
ob.ant_landscape.subdivision_x = operator.subdivision_x
ob.ant_landscape.subdivision_y = operator.subdivision_y
ob.ant_landscape.mesh_size_x = operator.mesh_size_x
ob.ant_landscape.mesh_size_y = operator.mesh_size_y
ob.ant_landscape.mesh_size = operator.mesh_size
ob.ant_landscape.random_seed = operator.random_seed
ob.ant_landscape.noise_offset_x = operator.noise_offset_x
ob.ant_landscape.noise_offset_y = operator.noise_offset_y
ob.ant_landscape.noise_offset_z = operator.noise_offset_z
ob.ant_landscape.noise_size_x = operator.noise_size_x
ob.ant_landscape.noise_size_y = operator.noise_size_y
ob.ant_landscape.noise_size_z = operator.noise_size_z
ob.ant_landscape.noise_size = operator.noise_size
ob.ant_landscape.noise_type = operator.noise_type
ob.ant_landscape.basis_type = operator.basis_type
ob.ant_landscape.vl_basis_type = operator.vl_basis_type
ob.ant_landscape.distortion = operator.distortion
ob.ant_landscape.hard_noise = operator.hard_noise
ob.ant_landscape.noise_depth = operator.noise_depth
ob.ant_landscape.amplitude = operator.amplitude
ob.ant_landscape.frequency = operator.frequency
ob.ant_landscape.dimension = operator.dimension
ob.ant_landscape.lacunarity = operator.lacunarity
ob.ant_landscape.offset = operator.offset
ob.ant_landscape.gain = operator.gain
ob.ant_landscape.marble_bias = operator.marble_bias
ob.ant_landscape.marble_sharp = operator.marble_sharp
ob.ant_landscape.marble_shape = operator.marble_shape
ob.ant_landscape.height = operator.height
ob.ant_landscape.height_invert = operator.height_invert
ob.ant_landscape.height_offset = operator.height_offset
ob.ant_landscape.maximum = operator.maximum
ob.ant_landscape.minimum = operator.minimum
ob.ant_landscape.edge_falloff = operator.edge_falloff
ob.ant_landscape.edge_level = operator.edge_level
ob.ant_landscape.falloff_x = operator.falloff_x
ob.ant_landscape.falloff_y = operator.falloff_y
ob.ant_landscape.strata_type = operator.strata_type
ob.ant_landscape.strata = operator.strata
ob.ant_landscape.water_plane = operator.water_plane
ob.ant_landscape.water_level = operator.water_level
ob.ant_landscape.vert_group = operator.vert_group
ob.ant_landscape.remove_double = operator.remove_double
ob.ant_landscape.fx_mixfactor = operator.fx_mixfactor
ob.ant_landscape.fx_mix_mode = operator.fx_mix_mode
ob.ant_landscape.fx_type = operator.fx_type
ob.ant_landscape.fx_bias = operator.fx_bias
ob.ant_landscape.fx_turb = operator.fx_turb
ob.ant_landscape.fx_depth = operator.fx_depth
ob.ant_landscape.fx_frequency = operator.fx_frequency
ob.ant_landscape.fx_amplitude = operator.fx_amplitude
ob.ant_landscape.fx_size = operator.fx_size
ob.ant_landscape.fx_loc_x = operator.fx_loc_x
ob.ant_landscape.fx_loc_y = operator.fx_loc_y
ob.ant_landscape.fx_height = operator.fx_height
ob.ant_landscape.fx_offset = operator.fx_offset
ob.ant_landscape.fx_invert = operator.fx_invert
# ------------------------------------------------------------
# "name": "ErosionR"
# "author": "Michel Anders, Ian Huish"
from random import random as rand
from math import tan, radians
from .eroder import Grid
from .stats import Stats
from .utils import numexpr_available
def availableVertexGroupsOrNone(self, context):
groups = [ ('None', 'None', 'None', 1) ]
return groups + [(name, name, name, n+1) for n,name in enumerate(context.active_object.vertex_groups.keys())]
class Eroder(bpy.types.Operator):
bl_idname = "mesh.eroder"
bl_label = "ErosionR"
bl_description = "Apply various kinds of erosion to a square ANT-Landscape grid. Also available in Weight Paint mode > Weights menu"
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
name="Iterations",
description="Number of overall iterations",
default=1,
name="River Iterations",
description="Number of river iterations",
default=30,
name="Avalanche Iterations",
description="Number of avalanche iterations",
default=5,
name="Diffuse Iterations",
description="Number of diffuse iterations",
default=5,
name="Rain on Plains",
description="1 gives equal rain across the terrain, 0 rains more at the mountain tops",
default=0.0,
min=0,
max=1
)
name="Kd",
description="Thermal diffusion rate (1.0 is a fairly high rate)",
default=0.1,
min=0,
soft_max=100
)
name="Kt",
description="Maximum stable talus angle",
default=radians(60),
min=0,
max=radians(90),
subtype='ANGLE'
)
name="Rain amount",
description="Total Rain amount",
default=.01,
min=0,
name="Rain variance",
description="Rain variance (0 is constant, 1 is uniform)",
default=0,
min=0,
max=1
)
name="Use rain map",
description="Use active vertex group as a rain map",
default=True
)
name="Soil solubility",
description="Soil solubility - how quickly water quickly reaches saturation point",
default=0.5,
min=0,
soft_max=1
)
name="Deposition rate",
description="Sediment deposition rate - how quickly silt is laid down once water stops flowing quickly",
default=0.1,
min=0,
soft_max=1
)
Kz: FloatProperty(name="Fluvial Erosion Rate",
description="Amount of sediment moved each main iteration - if 0, then rivers are formed but the mesh is not changed",
default=0.3,
min=0,
soft_max=20
)
name="Carrying capacity",
description="Base sediment carrying capacity",
default=0.9,
min=0,
soft_max=1
)
name="Slope dependence",
description="Slope dependence of carrying capacity (not used)",
default=1.0,
min=0,
soft_max=2
)
name="Evaporation",
description="Evaporation Rate per grid square in % - causes sediment to be dropped closer to the hills",
default=.5,
min=0,
soft_max=2
)
name="Numexpr",
description="Use numexpr module (if available)",
default=True
)
name="Diffusion Amount",
description="Diffusion probability",
default=0.2,
min=0,
max=1
)
name="Avalanche Amount",
description="Avalanche amount",
default=0.5,
min=0,
max=1
)
name="River Amount",
description="Water erosion probability",
default=1,
min=0,
max=1
)
name="Smooth",
description="Set smooth shading",
default=True
)
showiterstats: BoolProperty(
name="Iteration Stats",
description="Show iteraration statistics",
default=False
)
showmeshstats: BoolProperty(name="Mesh Stats",
description="Show mesh statistics",
default=False
)
stats = Stats()
counts= {}
maps = {
'rainmap': lambda g, r, c: g.rainmap[r, c],
'scree': lambda g, r, c: g.avalanced[r, c],
'avalanced': lambda g, r, c: -g.avalanced[r, c],
'water': lambda g, r, c: g.water[r, c] / g.watermax,
'scour': lambda g, r, c: g.scour[r, c] / max(g.scourmax, -g.scourmin),
'deposit': lambda g, r, c: g.scour[r, c] / min(-g.scourmax, g.scourmin),
'flowrate': lambda g, r, c: g.flowrate[r, c],
'sediment': lambda g, r, c: g.sediment[r, c],
'sedimentpct': lambda g, r, c: g.sedimentpct[r, c],
'capacity': lambda g, r, c: g.capacity[r, c]
}
oldMesh = ob.data
index_to_name = {}
for name in self.maps:
try:
ob.vertex_groups[name]
except:
ob.vertex_groups.new(name=name)
# Save a mapping from index to name, in case,
# the next iteration is different.
index_to_name[ob.vertex_groups[name].index] = name
g = Grid.fromBlenderMesh(oldMesh, ob.vertex_groups['rainmap'], self.Ef)
self.counts['diffuse'] = 0
self.counts['avalanche'] = 0
self.counts['water'] = 0
for i in range(self.Iterations):
if self.IterRiver > 0:
for i in range(self.IterRiver):
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)
if self.Kd > 0.0:
for k in range(self.IterDiffuse):
g.diffuse(self.Kd / 5, self.IterDiffuse, self.numexpr)
self.counts['diffuse']+=1
if self.Kt < radians(90) and self.Pa > 0:
for k in range(self.IterAva):
# since dx and dy are scaled to 1, tan(Kt) is the height for a given angle
g.avalanche(tan(self.Kt), self.IterAva, self.Pa, self.numexpr)
self.counts['avalanche']+=1
if self.Kz > 0:
g.fluvial_erosion(self.Kr, self.Kv, self.userainmap, self.Kc, self.Ks, self.Kz*50, self.Ka, 0,0,0,0, self.numexpr)
self.counts['water']+=1
newMesh = bpy.data.meshes.new(oldMesh.name)
g.toBlenderMesh(newMesh)
# This empties ob.vertex_groups.
ob.data = newMesh
# Copy vertex groups from the old mesh.
for name in self.maps:
ob.vertex_groups.new(name=name)
for vert in oldMesh.vertices:
for group in vert.groups:
name = index_to_name[group.group]
if name:
ob.vertex_groups[name].add([vert.index], group.weight, 'REPLACE')
# Add the new data.
for row in range(g.rainmap.shape[0]):
for col in range(g.rainmap.shape[1]):
i = row * g.rainmap.shape[1] + col
for name, fn in self.maps.items():
ob.vertex_groups[name].add([i], fn(g, row, col), 'ADD')
ob.vertex_groups.active = ob.vertex_groups['capacity']
if self.smooth:
bpy.ops.object.shade_smooth()
self.stats.time()
self.stats.memory()
if self.showmeshstats:
self.stats.meshstats = g.analyze()
return {'FINISHED'}
def draw(self,context):
layout = self.layout