3DCoat Python API
The 3DCoat Python API documentation.
Loading...
Searching...
No Matches
Autoexport.py

The script used for the File->Export->Decimate > Auto UV-Map > Export.

The script used for the File->Export->Decimate > Auto UV-Map > Export

1import coat
2import os
3
4# Set this to True to enable debug output
5_debugmode = False
6
7# the debug print function
8def _print(*args, **kwargs):
9 if _debugmode:
10 print(*args, **kwargs)
11
12# the autoexport class
13class AutoExport:
14 def __init__(self):
15 # Initialize variables with default values
16 # the texture resolution
17 self.TexRes = 4
18 # the desirable reduced polycount
19 self.ReducedPolycount = 40000
20 self.pReducedPolycount = self.ReducedPolycount
21
22 # the default export preset
23 self.expPreset = coat.utils.getEnumValue("EXPPRESETS", "PBR+EXR-displacement",)
24
25 # the default reduction percent
26 self.ReductionPercent = 80
27 self.pReductionPercent = self.ReductionPercent
28 # decumate from all volumes or from the current volume
29 self.DecimateFromAllVolumes = False
30 self.pDecimateFromAllVolumes = False
31
32 vol = coat.Scene.current().Volume()
33 self.CurrentPolycount = vol.getPolycount()
34
35 # export each volume to separate asset
36 self.EachVolumeToSeparateAsset = False
37 # center each asset around its bound box
38 self.CenterEachAssetAroundBoundBox = False
39 # export each asset to its own folder
40 self.EachAssetToOwnFolder = False
41 # drop export result directly to blender
42 self.DropToBlender = False
43 # export mesh optimized for UE5
44 self.UE5_optimized = False
45 # the regular export
46 self.RegularExport = True
47 # skip the filename preffix
48 self.SkipFilenamePreffix = True
49 # the scan depth percent (in percents of the diagonal of the bound box)
50 self.ScanDepthPercent = 2.0
51
52 # the mesh export path
53 self.exportMesh = ""
54 # the textures export path (optional)
55 self.texturesExportPath = ""
56
57 # called each frame
58 def process(self):
59 if self.ReductionPercent != self.pReductionPercent:
60 self.ReducedPolycount = int(self.CurrentPolycount * (100.0 - self.ReductionPercent) / 100.0)
61 self.pReducedPolycount = self.ReducedPolycount
62
63 # Update the reduction percent based on the current polycount
64 if self.CurrentPolycount > 0:
65 self.ReductionPercent = int(100.0 - (self.ReducedPolycount * 100.0) / self.CurrentPolycount)
66 if self.ReductionPercent < 0:
67 self.ReductionPercent = 0
68
69 # Check for changes in DecimateFromAllVolumes and update accordingly
70 if self.DecimateFromAllVolumes != self.pDecimateFromAllVolumes:
71 self.pDecimateFromAllVolumes = self.DecimateFromAllVolumes
72
73 # If DecimateFromAllVolumes is true, calculate the total polycount
74 if self.DecimateFromAllVolumes:
75 self.CurrentPolycount = 0
77 root.iterateVisibleSubtree(lambda e: self._update_polycount(e))
78
79 # Otherwise, use the polycount of the current volume
80 else:
81 vol = coat.Scene.current().Volume()
82 self.CurrentPolycount = vol.getPolycount()
83 self.pReductionPercent = self.ReductionPercent
84 self.pReducedPolycount = self.ReducedPolycount
85 return False
86 def UE5_YouTube():
87 coat.io.exec(coat.ui.getIdTranslation("UE5_ExportTutorial"))
88 def ui(self):
89 self.process()
90 items = []
91 items.append("RegularExport, group1")
92 items.append("DropToBlender, group1")
93 if self.UE5_optimized :
94 items.append("[2 1]")
95
96 items.append("UE5_optimized,group1")
97 if self.UE5_optimized :
98 items.append("UE5_YouTube")
99
100 items.append("---")
101 items.append("EachVolumeToSeparateAsset")
102 items.append("CenterEachAssetAroundBoundBox")
103 if not self.EachVolumeToSeparateAsset :
104 items.append("DecimateFromAllVolumes")
105 else:
106 items.append("EachAssetToOwnFolder")
107 if not self.EachAssetToOwnFolder :
108 items.append("SkipFilenamePreffix")
109
110 # gather all supported export extensions
111 if self.UE5_optimized :
112 items.append("exportMesh,save:*.usdz,*.usd")
113 elif self.DropToBlender :
114 items.append("exportMesh,save:*.fbx")
115 else:
116 items.append("exportMesh,save:" + coat.io.supportedMeshesFormats())
117 items.append("texturesExportPath,folder")
118 items.append("TexRes,[#256x256|#512x512|#1024x1024|#2048x2048|#4096x4096|#8192x8192]")
119
120 if self.RegularExport :
121 items.append("expPreset,[EXPPRESETS|]")
122 items.append("---")
123 items.append("#" + str(self.CurrentPolycount))
124 items.append("ReductionPercent,[0,99]")
125 items.append("[[] 6]")
126 items.append("#{maticon arrow_forward}")
127 items.append("ReducedPolycount")
128 items.append("ScanDepthPercent,[0.01,100]")
129 return items
130
131def RemovePaintObjects():
132 # Get the count of paint objects in the scene
134
135 # Loop through the paint objects and remove them
136 for k in range(n):
138
139def DecimateAutoMapExport():
140 se = AutoExport()
141 if coat.io.fileExists("UserPrefs/CoreAPI/auto_export_py.json"):
142 coat.io.fromJsonFile(se, "UserPrefs/CoreAPI/auto_export_py.json")
143 # show the parameters dialog
144 if coat.dialog().ok().cancel().text("SimpSculptExportHint").caption("SimpSculptExportHintCap").params(se).show() == 1 :
145 if coat.dialog().ok().cancel().text("RunBakeScriptInfo").caption("SimpSculptExportHintCap").dontShowAgainCheckbox().show() <= 1:
146 # save settings
147 coat.io.toJson(se, "UserPrefs/CoreAPI/auto_export_py.json")
148 # hide annoying message about textures locking
149 coat.ui.hideDontShowAgainMessage("AttachTextureHint")
150 # don't fade background, but keep it' state, auto_keep allows to save that value in the current scope,
151 fade = coat.settings.getBool("GreyOutBgInModalDialogs")
152 coat.settings.setBool("GreyOutBgInModalDialogs", False)
153 if se.DropToBlender :
154 # the blender applink toes not support embedding
155 coat.settings.setBool("EmbedTexturesToFBX", False)
156 # set blender's specific normalmapping settings
157 # Generally, you may use just English text itens below
158 # but I decided to use the identifiers instead
159 coat.settings.setString("SoftwarePreset", "Blender")
160 coat.settings.setString("NormalsCalculationMethod", "nm_AngleWeighed")
161 coat.settings.setString("NMAP_EXPORT_TYPE", "NM_MAYA")
162 coat.settings.setString("TriangulationMethod", "DelaunayTriangulation")
163 coat.settings.setString("TBNMethod", "MikkTSpace")
164 if se.UE5_optimized :
165 # set UE5 specific normalmapping settings
166 coat.settings.setString("SoftwarePreset", "UnrealEngine")
167 coat.settings.setString("NormalsCalculationMethod", "nm_AngleWeighed")
168 coat.settings.setString("NMAP_EXPORT_TYPE", "NM_3DMAX")
169 coat.settings.setString("TriangulationMethod", "NaiveTriangulation")
170 coat.settings.setString("TBNMethod", "MikkTSpace")
171 # set the export preset
172 coat.settings.setString("ExportPreset", "PBR+EXR-displacement")
173 se.expPreset = coat.utils.getEnumValue("EXPPRESETS", "USD (PBR Standard)")
174 # create the list of volumes we want to handle
175 volumes = []
176 if se.EachVolumeToSeparateAsset :
177 se.DecimateFromAllVolumes = False
178 coat.Scene.sculptRoot().iterateVisibleSubtree(lambda e: volumes.append(e.Volume()))
179 else :
180 volumes.append(coat.Scene.current().Volume())
181 # go through all volumes we want to operate
182 for i in range (len(volumes)):
183 vol = volumes[i]
184 _print("Processing volume: " + vol.inScene().name())
185 # select the volume
186 if se.EachVolumeToSeparateAsset :
187 vol.inScene().selectOne()
188 ab = coat.boundbox()
189 ab.SetEmpty()
190 if not se.DecimateFromAllVolumes :
191 ab = vol.calcWorldSpaceAABB()
192 else :
193 coat.Scene.sculptRoot().iterateVisibleSubtree(lambda e: ab.AddBounds(e.Volume().calcWorldSpaceAABB()))
194 center = ab.GetCenter()
195 _print("Center: " + str(center))
196 def transform(t):
197 if se.CenterEachAssetAroundBoundBox :
198 if not se.DecimateFromAllVolumes :
199 vol.inScene().transform_single(t)
200 else :
201 coat.Scene.sculptRoot().iterateVisibleSubtree(lambda e: e.transform_single(t))
202 transform(coat.mat4.Translation(-center))
203 d = ab.GetDiagonal() * se.ScanDepthPercent / 100.0
204 cp = vol.getPolycount()
205 _print("Polycount: " + str(cp))
206 if se.DecimateFromAllVolumes:
207 cp = 0
208 def accum(e):
209 cp += e.Volume().getPolycount()
210 return False
211 coat.Scene.sculptRoot().iterateVisibleSubtree(accum)
212 if cp > 0 :
213 # calculating the decimation percent
214 per = 100.0 - se.ReducedPolycount * 100.0 / float(cp)
215 if per > 100.0 :
216 per = 99.0
217 if per < 0.0 :
218 per = 0.0
219 # get the name of the export preset, there is index
220 # of the preset in the se.expPreset
221 preset = coat.utils.getEnumValueByIndex("EXPPRESETS",se.expPreset)
222 RemovePaintObjects()
223 coat.ui.toRoom("Retopo")
224 coat.io.step(4)
225 _print("In room: " + coat.ui.currentRoom())
226 nameCorr = coat.ui.getBoolField("$UseNamesCorrespondence")
227 coat.ui.setBoolValue("$UseNamesCorrespondence", True)
228 coat.ui.cmd("$ClearTM")
229 coat.ui.toRoom("Sculpt")
230 coat.ui.cmd("$DecimateAllToRetopo" if se.DecimateFromAllVolumes else "$DecimateToRetopo", lambda: coat.ui.cmd("$DialogButton#1"))
231 # switch to retopo room
232 coat.ui.toRoom("Retopo")
233 # refresh UI
234 coat.io.step(4)
235 # closest along normal to keep shape
236 coat.ui.cmd("$SnapToNearestAlongNormal")
237 # relax the mesh, snapping rurned ON
238 coat.ui.cmd("$ApplyTSm")
239 # refresh UI
240 coat.io.step(4)
241 # the string that contains decimation percent
242 texSize = str(256 << se.TexRes)
243 # set the baking scan depth, the depth is auto-cacculated and substituted by Coat
244 coat.ui.setOption("BakeScanDepthOut", d)
245 coat.ui.setOption("BakeScanDepthIn", d)
246 # call Bake->Bake with normal map+flat displacement
247 def inbake():
248 # set texture width when the field accessible
249 b1 = coat.ui.cmd("$COMBOBOX_TEXTURE_SIZE_X" + texSize)
250 _print("inbake1: " + str(b1))
251 # set texture height
252 b1 = coat.ui.cmd("$COMBOBOX_TEXTURE_SIZE_Y" + texSize)
253 _print("inbake2: " + str(b1))
254 # press OK at the end
255 coat.ui.cmd("$DialogButton#1")
256 _print("Baking...")
257 coat.ui.cmd("$MergeForDPNM_flatdisp", inbake)
258 coat.ui.setBoolValue("$UseNamesCorrespondence", nameCorr)
259 coat.ui.toRoom("Paint")
260 # fill the mesh name
261 name = se.exportMesh
262 if se.EachVolumeToSeparateAsset :
263 name, ext = os.path.splitext(name)
264 if se.EachAssetToOwnFolder :
265 name += "/"
266 name += vol.inScene().name()
267 name += "/"
268 name += vol.inScene().name()
269 name += ext
270 else :
271 if se.SkipFilenamePreffix :
272 name = os.path.dirname(name)
273 name += "/"
274 else :
275 name += "_"
276 name += vol.inScene().name()
277 name += ext
278 if se.UE5_optimized :
279 name, ext = os.path.splitext(name)
280 if not ("usd" in ext.lower()):
281 name += ".usdz"
282 if se.DropToBlender :
283 # remove file extension
284 name = os.path.splitext(name)[0]
285 name += ".fbx"
286 # call the export dialog
288 coat.ui.cmd("$Blender", lambda: coat.ui.cmd("$DialogButton#1"))
289 else :
290 def inexport() :
291 # set export preset
292 b1 = coat.ui.cmd("$COMBOBOX_" + preset)
293 _print("inexport: " + "$COMBOBOX_" + preset + " : " + str(b1))
294 coat.io.step(2)
295 b1 = coat.ui.setEditBoxValue("$ExportOpt::ExportMeshName", name)
296 _print("inexport: $ExportOpt::ExportMeshName " + name + " : " + str(b1))
297 # enable mesh export
298 b1 = coat.ui.setBoolValue("$ExportOpt::ExportTextures", True)
299 _print("inexport: $ExportOpt::ExportTextures : " + str(b1))
300 # enable textures export
301 b1 = coat.ui.setBoolValue("$ExportOpt::ExportGeometry", True)
302 _print("inexport: $ExportOpt::ExportGeometry : " + str(b1))
303 # set the textures folder path
304 if coat.ui.setEditBoxValue("$ExportOpt::PathForTextures", se.texturesExportPath) :
305 # press Export at the end
306 coat.ui.cmd("$DialogButton#1")
307 _print("inexport: $DialogButton#1 : " + str(b1))
308 _print("Export...")
309 coat.ui.cmd("$EXPORTOBJECT", inexport)
310 # remove all paint objects
311 RemovePaintObjects()
312 coat.ui.toRoom("Retopo")
313 coat.io.step(2)
314 # clear all retopo objects
315 coat.ui.cmd("$ClearTM")
316 coat.ui.toRoom("Sculpt")
317 transform(coat.mat4.Translation(center))
318 # restore the background fade state
319 coat.settings.setBool("GreyOutBgInModalDialogs", fade)
320
321if _debugmode : coat.io.showPythonConsole()
322
323DecimateAutoMapExport()
int PaintObjectsCount()
Get the count of paint objects in scene.
Definition coat.py:2845
SceneElement sculptRoot()
get the root of all sculpt objects
Definition coat.py:2687
RemovePaintObject(int idx)
Remove the paint object.
Definition coat.py:2857
SceneElement current()
returns the current sculpt object
Definition coat.py:2683
Definition coat.py:742
Definition coat.py:3450
fromJsonFile(any obj, str filename)
Restore the object from the json file.
Definition coat.py:3805
bool fileExists(str path)
check if file exists
Definition coat.py:3591
str supportedMeshesFormats()
returns the list of supported images formats
Definition coat.py:3749
showPythonConsole()
Show the python console, clear it and pop up.
Definition coat.py:3786
str toJson(any obj, str filename="")
Store the object to the file or string as json.
Definition coat.py:3800
exec(str command, str arguments=None)
execute command.
Definition coat.py:3719
step(int count=1)
perform rendering cycles
Definition coat.py:3714
mat4 Translation(float X, float Y)
Definition coat.py:554
bool setBool(str ID, bool value)
set the boolean value to the settings
Definition coat.py:2636
bool setString(str ID, str value)
set the string value to the settings
Definition coat.py:2642
bool getBool(str ID)
get the boolen value from the settings
Definition coat.py:2615
bool setEditBoxValue(str id, str value)
set the edit box value
Definition coat.py:3229
bool getBoolField(str id)
Get the bool field from the checkbox in UI.
Definition coat.py:3264
str currentRoom()
get the current room name
Definition coat.py:3274
bool setBoolValue(str id, bool value)
Set the value for the checkbox in UI.
Definition coat.py:3270
str getIdTranslation(str id)
Get the translation for the text identifier.
Definition coat.py:3372
bool cmd(str id, any fn=None)
execute some action in UI as if you pressed on some control
Definition coat.py:3186
toRoom(str name, bool Force=False)
switch to the room
Definition coat.py:3284
setFileForFileDialog(str filename)
Set the file for the next file dialog that will be triggered by user.
Definition coat.py:3259
hideDontShowAgainMessage(str id)
Hides the "Don't show again dialog" for the current session (not forever)
Definition coat.py:3315
bool setOption(str id, str value)
set the value to preferences
Definition coat.py:3307
str getEnumValueByIndex(str enumID, int index)
get the value from the global strings list by index.
Definition coat.py:3882
int getEnumValue(str enumID, str key)
get the integer value that corresponds to the string value from the global strings list.
Definition coat.py:3888