@needle-tools/engine 4.6.2 → 4.7.0-next.4ec920e
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/dist/needle-engine.bundle-C00XXoql.min.js +1603 -0
- package/dist/{needle-engine.bundle-DmYMLdWP.umd.cjs → needle-engine.bundle-CaRf1SjI.umd.cjs} +212 -184
- package/dist/{needle-engine.bundle-DGcStTA7.js → needle-engine.bundle-DDM6UaI6.js} +8708 -8445
- package/dist/needle-engine.js +2 -2
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/lib/engine/engine_context.js +3 -2
- package/lib/engine/engine_context.js.map +1 -1
- package/lib/engine-components/export/usdz/ThreeUSDZExporter.d.ts +5 -3
- package/lib/engine-components/export/usdz/ThreeUSDZExporter.js +124 -117
- package/lib/engine-components/export/usdz/ThreeUSDZExporter.js.map +1 -1
- package/lib/engine-components/export/usdz/extensions/NodeMaterialConverter.d.ts +10 -0
- package/lib/engine-components/export/usdz/extensions/NodeMaterialConverter.js +452 -0
- package/lib/engine-components/export/usdz/extensions/NodeMaterialConverter.js.map +1 -0
- package/lib/engine-components/postprocessing/Effects/BloomEffect.js +0 -3
- package/lib/engine-components/postprocessing/Effects/BloomEffect.js.map +1 -1
- package/lib/engine-components/postprocessing/PostProcessingHandler.js +28 -10
- package/lib/engine-components/postprocessing/PostProcessingHandler.js.map +1 -1
- package/lib/engine-components/postprocessing/utils.d.ts +1 -0
- package/lib/engine-components/postprocessing/utils.js.map +1 -1
- package/package.json +2 -2
- package/src/engine/engine_context.ts +3 -2
- package/src/engine-components/export/usdz/ThreeUSDZExporter.ts +155 -142
- package/src/engine-components/export/usdz/extensions/NodeMaterialConverter.ts +532 -0
- package/src/engine-components/postprocessing/Effects/BloomEffect.ts +0 -2
- package/src/engine-components/postprocessing/PostProcessingHandler.ts +36 -11
- package/src/engine-components/postprocessing/utils.ts +1 -0
- package/dist/needle-engine.bundle-D0XWaCQs.min.js +0 -1575
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
import {Color, Texture, Vector2, Vector3, Vector4} from "three";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
formatsWithAlphaChannel,
|
|
5
|
+
makeNameSafeForUSD
|
|
6
|
+
} from "../ThreeUSDZExporter.js";
|
|
7
|
+
|
|
8
|
+
const materialRoot = '</StageRoot/Materials';
|
|
9
|
+
declare type TextureMap = {[name: string]: {texture: Texture, scale?: Vector4}};
|
|
10
|
+
|
|
11
|
+
type MeshPhysicalNodeMaterial = import("three/src/materials/nodes/MeshPhysicalNodeMaterial.js").default;
|
|
12
|
+
|
|
13
|
+
function buildNodeMaterial( nodeMaterial: MeshPhysicalNodeMaterial, materialName: string, textures: TextureMap ) {
|
|
14
|
+
|
|
15
|
+
const collectedNodeTypes = new Map();
|
|
16
|
+
const getUniqueNodeName = (node) => {
|
|
17
|
+
const type = node["type___needle"];
|
|
18
|
+
const typeMap = collectedNodeTypes.get(type) || new Map();
|
|
19
|
+
collectedNodeTypes.set(type, typeMap);
|
|
20
|
+
|
|
21
|
+
if (!typeMap.has(node)) {
|
|
22
|
+
const name = `${type}${typeMap.size ? `_${typeMap.size}` : ""}`;
|
|
23
|
+
typeMap.set(node, name);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return typeMap.get(node);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const colorNodesToBeExported = nodeMaterial.colorNode ? getNodesToExport(nodeMaterial.colorNode) : [];
|
|
30
|
+
const colorOutputString = nodeMaterial.colorNode
|
|
31
|
+
? `color3f inputs:diffuseColor.connect = ${materialRoot}/${materialName}/${getUniqueNodeName(colorNodesToBeExported.values().next().value)}.outputs:out>`
|
|
32
|
+
: "";
|
|
33
|
+
|
|
34
|
+
const roughnessNodesToBeExported = nodeMaterial.roughnessNode ? getNodesToExport(nodeMaterial.roughnessNode) : [];
|
|
35
|
+
const roughnessOutputString = nodeMaterial.roughnessNode
|
|
36
|
+
? `float inputs:roughness.connect = ${materialRoot}/${materialName}/${getUniqueNodeName(roughnessNodesToBeExported.values().next().value)}.outputs:out>`
|
|
37
|
+
: "";
|
|
38
|
+
|
|
39
|
+
const normalNodesToBeExported = nodeMaterial.normalNode ? getNodesToExport(nodeMaterial.normalNode) : [];
|
|
40
|
+
const normalOutputString = nodeMaterial.normalNode
|
|
41
|
+
? `float3 inputs:normal.connect = ${materialRoot}/${materialName}/${getUniqueNodeName(normalNodesToBeExported.values().next().value)}.outputs:out>`
|
|
42
|
+
: "";
|
|
43
|
+
|
|
44
|
+
const metallicNodesToBeExported = nodeMaterial.metalnessNode ? getNodesToExport(nodeMaterial.metalnessNode) : [];
|
|
45
|
+
const metallicOutputString = nodeMaterial.metalnessNode
|
|
46
|
+
? `float inputs:metallic.connect = ${materialRoot}/${materialName}/${getUniqueNodeName(metallicNodesToBeExported.values().next().value)}.outputs:out>`
|
|
47
|
+
: "";
|
|
48
|
+
|
|
49
|
+
const combinedSetOfAllNodesToExport = new Set([...colorNodesToBeExported, ...roughnessNodesToBeExported, ...normalNodesToBeExported, ...metallicNodesToBeExported])
|
|
50
|
+
|
|
51
|
+
const shaderOutput = getShadersFromNodes(combinedSetOfAllNodesToExport, materialName, textures, getUniqueNodeName);
|
|
52
|
+
|
|
53
|
+
console.debug(shaderOutput);
|
|
54
|
+
|
|
55
|
+
return `
|
|
56
|
+
|
|
57
|
+
def Material "${materialName}" ${nodeMaterial.name ? `(
|
|
58
|
+
displayName = "${nodeMaterial.name}"
|
|
59
|
+
)` : ''}
|
|
60
|
+
{
|
|
61
|
+
token outputs:mtlx:surface.connect = ${materialRoot}/${materialName}/N_mtlxsurface.outputs:surface>
|
|
62
|
+
|
|
63
|
+
def Shader "N_mtlxsurface"
|
|
64
|
+
{
|
|
65
|
+
uniform token info:id = "ND_UsdPreviewSurface_surfaceshader"
|
|
66
|
+
${colorOutputString}
|
|
67
|
+
${roughnessOutputString}
|
|
68
|
+
${normalOutputString}
|
|
69
|
+
${metallicOutputString}
|
|
70
|
+
token outputs:surface
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
${shaderOutput}
|
|
74
|
+
|
|
75
|
+
}`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function getNodesToExport( rootNode: any ): Set<any> {
|
|
79
|
+
|
|
80
|
+
const getNodeType = (node) => {
|
|
81
|
+
if (node.nodeType) return node.nodeType;
|
|
82
|
+
switch (node.type) {
|
|
83
|
+
case "TimerNode": return "float";
|
|
84
|
+
case "TextureNode": return undefined;
|
|
85
|
+
case "ConvertNode": return node.convertTo;
|
|
86
|
+
default: return undefined;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const getSetOfAllNodes = (rootNode: any): Set<any> => {
|
|
91
|
+
const setOfAllNodes:Set<any> = new Set();
|
|
92
|
+
|
|
93
|
+
const collectNode = (node) => {
|
|
94
|
+
if (!node.isNode || setOfAllNodes.has(node)) return;
|
|
95
|
+
if (!node["nodeType___needle"]){
|
|
96
|
+
node["nodeType___needle"] = getNodeType(node);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (node.shaderNode){
|
|
100
|
+
node["type___needle"] = "ShaderCallNodeInternal";
|
|
101
|
+
node["shaderNodeLayoutName___needle"] = node.shaderNode.layout.name.slice(3);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
node["type___needle"] = node.type;
|
|
105
|
+
}
|
|
106
|
+
setOfAllNodes.add(node);
|
|
107
|
+
|
|
108
|
+
for (const key in node) {
|
|
109
|
+
if (node[key]?.isNode) {
|
|
110
|
+
|
|
111
|
+
collectNode(node[key]);
|
|
112
|
+
node["nodeType___needle"] ||= node[key]["nodeType___needle"];
|
|
113
|
+
}
|
|
114
|
+
if (Array.isArray(node[key])) {
|
|
115
|
+
node[key].forEach(child => {
|
|
116
|
+
if (child.isNode) {
|
|
117
|
+
collectNode(child)
|
|
118
|
+
|
|
119
|
+
node["nodeType___needle"] ||= child["nodeType___needle"];
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
collectNode(rootNode);
|
|
127
|
+
|
|
128
|
+
return setOfAllNodes;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const isSelfConversionNode = (node) => {
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
if (node.type === "ConvertNode"){
|
|
135
|
+
if (node.convertTo === node.node["nodeType___needle"])
|
|
136
|
+
{
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
else if (node.node.type === "ConstNode")
|
|
140
|
+
{
|
|
141
|
+
if (node.convertTo === "vec4" && node.node.value.isVector4){
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
else if (node.convertTo === "vec3" && node.node.value.isVector3){
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
else if (node.convertTo === "vec2" && node.node.value.isVector2){
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
else if (node.convertTo === "color" && node.node.value.isColor){
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
else if (node.convertTo === "float" && typeof node.node.value === 'number'){
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
else if (node.node.type == "SplitNode"){
|
|
158
|
+
if (node.convertTo == "float" && node.node.components.length === 1){
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const getNextValidNode = (node) => {
|
|
168
|
+
while (nodeShouldNotBeExported(node)) {
|
|
169
|
+
if (!node.node && node.shaderNode){
|
|
170
|
+
node = node.inputNodes[0];
|
|
171
|
+
}
|
|
172
|
+
else{
|
|
173
|
+
node = node.node ?? node.aNode ?? node.bNode ?? node.cNode;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return node;
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
const nodeShouldNotBeExported = (node: any | undefined): boolean => {
|
|
180
|
+
const ignorableNodeTypes = ["UniformNode", "UniformGroupNode", "ShaderNodeInternal"]
|
|
181
|
+
return !node || isSelfConversionNode(node) || ignorableNodeTypes.includes(node["type___needle"]) || node["type___needle"] === undefined;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const getParent = (currNode, nodeSet) => {
|
|
185
|
+
for (const node of nodeSet) {
|
|
186
|
+
for (const key in node) {
|
|
187
|
+
if (node[key]?.isNode && node[key] === currNode) {
|
|
188
|
+
return { parent: node, label: key };
|
|
189
|
+
}
|
|
190
|
+
if (Array.isArray(node[key])) {
|
|
191
|
+
const child = node[key].find(childNode => childNode.isNode && childNode === currNode);
|
|
192
|
+
if (child) return { parent: node, label: key };
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return null;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const updateNodeReferences = (node, refKeys) => {
|
|
200
|
+
if (node.shaderNode){
|
|
201
|
+
node.inputNodes[0] = getNextValidNode(node.inputNodes[0]);
|
|
202
|
+
}
|
|
203
|
+
else if (Array.isArray(node.nodes)) {
|
|
204
|
+
for(let i = 0; i < node.nodes.length; i++){
|
|
205
|
+
if (node.nodes[i] && nodeShouldNotBeExported(node.nodes[i])) {
|
|
206
|
+
node.nodes[i] = getNextValidNode(node.nodes[i]);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
else{
|
|
211
|
+
refKeys.forEach(key => {
|
|
212
|
+
if (node[key] && nodeShouldNotBeExported(node[key])) {
|
|
213
|
+
node[key] = getNextValidNode(node[key]);
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
const setMixNodeMixToFloat = (node) => {
|
|
220
|
+
if (node.type === "MathNode" && node.method === "mix"){
|
|
221
|
+
node.cNode["nodeType___needle"] = "float";
|
|
222
|
+
if (node.cNode.type === "ConvertNode"){
|
|
223
|
+
node.cNode.convertTo = "float";
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const setConstNodeTypeToParentType = (node, parentResponse) => {
|
|
229
|
+
if (!(parentResponse.label === 'cNode' && parentResponse.parent.type === "MathNode" && parentResponse.parent.method === "mix")){
|
|
230
|
+
if (parentResponse.parent.type === "JoinNode"){
|
|
231
|
+
node["nodeType___needle"] = "float";
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
node["nodeType___needle"] = parentResponse.parent["nodeType___needle"];
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const isConvertVector4ToColorNode = (node) => (
|
|
240
|
+
node?.type === "ConvertNode" && node["nodeType___needle"] === "color" && node.node["nodeType___needle"] === "vec4"
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
const createVec3ToColorConversionNode = (node, allNodes) => {
|
|
244
|
+
node.convertTo = "vec3";
|
|
245
|
+
node["nodeType___needle"] = "vec3";
|
|
246
|
+
|
|
247
|
+
const newNode = {
|
|
248
|
+
type: "ConvertNode",
|
|
249
|
+
convertTo: "color",
|
|
250
|
+
node: node,
|
|
251
|
+
isNode: true,
|
|
252
|
+
nodeType___needle: "color",
|
|
253
|
+
type___needle: "ConvertNode"
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
const parentInfo = getParent(node, allNodes);
|
|
257
|
+
if (parentInfo?.parent) {
|
|
258
|
+
parentInfo.parent[parentInfo.label] = newNode;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return newNode;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const isConvertFromTextureNode = (node) => (
|
|
265
|
+
node?.type === "ConvertNode" && node.node.type === "TextureNode" && node["nodeType___needle"] !== node.node["nodeType___needle"]
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
const pruneNodes = (allNodes: Set<any>) : Set<any> => {
|
|
269
|
+
|
|
270
|
+
const nodesToBeExported: Set<any> = new Set();
|
|
271
|
+
for (let node of allNodes) {
|
|
272
|
+
if (nodeShouldNotBeExported(node)) continue;
|
|
273
|
+
|
|
274
|
+
// Math mix nodes take a mix input that should always be a float, the typing gets messed up here because
|
|
275
|
+
// the other inputs that are being mixed can be anything and if we have a convert or a const that goes
|
|
276
|
+
// into the mix input, it gets set as whatever the output of the mix should be, not float. Here we make
|
|
277
|
+
// sure it will be float
|
|
278
|
+
setMixNodeMixToFloat(node);
|
|
279
|
+
|
|
280
|
+
if (node.type == "SplitNode"){
|
|
281
|
+
const parentResponse = getParent(node, allNodes);
|
|
282
|
+
if (node.components.length === 1){
|
|
283
|
+
node["nodeType___needle"] = "float";
|
|
284
|
+
}
|
|
285
|
+
else if(parentResponse) {
|
|
286
|
+
// TODO: this may not be sufficient and it may be better to always count the component lengths
|
|
287
|
+
node["nodeType___needle"] = parentResponse.parent["nodeType___needle"];
|
|
288
|
+
}
|
|
289
|
+
else throw new Error("SplitNode without parent found, this should not happen");
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Here we check child nodes to update the connections if they will be ignored later
|
|
293
|
+
updateNodeReferences(node, ["node", "aNode", "bNode", "cNode"]);
|
|
294
|
+
|
|
295
|
+
// Const nodes don't always have a type and sometimes they need to be converted from a value of 0 -> [0, 0, 0]
|
|
296
|
+
// this function does that conversion
|
|
297
|
+
if (node.type == "ConstNode" && node.nodeType == null){
|
|
298
|
+
setConstNodeTypeToParentType(node, getParent(node, allNodes));
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (isConvertVector4ToColorNode(node)) {
|
|
302
|
+
nodesToBeExported.add(createVec3ToColorConversionNode(node, allNodes));
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// We want Texture nodes to export the type they need, rather than something else and then convert
|
|
306
|
+
// here if we have a convert above a Texture that we missed on the first pass because there was
|
|
307
|
+
// some other node in between to prune, set the type on the Texture correctly and kill the convert
|
|
308
|
+
if (isConvertFromTextureNode(node)){
|
|
309
|
+
node.node["nodeType___needle"] = node.convertTo;
|
|
310
|
+
const parentInfo = getParent(node, allNodes);
|
|
311
|
+
if (parentInfo?.parent) {
|
|
312
|
+
parentInfo.parent[parentInfo.label] = node.node;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
node = node.node;
|
|
316
|
+
}
|
|
317
|
+
nodesToBeExported.add(node);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return nodesToBeExported;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const setOfAllNodes = getSetOfAllNodes(rootNode);
|
|
324
|
+
|
|
325
|
+
const prunedNodes = pruneNodes(setOfAllNodes);
|
|
326
|
+
|
|
327
|
+
return prunedNodes;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function getConstValueString(value: any, type: string){
|
|
331
|
+
switch (type) {
|
|
332
|
+
case "float4":
|
|
333
|
+
return value.isVector4
|
|
334
|
+
? `(${value.x}, ${value.y}, ${value.z}, ${value.w})`
|
|
335
|
+
: `(${value}, ${value}, ${value}, ${value})`;
|
|
336
|
+
case "float3":
|
|
337
|
+
return value.isVector3
|
|
338
|
+
? `(${value.x}, ${value.y}, ${value.z})`
|
|
339
|
+
: `(${value}, ${value}, ${value})`;
|
|
340
|
+
case "float2":
|
|
341
|
+
return value.isVector2
|
|
342
|
+
? `(${value.x}, ${value.y})`
|
|
343
|
+
: `(${value}, ${value})`;
|
|
344
|
+
case "color3f":
|
|
345
|
+
return value.isColor
|
|
346
|
+
? `(${value.r}, ${value.g}, ${value.b})`
|
|
347
|
+
: `(${value}, ${value}, ${value})`;
|
|
348
|
+
default:
|
|
349
|
+
return (value.isVector4 || value.isVector3 || value.isVector2)
|
|
350
|
+
? `${value.x}`
|
|
351
|
+
: value.isColor
|
|
352
|
+
? `${value.r}`
|
|
353
|
+
: `${value}`;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function TSLNodeToUsdShadeString(node:any, materialName:string, getUniqueNodeName, textures: TextureMap) {
|
|
358
|
+
const pad = ' ';
|
|
359
|
+
const getType = (nodeType) => {
|
|
360
|
+
const types = {
|
|
361
|
+
float: "float",
|
|
362
|
+
vec2: "vector2",
|
|
363
|
+
vec3: "vector3",
|
|
364
|
+
vec4: "vector4",
|
|
365
|
+
color: "color3"
|
|
366
|
+
};
|
|
367
|
+
return types[nodeType] || "float";
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
const getUsdType = (nodeType) => {
|
|
371
|
+
const usdTypes = {
|
|
372
|
+
float: "float",
|
|
373
|
+
vec2: "float2",
|
|
374
|
+
vec3: "float3",
|
|
375
|
+
vec4: "float4",
|
|
376
|
+
color: "color3f"
|
|
377
|
+
};
|
|
378
|
+
return usdTypes[nodeType] || "float";
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
const type = node["type___needle"];
|
|
382
|
+
|
|
383
|
+
const ndType = node["nodeType___needle"];
|
|
384
|
+
|
|
385
|
+
const mtlxNdType = getType(ndType);
|
|
386
|
+
let usdNdType = getUsdType(ndType);
|
|
387
|
+
|
|
388
|
+
let usdShadeNodeName = "";
|
|
389
|
+
const inputs = new Array<string>();
|
|
390
|
+
switch (type) {
|
|
391
|
+
case "UniformGroupNode":
|
|
392
|
+
case "UniformNode":
|
|
393
|
+
// break out of node loop
|
|
394
|
+
return "";
|
|
395
|
+
case "TimerNode":
|
|
396
|
+
usdShadeNodeName = "time_float";
|
|
397
|
+
break;
|
|
398
|
+
case "ConstNode":
|
|
399
|
+
usdShadeNodeName = "constant_" + mtlxNdType;
|
|
400
|
+
inputs.push(`${usdNdType} inputs:value = ${getConstValueString(node.value, usdNdType)}`);
|
|
401
|
+
break;
|
|
402
|
+
case "JoinNode":
|
|
403
|
+
usdShadeNodeName = "combine" + node.nodes.length + "_" + mtlxNdType;
|
|
404
|
+
let i = 1;
|
|
405
|
+
for (const childNode of node.nodes) {
|
|
406
|
+
inputs.push(`float inputs:in${i++}.connect = ${materialRoot}/${materialName}/${getUniqueNodeName(childNode)}.outputs:out>`);
|
|
407
|
+
}
|
|
408
|
+
break;
|
|
409
|
+
case "ConvertNode":
|
|
410
|
+
const inputType = getType(node.node["nodeType___needle"]);
|
|
411
|
+
usdShadeNodeName = "convert_" + inputType + "_" + mtlxNdType;
|
|
412
|
+
if (node.node)
|
|
413
|
+
inputs.push(`${getUsdType(node.node["nodeType___needle"])} inputs:in.connect = ${materialRoot}/${materialName}/${getUniqueNodeName(node.node)}.outputs:out>`);
|
|
414
|
+
break;
|
|
415
|
+
case "MathNode":
|
|
416
|
+
usdShadeNodeName = node.method + "_" + mtlxNdType;
|
|
417
|
+
|
|
418
|
+
if (node.aNode && !node.bNode)
|
|
419
|
+
inputs.push(`${getUsdType(node.aNode["nodeType___needle"])} inputs:in.connect = ${materialRoot}/${materialName}/${getUniqueNodeName(node.aNode)}.outputs:out>`);
|
|
420
|
+
if (node.aNode && node.bNode && !node.cNode) {
|
|
421
|
+
|
|
422
|
+
inputs.push(`${getUsdType(node.aNode["nodeType___needle"])} inputs:in1.connect = ${materialRoot}/${materialName}/${getUniqueNodeName(node.aNode)}.outputs:out>`);
|
|
423
|
+
inputs.push(`${getUsdType(node.bNode["nodeType___needle"])} inputs:in2.connect = ${materialRoot}/${materialName}/${getUniqueNodeName(node.bNode)}.outputs:out>`);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
if (node.aNode && node.bNode && node.cNode && node.method == "clamp") {
|
|
427
|
+
inputs.push(`${getUsdType(node.aNode["nodeType___needle"])} inputs:in.connect = ${materialRoot}/${materialName}/${getUniqueNodeName(node.aNode)}.outputs:out>`);
|
|
428
|
+
inputs.push(`${getUsdType(node.bNode["nodeType___needle"])} inputs:low.connect = ${materialRoot}/${materialName}/${getUniqueNodeName(node.bNode)}.outputs:out>`);
|
|
429
|
+
inputs.push(`${getUsdType(node.cNode["nodeType___needle"])} inputs:high.connect = ${materialRoot}/${materialName}/${getUniqueNodeName(node.cNode)}.outputs:out>`);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (node.aNode && node.bNode && node.cNode && node.method == "mix") {
|
|
433
|
+
inputs.push(`${getUsdType(node.aNode["nodeType___needle"])} inputs:fg.connect = ${materialRoot}/${materialName}/${getUniqueNodeName(node.bNode)}.outputs:out>`);
|
|
434
|
+
inputs.push(`${getUsdType(node.bNode["nodeType___needle"])} inputs:bg.connect = ${materialRoot}/${materialName}/${getUniqueNodeName(node.aNode)}.outputs:out>`);
|
|
435
|
+
inputs.push(`float inputs:mix.connect = ${materialRoot}/${materialName}/${getUniqueNodeName(node.cNode)}.outputs:out>`);
|
|
436
|
+
}
|
|
437
|
+
break;
|
|
438
|
+
case "OperatorNode":
|
|
439
|
+
let opName = "";
|
|
440
|
+
switch (node.op) {
|
|
441
|
+
case "*" : opName = "multiply"; break;
|
|
442
|
+
case "/" : opName = "divide"; break;
|
|
443
|
+
case "+" : opName = "add"; break;
|
|
444
|
+
case "-" : opName = "subtract"; break;
|
|
445
|
+
}
|
|
446
|
+
usdShadeNodeName = opName + "_" + mtlxNdType;
|
|
447
|
+
if (node.aNode && !node.bNode)
|
|
448
|
+
inputs.push(`${getUsdType(node.aNode["nodeType___needle"])} inputs:in.connect = ${materialRoot}/${materialName}/${getUniqueNodeName(node.aNode)}.outputs:out>`);
|
|
449
|
+
if (node.aNode && node.bNode) {
|
|
450
|
+
const aNodeType = getUsdType(node.aNode["nodeType___needle"])
|
|
451
|
+
const bNodeType = getUsdType(node.bNode["nodeType___needle"])
|
|
452
|
+
|
|
453
|
+
// todo: make this more generic / support all combinations
|
|
454
|
+
if (aNodeType === 'color3f' && bNodeType === 'float' || bNodeType === 'float' && bNodeType === 'color3f') {
|
|
455
|
+
usdShadeNodeName = opName + "_color3FA";
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
inputs.push(`${aNodeType} inputs:in1.connect = ${materialRoot}/${materialName}/${getUniqueNodeName(node.aNode)}.outputs:out>`);
|
|
459
|
+
inputs.push(`${bNodeType} inputs:in2.connect = ${materialRoot}/${materialName}/${getUniqueNodeName(node.bNode)}.outputs:out>`);
|
|
460
|
+
}
|
|
461
|
+
break;
|
|
462
|
+
case "TextureNode":
|
|
463
|
+
if (node.uvNode){
|
|
464
|
+
usdShadeNodeName = "tiledimage_" + mtlxNdType;
|
|
465
|
+
|
|
466
|
+
inputs.push(`float2 inputs:texcoord.connect = ${materialRoot}/${materialName}/${getUniqueNodeName(node.uvNode)}.outputs:out>`);
|
|
467
|
+
}
|
|
468
|
+
else{
|
|
469
|
+
usdShadeNodeName = "image_" + mtlxNdType;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
const texture = node._value;
|
|
473
|
+
const isRGBA = formatsWithAlphaChannel.includes( texture.format );
|
|
474
|
+
const textureName = texName(texture);
|
|
475
|
+
|
|
476
|
+
inputs.push( `asset inputs:file = @textures/${textureName}.${isRGBA ? 'png' : 'jpg'}@` );
|
|
477
|
+
textures[ textureName ] = { texture, scale: undefined };
|
|
478
|
+
break;
|
|
479
|
+
case "NormalMapNode":
|
|
480
|
+
usdNdType = "float3"
|
|
481
|
+
usdShadeNodeName = "normalmap";
|
|
482
|
+
|
|
483
|
+
inputs.push(`${usdNdType} inputs:in.connect = ${materialRoot}/${materialName}/${getUniqueNodeName(node.node)}.outputs:out>`);
|
|
484
|
+
break;
|
|
485
|
+
case "AttributeNode":
|
|
486
|
+
usdShadeNodeName = "geompropvalue_" + mtlxNdType;
|
|
487
|
+
inputs.push(`string inputs:geomprop = "st"`);
|
|
488
|
+
|
|
489
|
+
break;
|
|
490
|
+
case "ShaderCallNodeInternal":
|
|
491
|
+
usdShadeNodeName = node["shaderNodeLayoutName___needle"] + "_" + mtlxNdType;
|
|
492
|
+
inputs.push(`${usdNdType} inputs:in.connect = ${materialRoot}/${materialName}/${getUniqueNodeName(node.inputNodes[0])}.outputs:out>`);
|
|
493
|
+
|
|
494
|
+
break;
|
|
495
|
+
case "SplitNode":
|
|
496
|
+
usdShadeNodeName = "swizzle_" + getType(node.node["nodeType___needle"]) + "_" + mtlxNdType;
|
|
497
|
+
inputs.push(`${getUsdType(node.node["nodeType___needle"])} inputs:in.connect = ${materialRoot}/${materialName}/${getUniqueNodeName(node.node)}.outputs:out>`);
|
|
498
|
+
inputs.push(`string inputs:channels = "${node.components}"`);
|
|
499
|
+
break;
|
|
500
|
+
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// todo: better way to pad here for sure...
|
|
504
|
+
return `
|
|
505
|
+
${pad}def Shader "${getUniqueNodeName(node)}"
|
|
506
|
+
${pad}{
|
|
507
|
+
${pad}uniform token info:id = "ND_${usdShadeNodeName}"
|
|
508
|
+
${pad}${usdNdType} outputs:out
|
|
509
|
+
${pad}${inputs.length > 0 ? inputs.join("\n ") : ""}
|
|
510
|
+
${pad}}
|
|
511
|
+
`;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
function getShadersFromNodes( nodes: Set<any>, materialName: string, textures: TextureMap, getUniqueNodeName){
|
|
515
|
+
|
|
516
|
+
let shaderOutput = "";
|
|
517
|
+
for (const node of nodes) {
|
|
518
|
+
shaderOutput += TSLNodeToUsdShadeString(node, materialName, getUniqueNodeName, textures);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
return shaderOutput;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
function texName(tex: Texture) {
|
|
525
|
+
// If we have a source, we only want to use the source's id, not the texture's id
|
|
526
|
+
// to avoid exporting the same underlying data multiple times.
|
|
527
|
+
return makeNameSafeForUSD(tex.name) + '_' + (tex.source?.id ?? tex.id);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
export {
|
|
531
|
+
buildNodeMaterial
|
|
532
|
+
};
|
|
@@ -93,8 +93,6 @@ export class BloomEffect extends PostProcessingEffect {
|
|
|
93
93
|
radius: 0.85, // default value
|
|
94
94
|
intensity: this.intensity.value,
|
|
95
95
|
});
|
|
96
|
-
// @ts-ignore Well... it's protected but bloom needs this :(
|
|
97
|
-
bloom.setAttributes(EffectAttribute.CONVOLUTION);
|
|
98
96
|
}
|
|
99
97
|
|
|
100
98
|
this.intensity.onValueChanged = newValue => {
|
|
@@ -6,6 +6,7 @@ import { showBalloonWarning } from "../../engine/debug/index.js";
|
|
|
6
6
|
import { MODULES } from "../../engine/engine_modules.js";
|
|
7
7
|
import { Context } from "../../engine/engine_setup.js";
|
|
8
8
|
import { Graphics } from "../../engine/engine_three_utils.js";
|
|
9
|
+
import { Constructor } from "../../engine/engine_types.js";
|
|
9
10
|
import { getParam } from "../../engine/engine_utils.js";
|
|
10
11
|
import { Camera } from "../Camera.js";
|
|
11
12
|
import { threeToneMappingToEffectMode } from "./Effects/Tonemapping.utils.js";
|
|
@@ -166,6 +167,7 @@ export class PostProcessingHandler {
|
|
|
166
167
|
for (const effect of res) {
|
|
167
168
|
this._effects.push({
|
|
168
169
|
effect,
|
|
170
|
+
typeName: component.typeName,
|
|
169
171
|
priority: component.order
|
|
170
172
|
});
|
|
171
173
|
}
|
|
@@ -173,6 +175,7 @@ export class PostProcessingHandler {
|
|
|
173
175
|
else {
|
|
174
176
|
this._effects.push({
|
|
175
177
|
effect: res,
|
|
178
|
+
typeName: component.typeName,
|
|
176
179
|
priority: component.order
|
|
177
180
|
});
|
|
178
181
|
}
|
|
@@ -244,7 +247,11 @@ export class PostProcessingHandler {
|
|
|
244
247
|
const tonemapping = new MODULES.POSTPROCESSING.MODULE.ToneMappingEffect();
|
|
245
248
|
tonemapping.name = `ToneMapping (${renderer.toneMapping})`;
|
|
246
249
|
tonemapping.mode = threeToneMappingToEffectMode(renderer.toneMapping);
|
|
247
|
-
this._effects.push({
|
|
250
|
+
this._effects.push({
|
|
251
|
+
typeName: "ToneMapping",
|
|
252
|
+
effect: tonemapping,
|
|
253
|
+
priority: PostProcessingEffectOrder.ToneMapping
|
|
254
|
+
});
|
|
248
255
|
}
|
|
249
256
|
}
|
|
250
257
|
|
|
@@ -363,7 +370,8 @@ export class PostProcessingHandler {
|
|
|
363
370
|
const effectsToMerge: Array<Effect> = [];
|
|
364
371
|
let hasConvolutionEffectInArray = false;
|
|
365
372
|
|
|
366
|
-
for (
|
|
373
|
+
for (let i = 0; i < this._effects.length; i++) {
|
|
374
|
+
const entry = this._effects[i];
|
|
367
375
|
const ef = entry.effect;
|
|
368
376
|
|
|
369
377
|
if (ef instanceof MODULES.POSTPROCESSING.MODULE.SMAAEffect) {
|
|
@@ -373,8 +381,7 @@ export class PostProcessingHandler {
|
|
|
373
381
|
this._anyPassHasNormal = true;
|
|
374
382
|
}
|
|
375
383
|
|
|
376
|
-
|
|
377
|
-
// There can be only one tonemapping effect in the scene, so we can skip all others
|
|
384
|
+
// There can be only one tonemapping effect in the scene, so we skip all others
|
|
378
385
|
if (ef instanceof MODULES.POSTPROCESSING.MODULE.ToneMappingEffect && activeTonemappingEffect !== ef) {
|
|
379
386
|
// If we already have a tonemapping effect, we can skip this one
|
|
380
387
|
continue;
|
|
@@ -383,11 +390,13 @@ export class PostProcessingHandler {
|
|
|
383
390
|
// We can also not merge multiple effects of the same type in one pass
|
|
384
391
|
// So we first need to create a new pass with whatever effects we have so far
|
|
385
392
|
// TODO: this seems to work fine for some effects (like ColorAdjustments) and only caused issues with multiple Tonemapping effects so far which is handled above
|
|
386
|
-
// const constructor = ef.constructor
|
|
387
|
-
// if(effectsToMerge.find(e => e.constructor === constructor)) {
|
|
388
|
-
//
|
|
393
|
+
// const constructor = ef.constructor;
|
|
394
|
+
// if (effectsToMerge.find(e => e.constructor === constructor)) {
|
|
395
|
+
// this.createPassForMergeableEffects(effectsToMerge, composer, cam, scene);
|
|
389
396
|
// }
|
|
390
397
|
|
|
398
|
+
|
|
399
|
+
|
|
391
400
|
if (ef instanceof MODULES.POSTPROCESSING.MODULE.Effect) {
|
|
392
401
|
const attributes = ef.getAttributes();
|
|
393
402
|
const convolution = MODULES.POSTPROCESSING.MODULE.EffectAttribute.CONVOLUTION;
|
|
@@ -415,28 +424,44 @@ export class PostProcessingHandler {
|
|
|
415
424
|
this.createPassForMergeableEffects(effectsToMerge, composer, cam, scene);
|
|
416
425
|
composer.addPass(ef);
|
|
417
426
|
}
|
|
427
|
+
|
|
418
428
|
}
|
|
419
429
|
|
|
420
430
|
this.createPassForMergeableEffects(effectsToMerge, composer, cam, scene);
|
|
421
431
|
}
|
|
422
432
|
catch (e) {
|
|
423
433
|
console.error("Error while applying postprocessing effects", e);
|
|
434
|
+
composer.passes.forEach(p => p.dispose());
|
|
424
435
|
composer.removeAllPasses();
|
|
425
436
|
}
|
|
426
437
|
|
|
427
438
|
// The last pass is the one that renders to the screen, so we need to set the gamma correction for it (and enable it for all others)
|
|
428
|
-
|
|
439
|
+
let foundEnabled = false;
|
|
440
|
+
for (let i = composer.passes.length - 1; i >= 0; i--) {
|
|
429
441
|
const pass = composer.passes[i];
|
|
430
|
-
|
|
442
|
+
let gammaCorrect = false;
|
|
443
|
+
let renderToScreen = false;
|
|
444
|
+
if (pass.enabled) {
|
|
445
|
+
if (!foundEnabled) {
|
|
446
|
+
gammaCorrect = true;
|
|
447
|
+
renderToScreen = true;
|
|
448
|
+
}
|
|
449
|
+
foundEnabled = true;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
pass.renderToScreen = renderToScreen;
|
|
453
|
+
|
|
431
454
|
if ((pass as any)?.configuration !== undefined) {
|
|
432
|
-
(pass as any).configuration.gammaCorrection =
|
|
455
|
+
(pass as any).configuration.gammaCorrection = gammaCorrect;
|
|
433
456
|
}
|
|
434
457
|
else if ("autosetGamma" in pass) {
|
|
435
458
|
// Some effects have a autosetGamma property that we can use to set the gamma correction
|
|
436
|
-
pass.autosetGamma =
|
|
459
|
+
pass.autosetGamma = gammaCorrect;
|
|
437
460
|
}
|
|
438
461
|
|
|
462
|
+
|
|
439
463
|
this._anyPassHasDepth ||= pass.needsDepthTexture;
|
|
464
|
+
|
|
440
465
|
}
|
|
441
466
|
|
|
442
467
|
// DEBUG LAND BELOW
|