Hi,
in my app i want to change the materials of some nodes on run time.
The nodes are objects from Blender, imported via ogreXML.
What’s the best way to do this.
You need to query the geometries out of the node and then call setMaterial on them.
Read the scene graph and materials tutorials.
Even when you only have 1 geometry when exporting, it can be nested within a few nodes (very annoying). I proposed a solution where there existed a spatial.getMaterial()/node.getMaterial(), which would find the first geometry and extract the material from there and return it. But @pspeed didn’t like it, I added the methods to my local version though and I use them all the time its so handy
I can see why pspeed wouldn’t like it. That sort of “fuzzy” behaviour is very fragile and liable to break in unexpected ways.
@zarch said:
I can see why pspeed wouldn't like it. That sort of "fuzzy" behaviour is very fragile and liable to break in unexpected ways.
And the code to do the right thing is not so bad.
If you just want to set all of the Geometry to the same material then you can call node.setMaterial() and it will pass it down to the children.
If setting all Geometry's material to the same Material would be bad then you will have to visit the nodes/geometry properly and deal directly with their material.
In neither of these cases is getting just some random Geometry's material useful.
actually its quite useful, and one of things i wish existed when i started. I don’t think you export many models from blender yet Paul?
For instance, if I have a system like so (which i commonly do):
node → node → geometry
I either have to traverse the whole tree until i find a geometry (which is what my spatial.getMaterial() effectively does), or do something like java.getMaterial(); [/java] (which i did before i knew what traversing even was).
when i could just do spatial.getMaterial() <333 i’m not sure how it can break tbh. Then its up to the user to do what they want with it.
@wezrule said:
actually its quite useful, and one of things i wish existed when i started. I don't think you export many models from blender yet Paul?
For instance, if I have a system like so (which i commonly do):
node -> node -> geometry
I either have to traverse the whole tree until i find a geometry (which is what my spatial.getMaterial() effectively does), or do something like [java]((Geometry)((Node)((Node)spatial).getChild(0)).getChild(0)).getMaterial(); [/java] (which i did before i knew what traversing even was).
when i could just do spatial.getMaterial() <333 i'm not sure how it can break tbh. Then its up to the user to do what they want with it.
It will break if you have more than one texture. Since much of what Blender does is magic then it is very common that you might have more than one material, I guess.
For example, the model I use has three Materials. Grabbing the first one might as well just pick one at random for all of it's utility.
That it works in your isolated edge-case is amazing but not something to base an API on.
For the record, a proper solution would be to use something like:
http://hub.jmonkeyengine.org/javadoc/com/jme3/scene/SceneGraphVisitorAdapter.html
with:
http://hub.jmonkeyengine.org/javadoc/com/jme3/scene/Spatial.html#depthFirstTraversal(com.jme3.scene.SceneGraphVisitor)
The same “blender magic” that is adding so many Node layers might also be the same magic that adds multiple Geometries… and thus multiple Materials.
yeh thats what I used before, its ok tho I just keep the getMaterial() method for myself then <333
Thanks a lot for the quick answers.
I’ll describe my problem once more in detail:
I create an Object with Blender. For that Object i have to use uvi-mapping and it has sometimes two or three materials combined with different textures.
I can load that Object via ogreXML into the JME and then those materials and textures are loaded, too.
But when i load one more Object, my programm checks wether that Object has been loaded before and when that’s true, it clones it, to make loading a bit faster.
For example i loaded a plant with some green textured leaves. But there are more plants in the area which are visible for the user. Some of them have the same main Object but different materials. So i want to clone the Node and load for it some different material-files. Maybe later i want to change the scene that way to confuse the user but mainly i want to get a better performance.
Also is it very interesting to manipulate materials on run time.
you can clone it and you can set different material, but you need to specify that material by loading j3m file or creating it via code.
but… you propably want to use BatchNode (it do what you want)
check TestBatchNode.java
there also is something like “GeometryBatchFactory.optimize(rootNode);”
also you can manipulate materials/shaders.
you need to read last wezrule post about shader tutorial to know what to change exacly in material to keep a good performance(without reloading material shaders).
The system already caches materials/textures/etc for you in the asset manager…
Thanks a lot for the answers once more.
I’ll take a look at the things you told me. When i have more questions i’ll post them.
@wezrule said:
actually its quite useful, and one of things i wish existed when i started. I don't think you export many models from blender yet Paul?
For instance, if I have a system like so (which i commonly do):
node -> node -> geometry
I either have to traverse the whole tree until i find a geometry (which is what my spatial.getMaterial() effectively does), or do something like [java]((Geometry)((Node)((Node)spatial).getChild(0)).getChild(0)).getMaterial(); [/java] (which i did before i knew what traversing even was).
when i could just do spatial.getMaterial() <333 i'm not sure how it can break tbh. Then its up to the user to do what they want with it.
mhhhh why not fetch the geom with its name node.getChild("childName").getMaterial();. It works with any scenegraph depth...
Most of the time I don’t know/care about the name, and normally its garbage? And sometimes I have to export by different formats for the same model and it changes it (i think, not sure), the node structure defininately does. I would rather everything be encapsulated in 1 class, it already has a getMaterial() and I’m sure there’s other stuff which works on the first occurence of finding something.
I also don’t wanna have to bother searching the SDK (like I had to do before) for the geom name, and also strings have no validator, so its prone to human errors.
As I said tho, don’t add it
I usually make sure every Spatial of my scenegraph has a name and as much as possible a unique name. At least for the spatial i’ll have to manipulate.
This makes the fetching operation a lot simpler.
so i thought i would try your method as i was exporting something from blender :P, this is the simplest I could get it, still looks quite bad imo
[java]Spatial room = assetManager.loadModel("Models/Room.j3o");
((Geometry) ((Node) room).getChild("Plane.0061")).getMaterial();[/java]
And what is it that you need the material for again?
for this example i was gonna set the DiffuseMap
@wezrule said:
for this example i was gonna set the DiffuseMap
I was just wondering if it was something that could have been done in the j3o itself when using the SDK.