How to apply a shader to all items in a scene
Hiya,
I have 600 scenes using a simple shader that I created in Shader Builder, which has a diffuse texture and opacity texture, and nothing else. I've updated the shader (via a copy, and deleted the original) to include a new string field, layerName, which is used while post-processing the Rib file. Manually updating the shaders would likely take something like 20 hours of solid uninterrupted work, especially since it seems to hang when applying to complex objects like Gen2.
So the best bet seemed to be to write a script which goes through all items in a scene, and applies the new shader, then saves again (possibly even filling out the layerName via hints from the object names etc, but doing that manually wouldn't be as bad, it's the applying shaders that hangs for a minute or two and would add up to a lot of waiting). I can open a scene, iterate the items, and there's examples for how to save it again so I'm not currently worried about that.
But I've come to several dead ends after trying to work out how shaders are referenced and applied in scripts for nearly 9 hours (at this point it might have been faster to do it manually, heh). I know that I can get the material & shader names on each surface for objects in a scene, and could merge in a simple invisible cube with the new shader applied to get it or something, if there's no way to access it from the global Shader Builder list, but actually understanding where to apply the shader, why different materials apparently don't have the same shader instance on them when compared, etc, has left me scratching my head with no example code or documentation. So far all I've managed is to find the surfaces and material names, but the materials & shaders don't seemed to be shared instances or anything.
DoIt = function()
{
var nodes = Scene.getNodeList();
var obj;
for (var n=0 ; n;
var name = mat.getName(); // legs etc - i.e. the surfaces
var matName = mat.getMaterialName(); // shader name
var shader = mat.getShader(0);
var shaderName = shader.getName(); // also shader name
}
}
}
}
DoIt();
Comments
Woo, I did it. It's a bit messy, but here's my rough solution. It presumes that I'll merge in an invisible object with the new shader on it.
You could try saving an old-style, scripted shader preset - File>Save as>Deprecated>Shader Preset - as an ASCII script and pulling that apart to see how to build a brick shader directly, rather than using a donor object.
Thanks so much, I'll look into that, I was actually just trying to scan the global variables to find an instance of the material/shader without having to import a donor cube.
Well that was a billion times easier, and I just discovered a way to generate extensive example code to help make sense of the API.
So if I have a shader DUF (either in ShaderMixer or ShaderBuilder format) and I want the script to apply it everywhere in the scene, how would I go about setting this script to do that?
The above should nearly work, the only changes you'd need to make is remove the conditional where it checks for 'MyShader', and use the menu command mentioned above to generate a script where you can find the "DefinitionFile" value (in my case, "support/ShaderBuilder/Surface/MyShader_NewDef.dsa").
So if I get this to work for a commercial package, you want credit, right? Or just a cut of the first check? ;)
Hah, I think that Richard deserves most of the credit for knowing about the auto generate code button, which is where most of it came from.
There seems to be a bug in the above code for some materials, removing the setName call fixes it but then leaves the mats without an internal name. (though they still have a label)