Skip to content

Instantly share code, notes, and snippets.

@sina-mansour
Created September 30, 2024 05:02
Show Gist options
  • Save sina-mansour/3871564599e90cb3af9b5624e170b657 to your computer and use it in GitHub Desktop.
Save sina-mansour/3871564599e90cb3af9b5624e170b657 to your computer and use it in GitHub Desktop.
A blender 4.2 python script for cloth simulation along with a transparent brain surface rendered by cycles engine
# Blender executed via:
# PYTHONPATH=/home/sina/miniconda3/envs/blender_env/bin/python blender --python-use-system-env
# Notes:
# Setting up blender to use a conda virtualenv:
# https://stackoverflow.com/questions/70639689/how-to-use-the-anaconda-environment-on-blender
# Blender Python API:
# https://docs.blender.org/api/current/info_quickstart.html
# Blender Python tips and tricks:
# https://docs.blender.org/api/current/info_tips_and_tricks.html
# Blender can be built from source to enable direct python execution:
# https://developer.blender.org/docs/handbook/building_blender/python_module/
# Building blender:
# https://developer.blender.org/docs/handbook/building_blender/
# Idea: conda can be used to build blender for a python package:
# https://docs.aws.amazon.com/deadline-cloud/latest/developerguide/create-conda-recipe-blender.html
# https://github.com/aws-deadline/deadline-cloud-samples/blob/mainline/conda_recipes/blender-4.2/recipe/meta.yaml
import bpy
import bmesh
import math
import numpy as np
import nibabel as nib
import matplotlib.pyplot as plt
from Connectome_Spatial_Smoothing import CSS as css
from cerebro import cerebro_brain_utils as cbu
from cerebro import cerebro_brain_viewer as cbv
# function to load brain surface geometries
def load_cortical_mesh(surface):
left_surface_file, right_surface_file = cbu.get_left_and_right_GIFTI_template_surface(surface)
# left_surface = nib.load(left_surface_file)
left_vertices, left_triangles = cbu.load_GIFTI_surface(left_surface_file)
return left_vertices, left_triangles
# function to clean the blender scene
def blender_clean_up():
# make sure the active object is not in Edit Mode
if bpy.context.active_object and bpy.context.active_object.mode == "EDIT":
bpy.ops.object.editmode_toggle()
# make sure non of the objects are hidden from the viewport, selection, or disabled
for obj in bpy.data.objects:
obj.hide_set(False)
obj.hide_select = False
obj.hide_viewport = False
# select all the object and delete them (just like pressing A + X + D in the viewport)
bpy.ops.object.select_all(action="SELECT")
bpy.ops.object.delete()
# find all the collections and remove them
collection_names = [col.name for col in bpy.data.collections]
for name in collection_names:
bpy.data.collections.remove(bpy.data.collections[name])
# in the case when you modify the world shader
# delete and recreate the world object
world_names = [world.name for world in bpy.data.worlds]
for name in world_names:
bpy.data.worlds.remove(bpy.data.worlds[name])
# create a new world data block
bpy.ops.world.new()
bpy.context.scene.world = bpy.data.worlds["World"]
bpy.ops.outliner.orphans_purge(do_local_ids=True, do_linked_ids=True, do_recursive=True)
# function to adjust animation frame setup
def frame_setup(start_frame, end_frame):
# Set the start and end frames of the animation
bpy.context.scene.frame_start = start_frame
bpy.context.scene.frame_end = end_frame
# function to create a mesh
def create_mesh_object(vertices, faces, mesh_name, object_name):
# Create a new mesh and object
mesh = bpy.data.meshes.new(mesh_name)
obj = bpy.data.objects.new(object_name, mesh)
# Link the object to the current scene collection
bpy.context.collection.objects.link(obj)
# Set the object as active and select it
bpy.context.view_layer.objects.active = obj
obj.select_set(True)
# Create the mesh from the vertices and faces
mesh.from_pydata(vertices, [], faces)
mesh.update() # Update the mesh to make sure it's updated in the scene
# Return the created object
return mesh, obj
# function to apply smooth shading to object
def shade_smooth(obj):
# Ensure the object is selected and active
bpy.context.view_layer.objects.active = obj
obj.select_set(True)
# Apply smooth shading
bpy.ops.object.shade_smooth()
# function to turn data to colors
def generate_vertex_colors(data, colormap=plt.cm.coolwarm, alpha=1.0):
# Normalize the data and map to colors
scaled_data = (data - data.min()) / (data.max() - data.min())
vertex_data = np.zeros(left_vertices_white.shape[0]) * np.nan
vertex_data[np.array(left_cortical_surface_model.vertex_indices)] = scaled_data[left_cortical_surface_model.index_offset:left_cortical_surface_model.index_count]
vertex_colors = colormap(vertex_data)
# adjust alpha
vertex_colors[:, 3] = alpha
return vertex_colors
# Function to update vertex colors on a mesh
def update_vertex_colors(mesh, vertex_colors):
color_layer = mesh.vertex_colors.active.data
for loop_index, loop in enumerate(mesh.loops):
vertex_index = loop.vertex_index
color_layer[loop_index].color = vertex_colors[vertex_index]
# Function to add vertex colors to a mesh's vertex color layer
def create_layer(mesh, layer_name):
# Create a new vertex color layer or use an existing one
if layer_name not in mesh.vertex_colors:
mesh.vertex_colors.new(name=layer_name)
vertex_color_layer = mesh.vertex_colors[layer_name]
mesh.vertex_colors.active_index = [x[0] for x in mesh.vertex_colors.items()].index(layer_name)
return vertex_color_layer
# Function to add vertex colors to a mesh's vertex color layer
def add_vertex_colors_to_layer(mesh, vertex_colors, layer_name):
create_layer(mesh, layer_name)
update_vertex_colors(mesh, vertex_colors)
mesh.update()
# Function to create material and assign color to it
def assign_object_material_with_color(obj, color_layer_name, metallic, roughness):
# Create a new material if it doesn't exist
material = bpy.data.materials.new("VertexColorMaterial")
material.use_nodes = True
nodes = material.node_tree.nodes
links = material.node_tree.links
# Clear existing nodes
for node in nodes:
nodes.remove(node)
# Create nodes: Vertex Color node, Principled BSDF, and Material Output
vertex_color_node = nodes.new(type='ShaderNodeVertexColor')
vertex_color_node.layer_name = color_layer_name
principled_bsdf = nodes.new(type='ShaderNodeBsdfPrincipled')
material_output = nodes.new(type='ShaderNodeOutputMaterial')
# Connect vertex color layer to the Principled BSDF shader and then to the material output
links.new(vertex_color_node.outputs['Color'], principled_bsdf.inputs['Base Color'])
links.new(vertex_color_node.outputs['Alpha'], principled_bsdf.inputs['Alpha'])
links.new(principled_bsdf.outputs['BSDF'], material_output.inputs['Surface'])
# Other material properties
principled_bsdf.inputs["Metallic"].default_value = metallic
principled_bsdf.inputs["Roughness"].default_value = roughness
# Assign the material to the object
if len(obj.data.materials) > 0:
obj.data.materials[0] = material
else:
obj.data.materials.append(material)
return material
# Function to create object
def create_data_object(name, object_data):
obj = bpy.data.objects.new(name, object_data)
bpy.context.collection.objects.link(obj)
return obj
# Function to create empty
def create_empty(name, location):
# Create an empty object
bpy.ops.object.empty_add(type='PLAIN_AXES', location=location)
empty = bpy.context.object
empty.name = name
return empty
# Function to create plane
def create_plane(name, scale, location, rotation, subdivision_kws={}):
# Create a plane
bpy.ops.mesh.primitive_plane_add(location=location, rotation=rotation)
plane = bpy.context.object
plane.name = name
plane.scale[0] = scale[0] / 2
plane.scale[1] = scale[1] / 2
# subdivide if needed
if subdivision_kws['subdivide']:
# Enter edit mode to further subdivide
bpy.ops.object.mode_set(mode='EDIT')
bm = bmesh.from_edit_mesh(plane.data)
# Subdivide the mesh further to get 80 sections
bmesh.ops.subdivide_edges(bm, edges=bm.edges, cuts=subdivision_kws['cuts'], use_grid_fill=True,)
bmesh.update_edit_mesh(plane.data)
bpy.ops.object.mode_set(mode='OBJECT')
return plane
# Function to group a subset of plane vertices
def group_top_vertices(plane, group_name, epsilon=0.01):
# Create a vertex group for the top central vertices
vertex_group = plane.vertex_groups.new(name=group_name)
# Get the top vertices (considering Z direction)
top_z_coord = np.min([v.co.x for v in plane.data.vertices])
max_y_coord = np.max([v.co.y for v in plane.data.vertices])
mid_y_coord = np.mean([v.co.y for v in plane.data.vertices])
y_selection_lim = (max_y_coord - mid_y_coord) / 3
top_verts = [
v for v in plane.data.vertices
if (
(np.abs(v.co.x - top_z_coord) < epsilon) and
(np.abs(v.co.y - mid_y_coord) <= y_selection_lim)
)
]
# Assign the top vertices to the vertex group
for v in top_verts:
vertex_group.add([v.index], 1.0, 'ADD')
return vertex_group
# Function to set a hook modifier on vertex group
def set_hook_modifier(object_to_be_hooked, vertex_group_name, hook_object):
# Add a hook modifier to the plane and assign the top vertices
hook_modifier = object_to_be_hooked.modifiers.new(name="Hook", type='HOOK')
hook_modifier.object = hook_object
hook_modifier.vertex_group = vertex_group_name
return hook_modifier
# Function to add cloth physics to object
def add_cloth_physics(object, pin_vertex_group=None, vertex_mass=1):
# Ensure the object is a mesh
if object.type != 'MESH':
print(f"Error: {object.name} is not a mesh object.")
return
# Add Cloth modifier
cloth_modifier = object.modifiers.new(name="Cloth", type='CLOTH')
# Optional: Set the pinning vertex group (if specified)
if pin_vertex_group is not None and pin_vertex_group in object.vertex_groups:
cloth_modifier.settings.vertex_group_mass = pin_vertex_group
# Default settings for cloth simulation (you can customize these as needed)
cloth_modifier.settings.quality = 30 # Quality of the simulation (higher is slower but more accurate)
cloth_modifier.settings.time_scale = 5 # Speed Multiplier
cloth_modifier.settings.mass = vertex_mass # Mass of the cloth
cloth_modifier.settings.air_damping = 1 # Air resistance
cloth_modifier.settings.tension_stiffness = 10 # Stiffness of the cloth
cloth_modifier.settings.compression_stiffness = 10
cloth_modifier.settings.shear_stiffness = 5
cloth_modifier.settings.bending_stiffness = 0.2
# Enable self-collision to prevent parts of the cloth from intersecting itself
cloth_modifier.collision_settings.use_self_collision = True
cloth_modifier.collision_settings.self_friction = 5 # Friction during self-collision
cloth_modifier.collision_settings.self_distance_min = 0.015 # Minimum distance between self-collisions (tweak if needed)
return cloth_modifier
# Function to add collision physics to object
def add_collision_physics(object):
# Ensure the object is a mesh
if object.type != 'MESH':
print(f"Error: {object.name} is not a mesh object.")
return
# Add collision modifier
collision_modifier = object.modifiers.new(name="Collision", type='COLLISION')
# Set default collision settings (you can customize these)
collision_modifier.settings.thickness_outer = 0.8 # Outer thickness of collision boundary
collision_modifier.settings.thickness_inner = 0.8 # Inner thickness of collision boundary
collision_modifier.settings.damping = 0.5 # Damping factor for collision
collision_modifier.settings.cloth_friction = 0.5 # Friction factor for collision
# Function to add subdivision modifier
def add_subdivision_modifier(obj, levels=3):
# Ensure the object is a mesh
if obj.type != 'MESH':
print(f"Error: {obj.name} is not a mesh object.")
return
# Add Cloth modifier
subdivision_modifier = obj.modifiers.new(name="Subdivision", type='SUBSURF')
# Set subdivision settings
subdivision_modifier.levels = levels # Viewport levels
subdivision_modifier.render_levels = levels # render levels
def add_cloth_thickness(obj, thickness=0.001):
# Ensure the object is a mesh
if obj.type != 'MESH':
print(f"Error: {obj.name} is not a mesh object.")
return
# Add a Solidify modifier for thickness
solidify_mod = obj.modifiers.new(name="Solidify", type='SOLIDIFY')
# Set the thickness
solidify_mod.thickness = thickness
# Optional: Set other solidify settings
solidify_mod.offset = -1.0
solidify_mod.use_rim = True # Fill in any open edges
# Function to apply auto smooth shading
def enable_auto_smooth_shading(obj, angle=30.0):
# Ensure the object is selected and active
bpy.context.view_layer.objects.active = obj
obj.select_set(True)
# Enable Auto Smooth for the object
bpy.ops.object.shade_auto_smooth()
# Function to create a silk like material
def create_silk_material():
# Create a new material
silk_material = bpy.data.materials.new(name="Royal_Red_Silk")
# Enable the use of nodes (necessary for Principled BSDF)
silk_material.use_nodes = True
# Get the material's node tree
nodes = silk_material.node_tree.nodes
links = silk_material.node_tree.links
# Clear any default nodes
for node in nodes:
nodes.remove(node)
# Add a Principled BSDF shader node
bsdf = nodes.new(type='ShaderNodeBsdfPrincipled')
bsdf.location = (0, 0)
# Set the base color to royal red
bsdf.inputs['Base Color'].default_value = (0.3, 0.01, 0.03, 1) # RGB + Alpha (Red with a bit of saturation)
# Set a low roughness for silk-like smoothness
bsdf.inputs['Roughness'].default_value = 0.5 # Lower roughness makes the surface shiny
# Set a low roughness for silk-like material
bsdf.inputs['Metallic'].default_value = 0.7
# # Specular and sheen-related properties for silk fabric
# bsdf.inputs['Specular IOR Level'].default_value = 0.8 # Adjust specular reflection
# bsdf.inputs['Specular Tint'].default_value = (1, 0.1, 0.1, 1) # White specular reflection
# # Anisotropy for directional fabric-like sheen
# bsdf.inputs['Anisotropic'].default_value = 0.8 # High anisotropy
# bsdf.inputs['Anisotropic Rotation'].default_value = 0.2 # Low anisotropic rotation
# # Sheen settings for fabric glow
# bsdf.inputs['Sheen Weight'].default_value = 0.5 # Moderate sheen
# bsdf.inputs['Sheen Roughness'].default_value = 0.1 # Smooth sheen
# bsdf.inputs['Sheen Tint'].default_value = (0.9, 0.05, 0.05, 1) # Slight red tint to sheen
# Add an Output node and link it to the BSDF
output = nodes.new(type='ShaderNodeOutputMaterial')
output.location = (400, 0)
links.new(bsdf.outputs['BSDF'], output.inputs['Surface'])
return silk_material
# Function to assign material to object
def assign_material_to_object(material, obj):
# Ensure the object is a mesh
if obj.type != 'MESH':
print(f"Error: {obj.name} is not a mesh object.")
return
# Assign the material to the object
if obj.data.materials:
# If there are materials, assign to first slot
obj.data.materials[0] = material
else:
# If no materials, append a new one
obj.data.materials.append(material)
# Function to add a camera
def create_camera(camera_location, target_location):
camera_data = bpy.data.cameras.new(name="Camera")
camera_object = create_data_object("Camera", camera_data)
camera_object.location = camera_location
# Add a target (empty object) for the camera to focus on
target_object = create_data_object("CameraTarget", None)
target_object.location = target_location
# Add the "Track To" constraint to the camera to make it always look at the target
constraint = camera_object.constraints.new(type='TRACK_TO')
constraint.target = target_object
# parent to target to aid camera rotations
camera_object.parent = target_object
# Set the camera as the active camera for the scene
bpy.context.scene.camera = camera_object
return camera_object, target_object
# Function to add a sunlight
def create_sunlight(name, rotation, strength=1.0):
# Add a new sun light to the scene
light_data = bpy.data.lights.new(name=name, type='SUN')
light_object = create_data_object(name, light_data)
# Position the light in the scene
light_object.rotation_euler = rotation
# Set the strength of the light
light_data.energy = 1.0 # You can adjust this value to make the scene brighter or dimmer
return light_object
# Function to change background color and lighting
def set_background(background_color, ambient_light_strength=1.0):
bpy.context.scene.world.use_nodes = True
bg = bpy.context.scene.world.node_tree.nodes.get("Background")
bg.inputs[0].default_value = (0, 0, 0, 1) # Change to desired RGB (1,1,1) for white ambient light
bg.inputs[1].default_value = 0.5 # Strength of the ambient light
# Function to set the resolution of renders
def set_render_properties(resolution_x=1920, resolution_y=1080, resolution_percentage=100, fps=24):
bpy.context.scene.render.resolution_x = resolution_x # Width in pixels
bpy.context.scene.render.resolution_y = resolution_y # Height in pixels
bpy.context.scene.render.resolution_percentage = resolution_percentage # Scale
bpy.context.scene.render.fps = fps # frame rate
# Render a single frame to a PNG
def render_single_frame(frame, filepath, engine='BLENDER_EEVEE_NEXT', transparent=True):
# Set the current frame to render
bpy.context.scene.frame_set(frame)
# Set render output file path
bpy.context.scene.render.filepath = filepath
# Set render engine to 'CYCLES' or 'BLENDER_EEVEE_NEXT'
bpy.context.scene.render.engine = engine
# Set the file format to PNG
bpy.context.scene.render.image_settings.file_format = 'PNG'
# Make the background transparent
if transparent:
bpy.context.scene.render.film_transparent = True
bpy.context.scene.render.image_settings.color_mode = 'RGBA' # Enable alpha (transparency)
# Trigger the render and save the image
bpy.ops.render.render(write_still=True)
# Render an animation to MP4
def render_animation(filepath, engine='BLENDER_EEVEE_NEXT', render=False):
# Set render output file path
bpy.context.scene.render.filepath = filepath
# Set render engine to 'CYCLES' or 'BLENDER_EEVEE_NEXT'
bpy.context.scene.render.engine = engine
bpy.context.scene.render.use_persistent_data = True # Added to speedup rendering
if engine=='CYCLES':
bpy.context.scene.cycles.adaptive_threshold = 0.1 # Added to speedup rendering
# Set the file format to FFmpeg video
bpy.context.scene.render.image_settings.file_format = 'FFMPEG'
# Set the codec and encoding options
bpy.context.scene.render.ffmpeg.format = 'MPEG4'
bpy.context.scene.render.ffmpeg.codec = 'H264'
bpy.context.scene.render.ffmpeg.constant_rate_factor = 'HIGH'
# Render the animation
if render:
bpy.ops.render.render(animation=True)
# Render an animation with transparency to WEBM
def render_transparent_animation(filepath, engine='BLENDER_EEVEE_NEXT'):
# Set render output file path
bpy.context.scene.render.filepath = filepath
# Set render engine to 'CYCLES' or 'BLENDER_EEVEE_NEXT'
bpy.context.scene.render.engine = engine
bpy.context.scene.render.use_persistent_data = True # Added to speedup rendering
if engine=='CYCLES':
bpy.context.scene.cycles.adaptive_threshold = 0.1 # Added to speedup rendering
# Enable transparency for the world background
bpy.context.scene.render.film_transparent = True
# Set the file format to FFmpeg video
bpy.context.scene.render.image_settings.file_format = 'FFMPEG'
# Set the codec and encoding options
bpy.context.scene.render.ffmpeg.format = 'WEBM' # or 'QUICKTIME' for .mov
bpy.context.scene.render.ffmpeg.codec = 'VP9' # 'WEBM' or 'QTRLE' for QuickTime
# Ensure the color mode is RGBA (for transparency)
bpy.context.scene.render.image_settings.color_mode = 'RGBA'
# Render the animation
bpy.ops.render.render(animation=True)
# loading brain data
################################################################################
################################################################################
left_vertices_white, left_triangles_white = load_cortical_mesh('white')
left_vertices_pial, left_triangles_pial = load_cortical_mesh('inflated')
dscalar = nib.load(cbu.cifti_template_file)
brain_models = [x for x in dscalar.header.get_index_map(1).brain_models]
left_cortical_surface_model, right_cortical_surface_model = brain_models[0], brain_models[1]
gradients = nib.load("/mnt/local_storage/Research/Codes/fMRI/DataStore/Templates/principal_gradient/hcp.gradients.dscalar.nii")
# Blender scripting starts here
################################################################################
################################################################################
# First clean the scene
blender_clean_up()
################################################################################
################################################################################
################################################################################
# Frame timing
start_frame = 1
end_frame = 200
frame_setup(start_frame=start_frame, end_frame=end_frame)
################################################################################
################################################################################
################################################################################
# Now create the mesh
mesh, left_cortex_obj = create_mesh_object(
left_vertices_white, left_triangles_white, mesh_name="MyMesh", object_name="MyObject"
)
################################################################################
################################################################################
################################################################################
# Apply smooth shading
shade_smooth(left_cortex_obj)
################################################################################
################################################################################
################################################################################
# handling colors
# first create vertex color layer
vertex_color_layer = add_vertex_colors_to_layer(
mesh=mesh, layer_name="Principal_Gradient",
vertex_colors=generate_vertex_colors(gradients.get_fdata()[0], colormap=plt.cm.autumn, alpha=0.5),
)
# then apply to a material for an object
material = assign_object_material_with_color(
obj=left_cortex_obj, color_layer_name="Principal_Gradient",
metallic=0.8, roughness=0.6
)
################################################################################
################################################################################
################################################################################
# create a cloth
# first create the plane
cuts=100
cloth = create_plane(
name="ClothPlane", scale=(200, 300),
location=(
left_vertices_white[:,0].min() - 1,
left_vertices_white[:,1].mean(),
left_vertices_white[:,2].mean()
), rotation=(0,math.radians(90),0), subdivision_kws={'subdivide': True, 'cuts': cuts}
)
# select some of the vertices from the top of the cloth and make vertex group
cloth_top_group_name = "cloth_top"
cloth_top = group_top_vertices(plane=cloth, group_name=cloth_top_group_name)
# create a hook object
cloth_hook = create_empty(
name="cloth_hook",
location=(
left_vertices_white[:,0].min() - 1,
left_vertices_white[:,1].mean(),
left_vertices_white[:,2].mean() + 100
)
)
# Add a hook modifier to the plane and assign the top vertices
hook_modifier = set_hook_modifier(
object_to_be_hooked=cloth,
vertex_group_name=cloth_top_group_name,
hook_object=cloth_hook
)
# Add cloth physics to the plane
cloth_modifier = add_cloth_physics(object=cloth, pin_vertex_group=cloth_top_group_name, vertex_mass=(80/cuts))
# Subdivide the cloth after physics to render realistic
add_subdivision_modifier(obj=cloth, levels=3)
# Add collision physics to the brain
add_collision_physics(object=left_cortex_obj)
# Auto smooth shading for the cloth
enable_auto_smooth_shading(cloth)
# Make the cloth a bit thicker
add_cloth_thickness(cloth)
# Create the silk material
silk_material = create_silk_material()
# Make the cloth look silky
assign_material_to_object(silk_material, cloth)
# Use hook to animate the cloth
cloth_hook.keyframe_insert(data_path="location", frame=10)
cloth_hook.location = (
0,
left_vertices_white[:,1].mean(),
left_vertices_white[:,2].mean() + 100
)
cloth_hook.keyframe_insert(data_path="location", frame=40)
cloth_hook.location = (
40,
left_vertices_white[:,1].mean(),
left_vertices_white[:,2].mean() + 0
)
cloth_hook.keyframe_insert(data_path="location", frame=100)
cloth_hook.location = (
1600,
left_vertices_white[:,1].mean(),
left_vertices_white[:,2].mean() + 0
)
cloth_hook.keyframe_insert(data_path="location", frame=200)
################################################################################
################################################################################
################################################################################
# Add a camera object
camera_object, target_object = create_camera(
camera_location=(-260, 0, 0),
target_location=left_vertices_white.mean(0)
)
# camera animation
for i in range(end_frame):
# Keyframe the rotation of the empty (animate around the Z axis)
target_object.rotation_euler = (
0,
np.cos(2 * np.pi * i / end_frame)**3 * math.radians(4),
np.sin(4 * np.pi * i / end_frame) * math.radians(2)
)
target_object.keyframe_insert(data_path="rotation_euler", frame=i + 1)
################################################################################
################################################################################
################################################################################
# add multiple surrounding light sources
light_count = 4
for i in range(light_count + 1):
# Add a new sun light to the scene
create_sunlight(
name=f"SunLight_{i}",
rotation=(np.pi / 2, 0, (-np.pi * (i / light_count) / 2) - (np.pi/2)),
strength=1.0
)
# and a light source from above
create_sunlight(
name=f"SunLight_{light_count + 1}",
rotation=(0, 0, 0),
strength=2.0
)
################################################################################
print("here")
################################################################################
################################################################################
# Set the world background color
set_background(background_color=(0, 0, 0, 1), ambient_light_strength=0.5)
################################################################################
################################################################################
################################################################################
# Set render resolution
set_render_properties(resolution_x=1000, resolution_y=720)
################################################################################
################################################################################
################################################################################
# render a single frame and store as image
# render_single_frame(frame=1, filepath="/mnt/local_storage/Research/Codes/fMRI/DataStore/tmp/blender/rendered_image.png", engine='BLENDER_EEVEE_NEXT')
################################################################################
################################################################################
################################################################################
# render animation
render_animation(filepath="/mnt/local_storage/Research/Codes/fMRI/DataStore/tmp/blender/rendered_movie.mp4", engine='BLENDER_EEVEE_NEXT')
################################################################################
################################################################################
################################################################################
# bpy.ops.screen.animation_play()
################################################################################
################################################################################
################################################################################
# # Finally save the project
# # Specify the file path where you want to save the Blender project
# file_path = "/mnt/local_storage/Research/Codes/fMRI/DataStore/tmp/blender/project_cloth.blend"
# # Save the current Blender file
# bpy.ops.wm.save_as_mainfile(filepath=file_path)
################################################################################
@sina-mansour
Copy link
Author

Check out this video.

rendered_movie_4.2_cloth_5.mp4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment