-
Notifications
You must be signed in to change notification settings - Fork 104
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Handle materials referenced by name specified outside the link object #72
Conversation
Hey @Chaitanya-Deep, thanks for contributing! This looks like a great change. Do you have an publicly available URDF files I can use to test it? Thanks! |
javascript/URDFLoader.js
Outdated
@@ -403,23 +412,40 @@ class URDFLoader { | |||
} | |||
|
|||
// Process the <link> nodes | |||
_processLink(link, packages, path, loadMeshCb) { | |||
_processLink(link, materials, packages, path, loadMeshCb) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe a nit pick but can we change the materials
argument here to materialMap
so it's consistent?
javascript/URDFLoader.js
Outdated
}); | ||
} else { | ||
const name = n.getAttribute('name'); | ||
this._processMaterial(material, materialMap[name].type, materialMap[name].value, packages, path); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see that the _processMaterial
function takes an existing material and modifies it so each mesh has its own material instance. Considering these are links that share material nodes, does it make sense to preprocess material nodes into THREE.Material
objects up front and share them across many THREE.Mesh
instances? That way fewer material objects will be created. This also means that the meshes will share any material updates.
I'm also noticing we will always load a new texture when processing a material, even if that texture has been used elsewhere, which is maybe the bigger issue, memory-wise. I'll create a separate issue for that, though.
Edit: Created the texture duplication issue here #73
Hey, I'm not so sure about sharing materials. For the scenario that I'm using the library for, sharing a material object will be unexpected. If the library is used solely for viewing a urdf, that might be necessary. In my use case when a user loads a robot with two parts having the same color, he would want to select a part and change it's color rather than change the color value for the name the color corresponds to. |
javascript/URDFLoader.js
Outdated
} | ||
}); | ||
} else { | ||
material.copy(materialMap[materialName]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using Material.copy
concerns me a little bit because it does a lot more than just copy the color and texture and it doesn't work well when using different material types. new THREE.MeshStandardMaterial().copy(new THREE.MeshLambertMaterial())
throws an error, for example, so it's not always safe to call copy on a material of a different type. And if someone has created a custom material as a part of the mesh load function then things like the specular values will be overwritten. It would overwrite the color even if it wasn't specified in the URDF, too.
How do you feel about copying just those specific values:
const sharedMatData = materialMap[materialName];
if (sharedMatData.color) material.color.copy(sharedMatData.color);
if (sharedMatData.map) material.map = sharedMatData.map;
And then the material map could store just those preprocessed values in an object. The color and texture node parsing could be pulled out into separate functions that return the texture and color objects.
Sounds fine for that use case -- I don't think I have strong opinions about this. We may want to consider sharing materials in the future if there's a need, though. And thanks for handling the texture duplication in the shared materials case! |
Made the change. |
javascript/URDFLoader.js
Outdated
this.forEach(materials, m => { | ||
const name = m.getAttribute('name'); | ||
this.forEach(m.children, c => { | ||
materialMap[name] = materialMap[name] || this._processMaterial( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using ||
like this means that once the first object is created the return this._processMaterial
function will never get called and the parameters defined by the second node will never get used. I'd suggest passing an object in to the _processMaterial
function. Maybe something like:
const name = m.getAttribute('name');
materialMap[name] = {};
this.forEach(m.children, c => {
this._processMaterial(materialMap[name], c, packages, path)
});
That way _processMaterial
can modify the existing object and add any fields it needs to.
javascript/URDFLoader.js
Outdated
} | ||
}); | ||
} else { | ||
Object.assign(material, materialMap[materialName]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using Object.assign
here won't work well for fields like color
because then every instance of the materials used here will have the same instance of the THREE.Color
object. Using material.color.copy(materialMap[materialName].color)
seems like the better way to handle the color assignment.
It may be tedious but it might be necessary to handle every field assignment individually.
javascript/URDFLoader.js
Outdated
const rgba = value.split(/\s/g) | ||
.map(v => parseFloat(v)); | ||
return { | ||
color: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be an instance of THREE.Color
if we're planning to do a copy or assignment of the object to a material. Another option would be to keep it like this but use the THREE.Color.setRGB
function when updating the material.
Thanks for bearing with me on the changes!
Hey @Chaitanya-Deep, I wanted to get these changes merged in before I started reorganizing some files in the repo -- would you like me to help wrap up some remaining changes and get this merged in? Thanks! |
Hi, I'm slightly confused regarding the The assumption here is that two texture nodes with the same name will have the same texture hence the one in materialMap can be used rather than loading the texture again. |
Great! Looks like you've covered everything. I'll give it more of a test in a bit and try to get it merged today.
The problem with this logic in the const name = m.getAttribute('name');
this.forEach(m.children, c => {
materialMap[name] = materialMap[name] || this._processMaterial(c, packages, path);
}); Is that it will not include processing any of the information past the first material information node. Consider: <material name="blue">
<color rgba="0 0 .8 1"/>
<texture filename=".../texture.png"/>
</material> The |
No description provided.