-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathSubstancePainter.py
155 lines (116 loc) · 5.46 KB
/
SubstancePainter.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import time
from FranticX.Processes import *
from Deadline.Plugins import *
from Deadline.Scripting import *
# from process_wrapper import Painter
import httplib
import json
import base64
STARTUP_WAITING_TIME = 10
PROCESS_NAME = "Substance Painter Process"
def GetDeadlinePlugin():
return SubstancePainterPlugin()
def CleanupDeadlinePlugin(deadlinePlugin):
deadlinePlugin.Cleanup()
class SubstancePainterPlugin(DeadlinePlugin):
def __init__(self):
self.InitializeProcessCallback += self.InitializeProcess
self.StartJobCallback += self.StartJob
self.RenderTasksCallback += self.RenderTasks
self.EndJobCallback += self.EndJob
def Cleanup(self):
del self.InitializeProcessCallback
del self.StartJobCallback
del self.RenderTasksCallback
del self.EndJobCallback
# noinspection PyAttributeOutsideInit
def InitializeProcess(self):
self.SingleFramesOnly = True
self.PluginType = PluginType.Advanced
def StartJob(self):
process = SubstancePainterProces(self)
self.StartMonitoredManagedProcess(PROCESS_NAME, process)
self.SetStatusMessage("Waiting to start")
time.sleep(STARTUP_WAITING_TIME)
def RenderTasks(self):
port = int(self.GetConfigEntry("SubstancePainterPort"))
painter = Painter(port)
print 'checking connection'
self.SetStatusMessage("Connecting")
painter.checkConnection()
project_file = self.GetPluginInfoEntry("ProjectFile").replace("\\", "/")
preset = self.GetPluginInfoEntry("Preset").replace("\\", "/")
export_path = self.GetPluginInfoEntry("ExportPath").replace("\\", "/")
format_ = self.GetPluginInfoEntry("Format")
stack_paths = json.dumps(self.GetPluginInfoEntryWithDefault("TextureSets", "").split(","))
bit_depth = int(self.GetPluginInfoEntryWithDefault("BitDepth", "8"))
map_info = json.dumps(dict(bitDepth=bit_depth))
self.SetStatusMessage("Opening file")
print 'opening file', project_file
if not project_file.startswith("file:///"):
project_file = "file:///" + project_file
print painter.execScript('alg.project.open("{}")'.format(project_file))
self.SetStatusMessage("Exporting")
command = 'alg.mapexport.exportDocumentMaps("{preset}", "{export_path}", "{format}", {map_info}, {stack_paths});'.format(
preset=preset, export_path=export_path, format=format_, map_info=map_info, stack_paths=stack_paths)
print 'executing command', command
print painter.execScript(command)
def EndJob(self):
self.ShutdownMonitoredManagedProcess(PROCESS_NAME)
class SubstancePainterProces(ManagedProcess):
def __init__(self, deadline_plugin):
self.deadline_plugin = deadline_plugin
self.RenderExecutableCallback += self.RenderExecutable
self.InitializeProcessCallback += self.InitializeProcess
self.RenderArgumentCallback += self.RenderArgument
def Cleanup(self):
del self.InitializeProcessCallback
del self.RenderExecutableCallback
del self.RenderArgumentCallback
def InitializeProcess(self):
pass
def RenderExecutable(self):
return self.deadline_plugin.GetConfigEntry("SubstancePainterRenderExecutable")
def RenderArgument(self):
arguments = " --disable-version-checking"
return arguments
###############################################################################################
# Const data
###############################################################################################
# Json server connection
_PAINTER_ROUTE = '/run.json'
_HEADERS = {'Content-type': 'application/json', 'Accept': 'application/json'}
###############################################################################################
# Exceptions
###############################################################################################
# Generic exception on the Painter class
class PainterError(Exception):
def __init__(self, message):
super(PainterError, self).__init__(message)
class ExecuteScriptError(PainterError):
def __init__(self, data):
super(PainterError, self).__init__('An error occured when executing script: {0}'.format(data))
###############################################################################################
# Remote Substance Painter control
###############################################################################################
class Painter:
def __init__(self, port=60041, host='localhost'):
self._host = host
self._port = port
# Execute a HTTP POST request to the Substance Painter server and send/receive JSON data
def _jsonPostRequest(self, route, body):
connection = httplib.HTTPConnection(self._host, self._port, timeout=3600)
connection.request('POST', route, body, _HEADERS)
response = connection.getresponse()
data = json.loads(response.read().decode('utf-8'))
connection.close()
if type(data) == dict and 'error' in data:
raise ExecuteScriptError(data['error'])
return data
def checkConnection(self):
connection = httplib.HTTPConnection(self._host, self._port)
connection.connect()
# Execute a JavaScript script
def execScript(self, script):
main = base64.b64encode(script.encode('utf-8'))
return self._jsonPostRequest(_PAINTER_ROUTE, ('{"js":"' + main.decode('utf-8') + '"}').encode('utf-8'))