/r/blenderpython
A community to discuss Blender's built-in Python functionality, as well as to share your Blender/Python related work.
Welcome to Blender Python! There aren't too many rules, but please stick to them.
Webpages:
Discord:
Subreddits:
/r/learnpython
/r/blender
/r/BlenderGameEngine
/r/coding
/r/gamedev
/r/opengl
Books:
/r/blenderpython
Hi all!
I’ve written a Blender script that creates a vector attribute on selected meshes. The idea is to connect this vector to an image texture, which then simulates a camera-based projection. It works fine for most cases, but I’m facing an issue when dealing with objects that have non-uniform scaling or rotations applied in Object Mode.
The problem I’m encountering is that when I apply scaling or rotation in Object Mode, and then run the script, the image texture doesn’t behave as expected. The texture should maintain the same relative scale and rotation from the camera’s perspective, but it distorts when objects are non-uniformly scaled or rotated.
Here’s the script I’ve been using:
import bpy
import mathutils
bpy.ops.object.mode_set(mode='OBJECT')
camera =
bpy.context.scene.camera
if camera is None:
print("No camera found in the scene.")
else:
camera_location = camera.matrix_world.translation
camera_matrix = camera.matrix_world.to_3x3()
rotation_matrix = mathutils.Matrix([[-1, 0, 0], [0, -1, 0], [0, 0, 1]])
for obj in bpy.context.selected_objects:
if obj.type == 'MESH':
if "camera" not in obj.data.attributes:
obj.data.attributes.new(name="camera", type='FLOAT_VECTOR', domain='POINT')
camera_attr = obj.data.attributes["camera"]
for vert in obj.data.vertices:
vertex_world_pos = obj.matrix_world @
vert.co
vector_to_camera = camera_location - vertex_world_pos
vector_local_space = camera_matrix.inverted() @ vector_to_camera
rotated_vector = rotation_matrix @ vector_local_space
camera_attr.data[vert.index].vector = rotated_vector
bpy.ops.object.mode_set(mode='OBJECT')
Hey, im a newbie in using python, does anyone know where i can find information about how to import data from another .blend file, e.g. if i want a button that generates an object+modifier that already exists in another .blend file? Thanks in advance.
Hey everyone, I’m wanting to try and make a simple addon to search the side (N) panel for addons (as my addon layout is getting out of hand.)
Can I open and close these individual addon tabs with python?
I had a quick google and could only find ways to open and close the entire side panel, not individual tabs within. Claude.ai also couldnt find a solution so thought Id ask here. If someone could point me in the right direction I would be really grateful.
Thanks!
I am currently working on a vertex animation texture generation inside blender and I got pretty far, I specifically was working on remeshing water simulations to always use constant amount of vertecies/polygons.
But now I wanted to debug it with a simple Icosphere rolling down from a slope,
The problem is Bender refuses to make correct copies and get the correct vertex position data even tho it fully works for imported alembic files of water drops but not a simple rigid body simulation?
They both are mesh cash animated objects inside blender and the script expects them to be
relevant function from my script:
I'm looking for help creating a script that converts the material base color of selected (or all) objects into a color attribute. Ideally, the script should work by iterating through the material slots, without the need for baking. I tried using ChatGPT, but it resulted in several errors. Any guidance would be greatly appreciated!
Sorry, I don't know if it's annoying to ask for help with ChatGPT generated code. I gather it's a little imperfect. I myself am not a coder (hence ChatGPT).
import bpy
import os
# Define the directory to search for .gltf files
directory_path = r"D:\STOCK IMAGES\3D Stock Models\Buildings\_RUINED WALLS\07 Brick wall scan (great detail but no tops or sides)"
# Search for .gltf files in the directory
gltf_file = None
for file_name in os.listdir(directory_path):
if file_name.endswith(".gltf"):
gltf_file = os.path.join(directory_path, file_name)
break # Stop after finding the first .gltf file
if gltf_file:
# Import the found GLTF file
bpy.ops.import_scene.gltf(filepath=gltf_file)
# Unparent all objects while keeping their transformation
for obj in bpy.context.scene.objects:
if obj.parent is not None:
obj.select_set(True)
bpy.ops.object.parent_clear(type='CLEAR_KEEP_TRANSFORM')
# Delete all empties
for obj in bpy.context.scene.objects:
if obj.type == 'EMPTY':
obj.select_set(True)
bpy.ops.object.delete()
# Join all mesh objects into one
bpy.ops.object.select_all(action='DESELECT')
for obj in bpy.context.scene.objects:
if obj.type == 'MESH':
obj.select_set(True)
bpy.context.view_layer.objects.active = obj # Set one of the meshes as the active object
bpy.ops.object.join() # Join the selected meshes
# Weld vertices by removing doubles
bpy.ops.object.mode_set(mode='EDIT') # Switch to edit mode
bpy.ops.mesh.select_all(action='SELECT') # Select all vertices
bpy.ops.mesh.remove_doubles(threshold=0.001) # Merge vertices that are very close
bpy.ops.object.mode_set(mode='OBJECT') # Return to object mode
print(f"Imported and cleaned: {gltf_file}")
else:
print("No .gltf files found in the specified directory.")
I simply want to automate the importing of sketchfab scans so they unparent and merge.
I don't know much about coding i kinda understand the very basics so i am using chatgpt and other AIs to help me make it but i got stuck and the AI is just giving me the same code without any difference so basically i am trying to make an addon that takes the selected 3d model and takes each part and laies it out inside a mesh that i input the dimensions of basically like maxcut (i cant use max cut because it doesnt use weird shapes and i want it tin blender) so right now i cant get the xyz input field to work and there is a bunch of errors that i cant get the AI to fix
here is what i have so far
import bpy
import mathutils # Use mathutils for Blender math functions
class MyAddonSettings(bpy.types.PropertyGroup):
"""Stores dimensions for the addon"""
dimensions_x = bpy.props.FloatProperty(name="Dimension X", default=1.0)
dimensions_y = bpy.props.FloatProperty(name="Dimension Y", default=1.0)
dimensions_z = bpy.props.FloatProperty(name="Dimension Z", default=1.0)
class CopyPasteFlatOperator(bpy.types.Operator):
"""Copy and Paste Flat Operator"""
bl_idname = "object.copy_paste_flat" # Unique identifier
bl_label = "Copy Paste Flat" # Label displayed in the menu
def execute(self, context):
selected_meshes = bpy.context.selected_objects
new_collection = bpy.data.collections.new("Copied Meshes")
bpy.context.scene.collection.children.link(new_collection)
container_mesh = bpy.data.meshes.new(name="Container Mesh")
container_mesh.from_pydata(coords=[], edges=[], faces=[]) # Empty mesh data
container_object = bpy.data.objects.new(
name="Container", object_data=container_mesh
)
bpy.context.collection.objects.link(container_object)
container_object.scale = (
context.scene.my_addon.dimensions_x,
context.scene.my_addon.dimensions_y,
context.scene.my_addon.dimensions_z,
)
for mesh in selected_meshes:
mesh_copy = mesh.copy()
mesh_copy.data = mesh.data.copy()
new_collection.objects.link(mesh_copy)
mesh_copy.location = container_object.location + (
context.scene.my_addon.dimensions_x / 2,
context.scene.my_addon.dimensions_y / 2,
context.scene.my_addon.dimensions_z / 2,
)
max_x, max_y, max_z = (
context.scene.my_addon.dimensions_x,
context.scene.my_addon.dimensions_y,
context.scene.my_addon.dimensions_z,
)
min_x, min_y, min_z = -max_x, -max_y, -max_z
mesh_copy.scale = (
min(1.0, max_x / mesh_copy.dimensions.x),
min(1.0, max_y / mesh_copy.dimensions.y),
min(1.0, max_z / mesh_copy.dimensions.z),
)
return {'FINISHED'} # Indicate successful execution
class LayoutDemoPanel(bpy.types.Panel):
"""Creates a Panel in the scene context of the properties editor"""
bl_label = "Blender addon"
bl_idname = "SCENE_PT_layout"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "object"
u/classmethod
def get_settings(cls):
return bpy.context.scene.my_addon
def draw(self, context):
layout = self.layout
settings = self.get_settings()
layout.prop(settings, "dimensions_x")
layout.prop(settings, "dimensions_y")
layout.prop(settings, "dimensions_z")
layout.label(text="Create Mesh and Paste Copies:")
row = layout.row()
row.scale_y = 3.0
row.operator("object.copy_paste_flat")
def register():
bpy.utils.register_class(MyAddonSettings) # Register the property group first
bpy.utils.register_class(LayoutDemoPanel)
bpy.utils.register_class(CopyPasteFlatOperator)
bpy.types.Scene.my_addon = bpy.props.PointerProperty(type=MyAddonSettings)
def unregister():
del bpy.types.Scene.my_addon
bpy.utils.unregister_class(CopyPasteFlatOperator)
bpy.utils.unregister_class(LayoutDemoPanel)
bpy.utils.unregister_class(MyAddonSettings)
if __name__ == "__main__":
register()
Hello. I am working on a script that includes rotating and moving bones in Pose Mode. I would like to see these rotations in real time (i change the rotation on each frame) but having to always change from object to pose mode is slowing everything down.
I would also like to edit multiple different armatures per frame and my current script doesn't seem very scalable.
Is there a way to edit the rotation of the bones in Pose Mode without entering pose mode. Thanks
I´m working on a project where there is a scene with objects spread across it, simulating a simple warehouse.
I have a camera object that will be placed in several points and render images. Right now I want to check if the camera has an object in its view, before rendering the image. I also want to return a list of the objects that are in camera view, to save in a separate JSON file. But I´m having trouble getting the correct objects in the view, there may be problems with obtaining the camera frustum.
Can someone help me or provide me code that does what I intend to do?
This is my first attempt at Blender Python scripting. I am following this tutorial on bendy bones. I'm focusing only on the first part of the video which goes all the way to around 3 minutes.
I am trying to automate this process, and with ChatGPT doing the heavy lifting, I got most of the script working. What you do is select a bendy bone, and when you run the script it:
Miraculously, the script mostly works how I want it to, but then I noticed something strange and incredibly irritating. It makes no sense, which is why I'm here: the bone that gets placed into the start and end handle section is completely random. Sometimes they are correctly placed, sometimes "start" gets put into the end handle, and sometimes even the original bone gets placed in these sections!
Someone please put me out of my misery!
I have a bpy script that can take some time to execute. It basically freezes Blender. Is there any way to show a loader or something while my bpy script is executing?
I am trying to create a Python script that gives me the current visible keyframes in the Dope Sheet editor and their respective screen coordinates.
So, for example, in this image (Figure 1), I want my script to print out 18 lines (because there are 9 objects and each object has 2 keyframes), describing each of these keyframes' screen coordinates or their relative coordinates to the Dope Sheet window.
And in this example (Figure 2), I want my script to print out 34 lines (notice that the number of objects is the same as in Figure 1, but this time I have expanded the hierarchy for cube.007 and cube.008), including the screen coordinates of each keyframe.
I'm making an addon that has some remapping of the keyconfigurations how would you guys edit the transform modal map trough coda I want Y axis to be overriden by SPACE and SHIFT + SPACE
These are the specific keys that I want to override with code:
This is an extract from the code I´m trying to put together:
wm = bpy.context.window_manager
kc = wm.keyconfigs.addon
if kc:
# Handling modal keymap changes
km_transform_modal = kc.keymaps.get('Transform Modal Map')
if km_transform_modal:
# Add a modal keymap item for Y axis constraint during translation
kmi = km_transform_modal.keymap_items.new_modal('CONFIRM', 'SPACE', 'PRESS')
kmi_props = kmi.properties
kmi_props.name = 'axis_y'
addon_keymaps.append((km_transform_modal, kmi))
else:
print("Modal keymap 'Transform Modal Map' not found")
km_edit_mode = kc.keymaps.new(name='Mesh', space_type='EMPTY')
# LOOP CUT
kmi = km_edit_mode.keymap_items.new(HP_OT_loopcut.bl_idname, type='R', value='PRESS', ctrl=True, shift=False)
addon_keymaps.append((km_edit_mode, kmi))
# SMART PUSH AND SLIDE
kmi = km_edit_mode.keymap_items.new(HP_OT_PushAndSlide.bl_idname, type='G', value='PRESS', ctrl=False, shift=True)
addon_keymaps.append((km_edit_mode, kmi))
kmi = km_edit_mode.keymap_items.new('mesh.select_prev_item', type='WHEELOUTMOUSE', value='PRESS', ctrl=False, shift=True)
addon_keymaps.append((km_edit_mode, kmi))
kmi = km_edit_mode.keymap_items.new('mesh.select_next_item', type='WHEELINMOUSE', value='PRESS', ctrl=False, shift=True)
addon_keymaps.append((km_edit_mode, kmi))
kmi = km_edit_mode.keymap_items.new('mesh.loop_select', type='LEFTMOUSE', value='PRESS', ctrl=False, shift=False, alt=True)
addon_keymaps.append((km_edit_mode, kmi))
kmi = km_edit_mode.keymap_items.new('mesh.edgering_select', type='LEFTMOUSE', value='DOUBLE_CLICK', ctrl=False, shift=False, alt=True)
addon_keymaps.append((km_edit_mode, kmi))
I'm doing my best, but so far no breakthroughs.
It's a classic case of "It works on my PC", and it doesn't work on theirs.
When they try and use the new "Import lego" menu item, they tell me of the AttributeError: 'Converter' object has no attribute 'scene' error.
total = len(self.scene.Bricks)
This is where I get stuck.
I googled it, and there's clear instructions that the BPY Scene object isn't available during registration of a plugin, and only when it's triggered, and is passed through to the execute function via a "context" attribute.
That's just what the code is doing! (and it works on my PC)
I wonder if it's because the users scene is empty, Null? Whereas mine default Blender scene has a Plane, light, etc... I'm waiting for them to get back to me.
If not that, that's where I lose it completely, is Pythons "self" / "context" things not passing "scene" through?
https://github.com/Sarah-C/BlenderImportLDD/issues/1
Traceback (most recent call last):
File "C:\Users\Jack\AppData\Roaming\Blender Foundation\Blender\4.0\scripts\addons\BlenderImportLDD.py", line 1481, in execute
return convertldd_data(context, self.filepath, self.lddLIFPath, self.useLogoStuds, self.useLDDCamera)
File "C:\Users\Jack\AppData\Roaming\Blender Foundation\Blender\4.0\scripts\addons\BlenderImportLDD.py", line 1416, in convertldd_data
converter.Export(filename=filepath, useLogoStuds=useLogoStuds, useLDDCamera=useLDDCamera)
File "C:\Users\Jack\AppData\Roaming\Blender Foundation\Blender\4.0\scripts\addons\BlenderImportLDD.py", line 955, in Export
total = len(self.scene.Bricks)
AttributeError: 'Converter' object has no attribute 'scene'
Many thanks for your help!
I've managed to fix up and fine tune the old Lego LXF to Blender importer that's based on pyldd2obj version 0.4.8 - Copyright (c) 2019 by jonnysp.
The only Python and BPY API I know is that which I've learned doing this plugin tweaks, so not much at all.
During import I realised the object's where huge! The whole scale is some other unit, possibly mm to meters!
I had great difficulty resizing individual blocks as they were put in the scene - all the time I got "brick.scale isn't a method" or similar! I don't know what the constructors are making, so I was experimenting blind.
In the end, the solution I got working was some code I found on S.O.... which works great but with one problem I can't solve - the bit of code below scales the WHOLE scene!
So prior imports are shrunk repeatedly, like Russian Dolls!
Line 1421:
# Scale the world
scale = 0.025
# change scale and location
[setattr(obj, 'scale', obj.scale*scale) for obj in bpy.data.objects if ((obj.type != 'EMPTY') and (obj.type != 'CAMERA'))]
[setattr(obj, 'location', obj.location*scale) for obj in bpy.data.objects if obj.type != 'CAMERA']
The layout of the new blocks is like below, a new collection contains empty elements that each have some brick bits and the lego mesh as children.
So what I figured was a correct course of action was to iterate through the -collection- that was just imported, not the whole scene like I am.
How can I do that?
https://github.com/Sarah-C/BlenderImportLDD/blob/main/BlenderImportLDD.py
It's pretty simple stuff, but I have 0 understanding of Python so it's been hard to find things on how to do exactly what I want. The general idea is getting a few tools that I use often during rigging process inside a single add-on/panel, so I don't have to go back and forth between a bunch of tabs. Right now I'm trying to figure out how to add Icons to my panel labels instead of the row labels, and how to recreate the bone name and parent text boxes like they are in the relations tab. I've tried a few things but the general result is that nothing happens.
Edit: added some pictures of how the code is, as I said very basic stuff, I'm completely new to this, and sorry for the Portuguese comments.
Hi all. I'm looking into all methods (if at all possible) to convert alembic cache data into fbx format. I realize that the changing vertex count doesn't allow for conversion and imported alembics are baked animation data, but my thought process here is to convert between multiple formats.
I first made a simple geom node particle system and exported that out as a alembic, then brought that back into blender to convert to a lightwave point cache format. I did this because I thought perhaps then taking this data to fbx could provide a solution but i haven't found a working method yet. I get an export that is just the first frame of the animation.
My methodology to this point has been to somehow convert the data into shape keys to then export out. I found this script online, but it seems to crash my scene. Honestly, if someone were able to break this down into simpler terms, I may be able to edit this to my needs.
If anyone has insight or creative solutions to get this result of an alembic to an fbx format, it would be greatly appreciated. my target goal is to take a growing fluid sim and get that to an fbx.
PYTHON SCRIPT:
import bpy
ob = bpy.context
obAc = ob.active_object
mesh_data = obAc.data
start_frame = bpy.context.scene.frame_start
end_frame = bpy.context.scene.frame_end
if not obAc.data.shape_keys:
obAc.shape_key_add(name="Basis")
for frame in range(start_frame, end_frame + 1):
bpy.context.scene.frame_set(frame)
depsgraph = bpy.context.evaluated_depsgraph_get()
object_eval = obAc.evaluated_get(depsgraph)
mesh_eval = object_eval.data
shape_key = obAc.shape_key_add(name=str(frame))
vertices = [vertex.co for vertex in mesh_eval.vertices]
shape_key.data.foreach_set('co', [c for v in vertices for c in v])
if obAc.data.shape_keys:
shape_keys = obAc.data.shape_keys.key_blocks
for frame in range(start_frame, end_frame + 1):
ob.scene.frame_set(frame)
for shape_key in shape_keys:
if shape_key.name.isdigit():
value = 1.0 if int(shape_key.name) == frame else 0.0
shape_key.value = value
shape_key.keyframe_insert(data_path='value', index=-1)
I spent way too much time on making this __init,py__ file for use in add-on development... instead of actually working on the add-on I want to make
It scans it's directory for all sub-modules (.py files) and imports and/or reloads them. It is a very, very greedy approach and it's likely to load / reload the same file multiple times (because it got imported in an already loaded file)
Also the script that you run inside of Blender to load in the add-on is different than what I've seen around (included in the code as a text block).
Do whatever you want with this code, I donate it to the public domain as much as I can and whatnot.
bl_info = {
# your add-on info here
}
"""
### Dev Loader Script ###
# Run from Blender's Text Editor
import sys
import importlib
import importlib.util
# https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly
module_name = "module_name"
file_path = "C:/blah/module_name/__init__.py"
if module_name in sys.modules:
try:
sys.modules[module_name].unregister()
except Exception as err:
print(err)
spec = importlib.util.spec_from_file_location(module_name, file_path)
module = importlib.util.module_from_spec(spec)
sys.modules[module_name] = module
spec.loader.exec_module(module)
module.register()
"""
# bootstrapping code based on: https://b3d.interplanety.org/en/creating-multifile-add-on-for-blender/
import importlib
import sys
from glob import glob
child_modules = {mod_name: f'{__name__}.{mod_name}' for mod_name in (
p.replace('\\', '.').replace('/', '.').removesuffix('.py')
for p in glob("**/[!_]*.py", root_dir=__path__[0], recursive=True)
)}
for mod_name, full_name in child_modules.items():
if full_name in sys.modules:
print('Reload', full_name)
importlib.reload(sys.modules[full_name])
else:
print('Initial load', full_name)
parent, name = mod_name.rsplit('.', 1)
exec(f'from .{parent} import {name}')
del mod_name, full_name
def register():
for full_name in child_modules.values():
if full_name in sys.modules and hasattr(sys.modules[full_name], 'register'):
sys.modules[full_name].register()
def unregister():
for full_name in child_modules.values():
if full_name in sys.modules and hasattr(sys.modules[full_name], 'unregister'):
sys.modules[full_name].unregister()
I’m working on a project where I’ve got a mesh that I want to split into chunks. The splitting part is easy. I’m using bmesh so I can just use split_edges to break it up and that works great.
So now it’s split into chunks and all the vertices on the boundary of each chunk has a matching vertex on the other chunk it was split from. I want to join those matching vertices on the different chunks with an edge, but there doesn’t seem to be an easy way to do that. I could loop through all the boundary points until I find all matching pairs and then call a function to connect them with edges one at a time but that seems ridiculous. Any suggestions?
I want to use the blender gpu Module. In the examples they say that the draw function is only there to make the code short. The problem is that I want to remove (or update) the lines without restarting blender. How would that be possible?
Example: https://docs.blender.org/api/current/gpu.html#d-lines-with-single-color
Thank you
Building a fighting game, here's the system I'd like to build: a shared human rig, using other blends to import corresponding meshes of characters so that there arent a ton of unneeded characters sitting in the file. animation groups so that I can specify which animations are shared between characters and which are unique to a particular character for export. Does this sound possible? And if anyone has any guidance about implementation or other words of wisdom I'd really appreciate it.
edit: oh and do people use this or the Blender Artists: Python Support Forum thats linked in the docs? wheres the place 2 be basically ;p
Hi there, I'm not very experienced with python but I used:
import bpy bpy.data.node_groups['MyGroupName'].inputs.new('NodeSocketbool', 'SocketName')
to create custom boolean socket inputs for shaders. This used to work in 3.5 but doesn't in 4.0 anymore. I tried to understand the documentation on what changed but I don't really find the solution but that might be due to my inexpience with python.
It says there's not inputs attribute anymore. Can someone help?
I think the python API is pretty cumbersome for creating materials, so I've made a decorator that converts more typical python to materials/node groups. Check it out https://github.com/JamesPickersgill/python-shader-nodes, although note this is a just a proof of concept so lots is missing. If anyone else thinks this is useful I can spend some more time fleshing it out.