MaterialXMaterials 1.39.5
Utilities for retrieving materials from remote servers
Loading...
Searching...
No Matches
jsPolyHavenLoader.js
1
10 this.baseUrl = "https://api.polyhaven.com";
11 this.userAgent = "MTLX_Polyhaven_Loader/1.0";
12 }
13
19 try {
20 const response = await fetch(`${this.baseUrl}/assets?t=textures`, {
21 headers: { "User-Agent": this.userAgent }
22 });
23
24 if (!response.ok) {
25 throw new Error(`HTTP error! status: ${response.status}`);
26 }
27
28 const data = await response.json();
29 return this.processMaterialsData(data);
30 } catch (error) {
31 console.error('Error fetching materials:', error);
32 throw error;
33 }
34 }
35
42 const materials = [];
43 const categories = new Set();
44
45 for (const [id, materialData] of Object.entries(rawData)) {
46 // Skip materials without required data
47 if (!materialData.name || !materialData.categories) continue;
48
49 // Process authors
50 let authorsList = 'Author(s): ';
51 const authors = materialData.authors || {};
52 const authorNames = Object.keys(authors);
53 authorsList += authorNames.join(', ');
54
55 let max_resolution = materialData.max_resolution
56 let maxresolutionString = '';
57 if (max_resolution) {
58 maxresolutionString = `${max_resolution[0]} x ${max_resolution[1]}`
59 }
60
61 const material = {
62 id,
63 name: materialData.name,
64 description: authorsList,
65 categories: materialData.categories,
66 tags: materialData.tags || [],
67 thumb_url: `https://cdn.polyhaven.com/asset_img/thumbs/${id}.png?width=512`,
68 maps: materialData.maxresolutionString || {}
69 };
70
71 materials.push(material);
72
73 // Collect categories
74 material.categories.forEach(cat => categories.add(cat));
75 }
76
77 return { materials, categories };
78 }
79
85 async fetchMaterialFiles(materialId) {
86 try {
87 const response = await fetch(`${this.baseUrl}/files/${materialId}`, {
88 headers: { "User-Agent": this.userAgent }
89 });
90
91 if (!response.ok) {
92 throw new Error(`Failed to fetch MaterialX data for ${materialId}`);
93 }
94
95 return await response.json();
96 } catch (error) {
97 console.error(`Error fetching material files for ${materialId}:`, error);
98 throw error;
99 }
100 }
101
108 try {
109 const response = await fetch(url, {
110 headers: { "User-Agent": this.userAgent }
111 });
112
113 if (!response.ok) {
114 throw new Error('Failed to download MaterialX file');
115 }
116
117 return await response.text();
118 } catch (error) {
119 console.error('Error downloading MaterialX content:', error);
120 throw error;
121 }
122 }
123
129 async downloadTexture(url) {
130 try {
131 const response = await fetch(url, {
132 headers: { "User-Agent": this.userAgent }
133 });
134
135 if (!response.ok) {
136 throw new Error(`Failed to download texture from ${url}`);
137 }
138
139 return await response.blob();
140 } catch (error) {
141 console.error(`Error downloading texture from ${url}:`, error);
142 throw error;
143 }
144 }
145
151 async downloadThumbnail(thumbnailUrl) {
152 try {
153 const response = await fetch(thumbnailUrl, {
154 headers: { "User-Agent": this.userAgent }
155 });
156
157 if (!response.ok) {
158 throw new Error('Failed to download thumbnail');
159 }
160
161 return await response.blob();
162 } catch (error) {
163 console.error('Error downloading thumbnail:', error);
164 throw error;
165 }
166 }
167
174 async createMaterialXPackage(material, resolution) {
175 try {
176 // Fetch MaterialX files data
177 const filesData = await this.fetchMaterialFiles(material.id);
178 const mtlxData = filesData.mtlx?.[resolution]?.mtlx;
179
180 if (!mtlxData) {
181 throw new Error(`No MaterialX files found for ${resolution} resolution`);
182 }
183
184 // Create ZIP file
185 const zip = new JSZip();
186
187 // 1. Download and add the main MaterialX file
188 const mtlxContent = await this.downloadMaterialXContent(mtlxData.url);
189 zip.file(`${material.id}.mtlx`, mtlxContent);
190 console.log(`Added MaterialX file to ZIP: ${material.id}.mtlx, ${mtlxContent}`);
191
192 // 2. Download and add all included texture files
193 const textureFiles = mtlxData.include || {};
194 const texturePromises = Object.entries(textureFiles).map(async ([path, fileData]) => {
195 try {
196 const textureBlob = await this.downloadTexture(fileData.url);
197
198 // Maintain the folder structure from the include paths
199 const pathParts = path.split('/');
200 let currentFolder = zip;
201
202 // Handle nested folder structure
203 for (let i = 0; i < pathParts.length - 1; i++) {
204 const folderName = pathParts[i];
205 currentFolder = currentFolder.folder(folderName);
206 }
207
208 currentFolder.file(pathParts[pathParts.length - 1], textureBlob);
209
210 if (path.toLowerCase().endsWith('.exr')) {
211 console.warn(`EXR file present which may not be supported by MaterialX texture loader: ${path}`);
212 }
213 else {
214 console.log(`Added texture to ZIP: ${path}`);
215 }
216 } catch (error) {
217 console.error(`Error downloading texture ${path}:`, error);
218 // Add placeholder file if download fails
219 zip.file(path, `Failed to download: ${fileData.url}`);
220 }
221 });
222
223 // 3. Download and add thumbnail
224 if (material.thumb_url) {
225 try {
226 const thumbBlob = await this.downloadThumbnail(material.thumb_url);
227 const thumbUrl = new URL(material.thumb_url);
228 const thumbPath = thumbUrl.pathname.split('/').pop();
229 const thumbExt = thumbPath.split('.').pop();
230 zip.file(`${material.id}_thumbnail.${thumbExt}`, thumbBlob);
231 } catch (error) {
232 console.error('Error downloading thumbnail:', error);
233 }
234 }
235
236 // Wait for all downloads to complete
237 await Promise.all(texturePromises);
238
239 // Add README file
240 zip.file("README.txt",
241 `Material: ${material.name}\n` +
242 `Resolution: ${resolution}\n` +
243 `Source: https://polyhaven.com/a/${material.id}\n` +
244 `Downloaded: ${new Date().toISOString()}\n\n` +
245 `Contains the following files:\n` +
246 `- ${material.id}.mtlx\n` +
247 Object.keys(textureFiles).map(path => `- ${path}`).join('\n') +
248 (material.thumb_url ? `\n- ${material.id}_thumbnail.png` : '')
249 );
250
251 // Generate the ZIP file
252 return await zip.generateAsync({ type: 'blob' });
253
254 } catch (error) {
255 console.error('Error creating MaterialX package:', error);
256 throw error;
257 }
258 }
259
266 async getMaterialContent(materialId, resolution) {
267 try {
268 const filesData = await this.fetchMaterialFiles(materialId);
269 const mtlxData = filesData.mtlx?.[resolution]?.mtlx;
270
271 if (!mtlxData) {
272 throw new Error(`No MaterialX files found for ${resolution} resolution`);
273 }
274
275 // Get MaterialX content
276 const mtlxContent = await this.downloadMaterialXContent(mtlxData.url);
277
278 return {
279 mtlxContent,
280 textureFiles: mtlxData.include || {}
281 };
282 } catch (error) {
283 console.error('Error getting material content:', error);
284 throw error;
285 }
286 }
287}
288
289// Export for use in other scripts
290if (typeof module !== 'undefined' && module.exports) {
291 module.exports = JsPolyHavenAPILoader;
292} else if (typeof window !== 'undefined') {
293 window.JsPolyHavenAPILoader = JsPolyHavenAPILoader;
294}
JsPolyHavenAPILoader - A JavaScript class for interacting with the Poly Haven API Handles fetching ma...
async getMaterialContent(materialId, resolution)
Get MaterialX content and texture files for preview.
async downloadMaterialXContent(url)
Download MaterialX content from URL.
async downloadThumbnail(thumbnailUrl)
Download thumbnail image.
processMaterialsData(rawData)
Process raw materials data from API into structured format.
constructor()
Constructor for JsPolyHavenAPILoader.
async fetchMaterials()
Fetch all materials from Poly Haven API.
async createMaterialXPackage(material, resolution)
Create a complete MaterialX package with all textures.
async fetchMaterialFiles(materialId)
Fetch MaterialX files data for a specific material.
async downloadTexture(url)
Download a texture file from URL.