MaterialXMaterials 0.0.3
Utilities for retrieving materials from remote servers
Loading...
Searching...
No Matches
polyHavenLoader.py
Go to the documentation of this file.
1'''
2@file : polyHavenLoader.py
3@brief: A module to fetch MaterialX assets from PolyHaven API and download them.
4'''
5import requests
6import json
7from pathlib import Path
8import zipfile
9import logging
10
11
13 '''
14 A class to fetch MaterialX assets from PolyHaven API and download them.
15 '''
16 def __init__(self):
17 '''
18 Initialize the PolyHavenLoader with API endpoints and headers.
19 '''
20 self.BASE_API = "https://api.polyhaven.com"
21 self.ASSET_API = "https://api.polyhaven.com/assets"
22 self.INFO_API = "https://api.polyhaven.com/info"
23 self.FILES_API = "https://api.polyhaven.com/files"
24 self.HEADERS = {
25 "User-Agent": "MTLX_Polyaven_Loader/1.0", # Required by PolyHaven API
26 }
27
28 self.logger = logging.getLogger('PolyH')
29 logging.basicConfig(level=self.logger.info)
30
31 def fetch_materialx_assets(self, max_items=1, download_id=None):
32 '''
33 Fetch MaterialX assets from PolyHaven API and filter them by resolution.
34 @param resolution: The resolution of the MaterialX assets to fetch (e.g. "1k", "2k", "4k", "8k").
35 @return: A dictionary of MaterialX assets with their URLs and texture files.
36 '''
37 parameters = {
38 "type": "textures"
39 }
40
41 resp = requests.get(self.ASSET_API, headers=self.HEADERS, params=parameters)
42 resp.raise_for_status()
43 all_assets = resp.json()
44
45 materialx_assets = {}
46 filtered_polyhaven_assets = {}
47
48 item_count = 0;
49 for id, data in all_assets.items():
50
51 if download_id and id != download_id:
52 #self.logger.info(f"Skipping asset id: '{id}' (not matching {download_id})")
53 continue
54
55 # Get the thumbnail
56 thumbnail_url = data.get("thumbnail_url")
57
58 resp = requests.get(f"{self.FILES_API}/{id}", headers=self.HEADERS)
59 resp.raise_for_status()
60 files_data = resp.json()
61 #json_string = json.dumps(files_data, indent=4)
62
63 # Remove all keys other than "mtlx"
64 files_data = {k: v for k, v in files_data.items() if k == "mtlx"}
65
66 mtlx_files = files_data.get("mtlx", [])
67 if mtlx_files:
68
69 self.logger.info(f"Found MaterialX data for '{id}'")
70
71 # Look for 1K, 2K , 4K, and 8K versions
72 resolutions = {
73 "1k": None,
74 "2k": None,
75 "4k": None,
76 "8k": None
77 }
78 for resolution_key in resolutions.keys():
79 res = mtlx_files.get(resolution_key, None)
80 if not res:
81 continue
82
83 n_k_mtlx = res.get("mtlx")
84 texture_struct = {}
85 if n_k_mtlx:
86 #self.logger.info(f"Found MaterialX files for '{one_k}'")
87 include_files = n_k_mtlx.get("include", {})
88 #self.logger.info(f"Found include files for '{id}': {one_k}")
89 for path, data in include_files.items():
90 texture_url = data.get("url")
91 #self.logger.info("Texture path:", path, "URL:", texture_url)
92 texture_struct[path] = texture_url
93 mtlx_url = n_k_mtlx.get("url")
94 res_id = id + '___' + resolution_key
95 if mtlx_url:
96 materialx_assets[res_id] = {
97 "url": mtlx_url,
98 "texture_files": texture_struct,
99 "thumbnail_url": thumbnail_url
100 }
101 json_string = json.dumps(materialx_assets[res_id], indent=4)
102 #self.logger.info(f"Found MaterialX for '{res_id}': {json_string}")
103 #self.logger.info(f"Found MaterialX for '{res_id}'")
104 # Create folder poly_have_data
105
106 # Save asset data to JSON file
107 #with open(f"polyhaven_data/{id}_data.json", "w") as f:
108 # json.dump(asset_data, f, indent=4)
109 # self.logger.info(f"Saved asset data for '{id}' to polyhaven_data/{id}_data.json")
110
111 filtered_polyhaven_assets[id] = files_data
112
113 # Halt if download_id is specified and matches the current asset ID
114 if download_id == id:
115 break
116
117 # Halt if max_items is specified and reached
118 if not download_id and max_items:
119 item_count += 1
120 if item_count >= max_items:
121 break
122
123 #if "materialx" in formats:
124 # materialx_assets[slug] = formats["materialx"]
125
126 return materialx_assets, all_assets, filtered_polyhaven_assets
127
128 def download_asset(self, asset_list):
129 '''
130 Download MaterialX asset and its textures from PolyHaven.
131 e.g. asset_list = {'polystyrene': {'url': 'https://.../polystyrene.mtlx', 'texture_files': {...}}}
132
133 @param asset_list: A dictionary of MaterialX assets with their URLs and texture files.
134 @return: The ID of the downloaded asset, the MaterialX string, and a list of texture binaries.
135 '''
136 for id, asset in asset_list.items():
137 url = asset.get("url")
138 if not url:
139 self.logger.info(f"No MaterialX URL found for '{id}'")
140 continue
141
142 resp = requests.get(url, headers=self.HEADERS)
143 resp.raise_for_status()
144 mtlx_string = resp.text
145 self.logger.info(f"Download MaterialX document {url}, length: {len(mtlx_string)} characters")
146
147 texture_binaries = []
148 for path, texture_url in asset.get("texture_files", {}).items():
149 # Get texture files
150 self.logger.info(f"Download texture from {texture_url} ...")
151 texture_resp = requests.get(texture_url, headers=self.HEADERS)
152 texture_resp.raise_for_status()
153
154 ext = Path(path).suffix.lower()
155 name = Path(path).stem
156
157 if ext == ".exr":
158 self.logger.info(f" WARNING: EXR file present which may not be supported by MaterialX texture loader: {path}")
159 texture_binaries.append((path, texture_resp.content))
160
161 thumbnail_url = asset.get("thumbnail_url")
162 if thumbnail_url:
163 self.logger.info(f"Download thumbnail from {thumbnail_url} ...")
164 thumbnail_resp = requests.get(thumbnail_url, headers=self.HEADERS)
165 thumbnail_resp.raise_for_status()
166
167 clean_url = thumbnail_url
168 # Strip any ? or # from the URL
169 clean_url = clean_url.split('?')[0].split('#')[0]
170 clean_url = clean_url.split('/')[-1] # Get the last part of the URL
171 extension = Path(clean_url).suffix.lower()
172 texture_binaries.append((f"{id}_thumbnail.{extension}", thumbnail_resp.content))
173
174 return id, mtlx_string, texture_binaries
175
176 def save_materialx_with_textures(self, id, mtlx_string, texture_binaries, data_folder):
177 ''''
178 Save MaterialX string and texture binaries to a zip file.'
179 @param id: The ID of the MaterialX asset.
180 @param mtlx_string: The MaterialX string content.
181 @param texture_binaries: A list of tuples containing texture file paths and their binary content.
182 @param data_folder: The folder to save the zip file.
183 '''
184 # Create a zip file with MaterialX and textures
185 filename = f"{id}_materialx.zip"
186 filename = Path(data_folder) / filename
187 with zipfile.ZipFile(filename, "w") as zipf:
188 # Write MaterialX file
189 zipf.writestr(f"{id}.mtlx", mtlx_string)
190 # Write texture files
191 for path, content in texture_binaries:
192 zipf.writestr(path, content)
193 self.logger.info(f"Saved zip: {filename}")
194
A class to fetch MaterialX assets from PolyHaven API and download them.
__init__(self)
Initialize the PolyHavenLoader with API endpoints and headers.
download_asset(self, asset_list)
Download MaterialX asset and its textures from PolyHaven.
save_materialx_with_textures(self, id, mtlx_string, texture_binaries, data_folder)
' Save MaterialX string and texture binaries to a zip file.
fetch_materialx_assets(self, max_items=1, download_id=None)
Fetch MaterialX assets from PolyHaven API and filter them by resolution.