Newer
Older
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
"version": (0, 1, 4),
Jonathan Smith
committed
"location": "View3D > Add > Mesh",
"description": "Add a landscape primitive",
"warning": "", # used for warning icon and text in addons panel
CoDEmanX
committed
"wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
"Scripts/Add_Mesh/ANT_Landscape",
"tracker_url": "https://developer.blender.org/maniphest/task/create/?project=3&type=Bug",
"category": "Add Mesh",
}
Another Noise Tool: Landscape mesh generator
MESH OPTIONS:
Mesh update: Turn this on for interactive mesh update.
Sphere: Generate sphere or a grid mesh.
Smooth: Generate smooth shaded mesh.
Subdivision: Number of mesh subdivisions, higher numbers gives more detail but also slows down the script.
Mesh size: X,Y size of the grid mesh in blender units.
X_Offset: Noise x offset in blender units (make tiled terrain)
Y_Offset: Noise y offset in blender units
NOISE OPTIONS: ( Most of these options are the same as in blender textures. )
Random seed: Use this to randomise the origin of the noise function.
Noise size: Size of the noise.
Noise type: Available noise types: multiFractal, ridgedMFractal, fBm, hybridMFractal, heteroTerrain, Turbulence, Distorted Noise, Marble, Shattered_hTerrain, Strata_hTerrain, Planet_noise
Noise basis: Blender, Perlin, NewPerlin, Voronoi_F1, Voronoi_F2, Voronoi_F3, Voronoi_F4, Voronoi_F2-F1, Voronoi Crackle, Cellnoise
VLNoise basis: Blender, Perlin, NewPerlin, Voronoi_F1, Voronoi_F2, Voronoi_F3, Voronoi_F4, Voronoi_F2-F1, Voronoi Crackle, Cellnoise
Distortion: Distortion amount.
Hard: Hard/Soft turbulence noise.
Depth: Noise depth, number of frequencies in the fBm.
Dimension: Musgrave: Fractal dimension of the roughest areas.
Lacunarity: Musgrave: Gap between successive frequencies.
Offset: Musgrave: Raises the terrain from sea level.
Gain: Musgrave: Scale factor.
Marble Bias: Sin, Tri, Saw
Marble Sharpnes: Soft, Sharp, Sharper
Marble Shape: Shape of the marble function: Default, Ring, Swirl, X, Y
HEIGHT OPTIONS:
Invert: Invert terrain height.
Height: Scale terrain height.
Offset: Terrain height offset.
Falloff: Terrain height falloff: Type 1, Type 2, X, Y
Sealevel: Flattens terrain below sealevel.
Platlevel: Flattens terrain above plateau level.
Strata: Strata amount, number of strata/terrace layers.
Strata type: Strata types, Smooth, Sharp-sub, Sharp-add
# import modules
import bpy
from bpy.props import *
from mathutils import *
Brecht Van Lommel
committed
from mathutils.noise import *
# 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).
Campbell Barton
committed
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, edges, faces)
# Update mesh geometry after adding stuff.
mesh.update()
Campbell Barton
committed
from bpy_extras import object_utils
return object_utils.object_data_add(context, mesh, operator=None)
# ------------------------------------------------------------
def sin_bias(a):
return 0.5 + 0.5 * sin(a)
a = 1 - 2 * abs(floor((a * (1 / b)) + 0.5) - (a * (1 / b)))
n = int(a / b)
a -= n * b
if a < 0: a += b
return a / b
def sharper(a):
return sharp(sharp(a))
def shapes(x, y, shape=0):
if shape == 1:
# ring
x = x*2
y = y*2
s = (-cos(x**2 + y**2) / (x**2 + y**2 + 0.5))
x = x * 2
y = y * 2
s = ((x * sin(x * x + y * y) + y * cos(x * x + y * y)) / (x**2 + y**2 + 0.5))
x = x * 2
y = y * 2
s = ((cos(x * pi) + cos(y * pi)) - 0.5)
s = (y * pi)
s = (x * pi)
# marble default
s = ((x + y) * 5)
def marble_noise(x, y, z, origin, size, shape, bias, sharpnes, turb, depth, hard, basis):
s = shapes(x, y, shape)
value = s + turb * turbulence_vector((x, y, z), depth, hard, basis)[0]
# ------------------------------------------------------------
def shattered_hterrain(x, y, z, H, lacunarity, octaves, offset, distort, basis):
d = (turbulence_vector((x, y, z), 6, 0, 0)[0] * 0.5 + 0.5) * distort * 0.5
t1 = (turbulence_vector((x + d, y + d, z), 0, 0, 7)[0] + 0.5)
t2 = (hetero_terrain((x * 2, y * 2, z * 2), H, lacunarity, octaves, offset, basis) * 0.5)
return ((t1 * t2) + t2 * 0.5) * 0.5
def strata_hterrain(x, y, z, H, lacunarity, octaves, offset, distort, basis):
value = hetero_terrain((x, y, z), H, lacunarity, octaves, offset, basis) * 0.5
steps = (sin(value * (distort * 5) * pi) * (0.1 / (distort * 5) * pi))
return (value * (1.0 - 0.5) + steps * 0.5)
# planet_noise by Farsthary: https://farsthary.com/2010/11/24/new-planet-procedural-texture/
def planet_noise(coords, oct=6, hard=0, noisebasis=1, nabla=0.001):
x,y,z = coords
d = 0.001
offset = nabla * 1000
x = turbulence((x, y, z), oct, hard, noisebasis)
y = turbulence((x + offset, y , z), oct, hard, noisebasis)
z = turbulence((x, y + offset, z), oct, hard, noisebasis)
xdy = x-turbulence((x, y + d, z), oct, hard, noisebasis)
xdz = x-turbulence((x, y, z + d), oct, hard, noisebasis)
ydx = y-turbulence((x + d, y, z), oct, hard, noisebasis)
ydz = y-turbulence((x, y, z + d), oct, hard, noisebasis)
zdx = z-turbulence((x + d, y, z), oct, hard, noisebasis)
zdy = z-turbulence((x, y + d, z), oct, hard, noisebasis)
return (zdy - ydz), (zdx - xdz), (ydx - xdy)
# ------------------------------------------------------------
def landscape_gen(x, y, z, falloffsize, options):
# options = [0, 1.0, 'multi_fractal', 0, 0, 1.0, 0, 6, 1.0, 2.0, 1.0, 2.0, 0, 0, 0, 1.0, 0.0, 1, 0.0, 1.0, 0, 0, 0, 0.0, 0.0]
rseed = options[0]
nsize = options[1]
ntype = options[2]
nbasis = int(options[3][0])
vlbasis = int(options[4][0])
hardnoise = options[6]
depth = options[7]
dimension = options[8]
offset = options[10]
gain = options[11]
marblebias = int(options[12][0])
marbleshape = int(options[14][0])
invert = options[15]
height = options[16]
falloff = int(options[18][0])
sealevel = options[19]
platlevel = options[20]
strata = options[21]
stratatype = options[22]
sphere = options[23]
x_offset = options[24]
y_offset = options[25]
origin = 0.0 + x_offset, 0.0 + y_offset, 0.0
origin_x = x_offset
origin_y = y_offset
origin[0] += x_offset
origin[1] += y_offset
origin_x = ((0.5 - origin[0]) * 1000.0) + x_offset
origin_y = ((0.5 - origin[1]) * 1000.0) + y_offset
origin_z = (0.5 - origin[2]) * 1000.0
ncoords = (x / nsize + origin_x, y / nsize + origin_y, z / nsize + origin_z)
if nbasis == 9:
nbasis = 14 # to get cellnoise basis you must set 14 instead of 9
if vlbasis ==9:
vlbasis = 14
if ntype == 'multi_fractal':
value = multi_fractal(ncoords, dimension, lacunarity, depth, nbasis) * 0.5
elif ntype == 'ridged_multi_fractal':
value = ridged_multi_fractal(ncoords, dimension, lacunarity, depth, offset, gain, nbasis) * 0.5
elif ntype == 'hybrid_multi_fractal':
value = hybrid_multi_fractal(ncoords, dimension, lacunarity, depth, offset, gain, nbasis) * 0.5
elif ntype == 'hetero_terrain':
value = hetero_terrain(ncoords, dimension, lacunarity, depth, offset, nbasis) * 0.25
elif ntype == 'fractal':
value = fractal(ncoords, dimension, lacunarity, depth, nbasis)
elif ntype == 'turbulence_vector':
value = turbulence_vector(ncoords, depth, hardnoise, nbasis)[0]
elif ntype == 'variable_lacunarity':
value = variable_lacunarity(ncoords, distortion, nbasis, vlbasis) + 0.5
elif ntype == 'marble_noise':
value = marble_noise(x * 2.0 / falloffsize, y * 2.0 / falloffsize, z * 2 / falloffsize, origin, nsize, marbleshape, marblebias, marblesharpnes, distortion, depth, hardnoise, nbasis)
elif ntype == 'shattered_hterrain':
value = shattered_hterrain(ncoords[0], ncoords[1], ncoords[2], dimension, lacunarity, depth, offset, distortion, nbasis)
elif ntype == 'strata_hterrain':
value = strata_hterrain(ncoords[0], ncoords[1], ncoords[2], dimension, lacunarity, depth, offset, distortion, nbasis)
elif ntype == 'planet_noise':
value = planet_noise(ncoords, depth, hardnoise, nbasis)[2] * 0.5 + 0.5
else:
value = 0.0
# adjust height
if invert !=0:
value = (1 - value) * height + heightoffset
else:
value = value * height + heightoffset
# edge falloff
if sphere == 0: # no edge falloff if spherical
if falloff != 0:
fallofftypes = [0, hypot(x * x, y * y), hypot(x, y), abs(y), abs(x)]
dist = fallofftypes[falloff]
radius = (falloffsize / 2)**2
radius = falloffsize / 2
dist = (dist * dist * (3 - 2 * dist))
value = (value - value * dist) + sealevel
else:
value = sealevel
# strata / terrace / layered
if stratatype !='0':
strata = strata / height
steps = (sin(value * strata * pi) * (0.1 / strata * pi))
value = (value * (1.0 - 0.5) + steps * 0.5) * 2.0
steps = -abs(sin(value * strata * pi) * (0.1 / strata * pi))
value =(value * (1.0 - 0.5) + steps * 0.5) * 2.0
steps = abs(sin(value * strata * pi) * (0.1 / strata * pi))
value =(value * (1.0 - 0.5) + steps * 0.5) * 2.0
if (value < sealevel):
value = sealevel
if (value > platlevel):
value = platlevel
# ------------------------------------------------------------
def grid_gen(sub_d, size_me, options):
# mesh arrays
# fill verts array
for i in range (0, sub_d):
for j in range(0, sub_d):
u = (i / (sub_d - 1) - 1 / 2)
v = (j / (sub_d - 1) - 1 / 2)
x = size_me * u
y = size_me * v
z = landscape_gen(x, y, 0.0, size_me, options)
vert = (x, y, z)
verts.append(vert)
# fill faces array
count = 0
for i in range (0, sub_d*(sub_d - 1)):
if count < sub_d - 1:
A = i + 1
B = i
C = (i + sub_d)
D = (i + sub_d) + 1
face = (A, B, C, D)
faces.append(face)
count = count + 1
else:
count = 0
def sphere_gen(sub_d, size_me, options):
# mesh arrays
# fill verts array
for i in range (0, sub_d):
for j in range(0, sub_d):
u = sin(j * pi * 2 / (sub_d - 1)) * cos(-pi / 2 + i * pi / (sub_d - 1)) * size_me / 2
v = cos(j * pi * 2 / (sub_d - 1)) * cos(-pi / 2 + i * pi / (sub_d - 1)) * size_me / 2
w = sin(-pi / 2 + i * pi / (sub_d - 1)) * size_me / 2
h = landscape_gen(u, v, w, size_me, options) / size_me
u,v,w = u + u * h, v + v * h, w + w * h
vert = (u, v, w)
verts.append(vert)
# fill faces array
count = 0
for i in range (0, sub_d * (sub_d - 1)):
if count < sub_d - 1:
A = i + 1
B = i
C = (i + sub_d)
D = (i + sub_d) + 1
face = (A, B, C, D)
faces.append(face)
count = count + 1
else:
count = 0
# ------------------------------------------------------------
# Add landscape
class landscape_add(bpy.types.Operator):
"""Add a landscape mesh"""
bl_idname = "mesh.landscape_add"
bl_description = "Add landscape mesh"
# properties
default = True,
description = "Update mesh")
default = False,
description = "Generate Sphere mesh")
default = True,
description = "Shade smooth")
Subdivision = IntProperty(name="Subdivisions",
min = 4,
max = 6400,
default = 128,
description = "Mesh x y subdivisions")
MeshSize = FloatProperty(name="Mesh Size",
min = 0.01,
max = 100000.0,
default = 2.0,
description = "Mesh size")
XOffset = FloatProperty(name="X Offset",
default = 0.0,
description = "X Offset")
YOffset = FloatProperty(name="Y Offset",
default = 0.0,
description = "Y Offset")
min = 0,
max = 9999,
default = 0,
description = "Randomize noise origin")
NoiseSize = FloatProperty(name="Noise Size",
min = 0.01,
max = 10000.0,
default = 1.0,
description = "Noise size")
('multi_fractal', "multiFractal", "multiFractal"),
('ridged_multi_fractal', "ridgedMFractal", "ridgedMFractal"),
('hybrid_multi_fractal', "hybridMFractal", "hybridMFractal"),
('hetero_terrain', "heteroTerrain", "heteroTerrain"),
('fractal', "fBm", "fBm"),
('turbulence_vector', "Turbulence", "Turbulence"),
('variable_lacunarity', "Distorted Noise", "Distorted Noise"),
('marble_noise', "Marble", "Marble"),
('shattered_hterrain', "Shattered_hTerrain", "Shattered_hTerrain"),
('strata_hterrain', "Strata_hTerrain", "Strata_hTerrain"),
('planet_noise', "Planet_Noise", "Planet_Noise")]
CoDEmanX
committed
description = "Noise type",
items = NoiseTypes)
("0", "Blender", "Blender"),
("1", "Perlin", "Perlin"),
("2", "NewPerlin", "NewPerlin"),
("3", "Voronoi_F1", "Voronoi_F1"),
("4", "Voronoi_F2", "Voronoi_F2"),
("5", "Voronoi_F3", "Voronoi_F3"),
("6", "Voronoi_F4", "Voronoi_F4"),
("7", "Voronoi_F2-F1", "Voronoi_F2-F1"),
("8", "Voronoi Crackle", "Voronoi Crackle"),
("9", "Cellnoise", "Cellnoise")]
description = "Noise basis",
items = BasisTypes)
("0", "Blender", "Blender"),
("1", "Perlin", "Perlin"),
("2", "NewPerlin", "NewPerlin"),
("3", "Voronoi_F1", "Voronoi_F1"),
("4", "Voronoi_F2", "Voronoi_F2"),
("5", "Voronoi_F3", "Voronoi_F3"),
("6", "Voronoi_F4", "Voronoi_F4"),
("7", "Voronoi_F2-F1", "Voronoi_F2-F1"),
("8", "Voronoi Crackle", "Voronoi Crackle"),
("9", "Cellnoise", "Cellnoise")]
description = "VLNoise basis",
items = VLBasisTypes)
Distortion = FloatProperty(name="Distortion",
min = 0.01,
max = 1000.0,
default = 1.0,
description = "Distortion amount")
default = False,
description = "Hard noise")
min = 1,
max = 16,
default = 8,
description="Noise Depth - number of frequencies in the fBm")
mDimension = FloatProperty(name="Dimension",
min = 0.01,
max = 2.0,
default = 1.0,
description = "H - fractal dimension of the roughest areas")
mLacunarity = FloatProperty(name="Lacunarity",
min = 0.01,
max = 6.0,
default = 2.0,
description = "Lacunarity - gap between successive frequencies")
min = 0.01,
max = 6.0,
default = 1.0,
description = "Offset - raises the terrain from sea level")
min = 0.01,
max = 6.0,
default = 1.0,
description = "Gain - scale factor")
("0", "Sin", "Sin"),
("1", "Tri", "Tri"),
("2", "Saw", "Saw")]
description = "Marble bias",
items = BiasTypes)
("0", "Soft", "Soft"),
("1", "Sharp", "Sharp"),
("2", "Sharper", "Sharper")]
description = "Marble sharp",
items = SharpTypes)
("0", "Default", "Default"),
("1", "Ring", "Ring"),
("2", "Swirl", "Swirl"),
("3", "Bump", "Bump"),
("4", "Y", "Y"),
("5", "X", "X")]
description = "Marble shape",
items = ShapeTypes)
default = False,
description = "Invert noise input")
min = 0.01,
max = 10000.0,
default = 0.5,
description = "Height scale")
min = -10000.0,
max = 10000.0,
default = 0.0,
description = "Height offset")
("0", "None", "None"),
("1", "Type 1", "Type 1"),
("2", "Type 2", "Type 2"),
("3", "Y", "Y"),
("4", "X", "X")]
description = "Edge falloff",
default = "1",
items = fallTypes)
Sealevel = FloatProperty(name="Sealevel",
min = -10000.0,
max = 10000.0,
default = 0.0,
description = "Sealevel")
Plateaulevel = FloatProperty(name="Plateau",
min = -10000.0,
max = 10000.0,
default = 1.0,
description = "Plateau level")
min = 0.01,
max = 1000.0,
default = 5.0,
description = "Strata amount")
("0", "None", "None"),
("1", "Type 1", "Type 1"),
("2", "Type 2", "Type 2"),
("3", "Type 3", "Type 3")]
description = "Strata type",
default = "0",
items = StrataTypes)
# ------------------------------------------------------------
# Draw
def draw(self, context):
layout = self.layout
box = layout.box()
Campbell Barton
committed
box.prop(self, 'AutoUpdate')
box.prop(self, 'SphereMesh')
box.prop(self, 'SmoothMesh')
box.prop(self, 'Subdivision')
box.prop(self, 'MeshSize')
Campbell Barton
committed
box.prop(self, 'NoiseType')
Campbell Barton
committed
box.prop(self, 'RandomSeed')
box.prop(self, 'XOffset')
box.prop(self, 'YOffset')
Campbell Barton
committed
box.prop(self, 'NoiseSize')
if self.NoiseType == 'multi_fractal':
Campbell Barton
committed
box.prop(self, 'NoiseDepth')
box.prop(self, 'mDimension')
box.prop(self, 'mLacunarity')
elif self.NoiseType == 'ridged_multi_fractal':
Campbell Barton
committed
box.prop(self, 'NoiseDepth')
box.prop(self, 'mDimension')
box.prop(self, 'mLacunarity')
box.prop(self, 'mOffset')
box.prop(self, 'mGain')
elif self.NoiseType == 'hybrid_multi_fractal':
Campbell Barton
committed
box.prop(self, 'NoiseDepth')
box.prop(self, 'mDimension')
box.prop(self, 'mLacunarity')
box.prop(self, 'mOffset')
box.prop(self, 'mGain')
elif self.NoiseType == 'hetero_terrain':
Campbell Barton
committed
box.prop(self, 'NoiseDepth')
box.prop(self, 'mDimension')
box.prop(self, 'mLacunarity')
box.prop(self, 'mOffset')
elif self.NoiseType == 'fractal':
Campbell Barton
committed
box.prop(self, 'NoiseDepth')
box.prop(self, 'mDimension')
box.prop(self, 'mLacunarity')
elif self.NoiseType == 'turbulence_vector':
Campbell Barton
committed
box.prop(self, 'NoiseDepth')
box.prop(self, 'HardNoise')
elif self.NoiseType == 'variable_lacunarity':
Campbell Barton
committed
box.prop(self, 'VLBasisType')
box.prop(self, 'Distortion')
elif self.NoiseType == 'marble_noise':
Campbell Barton
committed
box.prop(self, 'MarbleShape')
box.prop(self, 'MarbleBias')
box.prop(self, 'MarbleSharp')
box.prop(self, 'Distortion')
box.prop(self, 'NoiseDepth')
box.prop(self, 'HardNoise')
elif self.NoiseType == 'shattered_hterrain':
Campbell Barton
committed
box.prop(self, 'NoiseDepth')
box.prop(self, 'mDimension')
box.prop(self, 'mLacunarity')
box.prop(self, 'mOffset')
box.prop(self, 'Distortion')
elif self.NoiseType == 'strata_hterrain':
Campbell Barton
committed
box.prop(self, 'NoiseDepth')
box.prop(self, 'mDimension')
box.prop(self, 'mLacunarity')
box.prop(self, 'mOffset')
box.prop(self, 'Distortion')
elif self.NoiseType == 'planet_noise':
box.prop(self, 'NoiseDepth')
box.prop(self, 'HardNoise')
Campbell Barton
committed
box.prop(self, 'Invert')
box.prop(self, 'Height')
box.prop(self, 'Offset')
box.prop(self, 'Plateaulevel')
box.prop(self, 'Sealevel')
Thomas Dinges
committed
if self.SphereMesh == False:
Campbell Barton
committed
box.prop(self, 'Falloff')
box.prop(self, 'StrataType')
Thomas Dinges
committed
if self.StrataType != '0':
Campbell Barton
committed
box.prop(self, 'Strata')
# ------------------------------------------------------------
# Execute
def execute(self, context):
Thomas Dinges
committed
if self.AutoUpdate != 0:
# turn off undo
undo = bpy.context.user_preferences.edit.use_global_undo
bpy.context.user_preferences.edit.use_global_undo = False
# deselect all objects when in object mode
if bpy.ops.object.select_all.poll():
bpy.ops.object.select_all(action='DESELECT')
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
self.RandomSeed,
self.NoiseSize,
self.NoiseType,
self.BasisType,
self.VLBasisType,
self.Distortion,
self.HardNoise,
self.NoiseDepth,
self.mDimension,
self.mLacunarity,
self.mOffset,
self.mGain,
self.MarbleBias,
self.MarbleSharp,
self.MarbleShape,
self.Invert,
self.Height,
self.Offset,
self.Falloff,
self.Sealevel,
self.Plateaulevel,
self.Strata,
self.StrataType,
self.SphereMesh,
self.XOffset,
self.YOffset
Thomas Dinges
committed
if self.SphereMesh !=0:
verts, faces = sphere_gen(self.Subdivision, self.MeshSize, options)
verts, faces = grid_gen(self.Subdivision, self.MeshSize, options)
Campbell Barton
committed
obj = create_mesh_object(context, verts, [], faces, "Landscape")
Thomas Dinges
committed
if self.SphereMesh !=0:
bpy.ops.mesh.remove_doubles(threshold=0.0001)
bpy.ops.object.mode_set(mode='OBJECT')
# Shade smooth
if self.SmoothMesh !=0:
if bpy.ops.object.shade_smooth.poll():
bpy.ops.object.shade_smooth()
else: # edit mode
bpy.ops.mesh.faces_shade_smooth()
# restore pre operator undo state
bpy.context.user_preferences.edit.use_global_undo = undo
return {'FINISHED'}
else:
return {'PASS_THROUGH'}
# ------------------------------------------------------------
def menu_func_landscape(self, context):
self.layout.operator(landscape_add.bl_idname, text="Landscape", icon="RNDCURVE")
Campbell Barton
committed
bpy.utils.register_module(__name__)
bpy.types.INFO_MT_mesh_add.append(menu_func_landscape)
Campbell Barton
committed
bpy.utils.unregister_module(__name__)
bpy.types.INFO_MT_mesh_add.remove(menu_func_landscape)