MaterialXWeb 1.39.5
Utilities for using MaterialX Packages with Web clients
Loading...
Searching...
No Matches
JsGPUOpenMaterialLoader.js
1
8
9
10// Use global fetch if available (Node.js v18+), otherwise use node-fetch
11const fetch =
12 typeof globalThis.fetch === 'function'
13 ? globalThis.fetch
14 : (...args) => import('node-fetch').then(({ default: fetch }) => fetch(...args));
15const fs = require('fs');
16const MATERIALS_CACHE_FILE = 'gpuopen_materials.json';
17const MATERIALS_PREVIEW_FILE = 'gpuopen_previews.json';
18
23
25 if (JsGPUOpenMaterialLoader.instance) {
26 return JsGPUOpenMaterialLoader.instance;
27 }
28
29 this.rootUrl = 'https://api.matlib.gpuopen.com/api';
30 this.url = `${this.rootUrl}/materials`;
31 this.packageUrl = `${this.rootUrl}/packages`;
32 this.materials = null;
33 this.materialNames = null;
34 this.previews = null;
35
36 this.logger = console;
37
38 // Cache the instance
39 JsGPUOpenMaterialLoader.instance = this;
40 }
41
42 loadMaterialsFromCache() {
43 if (fs.existsSync(MATERIALS_CACHE_FILE)) {
44 try {
45 const data = fs.readFileSync(MATERIALS_CACHE_FILE, 'utf8');
46 this.materials = JSON.parse(data);
47 // Rebuild materialNames
48 this.materialNames = [];
49 for (const jsonData of this.materials) {
50 for (const material of jsonData.results) {
51 this.materialNames.push(material['title']);
52 }
53 }
54 this.logger.info(`Loaded GPUOpen materials from cache: ${MATERIALS_CACHE_FILE}`);
55 } catch (e) {
56 this.logger.warn(`Failed to load GPUOpen materials cache: ${e.message}`);
57 }
58 }
59 if (fs.existsSync(MATERIALS_PREVIEW_FILE)) {
60 try {
61 const data = fs.readFileSync(MATERIALS_PREVIEW_FILE, 'utf8');
62 this.previews = JSON.parse(data);
63 this.logger.info(`Loaded GPUOpen material previews from cache: ${MATERIALS_PREVIEW_FILE}`);
64 } catch (e) {
65 this.logger.warn(`Failed to load GPUOpen material previews cache: ${e.message}`);
66 }
67 }
68 }
69
75 // Save to cache file after fetching
76 try {
77 fs.writeFileSync(MATERIALS_CACHE_FILE, JSON.stringify(this.materials, null, 2));
78 this.logger.info(`Saved GPUOpen materials to cache: ${MATERIALS_CACHE_FILE}`);
79 } catch (e) {
80 this.logger.warn(`Failed to write GPUOpen materials cache: ${e.message}`);
81 }
82 return this.materials;
83 }
84
90 // Save to cache file after fetching
91 try {
92 fs.writeFileSync(MATERIALS_PREVIEW_FILE, JSON.stringify(this.previews, null, 2));
93 this.logger.info(`Saved GPUOpen material previews to cache: ${MATERIALS_PREVIEW_FILE}`);
94 } catch (e) {
95 this.logger.warn(`Failed to write GPUOpen material previews cache: ${e.message}`);
96 }
97 return this.previews;
98 }
99
105 return this.materialNames;
106 }
107
108 async getPreviews() {
109
110 // try load previews from cache file
111 if (fs.existsSync(MATERIALS_PREVIEW_FILE)) {
112 try {
113 const data = fs.readFileSync(MATERIALS_PREVIEW_FILE, 'utf8');
114 //console.log('preview data', data);
115 this.previews = JSON.parse(data);
116 this.logger.info(`Loaded GPUOpen material previews from cache 2: ${MATERIALS_PREVIEW_FILE}`);
117 //this.logger.info(`GPUOpen material previews: ${this.previews}`);
118 return this.previews;
119 } catch (e) {
120 this.logger.warn(`Failed to load GPUOpen material previews cache: ${e.message}`);
121 }
122 }
123 else {
124 this.logger.info(`No GPUOpen material previews cache found.`);
125 this.previews = [];
126 }
127 return this.previews;
128 }
129
135 async getMaterials(batchSize = 50) {
136 // 1. Try to load from cache file
137 if (fs.existsSync(MATERIALS_CACHE_FILE)) {
138 try {
139 const data = fs.readFileSync(MATERIALS_CACHE_FILE, 'utf8');
140 this.materials = JSON.parse(data);
141 // Rebuild materialNames
142 this.materialNames = [];
143 for (const jsonData of this.materials) {
144 for (const material of jsonData.results) {
145 this.materialNames.push(material['title']);
146 }
147 }
148 this.logger.info(`Loaded GPUOpen materials from cache 2: ${MATERIALS_CACHE_FILE}`);
149 return this.materials;
150 } catch (e) {
151 this.logger.warn(`Failed to load GPUOpen materials cache: ${e.message}`);
152 }
153 }
154
155 // 2. If not in cache, fetch from network
156 this.materials = [];
157 this.materialNames = [];
158
159 let url = this.url;
160 let params = new URLSearchParams({
161 limit: batchSize,
162 offset: 0
163 });
164 if (params) {
165 url += '?' + params.toString();
166 }
167
168 let haveMoreMaterials = true;
169 while (haveMoreMaterials) {
170 try {
171 console.log('Fetch materials from url:', url);
172 const response = await fetch(url);
173 if (!response.ok) {
174 throw new Error(`HTTP error! status: ${response.status}`);
175 }
176 const jsonData = await response.json();
177 this.materials.push(jsonData);
178 for (const material of jsonData.results) {
179 this.materialNames.push(material['title']);
180 }
181 let nextURL = jsonData.next;
182 if (nextURL) {
183 url = nextURL;
184 haveMoreMaterials = true;
185 } else {
186 console.log('Finished fetching materials');
187 haveMoreMaterials = false;
188 break;
189 }
190 } catch (error) {
191 this.logger.info(`Error: ${error.message}`);
192 haveMoreMaterials = true;
193 }
194 }
195 // Save to cache file after fetching
196 try {
197 fs.writeFileSync(MATERIALS_CACHE_FILE, JSON.stringify(this.materials, null, 2));
198 this.logger.info(`Saved GPUOpen materials to cache: ${MATERIALS_CACHE_FILE}`);
199 } catch (e) {
200 this.logger.warn(`Failed to write GPUOpen materials cache: ${e.message}`);
201 }
202 return this.materials;
203 }
204
205 async downloadPackage(listNumber, materialNumber, packageId = 0) {
206 if (this.materials === null || this.materials.length === 0) {
207 return [null, null];
208 }
209
210 const jsonData = this.materials[listNumber];
211 if (!jsonData) {
212 return [null, null];
213 }
214
215 let jsonResults = null;
216 let jsonResult = null;
217 if ('results' in jsonData) {
218 jsonResults = jsonData['results'];
219 if (jsonResults.length <= materialNumber) {
220 return [null, null];
221 } else {
222 jsonResult = jsonResults[materialNumber];
223 }
224 }
225
226 if (!jsonResult) {
227 return [null, null];
228 }
229
230 let jsonPackages = null;
231 if ('packages' in jsonResult) {
232 jsonPackages = jsonResult['packages'];
233 }
234 if (!jsonPackages) {
235 return [null, null];
236 }
237
238 if (jsonPackages.length <= packageId) {
239 return [null, null];
240 }
241 const packageIdValue = jsonPackages[packageId];
242
243 if (!packageIdValue) {
244 return [null, null];
245 }
246
247 const url = `${this.packageUrl}/${packageIdValue}/download`
248 const response = await fetch(url);
249 const data = await response.arrayBuffer();
250 const title = jsonResult['title'];
251
252 console.log(`- Loader: Downloaded package: ${title} from ${url}`);
253 return [data, title];
254 }
255
262 findMaterialsByName(materialName)
263 {
264 if (!this.materials) {
265 return [];
266 }
267
268 const materialsList = [];
269 let listNumber = 0;
270
271 // Create a RegExp object for case-insensitive matching
272 const regex = new RegExp(materialName, 'i');
273
274 this.materials.forEach((materialList, materialIndex) => {
275 let materialNumber = 0;
276
277 materialList['results'].forEach((material) => {
278
279 //console.log('testing material:', material['title'], ' with regex:', regex)
280 if (regex.test(material['title'])) {
281 materialsList.push({
282 listNumber: listNumber,
283 materialNumber: materialNumber,
284 title: material['title'],
285 });
286 }
287 materialNumber += 1;
288 });
289
290 listNumber += 1;
291 });
292
293 return materialsList;
294 }
295
304 async downloadPackageByExpression(searchExpr, packageId = 0) {
305 const downloadList = [];
306
307 const foundList = this.findMaterialsByName(searchExpr);
308 if (foundList.length > 0) {
309 for (const found of foundList) {
310 const listNumber = found['listNumber'];
311 const materialNumber = found['materialNumber'];
312 const matName = found['title'];
313 this.logger.info(`> Download material: ${matName} List: ${listNumber}. Index: ${materialNumber}`);
314 const [data, title] = await this.downloadPackage(listNumber, materialNumber, packageId);
315 downloadList.push([data, title]);
316 }
317 }
318 return downloadList;
319 }
320}
321
322// Export a singleton instance of the class
323module.exports = new JsGPUOpenMaterialLoader();
324
Class to download MaterialX materials from the GPUOpen material database.
async getMaterials(batchSize=50)
Get lists of materials from the GPUOpen material database.
findMaterialsByName(materialName)
Find materials by name.
getMaterialList()
Return downloaded material list.
getMaterialNames()
Return downloaded material names.
constructor()
Constructor for the JsGPUOpenMaterialLoader class.
async downloadPackageByExpression(searchExpr, packageId=0)
Download a material package by string expression.
getMaterialPreviews()
Return downloaded material previews.