Remove "Object Grease Scatter" addon in Blender 2.8
There is a new "Object Scatter" addon as a replacement.
This commit is contained in:
parent
54d50aec6f
commit
b8a0f8c8fe
|
@ -1,401 +0,0 @@
|
|||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8-80 compliant>
|
||||
|
||||
# Script copyright (C) Campbell Barton
|
||||
|
||||
bl_info = {
|
||||
"name": "Grease Scatter Objects",
|
||||
"author": "Campbell Barton",
|
||||
"version": (0, 1),
|
||||
"blender": (2, 58, 0),
|
||||
"location": "3D View, Add Mesh",
|
||||
"description": "Scatter a group of objects onto the active mesh using "
|
||||
"the grease pencil lines",
|
||||
"warning": "",
|
||||
"wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
|
||||
"Scripts/Object/Grease_Scatter",
|
||||
"support": 'OFFICIAL',
|
||||
"category": "Object",
|
||||
}
|
||||
|
||||
from mathutils import Vector, Matrix, Quaternion
|
||||
from random import uniform, shuffle
|
||||
import bpy
|
||||
|
||||
|
||||
def _main(self,
|
||||
obj,
|
||||
group,
|
||||
DENSITY=1.0,
|
||||
SCALE=0.6,
|
||||
RAND_LOC=0.8,
|
||||
RAND_ALIGN=0.75,
|
||||
):
|
||||
|
||||
from math import radians, pi
|
||||
|
||||
# OFS = 0.2
|
||||
SEEK = 2.0 # distance for ray to seek
|
||||
BAD_NORMAL = Vector((0.0, 0.0, -1.0))
|
||||
WALL_LIMIT = radians(45.0)
|
||||
|
||||
mats = (Matrix.Rotation(radians(-45), 3, 'X'),
|
||||
Matrix.Rotation(radians(+45), 3, 'X'),
|
||||
Matrix.Rotation(radians(-45), 3, 'Y'),
|
||||
Matrix.Rotation(radians(+45), 3, 'Y'),
|
||||
Matrix.Rotation(radians(-45), 3, 'Z'),
|
||||
Matrix.Rotation(radians(+45), 3, 'Z'),
|
||||
)
|
||||
|
||||
Z_UP = Vector((0.0, 0.0, 1.0))
|
||||
Y_UP = Vector((0.0, 1.0, 0.0))
|
||||
|
||||
if not group:
|
||||
self.report({'WARNING'}, "Group '%s' not found" % obj.name)
|
||||
return
|
||||
|
||||
def debug_edge(v1, v2):
|
||||
mesh = bpy.data.meshes.new("Retopo")
|
||||
mesh.from_pydata([v1, v2], [(0.0, 1.0)], [])
|
||||
|
||||
scene = bpy.context.scene
|
||||
mesh.update()
|
||||
obj_new = bpy.data.objects.new("Torus", mesh)
|
||||
scene.objects.link(obj_new)
|
||||
|
||||
ray = obj.ray_cast
|
||||
closest_point_on_mesh = obj.closest_point_on_mesh
|
||||
|
||||
obj_mat = obj.matrix_world.copy()
|
||||
obj_mat_inv = obj_mat.inverted()
|
||||
# obj_quat = obj_mat.to_quaternion()
|
||||
# obj_quat_inv = obj_mat_inv.to_quaternion()
|
||||
|
||||
DEBUG = False
|
||||
|
||||
def fix_point(p):
|
||||
ok, hit, no, ind = closest_point_on_mesh(obj_mat_inv * p)
|
||||
if ok:
|
||||
if DEBUG:
|
||||
return [p, no, None]
|
||||
else:
|
||||
# print("good", hit, no)
|
||||
return [hit, no, None]
|
||||
|
||||
# worry!
|
||||
print("bad!", p, BAD_NORMAL)
|
||||
|
||||
return [p, BAD_NORMAL, None]
|
||||
|
||||
def get_points(stroke):
|
||||
return [fix_point(point.co) for point in stroke.points]
|
||||
|
||||
def get_splines(gp):
|
||||
if gp.layers.active:
|
||||
frame = gp.layers.active.active_frame
|
||||
return [get_points(stroke) for stroke in frame.strokes]
|
||||
else:
|
||||
return []
|
||||
|
||||
def main():
|
||||
scene = bpy.context.scene
|
||||
obj = bpy.context.object
|
||||
|
||||
gp = None
|
||||
|
||||
if obj:
|
||||
gp = obj.grease_pencil
|
||||
|
||||
if not gp:
|
||||
gp = scene.grease_pencil
|
||||
|
||||
if not gp:
|
||||
self.report({'WARNING'}, "No grease pencil layer found")
|
||||
return
|
||||
|
||||
splines = get_splines(gp)
|
||||
|
||||
for s in splines:
|
||||
for pt in s:
|
||||
p = pt[0]
|
||||
n = pt[1]
|
||||
# print(p, n)
|
||||
if n is BAD_NORMAL:
|
||||
continue
|
||||
|
||||
# # dont self intersect
|
||||
best_nor = None
|
||||
#best_hit = None
|
||||
best_dist = 10000000.0
|
||||
pofs = p + n * 0.01
|
||||
|
||||
n_seek = n * SEEK
|
||||
m_alt_1 = Matrix.Rotation(radians(22.5), 3, n)
|
||||
m_alt_2 = Matrix.Rotation(radians(-22.5), 3, n)
|
||||
for _m in mats:
|
||||
for m in (_m, m_alt_1 * _m, m_alt_2 * _m):
|
||||
pdir = m * n_seek
|
||||
ok, hit, nor, ind = ray(pofs, pdir, best_dist)
|
||||
if ok:
|
||||
best_dist = (pofs - hit).length
|
||||
best_nor = nor
|
||||
# best_hit = hit
|
||||
|
||||
if best_nor:
|
||||
pt[1].length = best_dist
|
||||
best_nor.negate()
|
||||
pt[2] = best_nor
|
||||
|
||||
#scene.cursor_location[:] = best_hitnyway
|
||||
# bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP',
|
||||
# iterations=1)
|
||||
# debug_edge(p, best_hit)
|
||||
# p[:] = best_hit
|
||||
|
||||
# Now we need to do scattering.
|
||||
# first corners
|
||||
hits = []
|
||||
nors = []
|
||||
oris = []
|
||||
for s in splines:
|
||||
# point, normal, n_other the closest hit normal
|
||||
for p, n, n_other in s:
|
||||
if n is BAD_NORMAL:
|
||||
continue
|
||||
if n_other:
|
||||
# cast vectors twice as long as the distance
|
||||
# needed just in case.
|
||||
n_down = (n * -SEEK)
|
||||
l = n_down.length
|
||||
n_other.length = l
|
||||
|
||||
vantage = p + n
|
||||
if DEBUG:
|
||||
p[:] = vantage
|
||||
|
||||
# We should cast rays between n_down and n_other
|
||||
#for f in (0.0, 0.2, 0.4, 0.6, 0.8, 1.0):
|
||||
TOT = int(10 * DENSITY)
|
||||
#for i in list(range(TOT)):
|
||||
for i in list(range(TOT))[int(TOT / 1.5):]: # second half
|
||||
f = i / (TOT - 1)
|
||||
|
||||
# focus on the center
|
||||
'''
|
||||
f -= 0.5
|
||||
f = f*f
|
||||
f += 0.5
|
||||
'''
|
||||
|
||||
ntmp = f * n_down + (1.0 - f) * n_other
|
||||
# randomize
|
||||
ntmp.x += uniform(-l, l) * RAND_LOC
|
||||
ntmp.y += uniform(-l, l) * RAND_LOC
|
||||
ntmp.z += uniform(-l, l) * RAND_LOC
|
||||
|
||||
ok, hit, hit_no, ind = ray(vantage, ntmp, ntmp.length)
|
||||
# print(hit, hit_no)
|
||||
if ok:
|
||||
if hit_no.angle(Z_UP) < WALL_LIMIT:
|
||||
hits.append(hit)
|
||||
nors.append(hit_no)
|
||||
oris.append(n_other.cross(hit_no))
|
||||
#oris.append(n_other)
|
||||
|
||||
if 0:
|
||||
mesh = bpy.data.meshes.new("ScatterDupliFace")
|
||||
mesh.from_pydata(hits, [], [])
|
||||
|
||||
scene = bpy.context.scene
|
||||
mesh.update()
|
||||
obj_new = bpy.data.objects.new("ScatterPar", mesh)
|
||||
scene.objects.link(obj_new)
|
||||
obj_new.layers[:] = obj.layers
|
||||
|
||||
# Now setup dupli-faces
|
||||
obj_new.dupli_type = 'VERTS'
|
||||
ob_child = bpy.data.objects["trash"]
|
||||
ob_child.location = obj_new.location
|
||||
ob_child.parent = obj_new
|
||||
else:
|
||||
|
||||
def apply_faces(triples):
|
||||
# first randomize the faces
|
||||
shuffle(triples)
|
||||
|
||||
obs = group.objects[:]
|
||||
tot = len(obs)
|
||||
tot_div = int(len(triples) / tot)
|
||||
|
||||
for inst_ob in obs:
|
||||
triple_sub = triples[0:tot_div]
|
||||
triples[0:tot_div] = []
|
||||
|
||||
vv = [tuple(v) for f in triple_sub for v in f]
|
||||
|
||||
mesh = bpy.data.meshes.new("ScatterDupliFace")
|
||||
mesh.from_pydata(vv, [], [(i * 3, i * 3 + 1, i * 3 + 2)
|
||||
for i in range(len(triple_sub))])
|
||||
|
||||
scene = bpy.context.scene
|
||||
mesh.update()
|
||||
obj_new = bpy.data.objects.new("ScatterPar", mesh)
|
||||
|
||||
scene.objects.link(obj_new)
|
||||
obj_new.layers[:] = obj.layers
|
||||
|
||||
# Now setup dupli-faces
|
||||
obj_new.dupli_type = 'FACES'
|
||||
obj_new.use_dupli_faces_scale = True
|
||||
obj_new.dupli_faces_scale = 100.0
|
||||
|
||||
inst_ob.location = 0.0, 0.0, 0.0
|
||||
inst_ob.parent = obj_new
|
||||
|
||||
# align the object with worldspace
|
||||
obj_new.matrix_world = obj_mat
|
||||
|
||||
# BGE settings for testing
|
||||
'''
|
||||
inst_ob.game.physics_type = 'RIGID_BODY'
|
||||
inst_ob.game.use_collision_bounds = True
|
||||
inst_ob.game.collision_bounds = 'TRIANGLE_MESH'
|
||||
inst_ob.game.collision_margin = 0.1
|
||||
obj_new.select = True
|
||||
'''
|
||||
|
||||
# build faces from vert/normals
|
||||
tri = (Vector((0.0, 0.0, 0.01)),
|
||||
Vector((0.0, 0.0, 0.0)),
|
||||
Vector((0.0, 0.01, 0.01)))
|
||||
|
||||
coords = []
|
||||
# face_ind = []
|
||||
for i in range(len(hits)):
|
||||
co = hits[i]
|
||||
no = nors[i]
|
||||
ori = oris[i]
|
||||
quat = no.to_track_quat('X', 'Z')
|
||||
|
||||
# make 2 angles and blend
|
||||
angle = uniform(-pi, pi)
|
||||
angle_aligned = -(ori.angle(quat * Y_UP, pi))
|
||||
|
||||
quat = Quaternion(no,
|
||||
(angle * (1.0 - RAND_ALIGN)) +
|
||||
(angle_aligned * RAND_ALIGN)
|
||||
).cross(quat)
|
||||
|
||||
f = uniform(0.1, 1.2) * SCALE
|
||||
|
||||
coords.append([co + (quat * (tri[0] * f)),
|
||||
co + (quat * (tri[1] * f)),
|
||||
co + (quat * (tri[2] * f)),
|
||||
])
|
||||
|
||||
apply_faces(coords)
|
||||
|
||||
main()
|
||||
|
||||
|
||||
from bpy.props import FloatProperty, StringProperty
|
||||
|
||||
|
||||
class Scatter(bpy.types.Operator):
|
||||
""""""
|
||||
bl_idname = "object.scatter"
|
||||
bl_label = "Grease Pencil Scatter"
|
||||
|
||||
density = FloatProperty(
|
||||
name="Density",
|
||||
description="Multiplier for the density of items",
|
||||
default=1.0, min=0.01, max=10.0,
|
||||
)
|
||||
scale = FloatProperty(
|
||||
name="Scale",
|
||||
description="Size multiplier for duplifaces",
|
||||
default=1.0, min=0.01, max=10.0,
|
||||
)
|
||||
rand_align = FloatProperty(
|
||||
name="Random Align",
|
||||
description="Randomize alignment with the walls",
|
||||
default=0.75, min=0.0, max=1.0,
|
||||
)
|
||||
rand_loc = FloatProperty(
|
||||
name="Random Loc",
|
||||
description="Randomize placement",
|
||||
default=0.75, min=0.0, max=1.0,
|
||||
)
|
||||
# XXX, should not be a string - TODO, add a way for scritps to select ID's
|
||||
group = StringProperty(
|
||||
name="Group",
|
||||
description=("Group name to use for object placement, "
|
||||
"defaults to object name when that matches a group"))
|
||||
|
||||
def execute(self, context):
|
||||
obj = bpy.context.object
|
||||
group = bpy.data.groups.get(self.group)
|
||||
|
||||
if not group:
|
||||
self.report({'ERROR'}, "Group %r not found" % self.group)
|
||||
return {'CANCELLED'}
|
||||
|
||||
_main(self,
|
||||
obj,
|
||||
group,
|
||||
DENSITY=self.density,
|
||||
SCALE=self.scale,
|
||||
RAND_LOC=self.rand_loc,
|
||||
RAND_ALIGN=self.rand_align,
|
||||
)
|
||||
return {'FINISHED'}
|
||||
|
||||
def check(self, context):
|
||||
if self.group not in bpy.data.groups:
|
||||
self.group = ""
|
||||
return True
|
||||
return False
|
||||
|
||||
def invoke(self, context, event):
|
||||
|
||||
# useful to initialize, take a guess
|
||||
if not self.group and context.object.name in bpy.data.groups:
|
||||
self.group = context.object.name
|
||||
|
||||
wm = context.window_manager
|
||||
wm.invoke_props_dialog(self, width=180)
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
|
||||
def menu_func(self, context):
|
||||
self.layout.operator(Scatter.bl_idname, icon='AUTO')
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(Scatter)
|
||||
bpy.types.VIEW3D_MT_mesh_add.append(menu_func)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(Scatter)
|
||||
bpy.types.VIEW3D_MT_mesh_add.remove(menu_func)
|
||||
|
||||
#if __name__ == "__main__":
|
||||
# _main()
|
Loading…
Reference in New Issue