@needle-tools/usd 0.0.1-412624
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/README.md +100 -0
- package/examples/index.html +58 -0
- package/examples/package-lock.json +1548 -0
- package/examples/package.json +24 -0
- package/examples/public/HttpReferences copy.usda +46 -0
- package/examples/public/HttpReferences.usda +44 -0
- package/examples/public/gingerbread/GingerbreadHouse.usda +35 -0
- package/examples/public/gingerbread/house/GingerBreadHouse.usdc +0 -0
- package/examples/public/gingerbread/house/textures/color.jpg +0 -0
- package/examples/public/gingerbread/house/textures/metallic_roughness.jpg +0 -0
- package/examples/public/gingerbread/house/textures/normal.jpg +0 -0
- package/examples/public/gingerbread/snowman/Snowman.usdc +0 -0
- package/examples/public/gingerbread/snowman/textures/color.jpg +0 -0
- package/examples/public/gingerbread/snowman/textures/metallic_roughness.jpg +0 -0
- package/examples/public/gingerbread/snowman/textures/normal.jpg +0 -0
- package/examples/public/test.usdz +0 -0
- package/examples/public/vite.svg +1 -0
- package/examples/src/fileHandling.ts +256 -0
- package/examples/src/main.ts +167 -0
- package/examples/src/three.ts +140 -0
- package/examples/src/vite-env.d.ts +1 -0
- package/examples/tsconfig.json +23 -0
- package/examples/vite.config.js +21 -0
- package/package.json +50 -0
- package/src/bindings/.gitattributes +2 -0
- package/src/bindings/emHdBindings.data +19331 -0
- package/src/bindings/emHdBindings.js +12227 -0
- package/src/bindings/emHdBindings.wasm +0 -0
- package/src/bindings/emHdBindings.worker.js +124 -0
- package/src/bindings/index.js +124 -0
- package/src/create.three.js +252 -0
- package/src/hydra/ThreeJsRenderDelegate.js +872 -0
- package/src/hydra/consoleRenderDelegate.js +47 -0
- package/src/hydra/index.js +5 -0
- package/src/index.d.ts +1 -0
- package/src/index.js +5 -0
- package/src/plugins/index.js +2 -0
- package/src/plugins/needle.js +158 -0
- package/src/types/bindings.d.ts +82 -0
- package/src/types/create.three.d.ts +65 -0
- package/src/types/hydra.d.ts +32 -0
- package/src/types/index.d.ts +5 -0
- package/src/types/plugins.d.ts +9 -0
- package/src/types/vite.d.ts +19 -0
- package/src/utils.js +24 -0
- package/src/vite/index.js +22 -0
|
@@ -0,0 +1,872 @@
|
|
|
1
|
+
import { TextureLoader, BufferGeometry, MeshPhysicalMaterial, DoubleSide, Color, Mesh, Float32BufferAttribute, SRGBColorSpace, RGBAFormat, RepeatWrapping, LinearSRGBColorSpace, Vector2 } from 'three';
|
|
2
|
+
import { TGALoader } from 'three/addons/loaders/TGALoader.js';
|
|
3
|
+
import { EXRLoader } from 'three/addons/loaders/EXRLoader.js';
|
|
4
|
+
|
|
5
|
+
const debugTextures = false;
|
|
6
|
+
const debugMaterials = false;
|
|
7
|
+
const debugMeshes = false;
|
|
8
|
+
const debugPrims = false;
|
|
9
|
+
const disableTextures = false;
|
|
10
|
+
const disableMaterials = false;
|
|
11
|
+
|
|
12
|
+
class TextureRegistry {
|
|
13
|
+
/**
|
|
14
|
+
* @param {import('..').threeJsRenderDelegateConfig} config
|
|
15
|
+
*/
|
|
16
|
+
constructor(config) {
|
|
17
|
+
this.config = config;
|
|
18
|
+
this.allPaths = config.paths;
|
|
19
|
+
this.textures = [];
|
|
20
|
+
this.loader = new TextureLoader();
|
|
21
|
+
this.tgaLoader = new TGALoader();
|
|
22
|
+
this.exrLoader = new EXRLoader();
|
|
23
|
+
|
|
24
|
+
// HACK get URL ?file parameter again
|
|
25
|
+
let urlParams = new URLSearchParams(window.location.search);
|
|
26
|
+
let fileParam = urlParams.get('file');
|
|
27
|
+
if (fileParam) {
|
|
28
|
+
let lastSlash = fileParam.lastIndexOf('/');
|
|
29
|
+
if (lastSlash >= 0)
|
|
30
|
+
fileParam = fileParam.substring(0, lastSlash);
|
|
31
|
+
this.baseUrl = fileParam;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
getTexture(resourcePath) {
|
|
36
|
+
if (debugTextures) console.log("get texture", resourcePath);
|
|
37
|
+
if (this.textures[resourcePath]) {
|
|
38
|
+
return this.textures[resourcePath];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let textureResolve, textureReject;
|
|
42
|
+
this.textures[resourcePath] = new Promise((resolve, reject) => {
|
|
43
|
+
textureResolve = resolve;
|
|
44
|
+
textureReject = reject;
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
if (!resourcePath) {
|
|
48
|
+
return Promise.reject(new Error('Empty resource path for file: ' + resourcePath));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let filetype = undefined;
|
|
52
|
+
let lowercaseFilename = resourcePath.toLowerCase();
|
|
53
|
+
if (lowercaseFilename.indexOf('.png') >= lowercaseFilename.length - 5) {
|
|
54
|
+
filetype = 'image/png';
|
|
55
|
+
} else if (lowercaseFilename.indexOf('.jpg') >= lowercaseFilename.length - 5) {
|
|
56
|
+
filetype = 'image/jpeg';
|
|
57
|
+
} else if (lowercaseFilename.indexOf('.jpeg') >= lowercaseFilename.length - 5) {
|
|
58
|
+
filetype = 'image/jpeg';
|
|
59
|
+
} else if (lowercaseFilename.indexOf('.exr') >= lowercaseFilename.length - 4) {
|
|
60
|
+
console.warn("EXR textures are not fully supported yet", resourcePath);
|
|
61
|
+
// using EXRLoader explicitly
|
|
62
|
+
filetype = 'image/x-exr';
|
|
63
|
+
} else if (lowercaseFilename.indexOf('.tga') >= lowercaseFilename.length - 4) {
|
|
64
|
+
console.warn("TGA textures are not fully supported yet", resourcePath);
|
|
65
|
+
// using TGALoader explicitly
|
|
66
|
+
filetype = 'image/tga';
|
|
67
|
+
} else {
|
|
68
|
+
console.error("Error when loading texture: unknown filetype", resourcePath);
|
|
69
|
+
// throw new Error('Unknown filetype');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
this.config.driver().getFile(resourcePath, async (loadedFile) => {
|
|
73
|
+
let loader = this.loader;
|
|
74
|
+
if (filetype === 'image/tga')
|
|
75
|
+
loader = this.tgaLoader;
|
|
76
|
+
else if (filetype === 'image/x-exr')
|
|
77
|
+
loader = this.exrLoader;
|
|
78
|
+
|
|
79
|
+
const baseUrl = this.baseUrl;
|
|
80
|
+
function loadFromFile(_loadedFile) {
|
|
81
|
+
let url = undefined;
|
|
82
|
+
if (debugTextures) console.log("window.driver.getFile", resourcePath, " => ", _loadedFile);
|
|
83
|
+
if (_loadedFile) {
|
|
84
|
+
let blob = new Blob([_loadedFile.slice(0)], { type: filetype });
|
|
85
|
+
url = URL.createObjectURL(blob);
|
|
86
|
+
} else {
|
|
87
|
+
if (baseUrl)
|
|
88
|
+
url = baseUrl + '/' + resourcePath;
|
|
89
|
+
else
|
|
90
|
+
url = resourcePath;
|
|
91
|
+
}
|
|
92
|
+
if (debugTextures) console.log("Loading texture from", url, "with loader", loader, "_loadedFile", _loadedFile, "baseUrl", baseUrl, "resourcePath", resourcePath);
|
|
93
|
+
// Load the texture
|
|
94
|
+
loader.load(
|
|
95
|
+
// resource URL
|
|
96
|
+
url,
|
|
97
|
+
|
|
98
|
+
// onLoad callback
|
|
99
|
+
(texture) => {
|
|
100
|
+
texture.name = resourcePath;
|
|
101
|
+
textureResolve(texture);
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
// onProgress callback currently not used
|
|
105
|
+
undefined,
|
|
106
|
+
|
|
107
|
+
// onError callback
|
|
108
|
+
(err) => {
|
|
109
|
+
textureReject(err);
|
|
110
|
+
}
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (!loadedFile) {
|
|
115
|
+
// if the file is not part of the filesystem, we can still try to fetch it from the network
|
|
116
|
+
if (baseUrl) {
|
|
117
|
+
console.log("File not found in filesystem, trying to fetch", resourcePath);
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
textureReject(new Error('Unknown file: ' + resourcePath));
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
loadFromFile(loadedFile);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
return this.textures[resourcePath];
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
class HydraMesh {
|
|
133
|
+
/**
|
|
134
|
+
* @param {string} id
|
|
135
|
+
* @param {ThreeRenderDelegateInterface} hydraInterface
|
|
136
|
+
*/
|
|
137
|
+
constructor(id, hydraInterface) {
|
|
138
|
+
this._geometry = new BufferGeometry();
|
|
139
|
+
this._id = id;
|
|
140
|
+
this._interface = hydraInterface;
|
|
141
|
+
this._points = undefined;
|
|
142
|
+
this._normals = undefined;
|
|
143
|
+
this._colors = undefined;
|
|
144
|
+
this._uvs = undefined;
|
|
145
|
+
this._indices = undefined;
|
|
146
|
+
this._materials = [];
|
|
147
|
+
|
|
148
|
+
let material = new MeshPhysicalMaterial({
|
|
149
|
+
side: DoubleSide,
|
|
150
|
+
color: new Color(0xB4B4B4),
|
|
151
|
+
// envMap: hydraInterface.config.envMap,
|
|
152
|
+
});
|
|
153
|
+
this._materials.push(material);
|
|
154
|
+
this._mesh = new Mesh(this._geometry, material);
|
|
155
|
+
this._mesh.castShadow = true;
|
|
156
|
+
this._mesh.receiveShadow = true;
|
|
157
|
+
|
|
158
|
+
// ID can contain paths, we strip those here
|
|
159
|
+
let _name = id;
|
|
160
|
+
let lastSlash = _name.lastIndexOf('/');
|
|
161
|
+
if (lastSlash >= 0) {
|
|
162
|
+
_name = _name.substring(lastSlash + 1);
|
|
163
|
+
}
|
|
164
|
+
this._mesh.name = _name;
|
|
165
|
+
|
|
166
|
+
// console.log("Creating HydraMesh: " + id + " -> " + _name);
|
|
167
|
+
|
|
168
|
+
hydraInterface.config.usdRoot.add(this._mesh); // FIXME
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
updateOrder(attribute, attributeName, dimension = 3) {
|
|
172
|
+
if (debugMeshes) console.log("updateOrder", attribute, attributeName, dimension);
|
|
173
|
+
if (attribute && this._indices) {
|
|
174
|
+
let values = [];
|
|
175
|
+
for (let i = 0; i < this._indices.length; i++) {
|
|
176
|
+
let index = this._indices[i]
|
|
177
|
+
for (let j = 0; j < dimension; ++j) {
|
|
178
|
+
values.push(attribute[dimension * index + j]);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
this._geometry.setAttribute(attributeName, new Float32BufferAttribute(values, dimension));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
updateIndices(indices) {
|
|
186
|
+
if (debugMeshes) console.log("updateIndices", indices);
|
|
187
|
+
this._indices = [];
|
|
188
|
+
for (let i = 0; i < indices.length; i++) {
|
|
189
|
+
this._indices.push(indices[i]);
|
|
190
|
+
}
|
|
191
|
+
//this._geometry.setIndex( indicesArray );
|
|
192
|
+
this.updateOrder(this._points, 'position');
|
|
193
|
+
this.updateOrder(this._normals, 'normal');
|
|
194
|
+
if (this._colors) {
|
|
195
|
+
this.updateOrder(this._colors, 'color');
|
|
196
|
+
}
|
|
197
|
+
if (this._uvs) {
|
|
198
|
+
this.updateOrder(this._uvs, 'uv', 2);
|
|
199
|
+
this._geometry.attributes.uv2 = this._geometry.attributes.uv;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Sets the transform of the mesh.
|
|
205
|
+
* @param {Iterable<number>} matrix - The 4x4 matrix to set on the mesh.
|
|
206
|
+
*/
|
|
207
|
+
setTransform(matrix) {
|
|
208
|
+
this._mesh.matrix.set(...matrix);
|
|
209
|
+
this._mesh.matrix.transpose();
|
|
210
|
+
this._mesh.matrixAutoUpdate = false;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Sets automatically generated normals on the mesh. Should only be used if there are no authored normals.
|
|
215
|
+
* @param {} normals
|
|
216
|
+
*/
|
|
217
|
+
updateNormals(normals) {
|
|
218
|
+
// don't apply automatically generated normals if there are already authored normals.
|
|
219
|
+
if (this._geometry.hasAttribute('normal')) return;
|
|
220
|
+
|
|
221
|
+
this._normals = normals.slice(0);
|
|
222
|
+
this.updateOrder(this._normals, 'normal');
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
setNormals(data, interpolation) {
|
|
226
|
+
if (interpolation === 'facevarying') {
|
|
227
|
+
// The UV buffer has already been prepared on the C++ side, so we just set it
|
|
228
|
+
this._geometry.setAttribute('normal', new Float32BufferAttribute(data, 3));
|
|
229
|
+
} else if (interpolation === 'vertex') {
|
|
230
|
+
// We have per-vertex UVs, so we need to sort them accordingly
|
|
231
|
+
this._normals = data.slice(0);
|
|
232
|
+
this.updateOrder(this._normals, 'normal');
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// This is always called before prims are updated
|
|
237
|
+
setMaterial(materialId) {
|
|
238
|
+
if (debugMaterials) console.log('Setting material on hydra prim: ' + materialId, this._mesh, materialId, this._interface.materials[materialId]);
|
|
239
|
+
if (this._interface.materials[materialId]) {
|
|
240
|
+
this._mesh.material = this._interface.materials[materialId]._material;
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
console.error("Material not found", materialId, this._interface.materials);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
setGeomSubsetMaterial(sections) {
|
|
248
|
+
//console.log("setting subset material: ", this._id, sections)
|
|
249
|
+
|
|
250
|
+
for (let i = 0; i < sections.length; i++) {
|
|
251
|
+
const section = sections[i];
|
|
252
|
+
if (this._interface.materials[section.materialId]) {
|
|
253
|
+
this._materials.push(this._interface.materials[section.materialId]._material);
|
|
254
|
+
this._geometry.addGroup(section.start, section.length, i + 1);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
this._mesh = new Mesh(this._geometry, this._materials);
|
|
259
|
+
this._interface.config.usdRoot.add(this._mesh);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
setDisplayColor(data, interpolation) {
|
|
263
|
+
if (disableMaterials) return;
|
|
264
|
+
|
|
265
|
+
let wasDefaultMaterial = false;
|
|
266
|
+
if (this._mesh.material === defaultMaterial) {
|
|
267
|
+
this._mesh.material = this._mesh.material.clone();
|
|
268
|
+
wasDefaultMaterial = true;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
this._colors = null;
|
|
272
|
+
|
|
273
|
+
if (interpolation === 'constant') {
|
|
274
|
+
this._mesh.material.color = new Color().fromArray(data);
|
|
275
|
+
} else if (interpolation === 'vertex') {
|
|
276
|
+
// Per-vertex buffer attribute
|
|
277
|
+
this._mesh.material.vertexColors = true;
|
|
278
|
+
if (wasDefaultMaterial) {
|
|
279
|
+
// Reset the pink debugging color
|
|
280
|
+
this._mesh.material.color = new Color(0xffffff);
|
|
281
|
+
}
|
|
282
|
+
this._colors = data.slice(0);
|
|
283
|
+
this.updateOrder(this._colors, 'color');
|
|
284
|
+
} else {
|
|
285
|
+
if (warningMessagesToCount.has(interpolation)) {
|
|
286
|
+
warningMessagesToCount.set(interpolation, warningMessagesToCount.get(interpolation) + 1);
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
warningMessagesToCount.set(interpolation, 1);
|
|
290
|
+
console.warn(`Unsupported displayColor interpolation type '${interpolation}'.`);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
setUV(data, dimension, interpolation) {
|
|
296
|
+
// TODO: Support multiple UVs. For now, we simply set uv = uv2, which is required when a material has an aoMap.
|
|
297
|
+
this._uvs = null;
|
|
298
|
+
|
|
299
|
+
if (interpolation === 'facevarying') {
|
|
300
|
+
// The UV buffer has already been prepared on the C++ side, so we just set it
|
|
301
|
+
this._geometry.setAttribute('uv', new Float32BufferAttribute(data, dimension));
|
|
302
|
+
} else if (interpolation === 'vertex') {
|
|
303
|
+
// We have per-vertex UVs, so we need to sort them accordingly
|
|
304
|
+
this._uvs = data.slice(0);
|
|
305
|
+
this.updateOrder(this._uvs, 'uv', 2);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (this._geometry.hasAttribute('uv'))
|
|
309
|
+
this._geometry.attributes.uv2 = this._geometry.attributes.uv;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
updatePrimvar(name, data, dimension, interpolation) {
|
|
313
|
+
if (!name) return;
|
|
314
|
+
|
|
315
|
+
if (name === 'points') { // || name === 'normals') {
|
|
316
|
+
// Points and normals are set separately
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// console.log('Setting PrimVar: ' + name + ", interpolation: " + interpolation);
|
|
321
|
+
|
|
322
|
+
// TODO: Support multiple UVs. For now, we simply set uv = uv2, which is required when a material has an aoMap.
|
|
323
|
+
if (name.startsWith('st')) {
|
|
324
|
+
name = 'uv';
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
switch (name) {
|
|
328
|
+
case 'displayColor':
|
|
329
|
+
this.setDisplayColor(data, interpolation);
|
|
330
|
+
break;
|
|
331
|
+
case 'uv':
|
|
332
|
+
case "UVMap":
|
|
333
|
+
case "uvmap":
|
|
334
|
+
case "uv0":
|
|
335
|
+
case "UVW":
|
|
336
|
+
case "uvw":
|
|
337
|
+
case "map1":
|
|
338
|
+
this.setUV(data, dimension, interpolation);
|
|
339
|
+
break;
|
|
340
|
+
case "normals":
|
|
341
|
+
this.setNormals(data, interpolation);
|
|
342
|
+
break;
|
|
343
|
+
default:
|
|
344
|
+
if (warningMessagesToCount.has(name)) {
|
|
345
|
+
warningMessagesToCount.set(name, warningMessagesToCount.get(name) + 1);
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
warningMessagesToCount.set(name, 1);
|
|
349
|
+
console.warn('Unsupported primvar: ', name);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
updatePoints(points) {
|
|
355
|
+
this._points = points.slice(0);
|
|
356
|
+
this.updateOrder(this._points, 'position');
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
commit() {
|
|
360
|
+
// Nothing to do here. All Three.js resources are already updated during the sync phase.
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
let warningMessagesToCount = new Map();
|
|
366
|
+
|
|
367
|
+
/** @type {MeshPhysicalMaterial} */
|
|
368
|
+
let defaultMaterial;
|
|
369
|
+
|
|
370
|
+
class HydraMaterial {
|
|
371
|
+
// Maps USD preview material texture names to Three.js MeshPhysicalMaterial names
|
|
372
|
+
static usdPreviewToMeshPhysicalTextureMap = {
|
|
373
|
+
'diffuseColor': 'map',
|
|
374
|
+
'clearcoat': 'clearcoatMap',
|
|
375
|
+
'clearcoatRoughness': 'clearcoatRoughnessMap',
|
|
376
|
+
'emissiveColor': 'emissiveMap',
|
|
377
|
+
'occlusion': 'aoMap',
|
|
378
|
+
'roughness': 'roughnessMap',
|
|
379
|
+
'metallic': 'metalnessMap',
|
|
380
|
+
'normal': 'normalMap',
|
|
381
|
+
'opacity': 'alphaMap'
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
static usdPreviewToColorSpaceMap = {
|
|
385
|
+
'diffuseColor': SRGBColorSpace,
|
|
386
|
+
'emissiveColor': SRGBColorSpace,
|
|
387
|
+
'opacity': SRGBColorSpace,
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
static channelMap = {
|
|
391
|
+
// Three.js expects many 8bit values such as roughness or metallness in a specific RGB texture channel.
|
|
392
|
+
// We could write code to combine multiple 8bit texture files into different channels of one RGB texture where it
|
|
393
|
+
// makes sense, but that would complicate this loader a lot. Most Three.js loaders don't seem to do it either.
|
|
394
|
+
// Instead, we simply provide the 8bit image as an RGBA texture, even though this might be less efficient.
|
|
395
|
+
'r': RGBAFormat,
|
|
396
|
+
'g': RGBAFormat,
|
|
397
|
+
'b': RGBAFormat,
|
|
398
|
+
'rgb': RGBAFormat,
|
|
399
|
+
'rgba': RGBAFormat
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
// Maps USD preview material property names to Three.js MeshPhysicalMaterial names
|
|
403
|
+
static usdPreviewToMeshPhysicalMap = {
|
|
404
|
+
'clearcoat': 'clearcoat',
|
|
405
|
+
'clearcoatRoughness': 'clearcoatRoughness',
|
|
406
|
+
'diffuseColor': 'color',
|
|
407
|
+
'emissiveColor': 'emissive',
|
|
408
|
+
'ior': 'ior',
|
|
409
|
+
'metallic': 'metalness',
|
|
410
|
+
'opacity': 'opacity',
|
|
411
|
+
'roughness': 'roughness',
|
|
412
|
+
'opacityThreshold': 'alphaTest',
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
constructor(id, hydraInterface) {
|
|
416
|
+
this._id = id;
|
|
417
|
+
this._nodes = {};
|
|
418
|
+
this._interface = hydraInterface;
|
|
419
|
+
if (!defaultMaterial) {
|
|
420
|
+
defaultMaterial = new MeshPhysicalMaterial({
|
|
421
|
+
side: DoubleSide,
|
|
422
|
+
color: new Color(0xff2997), // a bright pink color to indicate a missing material
|
|
423
|
+
// envMap: window.envMap,
|
|
424
|
+
name: 'DefaultMaterial',
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
// proper color when materials are disabled
|
|
428
|
+
if (disableMaterials)
|
|
429
|
+
defaultMaterial.color = new Color(0x999999);
|
|
430
|
+
|
|
431
|
+
/** @type {MeshPhysicalMaterial} */
|
|
432
|
+
this._material = defaultMaterial;
|
|
433
|
+
|
|
434
|
+
if (debugMaterials) console.log("Hydra Material", this)
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
updateNode(networkId, path, parameters) {
|
|
438
|
+
if (debugTextures) console.log('Updating Material Node: ' + networkId + ' ' + path, parameters);
|
|
439
|
+
this._nodes[path] = parameters;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
convertWrap(usdWrapMode) {
|
|
443
|
+
if (usdWrapMode === undefined)
|
|
444
|
+
return RepeatWrapping;
|
|
445
|
+
|
|
446
|
+
const WRAPPINGS = {
|
|
447
|
+
'repeat': 1000, // RepeatWrapping
|
|
448
|
+
'clamp': 1001, // ClampToEdgeWrapping
|
|
449
|
+
'mirror': 1002 // MirroredRepeatWrapping
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
if (WRAPPINGS[usdWrapMode])
|
|
453
|
+
return WRAPPINGS[usdWrapMode];
|
|
454
|
+
|
|
455
|
+
return RepeatWrapping;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* @return {Promise<void>}
|
|
460
|
+
*/
|
|
461
|
+
assignTexture(mainMaterial, parameterName) {
|
|
462
|
+
return new Promise((resolve, reject) => {
|
|
463
|
+
const materialParameterMapName = HydraMaterial.usdPreviewToMeshPhysicalTextureMap[parameterName];
|
|
464
|
+
if (materialParameterMapName === undefined) {
|
|
465
|
+
console.warn(`Unsupported material texture parameter '${parameterName}'.`);
|
|
466
|
+
resolve();
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
if (mainMaterial[parameterName] && mainMaterial[parameterName].nodeIn) {
|
|
470
|
+
const nodeIn = mainMaterial[parameterName].nodeIn;
|
|
471
|
+
if (!nodeIn.resolvedPath) {
|
|
472
|
+
console.warn("Texture node has no file!", nodeIn);
|
|
473
|
+
}
|
|
474
|
+
if (debugTextures)
|
|
475
|
+
console.log("Assigning texture with resolved path", parameterName, nodeIn.resolvedPath);
|
|
476
|
+
const textureFileName = nodeIn.resolvedPath?.replace("./", "");
|
|
477
|
+
const channel = mainMaterial[parameterName].inputName;
|
|
478
|
+
|
|
479
|
+
// For debugging
|
|
480
|
+
const matName = Object.keys(this._nodes).find(key => this._nodes[key] === mainMaterial);
|
|
481
|
+
if (debugTextures) console.log(`Setting texture '${materialParameterMapName}' (${textureFileName}) of material '${matName}'... with channel '${channel}'`);
|
|
482
|
+
|
|
483
|
+
this._interface.registry.getTexture(textureFileName).then(texture => {
|
|
484
|
+
if (!this._material) {
|
|
485
|
+
console.error("Material not set when trying to assign texture, this is likely a bug");
|
|
486
|
+
resolve();
|
|
487
|
+
}
|
|
488
|
+
// console.log("getTexture", texture, nodeIn);
|
|
489
|
+
if (materialParameterMapName === 'alphaMap') {
|
|
490
|
+
// If this is an opacity map, check if it's using the alpha channel of the diffuse map.
|
|
491
|
+
// If so, simply change the format of that diffuse map to RGBA and make the material transparent.
|
|
492
|
+
// If not, we need to copy the alpha channel into a new texture's green channel, because that's what Three.js
|
|
493
|
+
// expects for alpha maps (not supported at the moment).
|
|
494
|
+
// NOTE that this only works if diffuse maps are always set before opacity maps, so the order of
|
|
495
|
+
// 'assingTexture' calls for a material matters.
|
|
496
|
+
if (nodeIn.file === mainMaterial.diffuseColor?.nodeIn?.file && channel === 'a') {
|
|
497
|
+
this._material.map.format = RGBAFormat;
|
|
498
|
+
} else {
|
|
499
|
+
// TODO: Extract the alpha channel into a new RGB texture.
|
|
500
|
+
console.warn("Separate alpha channel is currently not supported.", nodeIn.file, mainMaterial.diffuseColor?.nodeIn?.file, channel);
|
|
501
|
+
}
|
|
502
|
+
if (!this._material.alphaClip)
|
|
503
|
+
this._material.transparent = true;
|
|
504
|
+
|
|
505
|
+
this._material.needsUpdate = true;
|
|
506
|
+
resolve();
|
|
507
|
+
return;
|
|
508
|
+
} else if (materialParameterMapName === 'metalnessMap') {
|
|
509
|
+
this._material.metalness = 1.0;
|
|
510
|
+
} else if (materialParameterMapName === 'roughnessMap') {
|
|
511
|
+
this._material.roughness = 1.0;
|
|
512
|
+
} else if (materialParameterMapName === 'emissiveMap') {
|
|
513
|
+
this._material.emissive = new Color(0xffffff);
|
|
514
|
+
} else if (!HydraMaterial.channelMap[channel]) {
|
|
515
|
+
console.warn(`Unsupported texture channel '${channel}'!`);
|
|
516
|
+
resolve();
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
// TODO need to apply bias/scale to the texture in some cases.
|
|
520
|
+
// May be able to extract that for metalness/roughness/opacity/normalScale
|
|
521
|
+
|
|
522
|
+
// Clone texture and set the correct format.
|
|
523
|
+
const clonedTexture = texture.clone();
|
|
524
|
+
let targetSwizzle = 'rgba';
|
|
525
|
+
|
|
526
|
+
if (materialParameterMapName == 'roughnessMap' && channel != 'g') {
|
|
527
|
+
targetSwizzle = '0' + channel + '11';
|
|
528
|
+
}
|
|
529
|
+
if (materialParameterMapName == 'metalnessMap' && channel != 'b') {
|
|
530
|
+
targetSwizzle = '01' + channel + '1';
|
|
531
|
+
}
|
|
532
|
+
if (materialParameterMapName == 'occlusionMap' && channel != 'r') {
|
|
533
|
+
targetSwizzle = channel + '111';
|
|
534
|
+
}
|
|
535
|
+
if (materialParameterMapName == 'opacityMap' && channel != 'a') {
|
|
536
|
+
targetSwizzle = channel + channel + channel + channel;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
clonedTexture.colorSpace = HydraMaterial.usdPreviewToColorSpaceMap[parameterName] || LinearSRGBColorSpace;
|
|
540
|
+
|
|
541
|
+
// console.log("Cloned texture", clonedTexture, "swizzled with", targetSwizzle);
|
|
542
|
+
// clonedTexture.image = HydraMaterial._swizzleImageChannels(clonedTexture.image, targetSwizzle);
|
|
543
|
+
// if (materialParameterToTargetChannel[materialParameterMapName] && channel != materialParameterToTargetChannel[materialParameterMapName])
|
|
544
|
+
if (targetSwizzle != 'rgba') {
|
|
545
|
+
clonedTexture.image = HydraMaterial._swizzleImageChannels(clonedTexture.image, targetSwizzle);
|
|
546
|
+
}
|
|
547
|
+
// clonedTexture.image = HydraMaterial._swizzleImageChannels(clonedTexture.image, channel, 'g')
|
|
548
|
+
|
|
549
|
+
clonedTexture.format = HydraMaterial.channelMap[channel];
|
|
550
|
+
clonedTexture.needsUpdate = true;
|
|
551
|
+
if (nodeIn.st && nodeIn.st.nodeIn) {
|
|
552
|
+
const uvData = nodeIn.st.nodeIn;
|
|
553
|
+
// console.log("Tiling data", uvData);
|
|
554
|
+
|
|
555
|
+
// TODO this is messed up but works for scale and translation, not really for rotation.
|
|
556
|
+
// Refer to https://github.com/mrdoob/three.js/blob/e5426b0514a1347d7aafca69aa34117503c1be88/examples/jsm/exporters/USDZExporter.js#L461
|
|
557
|
+
// (which is also not perfect but close)
|
|
558
|
+
|
|
559
|
+
const rotation = uvData.rotation ? (uvData.rotation / 180 * Math.PI) : 0;
|
|
560
|
+
const offset = uvData.translation ? new Vector2(uvData.translation[0], uvData.translation[1]) : new Vector2(0, 0);
|
|
561
|
+
const repeat = uvData.scale ? new Vector2(uvData.scale[0], uvData.scale[1]) : new Vector2(1, 1);
|
|
562
|
+
|
|
563
|
+
const xRotationOffset = Math.sin(rotation);
|
|
564
|
+
const yRotationOffset = Math.cos(rotation);
|
|
565
|
+
offset.y = offset.y - (1 - yRotationOffset) * repeat.y;
|
|
566
|
+
offset.x = offset.x - xRotationOffset * repeat.x;
|
|
567
|
+
// offset.y = 1 - offset.y - repeat.y;
|
|
568
|
+
/*
|
|
569
|
+
if (uvData.scale)
|
|
570
|
+
clonedTexture.repeat.set(uvData.scale[0], uvData.scale[1]);
|
|
571
|
+
if (uvData.translation)
|
|
572
|
+
clonedTexture.offset.set(uvData.translation[0], uvData.translation[1]);
|
|
573
|
+
if (uvData.rotation)
|
|
574
|
+
clonedTexture.rotation = uvData.rotation / 180 * Math.PI;
|
|
575
|
+
*/
|
|
576
|
+
|
|
577
|
+
clonedTexture.repeat.set(repeat.x, repeat.y);
|
|
578
|
+
clonedTexture.offset.set(offset.x, offset.y);
|
|
579
|
+
clonedTexture.rotation = rotation;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// TODO use nodeIn.wrapS and wrapT and map to THREE
|
|
583
|
+
clonedTexture.wrapS = this.convertWrap(nodeIn.wrapS);
|
|
584
|
+
clonedTexture.wrapT = this.convertWrap(nodeIn.wrapT);
|
|
585
|
+
if (debugTextures) console.log("Setting texture " + materialParameterMapName + " to", clonedTexture)
|
|
586
|
+
this._material[materialParameterMapName] = clonedTexture;
|
|
587
|
+
this._material.needsUpdate = true;
|
|
588
|
+
|
|
589
|
+
if (debugTextures) console.log("RESOLVED TEXTURE", clonedTexture.name, matName, parameterName);
|
|
590
|
+
resolve();
|
|
591
|
+
return;
|
|
592
|
+
}).catch(err => {
|
|
593
|
+
console.warn("Error when loading texture", err);
|
|
594
|
+
resolve();
|
|
595
|
+
return;
|
|
596
|
+
});
|
|
597
|
+
} else {
|
|
598
|
+
this._material[materialParameterMapName] = undefined;
|
|
599
|
+
resolve();
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// from https://github.com/mrdoob/three.js/blob/dev/src/math/ColorManagement.js
|
|
606
|
+
static SRGBToLinear(c) {
|
|
607
|
+
return (c < 0.04045) ? c * 0.0773993808 : Math.pow(c * 0.9478672986 + 0.0521327014, 2.4);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
static LinearToSRGB(c) {
|
|
611
|
+
return (c < 0.0031308) ? c * 12.92 : 1.055 * (Math.pow(c, 0.41666)) - 0.055;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Swizzle image channels (e.g. move red channel to green channel)
|
|
616
|
+
* @param {*} image three.js image
|
|
617
|
+
* @param {string} swizzle For example, "rgga". Must have max. 4 components. Can contain 0 and 1, e.g. "rgba1" is valid.
|
|
618
|
+
* @returns three.js image
|
|
619
|
+
*/
|
|
620
|
+
static _swizzleImageChannels(image, swizzle) {
|
|
621
|
+
if ((typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement) ||
|
|
622
|
+
(typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement) ||
|
|
623
|
+
(typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap)) {
|
|
624
|
+
|
|
625
|
+
const canvas = document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas');
|
|
626
|
+
|
|
627
|
+
canvas.width = image.width;
|
|
628
|
+
canvas.height = image.height;
|
|
629
|
+
|
|
630
|
+
const context = canvas.getContext('2d');
|
|
631
|
+
context.drawImage(image, 0, 0, image.width, image.height);
|
|
632
|
+
|
|
633
|
+
const imageData = context.getImageData(0, 0, image.width, image.height);
|
|
634
|
+
const data = imageData.data;
|
|
635
|
+
|
|
636
|
+
// console.log(data);
|
|
637
|
+
|
|
638
|
+
const swizzleToIndex = {
|
|
639
|
+
'r': 0,
|
|
640
|
+
'g': 1,
|
|
641
|
+
'b': 2,
|
|
642
|
+
'a': 3,
|
|
643
|
+
'x': 0,
|
|
644
|
+
'y': 1,
|
|
645
|
+
'z': 2,
|
|
646
|
+
'w': 3,
|
|
647
|
+
'0': 4, // set to 0
|
|
648
|
+
'1': 5, // set to 1
|
|
649
|
+
'-': -1, // passthrough
|
|
650
|
+
};
|
|
651
|
+
const arrayAccessBySwizzle = [4, 4, 4, 4]; // empty value if nothing defined in the swizzle pattern
|
|
652
|
+
for (let i = 0; i < swizzle.length; i++) {
|
|
653
|
+
arrayAccessBySwizzle[i] = swizzleToIndex[swizzle[i]];
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
const dataEntry = data.slice(0);
|
|
657
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
658
|
+
dataEntry[0] = data[i];
|
|
659
|
+
dataEntry[1] = data[i + 1];
|
|
660
|
+
dataEntry[2] = data[i + 2];
|
|
661
|
+
dataEntry[3] = data[i + 3];
|
|
662
|
+
dataEntry[4] = 0; // empty value
|
|
663
|
+
dataEntry[5] = 1;
|
|
664
|
+
|
|
665
|
+
const rAccess = arrayAccessBySwizzle[0];
|
|
666
|
+
const gAccess = arrayAccessBySwizzle[1];
|
|
667
|
+
const bAccess = arrayAccessBySwizzle[2];
|
|
668
|
+
const aAccess = arrayAccessBySwizzle[3];
|
|
669
|
+
|
|
670
|
+
if (rAccess !== -1)
|
|
671
|
+
data[i] = dataEntry[rAccess];
|
|
672
|
+
if (gAccess !== -1)
|
|
673
|
+
data[i + 1] = dataEntry[gAccess];
|
|
674
|
+
if (bAccess !== -1)
|
|
675
|
+
data[i + 2] = dataEntry[bAccess];
|
|
676
|
+
if (aAccess !== -1)
|
|
677
|
+
data[i + 3] = dataEntry[aAccess];
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
context.putImageData(imageData, 0, 0);
|
|
681
|
+
return canvas;
|
|
682
|
+
|
|
683
|
+
} else if (image.data) {
|
|
684
|
+
const data = image.data.slice(0);
|
|
685
|
+
|
|
686
|
+
for (let i = 0; i < data.length; i++) {
|
|
687
|
+
if (data instanceof Uint8Array || data instanceof Uint8ClampedArray) {
|
|
688
|
+
data[i] = Math.floor(this.SRGBToLinear(data[i] / 255) * 255);
|
|
689
|
+
} else {
|
|
690
|
+
// assuming float
|
|
691
|
+
data[i] = this.SRGBToLinear(data[i]);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
return {
|
|
696
|
+
data: data,
|
|
697
|
+
width: image.width,
|
|
698
|
+
height: image.height
|
|
699
|
+
};
|
|
700
|
+
} else {
|
|
701
|
+
console.warn('ImageUtils.sRGBToLinear(): Unsupported image type. No color space conversion applied.');
|
|
702
|
+
return image;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
assignProperty(mainMaterial, parameterName) {
|
|
707
|
+
const materialParameterName = HydraMaterial.usdPreviewToMeshPhysicalMap[parameterName];
|
|
708
|
+
if (materialParameterName === undefined) {
|
|
709
|
+
console.warn(`Unsupported material parameter '${parameterName}'.`);
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
if (mainMaterial[parameterName] !== undefined && !mainMaterial[parameterName].nodeIn) {
|
|
713
|
+
// console.log(`Assigning property ${parameterName}: ${mainMaterial[parameterName]}`);
|
|
714
|
+
if (Array.isArray(mainMaterial[parameterName])) {
|
|
715
|
+
this._material[materialParameterName] = new Color().fromArray(mainMaterial[parameterName]);
|
|
716
|
+
} else {
|
|
717
|
+
this._material[materialParameterName] = mainMaterial[parameterName];
|
|
718
|
+
if (materialParameterName === 'opacity' && mainMaterial[parameterName] < 1.0) {
|
|
719
|
+
this._material.transparent = true;
|
|
720
|
+
}
|
|
721
|
+
if (parameterName == 'opacityThreshold' && mainMaterial[parameterName] > 0.0) {
|
|
722
|
+
this._material.transparent = false;
|
|
723
|
+
this._material.alphaClip = true;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
async updateFinished(type, relationships) {
|
|
730
|
+
for (let relationship of relationships) {
|
|
731
|
+
relationship.nodeIn = this._nodes[relationship.inputId];
|
|
732
|
+
relationship.nodeOut = this._nodes[relationship.outputId];
|
|
733
|
+
relationship.nodeIn[relationship.inputName] = relationship;
|
|
734
|
+
relationship.nodeOut[relationship.outputName] = relationship;
|
|
735
|
+
}
|
|
736
|
+
if (debugMaterials) console.log('Finalizing Material: ' + this._id);
|
|
737
|
+
if (debugMaterials) console.log("updateFinished", type, relationships)
|
|
738
|
+
|
|
739
|
+
// find the main material node
|
|
740
|
+
let mainMaterialNode = undefined;
|
|
741
|
+
for (let node of Object.values(this._nodes)) {
|
|
742
|
+
if (node.diffuseColor) {
|
|
743
|
+
mainMaterialNode = node;
|
|
744
|
+
break;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
if (!mainMaterialNode || disableMaterials) {
|
|
749
|
+
this._material = defaultMaterial;
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// TODO: Ideally, we don't recreate the material on every update.
|
|
754
|
+
// Creating a new one requires to also update any meshes that reference it. So we're relying on the C++ side to
|
|
755
|
+
// call this before also calling `setMaterial` on the affected meshes.
|
|
756
|
+
this._material = new MeshPhysicalMaterial({});
|
|
757
|
+
this._material.side = DoubleSide;
|
|
758
|
+
// split _id
|
|
759
|
+
let _name = this._id;
|
|
760
|
+
let lastSlash = _name.lastIndexOf('/');
|
|
761
|
+
if (lastSlash >= 0)
|
|
762
|
+
_name = _name.substring(lastSlash + 1);
|
|
763
|
+
this._material.name = _name;
|
|
764
|
+
|
|
765
|
+
// Assign textures
|
|
766
|
+
const haveRoughnessMap = !!(mainMaterialNode.roughness && mainMaterialNode.roughness.nodeIn);
|
|
767
|
+
const haveMetalnessMap = !!(mainMaterialNode.metallic && mainMaterialNode.metallic.nodeIn);
|
|
768
|
+
const haveOcclusionMap = !!(mainMaterialNode.occlusion && mainMaterialNode.occlusion.nodeIn);
|
|
769
|
+
|
|
770
|
+
if (debugMaterials) {
|
|
771
|
+
console.log('Creating Material: ' + this._id, mainMaterialNode, {
|
|
772
|
+
haveRoughnessMap,
|
|
773
|
+
haveMetalnessMap,
|
|
774
|
+
haveOcclusionMap
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
if (!disableTextures) {
|
|
779
|
+
/** @type {Array<Promise<any>>} */
|
|
780
|
+
const texturePromises = [];
|
|
781
|
+
for (let key in HydraMaterial.usdPreviewToMeshPhysicalTextureMap) {
|
|
782
|
+
texturePromises.push(this.assignTexture(mainMaterialNode, key));
|
|
783
|
+
}
|
|
784
|
+
await Promise.all(texturePromises);
|
|
785
|
+
|
|
786
|
+
// Need to sanitize metallic/roughness/occlusion maps - if we want to export glTF they need to be identical right now
|
|
787
|
+
if (haveRoughnessMap && !haveMetalnessMap) {
|
|
788
|
+
if (debugMaterials) console.log(this._material.roughnessMap, this._material);
|
|
789
|
+
this._material.metalnessMap = this._material.roughnessMap;
|
|
790
|
+
if (this._material.metalnessMap) this._material.metalnessMap.needsUpdate = true;
|
|
791
|
+
else console.error("Something went wrong with the texture promise; haveRoughnessMap is true but no roughnessMap was loaded.");
|
|
792
|
+
}
|
|
793
|
+
else if (haveMetalnessMap && !haveRoughnessMap) {
|
|
794
|
+
this._material.roughnessMap = this._material.metalnessMap;
|
|
795
|
+
if (this._material.roughnessMap) this._material.roughnessMap.needsUpdate = true;
|
|
796
|
+
else console.error("Something went wrong with the texture promise; haveMetalnessMap is true but no metalnessMap was loaded.");
|
|
797
|
+
}
|
|
798
|
+
else if (haveMetalnessMap && haveRoughnessMap) {
|
|
799
|
+
console.warn("TODO: [Three USD] separate metalness and roughness textures need to be merged");
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
// Assign material properties
|
|
804
|
+
for (let key in HydraMaterial.usdPreviewToMeshPhysicalMap) {
|
|
805
|
+
this.assignProperty(mainMaterialNode, key);
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
if (debugMaterials) console.log("Material Node \"" + this._material.name + "\"", mainMaterialNode, "Resulting Material", this._material);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
/*
|
|
813
|
+
class SdfPath {
|
|
814
|
+
get name() { return this.GetName(); }
|
|
815
|
+
get absoluteRootPath() { return this.AbsoluteRootPath(); }
|
|
816
|
+
get reflexiveRelativePath() { return this.ReflexiveRelativePath(); }
|
|
817
|
+
}
|
|
818
|
+
*/
|
|
819
|
+
|
|
820
|
+
export class ThreeRenderDelegateInterface {
|
|
821
|
+
|
|
822
|
+
/**
|
|
823
|
+
* @param {import('..').threeJsRenderDelegateConfig} config
|
|
824
|
+
*/
|
|
825
|
+
constructor(config) {
|
|
826
|
+
this.config = config;
|
|
827
|
+
if (debugMaterials) console.log("RenderDelegateInterface", config);
|
|
828
|
+
this.registry = new TextureRegistry(config);
|
|
829
|
+
this.materials = {};
|
|
830
|
+
this.meshes = {};
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
/**
|
|
834
|
+
* Render Prims. See webRenderDelegate.h and webRenderDelegate.cpp
|
|
835
|
+
* @param {string} typeId // translated from TfToken
|
|
836
|
+
* @param {string} id // SdfPath.GetAsString()
|
|
837
|
+
* @param {*} instancerId
|
|
838
|
+
* @returns
|
|
839
|
+
*/
|
|
840
|
+
createRPrim(typeId, id, instancerId) {
|
|
841
|
+
if (debugPrims) console.log('Creating RPrim: ', typeId, id, typeof id);
|
|
842
|
+
let mesh = new HydraMesh(id, this);
|
|
843
|
+
this.meshes[id] = mesh;
|
|
844
|
+
return mesh;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
createBPrim(typeId, id) {
|
|
848
|
+
if (debugPrims) console.log('Creating BPrim: ', typeId, id);
|
|
849
|
+
/*let mesh = new HydraMesh(id, this);
|
|
850
|
+
this.meshes[id] = mesh;
|
|
851
|
+
return mesh;*/
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
createSPrim(typeId, id) {
|
|
855
|
+
if (debugPrims) console.log('Creating SPrim: ', typeId, id);
|
|
856
|
+
|
|
857
|
+
if (typeId === 'material') {
|
|
858
|
+
let material = new HydraMaterial(id, this);
|
|
859
|
+
this.materials[id] = material;
|
|
860
|
+
return material;
|
|
861
|
+
} else {
|
|
862
|
+
return undefined;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
CommitResources() {
|
|
867
|
+
for (const id in this.meshes) {
|
|
868
|
+
const hydraMesh = this.meshes[id]
|
|
869
|
+
hydraMesh.commit();
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
}
|