Today, I created a simple clothing addon with a two-mesh coat. The addon adds everything needed for the simulation including material types for the clothes.

Is a blog about python programming language. You can see my work with python programming language, tutorials and news.

bl_info = {
"name": "Append Materials from Folder",
"author": "Grok",
"version": (1, 2),
"blender": (3, 0, 0),
"location": "View3D > Sidebar > Append Materials",
"description": "Select a folder and append all materials from .blend files recursively",
"category": "Import-Export",
}
import bpy
import os
from bpy.types import Operator, Panel, PropertyGroup
from bpy.props import StringProperty, PointerProperty
class AppendMaterialsProperties(PropertyGroup):
folder_path: StringProperty(
name="Folder Path",
description="Path to the folder containing .blend files",
default="",
maxlen=1024,
subtype='DIR_PATH'
)
class APPEND_OT_materials_from_folder(Operator):
bl_idname = "append.materials_from_folder"
bl_label = "Append Materials from Folder"
bl_options = {'REGISTER', 'UNDO'}
bl_description = "Append all materials from .blend files in the selected folder and subfolders"
def execute(self, context):
props = context.scene.append_materials_props
folder_path = props.folder_path
# Normalize path to avoid issues with slashes
folder_path = os.path.normpath(bpy.path.abspath(folder_path))
if not folder_path or not os.path.isdir(folder_path):
self.report({'ERROR'}, f"Invalid or no folder selected: {folder_path}")
return {'CANCELLED'}
self.report({'INFO'}, f"Scanning folder: {folder_path}")
blend_files_found = 0
materials_appended = 0
errors = []
# Walk recursively through the folder
for root, dirs, files in os.walk(folder_path):
self.report({'INFO'}, f"Checking folder: {root}")
for file in files:
if file.lower().endswith('.blend'):
blend_files_found += 1
blend_path = os.path.join(root, file)
self.report({'INFO'}, f"Found .blend file: {blend_path}")
try:
# Open the .blend file to inspect materials
with bpy.data.libraries.load(blend_path, link=False) as (data_from, data_to):
if data_from.materials:
data_to.materials = data_from.materials
materials_appended += len(data_from.materials)
self.report({'INFO'}, f"Appended {len(data_from.materials)} materials from: {blend_path}")
else:
self.report({'WARNING'}, f"No materials found in: {blend_path}")
except Exception as e:
errors.append(f"Failed to process {blend_path}: {str(e)}")
self.report({'WARNING'}, f"Error in {blend_path}: {str(e)}")
# Final report
if blend_files_found == 0:
self.report({'WARNING'}, f"No .blend files found in {folder_path} or its subfolders!")
else:
self.report({'INFO'}, f"Found {blend_files_found} .blend files, appended {materials_appended} materials.")
if errors:
self.report({'WARNING'}, f"Encountered {len(errors)} errors: {'; '.join(errors)}")
return {'FINISHED'}
class VIEW3D_PT_append_materials(Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = "Append Materials"
bl_label = "Append Materials from Folder"
def draw(self, context):
layout = self.layout
props = context.scene.append_materials_props
layout.prop(props, "folder_path")
layout.operator("append.materials_from_folder", text="Append Materials")
def register():
bpy.utils.register_class(AppendMaterialsProperties)
bpy.utils.register_class(APPEND_OT_materials_from_folder)
bpy.utils.register_class(VIEW3D_PT_append_materials)
bpy.types.Scene.append_materials_props = PointerProperty(type=AppendMaterialsProperties)
def unregister():
bpy.utils.unregister_class(VIEW3D_PT_append_materials)
bpy.utils.unregister_class(APPEND_OT_materials_from_folder)
bpy.utils.unregister_class(AppendMaterialsProperties)
del bpy.types.Scene.append_materials_props
if __name__ == "__main__":
register()
bl_info = {
"name": "3D File Renderer by catafest",
"blender": (4, 3, 2),
"category": "Object",
"author": "Catalin George Festila\n"
"nicknames: catafest and mythcat\n"
"country: Romania\n"
"mail: catafest [at] yahoo.com",
"version": (1, 0),
"blender": (2, 80, 0),
"location": "View3D > UI > 3D File Renderer",
"description": "Addon for rendering 3D files",
"warning": "",
"doc_url": "https://github.com/catafest",
"tracker_url": "https://github.com/catafest/issues",
"support": "COMMUNITY",
}
import bpy
import os
class FileRendererProperties(bpy.types.PropertyGroup):
input_directory: bpy.props.StringProperty(
name="Input Directory",
description="Directory containing 3D files",
default="",
maxlen=1024,
subtype='DIR_PATH'
)
output_directory: bpy.props.StringProperty(
name="Output Directory",
description="Directory to save rendered images",
default="",
maxlen=1024,
subtype='DIR_PATH'
)
class RENDER_OT_files(bpy.types.Operator):
bl_idname = "render.files"
bl_label = "Start render 3D files for all files"
def execute(self, context):
input_directory = context.scene.file_renderer_props.input_directory
output_directory = context.scene.file_renderer_props.output_directory
if not input_directory or not output_directory:
self.report({'ERROR'}, "Input and Output directories must be set.")
return {'CANCELLED'}
if not os.path.exists(output_directory):
os.makedirs(output_directory)
def render_file(file_path, output_path):
try:
bpy.ops.wm.read_factory_settings(use_empty=True)
ext = os.path.splitext(file_path)[1].lower()
if ext == ".glb":
bpy.ops.import_scene.gltf(filepath=file_path)
elif ext == ".obj":
bpy.ops.import_scene.obj(filepath=file_path)
elif ext == ".fbx":
bpy.ops.import_scene.fbx(filepath=file_path)
else:
raise ValueError("Unsupported file format")
bpy.ops.object.camera_add(location=(0, -3, 1.5), rotation=(1.1, 0, 0))
camera = bpy.context.scene.objects['Camera']
bpy.context.scene.camera = camera
bpy.ops.object.light_add(type='POINT', location=(0, -3, 3))
light = bpy.context.view_layer.objects.active
light.data.energy = 1000
bpy.context.scene.render.resolution_x = 512
bpy.context.scene.render.resolution_y = 512
bpy.context.scene.render.filepath = output_path
bpy.ops.render.render(write_still=True)
except Exception as e:
# Generate a red image with "BAD FILE" text using Blender
bpy.ops.wm.read_factory_settings(use_empty=True)
bpy.ops.mesh.primitive_plane_add(size=2)
plane = bpy.context.active_object
mat = bpy.data.materials.new(name="BadFileMaterial")
mat.diffuse_color = (1, 0, 0, 1) # Red
plane.data.materials.append(mat)
# Add "BAD FILE" text
bpy.ops.object.text_add(location=(0, 0, 0.1))
text_obj = bpy.context.active_object
text_obj.data.body = "BAD FILE"
text_obj.data.size = 0.5
text_obj.data.align_x = 'CENTER'
text_obj.data.align_y = 'CENTER'
text_obj.rotation_euler = (1.5708, 0, 0)
# Set camera and light
bpy.ops.object.camera_add(location=(0, -3, 1.5), rotation=(1.1, 0, 0))
camera = bpy.context.scene.objects['Camera']
bpy.context.scene.camera = camera
bpy.ops.object.light_add(type='POINT', location=(0, -3, 3))
light = bpy.context.view_layer.objects.active
light.data.energy = 1000
bpy.context.scene.render.resolution_x = 512
bpy.context.scene.render.resolution_y = 512
bpy.context.scene.render.filepath = output_path
bpy.ops.render.render(write_still=True)
for filename in os.listdir(input_directory):
if filename.lower().endswith((".glb", ".obj", ".fbx")):
file_path = os.path.join(input_directory, filename)
output_path = os.path.join(output_directory, os.path.splitext(filename)[0] + ".png")
render_file(file_path, output_path)
self.report({'INFO'}, "Rendering of files is complete.")
return {'FINISHED'}
class ABOUT_OT_dialog(bpy.types.Operator):
bl_idname = "wm.about_dialog"
bl_label = "About this addon"
def execute(self, context):
return context.window_manager.invoke_props_dialog(self)
def draw(self, context):
layout = self.layout
layout.label(text="3D File Renderer by catafest")
layout.label(text="Author: Catalin George Festila")
layout.label(text="Nicknames: catafest and mythcat")
layout.label(text="Country: Romania")
layout.label(text="Email: catafest [at] yahoo.com")
layout.operator("wm.url_open", text="LinkedIn").url = "https://www.linkedin.com/in/c%C4%83t%C4%83lin-george-fe%C8%99til%C4%83-05780a67"
layout.operator("wm.url_open", text="Author Site").url = "https://sites.google.com/view/festila-george-catalin"
layout.operator("wm.url_open", text="catafest GitHub").url = "https://github.com/catafest"
layout.operator("wm.url_open", text="catafest-work GitHub").url = "https://github.com/catafest-work"
class FileRendererPanel(bpy.types.Panel):
bl_label = "3D File Renderer by catafest"
bl_idname = "OBJECT_PT_file_renderer"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = 'File Renderer'
def draw(self, context):
layout = self.layout
scene = context.scene
file_renderer_props = scene.file_renderer_props
layout.prop(file_renderer_props, "input_directory")
layout.prop(file_renderer_props, "output_directory")
# Styling the render button
render_button = layout.operator("render.files", text="Start render 3D files for all files")
layout.separator()
layout.operator("wm.about_dialog", text="About this addon")
def register():
bpy.utils.register_class(FileRendererProperties)
bpy.utils.register_class(RENDER_OT_files)
bpy.utils.register_class(ABOUT_OT_dialog)
bpy.utils.register_class(FileRendererPanel)
bpy.types.Scene.file_renderer_props = bpy.props.PointerProperty(type=FileRendererProperties)
def unregister():
bpy.utils.unregister_class(FileRendererProperties)
bpy.utils.unregister_class(RENDER_OT_files)
bpy.utils.unregister_class(ABOUT_OT_dialog)
bpy.utils.unregister_class(FileRendererPanel)
del bpy.types.Scene.file_renderer_props
if __name__ == "__main__":
register()

import bpy, bmesh
obj = bpy.context.active_object
me = obj.data
bpy.ops.object.mode_set(mode = 'EDIT')
bpy.ops.mesh.select_mode(type="VERT")
bm = bmesh.from_edit_mesh(obj.data)
selected = [False,False,True,True,True,True,True,True]
verts = [vert for vert in bpy.context.active_object.data.vertices if vert.select]
all = [vert for vert in bpy.context.active_object.data.vertices]
print("selected:",len(verts))
print("all:",len(all))
bpy.ops.object.mode_set(mode = 'OBJECT')
me.vertices.foreach_set(
"select",
selected
)
bpy.ops.object.mode_set(mode = 'EDIT')
# give Python access to Blender's functionality
import bpy
# extend Python's math functionality
import math
# extend Python functionality to generate random numbers
import random
def partially_clean_the_scene():
# select all object in the scene
bpy.ops.object.select_all(action="SELECT")
# delete all selected objects in the scene
bpy.ops.object.delete()
# make sure we remove data that was connected to the objects we just deleted
bpy.ops.outliner.orphans_purge(do_local_ids=True, do_linked_ids=True, do_recursive=True)
def create_noise_mask(material):
"""Add a set of nodes to create a noise mask using:
* Texture Coordinate node
* Mapping node
* Noise Texture node
* Color Ramp node
"""
node_location_x_step = 300
node_location_x = -node_location_x_step
# create a Color Ramp node
# https://docs.blender.org/api/current/bpy.types.ShaderNodeValToRGB.html
color_ramp_node = material.node_tree.nodes.new(type="ShaderNodeValToRGB")
color_ramp_node.color_ramp.elements[0].position = 0.45
color_ramp_node.color_ramp.elements[1].position = 0.5
color_ramp_node.location.x = node_location_x
node_location_x -= node_location_x_step
# create a Noise Texture node
# https://docs.blender.org/api/current/bpy.types.ShaderNodeTexNoise.html#bpy.types.ShaderNodeTexNoise
noise_texture_node = material.node_tree.nodes.new(type="ShaderNodeTexNoise")
noise_texture_node.inputs["Scale"].default_value = random.uniform(1.0, 20.0)
noise_texture_node.location.x = node_location_x
node_location_x -= node_location_x_step
# create a Mapping node
# https://docs.blender.org/api/current/bpy.types.ShaderNodeMapping.html#bpy.types.ShaderNodeMapping
mapping_node = material.node_tree.nodes.new(type="ShaderNodeMapping")
mapping_node.inputs["Rotation"].default_value.x = math.radians(random.uniform(0.0, 360.0))
mapping_node.inputs["Rotation"].default_value.y = math.radians(random.uniform(0.0, 360.0))
mapping_node.inputs["Rotation"].default_value.z = math.radians(random.uniform(0.0, 360.0))
mapping_node.location.x = node_location_x
node_location_x -= node_location_x_step
# create a Texture Coordinate node
texture_coordinate_node = material.node_tree.nodes.new(type="ShaderNodeTexCoord")
texture_coordinate_node.location.x = node_location_x
# connect the nodes
# https://docs.blender.org/api/current/bpy.types.NodeTree.html#bpy.types.NodeTree
# https://docs.blender.org/api/current/bpy.types.NodeLinks.html#bpy.types.NodeLinks
material.node_tree.links.new(noise_texture_node.outputs["Color"], color_ramp_node.inputs["Fac"])
material.node_tree.links.new(mapping_node.outputs["Vector"], noise_texture_node.inputs["Vector"])
material.node_tree.links.new(texture_coordinate_node.outputs["Generated"], mapping_node.inputs["Vector"])
return color_ramp_node
def create_material(name):
# create new material
material = bpy.data.materials.new(name=name)
# enable creating a material via nodes
material.use_nodes = True
# get a reference to the Principled BSDF shader node
principled_bsdf_node = material.node_tree.nodes["Principled BSDF"]
# set the base color of the material
principled_bsdf_node.inputs["Base Color"].default_value = (0.8, 0.120827, 0.0074976, 1)
# set the metallic value of the material
principled_bsdf_node.inputs["Metallic"].default_value = 1.0
color_ramp_node = create_noise_mask(material)
material.node_tree.links.new(color_ramp_node.outputs["Color"], principled_bsdf_node.inputs["Roughness"])
return material
def add_mesh():
# create an ico sphere
bpy.ops.mesh.primitive_ico_sphere_add(subdivisions=5)
# shade smooth
bpy.ops.object.shade_smooth()
# get reference to mesh object
mesh_obj = bpy.context.active_object
return mesh_obj
def main():
partially_clean_the_scene()
name = "my_generated_material"
material = create_material(name)
mesh_obj = add_mesh()
# apply the material to the mesh object
mesh_obj.data.materials.append(material)
main()
import bpy
import math
# Set the object that the camera will orbit around
#target_object = bpy.data.objects["MyObject"]
# Create a new empty object
empty = bpy.data.objects.new("Empty", None)
# Set the empty object's location to the origin point
empty.location = (0, 0, 0)
# Set the starting position for the camera
camera = bpy.data.objects["Camera"]
# Set the number of degrees to rotate the camera around the object
degrees = 360
# Set the distance that the camera should be from the object
distance = 7.6
# Set the speed at which the camera should orbit
speed = 10
# Set the direction in which the camera should orbit (1 for clockwise, -1 for counter-clockwise)
direction = 1
# Set the camera to track the object
bpy.ops.object.select_all(action="DESELECT")
camera.select_set(True)
# Set the distance to origin point
camera.location = (-distance, 0, 0)
bpy.context.view_layer.objects.active = camera
# Remove all constraints from the object "Cube"
bpy.data.objects['Cube'].select_get()
bpy.context.view_layer.objects.active = bpy.data.objects['Cube']
bpy.ops.object.constraints_clear()
# Add a track to constraint to the object and set it
bpy.ops.object.constraint_add(type="TRACK_TO")
bpy.ops.object.track_set(type="TRACKTO")
# Set the target object as the tracking target
bpy.data.objects['Cube'].select_get()
bpy.context.view_layer.objects.active = bpy.data.objects['Cube']
# Select the file image format
bpy.context.scene.render.image_settings.file_format = 'PNG'
# Animate the camera orbiting around the object
for frame in range(0, 36):
# Set the current frame
bpy.context.scene.frame_set(frame)
# Calculate the new position for the camera based on its distance from the object
x = distance * math.sin(math.radians(frame*speed*direction))
y = distance * math.cos(math.radians(frame*speed*direction))
camera.location = (x,y,0)
# Set the output path for the rendered image
bpy.context.scene.render.filepath = "C:\\tmp\\myimage_" + str(frame).zfill(3) + ".png"
# Render the frame and save it to the output file
bpy.ops.render.render(write_still=True)
import bpy
import random
# fix an active object before changing mode
if bpy.context.view_layer.objects.active is None:
# If there is no active object, set the first object in the scene as active
if len(bpy.context.scene.objects) > 0:
bpy.context.view_layer.objects.active = bpy.context.scene.objects[0]
else:
print("There are no objects in the scene to set as active.")
# set the mode
bpy.ops.object.mode_set(mode='OBJECT')
# set camera from the scene
cam_ob = bpy.context.scene.camera
# if is a camera in the scene and set it as active object
if cam_ob is None:
print("There is no camera in the scene.")
elif cam_ob.type == 'CAMERA':
# Set the camera as active object
bpy.context.view_layer.objects.active = cam_ob
print("The camera has been set as active object.")
else:
print(f"The object {cam_ob.type} is set as a camera, but it is not of type 'CAMERA'.")
# set data for the ink brush
gpencil = bpy.data.grease_pencils.new("Bright Pencil")
# make material for the ink brush
if "Bright Material" in bpy.data.materials.keys():
gp_mat = bpy.data.materials["Bright Material"]
else:
gp_mat = bpy.data.materials.new("Bright Material")
if not gp_mat.is_grease_pencil:
bpy.data.materials.create_gpencil_data(gp_mat)
bpy.context.object.active_material.grease_pencil.color = (0, 0, 1, 1)
# set the object for the ink brush
if "Bright Pencil" not in bpy.data.objects:
gp_data = bpy.data.objects.new("Bright Pencil", gpencil)
bpy.context.scene.collection.objects.link(gp_data)
# if it does not already exist in the scene
else:
gp_data = bpy.data.objects["Bright Pencil"]
# assign material for drawing
if gp_mat.name not in gp_data.data.materials:
gp_data.data.materials.append(gp_mat)
# define a function to create random lines
def create_random_strokes(num_strokes, max_width):
# Set the active object and mode
bpy.context.view_layer.objects.active = gp_data
bpy.ops.object.mode_set(mode='PAINT_GPENCIL')
# get or create layer and set it as active
if gpencil.layers and gpencil.layers.active:
layer = gpencil.layers.active
else:
layer = gpencil.layers.new('my_test_layer', set_active=True)
# set layer as active
gpencil.layers.active = layer
# get or create frame and set it as active using change_frame() method
if layer.frames and layer.active_frame:
frame = layer.active_frame
else:
frame = layer.frames.new(1)
for _ in range(num_strokes):
stroke = frame.strokes.new()
stroke.line_width = int(random.uniform(1.0, max_width))
stroke.points.add(count=3)
for point in stroke.points:
point.co = (random.uniform(-1.0, 1.0), random.uniform(-1.0, 1.0), 0.0)
# this function with desired parameters
create_random_strokes(num_strokes=10, max_width=16.0)
# return to original mode
bpy.ops.object.mode_set(mode='OBJECT')
import bpy
import mathutils
from mathutils import Vector
from math import *
class ArmatureMenu(bpy.types.Menu):
bl_label = "Mesh 2 Armature Menu"
bl_idname = "OBJECT_MT_Mesh_From_Armature"
def draw(self, context):
layout = self.layout
layout.operator("wm.mesh_from_armature", text="Pyramid").mesh_type = 'Pyramid' # from here
layout.operator("wm.mesh_from_armature", text="Tapered").mesh_type = 'Tapered' # from here
layout.operator("wm.mesh_from_armature", text="Box").mesh_type = 'Box' # from here
def CreateMesh(self, meshType):
obj = bpy.context.active_object
if obj == None:
self.report({"ERROR"}, "No selection" )
elif obj.type != 'ARMATURE':
self.report({"ERROR"}, "Armature expected" )
else:
processArmature( bpy.context, obj, meshType = meshType )
#Create the base object from the armature
def meshFromArmature( arm ):
name = arm.name + "_mesh"
meshData = bpy.data.meshes.new( name + "Data" )
meshObj = bpy.data.objects.new( name, meshData )
meshObj.matrix_world = arm.matrix_world.copy()
return meshObj
#Create the bone geometry (vertices and faces)
def boneGeometry( l1, l2, x, z, baseSize, l1Size, l2Size, base, meshType ):
if meshType == 'Tapered':
print(meshType)
x1 = x * baseSize * l1Size
z1 = z * baseSize * l1Size
x2 = x * baseSize * l2Size
z2 = z * baseSize * l2Size
elif meshType == 'Box':
print(meshType)
lSize = (l1Size + l2Size) / 2
x1 = x * baseSize * lSize
z1 = z * baseSize * lSize
x2 = x * baseSize * lSize
z2 = z * baseSize * lSize
else: # default to Pyramid
print(meshType)
x1 = x * baseSize * l1Size
z1 = z * baseSize * l1Size
x2 = Vector( (0, 0, 0) )
z2 = Vector( (0, 0, 0) )
verts = [
l1 - x1 + z1,
l1 + x1 + z1,
l1 - x1 - z1,
l1 + x1 - z1,
l2 - x2 + z2,
l2 + x2 + z2,
l2 - x2 - z2,
l2 + x2 - z2
]
faces = [
(base+3, base+1, base+0, base+2),
(base+6, base+4, base+5, base+7),
(base+4, base+0, base+1, base+5),
(base+7, base+3, base+2, base+6),
(base+5, base+1, base+3, base+7),
(base+6, base+2, base+0, base+4)
]
return verts, faces
#Process the armature, goes through its bones and creates the mesh
def processArmature(context, arm, genVertexGroups = True, meshType = 'Pyramid'):
print("processing armature {0} {1}".format(arm.name, meshType) )
#Creates the mesh object
meshObj = meshFromArmature( arm )
context.collection.objects.link( meshObj )
verts = []
edges = []
faces = []
vertexGroups = {}
bpy.ops.object.mode_set(mode='EDIT')
try:
#Goes through each bone
for editBone in [b for b in arm.data.edit_bones if b.use_deform]:
boneName = editBone.name
# print( boneName )
poseBone = arm.pose.bones[boneName]
#Gets edit bone informations
editBoneHead = editBone.head
editBoneTail = editBone.tail
editBoneVector = editBoneTail - editBoneHead
editBoneSize = editBoneVector.dot( editBoneVector )
editBoneRoll = editBone.roll
editBoneX = editBone.x_axis
editBoneZ = editBone.z_axis
editBoneHeadRadius = editBone.head_radius
editBoneTailRadius = editBone.tail_radius
#Creates the mesh data for the bone
baseIndex = len(verts)
baseSize = sqrt( editBoneSize )
newVerts, newFaces = boneGeometry( editBoneHead, editBoneTail, editBoneX, editBoneZ, baseSize, editBoneHeadRadius, editBoneTailRadius, baseIndex, meshType )
verts.extend( newVerts )
faces.extend( newFaces )
#Creates the weights for the vertex groups
vertexGroups[boneName] = [(x, 1.0) for x in range(baseIndex, len(verts))]
#Assigns the geometry to the mesh
meshObj.data.from_pydata(verts, edges, faces)
except:
bpy.ops.object.mode_set(mode='OBJECT')
else:
bpy.ops.object.mode_set(mode='OBJECT')
#Assigns the vertex groups
if genVertexGroups:
for name, vertexGroup in vertexGroups.items():
groupObject = meshObj.vertex_groups.new(name=name)
for (index, weight) in vertexGroup:
groupObject.add([index], weight, 'REPLACE')
#Creates the armature modifier
modifier = meshObj.modifiers.new('ArmatureMod', 'ARMATURE')
modifier.object = arm
modifier.use_bone_envelopes = False
modifier.use_vertex_groups = True
meshObj.data.update()
return meshObj
class MeshFromArmatureOperator(bpy.types.Operator):
bl_idname = "wm.mesh_from_armature"
bl_label = "MeshFromArmatureOperator"
mesh_type : bpy.props.StringProperty(name="mesh_type")
def execute(self, context):
print('The mesh type is', self.mesh_type)
CreateMesh(self, self.mesh_type)
return {'FINISHED'}
def register():
bpy.utils.register_class( ArmatureMenu )
bpy.utils.register_class( MeshFromArmatureOperator )
def unregister():
bpy.utils.unregister_class( ArmatureMenu )
bpy.utils.unregister_class( MeshFromArmatureOperator )
if __name__ == "__main__":
register()
# The menu can also be called from scripts
bpy.ops.wm.call_menu(name='OBJECT_MT_Mesh_From_Armature')
[mythcat@fedora PythonProjects]$ mkdir FedoraMessaging
[mythcat@fedora PythonProjects]$ cd FedoraMessaging[root@fedora FedoraMessaging]# dnf5 install fedora-messaging
Updating and loading repositories:
Repositories loaded.
Package Arch Version Repository Size
Installing:
fedora-messaging noarch 3.5.0-1.fc41 rawhide 38.6 KiB
...
[root@fedora FedoraMessaging]# dnf install rabbitmq-server[root@fedora FedoraMessaging]# pip install --user fedora-messaging
Collecting fedora-messaging
...
Installing collected packages: pytz, incremental, wrapt, tomli, rpds-py, pyasn1, pika, hyperlink, constantly, attrs,
referencing, pyasn1-modules, automat, twisted, jsonschema-specifications, service-identity, jsonschema, crochet,
fedora-messaging
Successfully installed attrs-23.2.0 automat-22.10.0 constantly-23.10.4 crochet-2.1.1 fedora-messaging-3.5.0
hyperlink-21.0.0 incremental-22.10.0 jsonschema-4.21.1 jsonschema-specifications-2023.12.1 pika-1.3.2 pyasn1-0.6.0
pyasn1-modules-0.4.0 pytz-2024.1 referencing-0.34.0 rpds-py-0.18.0 service-identity-24.1.0 tomli-2.0.1 twisted-24.3.0
wrapt-1.16.0[mythcat@fedora FedoraMessaging]$ sudo systemctl start rabbitmq-serverfrom fedora_messaging import api, config
config.conf.setup_logging()
api.consume(lambda message: print(message))
from fedora_messaging import api, config
config.conf.setup_logging()
api.publish(api.Message(topic="hello by mythcat", body={"Hello": "world!"}))[mythcat@fedora FedoraMessaging]$ python hello_test.py
[fedora_messaging.message INFO] Registering the 'base.message' key as the '<class 'fedora_messaging.message.Message'>'
class in the Message class registry
[fedora_messaging.twisted.protocol INFO] Waiting for 0 consumer(s) to finish processing before halting
[fedora_messaging.twisted.protocol INFO] Finished canceling 0 consumers
[fedora_messaging.twisted.protocol INFO] Disconnect requested, but AMQP connection already gonefrom fedora_messaging import api, config
# Setup logging
config.conf.setup_logging()
# Define the callback function to process messages
def process_message(message):
# Check if the message topic matches "hello by mythcat"
if message.topic == "hello by mythcat":
print(f"Received message: {message.body}")
else:
print(f"Ignoring message with topic: {message.topic}")
# Consume messages
api.consume(process_message)[mythcat@fedora FedoraMessaging]$ python my_consumer.py
[fedora_messaging.twisted.protocol INFO] Successfully registered AMQP consumer Consumer(queue=amq.gen-9lKk7sGeYY5I40bdc5VrzQ,
callback=<function process_message at 0x7fdb0f5da160>)
[fedora_messaging.message INFO] Registering the 'base.message' key as the '<class 'fedora_messaging.message.Message'>'
class in the Message class registry
[fedora_messaging.twisted.consumer INFO] Consuming message from topic hello by mythcat
(message id 800a1540-1e91-4b4a-a125-15e33eebb699)
Received message: {'Hello': 'world!'}
[fedora_messaging.twisted.consumer INFO] Successfully consumed message from topic hello by mythcat
(message id 800a1540-1e91-4b4a-a125-15e33eebb699)C:\PythonProjects\scapy_001>pip install scapy
Collecting scapy
Downloading scapy-2.5.0.tar.gz (1.3 MB)
---------------------------------------- 1.3/1.3 MB 3.5 MB/s eta 0:00:00
Installing build dependencies ... done
...
Successfully built scapy
Installing collected packages: scapy
Successfully installed scapy-2.5.0from scapy.all import Dot11,Dot11Beacon,Dot11Elt,RadioTap,sendp,hexdump
netSSID = 'testSSID' #Network name here
iface = 'Realtek PCIe GbE Family Controller' #Interface name here
dot11 = Dot11(type=0, subtype=8, addr1='ff:ff:ff:ff:ff:ff',
addr2='22:22:22:22:22:22', addr3='33:33:33:33:33:33')
beacon = Dot11Beacon(cap='ESS+privacy')
essid = Dot11Elt(ID='SSID',info=netSSID, len=len(netSSID))
rsn = Dot11Elt(ID='RSNinfo', info=(
'\x01\x00' #RSN Version 1
'\x00\x0f\xac\x02' #Group Cipher Suite : 00-0f-ac TKIP
'\x02\x00' #2 Pairwise Cipher Suites (next two lines)
'\x00\x0f\xac\x04' #AES Cipher
'\x00\x0f\xac\x02' #TKIP Cipher
'\x01\x00' #1 Authentication Key Managment Suite (line below)
'\x00\x0f\xac\x02' #Pre-Shared Key
'\x00\x00')) #RSN Capabilities (no extra capabilities)
frame = RadioTap()/dot11/beacon/essid/rsn
frame.show()
print("\nHexdump of frame:")
hexdump(frame)
input("\nPress enter to start\n")
sendp(frame, iface=iface, inter=0.100, loop=1)python scapy_network_001.py
###[ RadioTap ]###
version = 0
pad = 0
len = None
present = None
notdecoded= ''
###[ 802.11 ]###
subtype = Beacon
type = Management
proto = 0
FCfield =
ID = 0
addr1 = ff:ff:ff:ff:ff:ff (RA=DA)
addr2 = 22:22:22:22:22:22 (TA=SA)
addr3 = 33:33:33:33:33:33 (BSSID/STA)
SC = 0
###[ 802.11 Beacon ]###
timestamp = 0
beacon_interval= 100
cap = ESS+privpython scapy_network_001.py
###[ RadioTap ]### tion Element ]###
version = 0 = SSID
pad = 0 = 8
len = None = 'testSSID'
present = Noneation Element ]###
notdecoded= '' = RSN
###[ 802.11 ]### = None
subtype = Beacon'\x01\x00\x00\x0f¬\x02\x02\x00\x00\x0f¬\x04\x00\x0f¬\x02\x01\x00\x00\x
type = Management
proto = 0
FCfield =
ID = 0
addr1 = ff:ff:ff:ff:ff:ff (RA=DA)FF FF FF FF ................
addr2 = 22:22:22:22:22:22 (TA=SA)33 33 00 00 ..""""""333333..
addr3 = 33:33:33:33:33:33 (BSSID/STA)8 74 65 ........d.....te
SC = 049 44 30 1C 01 00 00 0F C2 AC 02 02 stSSID0.........
###[ 802.11 Beacon ]### 00 0F C2 AC 02 01 00 00 0F C2 ................
timestamp = 0 ....
beacon_interval= 100
cap = ESS+privacy
###[ 802.11 Information Element ]###
ID = SSID..................................................................
len = 8.....................................................................
info = 'testSSID'
###[ 802.11 Information Element ]###
ID = RSN
len = None>
info = '\x01\x00\x00\x0f¬\x02\x02\x00\x00\x0f¬\x04\x00\x0f¬\x02\x01\x00\x00\x0f¬\x02\x00\x00'
Hexdump of frame:
0000 00 00 08 00 00 00 00 00 80 00 00 00 FF FF FF FF ................
0010 FF FF 22 22 22 22 22 22 33 33 33 33 33 33 00 00 ..""""""333333..
0020 00 00 00 00 00 00 00 00 64 00 11 00 00 08 74 65 ........d.....te
0030 73 74 53 53 49 44 30 1C 01 00 00 0F C2 AC 02 02 stSSID0.........
0040 00 00 0F C2 AC 04 00 0F C2 AC 02 01 00 00 0F C2 ................
0050 AC 02 00 00 ....
Press enter to start
.................................................................
Sent 130 packets.
bl_info = {
"name": "Tools by catafest",
"blender": (3, 0, 0),
"category": "3D View",
}
import bpy
from bpy.types import Operator, Panel
from bpy.props import StringProperty
try:
import importlib
importlib.import_module("Pillow")
PIL_installed = True
except ImportError:
PIL_installed = False
def install_pillow():
import subprocess
try:
subprocess.run([bpy.app.binary_path, '--python-exit-code', '1', '-m', 'ensurepip'])
subprocess.check_call([bpy.app.binary_path, '-m', 'pip', 'install', 'Pillow'])
except subprocess.CalledProcessError as e:
print("Eroare la instalarea Pillow:", e)
# Operator pentru a afișa fereastra modală cu informații despre instalarea Pillow
class CATAFEST_IMAGES_OT_show_pillow_message(Operator):
bl_idname = "catafest.show_pillow_message"
bl_label = "Show Pillow Message"
def execute(self, context):
global PIL_installed
message = "Pillow este instalat." if PIL_installed else "Pillow nu este instalat."
# Dacă Pillow nu este instalat, încercați să-l instalați
if not PIL_installed:
install_pillow()
try:
import importlib
importlib.import_module("Pillow")
PIL_installed = True
message = "Pillow a fost instalat cu succes!" if PIL_installed else "Eroare la instalarea Pillow."
except ImportError:
PIL_installed = False
# Afișați fereastra modală în centrul ecranului
bpy.ops.catafest.show_modal_message('INVOKE_DEFAULT', title="Starea Pillow", message=message)
return {'FINISHED'}
def invoke(self, context, event):
return self.execute(context)
# Operator pentru a afișa fereastra modală personalizată
class CATAFEST_IMAGES_OT_show_modal_message(Operator):
bl_idname = "catafest.show_modal_message"
bl_label = "Show Modal Message"
title: bpy.props.StringProperty(default="Message")
message: bpy.props.StringProperty(default="")
def execute(self, context):
return {'FINISHED'}
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self, width=400)
def draw(self, context):
layout = self.layout
layout.label(text=self.message)
# Panel pentru bara laterală din 3D View
class VIEW3D_PT_tools_image(Panel):
bl_label = "Images"
bl_idname = "VIEW3D_PT_tools_image"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = 'Tools by catafest'
def draw(self, context):
layout = self.layout
layout.operator(CATAFEST_IMAGES_OT_show_modal_message.bl_idname)
layout.operator(CATAFEST_IMAGES_OT_show_pillow_message.bl_idname)
def register():
bpy.utils.register_class(CATAFEST_IMAGES_OT_show_modal_message)
bpy.utils.register_class(CATAFEST_IMAGES_OT_show_pillow_message)
bpy.utils.register_class(VIEW3D_PT_tools_image)
def unregister():
bpy.utils.unregister_class(CATAFEST_IMAGES_OT_show_modal_message)
bpy.utils.unregister_class(CATAFEST_IMAGES_OT_show_pillow_message)
bpy.utils.unregister_class(VIEW3D_PT_tools_image)
if __name__ == "__main__":
register()py --version
Python 3.11.0py -0
-V:3.11 * Python 3.11 (64-bit)
-V:3.10 Python 3.10 (64-bit)
-V:3.9 Python 3.9 (64-bit)
-V:3.7 Python 3.7 (64-bit)>py -3.10
Python 3.10.7 (tags/v3.10.7:6cc6b13, Sep 5 2022, 14:08:36) [MSC v.1933 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> exit()py -3.10 scripting001.py
import bpy
#get active object - default
obj = bpy.context.active_object
# set the default start for working with Geometry Nodes modifier
# you need to have a Geometry Nodes modifier
node_group = obj.modifiers['GeometryNodes'].node_group
nodes = node_group.nodes
#get the node named 'Group Output'
geom_out = nodes.get('Group Output')
#create a string node
string_node = nodes.new('FunctionNodeInputString')
# set the name to 'String'
string_out = string_node.outputs['String']
# set the value to "This is a string"
string_node.string = "This is a string"
# link to the Group Output
node_group.links.new(string_out, geom_out.inputs[-1])
winget install --id Python.Python.3 -e --source winget
Found Python 3 [Python.Python.3] Version 3.10.6
This application is licensed to you by its owner.
Microsoft is not responsible for, nor does it grant any licenses to, third-party packages.
Downloading https://www.python.org/ftp/python/3.10.6/python-3.10.6-amd64.exe
██████████████████████████████ 27.5 MB / 27.5 MB
Successfully verified installer hash
Starting package install...
Successfully installed

# import python packages
import bpy
from mathutils import Vector
# create a simpple BezierCurve and rename it with 'BezierCurveGeormetryNode'
bpy.ops.curve.primitive_bezier_curve_add()
bpy.ops.object.modifier_add(type='NODES')
curve = bpy.context.active_object
curve.name = 'BezierCurveGeormetryNode'
# define a function for GroupInit and GroupOutput
def new_GeometryNodes_group():
''' Create a new empty node group that can be used
in a GeometryNodes modifier.
'''
node_group = bpy.data.node_groups.new('GeometryNodes', 'GeometryNodeTree')
inNode = node_group.nodes.new('NodeGroupInput')
inNode.outputs.new('NodeSocketGeometry', 'Geometry')
outNode = node_group.nodes.new('NodeGroupOutput')
outNode.inputs.new('NodeSocketGeometry', 'Geometry')
node_group.links.new(inNode.outputs['Geometry'], outNode.inputs['Geometry'])
# the -3.5 is value for how far will be set the GroupInit and GroupOutput in the area of GeormetryNodes
inNode.location = Vector((-3.5*inNode.width, 0))
outNode.location = Vector((3.5*outNode.width, 0))
return node_group
# the default curve modifier has no node group set, you need to set :
if curve.modifiers[-1].node_group:
node_group = curve.modifiers[-1].node_group
else:
node_group = new_GeometryNodes_group()
curve.modifiers[-1].node_group = node_group
# set default grup node as nodes
nodes = node_group.nodes
# get both nodes for each one
group_in = nodes.get('Group Input')
group_out = nodes.get('Group Output')
# add the GeometryNodeObjectInfo to the GeometryNode area
new_node_obj = nodes.new('GeometryNodeObjectInfo')
new_node_obj.inputs[0].default_value = bpy.data.objects["BezierCurveGeormetryNode"]

import bpy
def menu_func(self, context):
'''Open explorer in windows systems'''
self.layout.operator(
"wm.url_open", text="Open explorer", icon='FILE_FOLDER').url = "C:/"
def register():
bpy.types.TOPBAR_MT_help.append(menu_func)
def unregister():
bpy.types.TOPBAR_MT_help.remove(menu_func)
if __name__ == "__main__":
register()
import bpy
def menu_func(self, context):
self.layout.operator(
OpenOperator.bl_idname, text="Open explorer", icon='FILE_FOLDER')
class OpenOperator(bpy.types.Operator):
"""Open explorer in windows systems"""
bl_idname = "wm.open_explorer"
bl_label = "Open explorer"
def execute(self, context):
bpy.ops.wm.url_open(url="C:/")
return {'FINISHED'}
def register():
bpy.utils.register_class(OpenOperator)
bpy.types.TOPBAR_MT_help.append(menu_func)
def unregister():
bpy.utils.unregister_class(OpenOperator)
bpy.types.TOPBAR_MT_help.remove(menu_func)
if __name__ == "__main__":
register()
StringProperty(
BoolProperty(
IntProperty(
IntVectorProperty(
FloatProperty(
FloatVectorProperty(
BoolVectorProperty(

import bpy
# Assign a collection
class SceneSettingItem(bpy.types.PropertyGroup):
name = bpy.props.StringProperty(name="Cube")
mesh = bpy.props.PointerProperty(type=bpy.types.Mesh)
PROPS = [
('myString', bpy.props.StringProperty(name='myString', default='this is my string!')),
('myBoolean', bpy.props.BoolProperty(name='myBoolean', default=False)),
('myInt', bpy.props.IntProperty(name='myInt', default=1)),
('myIntVectorXYZ', bpy.props.IntVectorProperty(subtype='XYZ')),
('myFloat', bpy.props.FloatProperty(name='myFloat', default=1)),
('myFloatVectorXYZ', bpy.props.FloatVectorProperty(subtype='XYZ')),
('myBooleanVector', bpy.props.BoolVectorProperty(size=3)),
('myBooleanVectorXYZ', bpy.props.BoolVectorProperty(size=3,subtype='XYZ')),
('myBooleanVectorColor', bpy.props.FloatVectorProperty(name="Edit Mode Color", subtype='COLOR', default=(0.76, 0.0, 0.0), size=3, min=0, max=1)),
('myCollectionProperty', bpy.props.CollectionProperty(type=SceneSettingItem)),
]
class HelloWorldPanelVariables(bpy.types.Panel):
"""Creates a Panel in the Object properties window"""
bl_label = "Hello World Panel Variables"
bl_idname = "OBJECT_PT_hello"
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.label(text="Hello world!", icon='WORLD_DATA')
# row = layout.row()
# row.label(text="Active object is: " + obj.name)
# row = layout.row()
# row.prop(obj, "name")
# row = layout.row()
# row.operator("mesh.primitive_cube_add")
def draw(self, context):
col = self.layout.column()
for (prop_name, _) in PROPS:
row = col.row()
row.prop(context.scene, prop_name)
def register():
bpy.utils.register_class(SceneSettingItem)
bpy.utils.register_class(HelloWorldPanelVariables)
for (prop_name, prop_value) in PROPS:
setattr(bpy.types.Scene, prop_name, prop_value)
def unregister():
bpy.utils.unregister_class(SceneSettingItem)
bpy.utils.unregister_class(HelloWorldPanelVariables)
for (prop_name, _) in PROPS:
delattr(bpy.types.Scene, prop_name)
if __name__ == "__main__":
register()

import bpy
class HelloWorldPanel(bpy.types.Panel):
"""Creates a Panel in the Object properties window"""
bl_label = "Hello World Panel"
bl_idname = "OBJECT_PT_hello"
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.label(text="Hello world!", icon='WORLD_DATA')
row = layout.row()
row.label(text="Active object is: " + obj.name)
row = layout.row()
row.prop(obj, "name")
row = layout.row()
row.operator("mesh.primitive_cube_add")
def register():
bpy.utils.register_class(HelloWorldPanel)
def unregister():
bpy.utils.unregister_class(HelloWorldPanel)
if __name__ == "__main__":
register()


bl_info = {
"name": "New branch",
"author": "Your Name Here",
"version": (1, 0),
"blender": (3, 3, 0),
"location": "View3D > Add > Mesh > New Object",
"description": "Adds a new branch Mesh Object",
"warning": "",
"doc_url": "",
"category": "Add Mesh",
}
import bpy
from bpy.types import Operator
from bpy.props import FloatVectorProperty
from bpy_extras.object_utils import AddObjectHelper, object_data_add
from mathutils import Vector
def add_object(self, context):
scale_x = self.scale.x
scale_y = self.scale.y
verts = [
Vector((-1 * scale_x, 1 * scale_y, 0)),
Vector((1 * scale_x, 1 * scale_y, 0)),
Vector((1 * scale_x, -1 * scale_y, 0)),
Vector((-1 * scale_x, -1 * scale_y, 0)),
]
edges = []
faces = [[0, 1, 2, 3]]
mesh = bpy.data.meshes.new(name="New Object Mesh")
mesh.from_pydata(verts, edges, faces)
# useful for development when the mesh may be invalid.
# mesh.validate(verbose=True)
object_data_add(context, mesh, operator=self)
class OBJECT_OT_add_object(Operator, AddObjectHelper):
"""Create a new branch Mesh Object"""
bl_idname = "mesh.add_object"
bl_label = "Add Mesh Object"
bl_options = {'REGISTER', 'UNDO'}
scale: FloatVectorProperty(
name="scale",
default=(1.0, 1.0, 1.0),
subtype='TRANSLATION',
description="scaling",
)
def execute(self, context):
add_object(self, context)
return {'FINISHED'}
# Registration
def add_object_button(self, context):
self.layout.operator(
OBJECT_OT_add_object.bl_idname,
text="catafest - add branch",
icon='PLUGIN')
# This allows you to right click on a button and link to documentation
def add_object_manual_map():
url_manual_prefix = "https://docs.blender.org/manual/en/latest/"
url_manual_mapping = (
("bpy.ops.mesh.add_object", "scene_layout/object/types.html"),
)
return url_manual_prefix, url_manual_mapping
def register():
bpy.utils.register_class(OBJECT_OT_add_object)
bpy.utils.register_manual_map(add_object_manual_map)
bpy.types.VIEW3D_MT_mesh_add.append(add_object_button)
def unregister():
bpy.utils.unregister_class(OBJECT_OT_add_object)
bpy.utils.unregister_manual_map(add_object_manual_map)
bpy.types.VIEW3D_MT_mesh_add.remove(add_object_button)
if __name__ == "__main__":
register()

import bpy
import random
# import bmesh
import bmesh
MinNubmer = -10
MaxNumber = 10
# Clean up the area , uncoment the next two row to keep
# branch after running the script
#bpy.ops.object.select_all(action="SELECT")
#bpy.ops.object.delete()
# Number of branches
branch = 4
# Create the verts array
verts = [(0,0,0)]
# Create the edges array
edges = [(0,0)]
# Create the faces array
faces = []
# define random number for X and Y axis
def RN():
return random.randint(MinNubmer, MaxNumber) / 20
# define random number for positive Z axis
def RNZ():
return random.randint(10, 50) / 10
# create a list of branch thicknesses
rand_list = []
name_branch = "TreeMesh"
# define createBranch
def createBranch(branch, name_branch):
# Create the mesh for branch
mesh = bpy.data.meshes.new(name_branch)
for i in range(1,branch):
rand_list.append(RNZ()/30)
# sort all reverse by thicknesses
rand_list.sort(reverse=True)
# generate vertices list for drawing the branch
for i in range(1,branch):
verts.append((rand_list[i-1] +0.1,rand_list[i-1]+0.1,RNZ()))
edges.append((i-1,i))
# sort the list of vertices by last number witch is Z axis
verts.sort(key=lambda x: x[2])
# create branch update and validate, see documentation
mesh.from_pydata(verts, edges, faces)
mesh.update()
mesh.validate()
# Create object to hold the mesh branch with the new name for object
obj = bpy.data.objects.new(name_branch+'_Obj', mesh)
return obj
# create a new branch
def createNewBranch(obj_branch, n):
bpy.ops.object.mode_set(mode="EDIT", toggle=False)
me = obj_branch.data
bm = bmesh.from_edit_mesh(me)
bm.select_mode = {'VERT'}
for i,v in enumerate(bm.verts):
# select only by the index of list
if i == n:
v.select = ( v.co.x > 0.0 )
v2 = v
else:
v.select = False
# flush and update view
v1 = bm.verts.new( (RN()+(v.co.x) + 1.0 , RN()+(v.co.y) + 1.0 , (v.co.z) - (v.co.z)/3) )
#v1 = bm.verts.new(1, 1, 3)
bm.edges.new((v1, v2))
rand_list.append(0.01)
rand_list.sort(reverse=True)
# update
bm.select_flush_mode()
me.update()
#mesh.validate()
#bmesh.update_edit_mesh(obj_branch.data)
# use the createBranch
obj_branch = createBranch(branch, name_branch)
## now set up shape key in Blender
#mesh=obj_branch.data
#sk_basis = obj_branch.shape_key_add(name='Basis',from_mix=False)
#sk_basis.interpolation = 'KEY_LINEAR'
## must set relative to false here
#obj_branch.data.shape_keys.use_relative = False
## create new shape key
#sk = obj_branch.shape_key_add(name='Deform',from_mix=False)
#sk.interpolation = 'KEY_LINEAR'
#sk.slider_min = 0
#sk.slider_max = 2
# ... and add it to the scene
scene = bpy.context.scene
scene.collection.objects.link(obj_branch)
# this will fix the error ... mode_set_poll()
bpy.context.view_layer.objects.active = obj_branch
createNewBranch(obj_branch, 1)
# print tool for developing area
def print_python_console(data):
for window in bpy.context.window_manager.windows:
screen = window.screen
for area in screen.areas:
if area.type == 'CONSOLE':
override = {'window': window, 'screen': screen, 'area': area}
bpy.ops.console.scrollback_append(override, text=str(data), type="OUTPUT")
# used to see the size of radius skin for each vertices
print_python_console(rand_list)
# fix error : skin modifier is locked when using edit mode.
bpy.ops.object.mode_set(mode="OBJECT", toggle=False)
# add the skin modifier - NOT FIXED FOR THE LAST BRANC ADDED
obj_branch.modifiers.new(name="SK", type="SKIN")
bpy.context.view_layer.objects.active = obj_branch
# get the skin vertices layers
skin_vertices = obj_branch.data.skin_vertices
# get the layer
skin_layer = skin_vertices[0]
for i in range(1,branch+1):
# assigns radius for each vertice to sized the branch
skin_layer.data[i-1].radius = (rand_list[i-1], rand_list[i-1])
#Indices 0 and 1 are the vertex indices
skin_layer.data[i].radius = (rand_list[i-1],rand_list[i-1])
# this will apply the modifier named 'SK'
bpy.ops.object.modifier_apply( modifier = 'SK' )
#
bpy.ops.object.mode_set(mode="EDIT", toggle=True)
bpy.ops.object.skin_root_mark()
bpy.ops.object.mode_set(mode="OBJECT", toggle=True)
# set modes for user
mesh = bpy.data.meshes.new(name_branch+'_Obj')
mesh.update()
mesh.validate()
import math
def get_dimension(normal):
x_abs = math.fabs(normal[0])
y_abs = math.fabs(normal[1])
z_abs = math.fabs(normal[2])
if z_abs >= x_abs and z_abs >= y_abs:
return 2
elif x_abs >= y_abs:
return 0
else:
return 1
texture_scale = 1.0
bpy.ops.object.mode_set(mode="EDIT", toggle=True)
bpy.ops.mesh.select_all(action='SELECT')
me = obj_branch.data
bm = bmesh.from_edit_mesh(me)
bpy.ops.uv.sphere_project()
uv_layer = bm.loops.layers.uv.verify()
# adjust uv coordinates
for face in bm.faces:
for l in face.loops:
luv = l[uv_layer]
# select UV vertex if these are in certain range
if 0 <= luv.uv.x <= 1 and 0 <= luv.uv.y <= 1:
luv.select = True
luv = l[uv_layer]
luv.uv = l.vert.co.yz * texture_scale
luv.uv = l.vert.co.xz * texture_scale
luv.uv = l.vert.co.xy * texture_scale
bmesh.update_edit_mesh(me)
me.update()
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.uv.smart_project(angle_limit=1.15192, island_margin=0, area_weight=0, correct_aspect=True, scale_to_bounds=False)
# use this for cylinder project for UV map
#bpy.ops.uv.cylinder_project(direction='ALIGN_TO_OBJECT',
#align='POLAR_ZX',
#radius=1.0,
#correct_aspect=True,
#clip_to_bounds=False,
#scale_to_bounds=True)
bmesh.update_edit_mesh(me)
bpy.ops.object.mode_set(mode="OBJECT", toggle=True)