Skip to content
Snippets Groups Projects
rope_alpha.py 30 KiB
Newer Older
  • Learn to ignore specific revisions
  • # Copyright (c) 2012 Jorge Hernandez - Melendez
    
    # ##### 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 #####
    
    
    # TODO : prop names into English, add missing tooltips
    
    
    bl_info = {
        "name": "Rope Creator",
        "description": "Dynamic rope (with cloth) creator",
        "author": "Jorge Hernandez - Melenedez",
    
        "blender": (2, 73, 0),
    
        "location": "Left Toolbar > ClothRope",
        "warning": "",
        "wiki_url": "",
        "category": "Add Mesh"
    }
    
    
    import bpy
    from bpy.types import Operator
    from bpy.props import (
            BoolProperty,
            FloatProperty,
            IntProperty,
            )
    
    
    def desocultar(quien):
        if quien == "todo":
            for ob in bpy.data.objects:
                ob.hide = False
        else:
            bpy.data.objects[quien].hide = False
    
    
    def deseleccionar_todo():
        bpy.ops.object.select_all(action='DESELECT')
    
    
    def seleccionar_todo():
        bpy.ops.object.select_all(action='SELECT')
    
    
    def salir_de_editmode():
    
        if bpy.context.mode in ["EDIT", "EDIT_MESH", "EDIT_CURVE"]:
    
            bpy.ops.object.mode_set(mode='OBJECT')
    
    
    
    def reset_scene():
        desocultar("todo")
    
        # playback to the start
    
        bpy.ops.screen.frame_jump(end=False)
        try:
            salir_de_editmode()
        except:
            pass
    
        try:
            area = bpy.context.area
            # expand everything in the outliner to be able to select children
            old_type = area.type
            area.type = 'OUTLINER'
            bpy.ops.outliner.expanded_toggle()
    
            # restore the original context
            area.type = old_type
    
            seleccionar_todo()
            bpy.ops.object.delete(use_global=False)
    
        except Exception as e:
            print("\n[rope_alpha]\nfunction: reset_scene\nError: %s" % e)
    
    
    
    def entrar_en_editmode():
        if bpy.context.mode == "OBJECT":
            bpy.ops.object.mode_set(mode='EDIT')
    
    
    def select_all_in_edit_mode(ob):
        if ob.mode != 'EDIT':
            entrar_en_editmode()
        bpy.ops.mesh.select_all(action="DESELECT")
        bpy.context.tool_settings.mesh_select_mode = (True, False, False)
        salir_de_editmode()
        for v in ob.data.vertices:
            if not v.select:
                v.select = True
        entrar_en_editmode()
    
    
    def deselect_all_in_edit_mode(ob):
        if ob.mode != 'EDIT':
            entrar_en_editmode()
        bpy.ops.mesh.select_all(action="DESELECT")
        bpy.context.tool_settings.mesh_select_mode = (True, False, False)
        salir_de_editmode()
        for v in ob.data.vertices:
            if not v.select:
                v.select = False
        entrar_en_editmode()
    
    
    def which_vertex_are_selected(ob):
        for v in ob.data.vertices:
            if v.select:
                print(str(v.index))
    
                print("Vertex " + str(v.index) + " is selected")
    
    
    
    def seleccionar_por_nombre(nombre):
        scn = bpy.context.scene
    
        bpy.data.objects[nombre].select_set(True)
    
        scn.objects.active = bpy.data.objects[nombre]
    
    
    def deseleccionar_por_nombre(nombre):
    
        bpy.data.objects[nombre].select_set(False)
    
    
    
    def crear_vertices(ob):
        ob.data.vertices.add(1)
        ob.data.update
    
    
    def borrar_elementos_seleccionados(tipo):
        if tipo == "vertices":
            bpy.ops.mesh.delete(type='VERT')
    
    
    def obtener_coords_vertex_seleccionados():
        coordenadas_de_vertices = []
        for ob in bpy.context.selected_objects:
            if ob.type == 'MESH':
                for v in ob.data.vertices:
                    if v.select:
                        coordenadas_de_vertices.append([v.co[0], v.co[1], v.co[2]])
                return coordenadas_de_vertices[0]
    
    
    def crear_locator(pos):
        bpy.ops.object.empty_add(
    
                type='PLAIN_AXES', radius=1, view_align=False,
                location=(pos[0], pos[1], pos[2]),
                layers=(True, False, False, False, False, False, False,
                        False, False, False, False, False, False, False,
                        False, False, False, False, False, False)
                )
    
    
    
    def extruir_vertices(longitud, cuantos_segmentos):
        bpy.ops.mesh.extrude_region_move(
    
                MESH_OT_extrude_region={"mirror": False},
                TRANSFORM_OT_translate={
                        "value": (longitud / cuantos_segmentos, 0, 0),
                        "constraint_axis": (True, False, False),
                        "constraint_orientation": 'GLOBAL', "mirror": False,
                        "proportional": 'DISABLED', "proportional_edit_falloff": 'SMOOTH',
                        "proportional_size": 1, "snap": False, "snap_target": 'CLOSEST',
                        "snap_point": (0, 0, 0), "snap_align": False, "snap_normal": (0, 0, 0),
                        "gpencil_strokes": False, "texture_space": False,
                        "remove_on_cancel": False, "release_confirm": False
                        }
                )
    
    
    
    def select_all_vertex_in_curve_bezier(bc):
        for i in range(len(bc.data.splines[0].points)):
            bc.data.splines[0].points[i].select = True
    
    
    def deselect_all_vertex_in_curve_bezier(bc):
        for i in range(len(bc.data.splines[0].points)):
            bc.data.splines[0].points[i].select = False
    
    
    def ocultar_relationships():
        for area in bpy.context.screen.areas:
            if area.type == 'VIEW_3D':
                area.spaces[0].show_relationship_lines = False
    
    
    class ClothRope(Operator):
        bl_idname = "clot.rope"
        bl_label = "Rope Cloth"
    
        bl_description = ("Create a new Scene with a Cloth modifier\n"
                          "Rope Simulation with hooked Helper Objects")
    
        ropelength: IntProperty(
    
                name="Rope Length",
                description="Length of the generated Rope",
                default=5
                )
    
        ropesegments: IntProperty(
    
                name="Rope Segments",
                description="Number of the Rope Segments",
                default=5
                )
    
        qcr: IntProperty(
    
                name="Collision Quality",
                description="Rope's Cloth modifier collsion quality",
                min=1, max=20,
                default=20
                )
    
        substeps: IntProperty(
    
                name="Rope Substeps",
                description="Rope's Cloth modifier quality",
                min=4, max=80,
                default=50
                )
    
        resrope: IntProperty(
    
                name="Rope Resolution",
                description="Rope's Bevel resolution",
                default=5
                )
    
        radiusrope: FloatProperty(
    
                name="Radius",
                description="Rope's Radius",
                min=0.04, max=1,
                default=0.04
                )
    
        hide_emptys: BoolProperty(
    
                name="Hide Empties",
                description="Hide Helper Objects",
                default=False
                )
    
    
        def execute(self, context):
    
            bpy.ops.scene.new(type="NEW")
            scene = bpy.context.scene
            scene.name = "Test Rope"
            seleccionar_todo()
    
    
            # For the middle to have x segments between the first and
            # last point, must add 1 to the quantity:
    
            cuantos_segmentos = self.ropesegments + 1
            calidad_de_colision = self.qcr
            substeps = self.substeps
            deseleccionar_todo()
    
            # collect the possible empties that already exist in the data
            empties_prev = [obj.name for obj in bpy.data.objects if obj.type == "EMPTY"]
    
            # create an empty that will be the parent of everything
    
            bpy.ops.object.empty_add(
    
                    type='SPHERE', radius=1, view_align=False, location=(0, 0, 0),
                    layers=(True, False, False, False, False, False, False, False,
                            False, False, False, False, False, False, False, False,
                            False, False, False, False)
                    )
    
            ob = bpy.context.selected_objects[0]
            ob.name = "Rope"
    
            # .001 and friends
            rope_name = ob.name
    
    
            # create a plane and delete it
    
            bpy.ops.mesh.primitive_plane_add(
    
                    radius=1, view_align=False, enter_editmode=False, location=(0, 0, 0),
                    layers=(True, False, False, False, False, False, False, False, False,
                            False, False, False, False, False, False, False, False,
                            False, False, False)
                    )
    
            ob = bpy.context.selected_objects[0]
    
            # .001 and friends
            cuerda_1_name = ob.name
    
            entrar_en_editmode()  # enter edit mode
    
            select_all_in_edit_mode(ob)
    
            borrar_elementos_seleccionados("vertices")
    
            salir_de_editmode()  # leave edit mode
            crear_vertices(ob)  # create a vertex
    
            # Creating a Group for the PIN
            # Group contains the vertices of the pin and the Group.001 contains the single main line
            entrar_en_editmode()  # enter edit mode
            bpy.ops.object.vertex_group_add()  # create a group
    
            select_all_in_edit_mode(ob)
    
            bpy.ops.object.vertex_group_assign()  # assign it
    
            salir_de_editmode()  # leave edit mode
    
            ob.vertex_groups[0].name = "Pin"
            deseleccionar_todo()
    
            seleccionar_por_nombre(cuerda_1_name)
    
            # extrude vertices:
    
            for i in range(cuantos_segmentos):
                entrar_en_editmode()
                extruir_vertices(longitud, cuantos_segmentos)
    
                bpy.ops.object.vertex_group_remove_from()
    
                # get the direction to create the locator on it's position
    
                pos = obtener_coords_vertex_seleccionados()
    
    
                salir_de_editmode()  # leave edit mode
                # create locator at position
    
                crear_locator(pos)
                deseleccionar_todo()
    
                seleccionar_por_nombre(cuerda_1_name)
    
    
            seleccionar_por_nombre(cuerda_1_name)  # select the rope
    
    
            pos = obtener_coords_vertex_seleccionados()  # get their positions
    
            # create the last locator
    
            crear_locator(pos)
            deseleccionar_todo()
    
            seleccionar_por_nombre(cuerda_1_name)
            entrar_en_editmode()  # enter edit mode
            bpy.ops.object.vertex_group_add()  # Creating Master guide group
    
            select_all_in_edit_mode(ob)
    
            bpy.ops.object.vertex_group_assign()  # and assign it
    
            ob.vertex_groups[1].name = "Guide_rope"
    
            # extrude the Curve so it has a minimum thickness for collide
    
            bpy.ops.mesh.extrude_region_move(
    
                    MESH_OT_extrude_region={"mirror": False},
                    TRANSFORM_OT_translate={
                            "value": (0, 0.005, 0), "constraint_axis": (False, True, False),
                            "constraint_orientation": 'GLOBAL', "mirror": False,
                            "proportional": 'DISABLED', "proportional_edit_falloff": 'SMOOTH',
                            "proportional_size": 1, "snap": False, "snap_target": 'CLOSEST',
                            "snap_point": (0, 0, 0), "snap_align": False, "snap_normal": (0, 0, 0),
                            "gpencil_strokes": False, "texture_space": False,
                            "remove_on_cancel": False, "release_confirm": False
                            }
                    )
    
            bpy.ops.object.vertex_group_remove_from()
            deselect_all_in_edit_mode(ob)
            salir_de_editmode()
            bpy.ops.object.modifier_add(type='CLOTH')
            bpy.context.object.modifiers["Cloth"].settings.use_pin_cloth = True
            bpy.context.object.modifiers["Cloth"].settings.vertex_group_mass = "Pin"
            bpy.context.object.modifiers["Cloth"].collision_settings.collision_quality = calidad_de_colision
            bpy.context.object.modifiers["Cloth"].settings.quality = substeps
    
    
            # Duplicate to convert into Curve:
            # select the vertices that are the part of the Group.001
            seleccionar_por_nombre(cuerda_1_name)
    
            entrar_en_editmode()
            bpy.ops.mesh.select_all(action="DESELECT")
            bpy.context.tool_settings.mesh_select_mode = (True, False, False)
            salir_de_editmode()
            gi = ob.vertex_groups["Guide_rope"].index  # get group index
    
            for v in ob.data.vertices:
                for g in v.groups:
                    if g.group == gi:  # compare with index in VertexGroupElement
                        v.select = True
    
    
            # now we have to make a table of names of cuerdas to see which one will be new
            cuerda_names = [obj.name for obj in bpy.data.objects if "cuerda" in obj.name]
    
    
    
            # we already have the selected guide:
            # duplicate it:
    
            bpy.ops.mesh.duplicate_move(
    
                    MESH_OT_duplicate={"mode": 1},
                    TRANSFORM_OT_translate={
                            "value": (0, 0, 0), "constraint_axis": (False, False, False),
                            "constraint_orientation": 'GLOBAL', "mirror": False,
                            "proportional": 'DISABLED', "proportional_edit_falloff": 'SMOOTH',
                            "proportional_size": 1, "snap": False, "snap_target": 'CLOSEST',
                            "snap_point": (0, 0, 0), "snap_align": False, "snap_normal": (0, 0, 0),
                            "gpencil_strokes": False, "texture_space": False,
                            "remove_on_cancel": False, "release_confirm": False
                            }
                    )
            # separate the selections:
    
            bpy.ops.mesh.separate(type='SELECTED')
            salir_de_editmode()
            deseleccionar_todo()
    
    
            cuerda_2_name = "cuerda.001"
            test = []
            for obj in bpy.data.objects:
                if "cuerda" in obj.name and obj.name not in cuerda_names:
                    cuerda_2_name = obj.name
                    test.append(obj.name)
    
            seleccionar_por_nombre(cuerda_2_name)
    
            # from the newly created curve remove the Cloth:
    
            bpy.ops.object.modifier_remove(modifier="Cloth")
    
            bpy.ops.object.convert(target='CURVE')
    
    
            # all Empties that are not previously present
    
            emptys = []
            for eo in bpy.data.objects:
    
                if eo.type == 'EMPTY' and eo.name not in empties_prev:
                    if eo.name != rope_name:
    
    
            # select and deselect:
            bc = bpy.data.objects[cuerda_2_name]
    
            for e in emptys:
                deseleccionar_todo()
                seleccionar_por_nombre(e.name)
                seleccionar_por_nombre(bc.name)
                entrar_en_editmode()
                deselect_all_vertex_in_curve_bezier(bc)
                bc.data.splines[0].points[n].select = True
                bpy.ops.object.hook_add_selob(use_bone=False)
                salir_de_editmode()
                n = n + 1
    
    
            ob = bpy.data.objects[cuerda_1_name]
    
            for e in emptys:
                deseleccionar_todo()
                seleccionar_por_nombre(e.name)
                seleccionar_por_nombre(ob.name)
                entrar_en_editmode()
                bpy.ops.mesh.select_all(action="DESELECT")
                bpy.context.tool_settings.mesh_select_mode = (True, False, False)
                salir_de_editmode()
    
                for v in ob.data.vertices:
                    if v.select:
                        v.select = False
                ob.data.vertices[n].select = True
                entrar_en_editmode()
                bpy.ops.object.vertex_parent_set()
    
                salir_de_editmode()
                n = n + 1
    
    
    
            # all parented to the spherical empty:
            seleccionar_por_nombre(cuerda_2_name)
            seleccionar_por_nombre(cuerda_1_name)
            seleccionar_por_nombre(rope_name)
    
            bpy.ops.object.parent_set(type='OBJECT', keep_transform=True)
            deseleccionar_todo()
    
    
            # do not display the relations
    
            ocultar_relationships()
    
            seleccionar_por_nombre(cuerda_2_name)
    
            # curved rope settings:
    
            bpy.context.object.data.fill_mode = 'FULL'
            bpy.context.object.data.bevel_depth = self.radiusrope
            bpy.context.object.data.bevel_resolution = self.resrope
    
            return {'FINISHED'}
    
        def invoke(self, context, event):
    
            return context.window_manager.invoke_props_dialog(self, width=350)
    
    
        def draw(self, context):
            layout = self.layout
            box = layout.box()
    
            col = box.column(align=True)
    
    
            col.label(text="Rope settings:")
    
            rowsub0.prop(self, "ropelength", text="Length")
    
            rowsub0.prop(self, "ropesegments", text="Segments")
            rowsub0.prop(self, "radiusrope", text="Radius")
    
            col.label(text="Quality Settings:")
    
            col.prop(self, "resrope", text="Resolution curve")
            col.prop(self, "qcr", text="Quality Collision")
            col.prop(self, "substeps", text="Substeps")
    
    
    
    class BallRope(Operator):
        bl_idname = "ball.rope"
    
        bl_label = "Wrecking Ball"
        bl_description = ("Create a new Scene with a Rigid Body simulation of\n"
                          "Wrecking Ball on a rope")
    
        ropelength2: IntProperty(
    
                name="Rope Length",
                description="Length of the Wrecking Ball rope",
                default=10
                )
    
        ropesegments2: IntProperty(
    
                name="Rope Segments",
                description="Number of the Wrecking Ball rope segments",
                min=0, max=999,
                default=6
                )
    
        radiuscubes: FloatProperty(
    
                name="Cube Radius",
                description="Size of the Linked Cubes helpers",
                default=0.5
                )
    
        radiusrope: FloatProperty(
    
                name="Rope Radius",
                description="Radius of the Rope",
                default=0.4
                )
    
        worldsteps: IntProperty(
    
                name="World Steps",
                description="Rigid Body Solver world steps per second (update)",
                min=60, max=1000,
                default=250
                )
    
        solveriterations: IntProperty(
    
                name="Solver Iterations",
                description="How many times the Rigid Body Solver should run",
                min=10, max=100,
                default=50
                )
    
        massball: IntProperty(
    
                name="Ball Mass",
                description="Mass of the Wrecking Ball",
                default=1
                )
    
        resrope: IntProperty(
    
                name="Resolution",
                description="Rope resolution",
                default=4
                )
    
        grados: FloatProperty(
    
                name="Degrees",
                description="Angle of the Wrecking Ball compared to the Ground Plane",
                default=45
                )
    
        separacion: FloatProperty(
    
                name="Link Cubes Gap",
                description="Space between the Rope's Linked Cubes",
                default=0.1
                )
    
        hidecubes: BoolProperty(
    
                name="Hide Link Cubes",
                description="Hide helper geometry for the Rope",
                default=False
                )
    
    
        def execute(self, context):
            world_steps = self.worldsteps
            solver_iterations = self.solveriterations
    
    
            # make a + 2, so the segments will be between the two end points...
    
            segmentos = self.ropesegments2 + 2
            offset_del_suelo = 1
            offset_del_suelo_real = (longitud / 2) + (segmentos / 2)
            radio = self.radiuscubes
            radiorope = self.radiusrope
            masa = self.massball
            resolucion = self.resrope
            rotrope = self.grados
            separation = self.separacion
            hidecubeslinks = self.hidecubes
    
            # add new scene
            bpy.ops.scene.new(type="NEW")
            scene = bpy.context.scene
            scene.name = "Test Ball"
    
    
            # collect the possible constraint empties that already exist in the data
            constraint_prev = [obj.name for obj in bpy.data.objects if
                               obj.type == "EMPTY" and "Constraint" in obj.name]
            # floor:
    
            bpy.ops.mesh.primitive_cube_add(
    
                    radius=1, view_align=False, enter_editmode=False, location=(0, 0, 0),
                    layers=(True, False, False, False, False, False, False, False, False,
                            False, False, False, False, False, False, False, False,
                            False, False, False)
                    )
    
            bpy.context.object.scale.x = 10 + longitud
            bpy.context.object.scale.y = 10 + longitud
            bpy.context.object.scale.z = 0.05
            bpy.context.object.name = "groundplane"
    
            # The secret agents .001, 002 etc.
            groundplane_name = bpy.context.object.name
    
    
            bpy.ops.rigidbody.objects_add(type='PASSIVE')
    
            cuboslink = []
            n = 0
            for i in range(segmentos):
    
                if i == 0:
                    i = offset_del_suelo
    
                else:  # if it is not 0, add one so it doesn't step on the first one starting from 1
    
                    i = i + offset_del_suelo
    
                separacion = longitud * 2 / segmentos  # distance between linked cubes
    
                bpy.ops.mesh.primitive_cube_add(
    
                        radius=1, view_align=False, enter_editmode=False,
                        location=(0, 0, i * separacion),
                        layers=(True, False, False, False, False, False, False, False,
                                False, False, False, False, False, False, False, False,
                                False, False, False, False)
                        )
    
                bpy.ops.rigidbody.objects_add(type='ACTIVE')
                bpy.context.object.name = "CubeLink"
                if n != 0:
    
                    bpy.context.object.display_type = 'WIRE'
    
                    bpy.context.object.hide_render = True
                n += 1
                bpy.context.object.scale.z = (longitud * 2) / (segmentos * 2) - separation
                bpy.context.object.scale.x = radio
                bpy.context.object.scale.y = radio
                cuboslink.append(bpy.context.object)
    
            for i in range(len(cuboslink)):
                deseleccionar_todo()
                if i != len(cuboslink) - 1:
                    nombre1 = cuboslink[i]
                    nombre2 = cuboslink[i + 1]
                    seleccionar_por_nombre(nombre1.name)
                    seleccionar_por_nombre(nombre2.name)
                    bpy.ops.rigidbody.connect()
    
    
            # select by name
            constraint_new = [
                        obj.name for obj in bpy.data.objects if
                        obj.type == "EMPTY" and "Constraint" in obj.name and
                        obj.name not in constraint_prev
                        ]
    
            for names in constraint_new:
                seleccionar_por_nombre(names)
    
            for c in bpy.context.selected_objects:
                c.rigid_body_constraint.type = 'POINT'
    
            # create a Bezier curve:
    
            bpy.ops.curve.primitive_bezier_curve_add(
    
                    radius=1, view_align=False, enter_editmode=False, location=(0, 0, 0),
                    layers=(True, False, False, False, False, False, False, False, False,
                    False, False, False, False, False, False, False, False, False, False, False)
                    )
    
            bpy.context.object.name = "Cuerda"
    
            # Blender will automatically append the .001
            # if it is already in data
            real_name = bpy.context.object.name
    
    
            for i in range(len(cuboslink)):
                cubonombre = cuboslink[i].name
                seleccionar_por_nombre(cubonombre)
    
                seleccionar_por_nombre(real_name)
    
                x = cuboslink[i].location[0]
                y = cuboslink[i].location[1]
                z = cuboslink[i].location[2]
    
    
                # if it is 0 make it start from 1 as the offset from the ground...
    
                if i == 0:
                    i = offset_del_suelo
    
                else:  # if it is not 0, add one so it doesn't step on the first one starting from 1
    
                    i = i + offset_del_suelo
    
                    # select all the vertices and delete them
                    select_all_vertex_in_curve_bezier(bpy.data.objects[real_name])
    
                    bpy.ops.curve.delete(type='VERT')
    
                    # create the first vertex:
    
                    bpy.ops.curve.vertex_add(location=(x, y, z))
                else:
    
                    bpy.ops.curve.extrude_move(
    
                            CURVE_OT_extrude={"mode": 'TRANSLATION'},
                            TRANSFORM_OT_translate={
                                "value": (0, 0, z / i),
                                "constraint_axis": (False, False, True),
                                "constraint_orientation": 'GLOBAL', "mirror": False,
                                "proportional": 'DISABLED', "proportional_edit_falloff": 'SMOOTH',
                                "proportional_size": 1, "snap": False, "snap_target": 'CLOSEST',
                                "snap_point": (0, 0, 0), "snap_align": False, "snap_normal": (0, 0, 0),
                                "gpencil_strokes": False, "texture_space": False,
                                "remove_on_cancel": False, "release_confirm": False
                                }
                            )
    
                bpy.ops.object.hook_add_selob(use_bone=False)
                salir_de_editmode()
                bpy.context.object.data.bevel_resolution = resolucion
                deseleccionar_todo()
    
    
            # create a sphere ball:
    
            deseleccionar_todo()
            seleccionar_por_nombre(cuboslink[0].name)
            entrar_en_editmode()
            z = cuboslink[0].scale.z + longitud / 2
            bpy.ops.view3d.snap_cursor_to_selected()
            bpy.ops.mesh.primitive_uv_sphere_add(
    
                    view_align=False, enter_editmode=False,
                    layers=(True, False, False, False, False, False, False,
                            False, False, False, False, False, False, False,
                            False, False, False, False, False, False)
                    )
    
            bpy.ops.transform.translate(
    
                    value=(0, 0, -z + 2), constraint_axis=(False, False, True),
                    constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED',
                    proportional_edit_falloff='SMOOTH', proportional_size=1
                    )
    
            bpy.ops.transform.resize(
    
                    value=(longitud / 2, longitud / 2, longitud / 2),
                    constraint_axis=(False, False, False),
                    constraint_orientation='GLOBAL',
                    mirror=False, proportional='DISABLED',
                    proportional_edit_falloff='SMOOTH', proportional_size=1
                    )
    
            deselect_all_in_edit_mode(cuboslink[0])
            salir_de_editmode()
            bpy.ops.object.shade_smooth()
            bpy.context.object.rigid_body.mass = masa
            bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS')
    
    
            # move it all up a bit more:
    
            deseleccionar_por_nombre(groundplane_name)
    
            bpy.ops.transform.translate(
    
                    value=(0, 0, offset_del_suelo_real),
                    constraint_axis=(False, False, True),
                    constraint_orientation='GLOBAL', mirror=False,
                    proportional='DISABLED', proportional_edit_falloff='SMOOTH',
                    proportional_size=1
                    )
    
    
            deseleccionar_todo()
            seleccionar_por_nombre(cuboslink[-1].name)
            bpy.ops.rigidbody.objects_add(type='PASSIVE')
    
            bpy.context.scene.rigidbody_world.steps_per_second = world_steps
            bpy.context.scene.rigidbody_world.solver_iterations = solver_iterations
    
    
            # move everything from the top one:
    
            seleccionar_por_nombre(cuboslink[-1].name)
            bpy.ops.view3d.snap_cursor_to_selected()
            seleccionar_todo()
    
            deseleccionar_por_nombre(groundplane_name)
    
            deseleccionar_por_nombre(cuboslink[-1].name)
            bpy.context.space_data.pivot_point = 'CURSOR'
            bpy.ops.transform.rotate(
    
                    value=rotrope, axis=(1, 0, 0),
                    constraint_axis=(True, False, False),
                    constraint_orientation='GLOBAL',
                    mirror=False, proportional='DISABLED',
                    proportional_edit_falloff='SMOOTH',
                    proportional_size=1
                    )
    
            bpy.context.space_data.pivot_point = 'MEDIAN_POINT'
            deseleccionar_todo()
    
    
            seleccionar_por_nombre(real_name)
    
            bpy.context.object.data.fill_mode = 'FULL'
            bpy.context.object.data.bevel_depth = radiorope
            for ob in bpy.data.objects:
                if ob.name != cuboslink[0].name:
                    if ob.name.find("CubeLink") >= 0:
                        deseleccionar_todo()
                        seleccionar_por_nombre(ob.name)
                        if hidecubeslinks:
                            bpy.context.object.hide = True
            ocultar_relationships()
            deseleccionar_todo()
            return {'FINISHED'}
    
        def invoke(self, context, event):
    
            return context.window_manager.invoke_props_dialog(self, width=350)
    
    
        def draw(self, context):
            layout = self.layout
            box = layout.box()
    
            col = box.column(align=True)
    
    
            col.label(text="Rope settings:")
    
            rowsub0.prop(self, "hidecubes", text="Hide Link Cubes")
    
            rowsub1 = col.row(align=True)
    
            rowsub1.prop(self, "ropelength2", text="Length")
    
            rowsub1.prop(self, "ropesegments2", text="Segments")
    
            rowsub2 = col.row(align=True)
            rowsub2.prop(self, "radiuscubes", text="Radius Link Cubes")
            rowsub2.prop(self, "radiusrope", text="Radius Rope")
    
            rowsub3 = col.row(align=True)
            rowsub3.prop(self, "grados", text="Degrees")
            rowsub3.prop(self, "separacion", text="Separation Link Cubes")
    
            col.label(text="Quality Settings:")
    
            col.prop(self, "resrope", text="Resolution Rope")
            col.prop(self, "massball", text="Ball Mass")
            col.prop(self, "worldsteps", text="World Steps")
            col.prop(self, "solveriterations", text="Solver Iterarions")
    
    
    
    # Register
    
    def register():
        bpy.utils.register_module(__name__)
    
    
    def unregister():
        bpy.utils.unregister_module(__name__)
    
    
    if __name__ == "__main__":
        register()