1 # SPDX-FileCopyrightText: 2019-2023 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 # Blender rock creation tool
7 # Based on BlenderGuru's asteroid tutorial and personal experimentation.
8 # Tutorial: http://www.blenderguru.com/how-to-make-a-realistic-asteroid/
9 # Update with another tutorial shared by "rusted" of BlenderArtists:
10 # Tutorial: http://saschahenrichs.blogspot.com/2010/03/3dsmax-environment-modeling-1.html
12 # Uses the NumPy Gaussian random number generator to generate a
13 # a rock within a given range and give some randomness to the displacement
14 # texture values. NumPy's gaussian generator was chosen as, based on
15 # profiling I performed, it runs in about half the time as the built in
16 # Python gaussian equivalent. I would like to shift the script to use the
17 # NumPy beta distribution as it ran in about half the time as the NumPy
18 # gaussian once the skew calculations are added.
20 # Set lower and upper bounds to the same for no randomness.
23 # Generate meshes with random scaling between given values.
24 # - Allow for a skewed distribution
25 # *** Completed on 4/17/2011 ***
26 # - Create a set of meshes that can be used
27 # Give the user the ability to set the subsurf level (detail level)
28 # *** Completed on 4/29/2011 ***
29 # - Set subsurf modifiers to default at view:3, render:3.
30 # *** Completed on 4/17/2011 ***
31 # - Set crease values to allow for hard edges on first subsurf.
32 # *** Completed on 4/29/2011 ***
33 # Be able to generate and add a texture to the displacement modifiers.
34 # *** Completed 5/17/2011 ***
35 # - Generate three displacement modifiers.
36 # - The first only uses a Musgrave for initial intentations.
37 # *** Now generating four displacement modifiers ***
38 # *** Completed on 5/17/2011 ***
39 # - Set a randomness for the type and values of the displacement texture.
40 # *** Completed 5/9/2011 ***
41 # - Allow the user to set a value for the range of displacement.
42 # -> Modification: have user set "roughness" and "roughness range".
43 # *** Compleded on 4/23/2011 ***
44 # Set material settings and assign material textures
45 # *** Completed 6/9/2011 ***
46 # - Mossiness of the rocks.
47 # *** Completed 6/9/2011 ***
48 # - Color of the rocks.
49 # *** Completed 5/16/2011 ***
50 # - Wetness/shinyness of the rock.
51 # *** Completed 5/6/2011 ***
52 # - For all the user provides a mean value for a skewed distribution.
53 # *** Removed to lessen usage complexity ***
54 # Add some presets (mesh) to make it easier to use
55 # - Examples: river rock, asteroid, quaried rock, etc
56 # *** Completed 7/12/2011 ***
59 # Remove all "bpy.ops" operations with "bpy.data" base operations.
60 # Remove material/texture cataloging with building a list of
61 # returned values from bpy.data.*.new() operations.
62 # *** Completed on 9/6/2011 ***
63 # Search for places where list comprehensions can be used.
64 # Look for alternate methods
65 # - Possible alternate and more efficient data structures
66 # - Possible alternate algorithms may realize greater performance
67 # - Look again at multi-processing. Without bpy.ops is might
71 # Multi-thread the script
72 # *** Will not be implemented. Multi-processing is adding to much
73 # overhead to realize a performance increase ***
74 # - Learn basic multi-threading in Python (multiprocessing)
75 # - Break material generation into separate threads (processes)
76 # - Break mesh generation into separate threads (processes)
77 # - Move name generation, texture ID generation, etc to process first
78 # - Roll version to 2.0 on completion
80 # Paul "BrikBot" Marshall
81 # Created: April 17, 2011
82 # Last Modified: November 17, 2011
83 # Homepage (blog): http://post.darkarsenic.com/
84 # //blog.darkarsenic.com/
85 # Thanks to Meta-Androco, RickyBlender, Ace Dragon, and PKHG for ideas
88 # Coded in IDLE, tested in Blender 2.59. NumPy Recommended.
89 # Search for "@todo" to quickly find sections that need work.
92 # Functional code comes before fast code. Once it works, then worry about
93 # 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 # Update mesh geometry after adding stuff.
156 if bpy
.context
.mode
== "EDIT_MESH":
157 bpy
.ops
.object.mode_set(mode
='OBJECT')
159 return object_utils
.object_data_add(context
, mesh
, operator
=None)
162 # Generates an object based on one of several different mesh types.
163 # All meshes have exactly eight vertices, and may be built from either
166 # param: muX - mean X offset value
167 # sigmaX - X offset standard deviation
168 # scaleX - X upper and lower bounds
169 # upperSkewX - Is the distribution upperskewed?
170 # muY - mean Y offset value
171 # sigmaY - Y offset standard deviation
172 # scaleY - Y upper and lower bounds
173 # upperSkewY - Is the distribution upperskewed?
174 # muZ - mean Z offset value
175 # sigmaZ - Z offset standard deviation
176 # scaleZ - Z upper and lower bounds
177 # upperSkewY - Is the distribution upperskewed?
178 # base - base number on the end of the object name
179 # shift - Addition to the base number for multiple runs.
180 # scaleDisplace - Scale the displacement maps
182 # return: name - the built name of the object
183 def generateObject(context
, muX
, sigmaX
, scaleX
, upperSkewX
, muY
, sigmaY
,
184 scaleY
, upperSkewY
, muZ
, sigmaZ
, scaleZ
, upperSkewZ
, base
,
185 shift
, scaleDisplace
, scale_fac
):
189 shape
= randint(0, 11)
192 # Use parameters to re-scale cube:
193 # Reversed if/for nesting. Should be a little faster.
197 x
.append(scaleX
[0] / 2)
199 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 2)
201 y
.append(scaleY
[0] / 2)
203 y
.append(skewedGauss(muY
, sigmaY
, scaleY
, upperSkewY
) / 2)
205 z
.append(scaleZ
[0] / 2)
207 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
210 if j
in [0, 1, 3, 4]:
212 x
.append(scaleX
[0] / 2)
214 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 2)
216 y
.append(scaleY
[0] / 2)
218 y
.append(skewedGauss(muY
, sigmaY
, scaleY
, upperSkewY
) / 2)
220 z
.append(scaleZ
[0] / 2)
222 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
227 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 4)
229 y
.append(scaleY
[0] / 2)
231 y
.append(skewedGauss(muY
, sigmaY
, scaleY
, upperSkewY
) / 2)
233 z
.append(scaleZ
[0] / 2)
235 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
240 x
.append(skewedGauss(0, sigmaX
, scaleX
, upperSkewX
) / 4)
244 y
.append(skewedGauss(0, sigmaY
, scaleY
, upperSkewY
) / 4)
246 z
.append(scaleZ
[0] / 2)
248 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
251 if j
in [0, 2, 5, 7]:
253 x
.append(scaleX
[0] / 4)
255 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 4)
259 y
.append(skewedGauss(0, sigmaY
, scaleY
, upperSkewY
) / 4)
261 z
.append(scaleZ
[0] / 2)
263 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 4)
264 elif j
in [1, 3, 4, 6]:
266 x
.append(scaleX
[0] / 2)
268 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 2)
270 y
.append(scaleY
[0] / 2)
272 y
.append(skewedGauss(muY
, sigmaY
, scaleY
, upperSkewY
) / 2)
274 z
.append(scaleZ
[0] / 2)
276 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
281 x
.append(scaleX
[0] / 2)
283 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 2)
285 y
.append(scaleY
[0] / 2)
287 y
.append(skewedGauss(muY
, sigmaY
, scaleY
, upperSkewY
) / 2)
289 z
.append(scaleZ
[0] / 2)
291 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
296 x
.append(skewedGauss(0, sigmaX
, scaleX
, upperSkewX
) / 8)
300 y
.append(skewedGauss(0, sigmaY
, scaleY
, upperSkewY
) / 8)
304 z
.append(skewedGauss(0, sigmaZ
, scaleZ
, upperSkewZ
) / 8)
311 x
.append(skewedGauss(0, sigmaX
, scaleX
, upperSkewX
) / 2)
315 y
.append(skewedGauss(0, sigmaY
, scaleY
, upperSkewY
) / 2)
317 z
.append(scaleZ
[0] / 2)
319 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
320 elif j
in [1, 2, 3, 4]:
322 x
.append(scaleX
[0] / 2)
324 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 2)
326 y
.append(scaleY
[0] / 2)
328 y
.append(skewedGauss(muY
, sigmaY
, scaleY
, upperSkewY
) / 2)
330 z
.append(scaleZ
[0] / 2)
332 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
337 x
.append(skewedGauss(0, sigmaX
, scaleX
, upperSkewX
) / 3)
339 y
.append(scaleY
[0] / 3)
341 y
.append(skewedGauss(muY
, sigmaY
, scaleY
, upperSkewY
) / 3)
345 z
.append(skewedGauss(0, sigmaZ
, scaleZ
, upperSkewZ
) / 6)
348 x
.append(scaleX
[0] / 3)
350 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 3)
354 y
.append(skewedGauss(0, sigmaY
, scaleY
, upperSkewY
) / 3)
358 z
.append(skewedGauss(0, sigmaZ
, scaleZ
, upperSkewZ
) / 6)
365 x
.append(skewedGauss(0, sigmaX
, scaleX
, upperSkewX
) / 8)
369 y
.append(skewedGauss(0, sigmaY
, scaleY
, upperSkewY
) / 8)
371 z
.append(scaleZ
[0] / 2)
373 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
376 x
.append(scaleZ
[0] * .125)
378 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) * 0.125)
380 y
.append(scaleZ
[0] * 0.2165)
382 y
.append(skewedGauss(muY
, sigmaY
, scaleY
, upperSkewY
) * 0.2165)
386 z
.append(skewedGauss(0, sigmaZ
, scaleZ
, upperSkewZ
) / 4)
389 x
.append(scaleX
[0] / 4)
391 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 4)
395 y
.append(skewedGauss(0, sigmaY
, scaleY
, upperSkewY
) / 4)
399 z
.append(skewedGauss(0, sigmaZ
, scaleZ
, upperSkewZ
) / 4)
402 x
.append(scaleX
[0] * 0.25)
404 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) * 0.25)
406 y
.append(scaleY
[0] * 0.433)
408 y
.append(skewedGauss(muY
, sigmaY
, scaleY
, upperSkewY
) * 0.433)
410 z
.append(scaleZ
[0] / 2)
412 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
415 x
.append(scaleX
[0] / 4)
417 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 4)
421 y
.append(skewedGauss(0, sigmaY
, scaleY
, upperSkewY
) / 2)
423 z
.append(scaleZ
[0] / 2)
425 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
428 x
.append(scaleX
[0] * 0.10825)
430 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) * 0.10825)
432 y
.append(scaleY
[0] * 0.2165)
434 y
.append(skewedGauss(muY
, sigmaY
, scaleY
, upperSkewY
) * 0.2165)
436 z
.append(scaleZ
[0] / 2)
438 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
441 x
.append(scaleX
[0] / 2)
443 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 2)
447 y
.append(skewedGauss(0, sigmaY
, scaleY
, upperSkewY
) / 4)
449 z
.append(scaleZ
[0] / 2)
451 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
456 x
.append(scaleX
[0] / 2)
458 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 2)
460 y
.append(scaleY
[0] / 2)
462 y
.append(skewedGauss(muY
, sigmaY
, scaleY
, upperSkewY
) / 2)
464 z
.append(scaleZ
[0] / 2)
466 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
469 x
.append(scaleX
[0] / 2)
471 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 2)
475 y
.append(skewedGauss(0, sigmaY
, scaleY
, upperSkewY
) / 2)
477 z
.append(scaleZ
[0] / 2)
479 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
482 if j
in [1, 3, 4, 5, 8, 9]:
484 x
.append(scaleX
[0] / 2)
486 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 2)
488 y
.append(scaleY
[0] / 2)
490 y
.append(skewedGauss(muY
, sigmaY
, scaleY
, upperSkewY
) / 2)
492 z
.append(scaleZ
[0] / 2)
494 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
497 x
.append(scaleX
[0] / 2)
499 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 2)
503 y
.append(skewedGauss(0, sigmaY
, scaleY
, upperSkewY
) / 2)
505 z
.append(scaleZ
[0] / 2)
507 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
511 x
.append(scaleX
[0] / 2)
513 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 2)
515 y
.append(scaleY
[0] / 2)
517 y
.append(skewedGauss(muY
, sigmaY
, scaleY
, upperSkewY
) / 2)
519 z
.append(scaleZ
[0] / 2)
521 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
525 x
.append(scaleX
[0] / 2)
527 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 2)
529 y
.append(scaleY
[0] / 2)
531 y
.append(skewedGauss(muY
, sigmaY
, scaleY
, upperSkewY
) / 2)
533 z
.append(scaleZ
[0] / 2)
535 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
539 x
.append(scaleX
[0] / 2)
541 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 2)
543 y
.append(scaleY
[0] / 2)
545 y
.append(skewedGauss(muY
, sigmaY
, scaleY
, upperSkewY
) / 2)
547 z
.append(scaleZ
[0] / 2)
549 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
553 x
.append(scaleX
[0] / 2)
555 x
.append(skewedGauss(muX
, sigmaX
, scaleX
, upperSkewX
) / 2)
557 y
.append(scaleY
[0] / 2)
559 y
.append(skewedGauss(muY
, sigmaY
, scaleY
, upperSkewY
) / 2)
561 z
.append(scaleZ
[0] / 2)
563 z
.append(skewedGauss(muZ
, sigmaZ
, scaleZ
, upperSkewZ
) / 2)
565 # This is for scaling the displacement textures.
566 # Scale the vertices so that their average is equal to 1 * scale factor.
568 averageX
= (sum(x
) / len(x
)) * scale_fac
[0]
569 for i
in range(len(x
)):
571 averageY
= (sum(y
) / len(y
)) * scale_fac
[1]
572 for i
in range(len(y
)):
574 averageZ
= (sum(z
) / len(z
)) * scale_fac
[2]
575 for i
in range(len(z
)):
578 # Build vertex and face arrays:
580 verts
= [(-x
[0], -y
[0], -z
[0]), (x
[1], -y
[1], -z
[1]), (x
[2], -y
[2], z
[2]),
581 (-x
[3], y
[3], -z
[3]), (x
[4], y
[4], -z
[4]), (x
[5], y
[5], z
[5]),
582 (x
[6], y
[6], z
[6]), (x
[7], y
[7], -z
[7])]
583 faces
= [[0, 1, 2], [0, 1, 7], [3, 0, 7], [3, 4, 7], [1, 4, 7], [3, 4, 5], [1, 2, 6],
584 [1, 4, 6], [4, 5, 6], [0, 2, 6], [0, 3, 6], [3, 5, 6]]
586 verts
= [(-x
[0], y
[0], -z
[0]), (x
[1], -y
[1], -z
[1]), (x
[2], y
[2], -z
[2]),
587 (-x
[3], y
[3], -z
[3]), (-x
[4], -y
[4], z
[4]), (x
[5], y
[5], z
[5]),
588 (x
[6], y
[6], z
[6]), (-x
[7], y
[7], z
[7])]
589 faces
= [[0, 1, 2], [0, 2, 3], [0, 3, 7], [0, 7, 4], [1, 4, 5], [0, 1, 4], [5, 1, 2],
590 [5, 2, 6], [3, 2, 6], [3, 6, 7], [5, 4, 7], [5, 6, 7]]
592 verts
= [(x
[0], y
[0], z
[0]), (x
[1], -y
[1], -z
[1]), (x
[2], y
[2], -z
[2]),
593 (-x
[3], y
[3], -z
[3]), (x
[4], -y
[4], z
[4]), (x
[5], y
[5], z
[5]),
594 (-x
[6], y
[6], z
[6]), (-x
[7], -y
[7], z
[7])]
595 faces
= [[0, 1, 2], [0, 2, 3], [0, 3, 6], [0, 6, 7], [0, 7, 4], [0, 4, 1], [5, 4, 1, 2],
596 [5, 6, 3, 2], [5, 4, 7, 6]]
598 verts
= [(x
[0], y
[0], z
[0]), (x
[1], -y
[1], -z
[1]), (x
[2], y
[2], -z
[2]),
599 (-x
[3], y
[3], -z
[3]), (-x
[4], -y
[4], -z
[4]), (x
[5], -y
[5], -z
[5]),
600 (x
[6], y
[6], -z
[6]), (x
[7], y
[7], -z
[7]), (-x
[8], y
[8], -z
[8]),
602 faces
= [[0, 1, 6], [0, 6, 2], [0, 2, 7], [0, 7, 3], [0, 3, 8], [0, 8, 4], [0, 4, 5],
603 [0, 5, 1], [1, 9, 2], [2, 9, 3], [3, 9, 4], [4, 9, 1], [1, 6, 2], [2, 7, 3],
604 [3, 8, 4], [4, 5, 1]]
606 verts
= [(x
[0], y
[0], z
[0]), (x
[1], -y
[1], z
[1]), (x
[2], y
[2], z
[2]),
607 (-x
[3], y
[3], z
[3]), (x
[4], -y
[4], -z
[4]), (x
[5], y
[5], -z
[5]),
608 (x
[6], y
[6], -z
[6]), (-x
[7], y
[7], -z
[7]), (-x
[8], y
[8], -z
[8]),
609 (-x
[9], -y
[9], -z
[9])]
610 faces
= [[0, 1, 2], [0, 2, 3], [0, 3, 1], [1, 4, 5], [1, 5, 2], [2, 5, 6], [2, 6, 7],
611 [2, 7, 3], [3, 7, 8], [3, 8, 9], [3, 9, 1], [1, 9, 4], [4, 5, 9], [5, 6, 7],
612 [7, 8, 9], [9, 5, 7]]
614 verts
= [(x
[0], y
[0], z
[0]), (x
[1], -y
[1], -z
[1]), (x
[2], y
[2], -z
[2]),
615 (-x
[3], y
[3], -z
[3]), (-x
[4], y
[4], z
[4]), (-x
[5], -y
[5], z
[5]),
616 (-x
[6], -y
[6], -z
[6])]
617 faces
= [[0, 1, 2], [0, 2, 3, 4], [0, 1, 6, 5], [0, 4, 5], [1, 2, 3, 6], [3, 4, 5, 6]]
619 verts
= [(x
[0], y
[0], z
[0]), (x
[1], -y
[1], -z
[1]), (x
[2], y
[2], -z
[2]),
620 (x
[3], y
[3], -z
[3]), (-x
[4], y
[4], -z
[4]), (-x
[5], y
[5], z
[5]),
621 (-x
[6], y
[6], z
[6]), (-x
[7], y
[7], -z
[7]), (-x
[8], -y
[8], -z
[8]),
622 (-x
[9], -y
[9], z
[9])]
623 faces
= [[0, 1, 2], [0, 2, 3], [0, 5, 6], [0, 6, 9], [0, 1, 8, 9], [0, 3, 4, 5],
624 [1, 2, 7, 8], [2, 3, 4, 7], [4, 5, 6, 7], [6, 7, 8, 9]]
626 verts
= [(x
[0], y
[0], z
[0]), (x
[1], -y
[1], -z
[1]), (x
[2], y
[2], -z
[2]),
627 (-x
[3], y
[3], -z
[3]), (-x
[4], -y
[4], -z
[4]), (-x
[5], -y
[5], z
[5]),
629 faces
= [[0, 2, 1], [0, 1, 4], [0, 4, 5], [0, 5, 6], [0, 6, 3, 2], [2, 1, 4, 3],
632 verts
= [(-x
[0], -y
[0], -z
[0]), (-x
[1], y
[1], -z
[1]), (-x
[2], y
[2], z
[2]),
633 (-x
[3], -y
[3], z
[3]), (x
[4], -y
[4], -z
[4]), (x
[5], y
[5], -z
[5]),
634 (x
[6], y
[6], z
[6]), (x
[7], -y
[7], z
[7])]
635 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]]
637 verts
= [(-x
[0], -y
[0], -z
[0]), (-x
[1], y
[1], -z
[1]), (-x
[2], y
[2], z
[2]),
638 (x
[3], -y
[3], z
[3]), (x
[4], y
[4], z
[4]), (x
[5], y
[5], -z
[5]),
639 (x
[6], -y
[6], -z
[6])]
640 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]]
642 verts
= [(-x
[0], -y
[0], -z
[0]), (-x
[1], y
[1], -z
[1]), (-x
[2], y
[2], z
[2]),
643 (x
[3], -y
[3], z
[3]), (x
[4], y
[4], z
[4]), (x
[5], y
[5], -z
[5]),
644 (x
[6], -y
[6], -z
[6])]
645 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]]
647 verts
= [(-x
[0], -y
[0], -z
[0]), (-x
[1], y
[1], -z
[1]), (-x
[2], -y
[2], z
[2]),
648 (-x
[3], y
[3], z
[3]), (x
[4], -y
[4], -z
[4]), (x
[5], y
[5], -z
[5]),
649 (x
[6], -y
[6], z
[6]), (x
[7], y
[7], z
[7])]
650 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]]
652 # name = "Rock." + str(base + shift).zfill(3)
656 obj
= createMeshObject(context
, verts
, [], faces
, name
)
659 # bpy.data.objects[name].scale = Vector((averageX, averageY, averageZ))
660 obj
.scale
= Vector((averageX
, averageY
, averageZ
))
662 # For a slight speed bump / Readability:
663 # mesh = bpy.data.meshes[name]
666 creases
= mesh
.edge_creases_ensure().data
671 # todo: "0.375 / 3"? WTF? That = 0.125. . . .
672 # *** Completed 7/15/2011: Changed second one ***
673 creases
[i
].value
= gauss(0.125, 0.125)
676 creases
[i
].value
= gauss(0.5, 0.125)
677 for i
in [6, 9, 11, 12]:
678 creases
[i
].value
= gauss(0.25, 0.05)
679 for i
in [5, 7, 15, 16]:
680 creases
[i
].value
= gauss(0.125, 0.025)
683 creases
[i
].value
= gauss(0.125, 0.025)
685 for i
in [0, 1, 6, 10, 13]:
686 creases
[i
].value
= gauss(0.25, 0.05)
687 creases
[8].value
= gauss(0.5, 0.125)
689 for i
in [5, 6, 7, 10, 14, 16, 19, 21]:
690 creases
[i
].value
= gauss(0.5, 0.125)
693 if i
in [0, 1, 2, 3, 6, 7, 8, 9, 13, 16]:
694 creases
[i
].value
= gauss(0.5, 0.125)
696 creases
[i
].value
= gauss(0.25, 0.05)
698 creases
[i
].value
= gauss(0.125, 0.025)
701 if i
in [0, 3, 8, 9, 10]:
702 creases
[i
].value
= gauss(0.5, 0.125)
704 creases
[i
].value
= gauss(0.25, 0.05)
706 creases
[i
].value
= gauss(0.125, 0.025)
709 if i
in [0, 3, 4, 11]:
710 creases
[i
].value
= gauss(0.5, 0.125)
712 creases
[i
].value
= gauss(0.25, 0.05)
715 if i
in [0, 2, 3, 4, 8, 11]:
716 creases
[i
].value
= gauss(0.5, 0.125)
718 creases
[i
].value
= gauss(0.25, 0.05)
720 creases
[i
].value
= gauss(0.125, 0.025)
723 if i
in [1, 2, 3, 4, 8, 11]:
724 creases
[i
].value
= gauss(0.25, 0.05)
726 creases
[i
].value
= gauss(0.125, 0.025)
731 # Creates rock objects:
732 def generateRocks(context
, scaleX
, skewX
, scaleY
, skewY
, scaleZ
, skewZ
,
733 scale_fac
, detail
, display_detail
, deform
, rough
,
734 smooth_fac
, smooth_it
,
735 numOfRocks
=1, userSeed
=1.0,
736 scaleDisplace
=False, randomSeed
=True, use_enter_edit_mode
=False):
747 # Seed the random Gaussian value generator:
749 seed(int(time
.time()))
753 # These values need to be really small to look good.
754 # So the user does not have to use such ridiculously small values:
758 # Verify that the min really is the min:
759 if scaleX
[1] < scaleX
[0]:
760 scaleX
[0], scaleX
[1] = scaleX
[1], scaleX
[0]
761 if scaleY
[1] < scaleY
[0]:
762 scaleY
[0], scaleY
[1] = scaleY
[1], scaleY
[0]
763 if scaleZ
[1] < scaleZ
[0]:
764 scaleZ
[0], scaleZ
[1] = scaleZ
[1], scaleZ
[0]
766 # todo: edit below to allow for skewing the distribution
767 # *** todo completed 4/22/2011 ***
768 # *** Code now generating "int not scriptable error" in Blender ***
770 # Calculate mu and sigma for a Gaussian distributed random number
772 # If the lower and upper bounds are the same, skip the math.
774 # sigma is the standard deviation of the values. The 95% interval is three
775 # standard deviations, which is what we want most generated values to fall
776 # in. Since it might be skewed we are going to use half the difference
777 # between the mean and the furthest bound and scale the other side down
778 # post-number generation.
779 if scaleX
[0] != scaleX
[1]:
780 skewX
= (skewX
+ 1) / 2
781 muX
= scaleX
[0] + ((scaleX
[1] - scaleX
[0]) * skewX
)
783 sigmaX
= (scaleX
[1] - muX
) / 3
785 sigmaX
= (muX
- scaleX
[0]) / 3
789 if scaleY
[0] != scaleY
[1]:
790 skewY
= (skewY
+ 1) / 2
791 muY
= scaleY
[0] + ((scaleY
[1] - scaleY
[0]) * skewY
)
793 sigmaY
= (scaleY
[1] - muY
) / 3
795 sigmaY
= (muY
- scaleY
[0]) / 3
799 if scaleZ
[0] != scaleZ
[1]:
800 skewZ
= (skewZ
+ 1) / 2
801 muZ
= scaleZ
[0] + ((scaleZ
[1] - scaleZ
[0]) * skewZ
)
803 sigmaZ
= (scaleZ
[1] - muZ
) / 3
805 sigmaZ
= (muZ
- scaleZ
[0]) / 3
812 for i
in range(numOfRocks
):
813 # todo: enable different random values for each (x,y,z) coordinate for
814 # each vertex. This will add additional randomness to the shape of the
816 # *** todo completed 4/19/2011 ***
817 # *** Code is notably slower at high rock counts ***
819 # name = generateObject(context, muX, sigmaX, scaleX, upperSkewX, muY,
820 rock
= generateObject(
821 context
, muX
, sigmaX
, scaleX
, upperSkewX
, muY
,
822 sigmaY
, scaleY
, upperSkewY
, muZ
, sigmaZ
, scaleZ
,
823 upperSkewZ
, i
, LASTROCK
, scaleDisplace
, scale_fac
)
825 # rock = bpy.data.objects[name]
827 # todo Map what the two new textures will be:
828 # This is not working. It works on paper so . . . ???
829 # *** todo completed on 4/23/2011 ***
830 # *** todo re-added as the first rock is getting
831 # 'Texture.001' twice. ***
832 # *** todo completed on 4/25/2011 ***
833 # *** Script no longer needs to map new texture names 9/6/2011 ***
835 # Create the four new textures:
836 # todo Set displacement texture parameters:
837 # *** todo completed on 5/31/2011 ***
838 # Voronoi has been removed from being an option for the fine detail
840 texTypes
= ['CLOUDS', 'MUSGRAVE', 'DISTORTED_NOISE', 'STUCCI', 'VORONOI']
842 # The first texture is to give a more ranodm base shape appearance:
843 newTex
.append(bpy
.data
.textures
.new(
844 name
='rock_displacement',
846 randomizeTexture(newTex
[0], 0)
847 newTex
.append(bpy
.data
.textures
.new(
848 name
='rock_displacement',
850 randomizeTexture(newTex
[1], 0)
852 newTex
.append(bpy
.data
.textures
.new(
853 name
='rock_displacement',
854 type=texTypes
[int(round(weibull(1, 1)[0] / 2.125))]))
855 randomizeTexture(newTex
[2], 1)
856 newTex
.append(bpy
.data
.textures
.new(
857 name
='rock_displacement',
858 type=texTypes
[int(round(weibull(1, 1)[0] / 2.125))]))
859 randomizeTexture(newTex
[3], 2)
861 newTex
.append(bpy
.data
.textures
.new(
862 name
='rock_displacement',
863 type=texTypes
[int(round(weibull(1, 1) / 2.125))]))
864 randomizeTexture(newTex
[2], 1)
865 newTex
.append(bpy
.data
.textures
.new(
866 name
='rock_displacement',
867 type=texTypes
[int(round(weibull(1, 1) / 2.125))]))
868 randomizeTexture(newTex
[3], 2)
871 rock
.modifiers
.new(name
="Subsurf", type='SUBSURF')
872 rock
.modifiers
.new(name
="Subsurf", type='SUBSURF')
873 rock
.modifiers
.new(name
="Displace", type='DISPLACE')
874 rock
.modifiers
.new(name
="Displace", type='DISPLACE')
875 rock
.modifiers
.new(name
="Displace", type='DISPLACE')
876 rock
.modifiers
.new(name
="Displace", type='DISPLACE')
878 # If smoothing is enabled, allow a little randomness into the
879 # smoothing factor. Then add the smoothing modifier.
880 if smooth_fac
> 0.0 and smooth_it
> 0:
881 rock
.modifiers
.new(name
="Smooth", type='SMOOTH')
882 rock
.modifiers
[6].factor
= gauss(smooth_fac
, (smooth_fac
** 0.5) / 12)
883 rock
.modifiers
[6].iterations
= smooth_it
884 # Make a call to random to keep things consistent:
888 # Set subsurf modifier parameters:
889 rock
.modifiers
[0].levels
= display_detail
890 rock
.modifiers
[0].render_levels
= detail
891 rock
.modifiers
[1].levels
= display_detail
892 rock
.modifiers
[1].render_levels
= detail
894 # todo Set displacement modifier parameters:
895 # *** todo completed on 4/23/2011 ***
896 # *** toned down the variance on 4/26/2011 ***
897 # *** added third modifier on 4/28/2011 ***
898 # *** texture access changed on 9/6/2011 ***
899 rock
.modifiers
[2].texture
= newTex
[0]
900 rock
.modifiers
[2].strength
= gauss(deform
/ 100, (1 / 300) * deform
)
901 rock
.modifiers
[2].mid_level
= 0
902 rock
.modifiers
[3].texture
= newTex
[1]
903 rock
.modifiers
[3].strength
= gauss(deform
, (1 / 3) * deform
)
904 rock
.modifiers
[3].mid_level
= 0
905 rock
.modifiers
[4].texture
= newTex
[2]
906 rock
.modifiers
[4].strength
= gauss(rough
* 2, (1 / 3) * rough
)
907 rock
.modifiers
[5].texture
= newTex
[3]
908 rock
.modifiers
[5].strength
= gauss(rough
, (1 / 3) * rough
)
910 # Set mesh to be smooth and fix the normals:
911 utils
.smooth(rock
.data
)
912 # utils.smooth(bpy.data.meshes[name])
913 bpy
.ops
.object.editmode_toggle()
914 bpy
.ops
.mesh
.normals_make_consistent()
915 bpy
.ops
.object.editmode_toggle()
917 if use_enter_edit_mode
:
918 for m
in rock
.modifiers
:
919 m
.show_in_editmode
= True
920 m
.show_on_cage
= True
922 # Store the last value of i:
927 # Add the shift to LASTROCK:
928 LASTROCK
+= shift
+ 1
933 # Much of the code below is more-or-less imitation of other addons and as such
934 # I have left it undocumented.
936 class OBJECT_OT_add_mesh_rock(bpy
.types
.Operator
):
937 """Add rock objects"""
938 bl_idname
= "mesh.add_mesh_rock"
939 bl_label
= "Add Rocks"
940 bl_options
= {'REGISTER', 'UNDO'}
941 bl_description
= "Add rocks"
943 # Get the preset values from the XML file.
944 # -> The script was morphed into a Python module
946 # Tell settings.py to parse the XML file with the settings.
947 # Then get the default values resulting from the parsing.
948 # Make a list containing the default values and append to that
949 # the presets specified in the same XML file. This list will
950 # be used to load preset values.
952 defaults
= settings
.getDefault()
953 presetsList
= [defaults
]
954 presetsList
+= settings
.getPresetLists()
958 # Build the presets list for the enum property.
959 # This needs to be a for loop as the user might add presets to
960 # the XML file and those should show here:
961 for i
, preset
in enumerate(presetsList
):
962 presets
.append((str(i
), preset
[0], preset
[0] + " preset values"))
964 preset_values
: EnumProperty(
967 description
="Preset values for some rock types")
969 num_of_rocks
: IntProperty(
970 name
="Number of rocks",
971 description
="Number of rocks to generate. WARNING: Slow at high values!",
976 scale_X
: FloatVectorProperty(
978 description
="X axis scaling range",
979 min=0.0, max=256.0, step
=1,
980 default
=defaults
[1], size
=2)
981 skew_X
: FloatProperty(
983 description
="X Skew ratio. 0.5 is no skew",
984 min=-1.0, max=1.0, default
=defaults
[4])
985 scale_Y
: FloatVectorProperty(
987 description
="Y axis scaling range",
988 min=.0, max=256.0, step
=1,
989 default
=defaults
[2], size
=2)
990 skew_Y
: FloatProperty(
992 description
="Y Skew ratio. 0.5 is no skew",
993 min=-1.0, max=1.0, default
=defaults
[5])
994 scale_Z
: FloatVectorProperty(
996 description
="Z axis scaling range",
997 min=0.0, max=256.0, step
=1,
998 default
=defaults
[3], size
=2)
999 skew_Z
: FloatProperty(
1001 description
="Z Skew ratio. 0.5 is no skew",
1002 min=-1.0, max=1.0, default
=defaults
[6])
1003 use_scale_dis
: BoolProperty(
1004 name
="Scale displace textures",
1005 description
="Scale displacement textures with dimensions. May cause stretched textures",
1006 default
=defaults
[7])
1007 scale_fac
: FloatVectorProperty(
1008 name
="Scaling Factor",
1009 description
="XYZ scaling factor. 1: no scaling",
1010 min=0.0001, max=256.0, step
=0.1,
1011 default
=defaults
[8], size
=3)
1013 # @todo Possible to title this section "Physical Properties:"?
1014 deform
: FloatProperty(
1016 description
="Rock deformation",
1017 min=0.0, soft_max
=50, max=1024.0, default
=defaults
[9])
1018 rough
: FloatProperty(
1020 description
="Rock roughness",
1021 min=0.0, soft_max
=50, max=1024.0, default
=defaults
[10])
1022 detail
: IntProperty(
1023 name
="Detail level",
1024 description
="Detail level. WARNING: Slow at high values!",
1025 min=1, soft_max
=4, max=10, default
=defaults
[11])
1026 display_detail
: IntProperty(
1027 name
="Display Detail",
1028 description
="Display detail. Use a lower value for high numbers of rocks",
1029 min=1, soft_max
=4, max=10, default
=defaults
[12])
1030 smooth_fac
: FloatProperty(
1031 name
="Smooth Factor",
1032 description
="Smoothing factor. A value of 0 disables",
1033 min=0.0, max=128.0, default
=defaults
[13])
1034 smooth_it
: IntProperty(
1035 name
="Smooth Iterations",
1036 description
="Smoothing iterations. A value of 0 disables",
1037 min=0, max=50, default
=defaults
[14])
1039 use_generate
: BoolProperty(
1040 name
="Generate Rocks",
1041 description
="Enable actual generation",
1042 default
=defaults
[15])
1043 use_random_seed
: BoolProperty(
1044 name
="Use a random seed",
1045 description
="Create a seed based on time. Causes user seed to be ignored",
1046 default
=defaults
[16])
1047 user_seed
: IntProperty(
1049 description
="Use a specific seed for the generator",
1050 min=0, max=1048576, default
=defaults
[17])
1052 def draw(self
, context
):
1053 layout
= self
.layout
1055 box
.prop(self
, 'num_of_rocks')
1057 box
.prop(self
, 'scale_X')
1058 box
.prop(self
, 'skew_X')
1059 box
.prop(self
, 'scale_Y')
1060 box
.prop(self
, 'skew_Y')
1061 box
.prop(self
, 'scale_Z')
1062 box
.prop(self
, 'skew_Z')
1063 box
.prop(self
, 'use_scale_dis')
1064 if self
.use_scale_dis
:
1065 box
.prop(self
, 'scale_fac')
1067 self
.scale_fac
= utils
.toFloats(self
.defaults
[8])
1069 box
.prop(self
, 'deform')
1070 box
.prop(self
, 'rough')
1071 box
.prop(self
, 'detail')
1072 box
.prop(self
, 'display_detail')
1073 box
.prop(self
, 'smooth_fac')
1074 box
.prop(self
, 'smooth_it')
1077 box
.prop(self
, 'use_generate')
1078 box
.prop(self
, 'use_random_seed')
1079 if not self
.use_random_seed
:
1080 box
.prop(self
, 'user_seed')
1081 box
.prop(self
, 'preset_values')
1083 def execute(self
, context
):
1084 # turn off 'Enter Edit Mode'
1085 use_enter_edit_mode
= bpy
.context
.preferences
.edit
.use_enter_edit_mode
1086 bpy
.context
.preferences
.edit
.use_enter_edit_mode
= False
1088 # The following "if" block loads preset values:
1089 if self
.lastPreset
!= int(self
.preset_values
):
1090 self
.scale_X
= utils
.toFloats(self
.presetsList
[int(self
.preset_values
)][1])
1091 self
.scale_Y
= utils
.toFloats(self
.presetsList
[int(self
.preset_values
)][2])
1092 self
.scale_Z
= utils
.toFloats(self
.presetsList
[int(self
.preset_values
)][3])
1093 self
.skew_X
= float(self
.presetsList
[int(self
.preset_values
)][4])
1094 self
.skew_Y
= float(self
.presetsList
[int(self
.preset_values
)][5])
1095 self
.skew_Z
= float(self
.presetsList
[int(self
.preset_values
)][6])
1096 self
.use_scale_dis
= bool(self
.presetsList
[int(self
.preset_values
)][7])
1097 self
.scale_fac
= utils
.toFloats(self
.presetsList
[int(self
.preset_values
)][8])
1098 self
.deform
= float(self
.presetsList
[int(self
.preset_values
)][9])
1099 self
.rough
= float(self
.presetsList
[int(self
.preset_values
)][10])
1100 self
.detail
= int(self
.presetsList
[int(self
.preset_values
)][11])
1101 self
.display_detail
= int(self
.presetsList
[int(self
.preset_values
)][12])
1102 self
.smooth_fac
= float(self
.presetsList
[int(self
.preset_values
)][13])
1103 self
.smooth_it
= int(self
.presetsList
[int(self
.preset_values
)][14])
1104 self
.use_generate
= bool(self
.presetsList
[int(self
.preset_values
)][15])
1105 self
.use_random_seed
= bool(self
.presetsList
[int(self
.preset_values
)][16])
1106 self
.user_seed
= int(self
.presetsList
[int(self
.preset_values
)][17])
1107 self
.lastPreset
= int(self
.preset_values
)
1109 # todo Add deform, deform_Var, rough, and rough_Var:
1110 # *** todo completed 4/23/2011 ***
1111 # *** Eliminated "deform_Var" and "rough_Var" so the script is not
1112 # as complex to use. May add in again as advanced features. ***
1113 if self
.use_generate
:
1114 rocks
= generateRocks(context
,
1123 self
.display_detail
,
1131 self
.use_random_seed
,
1132 use_enter_edit_mode
)
1135 rock
.select_set(True)
1137 if use_enter_edit_mode
:
1138 bpy
.ops
.object.mode_set(mode
= 'EDIT')
1140 # restore pre operator state
1141 bpy
.context
.preferences
.edit
.use_enter_edit_mode
= use_enter_edit_mode
1146 def menu_func_rocks(self
, context
):
1147 layout
= self
.layout
1150 OBJECT_OT_add_mesh_rock
.bl_idname
,
1151 text
="Rock Generator",
1152 icon
="MESH_ICOSPHERE")
1156 OBJECT_OT_add_mesh_rock
,
1161 from bpy
.utils
import register_class
1164 bpy
.types
.VIEW3D_MT_mesh_add
.append(menu_func_rocks
)
1168 from bpy
.utils
import unregister_class
1169 for cls
in reversed(classes
):
1170 unregister_class(cls
)
1171 bpy
.types
.VIEW3D_MT_mesh_add
.remove(menu_func_rocks
)
1174 if __name__
== "__main__":