User Tools

Blender

cushion

source : https://www.youtube.com/watch?v=MLUnfE4FcOY

  • Unordered List Itemcube
  • s z
  • loop cuts hor 30
  • loop cut vert 1
  • physics → cloth → change preset to silk
  • collision → quality 4
  • enable self collision
  • field weights → gravity 0
  • add a “force field” (make sure its placed in the center of the cube)
  • strength 150
  • start animation and stop when enough
  • apply the cloth modifier
  • remove the force field

dae cleaner

import bpy
 
def decimate():
    for obj in bpy.data.objects:
        if obj.type == 'MESH':
            bpy.context.scene.objects.active = obj
            bpy.ops.object.modifier_add(type="DECIMATE")
            mod_name = bpy.context.object.modifiers[-1].name
            bpy.context.object.modifiers[mod_name].decimate_type = 'DISSOLVE'
            bpy.ops.object.modifier_apply(apply_as = 'DATA', modifier=mod_name)
 
def recenter_origin():
    bpy.context.scene.cursor_location = (0.0,0.0,0.0)
    for obj in bpy.data.objects:
        if obj.type == 'MESH':
            obj.select = True
    #bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS')
    bpy.ops.object.origin_set(type='ORIGIN_CURSOR')
 
def set_layers():
    for obj in bpy.data.objects:
        if obj.type == 'MESH':
            bpy.context.scene.objects.active = obj
            if obj.parent.name == "group_0":
                if obj.name[0:5] == "group":
                    bpy.context.object.layers[1] = True
                elif obj.name[0:8] == "instance":
                    bpy.context.object.layers[2] = True
            if obj.parent.name[0:8] == "instance":
                bpy.context.object.layers[3] = True
            if (obj.parent.name[0:5] == "group") and (obj.parent.name != "group_0"):
                bpy.context.object.layers[4] = True
            bpy.context.object.layers[0] = False
 
def unparent_all():
    bpy.ops.object.select_all(action='DESELECT')
    for obj in bpy.data.objects:
        bpy.context.scene.objects.active = obj
        if bpy.context.active_object.parent != None:
            obj.select = True
    bpy.ops.object.parent_clear(type='CLEAR_KEEP_TRANSFORM')
 
def select_all(status=True):
    #bpy.ops.object.select_all(action='SELECT')
    #bpy.ops.object.select_all(action='DESELECT')
    for obj in bpy.data.objects:
        obj.select = status
 
def hide_all(status=True):
    for obj in bpy.data.objects:
        bpy.context.scene.objects.active = obj
        bpy.context.object.hide = status
 
def remove_duplicates():
    unparent_all()
    bpy.ops.object.select_all(action='DESELECT')
    datas = {}
    for obj in bpy.data.objects:
        if obj.type == 'MESH':
            bpy.context.scene.objects.active = obj
            actual = bpy.context.scene.objects.active 
            if actual.data.name not in datas:
                datas[actual.data.name] = [obj.name]
            else:
                datas[actual.data.name].append(obj.name)
    #print(datas)
    for values in datas:
        length = len(datas[values])
        if length > 1:
            print(values, length, sep=':')
            to_delete = sorted(datas[values])[1:length]
            for value in to_delete:
                bpy.data.objects[value].select = True
                bpy.ops.object.delete()
 
        #if length == 6:
        #    print(datas[values])
        #print(values, datas[values][0].split('_')[1], sep=" : ")
        #print(to_delete)
 
print("== Decimation Start ==")
print(bpy.data.scenes['Scene'].statistics())
 
remove_duplicates()
decimate()
recenter_origin()
 
print("== Decimation End ==")
print(bpy.data.scenes['Scene'].statistics())

shortcuts

Camera

Shortcut Function Notes
ctrl+alt+0align camera to view

Interface

http://www.blendertips.com/hotkeys.html

Shortcut Function Notes
ctrl + up/dow arrowsshow / hide full screen panel
keypad 1/3/7change view

Beziers

Shortcut Function Notes
vset handle type
alt+cclose

Edit mode

Shortcut Function Notes
aselect all / deselect all
bbox select
bbbrush select
ggrab (translate)
rrotate
sscale
x/y/zusing axis
shift + x/y/zuse 2 axis except the one selected (ex : shift + z = use x+y)
xx/yy/zzuse local axis
ctrl + lselect linked elements
pseparate items
jjoin item
hhide
alt + hshow all
fcreate a face

Utils

Shortcut Function Notes
ctrl + ttracking mode to align camera to an object
alt + gclear coordinates
shift + c3D Cursor to origin

Blender gmaps

sources

installation

renderdoc
C:\Windows\System32\cmd.exe /c "SET RENDERDOC_HOOK_EGL=0 && START "" ^"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe^" --disable-gpu-sandbox --gpu-startup-dialog"
renderdoc blender addon

meshlab

procedure

capture
  • run renderdoc
  • go to “file/inject into process”
  • run chrome using the shortcut and see the alert
  • get the alert “process id” and use “refresh” in renderdoc to see the process and then push “inject” button (bottom right)
  • click on the “ok” button on the chrome alert
  • chrome should display numbers on the top left
  • go to google maps and set to satellite and 3d
  • explore the place with zooming and turn around the buildings
  • go to renderdoc and use “capture after” 5 seconds
  • go to chrome and move the 3d view and then it will hang shortly (it has been captured)
  • go to renderdoc to see the capture
  • right click and save the rdc file
  • use import rdc file inside blender
optimizing mesh
  • remove unuseful items
  • select all the items and join them (ctrl+j)
  • “edit mode” and “merge by distance”
optimize uv map
  • duplicate the item and disable the first one
  • remove the materials using this script :

import bpy

C = bpy.context

for i in range(0,len(C.object.material_slots)):
    C.object.active_material_index = 1
    bpy.ops.object.material_slot_remove()

bpy.ops.object.mode_set(mode = 'EDIT') 
bpy.ops.mesh.select_all(action = 'SELECT')
bpy.ops.object.material_slot_assign()
bpy.ops.object.mode_set(mode = 'OBJECT')

  • export selection as obj
  • go to meshlab
  • “file/import mesh”
  • select mesh
  • go to “filters / texture / parametrization : trivial per-triangle”
  • set “texture dimension” to 8192 and click “apply”
  • go to “render / show uv tex map” to see the result
  • “file / export mesh”
  • uncheck “vert / normal” and “face / color” so keep “wedge / texcoord”
  • go back to blender
  • delete the object
  • import obj
bake the new texture
  • select the imported mesh
  • go to “shader editor”
  • add a new material
  • add an “image texture” node (do not connect it)
  • create a new texture with 8192px size
  • change renderer to “cycles” (set device to “cpu”)
  • go to “bake” tab and select “bake type” as “diffuse”
  • under “influence”, disable “direct” and “indirect”
  • check “selected to active”
  • “ray distance” to 0.1
  • “output margin” 1px
  • select image texture node in shader editor
  • select disbled mesh and then select imported mesh (using ctrl to set the last one as active)
  • click “bake”
  • wait
  • connect the image node
  • rename the new texture and export it from the image editor

blender utils

blender 2.8 get names

import bpy
 
def getNames():
    selection_names = bpy.context.selected_objects
    for i in selection_names:
        print(i.name)
getNames()

blender 2.8 custom join

import bpy
   
def customJoin():
    # convert to single user
    bpy.ops.object.make_single_user(type='SELECTED_OBJECTS', object=True, obdata=True, material=False, animation=False)
    # get selection
    selected_objects = bpy.context.selected_objects
   
    # do the job
    for o in selected_objects:
        if o.type != 'EMPTY':
            # set active
            bpy.context.view_layer.objects.active = o
     
            # convert to mesh
            if o.type in  ['CURVE', 'FONT']:
                bpy.ops.object.convert(target='MESH')
     
            # apply modifiers
            for mod in o.modifiers:
                bpy.ops.object.modifier_apply(modifier = mod.name)
     
            # get the object's name
            object_name = o.name
     
            # get group
            if object_name in o.vertex_groups:
                group = o.vertex_groups[object_name]
            else:
                group = o.vertex_groups.new(name = object_name)
     
            # get vertices
            verts = []
            for vert in o.data.vertices:
                verts.append(vert.index)
     
            # assign vertices to group
            group.add(verts, 1.0, 'REPLACE')
 
    # join to a single object
    bpy.ops.object.join()
     
customJoin()

2.79 groups

import bpy

def giveGroup(groupname=""):
    selected = bpy.context.selected_objects
    for item in selected:
        bpy.context.scene.objects.active = item
        bpy.ops.object.group_link(group='veranda')

#giveGroup("veranda")

def giveMaterial(materialname=""):
    selected = bpy.context.selected_objects
    for item in selected:
        bpy.context.scene.objects.active = item
        bpy.context.object.active_material.name = materialname

giveMaterial("vitre")

addon template

import bpy

bl_info = {
    "name": "my test addon",
    "author": "jojo",
    "version": (1,0),
    "blender": (2,83,0),
    "category": "Object",
    "location": "Operator Search",
    "description": "More monkeys!!",
    "warning" : "",
    "doc_url": "",
    "tracker_url": ""
}

class MESH_OT_monkey_grid(bpy.types.Operator):
    """Let's spread some joy"""
    bl_idname = "mesh.monkey_grid"
    bl_label = "Monkey Grid"
    bl_options = {'REGISTER', 'UNDO'}

    count_x: bpy.props.IntProperty(
        name="X",
        description="Number of monkeys in the x direction",
        default=3,
        min=1,soft_max=10
    )
    count_y: bpy.props.IntProperty(
        name="Y",
        description="Number of monkeys in the y direction",
        default=2,
        min=1,soft_max=10
    )
    size: bpy.props.FloatProperty(
        name="Size",
        description="Size of each monkey",
        min=0,max=1
    )

    @classmethod
    def poll(cls, context):
        if context.area.type == 'VIEW_3D':
            return True
        return False

    def execute(self, context):
        for idx in range(self.count_x * self.count_y):
            x = idx % self.count_x
            y = idx // self.count_x
            bpy.ops.mesh.primitive_monkey_add(
                size=self.size,
                location=(x, y, 1))
        return {'FINISHED'}

class VIEW3D_PT_monkey_grid(bpy.types.Panel):
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = "Monkeys"
    bl_label = "Grid"

    def draw(self, context):
        self.layout.operator('mesh.monkey_grid',
            text='Default grid',
            icon='MONKEY')
        props = self.layout.operator('mesh.monkey_grid',
            text='Big grid',
            icon='MONKEY')
        props.count_x = 10
        props.count_y = 10
        props.size = 0.8

def register():
    bpy.utils.register_class(MESH_OT_monkey_grid)
    bpy.utils.register_class(VIEW3D_PT_monkey_grid)

def unregister():
    bpy.utils.unregister_class(MESH_OT_monkey_grid)
    bpy.utils.unregister_class(VIEW3D_PT_monkey_grid)

Simple button

source : https://blenderartists.org/t/calling-a-function-from-a-button/616752/7

import bpy



class SimpleOperator(bpy.types.Operator):
    """Tooltip"""
    bl_idname = "object.simple_operator"
    bl_label = "Simple Object Operator"

    @classmethod
    def poll(cls, context):
        return context.active_object is not None

    def execute(self, context):
        self.report({'INFO'}, "Button clicked!")
        return {'FINISHED'}


def draw_func(self, context):
    layout = self.layout
    layout.operator("object.simple_operator")
    

def register():
    bpy.utils.register_class(SimpleOperator)
    bpy.types.VIEW3D_HT_header.prepend(draw_func)


def unregister():
    bpy.utils.unregister_class(SimpleOperator)
    bpy.types.VIEW3D_HT_header.remove(draw_func)


if __name__ == "__main__":
    register()

bl_info = {
    "name": "Exploded Bake",
    "category": "Render",
}


import bpy


class ExplodedBake(bpy.types.Panel):
    """Creates a Panel in the Object properties window"""
    bl_label = "Exploded Bake"
    bl_idname = "OBJECT_PT_exploded_bake" # follow Blender convention for id names
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "object"
    
    def draw(self, context):
        layout = self.layout
        obj = context.object
        
        row = layout.row()
        row.prop(obj, "name")
        
        row = layout.row()
        row.operator("button.explode")


class buttonExplode(bpy.types.Operator):
    bl_idname = "button.explode" # translates to C-name BUTTON_OT_explode
    bl_label = "Button text"

    def execute(self, context):
        #self.report({'INFO'}, "Hello world!")
        print("hello")
        return {'FINISHED'}
    
    
# (un-)register entire module, so you don't need to add every class here...
def register():
    bpy.utils.register_module(__name__)


def unregister():
    bpy.utils.unregister_module(__name__)


if __name__ == "__main__":
    register()

import bpy

class SEBMAS_PROP_move_origins(bpy.types.PropertyGroup):

    def update_function(self, context):
        if self.move_origin_toggle:
            bpy.context.scene.tool_settings.use_transform_data_origin = True
            bpy.context.scene.tool_settings.snap_elements = {'VERTEX'}
        else:
            bpy.context.scene.tool_settings.use_transform_data_origin = False
            bpy.context.scene.tool_settings.snap_elements = {'INCREMENT'}
        return

    bpy.types.WindowManager.move_origin_toggle = bpy.props.BoolProperty(
        default = False,
        update = update_function)

class SEBMAS_PT_tools_move_origin(bpy.types.Panel):
    bl_category = "Tool"
    bl_context = ".objectmode"  # dot on purpose (access from topbar)
    bl_label = "SebMas"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'

    def draw(self, context):
        self.layout.prop(context.window_manager,
            'move_origin_toggle',
            text="Move origin",
            toggle=True,
            icon='MONKEY')

classes = (
    SEBMAS_PT_tools_move_origin,
)

def register():
    for cls in classes:
        bpy.utils.register_class(cls)

def unregister():
    for cls in reversed(classes):
        bpy.utils.unregister_class(cls)

if __name__ == "__main__":
    register()

bounding box

Select an object and then :

import bpy;
ob = bpy.data.scenes.active.objects.active;
ob.getData(mesh=True).verts.extend(ob.getBoundBox(0))
the object needs to dont have any “transformation”

inventory

import bpy
import csv

def displayLine(title, qty, price):
    print(title+ " : "+ str(qty)+ "("+ str(price* qty)+ ")")

def cherche():
    # get selection
    selected_objects = bpy.context.selected_objects

    inventory = {
        "eclisse-de-liaison-40-52" : {
            "price":4.75,
            "qty":0
        },
        "corniere-murale-2000mm": {
            "price":33.82,
            "qty":0
        },
        # solives
        "solive-omega 65-1000mm": {
            "price":17,
            "qty":0
        },
        "solive-omega 65-2000mm": {
            "price":34,
            "qty":0
        },
        "solive-omega 65-2500mm": {
            "price":42.44,
            "qty":0
        },
        "solive-omega 65-3000mm": {
            "price":51.03,
            "qty":0
        },
        # lambourdes
        "lambourde-omega 35-100mm": {
            "price":1.018,
            "qty":0
        },
        "lambourde-omega 35-150mm": {
            "price":1.52,
            "qty":0
        },
        "lambourde-omega 35-2000mm": {
            "price":20.37,
            "qty":0
        },
        "lambourde-omega 35-3000mm": {
            "price":30.52,
            "qty":0
        },
        "longueur-en-metres-lambourde-a-decouper": {
            "price":10.18,
            "qty":0
        },
        # profil de descente
        "profil-de-descente-55-20-100mm": {
            "price":0.57,
            "qty":0
        },
        "longueur-en-metres-profil-de-descente": {
            "price":5.78,
            "qty":0
        },
        # plots
        "plot": {
            "price":2.4,
            "qty":0
        },
        # lames (7 par m² donc a 90€ le m² ça fait 12.85 la lame)
        "lame-terrasse-1000mm": {
            "price":12.85,
            "qty":0
        },
        # scotch
        "bande-adhesive-epdm-100mm": {
            "price":0.12,
            "qty":0
        },
        "bande-adhesive-epdm-150mm": {
            "price":0.18,
            "qty":0
        },
        "bande-adhesive-epdm-1000mm": {
            "price":1.21,
            "qty":0
        },
        "bande-adhesive-epdm-2000mm": {
            "price":2.42,
            "qty":0
        },
        "longueur-en-metres-bande-adhesive": {
            "price": 1.21,
            "qty":0
        }
    }

    # do the job
    for o in selected_objects:
        if o.type != 'EMPTY':
            # get the object's name
            object_name = o.name

            for item, details in inventory.items():
                if object_name.__contains__(item):
                    details["qty"] += 1
                    # longueur bande adhesive
                    if object_name.__contains__("bande-adhesive-epdm-100mm"):
                        inventory["longueur-en-metres-bande-adhesive"]["qty"] += 0.1
                    if object_name.__contains__("bande-adhesive-epdm-150mm"):
                        inventory["longueur-en-metres-bande-adhesive"]["qty"] += 0.15
                    if object_name.__contains__("bande-adhesive-epdm-1000mm"):
                        inventory["longueur-en-metres-bande-adhesive"]["qty"] += 1
                    if object_name.__contains__("bande-adhesive-epdm-2000mm"):
                        inventory["longueur-en-metres-bande-adhesive"]["qty"] += 2
                    # longueur lambourde a decouper
                    if object_name.__contains__("lambourde-omega 35-100mm"):
                        inventory["longueur-en-metres-lambourde-a-decouper"]["qty"] += 0.1
                    if object_name.__contains__("lambourde-omega 35-150mm"):
                        inventory["longueur-en-metres-lambourde-a-decouper"]["qty"] += 0.15
                    # longueur profil de descente
                    if object_name.__contains__("profil-de-descente-55-20-100mm"):
                        inventory["longueur-en-metres-profil-de-descente"]["qty"] += 0.1
    return inventory

def montre(inventory):
    total_price = 0
    for item, value in inventory.items():
        if not item.__contains__("bande-adhesive-epdm")\
        and not item.__contains__("lambourde-omega 35-100mm")\
        and not item.__contains__("lambourde-omega 35-150mm")\
        and not item.__contains__("profil-de-descente-55-20-100mm"):
            if value["qty"] > 0:
                print(item)
                print("\t\tqty:\t"+ str(round(value["qty"], 2)))
                print("\t\tp/u:\t"+ str(value["price"])+ " ht")
                price = value["qty"]*value["price"]
                total_price += price
                print("\ttotal price:\t"+ str(round(price, 2))+ " ht\n")
    print("----------------------------------------")
    print("total price: "+ str(round((total_price* 1.206), 2))+ " ttc")

def writeCSV(inventory):
    total_price = 0
    rows = [['designation', 'qty', 'pu', 'total']]
    for item, value in inventory.items():
        if not item.__contains__("bande-adhesive-epdm")\
        and not item.__contains__("lambourde-omega 35-100mm")\
        and not item.__contains__("lambourde-omega 35-150mm")\
        and not item.__contains__("profil-de-descente-55-20-100mm"):
            if value["qty"] > 0:
                price = value["qty"]*value["price"]
                rows.append([item, round(value["qty"], 2), value["price"], round(price, 2)])

    with open(bpy.path.abspath("//")+'\\prix_terrasse.csv', 'w', newline='') as file: 
        writer = csv.writer(file, delimiter='|')
        writer.writerows(rows)
    
print("========================================")
bpy.ops.object.select_all(action='DESELECT')
# get collection
col = bpy.data.collections["terrassteel-droit"]

# get all children collections recursively
for subcol in col.children_recursive:
    for obj in col.all_objects:
        obj.select_set(True)


inventory = cherche()
montre(inventory)
writeCSV(inventory)
print("========================================")

script as module

import bpy
my_module = bpy.data.texts["Text"].as_module()

my_module.toto()

delete collection

# source : https://blender.stackexchange.com/a/173894

import bpy
#from bpy import context


name = "Collection 1"
remove_collection_objects = True

#coll = context.collection # 
coll = bpy.data.collections.get(name)

if coll:
    if remove_collection_objects:
        obs = [o for o in coll.objects if o.users == 1]
        while obs:
            bpy.data.objects.remove(obs.pop())

    bpy.data.collections.remove(coll)

duplicate collection linked

import bpy

def duplicate_collection_by_name(collection_name):
    original_collection = bpy.data.collections.get(collection_name)
    
    if original_collection is None:
        print(f"Collection '{collection_name}' does not exist.")
        return
    
    new_collection = original_collection.copy()
    new_collection.name = f"{collection_name}_duplicate"
    
    bpy.context.scene.collection.children.link(new_collection)

duplicate collection

import bpy

def duplicate_collection_by_name(collection_name):
    original_collection = bpy.data.collections.get(collection_name)
    
    if original_collection is None:
        print(f"Collection '{collection_name}' does not exist.")
        return
    
    new_collection = bpy.data.collections.new(f"{collection_name}_duplicate")
    bpy.context.scene.collection.children.link(new_collection)
    
    for obj in original_collection.objects:
        new_object = obj.copy()
        new_object.data = obj.data.copy()
        new_collection.objects.link(new_object)

delete empty collection

import bpy

def delete_empty_collections():
    collections = bpy.data.collections
    
    for collection in collections:
        if len(collection.objects) == 0:
            bpy.data.collections.remove(collection, do_unlink=True)

delete collection and its content

import bpy

def delete_collection_by_name(collection_name):
    collection = bpy.data.collections.get(collection_name)
    
    if collection is not None:
        bpy.data.collections.remove(collection, do_unlink=True)
    else:
        print(f"Collection '{collection_name}' does not exist.")

This website uses cookies. By using the website, you agree with storing cookies on your computer. Also, you acknowledge that you have read and understand our Privacy Policy. If you do not agree, please leave the website.

More information