studio_asset_configureLayer.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757
  1. # THIS FILE IS A PART OF VCStudio
  2. # PYTHON 3
  3. import os
  4. import datetime
  5. from subprocess import *
  6. # GTK module ( Graphical interface
  7. import gi
  8. gi.require_version('Gtk', '3.0')
  9. from gi.repository import Gtk
  10. from gi.repository import GLib
  11. from gi.repository import Gdk
  12. import cairo
  13. # Own modules
  14. from settings import settings
  15. from settings import talk
  16. from settings import fileformats
  17. from settings import oscalls
  18. from project_manager import pm_project
  19. #UI modules
  20. from UI import UI_elements
  21. from UI import UI_color
  22. # Studio
  23. from studio import studio_dialogs
  24. from studio import analytics
  25. from studio import story
  26. from studio import history
  27. def layer(win, call):
  28. ##########################################################################
  29. # This file will do the asset configuration for linking assets into anima-
  30. # tion files. The ways it's going to be performed is following:
  31. # 0. User creates the asset. The /dev/ folder for the asset is created.
  32. # 1. User finished the asset and the checklists is now 100%.
  33. # 2. User Clicks on a configure button which launches this UI.
  34. # 3. User choses the main blendfile. It is copied to the /ast/ folder.
  35. # 4. User choses the collection and the rig with in the file. and it's
  36. # saved into autolink.data
  37. # 5. User creates a shot in the script editor. And creates a blend-file.
  38. # 6. Use presses the link button on the blend file. And it loads the assets
  39. # mentioned in the script.
  40. # 7. User presses the okay button and a script is running inside blender
  41. # that will read autolink.data files. And open /ast/ blend files. From
  42. # which it will loaded the right collection and make library override or
  43. # proxy to the rig. Both specified here by the user.
  44. # See:
  45. # studio/studio_shot_linkLayer.py
  46. # studio/bpy_do_linking.py
  47. # studio/studio_assetLayer.py
  48. # The way this file going to work will be like a step by step wizzard.
  49. # This file will be able to be openned from studio_shot_linkLayer dialog so
  50. # there will be 3 different UIs in this file.
  51. # 0. If checklist is not yet 100%. There will be a message to finish the
  52. # asset first.
  53. # 1. When the checklist is 100%. There will UI to select / copy /ast/file
  54. # 2. When that's done. The program going to launch a script to read data
  55. # from the newly created /ast/ file. And will present the user with
  56. # selection of collections and objects from the collections. So to
  57. # what to link and what to proxy.
  58. ##########################################################################
  59. # Making the layer
  60. surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, win.current['w'],
  61. win.current['h'])
  62. layer = cairo.Context(surface)
  63. #text setting
  64. layer.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
  65. UI_color.set(layer, win, "dark_overdrop")
  66. layer.rectangle(
  67. 0,
  68. 0,
  69. win.current["w"],
  70. win.current["h"],
  71. )
  72. layer.fill()
  73. UI_color.set(layer, win, "node_background")
  74. UI_elements.roundrect(layer, win,
  75. win.current["w"]/2-250,
  76. 100,
  77. 500,
  78. win.current["h"]-200,
  79. 10)
  80. # Documentation entry
  81. def do():
  82. def after(win, var):
  83. pass
  84. studio_dialogs.help(win, "help", after, SEARCH=talk.text("documentation_link_assets"))
  85. UI_elements.roundrect(layer, win,
  86. win.current["w"]/2-250,
  87. win.current["h"]-140,
  88. 40,
  89. 40,
  90. 10,
  91. do,
  92. "question")
  93. # Exit button
  94. def do():
  95. win.current["calls"][call]["var"] = False
  96. UI_elements.roundrect(layer, win,
  97. win.current["w"]/2+210,
  98. win.current["h"]-140,
  99. 40,
  100. 40,
  101. 10,
  102. button=do,
  103. icon="cancel",
  104. tip=talk.text("cancel"))
  105. # Okay so before we going to clip everything. Let's make a little widget.
  106. # this will be similar to the thing on top of shots. With the little round
  107. # icons. And a some kind of progress bar with fat points. But this one
  108. # will have only 3 points.
  109. # Let's simplify the process a little bit
  110. x = win.current["w"]/2-250
  111. y = 100
  112. width = 500
  113. height = win.current["h"]-200
  114. # Okay. let's draw the icons.
  115. threeicons = {
  116. "edit_mode":False,
  117. "blender":False,
  118. "link_configure":False
  119. }
  120. for numb, icon in enumerate(threeicons):
  121. UI_elements.image(layer, win,"settings/themes/"\
  122. +win.settings["Theme"]+"/icons/"+icon+".png",
  123. x+(width/4)*numb+(width/8)+40,
  124. y+20,
  125. 40,
  126. 40)
  127. # Before we can do progress bar and or progress dots we need to check
  128. # a few things about the asset.
  129. # First is whether it's done.
  130. threeicons["edit_mode"] = story.get_asset_data(win, win.current["asset_configure"]["asset"])["fraction"] == 1
  131. # Then whether the ast exists.
  132. threeicons["blender"] = os.path.exists(win.project+"/ast"+win.current["asset_configure"]["asset"]+".blend")
  133. # Then whether the autolink.data exists.
  134. threeicons["link_configure"] = os.path.exists(win.project+"/dev"+win.current["asset_configure"]["asset"]+"/autolink.data")
  135. # Now let's manually make the fraction.
  136. fraction = 0.0
  137. if threeicons["edit_mode"]:
  138. fraction = 0.33
  139. if threeicons["blender"]:
  140. fraction = 0.66
  141. if threeicons["link_configure"]:
  142. fraction = 1
  143. UI_color.set(layer, win, "progress_background")
  144. UI_elements.roundrect(layer, win,
  145. x+20,
  146. y+80,
  147. width-40,
  148. 0,
  149. 5)
  150. UI_color.set(layer, win, "progress_active")
  151. UI_elements.roundrect(layer, win,
  152. x+20,
  153. y+80,
  154. (width-40)*fraction,
  155. 0,
  156. 5)
  157. # And the 3 dots
  158. for numb, icon in enumerate(threeicons):
  159. UI_color.set(layer, win, "progress_background")
  160. if threeicons[icon]: # If this folder has any files.
  161. UI_color.set(layer, win, "progress_active")
  162. UI_elements.roundrect(layer, win,
  163. x+(width/4)*numb+(width/8)+50,
  164. y+75,
  165. 0,
  166. 0,
  167. 10)
  168. # Now let's do 3 UIs.
  169. ################## STEP 1: MAKE ASSET ######################
  170. if not threeicons["edit_mode"]:
  171. fraction = story.get_asset_data(win, win.current["asset_configure"]["asset"])["fraction"]
  172. UI_elements.text(layer, win, "asset_configuration_step",
  173. x,
  174. y+150,
  175. width,
  176. 30,
  177. 10,
  178. fill=False,
  179. centered=True,
  180. editable=False)
  181. win.text["asset_configuration_step"]["text"] = talk.text("asset_configuration_step_1")
  182. UI_elements.text(layer, win, "asset_configuration_step1",
  183. x,
  184. y+200,
  185. width,
  186. 30,
  187. 10,
  188. fill=False,
  189. centered=True,
  190. editable=False)
  191. win.text["asset_configuration_step1"]["text"] = str(int(round(fraction*100)))+"%"
  192. UI_color.set(layer, win, "progress_background")
  193. UI_elements.roundrect(layer, win,
  194. x+20,
  195. y+250,
  196. width-40,
  197. 0,
  198. 5)
  199. UI_color.set(layer, win, "progress_active")
  200. UI_elements.roundrect(layer, win,
  201. x+20,
  202. y+250,
  203. (width-40)*fraction,
  204. 0,
  205. 5)
  206. ####################### STEP 2 : COPY BLEND-FILE TO AST ####################
  207. # Setting up the scroll
  208. if "asset_configure" not in win.scroll:
  209. win.scroll["asset_configure"] = 0
  210. current_Y = 310 # The max scroll value
  211. if threeicons["edit_mode"] and not threeicons["blender"]:
  212. UI_elements.text(layer, win, "asset_configuration_step",
  213. x,
  214. y+150,
  215. width,
  216. 30,
  217. 10,
  218. fill=False,
  219. centered=True,
  220. editable=False)
  221. win.text["asset_configuration_step"]["text"] = talk.text("asset_configuration_step_2")
  222. # So if the file is selected I want to have an apply button.
  223. if win.current["asset_configure"]["blend_to_copy"]:
  224. UI_color.set(layer, win, "text_normal")
  225. layer.set_font_size(20)
  226. layer.move_to(x+40,
  227. y+height-20)
  228. layer.show_text(win.current["asset_configure"]["blend_to_copy"])
  229. def do():
  230. copy_from = win.project+"/dev"+win.current["asset_configure"]["asset"]+"/"+win.current["asset_configure"]["blend_to_copy"]
  231. copy_to = win.project+"/ast"+win.current["asset_configure"]["asset"]+".blend"
  232. newname = copy_to[copy_to.rfind("/")+1:]
  233. copy_to = copy_to[:copy_to.rfind("/")]
  234. oscalls.copy_file(
  235. win,
  236. copy_from,
  237. copy_to,
  238. newname)
  239. UI_elements.roundrect(layer, win,
  240. win.current["w"]/2+170,
  241. win.current["h"]-140,
  242. 40,
  243. 40,
  244. 10,
  245. button=do,
  246. icon="ok",
  247. tip=talk.text("checked"))
  248. # Here I want to draw a whole window with blend file selection and stuff.
  249. # so here we go.
  250. # Clipping everything
  251. UI_elements.roundrect(layer, win,
  252. win.current["w"]/2-250,
  253. 300,
  254. 500,
  255. win.current["h"]-460,
  256. 10,
  257. fill=False)
  258. layer.clip()
  259. clip = [
  260. win.current["w"]/2-250,
  261. 300,
  262. 500,
  263. win.current["h"]-460]
  264. # Background
  265. #UI_color.set(layer, win, "dark_overdrop")
  266. #layer.rectangle(x, y,width,height)
  267. #layer.fill()
  268. # Now we draw all the blend files of the file. I know it's redundant but
  269. # I want to be able to configure an asset from the linking window.
  270. tileX = x+70
  271. for filename in sorted(os.listdir(win.project+"/dev"+win.current["asset_configure"]["asset"])):
  272. if filename.endswith(".blend"):
  273. if int(current_Y + win.scroll["asset_configure"]) in range(0-200, height):
  274. # Making the layer
  275. node2surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 170, 200)
  276. node2 = cairo.Context(node2surface)
  277. node2.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
  278. UI_elements.roundrect(node2, win,
  279. 0,
  280. 0,
  281. 170,
  282. 200,
  283. 10,
  284. fill=False)
  285. node2.clip()
  286. # Background
  287. UI_color.set(node2, win, "dark_overdrop")
  288. node2.rectangle(0,0,170, 200)
  289. node2.fill()
  290. # Banner
  291. UI_color.set(node2, win, "node_blendfile")
  292. node2.rectangle(0,0,170, 20)
  293. node2.fill()
  294. # Outputting the layer
  295. layer.set_source_surface(node2surface,
  296. tileX-10,
  297. current_Y + win.scroll["asset_configure"] )
  298. layer.paint()
  299. UI_elements.image(layer, win, win.project+"/dev"+win.current["asset_configure"]["asset"]+"/"+filename,
  300. tileX,
  301. current_Y + win.scroll["asset_configure"] + 30,
  302. 150,
  303. 150)
  304. UI_color.set(layer, win, "text_normal")
  305. layer.set_font_size(12)
  306. layer.move_to(tileX,
  307. current_Y + win.scroll["asset_configure"]+15)
  308. layer.show_text(filename[filename.rfind("/")+1:][:22])
  309. # If selected
  310. layer.set_line_width(4)
  311. if win.current["asset_configure"]["blend_to_copy"] == filename:
  312. UI_color.set(layer, win, "progress_background")
  313. UI_elements.roundrect(layer, win,
  314. tileX-10,
  315. current_Y + win.scroll["asset_configure"],
  316. 170,
  317. 200,
  318. 10,
  319. fill=False)
  320. layer.stroke()
  321. # Button to activate it
  322. def do():
  323. win.current["asset_configure"]["blend_to_copy"] = filename
  324. UI_elements.roundrect(layer, win,
  325. tileX-10,
  326. current_Y + win.scroll["asset_configure"],
  327. 170,
  328. 200,
  329. 10,
  330. button=do,
  331. tip=filename,
  332. fill=False,
  333. clip=clip)
  334. layer.stroke()
  335. layer.set_line_width(2)
  336. tileX += 200
  337. if tileX > 1000:
  338. tileX = x+70
  339. current_Y += 230
  340. ####################### STEP 3 : CONFIGURE AUTOLINK.DATA ####################
  341. if threeicons["edit_mode"] and threeicons["blender"]:
  342. UI_elements.text(layer, win, "asset_configuration_step",
  343. x,
  344. y+150,
  345. width,
  346. 30,
  347. 10,
  348. fill=False,
  349. centered=True,
  350. editable=False)
  351. win.text["asset_configuration_step"]["text"] = talk.text("asset_configuration_step_3")
  352. if win.current["asset_configure"]["apply"]:
  353. def do():
  354. # Here I'm going to actually write stuff to the file. It's not hard.
  355. autolink = open(win.project+"/dev"+win.current["asset_configure"]["asset"]+"/autolink.data", "w")
  356. cols = win.current["asset_configure"]["collections"]
  357. for col in cols:
  358. # If the collection is selected we write it.
  359. if cols[col]["selected"]:
  360. autolink.write("Link : "+col+"\n")
  361. # Also we need to then look throught the collection to
  362. # see if there are any proxy objects. Aramtures...
  363. objs = cols[col]["objects"]
  364. for obj in objs:
  365. # Same thing if it's selected. Then write it to file.
  366. if objs[obj]["selected"]:
  367. autolink.write("Proxy : "+obj+"\n")
  368. autolink.close()
  369. # And a history entry real quick
  370. history.record(win, "/dev"+win.current["asset_configure"]["asset"]+"/autolink.data", "[Updated]")
  371. # And we need to quit the window. So...
  372. win.current["calls"][call]["var"] = False
  373. UI_elements.roundrect(layer, win,
  374. win.current["w"]/2+170,
  375. win.current["h"]-140,
  376. 40,
  377. 40,
  378. 10,
  379. button=do,
  380. icon="ok",
  381. tip=talk.text("checked"))
  382. # For this step I can't just copy stuff from assed layer and pretend that everything
  383. # is good. Because this part requires a whole new script to write and test.
  384. # See:
  385. # studio/bpy_get_blend_content.py
  386. # Now let's actually run this script quickly. Basically we need to get data out of
  387. # the asset blend file. But of course not on every frame. It's a bit too much. So
  388. # here is how we do it.
  389. if not win.current["asset_configure"]["collections"]:
  390. # If in the current data the data about the file still doesn't exists we load
  391. # it.
  392. blenderpath = oscalls.get_current_blender(win)
  393. assetblend = win.project+"/ast"+win.current["asset_configure"]["asset"]+".blend"
  394. checkframes = Popen([blenderpath, "-b", assetblend , "-P",
  395. os.getcwd()+"/studio/bpy_get_blend_content.py"],stdout=PIPE, universal_newlines=True)
  396. checkframes.wait()
  397. checkstring = checkframes.stdout.read()
  398. # Let's also see if the file autolink.data already there. Meaning I can get
  399. # data that is already saved there.
  400. linkcols = []
  401. linkobjs = []
  402. if threeicons["link_configure"]:
  403. autolink = open(win.project+"/dev"+win.current["asset_configure"]["asset"]+"/autolink.data")
  404. autolink = autolink.read()
  405. autolink = autolink.split("\n")
  406. for line in autolink:
  407. if line.startswith("Link : "):
  408. linkcols.append(line[7:])
  409. elif line.startswith("Proxy : "):
  410. linkobjs.append(line[8:])
  411. else:
  412. # If there is no file. I want to start the applying process already:
  413. win.current["asset_configure"]["apply"] = True
  414. # Now let's parse this crazy stuff that we've got from the blender script.
  415. for line in checkstring.split("\n"):
  416. if line.startswith(">>> "):
  417. line = line[4:]
  418. if "<==" in line:
  419. col = line[:line.find(" <==")]
  420. obj = line[line.find(" <==")+5:].split(" <== ")
  421. else:
  422. col = line
  423. obj = False
  424. selected = False
  425. if col in win.current["asset_configure"]["asset"] and not threeicons["link_configure"]:
  426. selected = True
  427. elif col in linkcols:
  428. selected = True
  429. # So we found a collection. Let's write it in
  430. if col not in win.current["asset_configure"]["collections"]:
  431. win.current["asset_configure"]["collections"][col] = {
  432. "selected":selected,
  433. "objects":{}
  434. }
  435. # We also got the object. Let's put it inside the collection.
  436. # But first. If it's an armature we want it to be selected.
  437. if obj:
  438. selected = False
  439. if "Armature" in obj[1] and not threeicons["link_configure"]:
  440. selected = True
  441. elif obj[0] in linkobjs:
  442. selected = True
  443. win.current["asset_configure"]["collections"][col]["objects"][obj[0]] = {
  444. "selected":selected,
  445. "type":obj[1]
  446. }
  447. # Now let's draw the findings to the screen. But first we need
  448. # buttons to see in what catergory are we.
  449. buttons = ["collection", "rig"]
  450. for num, button in enumerate(buttons):
  451. if win.current["asset_configure"]["step3_button"] == button:
  452. UI_color.set(layer, win, "progress_time")
  453. UI_elements.roundrect(layer, win,
  454. x+10+(40*num),
  455. y+200,
  456. 40,
  457. 40,
  458. 10)
  459. def do():
  460. win.current["asset_configure"]["step3_button"] = button
  461. UI_elements.roundrect(layer, win,
  462. x+10+(40*num),
  463. y+200,
  464. 40,
  465. 40,
  466. 10,
  467. do,
  468. button)
  469. # Now we can clip the bastard
  470. UI_elements.roundrect(layer, win,
  471. win.current["w"]/2-250,
  472. 350,
  473. 500,
  474. win.current["h"]-510,
  475. 10,
  476. fill=False)
  477. layer.clip()
  478. clip = [
  479. win.current["w"]/2-250,
  480. 350,
  481. 500,
  482. win.current["h"]-510]
  483. # Background
  484. #UI_color.set(layer, win, "dark_overdrop")
  485. #layer.rectangle(x, y,width,height)
  486. #layer.fill()
  487. # Let's start by drawing the collections first since it's the first thing
  488. # that the user will be doing. Is selecting the collections.
  489. current_Y = current_Y - 50
  490. if win.current["asset_configure"]["step3_button"] == "collection":
  491. for collection in win.current["asset_configure"]["collections"]:
  492. # We are going to draw a little collection icon. And put a text
  493. # with it's name.
  494. UI_elements.image(layer, win,
  495. "settings/themes/"+win.settings["Theme"]+"/icons/collection.png",
  496. x+60, y+current_Y+win.scroll["asset_configure"], 40, 40)
  497. UI_color.set(layer, win, "text_normal")
  498. layer.set_font_size(20)
  499. layer.move_to(x+120,
  500. y+current_Y + win.scroll["asset_configure"]+30)
  501. layer.show_text(collection)
  502. # Now let's make a checkbox button beside each collection.
  503. # Mimicing the Blender UI so to speak.
  504. icon = "unchecked"
  505. if win.current["asset_configure"]["collections"][collection]["selected"]:
  506. icon = "checked"
  507. def do():
  508. win.current["asset_configure"]["collections"][collection]["selected"]\
  509. = not win.current["asset_configure"]["collections"][collection]["selected"]
  510. win.current["asset_configure"]["apply"] = True
  511. UI_elements.roundrect(layer, win,
  512. x+10,
  513. y+current_Y + win.scroll["asset_configure"],
  514. 40,
  515. 40,
  516. 10,
  517. do,
  518. icon)
  519. current_Y = current_Y + 50
  520. else:
  521. # If we are not doing the collection but instead we are selecting the
  522. # rigs. We need to draw different stuff.
  523. for collection in win.current["asset_configure"]["collections"]:
  524. # But we want to draw only objects that are in selected colllections
  525. # so here what we do.
  526. if win.current["asset_configure"]["collections"][collection]["selected"]:
  527. for obj in win.current["asset_configure"]["collections"][collection]["objects"]:
  528. name = obj
  529. obj = win.current["asset_configure"]["collections"][collection]["objects"][obj]
  530. # Now first thing is we need to find an icon to represent
  531. # this object.
  532. icon = "obj"
  533. if "Mesh" in obj["type"]:
  534. icon = "mesh"
  535. elif "Armature" in obj["type"]:
  536. icon = "rig"
  537. elif "Camera" in obj["type"]:
  538. icon = "shot"
  539. UI_elements.image(layer, win,
  540. "settings/themes/"+win.settings["Theme"]+"/icons/"+icon+".png",
  541. x+60, y+current_Y+win.scroll["asset_configure"], 40, 40)
  542. UI_color.set(layer, win, "text_normal")
  543. layer.set_font_size(20)
  544. layer.move_to(x+120,
  545. y+current_Y + win.scroll["asset_configure"]+30)
  546. layer.show_text(name)
  547. icon = "link"
  548. if obj["selected"]:
  549. icon = "override"
  550. def do():
  551. obj["selected"]\
  552. = not obj["selected"]
  553. win.current["asset_configure"]["apply"] = True
  554. UI_elements.roundrect(layer, win,
  555. x+10,
  556. y+current_Y + win.scroll["asset_configure"],
  557. 40,
  558. 40,
  559. 10,
  560. do,
  561. icon)
  562. current_Y = current_Y + 50
  563. current_Y = current_Y - 300
  564. ###########################
  565. UI_elements.scroll_area(layer, win, "asset_configure",
  566. x,
  567. y+300,
  568. width,
  569. height-400,
  570. current_Y,
  571. bar=True,
  572. mmb=True,
  573. url="asset_configure"
  574. )
  575. return surface