diff --git a/object_grease_scatter.py b/object_grease_scatter.py index ad6f3018b8ecc0c3662e9ee1e4b45841acc6154f..2557f043abb5857a24957bb8ea34ff687b407b0c 100644 --- a/object_grease_scatter.py +++ b/object_grease_scatter.py @@ -37,44 +37,43 @@ bl_info = { "category": "Object"} from mathutils import Vector, Matrix, Quaternion -from math import radians from random import uniform, shuffle import bpy + def _main(self, DENSITY=1.0, SCALE=0.6, RAND_LOC=0.8, RAND_ALIGN=0.75): from math import radians - + C = bpy.context o = C.object # print(o.ray_cast(Vector(), Vector(0,0,0.2))) - + OFS = 0.2 - SEEK = 2.0 # distance for ray to seek - BAD_NORMAL = Vector((0,0,-1)) + 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,1.0)) - dirs = [ - Vector((0,0,OFS)), - Vector((0,0,-OFS))] + 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)) + dirs = [Vector((0.0, 0.0, OFS)), + Vector((0.0, 0.0, -OFS)), + ] ''' Vector(0,OFS,0), Vector(0,-OFS,0), Vector(OFS,0,0), Vector(-OFS,0,0) ''' - + group = bpy.data.groups.get(o.name) - + if not group: self.report({'WARNING'}, "Group '%s' not found, must match object name" % o.name) return @@ -105,14 +104,11 @@ def _main(self, DENSITY=1.0, SCALE=0.6, RAND_LOC=0.8, RAND_ALIGN=0.75): faces_flat = [v for f in faces for v in treat_face(f)] self.faces.foreach_set("verts_raw", faces_flat) del faces_flat - - - - def debug_edge(v1,v2): + def debug_edge(v1, v2): mesh = bpy.data.meshes.new("Retopo") - mesh.from_pydata([v1,v2], [(0,1)], []) - + mesh.from_pydata([v1, v2], [(0.0, 1.0)], []) + scene = bpy.context.scene mesh.update() obj_new = bpy.data.objects.new("Torus", mesh) @@ -122,20 +118,21 @@ def _main(self, DENSITY=1.0, SCALE=0.6, RAND_LOC=0.8, RAND_ALIGN=0.75): #ray = C.scene.ray_cast DEBUG = False + def fix_point(p): for d in dirs: # print(p) hit, no, ind = ray(p, p + d) if ind != -1: if DEBUG: - return [p, no, None] + return [p, no, None] else: # print("good", hit, no) - return [hit, no, None] + return [hit, no, None] # worry! print("bad!", p, BAD_NORMAL) - + return [p, BAD_NORMAL, None] def get_points(stroke): @@ -159,7 +156,7 @@ def _main(self, DENSITY=1.0, SCALE=0.6, RAND_LOC=0.8, RAND_ALIGN=0.75): if not gp: gp = scene.grease_pencil - + if not gp: self.report({'WARNING'}, "No grease pencil layer found") return @@ -173,13 +170,13 @@ def _main(self, DENSITY=1.0, SCALE=0.6, RAND_LOC=0.8, RAND_ALIGN=0.75): # print(p, n) if n is BAD_NORMAL: continue - + # # dont self intersect best_nor = None - best_hit = 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) @@ -192,25 +189,24 @@ def _main(self, DENSITY=1.0, SCALE=0.6, RAND_LOC=0.8, RAND_ALIGN=0.75): best_dist = dist 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: - for p,n,n_other in s: # point, normal, n_other the closest hit normal + for p, n, n_other in s: # point, normal, n_other the closest hit normal if n is BAD_NORMAL: continue if n_other: @@ -218,31 +214,31 @@ def _main(self, DENSITY=1.0, SCALE=0.6, RAND_LOC=0.8, RAND_ALIGN=0.75): 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) - + 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 = 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 - + hit, hit_no, ind = ray(vantage, vantage + ntmp) # print(hit, hit_no) if ind != -1: @@ -251,30 +247,28 @@ def _main(self, DENSITY=1.0, SCALE=0.6, RAND_LOC=0.8, RAND_ALIGN=0.75): nors.append(hit_no) oris.append(n_other.cross(hit_no)) #oris.append(n_other) - - - + if 0: mesh = bpy.data.meshes.new("Retopo") mesh.from_pydata(hits, [], []) - + scene = bpy.context.scene mesh.update() obj_new = bpy.data.objects.new("Torus", mesh) scene.objects.link(obj_new) obj_new.layers[:] = o.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) @@ -282,26 +276,26 @@ def _main(self, DENSITY=1.0, SCALE=0.6, RAND_LOC=0.8, RAND_ALIGN=0.75): for inst_ob in obs: triple_subset = triples[0:tot_div] triples[0:tot_div] = [] - + vv = [tuple(v) for f in triple_subset for v in f] mesh = bpy.data.meshes.new("Retopo") - mesh.from_pydata(vv, [], [(i*3, i*3+1, i*3+2) for i in range(len(triple_subset))]) + mesh.from_pydata(vv, [], [(i * 3, i * 3 + 1, i * 3 + 2) for i in range(len(triple_subset))]) scene = bpy.context.scene mesh.update() obj_new = bpy.data.objects.new("Torus", mesh) scene.objects.link(obj_new) obj_new.layers[:] = o.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 = obj_new.location inst_ob.parent = obj_new - + # BGE settings for testiing ''' inst_ob.game.physics_type = 'RIGID_BODY' @@ -310,38 +304,39 @@ def _main(self, DENSITY=1.0, SCALE=0.6, RAND_LOC=0.8, RAND_ALIGN=0.75): inst_ob.game.collision_margin = 0.1 obj_new.select = True ''' - - + # build faces from vert/normals - tri = Vector((0, 0 ,0.01)), Vector((0, 0, 0)), Vector((0.0, 0.01, 0.01)) - + 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 = [] + # 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 = radians(uniform(-180, 180.0)) - angle_aligned = -(ori.angle(Vector((0,1,0)) * quat, radians(180.0))) - - quat = Quaternion(no, (angle * (1.0-RAND_ALIGN)) + (angle_aligned * RAND_ALIGN)).cross(quat) + angle = radians(uniform(-180, 180.0)) + angle_aligned = -(ori.angle(Vector((0.0, 1.0, 0.0)) * quat, radians(180.0))) + + quat = Quaternion(no, (angle * (1.0 - RAND_ALIGN)) + (angle_aligned * RAND_ALIGN)).cross(quat) f = uniform(0.1, 1.2) * SCALE - + coords.append([co + ((tri[0] * f) * quat), co + ((tri[1] * f) * quat), co + ((tri[2] * f) * quat)]) # face_ind.append([i*3, i*3+1, i*3+2]) - - + apply_faces(coords) - main() -from bpy.props import * +from bpy.props import FloatProperty + + class Scatter(bpy.types.Operator): '''''' bl_idname = "object.scatter" @@ -355,7 +350,7 @@ class Scatter(bpy.types.Operator): 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 alignmet with the walls", default=0.75, min=0.0, max=1.0) @@ -373,7 +368,7 @@ class Scatter(bpy.types.Operator): for attr in self.__class__.__dict__["order"]: if not attr.startswith("_"): try: - setattr(self.properties, attr, getattr(self.__class__._parent.properties, attr)) + setattr(self.properties, attr, getattr(self.__class__._parent.properties, attr)) except: pass @@ -393,7 +388,7 @@ class Scatter(bpy.types.Operator): def draw(self, context): self.__class__._parent = self layout = self.layout - + for attr in self.__class__.__dict__["order"]: if not attr.startswith("_"): try: @@ -402,17 +397,19 @@ class Scatter(bpy.types.Operator): pass layout.operator_context = 'EXEC_DEFAULT' - props = layout.operator(self.bl_idname) + layout.operator(self.bl_idname) # Add to the menu menu_func = (lambda self, context: self.layout.operator(Scatter.bl_idname, text="Scatter", icon='AUTO')) + def register(): bpy.utils.register_class(Scatter) bpy.types.VIEW3D_PT_tools_objectmode.append(menu_func) + def unregister(): bpy.utils.unregister_class(Scatter) bpy.types.VIEW3D_PT_tools_objectmode.remove(menu_func)