-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathgltf-texturec.py
123 lines (113 loc) · 5.65 KB
/
gltf-texturec.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
import argparse
from pathlib import Path, PurePath
from pygltflib import GLTF2
import subprocess
import sys
import shutil
class GLTFConverter:
mimetypes = {
'.dds': 'image/vnd.ms-dds',
'.exr': 'image/x-exr',
'.hdr': 'image/vnd.radiance',
'.ktx': 'image/ktx',
'.png': 'image/png'
}
def __init__(self, file_format, texture_type, quality="default", generate_mips=True):
if file_format not in GLTFConverter.mimetypes.keys():
raise ValueError("file_format must be one of %s" % ", ".join(GLTFConverter.mimetypes.keys()))
self.generate_mips = generate_mips
self.file_format = file_format
self.texture_type = texture_type
self.quality = quality
self.gltf = None
self.directory = None
self.outdirectory = None
self.converted = []
def load(self, file):
self.file = file
self.directory = Path(file).parent.resolve()
self.gltf = GLTF2().load(file)
return self.gltf is not None
def convert(self, outfile):
if self.gltf is None:
raise RuntimeError("load must be called before convert")
self.outdirectory = Path(outfile).parent.resolve()
self.outdirectory.mkdir(parents=True, exist_ok=True)
self.converted = []
for mat in self.gltf.materials:
self.convert_material(mat)
self.gltf.save(outfile)
# Copy binary blobs if needed
# pygltflib doesn't seem to do this
if self.outdirectory != self.directory:
for buffer in self.gltf.buffers:
srcbuffer = self.directory.joinpath(buffer.uri)
dstbuffer = self.outdirectory.joinpath(buffer.uri)
shutil.copy2(srcbuffer, dstbuffer)
def convert_material(self, mat):
mr = mat.pbrMetallicRoughness
sg = mat.extensions.get('KHR_materials_pbrSpecularGlossiness')
if mr is not None:
if mr.baseColorTexture is not None:
self.convert_gltf_texture(mr.baseColorTexture.index, is_linear = False)
if mr.metallicRoughnessTexture is not None:
self.convert_gltf_texture(mr.metallicRoughnessTexture.index, is_linear = True)
elif sg is not None:
if 'diffuseTexture' in sg:
self.convert_gltf_texture(sg['diffuseTexture']['index'], is_linear = False)
if 'specularGlossinessTexture' in sg:
self.convert_gltf_texture(sg['specularGlossinessTexture']['index'], is_linear = True)
if mat.normalTexture is not None:
self.convert_gltf_texture(mat.normalTexture.index, is_linear = True, is_normal = True)
if mat.occlusionTexture is not None:
self.convert_gltf_texture(mat.occlusionTexture.index, is_linear = True)
if mat.emissiveTexture is not None:
self.convert_gltf_texture(mat.emissiveTexture.index, is_linear = False)
def convert_gltf_texture(self, texture_index, is_linear, is_normal=False):
texture = self.gltf.textures[texture_index]
if texture.source not in self.converted:
self.converted += [texture.source]
image = self.gltf.images[texture.source]
new_filename = self.convert_image_file(image.uri, is_linear, is_normal)
image.uri = new_filename
# is this necessary? should only apply to buffer views
image.mimeType = GLTFConverter.mimetypes[self.file_format]
def convert_image_file(self, filename, is_linear, is_normal=False):
new_filename = str(PurePath(filename).with_suffix(self.file_format))
inpath = str(self.directory.joinpath(filename))
outpath = str(self.outdirectory.joinpath(new_filename))
print("Converting %s" % inpath)
arguments = ["-f", inpath, "-o", outpath, "-t", self.texture_type, "-q", self.quality]
if self.generate_mips:
arguments += ["--mips"]
if is_linear:
arguments += ["--linear"] # don't apply gamma correction
if is_normal:
arguments += ["--normalmap"]
self.run_texturec(arguments)
return new_filename
def run_texturec(self, arguments):
try:
proc = subprocess.run(["texturec"] + arguments, encoding='utf-8', stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
if proc.returncode != 0:
print(proc.stdout, file=sys.stderr)
except FileNotFoundError:
sys.exit("texturec not found. Make sure its location is in the PATH environment variable.")
def main():
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
# args like -f show up as optional arguments in the help, even if required
# this is a workaround
required = parser.add_argument_group('required arguments')
required.add_argument("-i", type=str, help="Input file path", required=True)
required.add_argument("-o", type=str, help="Output file path", required=True)
parser.add_argument("-f", "--format", type=str, choices=GLTFConverter.mimetypes.keys(), default=".dds", help="File format")
parser.add_argument("-t", "--type", type=str, default="BC3", help="Output format type (run texturec --formats for a list)")
parser.add_argument("-q", "--quality", type=str, choices=["default", "fastest", "highest"], default="default", help="Encoding quality")
parser.add_argument("-m", "--mips", action="store_true", help="Generate mip-maps")
args = parser.parse_args()
converter = GLTFConverter(args.format, args.type, args.quality, args.mips)
if not converter.load(args.i):
sys.exit("Input file not found")
converter.convert(args.o)
if __name__ == "__main__":
main()