MaterialXMaterials 1.39.5
Utilities for retrieving materials from remote servers
Loading...
Searching...
No Matches
GPUOpenLoader.py
1'''
2@brief Utilities to extract materials from the GPUOpen material database. This is not a complete set of calls to extract out all material information but instead enough to find materials
3and extract out specific packages from the list of available materials.
4
5See: https://api.matlib.gpuopen.com/api/swagger/ for information on available API calls.
6'''
7
8import requests, json, os, io, re, zipfile, logging # type: ignore
9from http import HTTPStatus
10# Note: MaterialX is not currently a dependency since no MaterialX processing is required.
11#import MaterialX as mx
12
13import io
14import zipfile
15from io import BytesIO
16from PIL import Image as PILImage
17import base64
18
20 '''
21 This class is used to load materials from the GPUOpen material database.
22 See: https://api.matlib.gpuopen.com/api/swagger/ for API information.
23 '''
24 def __init__(self):
25 '''
26 Initialize the GPUOpen material loader.
27 '''
28
29 self.root_url = 'https://api.matlib.gpuopen.com/api'
30
31 self.url = self.root_url + '/materials'
32
33 self.package_url = self.root_url + '/packages'
34
35 self.render_url = self.root_url + '/renders'
36
37 self.materialPreviews = None
38
39 self.materials = None
40
41 self.materialNames = None
42
43 self.renders = None
44
45
46 self.logger = logging.getLogger('GPUO')
47 logging.basicConfig(level=logging.INFO)
48
49 def writePackageDataToFile(self, data, outputFolder, title, url, unzipFile=True) -> bool:
50 '''
51 Write a package data to a file.
52 @param data: The data to write.
53 @param outputFolder: The output folder to write the file to.
54 @param title: The title of the file.
55 @param url: The URL for the material preview image.
56 @param unzipFile: If true, the file is unzipped to a folder with the same name
57 as the title.
58 @return: True if the package was written.
59 '''
60 if not data:
61 return False
62
63 if not os.path.exists(outputFolder):
64 os.makedirs(outputFolder)
65
66 if unzipFile:
67 # Assuming `data` is the binary data of the zip file and `title` and `outputFolder` are defined
68 unzipFolder = os.path.join(outputFolder, title)
69
70 # Use BytesIO to handle the data in memory
71 with io.BytesIO(data) as data_io:
72 with zipfile.ZipFile(data_io, 'r') as zip_ref:
73 zip_ref.extractall(unzipFolder)
74
75 # Write a url.txt file with url information for the material preview
76 urlFile = os.path.join(unzipFolder, 'url.txt')
77 with open(urlFile, 'w') as f:
78 f.write(url)
79
80 self.logger.info(f'Unzipped to folder: "{unzipFolder}"')
81
82 else:
83 # Add the url.txt into the existing zip data
84 zip_buffer = io.BytesIO()
85 # Open the original zip data in append mode
86 with zipfile.ZipFile(io.BytesIO(data), 'a', zipfile.ZIP_DEFLATED) as zip_in:
87 # Copy all files to a new zip buffer
88 with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_out:
89 for item in zip_in.infolist():
90 zip_out.writestr(item, zip_in.read(item.filename))
91 # Add url.txt
92 zip_out.writestr('url.txt', url)
93 data = zip_buffer.getvalue()
94
95 outputFile = os.path.join(outputFolder, f"{title}.zip")
96 with open(outputFile, "wb") as f:
97 self.logger.info(f'Write package to file: "{outputFile}"')
98 f.write(data)
99
100
101 return True
102
103 def convertPilImageToBase64(self, image):
104 """
105 Convert a PIL image to a Base64 string.
106 @param image: An instance of PIL.Image
107 @return: Base64-encoded string of the image
108 """
109 pilImage = PILImage
110 if not pilImage:
111 self.logger.debug('Pillow (PIL) image module not provided. Image data will not be converted to Base64.')
112 return None
113 if not image:
114 self.logger.debug('No image data. Image data will not be converted to Base64.')
115 return None
116
117 # - Create an in-memory buffer
118 # - Save the image to the buffer in PNG format
119 # - Get the PNG file data from the buffer
120 # - Encode the binary data to Base64
121 buffer = BytesIO()
122 image.save(buffer, format="PNG")
123 binary_data = buffer.getvalue()
124 base64_encoded_data = base64.b64encode(binary_data).decode('utf-8')
125 buffer.close()
126
127 return base64_encoded_data
128
129 def check_usdz_image_restrictions(self, zip_data, pilImage):
130 '''
131 Check that the images in the zip file meet the USDZ format restrictions.
132 @param zip_data: The binary data of the zip file.
133 @param pilImage: The PIL image module.
134 @return: A list of issues found with the images. If the list is empty, then no issues were found.
135 '''
136 ZIP_COMPRESSION_NAMES = {
137 zipfile.ZIP_STORED: "ZIP_STORED",
138 zipfile.ZIP_DEFLATED: "ZIP_DEFLATED",
139 zipfile.ZIP_BZIP2: "ZIP_BZIP2",
140 zipfile.ZIP_LZMA: "ZIP_LZMA"
141 }
142
143 issues = []
144 with zipfile.ZipFile(io.BytesIO(zip_data), 'r') as zf:
145 for info in zf.infolist():
146 # 1. Check uncompressed
147 if info.compress_type != zipfile.ZIP_STORED:
148 compression_name = ZIP_COMPRESSION_NAMES.get(info.compress_type, str(info.compress_type))
149 issues.append(f"{info.filename} is not uncompressed (ZIP_STORED). {compression_name} found.")
150 # 2. Check image format
151 if info.filename.lower().endswith(('.png', '.jpg', '.jpeg')):
152 img_data = zf.read(info.filename)
153 try:
154 img = pilImage.open(io.BytesIO(img_data))
155 if img.format not in ('PNG', 'JPEG'):
156 issues.append(f"{info.filename} is not PNG or JPEG")
157 if img.format == 'PNG':
158 if img.info.get('interlace', 0) != 0:
159 issues.append(f"{info.filename} is interlaced PNG (not allowed)")
160 if img.mode not in ('L', 'LA', 'RGB', 'RGBA'):
161 issues.append(f"{info.filename} PNG mode {img.mode} not allowed")
162 if img.format == 'JPEG':
163 if img.info.get('progressive', 0) != 0:
164 issues.append(f"{info.filename} is progressive JPEG (not allowed)")
165 except Exception as e:
166 issues.append(f"{info.filename} could not be opened as image: {e}")
167 return issues
168
169 def extractPackageData(self, data, pilImage):
170 '''
171 Extract the package data from a zip file.
172 @param data: The data to extract.
173 @param pilImage: The PIL image module.
174 @return: A list of extracted data of the form:
175 [ { 'file_name': file_name, 'data': data, 'type': type } ]
176 '''
177 if not pilImage:
178 pilImage = PILImage
179 if not pilImage:
180 self.logger.debug('Pillow (PIL) image module provided. Image data will not be extracted.')
181
182 zip_object = io.BytesIO(data)
183
184 extracted_data_list = []
185 with zipfile.ZipFile(zip_object, 'r') as zip_file:
186 # Iterate through the files in the zip archive
187 for file_name in zip_file.namelist():
188 # Extract each file into memory
189 extracted_data = zip_file.read(file_name)
190 if file_name.endswith('.mtlx'):
191 mtlx_string = extracted_data.decode('utf-8')
192 extracted_data_list.append( {'file_name': file_name, 'data': mtlx_string, 'type': 'mtlx'} )
193
194 # If the data is a image, create a image in Python
195 elif file_name.endswith('.png'):
196 if pilImage:
197 image = pilImage.open(io.BytesIO(extracted_data))
198 else:
199 image = None
200 extracted_data_list.append( {'file_name': file_name, 'data': image, 'type': 'image'} )
201
202 return extracted_data_list
203
204 def downloadPackage(self, listNumber, materialNumber, packageId=0):
205 '''
206 Download a package for a given material from the GPUOpen material database.
207 @param listNumber: The list number of the material to download.
208 @param materialNumber: The material number to download.
209 @param packageId: The package ID to download.
210 Packages are numbered starting at 0. Default is 0.
211 with index 0 containing the smallest package (smallest resolution referenced textures).
212 '''
213 if self.materials == None or len(self.materials) == 0:
214 self.logger.info('No material loaded.')
215 return [None, None]
216
217 json_data = self.materials[listNumber]
218 if not json_data:
219 self.logger.info(f'No material for list {listNumber}.')
220 return [None, None]
221
222 jsonResults = None
223 jsonResult = None
224 if "results" in json_data:
225 jsonResults = json_data["results"]
226 if len(jsonResults) <= materialNumber:
227 self.logger.info(f'No material for index {materialNumber}.')
228 return [None, None]
229 else:
230 jsonResult = jsonResults[materialNumber]
231
232 if not jsonResult:
233 return [None, None]
234
235 # Get the package ID
236 jsonPackages = None
237 if "packages" in jsonResult:
238 jsonPackages = jsonResult["packages"]
239 if not jsonPackages:
240 self.logger.info(f'No packages for material {materialNumber}.')
241 return [None, None]
242
243 if len(jsonPackages) <= packageId:
244 self.logger.info(f'No package for index {packageId}.')
245 return [None, None]
246 package_id = jsonPackages[packageId]
247
248 if not package_id:
249 self.logger.info(f'No package for index {packageId}.')
250 return [None, None]
251
252 url = f"{self.package_url}/{package_id}/download"
253 data = requests.get(url).content
254
255 title = jsonResult["title"]
256
257 preview_url = self.getMaterialPreviewURL(title)
258 return [data, title, preview_url]
259
260 def downloadPackageByExpression(self, searchExpr, exact_match=False, packageId=0):
261 '''
262 Download a package for a given material from the GPUOpen material database.
263 @param searchExpr: The regular expression to match the material name.
264 @param exact_match: If true, the material name must match exactly. Default is false.
265 @param packageId: The package ID to download.
266 @return: A list of downloaded packages of the form:
267 '''
268 downloadList = []
269
270 foundList = self.findMaterialsByName(searchExpr, exact_match)
271 if len(foundList) > 0:
272 for found in foundList:
273 listNumber = found['listNumber']
274 materialNumber = found['materialNumber']
275 matName = found['title']
276 self.logger.info(f'> Download material: {matName} List: {listNumber}. Index: {materialNumber}')
277 result = [data, title, url] = self.downloadPackage(listNumber, materialNumber, packageId)
278 downloadList.append(result)
279 return downloadList
280
281 def findMaterialsByName(self, materialName, exact_match = False) -> list:
282 '''
283 Find materials by name.
284 @param materialName: Regular expression to match the material name.
285 @param exact_match: If true, the material name must match exactly. Default is false.
286 @return: A list of materials that match the regular expression of the form:
287 [ { 'listNumber': listNumber, 'materialNumber': materialNumber, 'title': title } ]
288 '''
289 if (self.materials == None):
290 return []
291
292 materialsList = []
293 listNumber = 0
294 for materialList in self.materials:
295 materialNumber = 0
296 for material in materialList['results']:
297 if exact_match:
298 if materialName.lower() == material['title'].lower():
299 materialsList.append({ 'listNumber': listNumber, 'materialNumber': materialNumber, 'title': material['title'] })
300 return materialsList
301 else:
302 if re.match(materialName, material['title'], re.IGNORECASE):
303 materialsList.append({ 'listNumber': listNumber, 'materialNumber': materialNumber, 'title': material['title'] })
304 materialNumber += 1
305 listNumber += 1
306
307 return materialsList
308
309 def getMaterialNames(self) -> list:
310 '''
311 Update the material names from the material lists.
312 @return: List of material names. If no materials are loaded, then an empty list is returned.
313 '''
314 self.materialNames = []
315 if (self.materials == None):
316 return []
317
318 for materialList in self.materials:
319 for material in materialList['results']:
320 self.materialNames.append(material['title'])
321
322 return self.materialNames
323
324 def getMaterialPreviewURL(self, title) -> str:
325 '''
326 @breif Given the title of a material, return the URL for the material preview image.
327 @param title: The title of the material to get the preview URL for.
328 @return: The URL for the material preview image. Else empty string.
329 '''
330 url = ''
331 if (self.materialPreviews == None):
333 if (not self.materialPreviews):
334 return url
335
336 for item in self.materialPreviews:
337 if item['title'] == title:
338 url = item['preview_url']
339 break
340 return url
341
342 def getMaterialPreviews(self, force = False) -> list | None:
343 if not self.materialPreviews or force:
345 return self.materialPreviews
346
347 def computeMaterialPreviews(self) -> list:
348 '''
349 @brief Get the material preview URLs for the materials loaded from the GPUOpen material database.
350 @return list of items of the form: { 'title': material_title, 'preview_url': url }
351 If no materials or renders are loaded, then an empty list is returned.
352 '''
353 self.materialPreviews = []
354 if (self.materials == None):
355 return []
356 if (self.renders == None):
357 return []
358 render_urls = self.renders["renders"]
359
360 for materialList in self.materials:
361 for material in materialList['results']:
362 renders_order_list = material['renders_order']
363 material_title = material['title']
364
365 if len(renders_order_list) > 0:
366 #print('renders_order_list:', renders_order_list)
367 render_lookup = renders_order_list[0]
368 # Look for item in "renders" list with "id" == render_lookup
369 for render in render_urls:
370 #print('render id:', render['id'], 'render_lookup:', render_lookup )
371 if render['id'] == render_lookup:
372 url = render['thumbnail_url']
373 item = { 'title': material_title, 'preview_url': url }
374 self.materialPreviews.append(item)
375 #print(f'Found render for material: {item}')
376 break
377 else:
378 self.logger.info(f'No renderings specified for material: {material_title}')
379 return self.materialPreviews
380
381 def getMaterials(self) -> list:
382 '''
383 Get the materials returned from the GPUOpen material database.
384 Will loop based on the linked-list of materials stored in the database.
385 Currently the batch size requested is 100 materials per batch.
386 @return: List of material lists
387 '''
388
389 self.materials = []
390 self.materialNames = []
391
392 url = self.url
393 headers = {
394 'accept': 'application/json'
395 }
396
397 # Get batches of materials. Start with the first 100.
398 # Can apply more filters to this as needed in the future.
399 # This will get every material in the database.
400 params = {
401 'limit': 100,
402 'offset': 0
403 }
404 haveMoreMaterials = True
405 while (haveMoreMaterials):
406
407 response = requests.get(url, headers=headers, params=params)
408
409 if response.status_code == HTTPStatus.OK:
410
411 raw_response = response.text
412
413 # Split the response text assuming the JSON objects are concatenated
414 json_strings = raw_response.split('}{')
415 #self.logger.info('Number of JSON strings:', len(json_strings))
416 json_result_string = json_strings[0]
417 jsonObject = json.loads(json_result_string)
418 self.materials.append(jsonObject)
419
420 # Scan for next batch of materials
421 nextQuery = jsonObject['next']
422 if (nextQuery):
423 # Get limit and offset from this: 'https://api.matlib.gpuopen.com/api/materials/?limit=100&offset=100"'
424 # Split the string by '?'
425 queryParts = nextQuery.split('?')
426 # Split the string by '&'
427 queryParts = queryParts[1].split('&')
428 # Split the string by '='
429 limitParts = queryParts[0].split('=')
430 offsetParts = queryParts[1].split('=')
431 params['limit'] = int(limitParts[1])
432 params['offset'] = int(offsetParts[1])
433 self.logger.info(f'Fetch set of materials: limit: {params["limit"]} offset: {params["offset"]}')
434 else:
435 haveMoreMaterials = False
436 break
437
438 else:
439 self.logger.info(f'Error: {response.status_code}, {response.text}')
440
441 return self.materials
442
443 def getRenders(self, force=False) -> list:
444 '''
445 Get the rendering information returned from the GPUOpen material database.
446 Will loop based on the linked-list of render info stored in the database.
447 Currently the batch size requested is 100 render infos per batch.
448 @param force: If true, forces a re-download of the render information. Default is false.
449 @return: List of material lists
450 '''
451 if self.renders and not force:
452 return self.renders
453
454 self.renders = { "renders": [] }
455
456 url = self.render_url
457 headers = {
458 'accept': 'application/json'
459 }
460
461 # Get batches of materials. Start with the first 100.
462 # Can apply more filters to this as needed in the future.
463 # This will get every material in the database.
464 params = {
465 'limit': 100,
466 'offset': 0
467 }
468 haveMoreMaterials = True
469 while (haveMoreMaterials):
470
471 response = requests.get(url, headers=headers, params=params)
472
473 if response.status_code == HTTPStatus.OK:
474
475 raw_response = response.text
476
477 # Split the response text assuming the JSON objects are concatenated
478 json_strings = raw_response.split('}{')
479 #self.logger.info('Number of JSON strings:', len(json_strings))
480 json_result_string = json_strings[0]
481 jsonObject = json.loads(json_result_string)
482
483 # Extrac out the "results": [] list
484 results_list = jsonObject['results']
485 for result in results_list:
486 self.renders["renders"].append(result)
487
488 # Scan for next batch of materials
489 nextQuery = jsonObject['next']
490 if (nextQuery):
491 # Get limit and offset from this: 'https://api.matlib.gpuopen.com/api/renders/?limit=100&offset=100"'
492 # Split the string by '?'
493 queryParts = nextQuery.split('?')
494 # Split the string by '&'
495 queryParts = queryParts[1].split('&')
496 # Split the string by '='
497 limitParts = queryParts[0].split('=')
498 offsetParts = queryParts[1].split('=')
499 params['limit'] = int(limitParts[1])
500 params['offset'] = int(offsetParts[1])
501 self.logger.info(f'Fetch set of render infos: limit: {params["limit"]} offset: {params["offset"]}')
502 else:
503 haveMoreMaterials = False
504 break
505
506 else:
507 self.logger.info(f'Error: {response.status_code}, {response.text}')
508
509 return self.renders
510
511 def getMaterialsAsJsonString(self) -> list:
512 '''
513 Get the JSON strings for the materials
514 @return: List of JSON strings for the materials. One string per material batch.
515 '''
516 results : list = []
517
518 if (self.materials == None):
519 return results
520 for material in self.materials:
521 results.append(json.dumps(material, indent=4, sort_keys=True))
522 return results
523
524 def getMaterialFileNames(self, rootName) -> list:
525 '''
526 Get list of material file names based on root file name.
527 @param rootName: The root name of the files to load. The files are assumed to be named: rootName_#.json
528 '''
529 filePaths = []
530 rootName = os.path.basename(rootName)
531 rootDir = os.path.dirname(rootName)
532 if not rootDir:
533 rootDir = '.'
534 #print('RootDir:', rootDir)
535 #print('RootName:', rootName)
536 for root, dirs, files in os.walk(rootDir):
537 for file in files:
538 # Check that it ends with a number + ".json". e.g.
539 # "GPUOpenMaterialX_0.json"
540 if file.startswith(rootName) and file.endswith('.json') and file[len(rootName):-5].isdigit():
541 filePath = os.path.join(root, file)
542 filePaths.append(filePath)
543 return filePaths
544
545 def readPackageFiles(self) -> None:
546 '''
547 @brief Read the material files from the "data/GPUOpenMaterialX" folder in the install Python package.
548 The files are expected to be named:
549 - "GPUOpenMaterialX_#.json" for material files,
550 - "GPUOpenMaterialX_Previews_.json" for material preview information, and
551 - "GPUOpenMaterialX_Names.json" for material names.
552 '''
553
554 self.materials = []
555 self.materialPreviews = []
556 self.materialNames = []
557
558 # Read "data/GPUOpenMaterialX" files from install Python package
559 # Get package:
560 packageFolder = os.path.join(os.path.dirname(__file__), 'data/GPUOpenMaterialX')
561 for fileName in os.listdir(packageFolder):
562 filePath = os.path.join(packageFolder, fileName)
563 self.logger.debug(f'> SCAN package file: "{filePath}"')
564 # Check for files of this form: GPUOpenMaterialX_#.json
565 if re.match(r'GPUOpenMaterialX_\d+\.json', fileName):
566 self.logger.debug(f'> Read package file: "{filePath}"')
567 with open(filePath) as f:
568 data = json.load(f)
569 results = data['results']
570 results_count = len(results)
571 self.materials.append(data)
572 elif fileName == 'GPUOpenMaterialX_Previews_.json':
573 self.logger.debug(f'> Read package file: "{filePath}"')
574 with open(filePath) as f:
575 data = json.load(f)
576 self.materialPreviews = data
577 #elif fileName == 'GPUOpenMaterialX_Names.json':
578 # self.logger.debug(f'> Read package file: "{filePath}"')
579 # with open(filePath) as f:
580 # data = json.load(f)
581 # self.materialNames = data
582 elif fileName == 'GPUOpenMaterialX_Renders_.json':
583 self.logger.debug(f'> Read package file: "{filePath}"')
584 with open(filePath) as f:
585 data = json.load(f)
586 self.renders = data
587
588 # Better to extract the names from materials vs reading from file
589 # which may be out of sync.
590 self.getMaterialNames()
591
592 self.logger.debug(f'Loaded {len(self.materials)} material files, '
593 f'{len(self.materialPreviews)} material previews, and '
594 f'{len(self.materialNames)} material names, '
595 f'{len(self.renders)} render files from package.')
596
597
598 def readMaterialFiles(self, fileNames) -> list:
599 '''
600 Load the materials from a set of JSON files downloaded from
601 the GPUOpen material database.
602 '''
603 self.materials = []
604 for fileName in fileNames:
605 with open(fileName) as f:
606 data = json.load(f)
607 results = data['results']
608 results_count = len(results)
609 self.materials.append(data)
610 return self.materials
611
612 def writeRenderFiles(self, folder, rootFileName) -> int:
613 '''
614 Write the render information to disk files.
615 @param folder: The folder to write the files to.
616 @param rootFileName: The root file name to use for the files.
617 @return: The number of files written.
618 '''
619 if (self.renders == None):
620 return 0
621
622 os.makedirs(folder, exist_ok=True)
623 # Write JSON to file
624 fileName = rootFileName + '_.json'
625 rendersFileName = os.path.join(folder, fileName)
626 self.logger.info(f'> Write render info to file: "{rendersFileName}"')
627 with open(rendersFileName, 'w') as f:
628 json.dump(self.renders, f, indent=4)
629
630 def writeMaterialPreviewFile(self, folder, rootFileName):
631 '''
632 Write the material preview information to disk files.
633 @param folder: The folder to write the files to.
634 @param rootFileName: The root file name to use for the files.
635 '''
636 if (self.materialPreviews == None):
637 return 0
638
639 os.makedirs(folder, exist_ok=True)
640 # Write JSON to file
641 fileName = rootFileName + '_.json'
642 previewsFileName = os.path.join(folder, fileName)
643 self.logger.info(f'> Write material preview info to file: "{previewsFileName}"')
644 with open(previewsFileName, 'w') as f:
645 json.dump(self.materialPreviews, f, indent=4)
646
647
648 def writeMaterialFiles(self, folder, rootFileName) -> int:
649 '''
650 Write the materials to disk.
651 @param folder: The folder to write the files to.
652 @param rootFileName: The root file name to use for the files.
653 @return: The number of files written.
654 '''
655 if (self.materials == None):
656 return 0
657
658 i = 0
659 if (len(self.materials) > 0):
660 os.makedirs(folder, exist_ok=True)
661 for material in self.materials:
662 # Write JSON to file
663 fileName = rootFileName + '_' + str(i) + '.json'
664 materialFileName = os.path.join(folder, fileName)
665 self.logger.info(f'> Write material to file: "{materialFileName}"')
666 with open(materialFileName, 'w') as f:
667 json.dump(material, f, indent=4, sort_keys=True)
668 i += 1
669
670 return i
671
672 def writeMaterialNamesToFile(self, fileName, sort=True):
673 '''
674 Write sorted list of the material names to a file in JSON format
675 @param fileName: The file name to write the material names to.
676 @param sort: If true, sort the material names.
677 '''
678 if (self.materialNames == None):
679 return
680
681 with open(fileName, 'w') as f:
682 json.dump(self.materialNames, f, indent=2, sort_keys=sort)
This class is used to load materials from the GPUOpen material database.
writeMaterialNamesToFile(self, fileName, sort=True)
Write sorted list of the material names to a file in JSON format.
str render_url
URL for getting rendering information.
list getMaterialFileNames(self, rootName)
Get list of material file names based on root file name.
list getMaterials(self)
Get the materials returned from the GPUOpen material database.
writeMaterialPreviewFile(self, folder, rootFileName)
Write the material preview information to disk files.
str getMaterialPreviewURL(self, title)
@breif Given the title of a material, return the URL for the material preview image.
int writeMaterialFiles(self, folder, rootFileName)
Write the materials to disk.
str package_url
URL for the package information.
convertPilImageToBase64(self, image)
Convert a PIL image to a Base64 string.
list materialPreviews
List of title, preview url pairs for the materials.
str root_url
Root URL for the GPUOpen material database.
downloadPackage(self, listNumber, materialNumber, packageId=0)
Download a package for a given material from the GPUOpen material database.
None readPackageFiles(self)
Read the material files from the "data/GPUOpenMaterialX" folder in the install Python package.
extractPackageData(self, data, pilImage)
Extract the package data from a zip file.
bool writePackageDataToFile(self, data, outputFolder, title, url, unzipFile=True)
Write a package data to a file.
list getMaterialsAsJsonString(self)
Get the JSON strings for the materials.
int writeRenderFiles(self, folder, rootFileName)
Write the render information to disk files.
list computeMaterialPreviews(self)
Get the material preview URLs for the materials loaded from the GPUOpen material database.
list findMaterialsByName(self, materialName, exact_match=False)
Find materials by name.
list getRenders(self, force=False)
Get the rendering information returned from the GPUOpen material database.
list getMaterialNames(self)
Update the material names from the material lists.
downloadPackageByExpression(self, searchExpr, exact_match=False, packageId=0)
Download a package for a given material from the GPUOpen material database.
list readMaterialFiles(self, fileNames)
Load the materials from a set of JSON files downloaded from the GPUOpen material database.
__init__(self)
Initialize the GPUOpen material loader.
check_usdz_image_restrictions(self, zip_data, pilImage)
Check that the images in the zip file meet the USDZ format restrictions.