0% found this document useful (0 votes)
255 views7 pages

Excel To Blender Data Comparison Full Script

The document reads data from an Excel file and uses it to generate 3D bars in Blender with text labels. It creates materials, collections, adds modifiers, and inserts keyframes to animate the bars and camera movement over time.

Uploaded by

jitendra prpc
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
255 views7 pages

Excel To Blender Data Comparison Full Script

The document reads data from an Excel file and uses it to generate 3D bars in Blender with text labels. It creates materials, collections, adds modifiers, and inserts keyframes to animate the bars and camera movement over time.

Uploaded by

jitendra prpc
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 7

import bpy

import math
import pandas as pds

context = bpy.context
scene = context.scene

excel_file_path = r"C:\MyTest\Database.xlsx"
font_file_path = r"C:\MyTest\Arialbd.ttf"
name_column = 2
data_column = 3
display_data_column = 4
display_pic_column = 5
anim_speed_factor = 5 # Enter a value between 1 (slow) and 10 (fast)

anim_start_frame = 10
camera_start_frame = 30
data_start_position = 0
distance_bet_data = 3
duration_fac_1 = 4
duration_fac_2 = 6
data_norm_factor = 5
min_diff_factor = 20

# Save the current location of the 3D cursor


saved_cursor_loc = scene.cursor.location.xyz

# Initialize the variables.


data_counter = 0
anim_curr_frame = anim_start_frame
speed_multiplier = 11 - anim_speed_factor
anim_length = speed_multiplier * duration_fac_1
anim_interval = speed_multiplier * duration_fac_2

# Read the excel file and store the data in an array


xls_data = pds.read_excel(excel_file_path)
name_list = xls_data.iloc[:,name_column-1]
data_list = xls_data.iloc[:,data_column-1]
display_data_list = xls_data.iloc[:,display_data_column-1]
display_pic_list = xls_data.iloc[:,display_pic_column-1]
number_of_data = len(name_list)
data_height_mean = sum(data_list) / len(data_list)

# Create a normalized data list


normalized_data_list = [data_list[count] * data_norm_factor/data_height_mean for
count in range(number_of_data)]
height_threshold = (max(normalized_data_list) - min(normalized_data_list)) /
min_diff_factor

# Maintain a minimum gap between two successive data bars


for i in range(number_of_data-1):
if (normalized_data_list[i] < normalized_data_list[i+1]) and \
(normalized_data_list[i+1] - normalized_data_list[i] < height_threshold):
for j in range(i+1,number_of_data):
normalized_data_list[j] += height_threshold
elif (normalized_data_list[i] > normalized_data_list[i+1]) and \
(normalized_data_list[i] - normalized_data_list[i+1] < height_threshold):
for j in range(i+1,number_of_data):
normalized_data_list[j] -= height_threshold
# Create a new material for the bars
material_1 = bpy.data.materials.new(name="anim_material_1")
material_1.use_nodes = True
if material_1.node_tree:
material_1.node_tree.links.clear()
material_1.node_tree.nodes.clear()
nodes = material_1.node_tree.nodes
links = material_1.node_tree.links
output = nodes.new(type='ShaderNodeOutputMaterial')
shader = nodes.new(type='ShaderNodeBsdfPrincipled')
shader.location = (-300, 0)
shader.inputs[0].default_value = (0.15, 0.0, 0.0, 1)
links.new(shader.outputs[0], output.inputs[0])

# Create a new material for the text


material_2 = bpy.data.materials.new(name="anim_material_2")
material_2.use_nodes = True
if material_2.node_tree:
material_2.node_tree.links.clear()
material_2.node_tree.nodes.clear()
nodes = material_2.node_tree.nodes
links = material_2.node_tree.links
output = nodes.new(type='ShaderNodeOutputMaterial')
shader = nodes.new(type='ShaderNodeBsdfPrincipled')
shader.location = (-300, 0)
shader.inputs[0].default_value = (1, 1, 1, 1)
links.new(shader.outputs[0], output.inputs[0])

# Add an empty and set its initial position


bpy.ops.object.empty_add()
empty = bpy.context.active_object
empty.location = [0,0,10]
empty.hide_set(True)
empty.hide_render = True

# Set initial camera position


camera = bpy.data.objects['Camera']
camera.location = [-3,-15,10]
camera.rotation_euler = [math.radians(80),0,0]
bpy.data.cameras['Camera'].passepartout_alpha = 1.0
bpy.data.cameras['Camera'].clip_start = 0.1
bpy.data.cameras['Camera'].clip_end = 1000

# Add a track-to constraint for the camera


camera.constraints.clear()
track_to = camera.constraints.new('TRACK_TO')
track_to.target = empty
track_to.track_axis = 'TRACK_NEGATIVE_Z'
track_to.up_axis = 'UP_Y'

# Set light properties


Light = bpy.data.objects['Light']
Light.data.type = "SUN"
Light.data.energy = 4
Light.data.use_shadow = True
Light.data.use_contact_shadow = True
Light.data.contact_shadow_distance = 1.2
Light.rotation_euler = [math.radians(37),math.radians(3),math.radians(40)]
# Set world properties
World = scene.world
World.use_nodes = True
if World.node_tree:
World.node_tree.links.clear()
World.node_tree.nodes.clear()
nodes = World.node_tree.nodes
links = World.node_tree.links
output = nodes.new(type='ShaderNodeOutputWorld')
background1 = nodes.new(type='ShaderNodeBackground')
background1.location = (-400, -120)
background1.inputs[1].default_value = 0.7
background2 = nodes.new(type='ShaderNodeBackground')
background2.location = (-400, 0)
background2.inputs[1].default_value = 1.0
texture = nodes.new(type='ShaderNodeTexEnvironment')
texture.location = (-800, 0)
texture.image = bpy.data.images.load("C:\MyTest\HDRI.exr")
mix = nodes.new(type='ShaderNodeMixShader')
mix.location = (-200, 0)
lightpath = nodes.new(type='ShaderNodeLightPath')
lightpath.location = (-520, 350)
links.new(texture.outputs["Color"], background1.inputs["Color"])
links.new(texture.outputs["Color"], background2.inputs["Color"])
links.new(background1.outputs[0], mix.inputs[1])
links.new(background2.outputs[0], mix.inputs[2])
links.new(lightpath.outputs[0], mix.inputs[0])
links.new(mix.outputs[0], output.inputs[0])

# Create new collections


new_col1 = bpy.data.collections.new(name="Data Bars")
new_col2 = bpy.data.collections.new(name="Data Names")
new_col3 = bpy.data.collections.new(name="Data Numbers")
new_col4 = bpy.data.collections.new(name="Data Pics")
bpy.context.scene.collection.children.link(new_col1)
bpy.context.scene.collection.children.link(new_col2)
bpy.context.scene.collection.children.link(new_col3)
bpy.context.scene.collection.children.link(new_col4)

# Create the bars in a loop

while (data_counter < number_of_data):

data_height = normalized_data_list[data_counter]
data_name = name_list[data_counter]
display_data = str(display_data_list[data_counter])
display_data = str(display_data_list[data_counter])

# Add a cube and set its dimensions


bpy.ops.mesh.primitive_cube_add()
obj = bpy.context.active_object
obj.name = "Bar_" + str(data_counter+1)
obj.dimensions = [1.5,1,data_height]
obj.location = [data_start_position,0,data_height/2]
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)

for other_col in obj.users_collection:


other_col.objects.unlink(obj)
if obj.name not in new_col1.objects:
new_col1.objects.link(obj)

# Set origin to the bottom of the cube


scene.cursor.location = (data_start_position,0,0)
bpy.ops.object.origin_set(type='ORIGIN_CURSOR')

# Add a bevel modifier


bevel_mod = obj.modifiers.new(name="Bevelmodifier", type='BEVEL')
bevel_mod.width = 0.05
bevel_mod.segments = 10

# Animate the height of the cube and also move the empty & the camera
obj.dimensions = [1.5,1,0]
obj.keyframe_insert(data_path="scale", frame = anim_curr_frame)
anim_curr_frame += anim_length
obj.dimensions = [1.5,1,data_height+0.25]
obj.keyframe_insert(data_path="scale", frame = anim_curr_frame)
anim_curr_frame += 10
obj.dimensions = [1.5,1,data_height]
obj.keyframe_insert(data_path="scale", frame = anim_curr_frame)

empty.location = [data_start_position+0.25,0,data_height-0.7]
empty.keyframe_insert(data_path="location", frame = anim_curr_frame +
anim_interval)
camera.location = [data_start_position+0.25,-15,data_height]
camera.keyframe_insert(data_path="location", frame = anim_curr_frame +
anim_interval)

if (data_counter == 0):
empty.location = [data_start_position-0.25,0,data_height-0.7]
empty.keyframe_insert(data_path="location", frame = camera_start_frame)
camera.location = [data_start_position-0.25,-15,data_height]
camera.keyframe_insert(data_path="location", frame = camera_start_frame)
camera.location = [data_start_position-0.25,-25,data_height]
camera.keyframe_insert(data_path="location", frame = anim_start_frame)

if (data_counter < number_of_data - 1):


next_data_height = normalized_data_list[data_counter+1]
anim_curr_frame += anim_interval
empty.location = [data_start_position+distance_bet_data-
0.25,0,next_data_height-0.7]
empty.keyframe_insert(data_path="location", frame = anim_curr_frame +
anim_interval*3/4)
camera.location = [data_start_position+distance_bet_data-0.25,-
15,next_data_height]
camera.keyframe_insert(data_path="location", frame = anim_curr_frame +
anim_interval*3/4)

# Assign the first material created above


obj.data.materials.append(material_1)

# Add the 1st caption


bpy.ops.object.text_add()
ob = bpy.context.active_object
ob.name = "Name_" + str(data_counter+1)
ob.data.body = data_name
ob.data.align_x = "CENTER"
ob.data.align_y = "CENTER"
ob.data.extrude = 0.01
ob.data.font = bpy.data.fonts.load(font_file_path)

ob.location = [data_start_position,-0.7,data_height-1.1]
ob.scale = [0.4, 0.4, 0.1]
ob.rotation_euler = [math.radians(90),0,0]
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)

for other_col in ob.users_collection:


other_col.objects.unlink(ob)
if ob.name not in new_col2.objects:
new_col2.objects.link(ob)

# Assign the second material created above


ob.data.materials.append(material_2)

# Add a shrinkwrap modifier


shrink_mod = ob.modifiers.new(name="Shrinkwrapmodifier", type='SHRINKWRAP')
shrink_mod.offset = 0.001
shrink_mod.target = obj

#bpy.ops.object.convert(target='MESH')

# Add the 2nd caption


bpy.ops.object.text_add()
ob = bpy.context.active_object
ob.name = "Data_" + str(data_counter+1)
ob.data.body = display_data
ob.data.align_x = "CENTER"
ob.data.align_y = "CENTER"
ob.data.extrude = 0.01
ob.data.font = bpy.data.fonts.load(font_file_path)

ob.location = [data_start_position,-0.7,data_height-1.4]
ob.scale = [0.35, 0.35, 0.1]
ob.rotation_euler = [math.radians(90),0,0]
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)

for other_col in ob.users_collection:


other_col.objects.unlink(ob)
if ob.name not in new_col3.objects:
new_col3.objects.link(ob)

# Assign the second material created above


ob.data.materials.append(material_2)

# Add a shrinkwrap modifier


shrink_mod = ob.modifiers.new(name="Shrinkwrapmodifier", type='SHRINKWRAP')
shrink_mod.offset = 0.001
shrink_mod.target = obj

#bpy.ops.object.convert(target='MESH')

# Add the display picture


bpy.ops.mesh.primitive_plane_add()
ob = bpy.context.active_object
ob.name = "Picture_" + str(data_counter+1)

ob.location = [data_start_position,-0.7,data_height-0.5]
ob.dimensions = [1.5, 0.9, 1]
ob.rotation_euler = [math.radians(90),0,0]
bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)

for other_col in ob.users_collection:


other_col.objects.unlink(ob)
if ob.name not in new_col4.objects:
new_col4.objects.link(ob)

# Create a new material and assign


dp_material =
bpy.data.materials.new(name="anim_dp_material"+str(data_counter+1))
dp_material.use_nodes = True
if dp_material.node_tree:
dp_material.node_tree.links.clear()
dp_material.node_tree.nodes.clear()
nodes = dp_material.node_tree.nodes
links = dp_material.node_tree.links
output = nodes.new(type='ShaderNodeOutputMaterial')
shader = nodes.new(type='ShaderNodeBsdfPrincipled')
shader.location = (-300, 0)
shader.inputs[0].default_value = (0.05, 0.05, 0.05, 1)
texture = nodes.new(type='ShaderNodeTexImage')
texture.location = (-700, 0)
texture.image = bpy.data.images.load(display_pic_list[data_counter])
links.new(texture.outputs["Color"], shader.inputs["Base Color"])
links.new(texture.outputs["Alpha"], shader.inputs["Alpha"])
links.new(shader.outputs[0], output.inputs[0])
dp_material.blend_method = 'BLEND'
ob.data.materials.append(dp_material)

# Add a shrinkwrap modifier


shrink_mod = ob.modifiers.new(name="Shrinkwrapmodifier", type='SHRINKWRAP')
shrink_mod.offset = 0.001
shrink_mod.target = obj

#increase the loop counters


data_counter += 1
data_start_position += distance_bet_data

# Add a plane (floor) and set its dimensions


bpy.ops.mesh.primitive_plane_add()
ob = bpy.context.active_object
ob.name = "Floor"
floor_length = distance_bet_data * number_of_data * 100
ob.dimensions = [floor_length,floor_length,1]
ob.location = [distance_bet_data/2 * (number_of_data+1),0,0.01]

# Create a new material for the floor and assign it


material_3 = bpy.data.materials.new(name="anim_material_3")
material_3.use_nodes = True
if material_3.node_tree:
material_3.node_tree.links.clear()
material_3.node_tree.nodes.clear()
nodes = material_3.node_tree.nodes
links = material_3.node_tree.links
output = nodes.new(type='ShaderNodeOutputMaterial')
shader = nodes.new(type='ShaderNodeBsdfPrincipled')
shader.location = (-300, 0)
coord = nodes.new(type='ShaderNodeTexCoord')
coord.location = (-1000, 0)
mapping = nodes.new(type='ShaderNodeMapping')
mapping.location = (-700, 0)
mapping.inputs[3].default_value = (floor_length / 50, floor_length / 100, 1.0)
texture = nodes.new(type='ShaderNodeTexChecker')
texture.location = (-500, 0)
texture.inputs[1].default_value = (0.01, 0.0, 0.0, 1.0)
texture.inputs[2].default_value = (0.3, 0.3, 0.3, 1.0)
texture.inputs[3].default_value = 100
links.new(coord.outputs[0], mapping.inputs[0])
links.new(mapping.outputs[0], texture.inputs[0])
links.new(texture.outputs["Color"], shader.inputs["Base Color"])
links.new(shader.outputs[0], output.inputs[0])
ob.data.materials.append(material_3)

# Clean-up work
# Reset 3D cursor location back to the original
scene.cursor.location.xyz = saved_cursor_loc
context.active_object.select_set(False)

# Set the current frame to frame# 1


scene.frame_set(1)

# Set the scene length


scene.frame_start = 1
scene.frame_end = anim_curr_frame + 50

You might also like