1 # SPDX-License-Identifier: GPL-2.0-or-later
3 # Blender rock creation tool
5 # Based on BlenderGuru's asteroid tutorial and personal experimentation.
6 # Tutorial: http://www.blenderguru.com/how-to-make-a-realistic-asteroid/
7 # Update with another tutorial shared by "rusted" of BlenderArtists:
8 # Tutorial: http://saschahenrichs.blogspot.com/2010/03/3dsmax-environment-modeling-1.html
10 # Uses the NumPy Gaussian random number generator to generate a
11 # a rock within a given range and give some randomness to the displacement
12 # texture values. NumPy's gaussian generator was chosen as, based on
13 # profiling I performed, it runs in about half the time as the built in
14 # Python gaussian equivalent. I would like to shift the script to use the
15 # NumPy beta distribution as it ran in about half the time as the NumPy
16 # gaussian once the skew calculations are added.
18 # Set lower and upper bounds to the same for no randomness.
21 # Generate meshes with random scaling between given values.
22 # - Allow for a skewed distribution
23 # *** Completed on 4/17/2011 ***
24 # - Create a set of meshes that can be used
25 # Give the user the ability to set the subsurf level (detail level)
26 # *** Completed on 4/29/2011 ***
27 # - Set subsurf modifiers to default at view:3, render:3.
28 # *** Completed on 4/17/2011 ***
29 # - Set crease values to allow for hard edges on first subsurf.
30 # *** Completed on 4/29/2011 ***
31 # Be able to generate and add a texture to the displacement modifiers.
32 # *** Completed 5/17/2011 ***
33 # - Generate three displacement modifiers.
34 # - The first only uses a Musgrave for initial intentations.
35 # *** Now generating four displacement modifiers ***
36 # *** Completed on 5/17/2011 ***
37 # - Set a randomness for the type and values of the displacement texture.
38 # *** Completed 5/9/2011 ***
39 # - Allow the user to set a value for the range of displacement.
40 # -> Modification: have user set "roughness" and "roughness range".
41 # *** Compleded on 4/23/2011 ***
42 # Set material settings and assign material textures
43 # *** Completed 6/9/2011 ***
44 # - Mossiness of the rocks.
45 # *** Completed 6/9/2011 ***
46 # - Color of the rocks.
47 # *** Completed 5/16/2011 ***
48 # - Wetness/shinyness of the rock.
49 # *** Completed 5/6/2011 ***
50 # - For all the user provides a mean value for a skewed distribution.
51 # *** Removed to lessen usage complexity ***
52 # Add some presets (mesh) to make it easier to use
53 # - Examples: river rock, asteroid, quaried rock, etc
54 # *** Completed 7/12/2011 ***
57 # Remove all "bpy.ops" operations with "bpy.data" base operations.
58 # Remove material/texture cataloging with building a list of
59 # returned values from bpy.data.*.new() operations.
60 # *** Completed on 9/6/2011 ***
61 # Search for places where list comprehensions can be used.
62 # Look for alternate methods
63 # - Possible alternate and more efficient data structures
64 # - Possible alternate algorithms may realize greater performance
65 # - Look again at multi-processing. Without bpy.ops is might
69 # Multi-thread the script
70 # *** Will not be implemented. Multi-processing is adding to much
71 # overhead to realize a performance increase ***
72 # - Learn basic multi-threading in Python (multiprocessing)
73 # - Break material generation into separate threads (processes)
74 # - Break mesh generation into separate threads (processes)
75 # - Move name generation, texture ID generation, etc to process first
76 # - Roll version to 2.0 on completion
78 # Paul "BrikBot" Marshall
79 # Created: April 17, 2011
80 # Last Modified: November 17, 2011
81 # Homepage (blog): http://post.darkarsenic.com/
82 # //blog.darkarsenic.com/
83 # Thanks to Meta-Androco, RickyBlender, Ace Dragon, and PKHG for ideas
86 # Coded in IDLE, tested in Blender 2.59. NumPy Recommended.
87 # Search for "@todo" to quickly find sections that need work.
90 # Functional code comes before fast code. Once it works, then worry about
91 # making it faster/more efficient.
101 from .utils
import skewedGauss
102 from .randomize_texture
import randomizeTexture
103 from bpy_extras
import object_utils
104 from mathutils
import (
107 from bpy
.props
import (
115 # This try block allows for the script to psudo-intelligently select the
116 # appropriate random to use. If Numpy's random is present it will use that.
117 # If Numpy's random is not present, it will through a "module not found"
118 # exception and instead use the slower built-in random that Python has.
120 from numpy
.random
import random_integers
as randint
121 from numpy
.random
import normal
as gauss
122 from numpy
.random
import (
125 # print("Rock Generator: Numpy found.")
132 from random
import weibullvariate
as weibull
133 print("Rock Generator: Numpy not found. Using Python's random.")
140 # Creates a new mesh:
142 # param: verts - Vector of vertices for the mesh.
143 # edges - Edges for the mesh. Can be "[]".
144 # faces - Face tuples corresponding to vertices.
145 # name - Name of the mesh.
146 def createMeshObject(context
, verts
, edges
, faces
, name
):
148 mesh
= bpy
.data
.meshes
.new(name
)
150 # Make a mesh from a list of verts/edges/faces.
151 mesh
.from_pydata(verts
, edges
, faces
)
153 # Set mesh to use auto smoothing:
154 mesh
.use_auto_smooth
= True
156 # Update mesh geometry after adding stuff.
159 if bpy
.context
.mode
== "EDIT_MESH":
160 bpy
.ops
.object.mode_set(mode
='OBJECT')
162 return object_utils
.object_data_add(context
, mesh
, operator
=None)
165 # Generates an object based on one of several different mesh types.
166 # All meshes have exactly eight vertices, and may be built from either
169 # param: muX - mean X offset value
170 # sigmaX - X offset standard deviation
171 # scaleX - X upper and lower bounds
172 # upperSkewX - Is the distribution upperskewed?
173 # muY - mean Y offset value
174 # sigmaY - Y offset standard deviation
175 # scaleY - Y upper and lower bounds
176 # upperSkewY - Is the distribution upperskewed?
177 # muZ - mean Z offset value
178 # sigmaZ - Z offset standard deviation
179 # scaleZ - Z upper and lower bounds
180 # upperSkewY - Is the distribution upperskewed?
181 # base - base number on the end of the object name
182 # shift - Addition to the base number for multiple runs.
183 # scaleDisplace - Scale the displacement maps
185 # return: name - the built name of the object
186 def generateObject(context
, muX
, sigmaX
, scaleX
, upperSkewX
, muY
, sigmaY
,
187 scaleY
, upperSkewY
, muZ
, sigmaZ
, scaleZ
, upperSkewZ
, base
,
188 shift
, scaleDisplace
, scale_fac
):
192 shape
= randint(0, 11)
195 # Use parameters to re-scale cube:
196 # Reversed if/for nesting. Should be a little faster.
200 x
.append(scaleX
[0] / 2)
202 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 2)
204 y
.append(scaleY
[0] / 2)
206 y
.append(skewedGauss(muY
, sigmaY
, scaleY
, upperSkewY
) / 2)
208 z
.append(scaleZ
[0] / 2)
210 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
213 if j
in [0, 1, 3, 4]:
215 x
.append(scaleX
[0] / 2)
217 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 2)
219 y
.append(scaleY
[0] / 2)
221 y
.append(skewedGauss(muY
, sigmaY
, scaleY
, upperSkewY
) / 2)
223 z
.append(scaleZ
[0] / 2)
225 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
230 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 4)
232 y
.append(scaleY
[0] / 2)
234 y
.append(skewedGauss(muY
, sigmaY
, scaleY
, upperSkewY
) / 2)
236 z
.append(scaleZ
[0] / 2)
238 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
243 x
.append(skewedGauss(0, sigmaX
, scaleX
, upperSkewX
) / 4)
247 y
.append(skewedGauss(0, sigmaY
, scaleY
, upperSkewY
) / 4)
249 z
.append(scaleZ
[0] / 2)
251 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
254 if j
in [0, 2, 5, 7]:
256 x
.append(scaleX
[0] / 4)
258 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 4)
262 y
.append(skewedGauss(0, sigmaY
, scaleY
, upperSkewY
) / 4)
264 z
.append(scaleZ
[0] / 2)
266 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 4)
267 elif j
in [1, 3, 4, 6]:
269 x
.append(scaleX
[0] / 2)
271 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 2)
273 y
.append(scaleY
[0] / 2)
275 y
.append(skewedGauss(muY
, sigmaY
, scaleY
, upperSkewY
) / 2)
277 z
.append(scaleZ
[0] / 2)
279 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
284 x
.append(scaleX
[0] / 2)
286 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 2)
288 y
.append(scaleY
[0] / 2)
290 y
.append(skewedGauss(muY
, sigmaY
, scaleY
, upperSkewY
) / 2)
292 z
.append(scaleZ
[0] / 2)
294 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
299 x
.append(skewedGauss(0, sigmaX
, scaleX
, upperSkewX
) / 8)
303 y
.append(skewedGauss(0, sigmaY
, scaleY
, upperSkewY
) / 8)
307 z
.append(skewedGauss(0, sigmaZ
, scaleZ
, upperSkewZ
) / 8)
314 x
.append(skewedGauss(0, sigmaX
, scaleX
, upperSkewX
) / 2)
318 y
.append(skewedGauss(0, sigmaY
, scaleY
, upperSkewY
) / 2)
320 z
.append(scaleZ
[0] / 2)
322 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
323 elif j
in [1, 2, 3, 4]:
325 x
.append(scaleX
[0] / 2)
327 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 2)
329 y
.append(scaleY
[0] / 2)
331 y
.append(skewedGauss(muY
, sigmaY
, scaleY
, upperSkewY
) / 2)
333 z
.append(scaleZ
[0] / 2)
335 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
340 x
.append(skewedGauss(0, sigmaX
, scaleX
, upperSkewX
) / 3)
342 y
.append(scaleY
[0] / 3)
344 y
.append(skewedGauss(muY
, sigmaY
, scaleY
, upperSkewY
) / 3)
348 z
.append(skewedGauss(0, sigmaZ
, scaleZ
, upperSkewZ
) / 6)
351 x
.append(scaleX
[0] / 3)
353 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 3)
357 y
.append(skewedGauss(0, sigmaY
, scaleY
, upperSkewY
) / 3)
361 z
.append(skewedGauss(0, sigmaZ
, scaleZ
, upperSkewZ
) / 6)
368 x
.append(skewedGauss(0, sigmaX
, scaleX
, upperSkewX
) / 8)
372 y
.append(skewedGauss(0, sigmaY
, scaleY
, upperSkewY
) / 8)
374 z
.append(scaleZ
[0] / 2)
376 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
379 x
.append(scaleZ
[0] * .125)
381 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) * 0.125)
383 y
.append(scaleZ
[0] * 0.2165)
385 y
.append(skewedGauss(muY
, sigmaY
, scaleY
, upperSkewY
) * 0.2165)
389 z
.append(skewedGauss(0, sigmaZ
, scaleZ
, upperSkewZ
) / 4)
392 x
.append(scaleX
[0] / 4)
394 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 4)
398 y
.append(skewedGauss(0, sigmaY
, scaleY
, upperSkewY
) / 4)
402 z
.append(skewedGauss(0, sigmaZ
, scaleZ
, upperSkewZ
) / 4)
405 x
.append(scaleX
[0] * 0.25)
407 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) * 0.25)
409 y
.append(scaleY
[0] * 0.433)
411 y
.append(skewedGauss(muY
, sigmaY
, scaleY
, upperSkewY
) * 0.433)
413 z
.append(scaleZ
[0] / 2)
415 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
418 x
.append(scaleX
[0] / 4)
420 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 4)
424 y
.append(skewedGauss(0, sigmaY
, scaleY
, upperSkewY
) / 2)
426 z
.append(scaleZ
[0] / 2)
428 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
431 x
.append(scaleX
[0] * 0.10825)
433 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) * 0.10825)
435 y
.append(scaleY
[0] * 0.2165)
437 y
.append(skewedGauss(muY
, sigmaY
, scaleY
, upperSkewY
) * 0.2165)
439 z
.append(scaleZ
[0] / 2)
441 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
444 x
.append(scaleX
[0] / 2)
446 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 2)
450 y
.append(skewedGauss(0, sigmaY
, scaleY
, upperSkewY
) / 4)
452 z
.append(scaleZ
[0] / 2)
454 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
459 x
.append(scaleX
[0] / 2)
461 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 2)
463 y
.append(scaleY
[0] / 2)
465 y
.append(skewedGauss(muY
, sigmaY
, scaleY
, upperSkewY
) / 2)
467 z
.append(scaleZ
[0] / 2)
469 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
472 x
.append(scaleX
[0] / 2)
474 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 2)
478 y
.append(skewedGauss(0, sigmaY
, scaleY
, upperSkewY
) / 2)
480 z
.append(scaleZ
[0] / 2)
482 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
485 if j
in [1, 3, 4, 5, 8, 9]:
487 x
.append(scaleX
[0] / 2)
489 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 2)
491 y
.append(scaleY
[0] / 2)
493 y
.append(skewedGauss(muY
, sigmaY
, scaleY
, upperSkewY
) / 2)
495 z
.append(scaleZ
[0] / 2)
497 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
500 x
.append(scaleX
[0] / 2)
502 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 2)
506 y
.append(skewedGauss(0, sigmaY
, scaleY
, upperSkewY
) / 2)
508 z
.append(scaleZ
[0] / 2)
510 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
514 x
.append(scaleX
[0] / 2)
516 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 2)
518 y
.append(scaleY
[0] / 2)
520 y
.append(skewedGauss(muY
, sigmaY
, scaleY
, upperSkewY
) / 2)
522 z
.append(scaleZ
[0] / 2)
524 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
528 x
.append(scaleX
[0] / 2)
530 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 2)
532 y
.append(scaleY
[0] / 2)
534 y
.append(skewedGauss(muY
, sigmaY
, scaleY
, upperSkewY
) / 2)
536 z
.append(scaleZ
[0] / 2)
538 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
542 x
.append(scaleX
[0] / 2)
544 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 2)
546 y
.append(scaleY
[0] / 2)
548 y
.append(skewedGauss(muY
, sigmaY
, scaleY
, upperSkewY
) / 2)
550 z
.append(scaleZ
[0] / 2)
552 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
556 x
.append(scaleX
[0] / 2)
558 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 2)
560 y
.append(scaleY
[0] / 2)
562 y
.append(skewedGauss(muY
, sigmaY
, scaleY
, upperSkewY
) / 2)
564 z
.append(scaleZ
[0] / 2)
566 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
568 # This is for scaling the displacement textures.
569 # Scale the vertices so that their average is equal to 1 * scale factor.
571 averageX
= (sum(x
) / len(x
)) * scale_fac
[0]
572 for i
in range(len(x
)):
574 averageY
= (sum(y
) / len(y
)) * scale_fac
[1]
575 for i
in range(len(y
)):
577 averageZ
= (sum(z
) / len(z
)) * scale_fac
[2]
578 for i
in range(len(z
)):
581 # Build vertex and face arrays:
583 verts
= [(-x
[0], -y
[0], -z
[0]), (x
[1], -y
[1], -z
[1]), (x
[2], -y
[2], z
[2]),
584 (-x
[3], y
[3], -z
[3]), (x
[4], y
[4], -z
[4]), (x
[5], y
[5], z
[5]),
585 (x
[6], y
[6], z
[6]), (x
[7], y
[7], -z
[7])]
586 faces
= [[0, 1, 2], [0, 1, 7], [3, 0, 7], [3, 4, 7], [1, 4, 7], [3, 4, 5], [1, 2, 6],
587 [1, 4, 6], [4, 5, 6], [0, 2, 6], [0, 3, 6], [3, 5, 6]]
589 verts
= [(-x
[0], y
[0], -z
[0]), (x
[1], -y
[1], -z
[1]), (x
[2], y
[2], -z
[2]),
590 (-x
[3], y
[3], -z
[3]), (-x
[4], -y
[4], z
[4]), (x
[5], y
[5], z
[5]),
591 (x
[6], y
[6], z
[6]), (-x
[7], y
[7], z
[7])]
592 faces
= [[0, 1, 2], [0, 2, 3], [0, 3, 7], [0, 7, 4], [1, 4, 5], [0, 1, 4], [5, 1, 2],
593 [5, 2, 6], [3, 2, 6], [3, 6, 7], [5, 4, 7], [5, 6, 7]]
595 verts
= [(x
[0], y
[0], z
[0]), (x
[1], -y
[1], -z
[1]), (x
[2], y
[2], -z
[2]),
596 (-x
[3], y
[3], -z
[3]), (x
[4], -y
[4], z
[4]), (x
[5], y
[5], z
[5]),
597 (-x
[6], y
[6], z
[6]), (-x
[7], -y
[7], z
[7])]
598 faces
= [[0, 1, 2], [0, 2, 3], [0, 3, 6], [0, 6, 7], [0, 7, 4], [0, 4, 1], [5, 4, 1, 2],
599 [5, 6, 3, 2], [5, 4, 7, 6]]
601 verts
= [(x
[0], y
[0], z
[0]), (x
[1], -y
[1], -z
[1]), (x
[2], y
[2], -z
[2]),
602 (-x
[3], y
[3], -z
[3]), (-x
[4], -y
[4], -z
[4]), (x
[5], -y
[5], -z
[5]),
603 (x
[6], y
[6], -z
[6]), (x
[7], y
[7], -z
[7]), (-x
[8], y
[8], -z
[8]),
605 faces
= [[0, 1, 6], [0, 6, 2], [0, 2, 7], [0, 7, 3], [0, 3, 8], [0, 8, 4], [0, 4, 5],
606 [0, 5, 1], [1, 9, 2], [2, 9, 3], [3, 9, 4], [4, 9, 1], [1, 6, 2], [2, 7, 3],
607 [3, 8, 4], [4, 5, 1]]
609 verts
= [(x
[0], y
[0], z
[0]), (x
[1], -y
[1], z
[1]), (x
[2], y
[2], z
[2]),
610 (-x
[3], y
[3], z
[3]), (x
[4], -y
[4], -z
[4]), (x
[5], y
[5], -z
[5]),
611 (x
[6], y
[6], -z
[6]), (-x
[7], y
[7], -z
[7]), (-x
[8], y
[8], -z
[8]),
612 (-x
[9], -y
[9], -z
[9])]
613 faces
= [[0, 1, 2], [0, 2, 3], [0, 3, 1], [1, 4, 5], [1, 5, 2], [2, 5, 6], [2, 6, 7],
614 [2, 7, 3], [3, 7, 8], [3, 8, 9], [3, 9, 1], [1, 9, 4], [4, 5, 9], [5, 6, 7],
615 [7, 8, 9], [9, 5, 7]]
617 verts
= [(x
[0], y
[0], z
[0]), (x
[1], -y
[1], -z
[1]), (x
[2], y
[2], -z
[2]),
618 (-x
[3], y
[3], -z
[3]), (-x
[4], y
[4], z
[4]), (-x
[5], -y
[5], z
[5]),
619 (-x
[6], -y
[6], -z
[6])]
620 faces
= [[0, 1, 2], [0, 2, 3, 4], [0, 1, 6, 5], [0, 4, 5], [1, 2, 3, 6], [3, 4, 5, 6]]
622 verts
= [(x
[0], y
[0], z
[0]), (x
[1], -y
[1], -z
[1]), (x
[2], y
[2], -z
[2]),
623 (x
[3], y
[3], -z
[3]), (-x
[4], y
[4], -z
[4]), (-x
[5], y
[5], z
[5]),
624 (-x
[6], y
[6], z
[6]), (-x
[7], y
[7], -z
[7]), (-x
[8], -y
[8], -z
[8]),
625 (-x
[9], -y
[9], z
[9])]
626 faces
= [[0, 1, 2], [0, 2, 3], [0, 5, 6], [0, 6, 9], [0, 1, 8, 9], [0, 3, 4, 5],
627 [1, 2, 7, 8], [2, 3, 4, 7], [4, 5, 6, 7], [6, 7, 8, 9]]
629 verts
= [(x
[0], y
[0], z
[0]), (x
[1], -y
[1], -z
[1]), (x
[2], y
[2], -z
[2]),
630 (-x
[3], y
[3], -z
[3]), (-x
[4], -y
[4], -z
[4]), (-x
[5], -y
[5], z
[5]),
632 faces
= [[0, 2, 1], [0, 1, 4], [0, 4, 5], [0, 5, 6], [0, 6, 3, 2], [2, 1, 4, 3],
635 verts
= [(-x
[0], -y
[0], -z
[0]), (-x
[1], y
[1], -z
[1]), (-x
[2], y
[2], z
[2]),
636 (-x
[3], -y
[3], z
[3]), (x
[4], -y
[4], -z
[4]), (x
[5], y
[5], -z
[5]),
637 (x
[6], y
[6], z
[6]), (x
[7], -y
[7], z
[7])]
638 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]]
640 verts
= [(-x
[0], -y
[0], -z
[0]), (-x
[1], y
[1], -z
[1]), (-x
[2], y
[2], z
[2]),
641 (x
[3], -y
[3], z
[3]), (x
[4], y
[4], z
[4]), (x
[5], y
[5], -z
[5]),
642 (x
[6], -y
[6], -z
[6])]
643 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]]
645 verts
= [(-x
[0], -y
[0], -z
[0]), (-x
[1], y
[1], -z
[1]), (-x
[2], y
[2], z
[2]),
646 (x
[3], -y
[3], z
[3]), (x
[4], y
[4], z
[4]), (x
[5], y
[5], -z
[5]),
647 (x
[6], -y
[6], -z
[6])]
648 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]]
650 verts
= [(-x
[0], -y
[0], -z
[0]), (-x
[1], y
[1], -z
[1]), (-x
[2], -y
[2], z
[2]),
651 (-x
[3], y
[3], z
[3]), (x
[4], -y
[4], -z
[4]), (x
[5], y
[5], -z
[5]),
652 (x
[6], -y
[6], z
[6]), (x
[7], y
[7], z
[7])]
653 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]]
655 # name = "Rock." + str(base + shift).zfill(3)
659 obj
= createMeshObject(context
, verts
, [], faces
, name
)
662 # bpy.data.objects[name].scale = Vector((averageX, averageY, averageZ))
663 obj
.scale
= Vector((averageX
, averageY
, averageZ
))
665 # For a slight speed bump / Readability:
666 # mesh = bpy.data.meshes[name]
672 # todo: "0.375 / 3"? WTF? That = 0.125. . . .
673 # *** Completed 7/15/2011: Changed second one ***
674 mesh
.edges
[i
].crease
= gauss(0.125, 0.125)
677 mesh
.edges
[i
].crease
= gauss(0.5, 0.125)
678 for i
in [6, 9, 11, 12]:
679 mesh
.edges
[i
].crease
= gauss(0.25, 0.05)
680 for i
in [5, 7, 15, 16]:
681 mesh
.edges
[i
].crease
= gauss(0.125, 0.025)
684 mesh
.edges
[i
].crease
= gauss(0.125, 0.025)
686 for i
in [0, 1, 6, 10, 13]:
687 mesh
.edges
[i
].crease
= gauss(0.25, 0.05)
688 mesh
.edges
[8].crease
= gauss(0.5, 0.125)
690 for i
in [5, 6, 7, 10, 14, 16, 19, 21]:
691 mesh
.edges
[i
].crease
= gauss(0.5, 0.125)
694 if i
in [0, 1, 2, 3, 6, 7, 8, 9, 13, 16]:
695 mesh
.edges
[i
].crease
= gauss(0.5, 0.125)
697 mesh
.edges
[i
].crease
= gauss(0.25, 0.05)
699 mesh
.edges
[i
].crease
= gauss(0.125, 0.025)
702 if i
in [0, 3, 8, 9, 10]:
703 mesh
.edges
[i
].crease
= gauss(0.5, 0.125)
705 mesh
.edges
[i
].crease
= gauss(0.25, 0.05)
707 mesh
.edges
[i
].crease
= gauss(0.125, 0.025)
710 if i
in [0, 3, 4, 11]:
711 mesh
.edges
[i
].crease
= gauss(0.5, 0.125)
713 mesh
.edges
[i
].crease
= gauss(0.25, 0.05)
716 if i
in [0, 2, 3, 4, 8, 11]:
717 mesh
.edges
[i
].crease
= gauss(0.5, 0.125)
719 mesh
.edges
[i
].crease
= gauss(0.25, 0.05)
721 mesh
.edges
[i
].crease
= gauss(0.125, 0.025)
724 if i
in [1, 2, 3, 4, 8, 11]:
725 mesh
.edges
[i
].crease
= gauss(0.25, 0.05)
727 mesh
.edges
[i
].crease
= gauss(0.125, 0.025)
732 # Creates rock objects:
733 def generateRocks(context
, scaleX
, skewX
, scaleY
, skewY
, scaleZ
, skewZ
,
734 scale_fac
, detail
, display_detail
, deform
, rough
,
735 smooth_fac
, smooth_it
,
736 numOfRocks
=1, userSeed
=1.0,
737 scaleDisplace
=False, randomSeed
=True, use_enter_edit_mode
=False):
748 # Seed the random Gaussian value generator:
750 seed(int(time
.time()))
754 # These values need to be really small to look good.
755 # So the user does not have to use such ridiculously small values:
759 # Verify that the min really is the min:
760 if scaleX
[1] < scaleX
[0]:
761 scaleX
[0], scaleX
[1] = scaleX
[1], scaleX
[0]
762 if scaleY
[1] < scaleY
[0]:
763 scaleY
[0], scaleY
[1] = scaleY
[1], scaleY
[0]
764 if scaleZ
[1] < scaleZ
[0]:
765 scaleZ
[0], scaleZ
[1] = scaleZ
[1], scaleZ
[0]
767 # todo: edit below to allow for skewing the distribution
768 # *** todo completed 4/22/2011 ***
769 # *** Code now generating "int not scriptable error" in Blender ***
771 # Calculate mu and sigma for a Gaussian distributed random number
773 # If the lower and upper bounds are the same, skip the math.
775 # sigma is the standard deviation of the values. The 95% interval is three
776 # standard deviations, which is what we want most generated values to fall
777 # in. Since it might be skewed we are going to use half the difference
778 # between the mean and the furthest bound and scale the other side down
779 # post-number generation.
780 if scaleX
[0] != scaleX
[1]:
781 skewX
= (skewX
+ 1) / 2
782 muX
= scaleX
[0] + ((scaleX
[1] - scaleX
[0]) * skewX
)
784 sigmaX
= (scaleX
[1] - muX
) / 3
786 sigmaX
= (muX
- scaleX
[0]) / 3
790 if scaleY
[0] != scaleY
[1]:
791 skewY
= (skewY
+ 1) / 2
792 muY
= scaleY
[0] + ((scaleY
[1] - scaleY
[0]) * skewY
)
794 sigmaY
= (scaleY
[1] - muY
) / 3
796 sigmaY
= (muY
- scaleY
[0]) / 3
800 if scaleZ
[0] != scaleZ
[1]:
801 skewZ
= (skewZ
+ 1) / 2
802 muZ
= scaleZ
[0] + ((scaleZ
[1] - scaleZ
[0]) * skewZ
)
804 sigmaZ
= (scaleZ
[1] - muZ
) / 3
806 sigmaZ
= (muZ
- scaleZ
[0]) / 3
813 for i
in range(numOfRocks
):
814 # todo: enable different random values for each (x,y,z) corrdinate for
815 # each vertex. This will add additional randomness to the shape of the
817 # *** todo completed 4/19/2011 ***
818 # *** Code is notably slower at high rock counts ***
820 # name = generateObject(context, muX, sigmaX, scaleX, upperSkewX, muY,
821 rock
= generateObject(
822 context
, muX
, sigmaX
, scaleX
, upperSkewX
, muY
,
823 sigmaY
, scaleY
, upperSkewY
, muZ
, sigmaZ
, scaleZ
,
824 upperSkewZ
, i
, LASTROCK
, scaleDisplace
, scale_fac
)
826 # rock = bpy.data.objects[name]
828 # todo Map what the two new textures will be:
829 # This is not working. It works on paper so . . . ???
830 # *** todo completed on 4/23/2011 ***
831 # *** todo re-added as the first rock is getting
832 # 'Texture.001' twice. ***
833 # *** todo completed on 4/25/2011 ***
834 # *** Script no longer needs to map new texture names 9/6/2011 ***
836 # Create the four new textures:
837 # todo Set displacement texture parameters:
838 # *** todo completed on 5/31/2011 ***
839 # Voronoi has been removed from being an option for the fine detail
841 texTypes
= ['CLOUDS', 'MUSGRAVE', 'DISTORTED_NOISE', 'STUCCI', 'VORONOI']
843 # The first texture is to give a more ranodm base shape appearance:
844 newTex
.append(bpy
.data
.textures
.new(
845 name
='rock_displacement',
847 randomizeTexture(newTex
[0], 0)
848 newTex
.append(bpy
.data
.textures
.new(
849 name
='rock_displacement',
851 randomizeTexture(newTex
[1], 0)
853 newTex
.append(bpy
.data
.textures
.new(
854 name
='rock_displacement',
855 type=texTypes
[int(round(weibull(1, 1)[0] / 2.125))]))
856 randomizeTexture(newTex
[2], 1)
857 newTex
.append(bpy
.data
.textures
.new(
858 name
='rock_displacement',
859 type=texTypes
[int(round(weibull(1, 1)[0] / 2.125))]))
860 randomizeTexture(newTex
[3], 2)
862 newTex
.append(bpy
.data
.textures
.new(
863 name
='rock_displacement',
864 type=texTypes
[int(round(weibull(1, 1) / 2.125))]))
865 randomizeTexture(newTex
[2], 1)
866 newTex
.append(bpy
.data
.textures
.new(
867 name
='rock_displacement',
868 type=texTypes
[int(round(weibull(1, 1) / 2.125))]))
869 randomizeTexture(newTex
[3], 2)
872 rock
.modifiers
.new(name
="Subsurf", type='SUBSURF')
873 rock
.modifiers
.new(name
="Subsurf", type='SUBSURF')
874 rock
.modifiers
.new(name
="Displace", type='DISPLACE')
875 rock
.modifiers
.new(name
="Displace", type='DISPLACE')
876 rock
.modifiers
.new(name
="Displace", type='DISPLACE')
877 rock
.modifiers
.new(name
="Displace", type='DISPLACE')
879 # If smoothing is enabled, allow a little randomness into the
880 # smoothing factor. Then add the smoothing modifier.
881 if smooth_fac
> 0.0 and smooth_it
> 0:
882 rock
.modifiers
.new(name
="Smooth", type='SMOOTH')
883 rock
.modifiers
[6].factor
= gauss(smooth_fac
, (smooth_fac
** 0.5) / 12)
884 rock
.modifiers
[6].iterations
= smooth_it
885 # Make a call to random to keep things consistent:
889 # Set subsurf modifier parameters:
890 rock
.modifiers
[0].levels
= display_detail
891 rock
.modifiers
[0].render_levels
= detail
892 rock
.modifiers
[1].levels
= display_detail
893 rock
.modifiers
[1].render_levels
= detail
895 # todo Set displacement modifier parameters:
896 # *** todo completed on 4/23/2011 ***
897 # *** toned down the variance on 4/26/2011 ***
898 # *** added third modifier on 4/28/2011 ***
899 # *** texture access changed on 9/6/2011 ***
900 rock
.modifiers
[2].texture
= newTex
[0]
901 rock
.modifiers
[2].strength
= gauss(deform
/ 100, (1 / 300) * deform
)
902 rock
.modifiers
[2].mid_level
= 0
903 rock
.modifiers
[3].texture
= newTex
[1]
904 rock
.modifiers
[3].strength
= gauss(deform
, (1 / 3) * deform
)
905 rock
.modifiers
[3].mid_level
= 0
906 rock
.modifiers
[4].texture
= newTex
[2]
907 rock
.modifiers
[4].strength
= gauss(rough
* 2, (1 / 3) * rough
)
908 rock
.modifiers
[5].texture
= newTex
[3]
909 rock
.modifiers
[5].strength
= gauss(rough
, (1 / 3) * rough
)
911 # Set mesh to be smooth and fix the normals:
912 utils
.smooth(rock
.data
)
913 # utils.smooth(bpy.data.meshes[name])
914 bpy
.ops
.object.editmode_toggle()
915 bpy
.ops
.mesh
.normals_make_consistent()
916 bpy
.ops
.object.editmode_toggle()
918 if use_enter_edit_mode
:
919 for m
in rock
.modifiers
:
920 m
.show_in_editmode
= True
921 m
.show_on_cage
= True
923 # Store the last value of i:
928 # Add the shift to LASTROCK:
929 LASTROCK
+= shift
+ 1
934 # Much of the code below is more-or-less imitation of other addons and as such
935 # I have left it undocumented.
937 class OBJECT_OT_add_mesh_rock(bpy
.types
.Operator
):
938 """Add rock objects"""
939 bl_idname
= "mesh.add_mesh_rock"
940 bl_label
= "Add Rocks"
941 bl_options
= {'REGISTER', 'UNDO'}
942 bl_description
= "Add rocks"
944 # Get the preset values from the XML file.
945 # -> The script was morphed into a Python module
947 # Tell settings.py to parse the XML file with the settings.
948 # Then get the default values resulting from the parsing.
949 # Make a list containing the default values and append to that
950 # the presets specified in the same XML file. This list will
951 # be used to load preset values.
953 defaults
= settings
.getDefault()
954 presetsList
= [defaults
]
955 presetsList
+= settings
.getPresetLists()
959 # Build the presets list for the enum property.
960 # This needs to be a for loop as the user might add presets to
961 # the XML file and those should show here:
962 for i
, preset
in enumerate(presetsList
):
963 presets
.append((str(i
), preset
[0], preset
[0] + " preset values"))
965 preset_values
: EnumProperty(
968 description
="Preset values for some rock types")
970 num_of_rocks
: IntProperty(
971 name
="Number of rocks",
972 description
="Number of rocks to generate. WARNING: Slow at high values!",
977 scale_X
: FloatVectorProperty(
979 description
="X axis scaling range",
980 min=0.0, max=256.0, step
=1,
981 default
=defaults
[1], size
=2)
982 skew_X
: FloatProperty(
984 description
="X Skew ratio. 0.5 is no skew",
985 min=-1.0, max=1.0, default
=defaults
[4])
986 scale_Y
: FloatVectorProperty(
988 description
="Y axis scaling range",
989 min=.0, max=256.0, step
=1,
990 default
=defaults
[2], size
=2)
991 skew_Y
: FloatProperty(
993 description
="Y Skew ratio. 0.5 is no skew",
994 min=-1.0, max=1.0, default
=defaults
[5])
995 scale_Z
: FloatVectorProperty(
997 description
="Z axis scaling range",
998 min=0.0, max=256.0, step
=1,
999 default
=defaults
[3], size
=2)
1000 skew_Z
: FloatProperty(
1002 description
="Z Skew ratio. 0.5 is no skew",
1003 min=-1.0, max=1.0, default
=defaults
[6])
1004 use_scale_dis
: BoolProperty(
1005 name
="Scale displace textures",
1006 description
="Scale displacement textures with dimensions. May cause stretched textures",
1007 default
=defaults
[7])
1008 scale_fac
: FloatVectorProperty(
1009 name
="Scaling Factor",
1010 description
="XYZ scaling factor. 1: no scaling",
1011 min=0.0001, max=256.0, step
=0.1,
1012 default
=defaults
[8], size
=3)
1014 # @todo Possible to title this section "Physical Properties:"?
1015 deform
: FloatProperty(
1017 description
="Rock deformation",
1018 min=0.0, soft_max
=50, max=1024.0, default
=defaults
[9])
1019 rough
: FloatProperty(
1021 description
="Rock roughness",
1022 min=0.0, soft_max
=50, max=1024.0, default
=defaults
[10])
1023 detail
: IntProperty(
1024 name
="Detail level",
1025 description
="Detail level. WARNING: Slow at high values!",
1026 min=1, soft_max
=4, max=10, default
=defaults
[11])
1027 display_detail
: IntProperty(
1028 name
="Display Detail",
1029 description
="Display detail. Use a lower value for high numbers of rocks",
1030 min=1, soft_max
=4, max=10, default
=defaults
[12])
1031 smooth_fac
: FloatProperty(
1032 name
="Smooth Factor",
1033 description
="Smoothing factor. A value of 0 disables",
1034 min=0.0, max=128.0, default
=defaults
[13])
1035 smooth_it
: IntProperty(
1036 name
="Smooth Iterations",
1037 description
="Smoothing iterations. A value of 0 disables",
1038 min=0, max=50, default
=defaults
[14])
1040 use_generate
: BoolProperty(
1041 name
="Generate Rocks",
1042 description
="Enable actual generation",
1043 default
=defaults
[15])
1044 use_random_seed
: BoolProperty(
1045 name
="Use a random seed",
1046 description
="Create a seed based on time. Causes user seed to be ignored",
1047 default
=defaults
[16])
1048 user_seed
: IntProperty(
1050 description
="Use a specific seed for the generator",
1051 min=0, max=1048576, default
=defaults
[17])
1053 def draw(self
, context
):
1054 layout
= self
.layout
1056 box
.prop(self
, 'num_of_rocks')
1058 box
.prop(self
, 'scale_X')
1059 box
.prop(self
, 'skew_X')
1060 box
.prop(self
, 'scale_Y')
1061 box
.prop(self
, 'skew_Y')
1062 box
.prop(self
, 'scale_Z')
1063 box
.prop(self
, 'skew_Z')
1064 box
.prop(self
, 'use_scale_dis')
1065 if self
.use_scale_dis
:
1066 box
.prop(self
, 'scale_fac')
1068 self
.scale_fac
= utils
.toFloats(self
.defaults
[8])
1070 box
.prop(self
, 'deform')
1071 box
.prop(self
, 'rough')
1072 box
.prop(self
, 'detail')
1073 box
.prop(self
, 'display_detail')
1074 box
.prop(self
, 'smooth_fac')
1075 box
.prop(self
, 'smooth_it')
1078 box
.prop(self
, 'use_generate')
1079 box
.prop(self
, 'use_random_seed')
1080 if not self
.use_random_seed
:
1081 box
.prop(self
, 'user_seed')
1082 box
.prop(self
, 'preset_values')
1084 def execute(self
, context
):
1085 # turn off 'Enter Edit Mode'
1086 use_enter_edit_mode
= bpy
.context
.preferences
.edit
.use_enter_edit_mode
1087 bpy
.context
.preferences
.edit
.use_enter_edit_mode
= False
1089 # The following "if" block loads preset values:
1090 if self
.lastPreset
!= int(self
.preset_values
):
1091 self
.scale_X
= utils
.toFloats(self
.presetsList
[int(self
.preset_values
)][1])
1092 self
.scale_Y
= utils
.toFloats(self
.presetsList
[int(self
.preset_values
)][2])
1093 self
.scale_Z
= utils
.toFloats(self
.presetsList
[int(self
.preset_values
)][3])
1094 self
.skew_X
= float(self
.presetsList
[int(self
.preset_values
)][4])
1095 self
.skew_Y
= float(self
.presetsList
[int(self
.preset_values
)][5])
1096 self
.skew_Z
= float(self
.presetsList
[int(self
.preset_values
)][6])
1097 self
.use_scale_dis
= bool(self
.presetsList
[int(self
.preset_values
)][7])
1098 self
.scale_fac
= utils
.toFloats(self
.presetsList
[int(self
.preset_values
)][8])
1099 self
.deform
= float(self
.presetsList
[int(self
.preset_values
)][9])
1100 self
.rough
= float(self
.presetsList
[int(self
.preset_values
)][10])
1101 self
.detail
= int(self
.presetsList
[int(self
.preset_values
)][11])
1102 self
.display_detail
= int(self
.presetsList
[int(self
.preset_values
)][12])
1103 self
.smooth_fac
= float(self
.presetsList
[int(self
.preset_values
)][13])
1104 self
.smooth_it
= int(self
.presetsList
[int(self
.preset_values
)][14])
1105 self
.use_generate
= bool(self
.presetsList
[int(self
.preset_values
)][15])
1106 self
.use_random_seed
= bool(self
.presetsList
[int(self
.preset_values
)][16])
1107 self
.user_seed
= int(self
.presetsList
[int(self
.preset_values
)][17])
1108 self
.lastPreset
= int(self
.preset_values
)
1110 # todo Add deform, deform_Var, rough, and rough_Var:
1111 # *** todo completed 4/23/2011 ***
1112 # *** Eliminated "deform_Var" and "rough_Var" so the script is not
1113 # as complex to use. May add in again as advanced features. ***
1114 if self
.use_generate
:
1115 rocks
= generateRocks(context
,
1124 self
.display_detail
,
1132 self
.use_random_seed
,
1133 use_enter_edit_mode
)
1136 rock
.select_set(True)
1138 if use_enter_edit_mode
:
1139 bpy
.ops
.object.mode_set(mode
= 'EDIT')
1141 # restore pre operator state
1142 bpy
.context
.preferences
.edit
.use_enter_edit_mode
= use_enter_edit_mode
1147 def menu_func_rocks(self
, context
):
1148 layout
= self
.layout
1151 OBJECT_OT_add_mesh_rock
.bl_idname
,
1152 text
="Rock Generator",
1153 icon
="MESH_ICOSPHERE")
1157 OBJECT_OT_add_mesh_rock
,
1162 from bpy
.utils
import register_class
1165 bpy
.types
.VIEW3D_MT_mesh_add
.append(menu_func_rocks
)
1169 from bpy
.utils
import unregister_class
1170 for cls
in reversed(classes
):
1171 unregister_class(cls
)
1172 bpy
.types
.VIEW3D_MT_mesh_add
.remove(menu_func_rocks
)
1175 if __name__
== "__main__":