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.
5See: https://api.matlib.gpuopen.com/api/swagger/ for information on available API calls.
8import requests, json, os, io, re, zipfile, logging
9from http
import HTTPStatus
15from PIL
import Image
as pilImage
19 This class is used to load materials from the GPUOpen material database.
20 See: https://api.matlib.gpuopen.com/api/swagger/ for API information.
23 self.
root_url =
'https://api.matlib.gpuopen.com/api'
28 self.
logger = logging.getLogger(
'GPUO')
29 logging.basicConfig(level=logging.INFO)
33 Write a package data to a file.
34 @param data: The data to write.
35 @param outputFolder: The output folder to write the file to.
36 @param title: The title of the file.
37 @param unzipFile: If true, the file is unzipped to a folder with the same name
39 @return: True if the package was written.
44 if not os.path.exists(outputFolder):
45 os.makedirs(outputFolder)
49 unzipFolder = os.path.join(outputFolder, title)
52 with io.BytesIO(data)
as data_io:
53 with zipfile.ZipFile(data_io,
'r')
as zip_ref:
54 zip_ref.extractall(unzipFolder)
56 self.
logger.info(f
'Unzipped to folder: "{unzipFolder}"')
59 outputFile = os.path.join(outputFolder, f
"{title}.zip")
60 with open(outputFile,
"wb")
as f:
61 self.
logger.info(f
'Write package to file: "{outputFile}"')
68 Extract the package data from a zip file.
69 @param data: The data to extract.
70 @param pilImage: The PIL image module.
71 @return: A list of extracted data of the form:
72 [ { 'file_name': file_name, 'data': data, 'type': type } ]
75 self.
logger.debug(
'Pillow (PIL) image module provided. Image data will not be extracted.')
77 zip_object = io.BytesIO(data)
79 extracted_data_list = []
80 with zipfile.ZipFile(zip_object,
'r')
as zip_file:
82 for file_name
in zip_file.namelist():
84 extracted_data = zip_file.read(file_name)
85 if file_name.endswith(
'.mtlx'):
86 mtlx_string = extracted_data.decode(
'utf-8')
87 extracted_data_list.append( {
'file_name': file_name,
'data': mtlx_string,
'type':
'mtlx'} )
90 elif file_name.endswith(
'.png'):
92 image = pilImage.open(io.BytesIO(extracted_data))
95 extracted_data_list.append( {
'file_name': file_name,
'data': image,
'type':
'image'} )
97 return extracted_data_list
101 Download a package for a given material from the GPUOpen material database.
102 @param listNumber: The list number of the material to download.
103 @param materialNumber: The material number to download.
104 @param packageId: The package ID to download.
105 Packages are numbered starting at 0. Default is 0.
106 with index 0 containing the smallest package (smallest resolution referenced textures).
117 if "results" in json_data:
118 jsonResults = json_data[
"results"]
119 if len(jsonResults) <= materialNumber:
122 jsonResult = jsonResults[materialNumber]
129 if "packages" in jsonResult:
130 jsonPackages = jsonResult[
"packages"]
134 if len(jsonPackages) <= packageId:
136 package_id = jsonPackages[packageId]
141 url = f
"{self.package_url}/{package_id}/download"
142 data = requests.get(url).content
144 title = jsonResult[
"title"]
149 Download a package for a given material from the GPUOpen material database.
150 @param searchExpr: The regular expression to match the material name.
151 @param packageId: The package ID to download.
152 @return: A list of downloaded packages of the form:
157 if len(foundList) > 0:
158 for found
in foundList:
159 listNumber = found[
'listNumber']
160 materialNumber = found[
'materialNumber']
161 matName = found[
'title']
162 self.
logger.info(f
'> Download material: {matName} List: {listNumber}. Index: {materialNumber}')
163 result = [data, title] = self.
downloadPackage(listNumber, materialNumber, packageId)
164 downloadList.append(result)
169 Find materials by name.
170 @param materialName: Regular expression to match the material name.
171 @return: A list of materials that match the regular expression of the form:
172 [ { 'listNumber': listNumber, 'materialNumber': materialNumber, 'title': title } ]
181 for material
in materialList[
'results']:
182 if re.match(materialName, material[
'title'], re.IGNORECASE):
183 materialsList.append({
'listNumber': listNumber,
'materialNumber': materialNumber,
'title': material[
'title'] })
191 Update the material names from the material lists.
192 @return: List of material names. If no materials are loaded, then an empty list is returned.
199 for material
in materialList[
'results']:
206 Get the materials returned from the GPUOpen material database.
207 Will loop based on the linked-list of materials stored in the database.
208 Currently the batch size requested is 100 materials per batch.
209 @return: List of material lists
217 'accept':
'application/json'
227 haveMoreMaterials =
True
228 while (haveMoreMaterials):
230 response = requests.get(url, headers=headers, params=params)
232 if response.status_code == HTTPStatus.OK:
234 raw_response = response.text
237 json_strings = raw_response.split(
'}{')
239 json_result_string = json_strings[0]
240 jsonObject = json.loads(json_result_string)
244 nextQuery = jsonObject[
'next']
248 queryParts = nextQuery.split(
'?')
250 queryParts = queryParts[1].split(
'&')
252 limitParts = queryParts[0].split(
'=')
253 offsetParts = queryParts[1].split(
'=')
254 params[
'limit'] = int(limitParts[1])
255 params[
'offset'] = int(offsetParts[1])
256 self.
logger.info(f
'Fetch set of materials: limit: {params["limit"]} offset: {params["offset"]}')
258 haveMoreMaterials =
False
262 self.
logger.info(f
'Error: {response.status_code}, {response.text}')
268 Get the JSON strings for the materials
269 @return: List of JSON strings for the materials. One string per material batch.
276 results.append(json.dumps(material, indent=4, sort_keys=
True))
281 Load the materials from a set of JSON files downloaded from
282 the GPUOpen material database.
285 for fileName
in fileNames:
286 with open(fileName)
as f:
293 Write the materials to a set of MaterialX files.
294 @param folder: The folder to write the files to.
295 @param rootFileName: The root file name to use for the files.
296 @return: The number of files written.
303 os.makedirs(folder, exist_ok=
True)
306 fileName = rootFileName +
'_' + str(i) +
'.json'
307 materialFileName = os.path.join(folder, fileName)
308 self.
logger.info(f
'> Write material to file: "{materialFileName}"')
309 with open(materialFileName,
'w')
as f:
310 json.dump(material, f, indent=4, sort_keys=
True)
317 Write sorted list of the material names to a file in JSON format
318 @param fileName: The file name to write the material names to.
319 @param sort: If true, sort the material names.
324 with open(fileName,
'w')
as f:
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.
list getMaterials(self)
Get the materials returned from the GPUOpen material database.
int writeMaterialFiles(self, folder, rootFileName)
Write the materials to a set of MaterialX files.
bool writePackageDataToFile(self, data, outputFolder, title, unzipFile=True)
Write a package data to a file.
downloadPackageByExpression(self, searchExpr, packageId=0)
Download a package for a given material from the GPUOpen material database.
list findMaterialsByName(self, materialName)
Find materials by name.
downloadPackage(self, listNumber, materialNumber, packageId=0)
Download a package for a given material from the GPUOpen material database.
extractPackageData(self, data, pilImage)
Extract the package data from a zip file.
list getMaterialsAsJsonString(self)
Get the JSON strings for the materials.
list getMaterialNames(self)
Update the material names from the material lists.
list readMaterialFiles(self, fileNames)
Load the materials from a set of JSON files downloaded from the GPUOpen material database.