# Blender rock creation tool # # Based on BlenderGuru's asteroid tutorial and personal experimentation. # Tutorial: http://www.blenderguru.com/how-to-make-a-realistic-asteroid/ # Update with another tutorial shared by "rusted" of BlenderArtists: # Tutorial: http://saschahenrichs.blogspot.com/2010/03/3dsmax-environment-modeling-1.html # # Uses the NumPy Gaussian random number generator to generate a # a rock within a given range and give some randomness to the displacement # texture values. NumPy's gaussian generator was chosen as, based on # profiling I performed, it runs in about half the time as the built in # Python gaussian equivalent. I would like to shift the script to use the # NumPy beta distribution as it ran in about half the time as the NumPy # gaussian once the skew calculations are added. # # Set lower and upper bounds to the same for no randomness. # # Tasks: # Generate meshes with random scaling between given values. # - Allow for a skewed distribution # *** Completed on 4/17/2011 *** # - Create a set of meshes that can be used # Give the user the ability to set the subsurf level (detail level) # *** Completed on 4/29/2011 *** # - Set subsurf modifiers to default at view:3, render:3. # *** Completed on 4/17/2011 *** # - Set crease values to allow for hard edges on first subsurf. # *** Completed on 4/29/2011 *** # Be able to generate and add a texture to the displacement modifiers. # *** Completed 5/17/2011 *** # - Generate three displacement modifiers. # - The first only uses a Musgrave for initial intentations. # *** Now generating four displacement modifiers *** # *** Completed on 5/17/2011 *** # - Set a randomness for the type and values of the displacement texture. # *** Completed 5/9/2011 *** # - Allow the user to set a value for the range of displacement. # -> Modification: have user set "roughness" and "roughness range". # *** Compleded on 4/23/2011 *** # Set material settings and assign material textures # *** Completed 6/9/2011 *** # - Mossiness of the rocks. # *** Completed 6/9/2011 *** # - Color of the rocks. # *** Completed 5/16/2011 *** # - Wetness/shinyness of the rock. # *** Completed 5/6/2011 *** # - For all the user provides a mean value for a skewed distribution. # *** Removed to lessen usage complexity *** # Add some presets (mesh) to make it easier to use # - Examples: river rock, asteroid, quaried rock, etc # *** Completed 7/12/2011 *** # # Code Optimization: # Remove all "bpy.ops" operations with "bpy.data" base operations. # Remove material/texture cataloging with building a list of # returned values from bpy.data.*.new() operations. # *** Completed on 9/6/2011 *** # Search for places where list comprehensions can be used. # Look for alternate methods # - Possible alternate and more efficient data structures # - Possible alternate algorithms may realize greater performance # - Look again at multi-processing. Without bpy.ops is might # be viable. # # Future tasks: # Multi-thread the script # *** Will not be implemented. Multi-processing is adding to much # overhead to realize a performance increase *** # - Learn basic multi-threading in Python (multiprocessing) # - Break material generation into separate threads (processes) # - Break mesh generation into separate threads (processes) # - Move name generation, texture ID generation, etc to process first # - Roll version to 2.0 on completion # # Paul "BrikBot" Marshall # Created: April 17, 2011 # Last Modified: November 17, 2011 # Homepage (blog): http://post.darkarsenic.com/ # //blog.darkarsenic.com/ # Thanks to Meta-Androco, RickyBlender, Ace Dragon, and PKHG for ideas # and testing. # # Coded in IDLE, tested in Blender 2.59. NumPy Recommended. # Search for "@todo" to quickly find sections that need work. # # Remeber - # Functional code comes before fast code. Once it works, then worry about # making it faster/more efficient. # # ##### BEGIN GPL LICENSE BLOCK ##### # # The Blender Rock Creation tool is for rapid generation of mesh rocks. # Copyright (C) 2011 Paul Marshall # # 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 3 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, see . # # ##### END GPL LICENSE BLOCK ##### # import bpy import math import time from add_mesh_rocks import (settings, utils) from bpy_extras import object_utils from mathutils import (Color, Vector) from bpy.props import (BoolProperty, IntProperty, FloatProperty, FloatVectorProperty, EnumProperty) # This try block allows for the script to psudo-intelligently select the # appropriate random to use. If Numpy's random is present it will use that. # If Numpy's random is not present, it will through a "module not found" # exception and instead use the slower built-in random that Python has. try: from numpy.random import random_integers as randint from numpy.random import normal as gauss from numpy.random import (beta, uniform, seed, weibull) print("Rock Generator: Numpy found.") numpy = True except: from random import (randint, gauss, uniform, seed) from random import betavariate as beta from random import weibullvariate as weibull print("Rock Generator: Numpy not found. Using Python's random.") numpy = False # Global variables: lastRock = 0 # Creates a new mesh: # # param: verts - Vector of vertices for the mesh. # edges - Edges for the mesh. Can be "[]". # faces - Face tuples corresponding to vertices. # name - Name of the mesh. def createMeshObject(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) # Set mesh to use auto smoothing: mesh.use_auto_smooth = True # Update mesh geometry after adding stuff. mesh.update() return object_utils.object_data_add(context, mesh, operator=None) # Set the values for a texture from parameters. # # param: texture - bpy.data.texture to modify. # level - designated tweaked settings to use # -> Below 10 is a displacment texture # -> Between 10 and 20 is a base material texture def randomizeTexture(texture, level=1): noises = ['BLENDER_ORIGINAL', 'ORIGINAL_PERLIN', 'IMPROVED_PERLIN', 'VORONOI_F1', 'VORONOI_F2', 'VORONOI_F3', 'VORONOI_F4', 'VORONOI_F2_F1', 'VORONOI_CRACKLE'] if texture.type == 'CLOUDS': if randint(0, 1) == 0: texture.noise_type = 'SOFT_NOISE' else: texture.noise_type = 'HARD_NOISE' if level != 11: tempInt = randint(0, 6) else: tempInt = randint(0, 8) texture.noise_basis = noises[tempInt] texture.noise_depth = 8 if level == 0: texture.noise_scale = gauss(0.625, 1 / 24) elif level == 2: texture.noise_scale = 0.15 elif level == 11: texture.noise_scale = gauss(0.5, 1 / 24) if texture.noise_basis in ['BLENDER_ORIGINAL', 'ORIGINAL_PERLIN', 'IMPROVED_PERLIN', 'VORONOI_F1']: texture.intensity = gauss(1, 1 / 6) texture.contrast = gauss(4, 1 / 3) elif texture.noise_basis in ['VORONOI_F2', 'VORONOI_F3', 'VORONOI_F4']: texture.intensity = gauss(0.25, 1 / 12) texture.contrast = gauss(2, 1 / 6) elif texture.noise_basis == 'VORONOI_F2_F1': texture.intensity = gauss(0.5, 1 / 6) texture.contrast = gauss(2, 1 / 6) elif texture.noise_basis == 'VORONOI_CRACKLE': texture.intensity = gauss(0.5, 1 / 6) texture.contrast = gauss(2, 1 / 6) elif texture.type == 'MUSGRAVE': musgraveType = ['MULTIFRACTAL', 'RIDGED_MULTIFRACTAL', 'HYBRID_MULTIFRACTAL', 'FBM', 'HETERO_TERRAIN'] texture.musgrave_type = 'MULTIFRACTAL' texture.dimension_max = abs(gauss(0, 0.6)) + 0.2 texture.lacunarity = beta(3, 8) * 8.2 + 1.8 if level == 0: texture.noise_scale = gauss(0.625, 1 / 24) texture.noise_intensity = 0.2 texture.octaves = 1.0 elif level == 2: texture.intensity = gauss(1, 1 / 6) texture.contrast = 0.2 texture.noise_scale = 0.15 texture.octaves = 8.0 elif level == 10: texture.intensity = gauss(0.25, 1 / 12) texture.contrast = gauss(1.5, 1 / 6) texture.noise_scale = 0.5 texture.octaves = 8.0 elif level == 12: texture.octaves = uniform(1, 3) elif level > 12: texture.octaves = uniform(2, 8) else: texture.intensity = gauss(1, 1 / 6) texture.contrast = 0.2 texture.octaves = 8.0 elif texture.type == 'DISTORTED_NOISE': tempInt = randint(0, 8) texture.noise_distortion = noises[tempInt] tempInt = randint(0, 8) texture.noise_basis = noises[tempInt] texture.distortion = skewedGauss(2.0, 2.6666, (0.0, 10.0), False) if level == 0: texture.noise_scale = gauss(0.625, 1 / 24) elif level == 2: texture.noise_scale = 0.15 elif level >= 12: texture.noise_scale = gauss(0.2, 1 / 48) elif texture.type == 'STUCCI': stucciTypes = ['PLASTIC', 'WALL_IN', 'WALL_OUT'] if randint(0, 1) == 0: texture.noise_type = 'SOFT_NOISE' else: texture.noise_type = 'HARD_NOISE' tempInt = randint(0, 2) texture.stucci_type = stucciTypes[tempInt] if level == 0: tempInt = randint(0, 6) texture.noise_basis = noises[tempInt] texture.noise_scale = gauss(0.625, 1 / 24) elif level == 2: tempInt = randint(0, 6) texture.noise_basis = noises[tempInt] texture.noise_scale = 0.15 elif level >= 12: tempInt = randint(0, 6) texture.noise_basis = noises[tempInt] texture.noise_scale = gauss(0.2, 1 / 30) else: tempInt = randint(0, 6) texture.noise_basis = noises[tempInt] elif texture.type == 'VORONOI': metrics = ['DISTANCE', 'DISTANCE_SQUARED', 'MANHATTAN', 'CHEBYCHEV', 'MINKOVSKY_HALF', 'MINKOVSKY_FOUR', 'MINKOVSKY'] # Settings for first dispalcement level: if level == 0: tempInt = randint(0, 1) texture.distance_metric = metrics[tempInt] texture.noise_scale = gauss(0.625, 1 / 24) texture.contrast = 0.5 texture.intensity = 0.7 elif level == 2: texture.noise_scale = 0.15 tempInt = randint(0, 6) texture.distance_metric = metrics[tempInt] elif level >= 12: tempInt = randint(0, 1) texture.distance_metric = metrics[tempInt] texture.noise_scale = gauss(0.125, 1 / 48) texture.contrast = 0.5 texture.intensity = 0.7 else: tempInt = randint(0, 6) texture.distance_metric = metrics[tempInt] return # Randomizes the given material given base values. # # param: Material to randomize def randomizeMaterial(material, color, dif_int, rough, spec_int, spec_hard, use_trans, alpha, cloudy, mat_IOR, mossiness, spec_IOR): skew = False stddev = 0.0 lastUsedTex = 1 numTex = 6 baseColor = [] # Diffuse settings: material.diffuse_shader = 'OREN_NAYAR' if 0.5 > dif_int: stddev = dif_int / 3 skew = False else: stddev = (1 - dif_int) / 3 skew = True material.diffuse_intensity = skewedGauss(dif_int, stddev, (0.0, 1.0), skew) if 1.57 > rough: stddev = rough / 3 skew = False else: stddev = (3.14 - rough) / 3 skew = True material.roughness = skewedGauss(rough, stddev, (0.0, 3.14), skew) for i in range(3): if color[i] > 0.9 or color[i] < 0.1: baseColor.append(skewedGauss(color[i], color[i] / 30, (0, 1), color[i] > 0.9)) else: baseColor.append(gauss(color[i], color[i] / 30)) material.diffuse_color = baseColor # Specular settings: material.specular_shader = 'BLINN' if 0.5 > spec_int: variance = spec_int / 3 skew = False else: variance = (1 - spec_int) / 3 skew = True material.specular_intensity = skewedGauss(spec_int, stddev, (0.0, 1.0), skew) if 256 > spec_hard: variance = (spec_hard - 1) / 3 skew = False else: variance = (511 - spec_hard) / 3 skew = True material.specular_hardness = int(round(skewedGauss(spec_hard, stddev, (1.0, 511.0), skew))) if 5.0 > spec_IOR: variance = spec_IOR / 3 skew = False else: variance = (10.0 - spec_IOR) / 3 skew = True material.specular_ior = skewedGauss(spec_IOR, stddev, (0.0, 10.0), skew) # Raytrans settings: # *** Added on 11/17/2011 *** material.use_transparency = use_trans if use_trans: trans = material.raytrace_transparency # Fixed values: material.transparency_method = 'RAYTRACE' trans.depth = 24 trans.gloss_samples = 32 trans.falloff = 1.0 # Needs randomization: material.alpha = -gauss(alpha, 0.05) + 1; trans.gloss_factor = -gauss(cloudy, 0.05) + 1 trans.filter = gauss(cloudy, 0.1) trans.ior = skewedGauss(mat_IOR, 0.01, [0.25, 4.0], mat_IOR > 2.125) #Misc. settings: material.use_transparent_shadows = True # Rock textures: # Now using slot.texture for texture access instead of # bpy.data.textures[newTex[]] # *** Completed on 9/6/2011 *** # Create the four new textures: textureTypes = ['MUSGRAVE', 'CLOUDS', 'DISTORTED_NOISE', 'STUCCI', 'VORONOI'] for i in range(numTex): texColor = [] # Set the active material slot: material.active_texture_index = i # Assign a texture to the active material slot: material.active_texture = bpy.data.textures.new(name = 'stone_tex', type = 'NONE') # Store the slot to easy coding access: slot = material.texture_slots[i] # If the texture is not a moss texture: if i > 1: slot.texture.type = textureTypes[randint(0, 3)] # Set the texture's color (RGB): for j in range(3): if color[j] > 0.9 or color[j] < 0.1: texColor.append(skewedGauss(color[j], color[j] / 30, (0, 1), color[j] > 0.9)) else: texColor.append(gauss(color[j], color[j] / 30)) slot.color = texColor # Randomize the value (HSV): v = material.diffuse_color.v if v == 0.5: slot.color.v = gauss(v, v / 3) elif v > 0.5: slot.color.v = skewedGauss(v, v / 3, (0, 1), True) else: slot.color.v = skewedGauss(v, (1 - v) / 3, (0, 1), False) # Adjust scale and normal based on texture type: if slot.texture.type == 'VORONOI': slot.scale = (gauss(5, 1), gauss(5, 1), gauss(5, 1)) slot.normal_factor = gauss(rough / 10, rough / 30) elif slot.texture.type == 'STUCCI': slot.scale = (gauss(1.5, 0.25), gauss(1.5, 0.25), gauss(1.5, 0.25)) slot.normal_factor = gauss(rough / 10, rough / 30) elif slot.texture.type == 'DISTORTED_NOISE': slot.scale = (gauss(1.5, 0.25), gauss(1.5, 0.25), gauss(1.5, 0.25)) slot.normal_factor = gauss(rough / 10, rough / 30) elif slot.texture.type == 'MUSGRAVE': slot.scale = (gauss(1.5, 0.25), gauss(1.5, 0.25), gauss(1.5, 0.25)) slot.normal_factor = gauss(rough, rough / 3) elif slot.texture.type == 'CLOUDS': slot.scale = (gauss(1.5, 0.25), gauss(1.5, 0.25), gauss(1.5, 0.25)) slot.normal_factor = gauss(rough, rough / 3) # Set the color influence to 0.5. # This allows for the moss textures to show: slot.diffuse_color_factor = 0.5 # Set additional influence booleans: slot.use_stencil = True slot.use_map_specular = True slot.use_map_color_spec = True slot.use_map_hardness = True slot.use_map_normal = True # The following is for setting up the moss textures: else: slot.texture.type = textureTypes[i] # Set the mosses color (RGB): texColor.append(gauss(0.5, 1 / 6)) texColor.append(1) texColor.append(0) slot.color = texColor # Randomize the value (HSV): slot.color.v = gauss(0.275, 1 / 24) # Scale the texture size: slot.scale = (gauss(1.5, 0.25), gauss(1.5, 0.25), gauss(1.5, 0.25)) # Set the strength of the moss color: slot.diffuse_color_factor = mossiness # Have it influence spec and hardness: slot.use_map_specular = True slot.use_map_color_spec = True slot.use_map_hardness = True # If the texutre is a voronoi crackle clouds, use "Negative": if slot.texture.type == 'CLOUDS': if slot.texture.noise_basis == 'VORONOI_CRACKLE': slot.invert = True if mossiness == 0: slot.use = False randomizeTexture(slot.texture, 10 + i) return # Generates an object based on one of several different mesh types. # All meshes have exactly eight vertices, and may be built from either # tri's or quads. # # param: muX - mean X offset value # sigmaX - X offset standard deviation # scaleX - X upper and lower bounds # upperSkewX - Is the distribution upperskewed? # muY - mean Y offset value # sigmaY - Y offset standard deviation # scaleY - Y upper and lower bounds # upperSkewY - Is the distribution upperskewed? # muZ - mean Z offset value # sigmaZ - Z offset standard deviation # scaleZ - Z upper and lower bounds # upperSkewY - Is the distribution upperskewed? # base - base number on the end of the object name # shift - Addition to the base number for multiple runs. # scaleDisplace - Scale the displacement maps # # return: name - the built name of the object def generateObject(context, muX, sigmaX, scaleX, upperSkewX, muY, sigmaY, scaleY, upperSkewY, muZ, sigmaZ, scaleZ, upperSkewZ, base, shift, scaleDisplace, scale_fac): x = [] y = [] z = [] shape = randint(0, 11) # Cube # Use parameters to re-scale cube: # Reversed if/for nesting. Should be a little faster. if shape == 0: for j in range(8): if sigmaX == 0: x.append(scaleX[0] / 2) else: x.append(skewedGauss(muX, sigmaX, scaleX, upperSkewX) / 2) if sigmaY == 0: y.append(scaleY[0] / 2) else: y.append(skewedGauss(muY, sigmaY, scaleY, upperSkewY) / 2) if sigmaZ == 0: z.append(scaleZ[0] / 2) else: z.append(skewedGauss(muZ, sigmaZ, scaleZ, upperSkewZ) / 2) elif shape == 1: for j in range(8): if j in [0, 1, 3, 4]: if sigmaX == 0: x.append(scaleX[0] / 2) else: x.append(skewedGauss(muX, sigmaX, scaleX, upperSkewX) / 2) if sigmaY == 0: y.append(scaleY[0] / 2) else: y.append(skewedGauss(muY, sigmaY, scaleY, upperSkewY) / 2) if sigmaZ == 0: z.append(scaleZ[0] / 2) else: z.append(skewedGauss(muZ, sigmaZ, scaleZ, upperSkewZ) / 2) elif j in [2, 5]: if sigmaX == 0: x.append(0) else: x.append(skewedGauss(muX, sigmaX, scaleX, upperSkewX) / 4) if sigmaY == 0: y.append(scaleY[0] / 2) else: y.append(skewedGauss(muY, sigmaY, scaleY, upperSkewY) / 2) if sigmaZ == 0: z.append(scaleZ[0] / 2) else: z.append(skewedGauss(muZ, sigmaZ, scaleZ, upperSkewZ) / 2) elif j in [6, 7]: if sigmaX == 0: x.append(0) else: x.append(skewedGauss(0, sigmaX, scaleX, upperSkewX) / 4) if sigmaY == 0: y.append(0) else: y.append(skewedGauss(0, sigmaY, scaleY, upperSkewY) / 4) if sigmaZ == 0: z.append(scaleZ[0] / 2) else: z.append(skewedGauss(muZ, sigmaZ, scaleZ, upperSkewZ) / 2) elif shape == 2: for j in range(8): if j in [0, 2, 5, 7]: if sigmaX == 0: x.append(scaleX[0] / 4) else: x.append(skewedGauss(muX, sigmaX, scaleX, upperSkewX) / 4) if sigmaY == 0: y.append(0) else: y.append(skewedGauss(0, sigmaY, scaleY, upperSkewY) / 4) if sigmaZ == 0: z.append(scaleZ[0] / 2) else: z.append(skewedGauss(muZ, sigmaZ, scaleZ, upperSkewZ) / 4) elif j in [1, 3, 4, 6]: if sigmaX == 0: x.append(scaleX[0] / 2) else: x.append(skewedGauss(muX, sigmaX, scaleX, upperSkewX) / 2) if sigmaY == 0: y.append(scaleY[0] / 2) else: y.append(skewedGauss(muY, sigmaY, scaleY, upperSkewY) / 2) if sigmaZ == 0: z.append(scaleZ[0] / 2) else: z.append(skewedGauss(muZ, sigmaZ, scaleZ, upperSkewZ) / 2) elif shape == 3: for j in range(8): if j > 0: if sigmaX == 0: x.append(scaleX[0] / 2) else: x.append(skewedGauss(muX, sigmaX, scaleX, upperSkewX) / 2) if sigmaY == 0: y.append(scaleY[0] / 2) else: y.append(skewedGauss(muY, sigmaY, scaleY, upperSkewY) / 2) if sigmaZ == 0: z.append(scaleZ[0] / 2) else: z.append(skewedGauss(muZ, sigmaZ, scaleZ, upperSkewZ) / 2) else: if sigmaX == 0: x.append(0) else: x.append(skewedGauss(0, sigmaX, scaleX, upperSkewX) / 8) if sigmaY == 0: y.append(0) else: y.append(skewedGauss(0, sigmaY, scaleY, upperSkewY) / 8) if sigmaZ == 0: z.append(0) else: z.append(skewedGauss(0, sigmaZ, scaleZ, upperSkewZ) / 8) elif shape == 4: for j in range(10): if j in [0, 9]: if sigmaX == 0: x.append(0) else: x.append(skewedGauss(0, sigmaX, scaleX, upperSkewX) / 2) if sigmaY == 0: y.append(0) else: y.append(skewedGauss(0, sigmaY, scaleY, upperSkewY) / 2) if sigmaZ == 0: z.append(scaleZ[0] / 2) else: z.append(skewedGauss(muZ, sigmaZ, scaleZ, upperSkewZ) / 2) elif j in [1, 2, 3, 4]: if sigmaX == 0: x.append(scaleX[0] / 2) else: x.append(skewedGauss(muX, sigmaX, scaleX, upperSkewX) / 2) if sigmaY == 0: y.append(scaleY[0] / 2) else: y.append(skewedGauss(muY, sigmaY, scaleY, upperSkewY) / 2) if sigmaZ == 0: z.append(scaleZ[0] / 2) else: z.append(skewedGauss(muZ, sigmaZ, scaleZ, upperSkewZ) / 2) elif j in [5, 7]: if sigmaX == 0: x.append(0) else: x.append(skewedGauss(0, sigmaX, scaleX, upperSkewX) / 3) if sigmaY == 0: y.append(scaleY[0] / 3) else: y.append(skewedGauss(muY, sigmaY, scaleY, upperSkewY) / 3) if sigmaZ == 0: z.append(0) else: z.append(skewedGauss(0, sigmaZ, scaleZ, upperSkewZ) / 6) elif j in [6, 8]: if sigmaX == 0: x.append(scaleX[0] / 3) else: x.append(skewedGauss(muX, sigmaX, scaleX, upperSkewX) / 3) if sigmaY == 0: y.append(0) else: y.append(skewedGauss(0, sigmaY, scaleY, upperSkewY) / 3) if sigmaZ == 0: z.append(0) else: z.append(skewedGauss(0, sigmaZ, scaleZ, upperSkewZ) / 6) elif shape == 5: for j in range(10): if j == 0: if sigmaX == 0: x.append(0) else: x.append(skewedGauss(0, sigmaX, scaleX, upperSkewX) / 8) if sigmaY == 0: y.append(0) else: y.append(skewedGauss(0, sigmaY, scaleY, upperSkewY) / 8) if sigmaZ == 0: z.append(scaleZ[0] / 2) else: z.append(skewedGauss(muZ, sigmaZ, scaleZ, upperSkewZ) / 2) elif j in [1, 2]: if sigmaX == 0: x.append(scaleZ[0] * .125) else: x.append(skewedGauss(muX, sigmaX, scaleX, upperSkewX) * 0.125) if sigmaY == 0: y.append(scaleZ[0] * 0.2165) else: y.append(skewedGauss(muY, sigmaY, scaleY, upperSkewY) * 0.2165) if sigmaZ == 0: z.append(0) else: z.append(skewedGauss(0, sigmaZ, scaleZ, upperSkewZ) / 4) elif j == 3: if sigmaX == 0: x.append(scaleX[0] / 4) else: x.append(skewedGauss(muX, sigmaX, scaleX, upperSkewX) / 4) if sigmaY == 0: y.append(0) else: y.append(skewedGauss(0, sigmaY, scaleY, upperSkewY) / 4) if sigmaZ == 0: z.append(0) else: z.append(skewedGauss(0, sigmaZ, scaleZ, upperSkewZ) / 4) elif j in [4, 6]: if sigmaX == 0: x.append(scaleX[0] * 0.25) else: x.append(skewedGauss(muX, sigmaX, scaleX, upperSkewX) * 0.25) if sigmaY == 0: y.append(scaleY[0] * 0.433) else: y.append(skewedGauss(muY, sigmaY, scaleY, upperSkewY) * 0.433) if sigmaZ == 0: z.append(scaleZ[0] / 2) else: z.append(skewedGauss(muZ, sigmaZ, scaleZ, upperSkewZ) / 2) elif j == 5: if sigmaX == 0: x.append(scaleX[0] / 4) else: x.append(skewedGauss(muX, sigmaX, scaleX, upperSkewX) / 4) if sigmaY == 0: y.append(0) else: y.append(skewedGauss(0, sigmaY, scaleY, upperSkewY) / 2) if sigmaZ == 0: z.append(scaleZ[0] / 2) else: z.append(skewedGauss(muZ, sigmaZ, scaleZ, upperSkewZ) / 2) elif j in [7, 9]: if sigmaX == 0: x.append(scaleX[0] * 0.10825) else: x.append(skewedGauss(muX, sigmaX, scaleX, upperSkewX) * 0.10825) if sigmaY == 0: y.append(scaleY[0] * 0.2165) else: y.append(skewedGauss(muY, sigmaY, scaleY, upperSkewY) * 0.2165) if sigmaZ == 0: z.append(scaleZ[0] / 2) else: z.append(skewedGauss(muZ, sigmaZ, scaleZ, upperSkewZ) / 2) elif j == 8: if sigmaX == 0: x.append(scaleX[0] / 2) else: x.append(skewedGauss(muX, sigmaX, scaleX, upperSkewX) / 2) if sigmaY == 0: y.append(0) else: y.append(skewedGauss(0, sigmaY, scaleY, upperSkewY) / 4) if sigmaZ == 0: z.append(scaleZ[0] / 2) else: z.append(skewedGauss(muZ, sigmaZ, scaleZ, upperSkewZ) / 2) elif shape == 6: for j in range(7): if j > 0: if sigmaX == 0: x.append(scaleX[0] / 2) else: x.append(skewedGauss(muX, sigmaX, scaleX, upperSkewX) / 2) if sigmaY == 0: y.append(scaleY[0] / 2) else: y.append(skewedGauss(muY, sigmaY, scaleY, upperSkewY) / 2) if sigmaZ == 0: z.append(scaleZ[0] / 2) else: z.append(skewedGauss(muZ, sigmaZ, scaleZ, upperSkewZ) / 2) else: if sigmaX == 0: x.append(scaleX[0] / 2) else: x.append(skewedGauss(muX, sigmaX, scaleX, upperSkewX) / 2) if sigmaY == 0: y.append(0) else: y.append(skewedGauss(0, sigmaY, scaleY, upperSkewY) / 2) if sigmaZ == 0: z.append(scaleZ[0] / 2) else: z.append(skewedGauss(muZ, sigmaZ, scaleZ, upperSkewZ) / 2) elif shape == 7: for j in range(10): if j in [1, 3, 4, 5, 8, 9]: if sigmaX == 0: x.append(scaleX[0] / 2) else: x.append(skewedGauss(muX, sigmaX, scaleX, upperSkewX) / 2) if sigmaY == 0: y.append(scaleY[0] / 2) else: y.append(skewedGauss(muY, sigmaY, scaleY, upperSkewY) / 2) if sigmaZ == 0: z.append(scaleZ[0] / 2) else: z.append(skewedGauss(muZ, sigmaZ, scaleZ, upperSkewZ) / 2) else: if sigmaX == 0: x.append(scaleX[0] / 2) else: x.append(skewedGauss(muX, sigmaX, scaleX, upperSkewX) / 2) if sigmaY == 0: y.append(0) else: y.append(skewedGauss(0, sigmaY, scaleY, upperSkewY) / 2) if sigmaZ == 0: z.append(scaleZ[0] / 2) else: z.append(skewedGauss(muZ, sigmaZ, scaleZ, upperSkewZ) / 2) elif shape == 8: for j in range(7): if sigmaX == 0: x.append(scaleX[0] / 2) else: x.append(skewedGauss(muX, sigmaX, scaleX, upperSkewX) / 2) if sigmaY == 0: y.append(scaleY[0] / 2) else: y.append(skewedGauss(muY, sigmaY, scaleY, upperSkewY) / 2) if sigmaZ == 0: z.append(scaleZ[0] / 2) else: z.append(skewedGauss(muZ, sigmaZ, scaleZ, upperSkewZ) / 2) elif shape == 9: for j in range(8): if sigmaX == 0: x.append(scaleX[0] / 2) else: x.append(skewedGauss(muX, sigmaX, scaleX, upperSkewX) / 2) if sigmaY == 0: y.append(scaleY[0] / 2) else: y.append(skewedGauss(muY, sigmaY, scaleY, upperSkewY) / 2) if sigmaZ == 0: z.append(scaleZ[0] / 2) else: z.append(skewedGauss(muZ, sigmaZ, scaleZ, upperSkewZ) / 2) elif shape == 10: for j in range(7): if sigmaX == 0: x.append(scaleX[0] / 2) else: x.append(skewedGauss(muX, sigmaX, scaleX, upperSkewX) / 2) if sigmaY == 0: y.append(scaleY[0] / 2) else: y.append(skewedGauss(muY, sigmaY, scaleY, upperSkewY) / 2) if sigmaZ == 0: z.append(scaleZ[0] / 2) else: z.append(skewedGauss(muZ, sigmaZ, scaleZ, upperSkewZ) / 2) elif shape == 11: for j in range(7): if sigmaX == 0: x.append(scaleX[0] / 2) else: x.append(skewedGauss(muX, sigmaX, scaleX, upperSkewX) / 2) if sigmaY == 0: y.append(scaleY[0] / 2) else: y.append(skewedGauss(muY, sigmaY, scaleY, upperSkewY) / 2) if sigmaZ == 0: z.append(scaleZ[0] / 2) else: z.append(skewedGauss(muZ, sigmaZ, scaleZ, upperSkewZ) / 2) # This is for scaling the displacement textures. # Scale the vertices so that their average is equal to 1 * scale factor. if scaleDisplace: averageX = (sum(x) / len(x)) * scale_fac[0] for i in range(len(x)): x[i] /= averageX averageY = (sum(y) / len(y)) * scale_fac[1] for i in range(len(y)): y[i] /= averageY averageZ = (sum(z) / len(z)) * scale_fac[2] for i in range(len(z)): z[i] /= averageZ # Build vertex and face arrays: if shape == 1: verts = [(-x[0],-y[0],-z[0]),(x[1],-y[1],-z[1]),(x[2],-y[2],z[2]), (-x[3],y[3],-z[3]),(x[4],y[4],-z[4]),(x[5],y[5],z[5]), (x[6],y[6],z[6]),(x[7],y[7],-z[7])] faces = [[0,1,2],[0,1,7],[3,0,7],[3,4,7],[1,4,7],[3,4,5],[1,2,6], [1,4,6],[4,5,6],[0,2,6],[0,3,6],[3,5,6]] elif shape == 2: verts = [(-x[0],y[0],-z[0]),(x[1],-y[1],-z[1]),(x[2],y[2],-z[2]), (-x[3],y[3],-z[3]),(-x[4],-y[4],z[4]),(x[5],y[5],z[5]), (x[6],y[6],z[6]),(-x[7],y[7],z[7])] faces = [[0,1,2],[0,2,3],[0,3,7],[0,7,4],[1,4,5],[0,1,4],[5,1,2], [5,2,6],[3,2,6],[3,6,7],[5,4,7],[5,6,7]] elif shape == 3: verts = [(x[0],y[0],z[0]),(x[1],-y[1],-z[1]),(x[2],y[2],-z[2]), (-x[3],y[3],-z[3]),(x[4],-y[4],z[4]),(x[5],y[5],z[5]), (-x[6],y[6],z[6]),(-x[7],-y[7],z[7])] faces = [[0,1,2],[0,2,3],[0,3,6],[0,6,7],[0,7,4],[0,4,1],[5,4,1,2], [5,6,3,2],[5,4,7,6]] elif shape == 4: verts = [(x[0],y[0],z[0]),(x[1],-y[1],-z[1]),(x[2],y[2],-z[2]), (-x[3],y[3],-z[3]),(-x[4],-y[4],-z[4]),(x[5],-y[5],-z[5]), (x[6],y[6],-z[6]),(x[7],y[7],-z[7]),(-x[8],y[8],-z[8]), (x[9],y[9],-z[9])] faces = [[0,1,6],[0,6,2],[0,2,7],[0,7,3],[0,3,8],[0,8,4],[0,4,5], [0,5,1],[1,9,2],[2,9,3],[3,9,4],[4,9,1],[1,6,2],[2,7,3], [3,8,4],[4,5,1]] elif shape == 5: verts = [(x[0],y[0],z[0]),(x[1],-y[1],z[1]),(x[2],y[2],z[2]), (-x[3],y[3],z[3]),(x[4],-y[4],-z[4]),(x[5],y[5],-z[5]), (x[6],y[6],-z[6]),(-x[7],y[7],-z[7]),(-x[8],y[8],-z[8]), (-x[9],-y[9],-z[9])] faces = [[0,1,2],[0,2,3],[0,3,1],[1,4,5],[1,5,2],[2,5,6],[2,6,7], [2,7,3],[3,7,8],[3,8,9],[3,9,1],[1,9,4],[4,5,9],[5,6,7], [7,8,9],[9,5,7]] elif shape == 6: verts = [(x[0],y[0],z[0]),(x[1],-y[1],-z[1]),(x[2],y[2],-z[2]), (-x[3],y[3],-z[3]),(-x[4],y[4],z[4]),(-x[5],-y[5],z[5]), (-x[6],-y[6],-z[6])] faces = [[0,1,2],[0,2,3,4],[0,1,6,5],[0,4,5],[1,2,3,6],[3,4,5,6]] elif shape == 7: verts = [(x[0],y[0],z[0]),(x[1],-y[1],-z[1]),(x[2],y[2],-z[2]), (x[3],y[3],-z[3]),(-x[4],y[4],-z[4]),(-x[5],y[5],z[5]), (-x[6],y[6],z[6]),(-x[7],y[7],-z[7]),(-x[8],-y[8],-z[8]), (-x[9],-y[9],z[9])] faces = [[0,1,2],[0,2,3],[0,5,6],[0,6,9],[0,1,8,9],[0,3,4,5], [1,2,7,8],[2,3,4,7],[4,5,6,7],[6,7,8,9]] elif shape == 8: verts = [(x[0],y[0],z[0]),(x[1],-y[1],-z[1]),(x[2],y[2],-z[2]), (-x[3],y[3],-z[3]),(-x[4],-y[4],-z[4]),(-x[5],-y[5],z[5]), (-x[6],y[6],z[6])] faces = [[0,2,1],[0,1,4],[0,4,5],[0,5,6],[0,6,3,2],[2,1,4,3], [3,6,5,4]] elif shape == 9: verts = [(-x[0],-y[0],-z[0]),(-x[1],y[1],-z[1]),(-x[2],y[2],z[2]), (-x[3],-y[3],z[3]),(x[4],-y[4],-z[4]),(x[5],y[5],-z[5]), (x[6],y[6],z[6]),(x[7],-y[7],z[7])] faces = [[0,1,6,2],[1,5,7,6],[5,4,3,7],[4,0,2,3],[0,1,5,4],[3,2,6,7]] elif shape == 10: verts = [(-x[0],-y[0],-z[0]),(-x[1],y[1],-z[1]),(-x[2],y[2],z[2]), (x[3],-y[3],z[3]),(x[4],y[4],z[4]),(x[5],y[5],-z[5]), (x[6],-y[6],-z[6])] faces = [[0,2,3],[0,3,6],[0,1,5,6],[2,3,4],[0,1,2],[1,2,4,5],[3,4,5,6]] elif shape == 11: verts = [(-x[0],-y[0],-z[0]),(-x[1],y[1],-z[1]),(-x[2],y[2],z[2]), (x[3],-y[3],z[3]),(x[4],y[4],z[4]),(x[5],y[5],-z[5]), (x[6],-y[6],-z[6])] faces = [[0,2,3],[0,3,6],[0,1,5,6],[2,3,4],[5,6,3],[1,5,3,4],[0,1,4,2]] else: verts = [(-x[0],-y[0],-z[0]),(-x[1],y[1],-z[1]),(-x[2],-y[2],z[2]), (-x[3],y[3],z[3]),(x[4],-y[4],-z[4]),(x[5],y[5],-z[5]), (x[6],-y[6],z[6]),(x[7],y[7],z[7])] faces = [[0,1,3,2],[0,1,5,4],[0,4,6,2],[7,5,4,6],[7,3,2,6],[7,5,1,3]] ## name = "Rock." + str(base + shift).zfill(3) name = "rock" # Make object: obj = createMeshObject(context, verts, [], faces, name) if scaleDisplace: ## bpy.data.objects[name].scale = Vector((averageX, averageY, averageZ)) obj.object.scale = Vector((averageX, averageY, averageZ)) # For a slight speed bump / Readability: ## mesh = bpy.data.meshes[name] mesh = obj.object.data # Apply creasing: if shape == 0: for i in range(12): # todo: "0.375 / 3"? WTF? That = 0.125. . . . # *** Completed 7/15/2011: Changed second one *** mesh.edges[i].crease = gauss(0.125, 0.125) elif shape == 1: for i in [0, 2]: mesh.edges[i].crease = gauss(0.5, 0.125) for i in [6, 9, 11, 12]: mesh.edges[i].crease = gauss(0.25, 0.05) for i in [5, 7, 15, 16]: mesh.edges[i].crease = gauss(0.125, 0.025) elif shape == 2: for i in range(18): mesh.edges[i].crease = gauss(0.125, 0.025) elif shape == 3: for i in [0, 1, 6, 10, 13]: mesh.edges[i].crease = gauss(0.25, 0.05) mesh.edges[8].crease = gauss(0.5, 0.125) elif shape == 4: for i in [5, 6, 7, 10, 14, 16, 19, 21]: mesh.edges[i].crease = gauss(0.5, 0.125) elif shape == 7: for i in range(18): if i in [0, 1, 2, 3, 6, 7, 8, 9, 13, 16]: mesh.edges[i].crease = gauss(0.5, 0.125) elif i in [11,17]: mesh.edges[i].crease = gauss(0.25, 0.05) else: mesh.edges[i].crease = gauss(0.125, 0.025) elif shape == 8: for i in range(12): if i in [0, 3, 8, 9, 10]: mesh.edges[i].crease = gauss(0.5, 0.125) elif i == 11: mesh.edges[i].crease = gauss(0.25, 0.05) else: mesh.edges[i].crease = gauss(0.125, 0.025) elif shape == 9: for i in range(12): if i in [0, 3, 4, 11]: mesh.edges[i].crease = gauss(0.5, 0.125) else: mesh.edges[i].crease = gauss(0.25, 0.05) elif shape == 10: for i in range(12): if i in [0, 2, 3, 4, 8, 11]: mesh.edges[i].crease = gauss(0.5, 0.125) elif i in [1, 5, 7]: mesh.edges[i].crease = gauss(0.25, 0.05) else: mesh.edges[i].crease = gauss(0.125, 0.025) elif shape == 11: for i in range(11): if i in [1, 2, 3, 4, 8, 11]: mesh.edges[i].crease = gauss(0.25, 0.05) else: mesh.edges[i].crease = gauss(0.125, 0.025) return obj.object ## return name # Artifically skews a normal (gaussian) distribution. This will not create # a continuous distribution curve but instead acts as a piecewise finction. # This linearly scales the output on one side to fit the bounds. # # Example output historgrams: # # Upper skewed: Lower skewed: # | ▄ | _ # | █ | █ # | █_ | █ # | ██ | _█ # | _██ | ██ # | _▄███_ | ██ _ # | ▄██████ | ▄██▄█▄_ # | _█▄███████ | ███████ # | _██████████_ | ████████▄▄█_ _ # | _▄▄████████████ | ████████████▄█_ # | _▄_ ▄███████████████▄_ | _▄███████████████▄▄_ # ------------------------- ----------------------- # |mu |mu # Historgrams were generated in R (http://www.r-project.org/) based on the # calculations below and manually duplicated here. # # param: mu - mu is the mean of the distribution. # sigma - sigma is the standard deviation of the distribution. # bounds - bounds[0] is the lower bound and bounds[1] # is the upper bound. # upperSkewed - if the distribution is upper skewed. # return: out - Rondomly generated value from the skewed distribution. # # @todo: Because NumPy's random value generators are faster when called # a bunch of times at once, maybe allow this to generate and return # multiple values at once? def skewedGauss(mu, sigma, bounds, upperSkewed=True): raw = gauss(mu, sigma) # Quicker to check an extra condition than do unnecessary math. . . . if raw < mu and not upperSkewed: out = ((mu - bounds[0]) / (3 * sigma)) * raw + ((mu * (bounds[0] - (mu - 3 * sigma))) / (3 * sigma)) elif raw > mu and upperSkewed: out = ((mu - bounds[1]) / (3 * -sigma)) * raw + ((mu * (bounds[1] - (mu + 3 * sigma))) / (3 * -sigma)) else: out = raw return out # @todo create a def for generating an alpha and beta for a beta distribution # given a mu, sigma, and an upper and lower bound. This proved faster in # profiling in addition to providing a much better distribution curve # provided multiple iterations happen within this function; otherwise it was # slower. # This might be a scratch because of the bounds placed on mu and sigma: # # For alpha > 1 and beta > 1: # mu^2 - mu^3 mu^3 - mu^2 + mu # ----------- < sigma < ---------------- # 1 + mu 2 - mu # ##def generateBeta(mu, sigma, scale, repitions=1): ## results = [] ## ## return results # Creates rock objects: def generateRocks(context, scaleX, skewX, scaleY, skewY, scaleZ, skewZ, scale_fac, detail, display_detail, deform, rough, smooth_fac, smooth_it, mat_enable, color, mat_bright, mat_rough, mat_spec, mat_hard, mat_use_trans, mat_alpha, mat_cloudy, mat_IOR, mat_mossy, numOfRocks=1, userSeed=1.0, scaleDisplace=False, randomSeed=True): global lastRock newMat = [] sigmaX = 0 sigmaY = 0 sigmaZ = 0 upperSkewX = False upperSkewY = False upperSkewZ = False shift = 0 lastUsedTex = 1 vertexScaling = [] # Seed the random Gaussian value generator: if randomSeed: seed(int(time.time())) else: seed(userSeed) if mat_enable: # Calculate the number of materials to use. # If less than 10 rocks are being generated, generate one material # per rock. # If more than 10 rocks are being generated, generate # ceil[(1/9)n + (80/9)] materials. # -> 100 rocks will result in 20 materials # -> 1000 rocks will result in 120 materials. if numOfRocks < 10: numOfMats = numOfRocks else: numOfMats = math.ceil((1/9) * numOfRocks + (80/9)) # newMat = generateMaterialsList(numOfMats) # *** No longer needed on 9/6/2011 *** # todo Set general material settings: # *** todo completed 5/25/2011 *** # Material roughness actual max = 3.14. Needs scaling. mat_rough *= 0.628 spec_IOR = 1.875 * (mat_spec ** 2) + 7.125 * mat_spec + 1 # Changed as material mapping is no longer needed. # *** Complete 9/6/2011 *** for i in range(numOfMats): newMat.append(bpy.data.materials.new(name = 'stone')) randomizeMaterial(newMat[i], color, mat_bright, mat_rough, mat_spec, mat_hard, mat_use_trans, mat_alpha, mat_cloudy, mat_IOR, mat_mossy, spec_IOR) # These values need to be really small to look good. # So the user does not have to use such ridiculously small values: deform /= 10 rough /= 100 # Verify that the min really is the min: if scaleX[1] < scaleX[0]: scaleX[0], scaleX[1] = scaleX[1], scaleX[0] if scaleY[1] < scaleY[0]: scaleY[0], scaleY[1] = scaleY[1], scaleY[0] if scaleZ[1] < scaleZ[0]: scaleZ[0], scaleZ[1] = scaleZ[1], scaleZ[0] # todo: edit below to allow for skewing the distribution # *** todo completed 4/22/2011 *** # *** Code now generating "int not scriptable error" in Blender *** # # Calculate mu and sigma for a Gaussian distributed random number # generation: # If the lower and upper bounds are the same, skip the math. # # sigma is the standard deviation of the values. The 95% interval is three # standard deviations, which is what we want most generated values to fall # in. Since it might be skewed we are going to use half the difference # betwee the mean and the furthest bound and scale the other side down # post-number generation. if scaleX[0] != scaleX[1]: skewX = (skewX + 1) / 2 muX = scaleX[0] + ((scaleX[1] - scaleX[0]) * skewX) if skewX < 0.5: sigmaX = (scaleX[1] - muX) / 3 else: sigmaX = (muX - scaleX[0]) / 3 upperSkewX = True else: muX = scaleX[0] if scaleY[0] != scaleY[1]: skewY = (skewY + 1) / 2 muY = scaleY[0] + ((scaleY[1] - scaleY[0]) * skewY) if skewY < 0.5: sigmaY = (scaleY[1] - muY) / 3 else: sigmaY = (muY - scaleY[0]) / 3 upperSkewY = True else: muY = scaleY[0] if scaleZ[0] != scaleZ[1]: skewZ = (skewZ + 1) / 2 muZ = scaleZ[0] + ((scaleZ[1] - scaleZ[0]) * skewZ) if skewZ < 0.5: sigmaZ = (scaleZ[1] - muZ) / 3 else: sigmaZ = (muZ - scaleZ[0]) / 3 upperSkewZ = True else: muZ = scaleZ for i in range(numOfRocks): # todo: enable different random values for each (x,y,z) corrdinate for # each vertex. This will add additional randomness to the shape of the # generated rocks. # *** todo completed 4/19/2011 *** # *** Code is notably slower at high rock counts *** rock = generateObject(context, muX, sigmaX, scaleX, upperSkewX, muY, ## name = generateObject(context, muX, sigmaX, scaleX, upperSkewX, muY, sigmaY, scaleY, upperSkewY, muZ, sigmaZ, scaleZ, upperSkewZ, i, lastRock, scaleDisplace, scale_fac) ## rock = bpy.data.objects[name] # todo Map what the two new textures will be: # This is not working. It works on paper so . . . ??? # *** todo completed on 4/23/2011 *** # *** todo re-added as the first rock is getting # 'Texture.001' twice. *** # *** todo completed on 4/25/2011 *** # *** Script no longer needs to map new texture names 9/6/2011 *** # Create the four new textures: # todo Set displacement texture parameters: # *** todo completed on 5/31/2011 *** # Voronoi has been removed from being an option for the fine detail # texture. texTypes = ['CLOUDS', 'MUSGRAVE', 'DISTORTED_NOISE', 'STUCCI', 'VORONOI'] newTex = [] # The first texture is to give a more ranodm base shape appearance: newTex.append(bpy.data.textures.new(name = 'rock_displacement', type = texTypes[1])) randomizeTexture(newTex[0], 0) newTex.append(bpy.data.textures.new(name = 'rock_displacement', type = texTypes[4])) randomizeTexture(newTex[1], 0) if numpy: newTex.append(bpy.data.textures.new(name = 'rock_displacement', type = texTypes[int(round(weibull(1, 1)[0] / 2.125))])) randomizeTexture(newTex[2], 1) newTex.append(bpy.data.textures.new(name = 'rock_displacement', type = texTypes[int(round(weibull(1, 1)[0] / 2.125))])) randomizeTexture(newTex[3], 2) else: newTex.append(bpy.data.textures.new(name = 'rock_displacement', type = texTypes[int(round(weibull(1, 1) / 2.125))])) randomizeTexture(newTex[2], 1) newTex.append(bpy.data.textures.new(name = 'rock_displacement', type = texTypes[int(round(weibull(1, 1) / 2.125))])) randomizeTexture(newTex[3], 2) # Add modifiers: rock.modifiers.new(name = "Subsurf", type = 'SUBSURF') rock.modifiers.new(name = "Subsurf", type = 'SUBSURF') rock.modifiers.new(name = "Displace", type = 'DISPLACE') rock.modifiers.new(name = "Displace", type = 'DISPLACE') rock.modifiers.new(name = "Displace", type = 'DISPLACE') rock.modifiers.new(name = "Displace", type = 'DISPLACE') # If smoothing is enabled, allow a little randomness into the # smoothing factor. Then add the smoothing modifier. if smooth_fac > 0.0 and smooth_it > 0: rock.modifiers.new(name = "Smooth", type='SMOOTH') rock.modifiers[6].factor = gauss(smooth_fac, (smooth_fac ** 0.5) / 12) rock.modifiers[6].iterations = smooth_it # Make a call to random to keep things consistant: else: gauss(0, 1) # Set subsurf modifier parameters: rock.modifiers[0].levels = display_detail rock.modifiers[0].render_levels = detail rock.modifiers[1].levels = display_detail rock.modifiers[1].render_levels = detail # todo Set displacement modifier parameters: # *** todo completed on 4/23/2011 *** # *** toned down the variance on 4/26/2011 *** # *** added third modifier on 4/28/2011 *** # *** texture access changed on 9/6/2011 *** rock.modifiers[2].texture = newTex[0] rock.modifiers[2].strength = gauss(deform / 100, (1 / 300) * deform) rock.modifiers[2].mid_level = 0 rock.modifiers[3].texture = newTex[1] rock.modifiers[3].strength = gauss(deform, (1 / 3) * deform) rock.modifiers[3].mid_level = 0 rock.modifiers[4].texture = newTex[2] rock.modifiers[4].strength = gauss(rough * 2, (1 / 3) * rough) rock.modifiers[5].texture = newTex[3] rock.modifiers[5].strength = gauss(rough, (1 / 3) * rough) # Set mesh to be smooth and fix the normals: utils.smooth(rock.data) ## utils.smooth(bpy.data.meshes[name]) bpy.ops.object.editmode_toggle() bpy.ops.mesh.normals_make_consistent() bpy.ops.object.editmode_toggle() if mat_enable: bpy.ops.object.material_slot_add() rock.material_slots[0].material = newMat[randint(0, numOfMats - 1)] # Store the last value of i: shift = i # Add the shift to lastRock: lastRock += shift + 1 return # Much of the code below is more-or-less imitation of other addons and as such # I have left it undocumented. class rocks(bpy.types.Operator): """Add rock objects""" bl_idname = "mesh.rocks" bl_label = "Add Rocks" bl_options = {'REGISTER', 'UNDO'} bl_description = "Add rocks" # Get the preset values from the XML file. # -> The script was morphed into a Python module # to support this. # Tell settings.py to parse the XML file with the settings. # Then get the default values resulting from the parsing. # Make a list containing the default values and append to that # the presets specified in the same XML file. This list will # be used to load preset values. settings.parse() defaults = settings.getDefault() presetsList = [defaults] presetsList += settings.getPresetLists() presets = [] lastPreset = 0 # Build the presets list for the enum property. # This needs to be a for loop as the user might add presets to # the XML file and those should show here: for i in range(len(presetsList)): value = str(i) name = presetsList[i][0] description = name + " preset values." presets.append((value, name, description)) preset_values = EnumProperty(items = presets, name = "Presets", description = "Preset values for some rock types") num_of_rocks = IntProperty(name = "Number of rocks", description = "Number of rocks to generate. WARNING: Slow at high values!", min = 1, max = 1048576, soft_max = 20, default = 1) scale_X = FloatVectorProperty(name = "X scale", description = "X axis scaling range.", min = 0.0, max = 256.0, step = 1, default = defaults[1], size = 2) skew_X = FloatProperty(name = "X skew", description = "X Skew ratio. 0.5 is no skew.", min = -1.0, max = 1.0, default = defaults[4]) scale_Y = FloatVectorProperty(name = "Y scale", description = "Y axis scaling range.", min = 0.0, max = 256.0, step = 1, default = defaults[2], size = 2) skew_Y = FloatProperty(name = "Y skew", description = "Y Skew ratio. 0.5 is no skew.", min = -1.0, max = 1.0, default = defaults[5]) scale_Z = FloatVectorProperty(name = "Z scale", description = "Z axis scaling range.", min = 0.0, max = 256.0, step = 1, default = defaults[3], size = 2) skew_Z = FloatProperty(name = "Z skew", description = "Z Skew ratio. 0.5 is no skew.", min = -1.0, max = 1.0, default = defaults[6]) use_scale_dis = BoolProperty(name = "Scale displace textures", description = "Scale displacement textures with dimensions. May cause streched textures.", default = defaults[7]) scale_fac = FloatVectorProperty(name = "Scaling Factor", description = "XYZ scaling factor. 1 = no scaling.", min = 0.0001, max = 256.0, step = 0.1, default = defaults[8], size = 3) # @todo Possible to title this section "Physical Properties:"? deform = FloatProperty(name = "Deformation", description = "Rock deformation", min = 0.0, max = 1024.0, default = defaults[9]) rough = FloatProperty(name = "Roughness", description = "Rock roughness", min = 0.0, max = 1024.0, default = defaults[10]) detail = IntProperty(name = "Detail level", description = "Detail level. WARNING: Slow at high values!", min = 1, max = 1024, default = defaults[11]) display_detail = IntProperty(name = "Display Detail", description = "Display detail. Use a lower value for high numbers of rocks.", min = 1, max = 128, default = defaults[12]) smooth_fac = FloatProperty(name = "Smooth Factor", description = "Smoothing factor. A value of 0 disables.", min = 0.0, max = 128.0, default = defaults[13]) smooth_it = IntProperty(name = "Smooth Iterations", description = "Smoothing iterations. A value of 0 disables.", min = 0, max = 128, default = defaults[14]) # @todo Add material properties mat_enable = BoolProperty(name = "Generate materials", description = "Generate materials and textures for the rocks", default = defaults[15]) mat_color = FloatVectorProperty(name = "Color", description = "Base color settings (RGB)", min = 0.0, max = 1.0, default = defaults[16], size = 3, subtype = 'COLOR') mat_bright = FloatProperty(name = "Brightness", description = "Material brightness", min = 0.0, max = 1.0, default = defaults[17]) mat_rough = FloatProperty(name = "Roughness", description = "Material roughness", min = 0.0, max = 5.0, default = defaults[18]) mat_spec = FloatProperty(name = "Shine", description = "Material specularity strength", min = 0.0, max = 1.0, default = defaults[19]) mat_hard = IntProperty(name = "Hardness", description = "Material hardness", min = 0, max = 511, default = defaults[20]) mat_use_trans = BoolProperty(name = "Use Transparency", description = "Enables transparency in rocks (WARNING: SLOW RENDER TIMES)", default = defaults[21]) mat_alpha = FloatProperty(name = "Alpha", description = "Transparency of the rocks", min = 0.0, max = 1.0, default = defaults[22]) mat_cloudy = FloatProperty(name = "Cloudy", description = "How cloudy the transparent rocks look", min = 0.0, max = 1.0, default = defaults[23]) mat_IOR = FloatProperty(name = "IoR", description = "Index of Refraction", min = 0.25, max = 4.0, soft_max = 2.5, default = defaults[24]) mat_mossy = FloatProperty(name = "Mossiness", description = "Amount of mossiness on the rocks", min = 0.0, max = 1.0, default = defaults[25]) use_generate = BoolProperty(name = "Generate Rocks", description = "Enable actual generation.", default = defaults[26]) use_random_seed = BoolProperty(name = "Use a random seed", description = "Create a seed based on time. Causes user seed to be ignored.", default = defaults[27]) user_seed = IntProperty(name = "User seed", description = "Use a specific seed for the generator.", min = 0, max = 1048576, default = defaults[28]) def draw(self, context): layout = self.layout box = layout.box() box.prop(self, 'num_of_rocks') box = layout.box() box.prop(self, 'scale_X') box.prop(self, 'skew_X') box.prop(self, 'scale_Y') box.prop(self, 'skew_Y') box.prop(self, 'scale_Z') box.prop(self, 'skew_Z') box.prop(self, 'use_scale_dis') if self.use_scale_dis: box.prop(self, 'scale_fac') else: self.scale_fac = utils.toFloats(self.defaults[8]) box = layout.box() box.prop(self, 'deform') box.prop(self, 'rough') box.prop(self, 'detail') box.prop(self, 'display_detail') box.prop(self, 'smooth_fac') box.prop(self, 'smooth_it') box = layout.box() box.prop(self, 'mat_enable') if self.mat_enable: box.prop(self, 'mat_color') box.prop(self, 'mat_bright') box.prop(self, 'mat_rough') box.prop(self, 'mat_spec') box.prop(self, 'mat_hard') box.prop(self, 'mat_use_trans') if self.mat_use_trans: box.prop(self, 'mat_alpha') box.prop(self, 'mat_cloudy') box.prop(self, 'mat_IOR') box.prop(self, 'mat_mossy') box = layout.box() box.prop(self, 'use_generate') box.prop(self, 'use_random_seed') if not self.use_random_seed: box.prop(self, 'user_seed') box.prop(self, 'preset_values') def execute(self, context): # The following "if" block loads preset values: if self.lastPreset != int(self.preset_values): self.scale_X = utils.toFloats(self.presetsList[int(self.preset_values)][1]) self.scale_Y = utils.toFloats(self.presetsList[int(self.preset_values)][2]) self.scale_Z = utils.toFloats(self.presetsList[int(self.preset_values)][3]) self.skew_X = float(self.presetsList[int(self.preset_values)][4]) self.skew_Y = float(self.presetsList[int(self.preset_values)][5]) self.skew_Z = float(self.presetsList[int(self.preset_values)][6]) self.use_scale_dis = bool(self.presetsList[int(self.preset_values)][7]) self.scale_fac = utils.toFloats(self.presetsList[int(self.preset_values)][8]) self.deform = float(self.presetsList[int(self.preset_values)][9]) self.rough = float(self.presetsList[int(self.preset_values)][10]) self.detail = int(self.presetsList[int(self.preset_values)][11]) self.display_detail = int(self.presetsList[int(self.preset_values)][12]) self.smooth_fac = float(self.presetsList[int(self.preset_values)][13]) self.smooth_it = int(self.presetsList[int(self.preset_values)][14]) self.mat_enable = bool(self.presetsList[int(self.preset_values)][15]) self.mat_color = utils.toFloats(self.presetsList[int(self.preset_values)][16]) self.mat_bright = float(self.presetsList[int(self.preset_values)][17]) self.mat_rough = float(self.presetsList[int(self.preset_values)][18]) self.mat_spec = float(self.presetsList[int(self.preset_values)][19]) self.mat_hard = int(self.presetsList[int(self.preset_values)][20]) self.mat_use_trans = bool(self.presetsList[int(self.preset_values)][21]) self.mat_alpha = float(self.presetsList[int(self.preset_values)][22]) self.mat_cloudy = float(self.presetsList[int(self.preset_values)][23]) self.mat_IOR = float(self.presetsList[int(self.preset_values)][24]) self.mat_mossy = float(self.presetsList[int(self.preset_values)][25]) self.use_generate = bool(self.presetsList[int(self.preset_values)][26]) self.use_random_seed = bool(self.presetsList[int(self.preset_values)][27]) self.user_seed = int(self.presetsList[int(self.preset_values)][28]) self.lastPreset = int(self.preset_values) # todo Add deform, deform_Var, rough, and rough_Var: # *** todo completed 4/23/2011 *** # *** Eliminated "deform_Var" and "rough_Var" so the script is not # as complex to use. May add in again as advanced features. *** if self.use_generate: generateRocks(context, self.scale_X, self.skew_X, self.scale_Y, self.skew_Y, self.scale_Z, self.skew_Z, self.scale_fac, self.detail, self.display_detail, self.deform, self.rough, self.smooth_fac, self.smooth_it, self.mat_enable, self.mat_color, self.mat_bright, self.mat_rough, self.mat_spec, self.mat_hard, self.mat_use_trans, self.mat_alpha, self.mat_cloudy, self.mat_IOR, self.mat_mossy, self.num_of_rocks, self.user_seed, self.use_scale_dis, self.use_random_seed) return {'FINISHED'}