extapps 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- extapps/__init__.py +57 -0
- extapps/map_compositor/__init__.py +22 -0
- extapps/map_compositor/launcher.py +63 -0
- extapps/map_compositor/map_compositor.ui +248 -0
- extapps/map_compositor/map_compositor_ui.py +133 -0
- extapps/map_compositor/slots.py +443 -0
- extapps/map_converter/__init__.py +23 -0
- extapps/map_converter/launcher.py +39 -0
- extapps/map_converter/map_converter.ui +794 -0
- extapps/map_converter/map_converter_ui.py +374 -0
- extapps/map_converter/slots.py +1146 -0
- extapps/map_packer/__init__.py +23 -0
- extapps/map_packer/launcher.py +28 -0
- extapps/map_packer/map_packer.ui +547 -0
- extapps/map_packer/map_packer_ui.py +260 -0
- extapps/map_packer/slots.py +250 -0
- extapps/mesh_convert/__init__.py +22 -0
- extapps/mesh_convert/launcher.py +29 -0
- extapps/mesh_convert/mesh_convert.ui +226 -0
- extapps/mesh_convert/mesh_convert_ui.py +124 -0
- extapps/mesh_convert/slots.py +288 -0
- extapps/metashape_workflow/__init__.py +38 -0
- extapps/metashape_workflow/_metashape_workflow.py +360 -0
- extapps/metashape_workflow/launcher.py +45 -0
- extapps/metashape_workflow/metashape_workflow.ui +387 -0
- extapps/metashape_workflow/metashape_workflow_ui.py +199 -0
- extapps/metashape_workflow/slots.py +524 -0
- extapps-0.1.0.dist-info/METADATA +67 -0
- extapps-0.1.0.dist-info/RECORD +33 -0
- extapps-0.1.0.dist-info/WHEEL +5 -0
- extapps-0.1.0.dist-info/entry_points.txt +6 -0
- extapps-0.1.0.dist-info/licenses/LICENSE +21 -0
- extapps-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
################################################################################
|
|
4
|
+
## Form generated from reading UI file 'map_packer.ui'
|
|
5
|
+
##
|
|
6
|
+
## Created by: Qt User Interface Compiler version 6.10.1
|
|
7
|
+
##
|
|
8
|
+
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
|
9
|
+
################################################################################
|
|
10
|
+
|
|
11
|
+
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
|
|
12
|
+
QMetaObject, QObject, QPoint, QRect,
|
|
13
|
+
QSize, QTime, QUrl, Qt)
|
|
14
|
+
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
|
|
15
|
+
QFont, QFontDatabase, QGradient, QIcon,
|
|
16
|
+
QImage, QKeySequence, QLinearGradient, QPainter,
|
|
17
|
+
QPalette, QPixmap, QRadialGradient, QTransform)
|
|
18
|
+
from PySide6.QtWidgets import (QApplication, QComboBox, QGridLayout, QGroupBox,
|
|
19
|
+
QLabel, QLayout, QMainWindow, QPushButton,
|
|
20
|
+
QSizePolicy, QSpacerItem, QTabWidget, QVBoxLayout,
|
|
21
|
+
QWidget)
|
|
22
|
+
|
|
23
|
+
from uitk.widgets.comboBox.ComboBox import ComboBox
|
|
24
|
+
from uitk.widgets.footer import Footer
|
|
25
|
+
from uitk.widgets.header import Header
|
|
26
|
+
from uitk.widgets.lineEdit import LineEdit
|
|
27
|
+
|
|
28
|
+
class Ui_QtUi(object):
|
|
29
|
+
def setupUi(self, QtUi):
|
|
30
|
+
if not QtUi.objectName():
|
|
31
|
+
QtUi.setObjectName(u"QtUi")
|
|
32
|
+
QtUi.setEnabled(True)
|
|
33
|
+
QtUi.resize(200, 299)
|
|
34
|
+
QtUi.setTabShape(QTabWidget.Triangular)
|
|
35
|
+
QtUi.setDockNestingEnabled(True)
|
|
36
|
+
QtUi.setDockOptions(QMainWindow.AllowNestedDocks|QMainWindow.AllowTabbedDocks|QMainWindow.AnimatedDocks|QMainWindow.ForceTabbedDocks)
|
|
37
|
+
self.central_widget = QWidget(QtUi)
|
|
38
|
+
self.central_widget.setObjectName(u"central_widget")
|
|
39
|
+
self.central_widget.setMinimumSize(QSize(200, 0))
|
|
40
|
+
self.verticalLayout = QVBoxLayout(self.central_widget)
|
|
41
|
+
self.verticalLayout.setSpacing(2)
|
|
42
|
+
self.verticalLayout.setObjectName(u"verticalLayout")
|
|
43
|
+
self.verticalLayout.setContentsMargins(2, 2, 2, 2)
|
|
44
|
+
self.main_layout = QVBoxLayout()
|
|
45
|
+
self.main_layout.setSpacing(6)
|
|
46
|
+
self.main_layout.setObjectName(u"main_layout")
|
|
47
|
+
self.main_layout.setSizeConstraint(QLayout.SetFixedSize)
|
|
48
|
+
self.header = Header(self.central_widget)
|
|
49
|
+
self.header.setObjectName(u"header")
|
|
50
|
+
sizePolicy = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
|
|
51
|
+
sizePolicy.setHorizontalStretch(0)
|
|
52
|
+
sizePolicy.setVerticalStretch(0)
|
|
53
|
+
sizePolicy.setHeightForWidth(self.header.sizePolicy().hasHeightForWidth())
|
|
54
|
+
self.header.setSizePolicy(sizePolicy)
|
|
55
|
+
self.header.setMinimumSize(QSize(0, 19))
|
|
56
|
+
self.header.setMaximumSize(QSize(999, 19))
|
|
57
|
+
font = QFont()
|
|
58
|
+
font.setBold(True)
|
|
59
|
+
self.header.setFont(font)
|
|
60
|
+
|
|
61
|
+
self.main_layout.addWidget(self.header)
|
|
62
|
+
|
|
63
|
+
self.Formatting = QGroupBox(self.central_widget)
|
|
64
|
+
self.Formatting.setObjectName(u"Formatting")
|
|
65
|
+
self.gridLayout = QGridLayout(self.Formatting)
|
|
66
|
+
self.gridLayout.setObjectName(u"gridLayout")
|
|
67
|
+
self.gridLayout.setContentsMargins(0, 0, 0, 0)
|
|
68
|
+
self.cmbB = ComboBox(self.Formatting)
|
|
69
|
+
self.cmbB.setObjectName(u"cmbB")
|
|
70
|
+
sizePolicy.setHeightForWidth(self.cmbB.sizePolicy().hasHeightForWidth())
|
|
71
|
+
self.cmbB.setSizePolicy(sizePolicy)
|
|
72
|
+
self.cmbB.setMinimumSize(QSize(0, 19))
|
|
73
|
+
self.cmbB.setMaximumSize(QSize(16777215, 19))
|
|
74
|
+
self.cmbB.setMaxVisibleItems(30)
|
|
75
|
+
self.cmbB.setSizeAdjustPolicy(QComboBox.AdjustToContentsOnFirstShow)
|
|
76
|
+
self.cmbB.setFrame(False)
|
|
77
|
+
|
|
78
|
+
self.gridLayout.addWidget(self.cmbB, 2, 1, 1, 1)
|
|
79
|
+
|
|
80
|
+
self.cmbR = ComboBox(self.Formatting)
|
|
81
|
+
self.cmbR.setObjectName(u"cmbR")
|
|
82
|
+
sizePolicy.setHeightForWidth(self.cmbR.sizePolicy().hasHeightForWidth())
|
|
83
|
+
self.cmbR.setSizePolicy(sizePolicy)
|
|
84
|
+
self.cmbR.setMinimumSize(QSize(0, 19))
|
|
85
|
+
self.cmbR.setMaximumSize(QSize(16777215, 19))
|
|
86
|
+
self.cmbR.setMaxVisibleItems(30)
|
|
87
|
+
self.cmbR.setSizeAdjustPolicy(QComboBox.AdjustToContentsOnFirstShow)
|
|
88
|
+
self.cmbR.setFrame(False)
|
|
89
|
+
|
|
90
|
+
self.gridLayout.addWidget(self.cmbR, 0, 1, 1, 1)
|
|
91
|
+
|
|
92
|
+
self.cmbA = ComboBox(self.Formatting)
|
|
93
|
+
self.cmbA.setObjectName(u"cmbA")
|
|
94
|
+
sizePolicy.setHeightForWidth(self.cmbA.sizePolicy().hasHeightForWidth())
|
|
95
|
+
self.cmbA.setSizePolicy(sizePolicy)
|
|
96
|
+
self.cmbA.setMinimumSize(QSize(0, 19))
|
|
97
|
+
self.cmbA.setMaximumSize(QSize(16777215, 19))
|
|
98
|
+
self.cmbA.setMaxVisibleItems(30)
|
|
99
|
+
self.cmbA.setSizeAdjustPolicy(QComboBox.AdjustToContentsOnFirstShow)
|
|
100
|
+
self.cmbA.setFrame(False)
|
|
101
|
+
|
|
102
|
+
self.gridLayout.addWidget(self.cmbA, 3, 1, 1, 1)
|
|
103
|
+
|
|
104
|
+
self.cmbG = ComboBox(self.Formatting)
|
|
105
|
+
self.cmbG.setObjectName(u"cmbG")
|
|
106
|
+
sizePolicy.setHeightForWidth(self.cmbG.sizePolicy().hasHeightForWidth())
|
|
107
|
+
self.cmbG.setSizePolicy(sizePolicy)
|
|
108
|
+
self.cmbG.setMinimumSize(QSize(0, 19))
|
|
109
|
+
self.cmbG.setMaximumSize(QSize(16777215, 19))
|
|
110
|
+
self.cmbG.setMaxVisibleItems(30)
|
|
111
|
+
self.cmbG.setSizeAdjustPolicy(QComboBox.AdjustToContentsOnFirstShow)
|
|
112
|
+
self.cmbG.setFrame(False)
|
|
113
|
+
|
|
114
|
+
self.gridLayout.addWidget(self.cmbG, 1, 1, 1, 1)
|
|
115
|
+
|
|
116
|
+
self.lblR = QLabel(self.Formatting)
|
|
117
|
+
self.lblR.setObjectName(u"lblR")
|
|
118
|
+
self.lblR.setMaximumSize(QSize(18, 16777215))
|
|
119
|
+
self.lblR.setFont(font)
|
|
120
|
+
|
|
121
|
+
self.gridLayout.addWidget(self.lblR, 0, 0, 1, 1)
|
|
122
|
+
|
|
123
|
+
self.lblG = QLabel(self.Formatting)
|
|
124
|
+
self.lblG.setObjectName(u"lblG")
|
|
125
|
+
self.lblG.setMaximumSize(QSize(18, 16777215))
|
|
126
|
+
self.lblG.setFont(font)
|
|
127
|
+
|
|
128
|
+
self.gridLayout.addWidget(self.lblG, 1, 0, 1, 1)
|
|
129
|
+
|
|
130
|
+
self.lblB = QLabel(self.Formatting)
|
|
131
|
+
self.lblB.setObjectName(u"lblB")
|
|
132
|
+
self.lblB.setMaximumSize(QSize(18, 16777215))
|
|
133
|
+
self.lblB.setFont(font)
|
|
134
|
+
|
|
135
|
+
self.gridLayout.addWidget(self.lblB, 2, 0, 1, 1)
|
|
136
|
+
|
|
137
|
+
self.lblA = QLabel(self.Formatting)
|
|
138
|
+
self.lblA.setObjectName(u"lblA")
|
|
139
|
+
self.lblA.setMaximumSize(QSize(18, 16777215))
|
|
140
|
+
self.lblA.setFont(font)
|
|
141
|
+
|
|
142
|
+
self.gridLayout.addWidget(self.lblA, 3, 0, 1, 1)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
self.main_layout.addWidget(self.Formatting)
|
|
146
|
+
|
|
147
|
+
self.groupBox = QGroupBox(self.central_widget)
|
|
148
|
+
self.groupBox.setObjectName(u"groupBox")
|
|
149
|
+
self.verticalLayout_2 = QVBoxLayout(self.groupBox)
|
|
150
|
+
self.verticalLayout_2.setSpacing(1)
|
|
151
|
+
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
|
|
152
|
+
self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
|
|
153
|
+
self.cmbFormat = ComboBox(self.groupBox)
|
|
154
|
+
self.cmbFormat.setObjectName(u"cmbFormat")
|
|
155
|
+
sizePolicy.setHeightForWidth(self.cmbFormat.sizePolicy().hasHeightForWidth())
|
|
156
|
+
self.cmbFormat.setSizePolicy(sizePolicy)
|
|
157
|
+
self.cmbFormat.setMinimumSize(QSize(0, 19))
|
|
158
|
+
self.cmbFormat.setMaximumSize(QSize(16777215, 19))
|
|
159
|
+
self.cmbFormat.setMaxVisibleItems(30)
|
|
160
|
+
self.cmbFormat.setSizeAdjustPolicy(QComboBox.AdjustToContentsOnFirstShow)
|
|
161
|
+
self.cmbFormat.setFrame(False)
|
|
162
|
+
|
|
163
|
+
self.verticalLayout_2.addWidget(self.cmbFormat)
|
|
164
|
+
|
|
165
|
+
self.txtSuffix = LineEdit(self.groupBox)
|
|
166
|
+
self.txtSuffix.setObjectName(u"txtSuffix")
|
|
167
|
+
sizePolicy.setHeightForWidth(self.txtSuffix.sizePolicy().hasHeightForWidth())
|
|
168
|
+
self.txtSuffix.setSizePolicy(sizePolicy)
|
|
169
|
+
self.txtSuffix.setMinimumSize(QSize(0, 19))
|
|
170
|
+
self.txtSuffix.setMaximumSize(QSize(16777215, 19))
|
|
171
|
+
self.txtSuffix.setFrame(False)
|
|
172
|
+
|
|
173
|
+
self.verticalLayout_2.addWidget(self.txtSuffix)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
self.main_layout.addWidget(self.groupBox)
|
|
177
|
+
|
|
178
|
+
self.verticalLayout_3 = QVBoxLayout()
|
|
179
|
+
self.verticalLayout_3.setSpacing(0)
|
|
180
|
+
self.verticalLayout_3.setObjectName(u"verticalLayout_3")
|
|
181
|
+
self.b001 = QPushButton(self.central_widget)
|
|
182
|
+
self.b001.setObjectName(u"b001")
|
|
183
|
+
self.b001.setEnabled(False)
|
|
184
|
+
sizePolicy.setHeightForWidth(self.b001.sizePolicy().hasHeightForWidth())
|
|
185
|
+
self.b001.setSizePolicy(sizePolicy)
|
|
186
|
+
self.b001.setMinimumSize(QSize(0, 19))
|
|
187
|
+
self.b001.setMaximumSize(QSize(16777215, 19))
|
|
188
|
+
|
|
189
|
+
self.verticalLayout_3.addWidget(self.b001)
|
|
190
|
+
|
|
191
|
+
self.b000 = QPushButton(self.central_widget)
|
|
192
|
+
self.b000.setObjectName(u"b000")
|
|
193
|
+
self.b000.setEnabled(True)
|
|
194
|
+
self.b000.setMinimumSize(QSize(0, 30))
|
|
195
|
+
self.b000.setMaximumSize(QSize(16777215, 19))
|
|
196
|
+
|
|
197
|
+
self.verticalLayout_3.addWidget(self.b000)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
self.main_layout.addLayout(self.verticalLayout_3)
|
|
201
|
+
|
|
202
|
+
self.verticalSpacer = QSpacerItem(0, 10, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
|
|
203
|
+
|
|
204
|
+
self.main_layout.addItem(self.verticalSpacer)
|
|
205
|
+
|
|
206
|
+
self.footer = Footer(self.central_widget)
|
|
207
|
+
self.footer.setObjectName(u"footer")
|
|
208
|
+
self.footer.setMinimumSize(QSize(0, 19))
|
|
209
|
+
self.footer.setMaximumSize(QSize(16777215, 19))
|
|
210
|
+
|
|
211
|
+
self.main_layout.addWidget(self.footer)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
self.verticalLayout.addLayout(self.main_layout)
|
|
215
|
+
|
|
216
|
+
QtUi.setCentralWidget(self.central_widget)
|
|
217
|
+
|
|
218
|
+
self.retranslateUi(QtUi)
|
|
219
|
+
|
|
220
|
+
QMetaObject.connectSlotsByName(QtUi)
|
|
221
|
+
# setupUi
|
|
222
|
+
|
|
223
|
+
def retranslateUi(self, QtUi):
|
|
224
|
+
self.header.setText(QCoreApplication.translate("QtUi", u"MAP PACKER", None))
|
|
225
|
+
self.Formatting.setTitle(QCoreApplication.translate("QtUi", u"Channels", None))
|
|
226
|
+
#if QT_CONFIG(tooltip)
|
|
227
|
+
self.cmbB.setToolTip(QCoreApplication.translate("QtUi", u"<html><head/><body><p>Specify the map to pack to the <span style=\" font-weight:600;\">Blue Channel</span>.</p></body></html>", None))
|
|
228
|
+
#endif // QT_CONFIG(tooltip)
|
|
229
|
+
#if QT_CONFIG(tooltip)
|
|
230
|
+
self.cmbR.setToolTip(QCoreApplication.translate("QtUi", u"<html><head/><body><p>Specify the map to pack to the <span style=\" font-weight:600;\">Red Channel</span>.</p></body></html>", None))
|
|
231
|
+
#endif // QT_CONFIG(tooltip)
|
|
232
|
+
#if QT_CONFIG(tooltip)
|
|
233
|
+
self.cmbA.setToolTip(QCoreApplication.translate("QtUi", u"<html><head/><body><p>Specify the map to pack to the <span style=\" font-weight:600;\">Alpha Channel</span>.</p></body></html>", None))
|
|
234
|
+
#endif // QT_CONFIG(tooltip)
|
|
235
|
+
#if QT_CONFIG(tooltip)
|
|
236
|
+
self.cmbG.setToolTip(QCoreApplication.translate("QtUi", u"<html><head/><body><p>Specify the map to pack to the <span style=\" font-weight:600;\">Green Channel</span>.</p></body></html>", None))
|
|
237
|
+
#endif // QT_CONFIG(tooltip)
|
|
238
|
+
self.lblR.setText(QCoreApplication.translate("QtUi", u"R", None))
|
|
239
|
+
self.lblG.setText(QCoreApplication.translate("QtUi", u"G", None))
|
|
240
|
+
self.lblB.setText(QCoreApplication.translate("QtUi", u"B", None))
|
|
241
|
+
self.lblA.setText(QCoreApplication.translate("QtUi", u"A", None))
|
|
242
|
+
self.groupBox.setTitle(QCoreApplication.translate("QtUi", u"Format", None))
|
|
243
|
+
#if QT_CONFIG(tooltip)
|
|
244
|
+
self.cmbFormat.setToolTip(QCoreApplication.translate("QtUi", u"<html><head/><body><p>Specify the map <span style=\" font-weight:600;\">output file format</span>.</p></body></html>", None))
|
|
245
|
+
#endif // QT_CONFIG(tooltip)
|
|
246
|
+
#if QT_CONFIG(tooltip)
|
|
247
|
+
self.txtSuffix.setToolTip(QCoreApplication.translate("QtUi", u"<html><head/><body><p>Specify a suffix for the packed texture's file name.</p></body></html>", None))
|
|
248
|
+
#endif // QT_CONFIG(tooltip)
|
|
249
|
+
self.txtSuffix.setPlaceholderText(QCoreApplication.translate("QtUi", u"Suffix:", None))
|
|
250
|
+
#if QT_CONFIG(tooltip)
|
|
251
|
+
self.b001.setToolTip(QCoreApplication.translate("QtUi", u"Choose the maps to pack and perform the operation.", None))
|
|
252
|
+
#endif // QT_CONFIG(tooltip)
|
|
253
|
+
self.b001.setText(QCoreApplication.translate("QtUi", u"Open Output Dir", None))
|
|
254
|
+
#if QT_CONFIG(tooltip)
|
|
255
|
+
self.b000.setToolTip(QCoreApplication.translate("QtUi", u"Choose the maps to pack and perform the operation.", None))
|
|
256
|
+
#endif // QT_CONFIG(tooltip)
|
|
257
|
+
self.b000.setText(QCoreApplication.translate("QtUi", u"Pack", None))
|
|
258
|
+
pass
|
|
259
|
+
# retranslateUi
|
|
260
|
+
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
# !/usr/bin/python
|
|
2
|
+
# coding=utf-8
|
|
3
|
+
from typing import List, Dict, Optional, Any
|
|
4
|
+
from pythontk.img_utils._img_utils import ImgUtils
|
|
5
|
+
from pythontk.img_utils.map_factory import MapFactory
|
|
6
|
+
from pythontk.file_utils._file_utils import FileUtils
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class MapPackerSlots(ImgUtils):
|
|
10
|
+
channels = ["R", "G", "B", "A"]
|
|
11
|
+
grayscale_types = [
|
|
12
|
+
"None",
|
|
13
|
+
"Metallic",
|
|
14
|
+
"Roughness",
|
|
15
|
+
"Ambient_Occlusion",
|
|
16
|
+
"Smoothness",
|
|
17
|
+
"Opacity",
|
|
18
|
+
"Height",
|
|
19
|
+
"Thickness",
|
|
20
|
+
"Glossiness",
|
|
21
|
+
"Displacement",
|
|
22
|
+
]
|
|
23
|
+
output_formats = ["PNG", "TGA", "JPG", "BMP", "TIFF", "EXR"]
|
|
24
|
+
|
|
25
|
+
PRESET_DIR = "~/.pythontk/presets/map_packer"
|
|
26
|
+
|
|
27
|
+
# Built-in presets defined using human-readable names.
|
|
28
|
+
# Resolved to combo indices at seed time via grayscale_types/output_formats.
|
|
29
|
+
BUILTIN_PRESETS = {
|
|
30
|
+
"ORM (Unreal, glTF)": {
|
|
31
|
+
"R": "Ambient_Occlusion",
|
|
32
|
+
"G": "Roughness",
|
|
33
|
+
"B": "Metallic",
|
|
34
|
+
"A": "None",
|
|
35
|
+
"format": "PNG",
|
|
36
|
+
"suffix": "_ORM",
|
|
37
|
+
},
|
|
38
|
+
"MSAO (HDRP Mask Map)": {
|
|
39
|
+
"R": "Metallic",
|
|
40
|
+
"G": "Ambient_Occlusion",
|
|
41
|
+
"B": "None",
|
|
42
|
+
"A": "Smoothness",
|
|
43
|
+
"format": "PNG",
|
|
44
|
+
"suffix": "_MSAO",
|
|
45
|
+
},
|
|
46
|
+
"Metallic Smoothness (URP)": {
|
|
47
|
+
"R": "Metallic",
|
|
48
|
+
"G": "None",
|
|
49
|
+
"B": "None",
|
|
50
|
+
"A": "Smoothness",
|
|
51
|
+
"format": "PNG",
|
|
52
|
+
"suffix": "_MetallicSmoothness",
|
|
53
|
+
},
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
def __init__(self, switchboard, **kwargs):
|
|
57
|
+
super().__init__()
|
|
58
|
+
|
|
59
|
+
self.sb = switchboard
|
|
60
|
+
self.ui = self.sb.loaded_ui.map_packer
|
|
61
|
+
|
|
62
|
+
self._source_dir = kwargs.get("source_dir", "")
|
|
63
|
+
|
|
64
|
+
self._init_ui_comboboxes()
|
|
65
|
+
self._set_channel_label_colors()
|
|
66
|
+
self.ui.b001.setEnabled(False) # Disable the open output dir button
|
|
67
|
+
|
|
68
|
+
def _set_channel_label_colors(self):
|
|
69
|
+
"""Set background color for each channel label."""
|
|
70
|
+
channel_colors = {
|
|
71
|
+
"R": "#ef9a9a", # Pastel Red
|
|
72
|
+
"G": "#a5d6a7", # Pastel Green
|
|
73
|
+
"B": "#90caf9", # Pastel Blue
|
|
74
|
+
"A": "#bdbdbd", # Pastel Gray
|
|
75
|
+
}
|
|
76
|
+
for c in self.channels:
|
|
77
|
+
lbl = getattr(self.ui, f"lbl{c}", None)
|
|
78
|
+
if lbl:
|
|
79
|
+
lbl.setStyleSheet(
|
|
80
|
+
f"background-color: {channel_colors[c]}; color: white; border-radius: 3px;"
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
def _init_ui_comboboxes(self):
|
|
84
|
+
"""Initialize channel and format comboboxes and connect format change signal."""
|
|
85
|
+
for c in self.channels:
|
|
86
|
+
cmb = getattr(self.ui, f"cmb{c}")
|
|
87
|
+
cmb.clear()
|
|
88
|
+
cmb.addItems(self.grayscale_types)
|
|
89
|
+
cmb.restore_state = True # <-- Enable state restore
|
|
90
|
+
|
|
91
|
+
self.ui.cmbFormat.clear()
|
|
92
|
+
self.ui.cmbFormat.addItems(self.output_formats)
|
|
93
|
+
self.ui.cmbFormat.restore_state = True # <-- Enable state restore
|
|
94
|
+
self.ui.cmbFormat.currentTextChanged.connect(self._on_format_changed)
|
|
95
|
+
self._on_format_changed(self.ui.cmbFormat.currentText())
|
|
96
|
+
|
|
97
|
+
def _on_format_changed(self, fmt: str):
|
|
98
|
+
"""Disable alpha combobox for formats without alpha support."""
|
|
99
|
+
supports_alpha = fmt.upper() in {"PNG", "TGA", "TIFF", "EXR", "BMP"}
|
|
100
|
+
self.ui.cmbA.setEnabled(supports_alpha)
|
|
101
|
+
if not supports_alpha:
|
|
102
|
+
self.ui.cmbA.setCurrentIndex(self.ui.cmbA.findText("None"))
|
|
103
|
+
|
|
104
|
+
def header_init(self, widget):
|
|
105
|
+
"""Configure the header menu with presets for common packed map types."""
|
|
106
|
+
presets = widget.menu.presets
|
|
107
|
+
presets.preset_dir = self.PRESET_DIR
|
|
108
|
+
# Seed built-in presets BEFORE setup so the combo is populated on first launch.
|
|
109
|
+
self._seed_builtin_presets(presets)
|
|
110
|
+
presets.setup(
|
|
111
|
+
preset_dir=self.PRESET_DIR,
|
|
112
|
+
widgets=[
|
|
113
|
+
self.ui.cmbR,
|
|
114
|
+
self.ui.cmbG,
|
|
115
|
+
self.ui.cmbB,
|
|
116
|
+
self.ui.cmbA,
|
|
117
|
+
self.ui.cmbFormat,
|
|
118
|
+
self.ui.txtSuffix,
|
|
119
|
+
],
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
def _seed_builtin_presets(self, preset_mgr):
|
|
123
|
+
"""Write built-in preset JSON files if they don't already exist.
|
|
124
|
+
|
|
125
|
+
Converts human-readable BUILTIN_PRESETS (map-type/format names)
|
|
126
|
+
into the index-based JSON format that PresetManager expects.
|
|
127
|
+
"""
|
|
128
|
+
import json
|
|
129
|
+
|
|
130
|
+
preset_dir = preset_mgr.preset_dir
|
|
131
|
+
for name, preset in self.BUILTIN_PRESETS.items():
|
|
132
|
+
filepath = preset_mgr._preset_path(name)
|
|
133
|
+
if not filepath.exists():
|
|
134
|
+
data = {
|
|
135
|
+
"_meta": {"version": 1},
|
|
136
|
+
"cmbR": self.grayscale_types.index(preset["R"]),
|
|
137
|
+
"cmbG": self.grayscale_types.index(preset["G"]),
|
|
138
|
+
"cmbB": self.grayscale_types.index(preset["B"]),
|
|
139
|
+
"cmbA": self.grayscale_types.index(preset["A"]),
|
|
140
|
+
"cmbFormat": self.output_formats.index(preset["format"]),
|
|
141
|
+
"txtSuffix": preset["suffix"],
|
|
142
|
+
}
|
|
143
|
+
with open(filepath, "w", encoding="utf-8") as f:
|
|
144
|
+
json.dump(data, f, indent=4)
|
|
145
|
+
|
|
146
|
+
@property
|
|
147
|
+
def source_dir(self):
|
|
148
|
+
return self._source_dir
|
|
149
|
+
|
|
150
|
+
@source_dir.setter
|
|
151
|
+
def source_dir(self, value):
|
|
152
|
+
self._source_dir = value
|
|
153
|
+
|
|
154
|
+
def b000(self):
|
|
155
|
+
"""Batch pack up to 4 channels into RGBA maps across texture sets."""
|
|
156
|
+
file_paths = self.sb.file_dialog(
|
|
157
|
+
file_types=[f"*.{ext}" for ext in self.texture_file_types],
|
|
158
|
+
title="Select textures for batch packing (multiple sets allowed):",
|
|
159
|
+
start_dir=self.source_dir,
|
|
160
|
+
allow_multiple=True,
|
|
161
|
+
)
|
|
162
|
+
if not file_paths:
|
|
163
|
+
print("No files selected.")
|
|
164
|
+
self.ui.b001.setEnabled(False)
|
|
165
|
+
return
|
|
166
|
+
|
|
167
|
+
texture_sets = MapFactory.group_textures_by_set(file_paths)
|
|
168
|
+
combos = [
|
|
169
|
+
self.ui.cmbR.currentText(),
|
|
170
|
+
self.ui.cmbG.currentText(),
|
|
171
|
+
self.ui.cmbB.currentText(),
|
|
172
|
+
self.ui.cmbA.currentText(),
|
|
173
|
+
]
|
|
174
|
+
suffix = self.ui.txtSuffix.text().strip() or "_Packed"
|
|
175
|
+
if not suffix.startswith("_"):
|
|
176
|
+
suffix = f"_{suffix}"
|
|
177
|
+
ext = self.ui.cmbFormat.currentText().lower()
|
|
178
|
+
fmt = self.ui.cmbFormat.currentText().upper()
|
|
179
|
+
|
|
180
|
+
success = False
|
|
181
|
+
for base_name, files in texture_sets.items():
|
|
182
|
+
sorted_maps = MapFactory.sort_images_by_type(files)
|
|
183
|
+
assigned = {c: None for c in self.channels}
|
|
184
|
+
available_map_types = {MapFactory.resolve_map_type(f): f for f in files}
|
|
185
|
+
used_files = set()
|
|
186
|
+
|
|
187
|
+
for idx, channel in enumerate(self.channels):
|
|
188
|
+
map_type = combos[idx]
|
|
189
|
+
if map_type == "None":
|
|
190
|
+
continue
|
|
191
|
+
file = next(
|
|
192
|
+
(
|
|
193
|
+
f
|
|
194
|
+
for f in files
|
|
195
|
+
if MapFactory.resolve_map_type(f) == map_type
|
|
196
|
+
and f not in used_files
|
|
197
|
+
),
|
|
198
|
+
None,
|
|
199
|
+
)
|
|
200
|
+
if file:
|
|
201
|
+
assigned[channel] = file
|
|
202
|
+
used_files.add(file)
|
|
203
|
+
continue
|
|
204
|
+
# Try conversion if not found
|
|
205
|
+
converted = self.get_converted_map(map_type, available_map_types)
|
|
206
|
+
if converted is not None:
|
|
207
|
+
assigned[channel] = converted
|
|
208
|
+
continue
|
|
209
|
+
print(
|
|
210
|
+
f"// Required map '{map_type}' for channel {channel} in '{base_name}' not found and cannot be converted. Skipping."
|
|
211
|
+
)
|
|
212
|
+
break # skip this set if not all required maps are present
|
|
213
|
+
|
|
214
|
+
if any(assigned[c] for c in self.channels):
|
|
215
|
+
out_mode = "RGBA" if assigned["A"] else "RGB"
|
|
216
|
+
output_dir = FileUtils.format_path(files[0], "path")
|
|
217
|
+
output_path = f"{output_dir}/{base_name}{suffix}.{ext}"
|
|
218
|
+
self.pack_channels(
|
|
219
|
+
channel_files=assigned,
|
|
220
|
+
output_path=output_path,
|
|
221
|
+
out_mode=out_mode,
|
|
222
|
+
output_format=fmt,
|
|
223
|
+
)
|
|
224
|
+
print(f"// Packed map saved: {output_path}")
|
|
225
|
+
success = True
|
|
226
|
+
|
|
227
|
+
if success:
|
|
228
|
+
self._last_output_dir = FileUtils.format_path(file_paths[0], "path")
|
|
229
|
+
self.source_dir = self._last_output_dir
|
|
230
|
+
self.ui.b001.setEnabled(True)
|
|
231
|
+
else:
|
|
232
|
+
self.ui.b001.setEnabled(False)
|
|
233
|
+
|
|
234
|
+
def b001(self):
|
|
235
|
+
"""Open the last output directory in the system file explorer."""
|
|
236
|
+
import os
|
|
237
|
+
import sys
|
|
238
|
+
|
|
239
|
+
output_dir = getattr(self, "_last_output_dir", None)
|
|
240
|
+
if not output_dir or not os.path.isdir(output_dir):
|
|
241
|
+
print("// No output directory available.")
|
|
242
|
+
return
|
|
243
|
+
if sys.platform.startswith("darwin"):
|
|
244
|
+
os.system(f'open "{output_dir}"')
|
|
245
|
+
elif sys.platform.startswith("win"):
|
|
246
|
+
os.startfile(output_dir)
|
|
247
|
+
elif sys.platform.startswith("linux"):
|
|
248
|
+
os.system(f'xdg-open "{output_dir}"')
|
|
249
|
+
else:
|
|
250
|
+
print("// Unsupported OS for opening directories.")
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# !/usr/bin/python
|
|
2
|
+
# coding=utf-8
|
|
3
|
+
"""Mesh Convert — FBX → glTF / GLB conversion.
|
|
4
|
+
|
|
5
|
+
Engine logic lives in :mod:`pythontk.file_utils.mesh_convert`; this
|
|
6
|
+
package holds only the Switchboard panel and launcher.
|
|
7
|
+
"""
|
|
8
|
+
from pythontk.core_utils.module_resolver import bootstrap_package
|
|
9
|
+
|
|
10
|
+
__package__ = "extapps.mesh_convert"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
DEFAULT_INCLUDE = {
|
|
14
|
+
"launcher": ["MeshConvertUI"],
|
|
15
|
+
"slots": ["MeshConvertSlots"],
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
bootstrap_package(globals(), include=DEFAULT_INCLUDE)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
__all__ = ["MeshConvertUI", "MeshConvertSlots"]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# !/usr/bin/python
|
|
2
|
+
# coding=utf-8
|
|
3
|
+
"""Application shell for the Mesh Convert UI.
|
|
4
|
+
|
|
5
|
+
Engine logic lives in :mod:`pythontk.file_utils.mesh_convert` and slot
|
|
6
|
+
bindings in :mod:`extapps.mesh_convert.slots`; this module only assembles
|
|
7
|
+
the Switchboard-driven UI and provides the script entry point.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class MeshConvertUI:
|
|
12
|
+
def __new__(cls):
|
|
13
|
+
from uitk import Switchboard
|
|
14
|
+
from extapps.mesh_convert.slots import MeshConvertSlots
|
|
15
|
+
|
|
16
|
+
sb = Switchboard(ui_source="mesh_convert.ui", slot_source=MeshConvertSlots)
|
|
17
|
+
ui = sb.loaded_ui.mesh_convert
|
|
18
|
+
|
|
19
|
+
ui.set_attributes(WA_TranslucentBackground=True)
|
|
20
|
+
ui.set_flags(FramelessWindowHint=True)
|
|
21
|
+
ui.style.set(theme="dark", style_class="translucentBgWithBorder")
|
|
22
|
+
ui.header.config_buttons("menu", "minimize", "hide")
|
|
23
|
+
return ui
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# -----------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
if __name__ == "__main__":
|
|
29
|
+
MeshConvertUI().show(pos="screen", app_exec=True)
|