'''
Renders text from a CSV file to texture and applies they to multiple objects.
Use snippets...
No console Python do Blender:
>>> import os, sys; sys.path.append(os.path.dirname(bpy.data.filepath)); import texture_painter
>>> import importlib; importlib.reload(texture_painter); texture_painter.go()
Abrindo o arquivo .csv no console Python do Blender:
>>> import codecs
>>> codecs.open('backers_10.csv', 'r', 'utf-8-sig')
No meu ambiente, foi preciso usar:
>>> codecs.open(os.path.dirname(bpy.data.filepath)+'/backers_10.csv', 'r', 'utf-8-sig')
Outro usuário do curso criou a seguinte solução:
directory = os.path.dirname(bpy.data.filepath)
codecs.open(os.path.join(directory, 'backers.csv'), 'r' , 'utf-8-sig')
Documentos para consulta:
http://nvie.com/posts/iterators-vs-generators/
https://docs.python.org/3/library/csv.html
https://stackoverflow.com/questions/231767/what-does-the-yield-keyword-do-in-python
https://docs.python.org/3/tutorial/errors.html
https://blender.stackexchange.com/questions/5781/how-to-list-all-selected-elements-in-python
https://docs.python.org/3/library/functions.html#enumerate
Dar preferência ao uso de Generators em detrenimento de Iterators sempre que possível. Uma vez que são mais rápidos e consomem nenos da RAM e da CPU.
'''
import codecs, os, sys, bpy, csv
from PIL import Image, ImageFont, ImageDraw
basedir = os.path.dirname(bpy.data.filepath) + '/'
basedir = basedir.replace('\\', '/')
def get_backers(csv_filename):
"""
Ganarator retorna um dicionário contendo os valores de cada linha do arquivo .csv.
Keyword arguments:
csv_filename -- Nome do arquivo .csv de origem (não digitar a extensão)
"""
# Setando o caminho para o arquivo
#basedir = os.path.dirname(bpy.data.filepath) + '\\'
# theFile = csv_filename + '.csv'
# fileToOpen = basedir + theFile
# fileToOpen = fileToOpen.replace('\\', '/')
fileName = basedir + csv_filename + '.csv'
# codecs.open(fileToOpen, 'r', 'utf-8-sig')
# print('Opened: ' + fileToOpen)
#print(codecs.open(fileToOpen, 'r', 'utf-8-sig'))
# O Generator propriamente dito
with codecs.open(fileName, 'r', 'utf-8-sig') as stream:
iterable = csv.reader(stream)
header = next(iterable)
for row in iterable:
backer = dict(zip(header, row))
yield backer
def render_text_to_file(text_to_render, to_filename):
"""
Generator as imagens baseadas nos textos.
Keyword arguments:
text_to_render -- Text for rendering the images
to_filename -- File name to the images
"""
# basedir = os.path.dirname(bpy.data.filepath) + '/'
# basedir = basedir.replace('\\', '/')
# Create a image
text_to_render = str(text_to_render)
text_to_render = text_to_render.replace('{', '').replace('}', '').replace('\'', '')
# largura = int(len(text_to_render)) * 27
# image = Image.new('RGB', (largura,64))
image = Image.new('RGB', (512, 64))
# Create a image draw and font objects
draw = ImageDraw.Draw(image)
fnt = ImageFont.truetype(basedir + 'Turtles.otf', 50)
# Draw text to image
draw.text((0,0), text_to_render, font=fnt, fill=(255,255,255))
# Save image to file
fileName = basedir + 'image_cache/' + str(to_filename) + '.png'
# print("Rendering", text_to_render, "to", fileName)
image.save(fileName)
def throw_invalid_selection():
"""
Throw an exception and ends the execution if none or more than one object is selected
"""
if len(bpy.context.selected_objects) == 0 or len(bpy.context.selected_objects) > 1:
raise Exception("Please select exactly one prototype object")
else:
print("One prototype object found.")
def create_plaque(prototype, offset=(3,0,0)):
"""
Returns a copy of the original object, positioning it as set on the "offset" parameter.
Keyword arguments:
prototype --
offset -- A tuple (x,y,z) thats define the copied object, has its value (3,0,0) as default.
"""
prototype.select = True
# Copying and positioning
bpy.ops.object.duplicate_move(TRANSFORM_OT_translate={"value":tuple(offset)})
# Decelecting the copied object
new_plaque = bpy.context.selected_objects[0]
new_plaque.select = False
return new_plaque
def get_offset(num, rows, spacing):
"""
Returning offset from prototype position.
Keyword arguments:
num -- The number of the object starting from zero
rows -- How many rows before wrapping
spacing -- A tuple of (x,y) spacing between objects
"""
x_offset = (num % rows) * spacing[0] # x-spacing
y_offset = (num // rows) * spacing[1] # y-spacing
return (x_offset, y_offset)
# def read_csv():
# """
# Loop modelo de passagem de argumentos para a função render_text_to_file(). Está usando um arquivo .csv de exemplo.
# """
# # Chamando a função que contem o Generator
# for backer in get_backers('backers_10'): # Nome do arquivo aqui
# # print(backer)
# # render_text_to_file(str(backer), str(backer['Number']))
# text_to_render = backer['Name'] + ', ' + backer['Country']
# render_text_to_file(text_to_render, backer['Number'])
def swap_texture_blenderRender(blender_obj, img_filename):
"""
Método funciona somente com Blender Render
Swaps the first texture, on the first material to supplied.
Keyword arguments:
blender_obj --
img_filename --
"""
# texture_img = plaque.material_slots[0].material.texture_slots[0].texture.image
# texture_img.filepath = '/image_cache/' + str(img_filename) + '.png'
# Create a copy of the material in slot 0
new_material = blender_obj.material_slots[0].material.copy()
# ensure new item has this new material applied
blender_obj.material_slots[0].material = new_material
# Create a new texture
new_texture = new_material.texture_slots[0].texture.copy()
# Substitui a textura original pela cópia
new_material.texture_slots[0].texture = new_texture
# Create a new image by loading an existing one
# img_filename = basedir + 'image_cache/' + str(img_filename) + '.png'
img_filename = '//image_cache\\' + str(img_filename) + '.png' # Could I use -> img_filename =
new_image = bpy.data.images.load(img_filename)
# Apply new image to the texture
new_texture.image = new_image
def swap_texture_cyclesRender(blender_obj, img_filename):
"""
Método funciona somente com Cycles Render
Swaps the first texture, on the first material to supplied.
Keyword arguments:
blender_obj --
img_filename --
"""
'''
bpy.context.selected_objects[0].material_slots[0].material
.node_tree.nodes['Image Texture'].image
'''
# Create a copy of the material in slot 0
new_material = blender_obj.material_slots[0].material.copy()
# ensure new item has this new material applied
blender_obj.material_slots[0].material = new_material
# Create a new image by loading an existing one
# img_filename = basedir + 'image_cache/' + str(img_filename) + '.png'
img_filename = '//image_cache\\' + str(img_filename) + '.png'
new_image = bpy.data.images.load(img_filename)
# Apply new image to the texture
new_material.node_tree.nodes['Image Texture'].image = new_image
def swap_text(blender_obj, backer, index):
"""
Loop modelo de passagem de argumentos para a função render_text_to_file(). Está usando um arquivo .csv de exemplo.
Keyword arguments:
object -- Objeto atualmente selecionado no Blender
backer -- Linha extraída do arquivo .csv
index -- Inteiro que será usado para nomear as imagens (independente dos valores no arquivo csv)
"""
text_to_render = backer['Name'] + ', ' + backer['Country']
# render_text_to_file(text_to_render, backer['Number'])
render_text_to_file(text_to_render, index)
rendEng = bpy.data.scenes["Scene"].render.engine
if rendEng == 'CYCLES':
swap_texture_cyclesRender(blender_obj, index)
else:
swap_texture_blenderRender(blender_obj, index)
def go():
"""
Função inicial para testes. Faz a chamada para a função read_csv() que executará as demais.
"""
print("Texture Painter starting up.")
# read_csv()
throw_invalid_selection()
# pass in selected object
# create_plaque()
# print(get_offset(3, 3, (10,10,0)))
prototype = bpy.context.selected_objects[0]
# Criando um sistema de numeração independente dos valores estarem ou não corretos no arquivo .csv
for num, backer in enumerate(get_backers('backers_10')):
# print(num, backer['Name'] + ', ' + backer['Country'])
if num == 0:
plaque = prototype
else:
# Função retorna uma tupla de 2 valores que serão atribuidos na ordem às variáveis x e y
x, y = get_offset(num, 3, (-2, 2, 0))
plaque = create_plaque(prototype, (x, y, 0))
swap_text(plaque, backer, num)
'''
Exibindo tds os atributos de um objeto:
dir(nome_do_objeto)
Ex:
atual = bpy.context.selected_objects[0]
dir(atual) # Exibirá tds os atributos do(s) objeto(s) selecionados
dentre elas:
atual.dimensions[0] -> Dimenssões no eixo X
atual.dimensions[1] -> Dimenssões no eixo Y
atual.dimensions[2] -> Dimenssões no eixo Z
atual.name -> Exibe o nome do objeto
atual.location -> Exibe a localização do objeto (X,Y,Z)
'''