Blender-Pipeline/studio/studio_asset_configureLayer.py

757 lines
26 KiB
Python

# THIS FILE IS A PART OF VCStudio
# PYTHON 3
import os
import datetime
from subprocess import *
# GTK module ( Graphical interface
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import GLib
from gi.repository import Gdk
import cairo
# Own modules
from settings import settings
from settings import talk
from settings import fileformats
from settings import oscalls
from project_manager import pm_project
#UI modules
from UI import UI_elements
from UI import UI_color
# Studio
from studio import studio_dialogs
from studio import analytics
from studio import story
from studio import history
def layer(win, call):
##########################################################################
# This file will do the asset configuration for linking assets into anima-
# tion files. The ways it's going to be performed is following:
# 0. User creates the asset. The /dev/ folder for the asset is created.
# 1. User finished the asset and the checklists is now 100%.
# 2. User Clicks on a configure button which launches this UI.
# 3. User choses the main blendfile. It is copied to the /ast/ folder.
# 4. User choses the collection and the rig with in the file. and it's
# saved into autolink.data
# 5. User creates a shot in the script editor. And creates a blend-file.
# 6. Use presses the link button on the blend file. And it loads the assets
# mentioned in the script.
# 7. User presses the okay button and a script is running inside blender
# that will read autolink.data files. And open /ast/ blend files. From
# which it will loaded the right collection and make library override or
# proxy to the rig. Both specified here by the user.
# See:
# studio/studio_shot_linkLayer.py
# studio/bpy_do_linking.py
# studio/studio_assetLayer.py
# The way this file going to work will be like a step by step wizzard.
# This file will be able to be openned from studio_shot_linkLayer dialog so
# there will be 3 different UIs in this file.
# 0. If checklist is not yet 100%. There will be a message to finish the
# asset first.
# 1. When the checklist is 100%. There will UI to select / copy /ast/file
# 2. When that's done. The program going to launch a script to read data
# from the newly created /ast/ file. And will present the user with
# selection of collections and objects from the collections. So to
# what to link and what to proxy.
##########################################################################
# Making the layer
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, win.current['w'],
win.current['h'])
layer = cairo.Context(surface)
#text setting
layer.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
UI_color.set(layer, win, "dark_overdrop")
layer.rectangle(
0,
0,
win.current["w"],
win.current["h"],
)
layer.fill()
UI_color.set(layer, win, "node_background")
UI_elements.roundrect(layer, win,
win.current["w"]/2-250,
100,
500,
win.current["h"]-200,
10)
# Documentation entry
def do():
def after(win, var):
pass
studio_dialogs.help(win, "help", after, SEARCH=talk.text("documentation_link_assets"))
UI_elements.roundrect(layer, win,
win.current["w"]/2-250,
win.current["h"]-140,
40,
40,
10,
do,
"question")
# Exit button
def do():
win.current["calls"][call]["var"] = False
UI_elements.roundrect(layer, win,
win.current["w"]/2+210,
win.current["h"]-140,
40,
40,
10,
button=do,
icon="cancel",
tip=talk.text("cancel"))
# Okay so before we going to clip everything. Let's make a little widget.
# this will be similar to the thing on top of shots. With the little round
# icons. And a some kind of progress bar with fat points. But this one
# will have only 3 points.
# Let's simplify the process a little bit
x = win.current["w"]/2-250
y = 100
width = 500
height = win.current["h"]-200
# Okay. let's draw the icons.
threeicons = {
"edit_mode":False,
"blender":False,
"link_configure":False
}
for numb, icon in enumerate(threeicons):
UI_elements.image(layer, win,"settings/themes/"\
+win.settings["Theme"]+"/icons/"+icon+".png",
x+(width/4)*numb+(width/8)+40,
y+20,
40,
40)
# Before we can do progress bar and or progress dots we need to check
# a few things about the asset.
# First is whether it's done.
threeicons["edit_mode"] = story.get_asset_data(win, win.current["asset_configure"]["asset"])["fraction"] == 1
# Then whether the ast exists.
threeicons["blender"] = os.path.exists(win.project+"/ast"+win.current["asset_configure"]["asset"]+".blend")
# Then whether the autolink.data exists.
threeicons["link_configure"] = os.path.exists(win.project+"/dev"+win.current["asset_configure"]["asset"]+"/autolink.data")
# Now let's manually make the fraction.
fraction = 0.0
if threeicons["edit_mode"]:
fraction = 0.33
if threeicons["blender"]:
fraction = 0.66
if threeicons["link_configure"]:
fraction = 1
UI_color.set(layer, win, "progress_background")
UI_elements.roundrect(layer, win,
x+20,
y+80,
width-40,
0,
5)
UI_color.set(layer, win, "progress_active")
UI_elements.roundrect(layer, win,
x+20,
y+80,
(width-40)*fraction,
0,
5)
# And the 3 dots
for numb, icon in enumerate(threeicons):
UI_color.set(layer, win, "progress_background")
if threeicons[icon]: # If this folder has any files.
UI_color.set(layer, win, "progress_active")
UI_elements.roundrect(layer, win,
x+(width/4)*numb+(width/8)+50,
y+75,
0,
0,
10)
# Now let's do 3 UIs.
################## STEP 1: MAKE ASSET ######################
if not threeicons["edit_mode"]:
fraction = story.get_asset_data(win, win.current["asset_configure"]["asset"])["fraction"]
UI_elements.text(layer, win, "asset_configuration_step",
x,
y+150,
width,
30,
10,
fill=False,
centered=True,
editable=False)
win.text["asset_configuration_step"]["text"] = talk.text("asset_configuration_step_1")
UI_elements.text(layer, win, "asset_configuration_step1",
x,
y+200,
width,
30,
10,
fill=False,
centered=True,
editable=False)
win.text["asset_configuration_step1"]["text"] = str(int(round(fraction*100)))+"%"
UI_color.set(layer, win, "progress_background")
UI_elements.roundrect(layer, win,
x+20,
y+250,
width-40,
0,
5)
UI_color.set(layer, win, "progress_active")
UI_elements.roundrect(layer, win,
x+20,
y+250,
(width-40)*fraction,
0,
5)
####################### STEP 2 : COPY BLEND-FILE TO AST ####################
# Setting up the scroll
if "asset_configure" not in win.scroll:
win.scroll["asset_configure"] = 0
current_Y = 310 # The max scroll value
if threeicons["edit_mode"] and not threeicons["blender"]:
UI_elements.text(layer, win, "asset_configuration_step",
x,
y+150,
width,
30,
10,
fill=False,
centered=True,
editable=False)
win.text["asset_configuration_step"]["text"] = talk.text("asset_configuration_step_2")
# So if the file is selected I want to have an apply button.
if win.current["asset_configure"]["blend_to_copy"]:
UI_color.set(layer, win, "text_normal")
layer.set_font_size(20)
layer.move_to(x+40,
y+height-20)
layer.show_text(win.current["asset_configure"]["blend_to_copy"])
def do():
copy_from = win.project+"/dev"+win.current["asset_configure"]["asset"]+"/"+win.current["asset_configure"]["blend_to_copy"]
copy_to = win.project+"/ast"+win.current["asset_configure"]["asset"]+".blend"
newname = copy_to[copy_to.rfind("/")+1:]
copy_to = copy_to[:copy_to.rfind("/")]
oscalls.copy_file(
win,
copy_from,
copy_to,
newname)
UI_elements.roundrect(layer, win,
win.current["w"]/2+170,
win.current["h"]-140,
40,
40,
10,
button=do,
icon="ok",
tip=talk.text("checked"))
# Here I want to draw a whole window with blend file selection and stuff.
# so here we go.
# Clipping everything
UI_elements.roundrect(layer, win,
win.current["w"]/2-250,
300,
500,
win.current["h"]-460,
10,
fill=False)
layer.clip()
clip = [
win.current["w"]/2-250,
300,
500,
win.current["h"]-460]
# Background
#UI_color.set(layer, win, "dark_overdrop")
#layer.rectangle(x, y,width,height)
#layer.fill()
# Now we draw all the blend files of the file. I know it's redundant but
# I want to be able to configure an asset from the linking window.
tileX = x+70
for filename in sorted(os.listdir(win.project+"/dev"+win.current["asset_configure"]["asset"])):
if filename.endswith(".blend"):
if int(current_Y + win.scroll["asset_configure"]) in range(0-200, height):
# Making the layer
node2surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 170, 200)
node2 = cairo.Context(node2surface)
node2.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
UI_elements.roundrect(node2, win,
0,
0,
170,
200,
10,
fill=False)
node2.clip()
# Background
UI_color.set(node2, win, "dark_overdrop")
node2.rectangle(0,0,170, 200)
node2.fill()
# Banner
UI_color.set(node2, win, "node_blendfile")
node2.rectangle(0,0,170, 20)
node2.fill()
# Outputting the layer
layer.set_source_surface(node2surface,
tileX-10,
current_Y + win.scroll["asset_configure"] )
layer.paint()
UI_elements.image(layer, win, win.project+"/dev"+win.current["asset_configure"]["asset"]+"/"+filename,
tileX,
current_Y + win.scroll["asset_configure"] + 30,
150,
150)
UI_color.set(layer, win, "text_normal")
layer.set_font_size(12)
layer.move_to(tileX,
current_Y + win.scroll["asset_configure"]+15)
layer.show_text(filename[filename.rfind("/")+1:][:22])
# If selected
layer.set_line_width(4)
if win.current["asset_configure"]["blend_to_copy"] == filename:
UI_color.set(layer, win, "progress_background")
UI_elements.roundrect(layer, win,
tileX-10,
current_Y + win.scroll["asset_configure"],
170,
200,
10,
fill=False)
layer.stroke()
# Button to activate it
def do():
win.current["asset_configure"]["blend_to_copy"] = filename
UI_elements.roundrect(layer, win,
tileX-10,
current_Y + win.scroll["asset_configure"],
170,
200,
10,
button=do,
tip=filename,
fill=False,
clip=clip)
layer.stroke()
layer.set_line_width(2)
tileX += 200
if tileX > 1000:
tileX = x+70
current_Y += 230
####################### STEP 3 : CONFIGURE AUTOLINK.DATA ####################
if threeicons["edit_mode"] and threeicons["blender"]:
UI_elements.text(layer, win, "asset_configuration_step",
x,
y+150,
width,
30,
10,
fill=False,
centered=True,
editable=False)
win.text["asset_configuration_step"]["text"] = talk.text("asset_configuration_step_3")
if win.current["asset_configure"]["apply"]:
def do():
# Here I'm going to actually write stuff to the file. It's not hard.
autolink = open(win.project+"/dev"+win.current["asset_configure"]["asset"]+"/autolink.data", "w")
cols = win.current["asset_configure"]["collections"]
for col in cols:
# If the collection is selected we write it.
if cols[col]["selected"]:
autolink.write("Link : "+col+"\n")
# Also we need to then look throught the collection to
# see if there are any proxy objects. Aramtures...
objs = cols[col]["objects"]
for obj in objs:
# Same thing if it's selected. Then write it to file.
if objs[obj]["selected"]:
autolink.write("Proxy : "+obj+"\n")
autolink.close()
# And a history entry real quick
history.record(win, "/dev"+win.current["asset_configure"]["asset"]+"/autolink.data", "[Updated]")
# And we need to quit the window. So...
win.current["calls"][call]["var"] = False
UI_elements.roundrect(layer, win,
win.current["w"]/2+170,
win.current["h"]-140,
40,
40,
10,
button=do,
icon="ok",
tip=talk.text("checked"))
# For this step I can't just copy stuff from assed layer and pretend that everything
# is good. Because this part requires a whole new script to write and test.
# See:
# studio/bpy_get_blend_content.py
# Now let's actually run this script quickly. Basically we need to get data out of
# the asset blend file. But of course not on every frame. It's a bit too much. So
# here is how we do it.
if not win.current["asset_configure"]["collections"]:
# If in the current data the data about the file still doesn't exists we load
# it.
blenderpath = oscalls.get_current_blender(win)
assetblend = win.project+"/ast"+win.current["asset_configure"]["asset"]+".blend"
checkframes = Popen([blenderpath, "-b", assetblend , "-P",
os.getcwd()+"/studio/bpy_get_blend_content.py"],stdout=PIPE, universal_newlines=True)
checkframes.wait()
checkstring = checkframes.stdout.read()
# Let's also see if the file autolink.data already there. Meaning I can get
# data that is already saved there.
linkcols = []
linkobjs = []
if threeicons["link_configure"]:
autolink = open(win.project+"/dev"+win.current["asset_configure"]["asset"]+"/autolink.data")
autolink = autolink.read()
autolink = autolink.split("\n")
for line in autolink:
if line.startswith("Link : "):
linkcols.append(line[7:])
elif line.startswith("Proxy : "):
linkobjs.append(line[8:])
else:
# If there is no file. I want to start the applying process already:
win.current["asset_configure"]["apply"] = True
# Now let's parse this crazy stuff that we've got from the blender script.
for line in checkstring.split("\n"):
if line.startswith(">>> "):
line = line[4:]
if "<==" in line:
col = line[:line.find(" <==")]
obj = line[line.find(" <==")+5:].split(" <== ")
else:
col = line
obj = False
selected = False
if col in win.current["asset_configure"]["asset"] and not threeicons["link_configure"]:
selected = True
elif col in linkcols:
selected = True
# So we found a collection. Let's write it in
if col not in win.current["asset_configure"]["collections"]:
win.current["asset_configure"]["collections"][col] = {
"selected":selected,
"objects":{}
}
# We also got the object. Let's put it inside the collection.
# But first. If it's an armature we want it to be selected.
if obj:
selected = False
if "Armature" in obj[1] and not threeicons["link_configure"]:
selected = True
elif obj[0] in linkobjs:
selected = True
win.current["asset_configure"]["collections"][col]["objects"][obj[0]] = {
"selected":selected,
"type":obj[1]
}
# Now let's draw the findings to the screen. But first we need
# buttons to see in what catergory are we.
buttons = ["collection", "rig"]
for num, button in enumerate(buttons):
if win.current["asset_configure"]["step3_button"] == button:
UI_color.set(layer, win, "progress_time")
UI_elements.roundrect(layer, win,
x+10+(40*num),
y+200,
40,
40,
10)
def do():
win.current["asset_configure"]["step3_button"] = button
UI_elements.roundrect(layer, win,
x+10+(40*num),
y+200,
40,
40,
10,
do,
button)
# Now we can clip the bastard
UI_elements.roundrect(layer, win,
win.current["w"]/2-250,
350,
500,
win.current["h"]-510,
10,
fill=False)
layer.clip()
clip = [
win.current["w"]/2-250,
350,
500,
win.current["h"]-510]
# Background
#UI_color.set(layer, win, "dark_overdrop")
#layer.rectangle(x, y,width,height)
#layer.fill()
# Let's start by drawing the collections first since it's the first thing
# that the user will be doing. Is selecting the collections.
current_Y = current_Y - 50
if win.current["asset_configure"]["step3_button"] == "collection":
for collection in win.current["asset_configure"]["collections"]:
# We are going to draw a little collection icon. And put a text
# with it's name.
UI_elements.image(layer, win,
"settings/themes/"+win.settings["Theme"]+"/icons/collection.png",
x+60, y+current_Y+win.scroll["asset_configure"], 40, 40)
UI_color.set(layer, win, "text_normal")
layer.set_font_size(20)
layer.move_to(x+120,
y+current_Y + win.scroll["asset_configure"]+30)
layer.show_text(collection)
# Now let's make a checkbox button beside each collection.
# Mimicing the Blender UI so to speak.
icon = "unchecked"
if win.current["asset_configure"]["collections"][collection]["selected"]:
icon = "checked"
def do():
win.current["asset_configure"]["collections"][collection]["selected"]\
= not win.current["asset_configure"]["collections"][collection]["selected"]
win.current["asset_configure"]["apply"] = True
UI_elements.roundrect(layer, win,
x+10,
y+current_Y + win.scroll["asset_configure"],
40,
40,
10,
do,
icon)
current_Y = current_Y + 50
else:
# If we are not doing the collection but instead we are selecting the
# rigs. We need to draw different stuff.
for collection in win.current["asset_configure"]["collections"]:
# But we want to draw only objects that are in selected colllections
# so here what we do.
if win.current["asset_configure"]["collections"][collection]["selected"]:
for obj in win.current["asset_configure"]["collections"][collection]["objects"]:
name = obj
obj = win.current["asset_configure"]["collections"][collection]["objects"][obj]
# Now first thing is we need to find an icon to represent
# this object.
icon = "obj"
if "Mesh" in obj["type"]:
icon = "mesh"
elif "Armature" in obj["type"]:
icon = "rig"
elif "Camera" in obj["type"]:
icon = "shot"
UI_elements.image(layer, win,
"settings/themes/"+win.settings["Theme"]+"/icons/"+icon+".png",
x+60, y+current_Y+win.scroll["asset_configure"], 40, 40)
UI_color.set(layer, win, "text_normal")
layer.set_font_size(20)
layer.move_to(x+120,
y+current_Y + win.scroll["asset_configure"]+30)
layer.show_text(name)
icon = "link"
if obj["selected"]:
icon = "override"
def do():
obj["selected"]\
= not obj["selected"]
win.current["asset_configure"]["apply"] = True
UI_elements.roundrect(layer, win,
x+10,
y+current_Y + win.scroll["asset_configure"],
40,
40,
10,
do,
icon)
current_Y = current_Y + 50
current_Y = current_Y - 300
###########################
UI_elements.scroll_area(layer, win, "asset_configure",
x,
y+300,
width,
height-400,
current_Y,
bar=True,
mmb=True,
url="asset_configure"
)
return surface