Compare commits
1 Commits
1.1
...
bd885f1b99
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd885f1b99 |
Binary file not shown.
@@ -2238,6 +2238,10 @@
|
|||||||
('_pyi_rth_utils',
|
('_pyi_rth_utils',
|
||||||
'C:\\Users\\Niklas\\AppData\\Local\\Packages\\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\\LocalCache\\local-packages\\Python310\\site-packages\\PyInstaller\\fake-modules\\_pyi_rth_utils\\__init__.py',
|
'C:\\Users\\Niklas\\AppData\\Local\\Packages\\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\\LocalCache\\local-packages\\Python310\\site-packages\\PyInstaller\\fake-modules\\_pyi_rth_utils\\__init__.py',
|
||||||
'PYMODULE'),
|
'PYMODULE'),
|
||||||
|
('_py_abc',
|
||||||
|
'C:\\Program '
|
||||||
|
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\_py_abc.py',
|
||||||
|
'PYMODULE'),
|
||||||
('stringprep',
|
('stringprep',
|
||||||
'C:\\Program '
|
'C:\\Program '
|
||||||
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\stringprep.py',
|
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\stringprep.py',
|
||||||
@@ -2246,10 +2250,6 @@
|
|||||||
'C:\\Program '
|
'C:\\Program '
|
||||||
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\tracemalloc.py',
|
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\tracemalloc.py',
|
||||||
'PYMODULE'),
|
'PYMODULE'),
|
||||||
('_py_abc',
|
|
||||||
'C:\\Program '
|
|
||||||
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\_py_abc.py',
|
|
||||||
'PYMODULE'),
|
|
||||||
('typing',
|
('typing',
|
||||||
'C:\\Program '
|
'C:\\Program '
|
||||||
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\typing.py',
|
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\typing.py',
|
||||||
@@ -3232,35 +3232,35 @@
|
|||||||
'E:\\Arma Reforger '
|
'E:\\Arma Reforger '
|
||||||
'Work\\1960-utils\\Texturing\\MergeTextures2\\build\\merge_textures\\base_library.zip',
|
'Work\\1960-utils\\Texturing\\MergeTextures2\\build\\merge_textures\\base_library.zip',
|
||||||
'DATA'),
|
'DATA'),
|
||||||
('setuptools-65.5.0.dist-info\\LICENSE',
|
('setuptools-65.5.0.dist-info\\INSTALLER',
|
||||||
'C:\\Program '
|
'C:\\Program '
|
||||||
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\LICENSE',
|
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\INSTALLER',
|
||||||
'DATA'),
|
|
||||||
('setuptools-65.5.0.dist-info\\WHEEL',
|
|
||||||
'C:\\Program '
|
|
||||||
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\WHEEL',
|
|
||||||
'DATA'),
|
|
||||||
('setuptools-65.5.0.dist-info\\top_level.txt',
|
|
||||||
'C:\\Program '
|
|
||||||
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\top_level.txt',
|
|
||||||
'DATA'),
|
|
||||||
('setuptools-65.5.0.dist-info\\METADATA',
|
|
||||||
'C:\\Program '
|
|
||||||
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\METADATA',
|
|
||||||
'DATA'),
|
'DATA'),
|
||||||
('setuptools-65.5.0.dist-info\\REQUESTED',
|
('setuptools-65.5.0.dist-info\\REQUESTED',
|
||||||
'C:\\Program '
|
'C:\\Program '
|
||||||
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\REQUESTED',
|
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\REQUESTED',
|
||||||
'DATA'),
|
'DATA'),
|
||||||
|
('setuptools-65.5.0.dist-info\\METADATA',
|
||||||
|
'C:\\Program '
|
||||||
|
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\METADATA',
|
||||||
|
'DATA'),
|
||||||
|
('setuptools-65.5.0.dist-info\\LICENSE',
|
||||||
|
'C:\\Program '
|
||||||
|
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\LICENSE',
|
||||||
|
'DATA'),
|
||||||
('setuptools-65.5.0.dist-info\\entry_points.txt',
|
('setuptools-65.5.0.dist-info\\entry_points.txt',
|
||||||
'C:\\Program '
|
'C:\\Program '
|
||||||
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\entry_points.txt',
|
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\entry_points.txt',
|
||||||
'DATA'),
|
'DATA'),
|
||||||
('setuptools-65.5.0.dist-info\\INSTALLER',
|
|
||||||
'C:\\Program '
|
|
||||||
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\INSTALLER',
|
|
||||||
'DATA'),
|
|
||||||
('setuptools-65.5.0.dist-info\\RECORD',
|
('setuptools-65.5.0.dist-info\\RECORD',
|
||||||
'C:\\Program '
|
'C:\\Program '
|
||||||
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\RECORD',
|
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\RECORD',
|
||||||
|
'DATA'),
|
||||||
|
('setuptools-65.5.0.dist-info\\top_level.txt',
|
||||||
|
'C:\\Program '
|
||||||
|
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\top_level.txt',
|
||||||
|
'DATA'),
|
||||||
|
('setuptools-65.5.0.dist-info\\WHEEL',
|
||||||
|
'C:\\Program '
|
||||||
|
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\WHEEL',
|
||||||
'DATA')])
|
'DATA')])
|
||||||
|
|||||||
@@ -277,42 +277,42 @@
|
|||||||
'E:\\Arma Reforger '
|
'E:\\Arma Reforger '
|
||||||
'Work\\1960-utils\\Texturing\\MergeTextures2\\build\\merge_textures\\base_library.zip',
|
'Work\\1960-utils\\Texturing\\MergeTextures2\\build\\merge_textures\\base_library.zip',
|
||||||
'DATA'),
|
'DATA'),
|
||||||
('setuptools-65.5.0.dist-info\\LICENSE',
|
('setuptools-65.5.0.dist-info\\INSTALLER',
|
||||||
'C:\\Program '
|
'C:\\Program '
|
||||||
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\LICENSE',
|
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\INSTALLER',
|
||||||
'DATA'),
|
|
||||||
('setuptools-65.5.0.dist-info\\WHEEL',
|
|
||||||
'C:\\Program '
|
|
||||||
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\WHEEL',
|
|
||||||
'DATA'),
|
|
||||||
('setuptools-65.5.0.dist-info\\top_level.txt',
|
|
||||||
'C:\\Program '
|
|
||||||
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\top_level.txt',
|
|
||||||
'DATA'),
|
|
||||||
('setuptools-65.5.0.dist-info\\METADATA',
|
|
||||||
'C:\\Program '
|
|
||||||
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\METADATA',
|
|
||||||
'DATA'),
|
'DATA'),
|
||||||
('setuptools-65.5.0.dist-info\\REQUESTED',
|
('setuptools-65.5.0.dist-info\\REQUESTED',
|
||||||
'C:\\Program '
|
'C:\\Program '
|
||||||
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\REQUESTED',
|
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\REQUESTED',
|
||||||
'DATA'),
|
'DATA'),
|
||||||
|
('setuptools-65.5.0.dist-info\\METADATA',
|
||||||
|
'C:\\Program '
|
||||||
|
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\METADATA',
|
||||||
|
'DATA'),
|
||||||
|
('setuptools-65.5.0.dist-info\\LICENSE',
|
||||||
|
'C:\\Program '
|
||||||
|
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\LICENSE',
|
||||||
|
'DATA'),
|
||||||
('setuptools-65.5.0.dist-info\\entry_points.txt',
|
('setuptools-65.5.0.dist-info\\entry_points.txt',
|
||||||
'C:\\Program '
|
'C:\\Program '
|
||||||
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\entry_points.txt',
|
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\entry_points.txt',
|
||||||
'DATA'),
|
'DATA'),
|
||||||
('setuptools-65.5.0.dist-info\\INSTALLER',
|
|
||||||
'C:\\Program '
|
|
||||||
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\INSTALLER',
|
|
||||||
'DATA'),
|
|
||||||
('setuptools-65.5.0.dist-info\\RECORD',
|
('setuptools-65.5.0.dist-info\\RECORD',
|
||||||
'C:\\Program '
|
'C:\\Program '
|
||||||
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\RECORD',
|
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\RECORD',
|
||||||
|
'DATA'),
|
||||||
|
('setuptools-65.5.0.dist-info\\top_level.txt',
|
||||||
|
'C:\\Program '
|
||||||
|
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\top_level.txt',
|
||||||
|
'DATA'),
|
||||||
|
('setuptools-65.5.0.dist-info\\WHEEL',
|
||||||
|
'C:\\Program '
|
||||||
|
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\WHEEL',
|
||||||
'DATA')],
|
'DATA')],
|
||||||
[],
|
[],
|
||||||
False,
|
False,
|
||||||
False,
|
False,
|
||||||
1743968683,
|
1759164736,
|
||||||
[('run.exe',
|
[('run.exe',
|
||||||
'C:\\Users\\Niklas\\AppData\\Local\\Packages\\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\\LocalCache\\local-packages\\Python310\\site-packages\\PyInstaller\\bootloader\\Windows-64bit-intel\\run.exe',
|
'C:\\Users\\Niklas\\AppData\\Local\\Packages\\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\\LocalCache\\local-packages\\Python310\\site-packages\\PyInstaller\\bootloader\\Windows-64bit-intel\\run.exe',
|
||||||
'EXECUTABLE')],
|
'EXECUTABLE')],
|
||||||
|
|||||||
@@ -253,37 +253,37 @@
|
|||||||
'E:\\Arma Reforger '
|
'E:\\Arma Reforger '
|
||||||
'Work\\1960-utils\\Texturing\\MergeTextures2\\build\\merge_textures\\base_library.zip',
|
'Work\\1960-utils\\Texturing\\MergeTextures2\\build\\merge_textures\\base_library.zip',
|
||||||
'DATA'),
|
'DATA'),
|
||||||
('setuptools-65.5.0.dist-info\\LICENSE',
|
('setuptools-65.5.0.dist-info\\INSTALLER',
|
||||||
'C:\\Program '
|
'C:\\Program '
|
||||||
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\LICENSE',
|
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\INSTALLER',
|
||||||
'DATA'),
|
|
||||||
('setuptools-65.5.0.dist-info\\WHEEL',
|
|
||||||
'C:\\Program '
|
|
||||||
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\WHEEL',
|
|
||||||
'DATA'),
|
|
||||||
('setuptools-65.5.0.dist-info\\top_level.txt',
|
|
||||||
'C:\\Program '
|
|
||||||
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\top_level.txt',
|
|
||||||
'DATA'),
|
|
||||||
('setuptools-65.5.0.dist-info\\METADATA',
|
|
||||||
'C:\\Program '
|
|
||||||
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\METADATA',
|
|
||||||
'DATA'),
|
'DATA'),
|
||||||
('setuptools-65.5.0.dist-info\\REQUESTED',
|
('setuptools-65.5.0.dist-info\\REQUESTED',
|
||||||
'C:\\Program '
|
'C:\\Program '
|
||||||
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\REQUESTED',
|
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\REQUESTED',
|
||||||
'DATA'),
|
'DATA'),
|
||||||
|
('setuptools-65.5.0.dist-info\\METADATA',
|
||||||
|
'C:\\Program '
|
||||||
|
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\METADATA',
|
||||||
|
'DATA'),
|
||||||
|
('setuptools-65.5.0.dist-info\\LICENSE',
|
||||||
|
'C:\\Program '
|
||||||
|
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\LICENSE',
|
||||||
|
'DATA'),
|
||||||
('setuptools-65.5.0.dist-info\\entry_points.txt',
|
('setuptools-65.5.0.dist-info\\entry_points.txt',
|
||||||
'C:\\Program '
|
'C:\\Program '
|
||||||
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\entry_points.txt',
|
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\entry_points.txt',
|
||||||
'DATA'),
|
'DATA'),
|
||||||
('setuptools-65.5.0.dist-info\\INSTALLER',
|
|
||||||
'C:\\Program '
|
|
||||||
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\INSTALLER',
|
|
||||||
'DATA'),
|
|
||||||
('setuptools-65.5.0.dist-info\\RECORD',
|
('setuptools-65.5.0.dist-info\\RECORD',
|
||||||
'C:\\Program '
|
'C:\\Program '
|
||||||
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\RECORD',
|
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\RECORD',
|
||||||
|
'DATA'),
|
||||||
|
('setuptools-65.5.0.dist-info\\top_level.txt',
|
||||||
|
'C:\\Program '
|
||||||
|
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\top_level.txt',
|
||||||
|
'DATA'),
|
||||||
|
('setuptools-65.5.0.dist-info\\WHEEL',
|
||||||
|
'C:\\Program '
|
||||||
|
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\site-packages\\setuptools-65.5.0.dist-info\\WHEEL',
|
||||||
'DATA')],
|
'DATA')],
|
||||||
'python310.dll',
|
'python310.dll',
|
||||||
False,
|
False,
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
BIN
Texturing/MergeTextures2/dist/merge_textures.exe
vendored
BIN
Texturing/MergeTextures2/dist/merge_textures.exe
vendored
Binary file not shown.
@@ -5,81 +5,107 @@ import time
|
|||||||
import concurrent.futures
|
import concurrent.futures
|
||||||
from typing import Dict, List, Optional, Tuple
|
from typing import Dict, List, Optional, Tuple
|
||||||
|
|
||||||
# Define suffix lists for BaseColor, Normal, RMA/ORM
|
# Define suffix lists for BaseColor, Normal, packed maps, and single-channel maps
|
||||||
BASECOLOR_SUFFIXES = ['_alb.', '_albedo.', '_bc.', '_basecolor.', '_b.']
|
BASECOLOR_SUFFIXES = ['_alb.', '_albedo.', '_bc.', '_basecolor.', '_b.']
|
||||||
NORMAL_SUFFIXES = ['_nrm.', '_normal.', '_n.']
|
NORMAL_SUFFIXES = ['_nrm.', '_normal.', '_n.']
|
||||||
RMA_SUFFIXES = ['_rma.']
|
RMA_SUFFIXES = ['_rma.']
|
||||||
ORM_SUFFIXES = ['_orm.']
|
ORM_SUFFIXES = ['_orm.']
|
||||||
|
ROUGHNESS_SUFFIXES = ['_roughness.', '_rough.', '_rgh.']
|
||||||
|
METALLIC_SUFFIXES = ['_metallic.', '_metalness.', '_metal.', '_met.']
|
||||||
|
AO_SUFFIXES = ['_ao.', '_ambientocclusion.', '_occlusion.']
|
||||||
EMISSIVE_SUFFIXES = ['_emissive.']
|
EMISSIVE_SUFFIXES = ['_emissive.']
|
||||||
OPACITY_SUFFIXES = ['_opacity.']
|
OPACITY_SUFFIXES = ['_opacity.']
|
||||||
MASK_SUFFIXES = ['_mask.','_m.']
|
MASK_SUFFIXES = ['_mask.', '_m.']
|
||||||
|
|
||||||
def detect_texture_type(filename):
|
def detect_texture_type(filename):
|
||||||
""" Detects the type of texture based on its suffix """
|
"""Detect the type of texture based on naming suffixes."""
|
||||||
if any(suffix in filename.lower() for suffix in BASECOLOR_SUFFIXES):
|
lowered = filename.lower()
|
||||||
|
if any(suffix in lowered for suffix in BASECOLOR_SUFFIXES):
|
||||||
return 'BaseColor'
|
return 'BaseColor'
|
||||||
elif any(suffix in filename.lower() for suffix in NORMAL_SUFFIXES):
|
if any(suffix in lowered for suffix in NORMAL_SUFFIXES):
|
||||||
return 'Normal'
|
return 'Normal'
|
||||||
elif any(suffix in filename.lower() for suffix in RMA_SUFFIXES):
|
if any(suffix in lowered for suffix in RMA_SUFFIXES):
|
||||||
return 'RMA'
|
return 'RMA'
|
||||||
elif any(suffix in filename.lower() for suffix in ORM_SUFFIXES):
|
if any(suffix in lowered for suffix in ORM_SUFFIXES):
|
||||||
return 'ORM'
|
return 'ORM'
|
||||||
elif any(suffix in filename.lower() for suffix in EMISSIVE_SUFFIXES):
|
if any(suffix in lowered for suffix in ROUGHNESS_SUFFIXES):
|
||||||
|
return 'Roughness'
|
||||||
|
if any(suffix in lowered for suffix in METALLIC_SUFFIXES):
|
||||||
|
return 'Metallic'
|
||||||
|
if any(suffix in lowered for suffix in AO_SUFFIXES):
|
||||||
|
return 'AO'
|
||||||
|
if any(suffix in lowered for suffix in EMISSIVE_SUFFIXES):
|
||||||
return 'Emissive'
|
return 'Emissive'
|
||||||
elif any(suffix in filename.lower() for suffix in OPACITY_SUFFIXES):
|
if any(suffix in lowered for suffix in OPACITY_SUFFIXES):
|
||||||
return 'Opacity'
|
return 'Opacity'
|
||||||
elif any(suffix in filename.lower() for suffix in MASK_SUFFIXES):
|
if any(suffix in lowered for suffix in MASK_SUFFIXES):
|
||||||
return 'Mask'
|
return 'Mask'
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_material_name(filename):
|
def get_material_name(filename):
|
||||||
""" Strips the 'T_' or 'TX_' prefix but keeps the suffix for texture type detection.
|
"""Strip the T_/TX_ prefix while keeping the suffix for detection and return the material name."""
|
||||||
Returns the full material name without the suffix for output file naming. """
|
|
||||||
base_name = os.path.basename(filename)
|
base_name = os.path.basename(filename)
|
||||||
|
|
||||||
# Remove the 'T_' or 'TX_' prefix
|
|
||||||
if base_name.startswith('T_'):
|
if base_name.startswith('T_'):
|
||||||
base_name = base_name[2:]
|
base_name = base_name[2:]
|
||||||
elif base_name.startswith('TX_'):
|
elif base_name.startswith('TX_'):
|
||||||
base_name = base_name[3:]
|
base_name = base_name[3:]
|
||||||
|
|
||||||
# Return the base_name without the suffix for output naming
|
return base_name.rsplit('_', 1)[0]
|
||||||
return base_name.rsplit('_', 1)[0] # Split only at the last underscore
|
|
||||||
|
|
||||||
def convert_single_material(material_data: Tuple[str, Dict[str, str]], output_folder: str) -> Tuple[bool, str]:
|
def convert_single_material(material_data: Tuple[str, Dict[str, str]], output_folder: str) -> Tuple[bool, str]:
|
||||||
"""Convert a single material to BCR/NMO format"""
|
"""Convert a single material to BCR/NMO format."""
|
||||||
material, files = material_data
|
material, files = material_data
|
||||||
basecolor_file = files.get('BaseColor')
|
basecolor_file = files.get('BaseColor')
|
||||||
normal_file = files.get('Normal')
|
normal_file = files.get('Normal')
|
||||||
rma_file = files.get('RMA')
|
rma_file = files.get('RMA')
|
||||||
orm_file = files.get('ORM')
|
orm_file = files.get('ORM')
|
||||||
|
roughness_file = files.get('Roughness')
|
||||||
|
metallic_file = files.get('Metallic')
|
||||||
|
ao_file = files.get('AO')
|
||||||
emissive_file = files.get('Emissive')
|
emissive_file = files.get('Emissive')
|
||||||
opacity_file = files.get('Opacity')
|
opacity_file = files.get('Opacity')
|
||||||
mask_file = files.get('Mask')
|
mask_file = files.get('Mask')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if convert_to_bcr_nmo(material, basecolor_file, normal_file, rma_file, orm_file, emissive_file, opacity_file, mask_file, output_folder):
|
success, warnings = convert_to_bcr_nmo(
|
||||||
|
material,
|
||||||
|
basecolor_file,
|
||||||
|
normal_file,
|
||||||
|
rma_file,
|
||||||
|
orm_file,
|
||||||
|
roughness_file,
|
||||||
|
metallic_file,
|
||||||
|
ao_file,
|
||||||
|
emissive_file,
|
||||||
|
opacity_file,
|
||||||
|
mask_file,
|
||||||
|
output_folder,
|
||||||
|
)
|
||||||
|
if success:
|
||||||
|
if warnings:
|
||||||
|
return True, f"{material}: Successfully converted. Warning(s): {' '.join(warnings)}"
|
||||||
return True, f"{material}: Successfully converted."
|
return True, f"{material}: Successfully converted."
|
||||||
else:
|
|
||||||
return False, f"Skipping {material}: input file sizes do not match."
|
return False, f"Skipping {material}: input file sizes do not match."
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return False, f"Error processing {material}: {str(e)}"
|
return False, f"Error processing {material}: {str(e)}"
|
||||||
|
|
||||||
def process_textures(input_files):
|
def process_textures(input_files):
|
||||||
""" Main function to process all textures in a folder and convert to BCR/NMO """
|
"""Main function to process all textures in a folder and convert to BCR/NMO."""
|
||||||
textures = {}
|
textures: Dict[str, Dict[str, str]] = {}
|
||||||
|
|
||||||
# Group files by material name
|
|
||||||
for filepath in input_files:
|
for filepath in input_files:
|
||||||
filename = os.path.basename(filepath)
|
filename = os.path.basename(filepath)
|
||||||
material_name = get_material_name(filename)
|
material_name = get_material_name(filename)
|
||||||
texture_type = detect_texture_type(filename)
|
texture_type = detect_texture_type(filename)
|
||||||
|
|
||||||
|
if texture_type is None:
|
||||||
|
continue
|
||||||
|
|
||||||
if material_name not in textures:
|
if material_name not in textures:
|
||||||
textures[material_name] = {}
|
textures[material_name] = {}
|
||||||
textures[material_name][texture_type] = filepath
|
textures[material_name][texture_type] = filepath
|
||||||
|
|
||||||
# Create a merged folder in the same directory as the input
|
|
||||||
base_path = os.path.dirname(input_files[0])
|
base_path = os.path.dirname(input_files[0])
|
||||||
output_folder = os.path.join(base_path, 'merged')
|
output_folder = os.path.join(base_path, 'merged')
|
||||||
os.makedirs(output_folder, exist_ok=True)
|
os.makedirs(output_folder, exist_ok=True)
|
||||||
@@ -87,8 +113,7 @@ def process_textures(input_files):
|
|||||||
material_count = len(textures)
|
material_count = len(textures)
|
||||||
print(f"Detected {material_count} Materials to process.")
|
print(f"Detected {material_count} Materials to process.")
|
||||||
|
|
||||||
# Check for required textures and filter out incomplete materials
|
valid_materials: Dict[str, Dict[str, str]] = {}
|
||||||
valid_materials = {}
|
|
||||||
failed_converts = 0
|
failed_converts = 0
|
||||||
|
|
||||||
for material, files in textures.items():
|
for material, files in textures.items():
|
||||||
@@ -98,7 +123,10 @@ def process_textures(input_files):
|
|||||||
if not files.get('Normal'):
|
if not files.get('Normal'):
|
||||||
missing_files.append('Normal')
|
missing_files.append('Normal')
|
||||||
if not (files.get('RMA') or files.get('ORM')):
|
if not (files.get('RMA') or files.get('ORM')):
|
||||||
missing_files.append('RMA or ORM')
|
if not files.get('Roughness'):
|
||||||
|
missing_files.append('Roughness')
|
||||||
|
if not files.get('AO'):
|
||||||
|
missing_files.append('AO')
|
||||||
|
|
||||||
if missing_files:
|
if missing_files:
|
||||||
print(f"Skipping {material}: missing {', '.join(missing_files)}")
|
print(f"Skipping {material}: missing {', '.join(missing_files)}")
|
||||||
@@ -106,16 +134,13 @@ def process_textures(input_files):
|
|||||||
else:
|
else:
|
||||||
valid_materials[material] = files
|
valid_materials[material] = files
|
||||||
|
|
||||||
# Process materials in parallel
|
|
||||||
success_count = 0
|
success_count = 0
|
||||||
with concurrent.futures.ThreadPoolExecutor() as executor:
|
with concurrent.futures.ThreadPoolExecutor() as executor:
|
||||||
# Submit all materials for processing
|
|
||||||
future_to_material = {
|
future_to_material = {
|
||||||
executor.submit(convert_single_material, (material, files), output_folder): material
|
executor.submit(convert_single_material, (material, files), output_folder): material
|
||||||
for material, files in valid_materials.items()
|
for material, files in valid_materials.items()
|
||||||
}
|
}
|
||||||
|
|
||||||
# Process results as they complete
|
|
||||||
for future in concurrent.futures.as_completed(future_to_material):
|
for future in concurrent.futures.as_completed(future_to_material):
|
||||||
material = future_to_material[future]
|
material = future_to_material[future]
|
||||||
try:
|
try:
|
||||||
@@ -132,58 +157,102 @@ def process_textures(input_files):
|
|||||||
print(f"+++{success_count} of {material_count} materials successfully converted+++")
|
print(f"+++{success_count} of {material_count} materials successfully converted+++")
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
|
|
||||||
def convert_to_bcr_nmo(material, basecolor_file, normal_file, rma_file, orm_file, emissive_file, opacity_file, mask_file, output_folder):
|
def _ensure_single_channel(image_path: str):
|
||||||
""" Converts given textures to BCR and NMO formats """
|
"""Load an arbitrary image and return a single-channel version for merging."""
|
||||||
|
channel_image = Image.open(image_path)
|
||||||
|
if channel_image.mode != 'L':
|
||||||
|
channel_image = channel_image.convert('L')
|
||||||
|
return channel_image
|
||||||
|
|
||||||
|
def convert_to_bcr_nmo(
|
||||||
|
material: str,
|
||||||
|
basecolor_file: Optional[str],
|
||||||
|
normal_file: Optional[str],
|
||||||
|
rma_file: Optional[str],
|
||||||
|
orm_file: Optional[str],
|
||||||
|
roughness_file: Optional[str],
|
||||||
|
metallic_file: Optional[str],
|
||||||
|
ao_file: Optional[str],
|
||||||
|
emissive_file: Optional[str],
|
||||||
|
opacity_file: Optional[str],
|
||||||
|
mask_file: Optional[str],
|
||||||
|
output_folder: str,
|
||||||
|
) -> Tuple[bool, List[str]]:
|
||||||
|
"""Convert the provided textures to BCR and NMO targets."""
|
||||||
|
if basecolor_file is None or normal_file is None:
|
||||||
|
raise ValueError('BaseColor and Normal textures are required.')
|
||||||
|
|
||||||
|
warnings: List[str] = []
|
||||||
basecolor_img = Image.open(basecolor_file).convert('RGBA')
|
basecolor_img = Image.open(basecolor_file).convert('RGBA')
|
||||||
normal_img = Image.open(normal_file).convert('RGBA')
|
normal_img = Image.open(normal_file).convert('RGBA')
|
||||||
|
base_r, base_g, base_b, _ = basecolor_img.split()
|
||||||
|
normal_r, normal_g, _, _ = normal_img.split()
|
||||||
|
|
||||||
|
roughness_channel = metallic_channel = ao_channel = None
|
||||||
|
|
||||||
if rma_file:
|
if rma_file:
|
||||||
rma_img = Image.open(rma_file).convert('RGBA')
|
packed_img = Image.open(rma_file).convert('RGBA')
|
||||||
if not (basecolor_img.size == normal_img.size == rma_img.size):
|
if not (basecolor_img.size == normal_img.size == packed_img.size):
|
||||||
return False
|
return False, warnings
|
||||||
# BCR conversion
|
roughness_channel, metallic_channel, ao_channel, _ = packed_img.split()
|
||||||
bcr_img = Image.merge('RGBA', (basecolor_img.split()[0], basecolor_img.split()[1], basecolor_img.split()[2], rma_img.split()[0])) # Use Roughness (Alpha from RMA/ORM)
|
|
||||||
bcr_img.save(os.path.join(output_folder, f"{material}_BCR.tga"))
|
|
||||||
# NMO conversion
|
|
||||||
nmo_img = Image.merge('RGBA', (normal_img.split()[0], normal_img.split()[1], rma_img.split()[1], rma_img.split()[2])) # Use Metallic, AO from RMA/ORM
|
|
||||||
nmo_img.save(os.path.join(output_folder, f"{material}_NMO.tga"))
|
|
||||||
elif orm_file:
|
elif orm_file:
|
||||||
rma_img = Image.open(orm_file).convert('RGBA')
|
packed_img = Image.open(orm_file).convert('RGBA')
|
||||||
if not (basecolor_img.size == normal_img.size == rma_img.size):
|
if not (basecolor_img.size == normal_img.size == packed_img.size):
|
||||||
return False
|
return False, warnings
|
||||||
# BCR conversion
|
ao_channel, roughness_channel, metallic_channel, _ = packed_img.split()
|
||||||
bcr_img = Image.merge('RGBA', (basecolor_img.split()[0], basecolor_img.split()[1], basecolor_img.split()[2], rma_img.split()[1])) # Use Roughness (Alpha from RMA/ORM)
|
else:
|
||||||
|
if roughness_file is None or ao_file is None:
|
||||||
|
raise ValueError('Roughness and AO textures are required when RMA/ORM is absent.')
|
||||||
|
roughness_channel = _ensure_single_channel(roughness_file)
|
||||||
|
ao_channel = _ensure_single_channel(ao_file)
|
||||||
|
if metallic_file is not None:
|
||||||
|
metallic_channel = _ensure_single_channel(metallic_file)
|
||||||
|
else:
|
||||||
|
metallic_channel = Image.new('L', basecolor_img.size, 0)
|
||||||
|
warnings.append(f"{material}: Missing Metallic texture. Using a black channel.")
|
||||||
|
|
||||||
|
if not (
|
||||||
|
basecolor_img.size
|
||||||
|
== normal_img.size
|
||||||
|
== roughness_channel.size
|
||||||
|
== ao_channel.size
|
||||||
|
):
|
||||||
|
return False, warnings
|
||||||
|
if metallic_channel.size != basecolor_img.size:
|
||||||
|
return False, warnings
|
||||||
|
|
||||||
|
if roughness_channel is None or metallic_channel is None or ao_channel is None:
|
||||||
|
raise RuntimeError('Failed to resolve packed or single-channel textures.')
|
||||||
|
|
||||||
|
bcr_img = Image.merge('RGBA', (base_r, base_g, base_b, roughness_channel))
|
||||||
bcr_img.save(os.path.join(output_folder, f"{material}_BCR.tga"))
|
bcr_img.save(os.path.join(output_folder, f"{material}_BCR.tga"))
|
||||||
# NMO conversion
|
|
||||||
nmo_img = Image.merge('RGBA', (normal_img.split()[0], normal_img.split()[1], rma_img.split()[2], rma_img.split()[0])) # Use Metallic, AO from RMA/ORM
|
nmo_img = Image.merge('RGBA', (normal_r, normal_g, metallic_channel, ao_channel))
|
||||||
nmo_img.save(os.path.join(output_folder, f"{material}_NMO.tga"))
|
nmo_img.save(os.path.join(output_folder, f"{material}_NMO.tga"))
|
||||||
# Optionally handle emissive and opacity maps
|
|
||||||
if emissive_file:
|
if emissive_file:
|
||||||
emissive_img = Image.open(emissive_file)
|
emissive_img = Image.open(emissive_file)
|
||||||
# Preserve original color mode instead of forcing RGB
|
|
||||||
if emissive_img.mode != 'RGBA':
|
if emissive_img.mode != 'RGBA':
|
||||||
emissive_img = emissive_img.convert('RGBA')
|
emissive_img = emissive_img.convert('RGBA')
|
||||||
emissive_img.save(os.path.join(output_folder, f"{material}_EM.tga"))
|
emissive_img.save(os.path.join(output_folder, f"{material}_EM.tga"))
|
||||||
|
|
||||||
if opacity_file:
|
if opacity_file:
|
||||||
opacity_img = Image.open(opacity_file)
|
opacity_img = Image.open(opacity_file)
|
||||||
# Preserve original color mode instead of forcing grayscale
|
|
||||||
if opacity_img.mode != 'RGBA':
|
if opacity_img.mode != 'RGBA':
|
||||||
opacity_img = opacity_img.convert('RGBA')
|
opacity_img = opacity_img.convert('RGBA')
|
||||||
opacity_img.save(os.path.join(output_folder, f"{material}_OP.tga"))
|
opacity_img.save(os.path.join(output_folder, f"{material}_OP.tga"))
|
||||||
|
|
||||||
if mask_file:
|
if mask_file:
|
||||||
mask_img = Image.open(mask_file)
|
mask_img = Image.open(mask_file)
|
||||||
# Preserve original color mode instead of forcing grayscale
|
|
||||||
if mask_img.mode != 'RGBA':
|
if mask_img.mode != 'RGBA':
|
||||||
mask_img = mask_img.convert('RGBA')
|
mask_img = mask_img.convert('RGBA')
|
||||||
mask_img.save(os.path.join(output_folder, f"{material}_MASK.tga"))
|
mask_img.save(os.path.join(output_folder, f"{material}_MASK.tga"))
|
||||||
|
|
||||||
return True
|
return True, warnings
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
if len(sys.argv) < 2:
|
if len(sys.argv) < 2:
|
||||||
print("Usage: drag and drop texture files onto the script")
|
print("Usage: drag and drop texture files onto the script")
|
||||||
else:
|
else:
|
||||||
# Get the file paths from sys.argv (ignoring the first argument which is the script name)
|
|
||||||
input_files = sys.argv[1:]
|
input_files = sys.argv[1:]
|
||||||
process_textures(input_files)
|
process_textures(input_files)
|
||||||
|
|||||||
Reference in New Issue
Block a user