diff options
author | Luca Bonavita <mindrones@gmail.com> | 2010-07-13 18:56:45 +0400 |
---|---|---|
committer | Luca Bonavita <mindrones@gmail.com> | 2010-07-13 18:56:45 +0400 |
commit | 92da7c0c51d02af71a27e80f5d57d31c3453626f (patch) | |
tree | b6af14e805656777bacf1a639d3849c74f7b82df /object_fracture | |
parent | 13f8ca3705a59f857f4f3d1f00ba40427241a0ed (diff) |
== scripts conforming ==
moved fracture/ to object_fracture/ so that appears near the other object_ scripts
Diffstat (limited to 'object_fracture')
-rw-r--r-- | object_fracture/__init__.py | 86 | ||||
-rw-r--r-- | object_fracture/data.blend | bin | 0 -> 232271 bytes | |||
-rw-r--r-- | object_fracture/fracture_ops.py | 492 | ||||
-rw-r--r-- | object_fracture/fracture_setup.py | 74 |
4 files changed, 652 insertions, 0 deletions
diff --git a/object_fracture/__init__.py b/object_fracture/__init__.py new file mode 100644 index 00000000..2e76543b --- /dev/null +++ b/object_fracture/__init__.py @@ -0,0 +1,86 @@ +# ##### 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 ##### + +bl_addon_info = { + 'name': 'Object: Fracture Tools', + 'author': 'pildanovak', + 'version': '2.0', + 'blender': (2, 5, 3), + 'location': 'Fracture tools (Search > Fracture Object & ,' \ + 'Add -> Fracture Helper Objects', + 'description': 'Fractured Object, Bomb, Projectile, Recorder', + 'warning': '', # used for warning icon and text in addons panel + 'wiki_url': 'http://wiki.blender.org/index.php/Extensions:2.5/Py/' \ + 'Scripts/Object/Fracture', + 'tracker_url': 'https://projects.blender.org/tracker/index.php?'\ + 'func=detail&aid=21793&group_id=153&atid=469', + 'category': 'Object'} + + +import bpy +from fracture import fracture_ops, fracture_setup + + +class INFO_MT_add_fracture_objects(bpy.types.Menu): + bl_idname = "INFO_MT_add_fracture_objects" + bl_label = "Fracture Helper Objects" + + def draw(self, context): + layout = self.layout + layout.operator_context = 'INVOKE_REGION_WIN' + + layout.operator("object.import_fracture_bomb", + text="Bomb") + layout.operator("object.import_fracture_projectile", + text="Projectile") + layout.operator("object.import_fracture_recorder", + text="Rigidbody Recorder") + +import space_info +# Define the submenu +menu_func = (lambda self, + context: self.layout.menu("INFO_MT_add_fracture_objects", icon="PLUGIN")) + + +def register(): + bpy.types.register(fracture_ops.FractureSimple) + bpy.types.register(fracture_ops.FractureGroup) + bpy.types.register(fracture_ops.ImportFractureRecorder) + bpy.types.register(fracture_ops.ImportFractureBomb) + bpy.types.register(fracture_ops.ImportFractureProjectile) + bpy.types.register(fracture_setup.SetupFractureShards) + bpy.types.register(INFO_MT_add_fracture_objects) + + # Add the "add fracture objects" menu to the "Add" menu + space_info.INFO_MT_add.append(menu_func) + + +def unregister(): + bpy.types.unregister(fracture_ops.FractureSimple) + bpy.types.unregister(fracture_ops.FractureGroup) + bpy.types.unregister(fracture_ops.ImportFractureRecorder) + bpy.types.unregister(fracture_ops.ImportFractureBomb) + bpy.types.unregister(fracture_ops.ImportFractureProjectile) + bpy.types.unregister(fracture_setup.SetupFractureShards) + bpy.types.unregister(INFO_MT_add_fracture_objects) + + # Remove "add fracture objects" menu from the "Add" menu. + space_info.INFO_MT_add.remove(menu_func) + +if __name__ == "__main__": + register() diff --git a/object_fracture/data.blend b/object_fracture/data.blend Binary files differnew file mode 100644 index 00000000..e0e72dcf --- /dev/null +++ b/object_fracture/data.blend diff --git a/object_fracture/fracture_ops.py b/object_fracture/fracture_ops.py new file mode 100644 index 00000000..c6043b8e --- /dev/null +++ b/object_fracture/fracture_ops.py @@ -0,0 +1,492 @@ +# ##### 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 ##### + +import bpy +from bpy.props import * +import os +import random +import mathutils +from mathutils import * + + +def create_cutter(context, crack_type, scale, roughness): + ncuts = 12 + if crack_type == 'FLAT' or crack_type == 'FLAT_ROUGH': + bpy.ops.mesh.primitive_cube_add( + view_align=False, + enter_editmode=False, + location=(0, 0, 0), + rotation=(0, 0, 0), + layer=(True, False, False, False, + False, False, False, False, + False, False, False, False, + False, False, False, False, + False, False, False, False)) + + for v in context.scene.objects.active.data.verts: + v.co[0] += 1 + v.co[0] *= scale + v.co[1] *= scale + v.co[2] *= scale + + bpy.ops.object.editmode_toggle() + bpy.ops.mesh.faces_shade_smooth() + bpy.ops.uv.reset() + + if crack_type == 'FLAT_ROUGH': + bpy.ops.mesh.subdivide( + number_cuts=ncuts, + fractal=roughness * 7 * scale, + smoothness=0) + + bpy.ops.mesh.vertices_smooth(repeat=5) + + bpy.ops.object.editmode_toggle() + + if crack_type == 'SPHERE' or crack_type == 'SPHERE_ROUGH': + bpy.ops.mesh.primitive_ico_sphere_add(subdivisions=4, + size=1, + view_align=False, + enter_editmode=False, + location=(0, 0, 0), + rotation=(0, 0, 0), + layer=(True, False, False, False, + False, False, False, False, + False, False, False, False, + False, False, False, False, + False, False, False, False, + False, False, False, False, + False, False, False, False, + False, False, False, False)) + + bpy.ops.object.editmode_toggle() + bpy.ops.mesh.faces_shade_smooth() + bpy.ops.uv.smart_project(angle_limit=66, island_margin=0) + + bpy.ops.object.editmode_toggle() + for v in context.scene.objects.active.data.verts: + v.co[0] += 1 + v.co[0] *= scale + v.co[1] *= scale + v.co[2] *= scale + + if crack_type == 'SPHERE_ROUGH': + for v in context.scene.objects.active.data.verts: + v.co[0] += roughness * scale * 0.2 * (random.random() - 0.5) + v.co[1] += roughness * scale * 0.1 * (random.random() - 0.5) + v.co[2] += roughness * scale * 0.1 * (random.random() - 0.5) + + bpy.context.scene.objects.active.selected = True + + ''' + # Adding fracture material + # @todo Doesn't work at all yet. + sce = bpy.context.scene + if bpy.data.materials.get('fracture')==None: + bpy.ops.material.new() + bpy.ops.object.material_slot_add() + sce.objects.active.material_slots[0].material.name = 'fracture' + else: + bpy.ops.object.material_slot_add() + sce.objects.active.material_slots[0].material + = bpy.data.materials['fracture'] + ''' + + +#UNWRAP +def getsizefrommesh(ob): + bb = ob.bound_box + return ( + bb[5][0] - bb[0][0], + bb[3][1] - bb[0][1], + bb[1][2] - bb[0][2]) + + +def getIslands(shard): + sm = shard.data + islands = [] + vgroups = [] + fgroups = [] + + vgi = [] + for v in sm.verts: + vgi.append(-1) + + gindex = 0 + for i in range(len(vgi)): + if vgi[i] == -1: + gproc = [i] + vgroups.append([i]) + fgroups.append([]) + + while len(gproc) > 0: + i = gproc.pop(0) + for f in sm.faces: + #if i in f.verts: + for v in f.verts: + if v == i: + for v1 in f.verts: + if vgi[v1] == -1: + vgi[v1] = gindex + vgroups[gindex].append(v1) + gproc.append(v1) + + fgroups[gindex].append(f.index) + + gindex += 1 + + #print( gindex) + + if gindex == 1: + shards = [shard] + + else: + shards = [] + for gi in range(0, gindex): + bpy.ops.object.select_all(action='DESELECT') + bpy.context.scene.objects.active = shard + shard.selected = True + bpy.ops.object.duplicate(linked=False, mode=1) + a = bpy.context.scene.objects.active + sm = a.data + print (a.name) + + bpy.ops.object.editmode_toggle() + bpy.ops.mesh.select_all(action='DESELECT') + bpy.ops.object.editmode_toggle() + + for x in range(len(sm.verts) - 1, -1, -1): + if vgi[x] != gi: + #print('getIslands: selecting') + #print('getIslands: ' + str(x)) + a.data.verts[x].selected = True + + print(bpy.context.scene.objects.active.name) + + bpy.ops.object.editmode_toggle() + bpy.ops.mesh.delete() + bpy.ops.object.editmode_toggle() + + bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY') + + shards.append(a) + + bpy.context.scene.objects.unlink(shard) + + return shards + + +def boolop(ob, cutter, op): + sce = bpy.context.scene + + fault = 0 + new_shards = [] + + sizex, sizey, sizez = getsizefrommesh(ob) + gsize = sizex + sizey + sizez + + bpy.ops.object.select_all() + ob.selected = True + sce.objects.active = ob + cutter.selected = False + + bpy.ops.object.modifier_add(type='BOOLEAN') + a = sce.objects.active + a.modifiers['Boolean'].object = cutter + a.modifiers['Boolean'].operation = op + + nmesh = a.create_mesh(sce, apply_modifiers=True, settings='PREVIEW') + + if len(nmesh.verts) > 0: + a.modifiers.remove(a.modifiers['Boolean']) + bpy.ops.object.duplicate(linked=False, mode=1) + + new_shard = sce.objects.active + new_shard.data = nmesh + #scene.objects.link(new_shard) + + new_shard.location = a.location + bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY') + + sizex, sizey, sizez = getsizefrommesh(new_shard) + gsize2 = sizex + sizey + sizez + + if gsize2 > gsize * 1.01: # Size check + print (gsize2, gsize, ob.name, cutter.name) + fault = 1 + #print ('boolop: sizeerror') + + elif min(nmesh.edge_face_count) < 2: # Manifold check + fault = 1 + + if not fault: + new_shards = getIslands(new_shard) + + else: + sce.objects.unlink(new_shard) + + else: + fault = 2 + + return fault, new_shards + + +def splitobject(context, ob, crack_type, roughness): + scene = context.scene + + size = getsizefrommesh(ob) + shards = [] + scale = max(size) * 1.3 + + create_cutter(context, crack_type, scale, roughness) + cutter = context.active_object + cutter.location = ob.location + + cutter.location[0] += random.random() * size[0] * 0.1 + cutter.location[1] += random.random() * size[1] * 0.1 + cutter.location[2] += random.random() * size[2] * 0.1 + cutter.rotation_euler = [ + random.random() * 5000.0, + random.random() * 5000.0, + random.random() * 5000.0] + + scene.objects.active = ob + operations = ['INTERSECT', 'DIFFERENCE'] + + for op in operations: + fault, newshards = boolop(ob, cutter, op) + + shards.extend(newshards) + if fault > 0: + # Delete all shards in case of fault from previous operation. + for s in shards: + scene.objects.unlink(s) + + scene.objects.unlink(cutter) + #print('splitobject: fault') + + return [ob] + + if shards[0] != ob: + bpy.context.scene.objects.unlink(ob) + + bpy.context.scene.objects.unlink(cutter) + + return shards + + +def fracture_basic(context, nshards, crack_type, roughness): + tobesplit = [] + shards = [] + + for ob in context.scene.objects: + if ob.selected: + tobesplit.append(ob) + + i = 1 # I counts shards, starts with 1 - the original object + iter = 0 # counts iterations, to prevent eternal loops in case + # of boolean faults + + maxshards = nshards * len(tobesplit) + + while i < maxshards and len(tobesplit) > 0 and iter < maxshards * 10: + ob = tobesplit.pop(0) + newshards = splitobject(context, ob, crack_type, roughness) + + tobesplit.extend(newshards) + + if len(newshards) > 1: + shards.extend(newshards) + #shards.remove(ob) + + i += (len(newshards) - 1) + + #print('fracture_basic: ' + str(i)) + #print('fracture_basic: lenobs', len(context.scene.objects)) + + iter += 1 + + +def fracture_group(context, group): + tobesplit = [] + shards = [] + + for ob in context.scene.objects: + if (ob.selected + and (len(ob.users_group) == 0 or ob.users_group[0].name != group)): + tobesplit.append(ob) + + cutters = bpy.data.groups[group].objects + + # @todo This can be optimized. + # Avoid booleans on obs where bbox doesn't intersect. + i = 0 + for ob in tobesplit: + for cutter in cutters: + fault, newshards = boolop(ob, cutter, 'INTERSECT') + shards.extend(newshards) + + if fault == 1: + # Delete all shards in case of fault from previous operation. + for s in shards: + bpy.context.scene.objects.unlink(s) + + #print('fracture_group: fault') + #print('fracture_group: ' + str(i)) + + return + + i += 1 + + +class FractureSimple(bpy.types.Operator): + '''Split object with boolean operations for simulation, uses an object.''' + bl_idname = "object.fracture_simple" + bl_label = "Fracture Object" + bl_options = {'REGISTER', 'UNDO'} + + exe = BoolProperty(name="Execute", + description="If it shall actually run, for optimal performance...", + default=False) + + hierarchy = BoolProperty(name="Generate hierarchy", + description="Hierarchy is usefull for simulation of objects" \ + " breaking in motion.", + default=False) + + nshards = IntProperty(name="Number of shards", + description="Number of shards the object should be split into.", + min=2, + default=5) + + crack_type = EnumProperty(name='Crack type', + items=( + ('FLAT', 'Flat', 'a'), + ('FLAT_ROUGH', 'Flat rough', 'a'), + ('SPHERE', 'Spherical', 'a'), + ('SPHERE_ROUGH', 'Spherical rough', 'a')), + description='Look of the fracture surface', + default='FLAT') + + roughness = FloatProperty(name="Roughness", + description="Roughness of the fracture surface", + min=0.0, + max=3.0, + default=0.5) + + def execute(self, context): + #getIslands(context.object) + props = self.properties + + if props.exe: + fracture_basic(context, + props.nshards, + props.crack_type, + props.roughness) + + return {'FINISHED'} + + +class FractureGroup(bpy.types.Operator): + '''Split object with boolean operations for simulation, uses a group.''' + bl_idname = "object.fracture_group" + bl_label = "Fracture Object (Group)" + bl_options = {'REGISTER', 'UNDO'} + + exe = BoolProperty(name="Execute", + description="If it shall actually run, for optimal performance...", + default=False) + + e = [] + for i, g in enumerate(bpy.data.groups): + e.append((g.name, g.name, '')) + + group = EnumProperty(name='Group (hit F8 to refresh list)', + items=e, + description='Specify the group used for fracturing') + + def execute(self, context): + #getIslands(context.object) + + if self.properties.exe: + fracture_group(context, self.properties.group) + + return {'FINISHED'} + + +##################################################################### +# Import Functions + +def import_object(obname): + opath = "//data.blend\\Object\\" + obname + s = os.sep + dpath = bpy.utils.script_paths()[0] + \ + '%saddons%sfracture%sdata.blend\\Object\\' % (s, s, s) + + # DEBUG + #print('import_object: ' + opath) + + bpy.ops.wm.link_append( + filepath=opath, + filename=obname, + directory=dpath, + filemode=1, + link=False, + autoselect=True, + active_layer=True, + instance_groups=True, + relative_path=True) + + for ob in bpy.context.selected_objects: + ob.location = bpy.context.scene.cursor_location + + +class ImportFractureRecorder(bpy.types.Operator): + '''Imports a rigidbody recorder''' + bl_idname = "object.import_fracture_recorder" + bl_label = "Add Rigidbody Recorder (Fracture)" + bl_options = {'REGISTER', 'UNDO'} + + def execute(self, context): + import_object("RECORDER") + + return {'FINISHED'} + + +class ImportFractureBomb(bpy.types.Operator): + '''Import a bomb''' + bl_idname = "object.import_fracture_bomb" + bl_label = "Add Bomb (Fracture)" + bl_options = {'REGISTER', 'UNDO'} + + def execute(self, context): + import_object("BOMB") + + return {'FINISHED'} + + +class ImportFractureProjectile(bpy.types.Operator, ): + '''Imports a projectile''' + bl_idname = "object.import_fracture_projectile" + bl_label = "Add Projectile (Fracture)" + bl_options = {'REGISTER', 'UNDO'} + + def execute(self, context): + import_object("PROJECTILE") + + return {'FINISHED'} diff --git a/object_fracture/fracture_setup.py b/object_fracture/fracture_setup.py new file mode 100644 index 00000000..235dd213 --- /dev/null +++ b/object_fracture/fracture_setup.py @@ -0,0 +1,74 @@ +# ##### 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 ##### + +import bpy +from bpy.props import * + + +def getsizefrommesh(ob): + bb = ob.bound_box + return( + bb[5][0] - bb[0][0], + bb[3][1] - bb[0][1], + bb[1][2] - bb[0][2]) + + +def setupshards(context): + sce = context.scene + #print(dir(context)) + #bpy.data.scenes[0].game_data.all_frames + + tobeprocessed = [] + for ob in sce.objects: + if ob.selected: + tobeprocessed.append(ob) + + for ob in tobeprocessed: + g = ob.game + + g.physics_type = 'RIGID_BODY' + g.use_collision_bounds = 1 + g.collision_bounds = 'CONVEX_HULL' + g.rotation_damping = 0.9 + + sizex, sizey, sizez = getsizefrommesh(ob) + approxvolume = sizex * sizey * sizez + g.mass = approxvolume + + sce.objects.active = ob + + bpy.ops.object.game_property_new() + g.properties['prop'].name = 'shard' + #sm=FloatProperty(name='shard',description='shardprop',default=0.0) + #print (sm) + #np=bpy.types.GameFloatProperty(sm) + #name='shard',type='BOOL', value=1 + #print(ob) + + +class SetupFractureShards(bpy.types.Operator): + '''''' + bl_idname = "object.setup_fracture_shards" + bl_label = "Setup Fracture Shards" + bl_options = {'REGISTER', 'UNDO'} + + #def poll(self, context): + + def execute(self, context): + setupshards(context) + return {'FINISHED'} |