642 lines
21 KiB
Python
642 lines
21 KiB
Python
# THIS FILE IS A PART OF VCStudio
|
|
# PYTHON 3
|
|
|
|
|
|
|
|
import os
|
|
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 oscalls
|
|
from settings import talk
|
|
from settings import fileformats
|
|
from project_manager import pm_project
|
|
|
|
#UI modules
|
|
from UI import UI_elements
|
|
from UI import UI_color
|
|
|
|
# story
|
|
from studio import story
|
|
from studio import analytics
|
|
from studio import history
|
|
from studio import studio_dialogs
|
|
|
|
def link_from_script(win):
|
|
|
|
#########################################################################
|
|
|
|
# This file is here because there are multiple places in the window where
|
|
# I want to link assets based on what's written in the text of the script.
|
|
|
|
# So this file will basically parse the text and add the assets it finds
|
|
# linked to the link list.
|
|
|
|
#########################################################################
|
|
|
|
print("Reading from the scene")
|
|
|
|
# Fist we need to know what scene and shot are we.
|
|
|
|
if win.cur.count("/") > 1:
|
|
tmp = win.cur.replace("/","",1).split("/")
|
|
scene, shotis = tmp[0], tmp[1]
|
|
else:
|
|
scene = win.cur[win.cur.rfind("/")+1:]
|
|
shotis = ""
|
|
|
|
print(scene, shotis)
|
|
|
|
# Now let's read through the scene.
|
|
|
|
for shot in win.story["scenes"][scene]["shots"]:
|
|
|
|
|
|
# Ignore the non shots.
|
|
if shot[0] != "shot_block":
|
|
continue
|
|
|
|
# Ingore all other shots.
|
|
if shot[1] != shotis:
|
|
continue
|
|
|
|
# Let's read the shot
|
|
for asset in shot[-1]:
|
|
# Ignore text
|
|
if asset[0] == "text":
|
|
continue
|
|
|
|
# It's it's a staight up link. Just add it.
|
|
if asset[0] == "link" and "/dev"+asset[1] not in win.current["linking_asset_data"]["assets"]:
|
|
win.current["linking_asset_data"]["assets"].append("/dev"+asset[1])
|
|
|
|
# It's a link in a frase.
|
|
if asset[0] == "frase" and asset[1][0] == "link" and "/dev"+asset[1][1] not in win.current["linking_asset_data"]["assets"]:
|
|
win.current["linking_asset_data"]["assets"].append("/dev"+asset[1][1])
|
|
|
|
|
|
def layer(win, call):
|
|
|
|
# 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,
|
|
40,
|
|
40,
|
|
win.current["w"]-80,
|
|
win.current["h"]-80,
|
|
10)
|
|
|
|
|
|
##############################################################################
|
|
|
|
# This file is configuring and lanunching the linking into blend files. But it's
|
|
# not the only file in the chain of files that is responsible for this operation.
|
|
|
|
# For example the linking it self does studio/bpy_do_linking.py that is running
|
|
# inside blender and communicates with this file for the UI side of things.
|
|
|
|
##############################################################################
|
|
|
|
# Okay
|
|
if not win.current["linking_asset_data"]["fraction"]:
|
|
def do():
|
|
|
|
if not win.current["linking_asset_data"]["process"]:
|
|
|
|
# So this is an apply button. I know it's a little not in the right
|
|
# order. So please read the rest of the file to see what it does.
|
|
|
|
# So now when a person clicks okay. We first need to save the
|
|
# autolink.data.
|
|
|
|
datafile = open(win.project+"/rnd"+win.cur+"/extra/autolink.data", "w")
|
|
for asset in win.current["linking_asset_data"]["assets"]:
|
|
datafile.write("Link : "+asset+"\n")
|
|
|
|
# Also let's write the mode. Because it's important.
|
|
datafile.write("Mode : "+win.current["linking_asset_data"]["mode"]+"\n")
|
|
|
|
datafile.close()
|
|
|
|
# Okay... now that we have the file. We can start the process of linking
|
|
# Which will be done using Blender. So let's get blender's url
|
|
|
|
blenderpath = oscalls.get_current_blender(win)
|
|
|
|
# Now here is the fun part. We gonna start a background process
|
|
# open there blender and will start linking stuff.
|
|
|
|
win.current["linking_asset_data"]["process"] = Popen(['stdbuf', '-o0', blenderpath, \
|
|
"-b", win.project+win.current["linking_asset_data"]["linking_to"], \
|
|
"-P", os.getcwd()+"/studio/bpy_do_linking.py"],\
|
|
stdout=PIPE, universal_newlines=True)
|
|
|
|
win.current["linking_asset_data"]["fraction"] = 0.01
|
|
|
|
# Hell of a command isn't it.
|
|
|
|
UI_elements.roundrect(layer, win,
|
|
win.current["w"]-120,
|
|
win.current["h"]-80,
|
|
40,
|
|
40,
|
|
10,
|
|
button=do,
|
|
icon="ok",
|
|
tip=talk.text("checked"),
|
|
url="asset_link")
|
|
|
|
# CANCEl
|
|
if not win.current["linking_asset_data"]["process"]:
|
|
def do():
|
|
if not win.current["linking_asset_data"]["process"]:
|
|
win.current["calls"][call]["var"] = False
|
|
win.assets = {}
|
|
|
|
UI_elements.roundrect(layer, win,
|
|
win.current["w"]-80,
|
|
win.current["h"]-80,
|
|
40,
|
|
40,
|
|
10,
|
|
button=do,
|
|
icon="cancel",
|
|
tip=talk.text("cancel"),
|
|
url="asset_link")
|
|
|
|
# Short cut ESC
|
|
if 65307 in win.current["keys"] and not win.textactive:
|
|
do()
|
|
|
|
# Now let's prepare the ground for the next part.
|
|
|
|
|
|
|
|
# Before we do anything we need to make sure that there is an autolink.data
|
|
# file in the extra folder of the shot. But since this is a system that is
|
|
# designed to be very simple on the user side. There could not even be the
|
|
# extra folder. So...
|
|
|
|
try:
|
|
os.mkdir(win.project+"/rnd"+win.cur+"/extra")
|
|
except:
|
|
pass
|
|
|
|
# Next thing will be to see if there is a file called autolink.data. And if
|
|
# yes read from it. We are going to load the data into the memory.
|
|
|
|
# So if there is nothing yet loaded let's try loading it from file. Try
|
|
# because there nothing quarantees that the file exists. If file does not
|
|
# exists we gonna populate it with assets from the shot. As marked in the
|
|
# scene the user wrote. But it's going to be a separate function since I
|
|
# want to make a button to do it.
|
|
|
|
if not win.current["linking_asset_data"]["assets"] and not win.current["linking_asset_data"]["read"]:
|
|
|
|
win.current["linking_asset_data"]["read"] = True
|
|
|
|
try:
|
|
datafile = open(win.project+"/rnd"+win.cur+"/extra/autolink.data")
|
|
datafile = datafile.read()
|
|
datafile = datafile.split("\n")
|
|
|
|
for line in datafile:
|
|
if line.startswith("Link : "):
|
|
asset = line[7:]
|
|
win.current["linking_asset_data"]["assets"].append(asset)
|
|
except:
|
|
|
|
# So if it fails to load the file. Probably because it's not created
|
|
# yet. Let's load from the story. What's so bad about it?
|
|
|
|
link_from_script(win)
|
|
|
|
# Now keep in mind that to help the script that runs inside blender all
|
|
# assets have /dev/ in their folder. So to reach /ast/ blend file we will
|
|
# need to parse the link. It's not hard just reminding it here.
|
|
|
|
|
|
# IK everything is all over the place in the file but...
|
|
# If there is a process. We should be able to see what it does.
|
|
# Draw like a litte progress bar and stuff
|
|
|
|
if win.current["linking_asset_data"]["process"]:
|
|
|
|
# Basically during the execution of the linking. I want to read the
|
|
# output and give the user some feedback of what is going on.
|
|
|
|
currentline = win.current["linking_asset_data"]["process"].stdout.readline()[:-1]
|
|
|
|
# In the bpy_do_linking.py file there is an instruction to write
|
|
# out a fraction of a current operation into terminal. Which is
|
|
# piped into the VCStudio and I can read it. So we can use it
|
|
# to draw a progress bar.
|
|
|
|
if currentline.startswith("FRACTION:"):
|
|
try:
|
|
fraction = float(currentline.split(":")[1])
|
|
win.current["linking_asset_data"]["fraction"] = fraction
|
|
except:
|
|
pass
|
|
|
|
if currentline == "FINISHED":
|
|
win.current["linking_asset_data"]["process"] = False
|
|
win.current["linking_asset_data"]["fraction"] = 1
|
|
|
|
|
|
# The prgoress bar:
|
|
if win.current["linking_asset_data"]["fraction"]:
|
|
fraction = win.current["linking_asset_data"]["fraction"]
|
|
|
|
UI_color.set(layer, win, "progress_background")
|
|
UI_elements.roundrect(layer, win,
|
|
70,
|
|
win.current["h"]-75,
|
|
(win.current["w"]-220),
|
|
0,
|
|
7)
|
|
|
|
UI_color.set(layer, win, "progress_active")
|
|
UI_elements.roundrect(layer, win,
|
|
70,
|
|
win.current["h"]-75,
|
|
(win.current["w"]-220)*fraction,
|
|
0,
|
|
7)
|
|
|
|
|
|
# This file will fuel of nightmares. Okay. So while developping it I realzed
|
|
# one little problem. Overrides are kind a still buggy. Tho it's good to
|
|
# have them for future releases. And most of rigs work just fine.
|
|
|
|
# The thing that I want to do is to give the user options. Let's say their
|
|
# rig is broken when trying Library- Overrides but it's okay if you do
|
|
# legacy Proxy thing. So why not give the user the option of doing both.
|
|
|
|
# And also sometimes you just want to link and decide later what you do with
|
|
# your rig.
|
|
|
|
# So I gonna make 3 icons. 3 modes so to speak. LINK, OVERRIDE, PROXY.
|
|
|
|
linkoptions = {
|
|
"link":"link_mode_link",
|
|
"override":"link_mode_overrides", # Icon / Mode : Tooltip
|
|
"proxy":"link_mode_proxy"
|
|
}
|
|
|
|
for num, mode in enumerate(linkoptions):
|
|
|
|
if win.current["linking_asset_data"]["mode"] == mode:
|
|
|
|
UI_color.set(layer, win, "progress_time")
|
|
UI_elements.roundrect(layer, win,
|
|
50+(40*num),
|
|
50,
|
|
40,
|
|
40,
|
|
10)
|
|
|
|
def do():
|
|
win.current["linking_asset_data"]["mode"] = mode
|
|
|
|
UI_elements.roundrect(layer, win,
|
|
50+(40*num),
|
|
50,
|
|
40,
|
|
40,
|
|
10,
|
|
do,
|
|
mode,
|
|
tip=talk.text(linkoptions[mode]))
|
|
|
|
# Let's add a button that adds assets based on what's written in the script.
|
|
|
|
def do():
|
|
link_from_script(win)
|
|
|
|
UI_elements.roundrect(layer, win,
|
|
200,
|
|
50,
|
|
40,
|
|
40,
|
|
10,
|
|
do,
|
|
"scene_new",
|
|
tip=talk.text("link_add_from_scene"))
|
|
|
|
|
|
if not os.path.exists(win.project+win.current["linking_asset_data"]["selected"]+"/autolink.data")\
|
|
or not os.path.exists(win.project+win.current["linking_asset_data"]["selected"].replace("/dev/","/ast/")+".blend"):
|
|
|
|
def do():
|
|
|
|
def after(win, var):
|
|
print(var)
|
|
|
|
studio_dialogs.asset_configure(win, "configuring_asset", after,
|
|
win.current["linking_asset_data"]["selected"].replace("/dev",""))
|
|
|
|
UI_elements.roundrect(layer, win,
|
|
250,
|
|
50,
|
|
40,
|
|
40,
|
|
10,
|
|
button=do,
|
|
icon="link_configure")
|
|
|
|
|
|
|
|
# That's it. Now we are clipping and drawing the assets them selves.
|
|
|
|
UI_elements.roundrect(layer, win,
|
|
50,
|
|
100,
|
|
win.current["w"]-100,
|
|
win.current["h"]-200,
|
|
10,
|
|
fill=False)
|
|
layer.clip()
|
|
|
|
tileX = 70
|
|
current_Y = 0
|
|
|
|
if "asset_link" not in win.scroll:
|
|
win.scroll["asset_link"] = 0
|
|
|
|
#############################
|
|
|
|
# Let's draw the list of assets that the user chose.
|
|
|
|
for num, asset in enumerate(win.current["linking_asset_data"]["assets"]):
|
|
|
|
cur, name = asset.split("/")[2], asset.split("/")[3]
|
|
|
|
if int(current_Y + win.scroll["asset_link"] + 100) in range(0-100, win.current["h"]):
|
|
|
|
# Making the layer
|
|
nodesurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 170, 200)
|
|
node = cairo.Context(nodesurface)
|
|
node.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
|
|
|
|
|
|
UI_elements.roundrect(node, win,
|
|
0,
|
|
0,
|
|
170,
|
|
200,
|
|
10,
|
|
fill=False)
|
|
|
|
node.clip()
|
|
|
|
# Background
|
|
UI_color.set(node, win, "dark_overdrop")
|
|
node.rectangle(0,0,170, 200)
|
|
node.fill()
|
|
|
|
# Banner
|
|
UI_color.set(node, win, "node_asset")
|
|
|
|
# Now the banner will be node_asset color if the asset is configured
|
|
# but it could be not configured. Or not even have the asset blend.
|
|
# so we need to quickly check both. And if any of them missing. Make
|
|
# the banner a node_badfile color.
|
|
|
|
warning = ""
|
|
|
|
if not os.path.exists(win.project+asset+"/autolink.data")\
|
|
or not os.path.exists(win.project+asset.replace("/dev/","/ast/")+".blend"):
|
|
UI_color.set(node, win, "node_badfile")
|
|
|
|
warning = "\n"+talk.text("link_asset_not_configured")
|
|
|
|
# Let's do even cooler. And let's draw a thing arround the asset.
|
|
node.set_line_width(10)
|
|
UI_elements.roundrect(node, win,
|
|
0,
|
|
0,
|
|
170,
|
|
200,
|
|
10,
|
|
fill=False)
|
|
|
|
node.stroke()
|
|
node.set_line_width(2)
|
|
|
|
# Even cooler. Let's put a configuration button right into here
|
|
|
|
|
|
|
|
|
|
|
|
node.rectangle(0,0,170, 20)
|
|
node.fill()
|
|
|
|
# Outputting the layer
|
|
layer.set_source_surface(nodesurface,
|
|
tileX-10,
|
|
current_Y + win.scroll["asset_link"] + 120)
|
|
layer.paint()
|
|
|
|
|
|
# Previes image
|
|
|
|
if os.path.exists(win.project+asset+"/renders/Preview.png"):
|
|
UI_elements.image(layer, win, win.project+asset+"/renders/Preview.png",
|
|
tileX,
|
|
current_Y + win.scroll["asset_link"] + 140,
|
|
150,
|
|
150)
|
|
|
|
elif os.path.exists(win.project+asset+"/renders/Preview.jpg"):
|
|
UI_elements.image(layer, win, win.project+asset+"/renders/Preview.jpg",
|
|
tileX,
|
|
current_Y + win.scroll["asset_link"] + 140,
|
|
150,
|
|
150)
|
|
else:
|
|
UI_elements.image(layer, win, "settings/themes/"+win.settings["Theme"]+"/icons/"+cur+".png",
|
|
tileX+55,
|
|
current_Y + win.scroll["asset_link"] + 150+55,
|
|
150,
|
|
150)
|
|
|
|
|
|
|
|
UI_color.set(layer, win, "text_normal")
|
|
layer.set_font_size(12)
|
|
layer.move_to(tileX,
|
|
current_Y + win.scroll["asset_link"] + 135)
|
|
layer.show_text(name[:22])
|
|
|
|
# Let's make a selection of an asset so you could delete one. Or
|
|
# do other stuff. Maybe.
|
|
|
|
if win.current["linking_asset_data"]["selected"] == asset:
|
|
|
|
layer.set_line_width(4)
|
|
UI_color.set(layer, win, "progress_background")
|
|
UI_elements.roundrect(layer, win,
|
|
tileX-10,
|
|
current_Y + win.scroll["asset_link"] + 120,
|
|
170,
|
|
200,
|
|
10,
|
|
fill=False)
|
|
|
|
layer.stroke()
|
|
layer.set_line_width(2)
|
|
|
|
# And the delete key.
|
|
|
|
if 65535 in win.current["keys"]:
|
|
try:
|
|
win.current["linking_asset_data"]["assets"].remove(asset)
|
|
except:
|
|
pass
|
|
win.current["keys"] = []
|
|
|
|
# Button to activate it
|
|
def do():
|
|
win.current["linking_asset_data"]["selected"] = asset
|
|
|
|
layer.set_line_width(4)
|
|
UI_elements.roundrect(layer, win,
|
|
tileX-10,
|
|
current_Y + win.scroll["asset_link"] + 120,
|
|
170,
|
|
200,
|
|
10,
|
|
button=do,
|
|
tip=talk.text(cur)+": "+name+warning,
|
|
fill=False,
|
|
clip=[
|
|
50,
|
|
100,
|
|
win.current["w"]-100,
|
|
win.current["h"]-200
|
|
],
|
|
url="asset_link")
|
|
|
|
layer.stroke()
|
|
layer.set_line_width(2)
|
|
|
|
tileX += 200
|
|
|
|
if tileX > win.current["w"]-220:
|
|
tileX = 70
|
|
|
|
current_Y += 230
|
|
|
|
|
|
# Now here I want to put the add new one button. So you could add assets
|
|
# to the stuff manually.
|
|
|
|
def do():
|
|
|
|
# Now this is going to be wild. Because I'm going to run a dialog with
|
|
# in a dialog. And I hope that it's going to work.
|
|
|
|
def after(win, var):
|
|
|
|
# Funny that it's actually working.
|
|
# Tho there is a little rub. If you press Cancel there is a bug that
|
|
# makes nothing unusable. so...
|
|
|
|
try:
|
|
del win.current["calls"]["link_add_asset_select"]
|
|
except:
|
|
pass
|
|
|
|
|
|
if var and "/dev"+var not in win.current["linking_asset_data"]["assets"]:
|
|
win.current["linking_asset_data"]["assets"].append("/dev"+var)
|
|
|
|
# So basically I want to add one only if there is not one already.
|
|
# And then select it. So if the use gets which one is added or
|
|
# whether it was already there.
|
|
|
|
win.current["linking_asset_data"]["selected"] = "/dev"+var
|
|
|
|
# And since it could allight with the add button. Let's clear the
|
|
# click
|
|
|
|
win.current["LMB"] = False
|
|
win.previous["LMB"] = False
|
|
|
|
|
|
studio_dialogs.asset_select(win, "link_add_asset_select", after)
|
|
|
|
UI_elements.roundrect(layer, win,
|
|
tileX-10,
|
|
current_Y + win.scroll["asset_link"] + 120,
|
|
170,
|
|
200,
|
|
10,
|
|
button=do,
|
|
url="asset_link")
|
|
|
|
UI_color.set(layer, win, "progress_background")
|
|
UI_elements.roundrect(layer, win,
|
|
tileX-10,
|
|
current_Y + win.scroll["asset_link"] + 120,
|
|
170,
|
|
200,
|
|
10,
|
|
fill=False)
|
|
layer.stroke()
|
|
|
|
UI_elements.image(layer, win,
|
|
"settings/themes/"+win.settings["Theme"]+"/icons/asset_new.png",
|
|
tileX+55,
|
|
current_Y + win.scroll["asset_link"] + 200,
|
|
40, 40)
|
|
|
|
#############################
|
|
|
|
current_Y += 230
|
|
|
|
UI_elements.scroll_area(layer, win, "asset_link",
|
|
50,
|
|
100,
|
|
win.current["w"]-100,
|
|
win.current["h"]-200,
|
|
current_Y,
|
|
bar=True,
|
|
mmb=True,
|
|
url="asset_link",
|
|
strenght=130
|
|
)
|
|
|
|
|
|
|
|
return surface
|