# 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 time
from . import (
settings,
utils
)
from .utils import skewedGauss
from .randomize_texture import randomizeTexture
from bpy_extras import object_utils
from mathutils import (
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 (
seed,
weibull)
# print("Rock Generator: Numpy found.")
numpy = True
except:
from random import (
randint,
gauss,
seed)
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)
# 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.scale = Vector((averageX, averageY, averageZ))
# For a slight speed bump / Readability:
# mesh = bpy.data.meshes[name]
mesh = obj.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
# Creates rock objects:
def generateRocks(context, scaleX, skewX, scaleY, skewY, scaleZ, skewZ,
scale_fac, detail, display_detail, deform, rough,
smooth_fac, smooth_it,
numOfRocks=1, userSeed=1.0,
scaleDisplace=False, randomSeed=True):
global LASTROCK
sigmaX = 0
sigmaY = 0
sigmaZ = 0
upperSkewX = False
upperSkewY = False
upperSkewZ = False
shift = 0
# vertexScaling = []
# Seed the random Gaussian value generator:
if randomSeed:
seed(int(time.time()))
else:
seed(userSeed)
# 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 ***
# name = generateObject(context, muX, sigmaX, scaleX, upperSkewX, muY,
rock = 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()
# 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 OBJECT_OT_add_mesh_rock(bpy.types.Operator):
"""Add rock objects"""
bl_idname = "mesh.add_mesh_rock"
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, preset in enumerate(presetsList):
presets.append((str(i), preset[0], preset[0] + " preset values"))
preset_values: EnumProperty(
name="Presets",
items=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, 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, soft_max=50, max=1024.0, default=defaults[9])
rough: FloatProperty(
name="Roughness",
description="Rock roughness",
min=0.0, soft_max=50, max=1024.0, default=defaults[10])
detail: IntProperty(
name="Detail level",
description="Detail level. WARNING: Slow at high values!",
min=1, soft_max=4, max=10, default=defaults[11])
display_detail: IntProperty(
name="Display Detail",
description="Display detail. Use a lower value for high numbers of rocks",
min=1, soft_max=4, max=10, 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=50, default=defaults[14])
use_generate: BoolProperty(
name="Generate Rocks",
description="Enable actual generation",
default=defaults[15])
use_random_seed: BoolProperty(
name="Use a random seed",
description="Create a seed based on time. Causes user seed to be ignored",
default=defaults[16])
user_seed: IntProperty(
name="User seed",
description="Use a specific seed for the generator",
min=0, max=1048576, default=defaults[17])
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, 'use_generate')
box.prop(self, 'use_random_seed')
if not self.use_random_seed:
box.prop(self, 'user_seed')
box.prop(self, 'preset_values')
@classmethod
def poll(cls, context):
return context.mode == 'OBJECT'
# return (context.object is not None and context.object.mode == 'OBJECT')
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.use_generate = bool(self.presetsList[int(self.preset_values)][15])
self.use_random_seed = bool(self.presetsList[int(self.preset_values)][16])
self.user_seed = int(self.presetsList[int(self.preset_values)][17])
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.num_of_rocks,
self.user_seed,
self.use_scale_dis,
self.use_random_seed)
return {'FINISHED'}
# Register:
def menu_func_rocks(self, context):
layout = self.layout
layout.separator()
layout.operator(
OBJECT_OT_add_mesh_rock.bl_idname,
text="Rock Generator",
icon="MESH_ICOSPHERE")
classes = (
OBJECT_OT_add_mesh_rock,
)
def register():
from bpy.utils import register_class
for cls in classes:
register_class(cls)
bpy.types.VIEW3D_MT_mesh_add.append(menu_func_rocks)
def unregister():
from bpy.utils import unregister_class
for cls in reversed(classes):
unregister_class(cls)
bpy.types.VIEW3D_MT_mesh_add.remove(menu_func_rocks)
if __name__ == "__main__":
register()