While exporting mesh to .babylon file and checking option:
World Properties > Babylon.js ver 6.2.3 > Use PBR Materials
I got when Loading the scene with SceneLoader.Append a situation that I would like to modify ambientColor for all materials in mesh.materials array.
for (let index = 0; index < scene.materials.length; index++) {
const material = scene.materials[index];
material.ambientColor = new Color3(1, 1, 1);
}
And linter for typescript is throwing an error:
Property âambientColorâ does not exist on type
Ok, so I cast the material to PBRMaterial:
const material = scene.materials[index] as PBRMaterial;
And this error is gone but then I got a new one:
error Do not use any type assertions @typescript-eslint/consistent-type-assertions
The solution to this is for me now:
const material = scene.materials[index] as PBRMaterial; // eslint-disable-line @typescript-eslint/consistent-type-assertions
But I think if Iâm selecting Use PBR Materials my materials in the scene will all be PBRMaterials and they should have a proper meshes type so Materials[] | PBRMaterials[].
It fails cause I got PBRMaterials and TypeScript assume they will be Materials.
I will like to loop over materials in the mesh to change its ambientColor but I canât cause I got errors so I need to suppress casting error to get around it.
SceneLader expects mesh have Material and not PBRMaterial. That is the problem. If he would expect one of them would be great but it doesnât.
But it doesnât. I know its extended but TS is calling this on error.
Check yourself.
This is the problem. This is a tooltip out of VSCode when you hover over scene word in for loop in this code::
SceneLoader.Append('/3d/', 'scene.babylon', scene, scene => {
for (let index = 0; index < scene.materials.length; index++) {
const pbrMaterial = scene.materials[index] as PBRMaterial;
pbrMaterial.ambientColor = new Color3(1, 1, 1);
}
});
Tooltop:
(property) AbstractScene.materials: Material
All of the materials added to this scene In the context of a Scene, it is not supposed to be modified manually. Any addition or removal should be done using the addMaterial and removeMaterial Scene methods. Note also that the order of the Material within the array is not significant and might change.
So AbstractScene.materials: Material is purely expected to be Materials type and if you export PBRMaterials they are not and they are still Material type. So they do not have property ambientColor and if you cast them you got above error
First, there is no such thing as a PBRMaterial. It is BABYLON.PBRMaterial. That is an error for sure.
Also, I have found when Typescript has a exclusionary if, it permits things that it would not otherwise.
SceneLoader.Append('/3d/', 'scene.babylon', scene, scene => {
for (const material of scene.materials) {
if (material instanceOf BABYLON.PBRMaterial) {
material.ambientColor = new Color3(1, 1, 1);
}
}
});
You could do a cast inside the if. Coming from Java, pbrMat = <BABYLON.PBRMaterial> material, looks better to me.
Sorry, I do not get it. This is not Java itâs JavaScript with a TypeScript which is very specific what is what.
Checking if something is instanceof is not cutting it.
The situation is very simple. You should allow having a different Material coming from mesh and not only Material but also PBRMaterials, especially cause in Blender exporter there is an option âUse PBRMaterials onlyâ tick box. So this tick box is not supported fully in the code.
If you import Babylon in a specific way you do not need âBABYLON.â in front of any Babylon class.
Also, I report issues cause I do not have time to fix it for you.
Itâs a minor change so I do not see a problem for somebody from your team to implement it.
Also, TS is a pain in the ⌠and pleas test your solutions before you suggest them. This situation is running around in circles and there is no other solution for me wright no when suppressing one of the errors.
But you got now the solution that you could and should implement to make this all right
@peterimg, May I ask why preventing completely the use of type assertions in your project ???
This is what does not work here, not ts or js or babylon but the fact that you do not want type assertion.
The loader and scenes are designed to host any kind of materials so obviously the common type between them is Material. Now you want to filter on only one type cause you do not want to cast so if you look into typescript functionalities with type guards:
Iâm not rude. Iâm just pointing out that this is not working with typescrypt. I still got errors in this situation.
This issue is about typescript, not Babylon not working. So without typescript strict policies you think that It works. Cause it does but itâs giving errors.
The problem is the errors, and not code not working.
So your example itâs not having TypeScript strict rules that we have. Itâs working for me in every option itâs just giving my TS errors in different places depending on the implementation.
@peterimg, I am sorry but I think you are completely mistaken and that your attitude does not help in the slightest.
The error does not come from TS, Babylon or the tools you use but from your OWN code. Your configuration of ts eslint is preventing the use of type assertion and you need it here. In many cases it is good to be strict but it is also good to understand when it is totally safe to bypass those rules or why they would be here in the first place.
For instance how would you be able to use the DOM. Let say you go through the children of an element (they would be of type Element ) and you want to cast as a VideoElement which is totally valid as long as you check it is well a VideoElement. You will end up having the same exact behavior.
At some point, you have a list containing the base type to group all of them in e.g. Element or Material in the babylon case. This is basic programming where you would have a list of Animal containing Fish, Cat and Bird and while going through the list you want all the birds to Fly(). So you would iterate, cast to Bird and call Fly(). If you use any helper filtering on types, they would do the same internally anyway. Please keep in mind we do not have just StandardMaterial or PBRMaterial.
You could argue we should have different lists like one per kind but how would this scale when you introduce more types ?
If you know which type it is and you can test it (with instanceof or other means like getClassName() in babylon). It is totally safe to âcastâ. Please note I put cast in quote cause it is not a real cast like in other programming languages, these are just helping the transpiler with types yo make your code safer and they won t appear in JS.
Searching a bit in the TS doc would lead you to Advanced Types ¡ TypeScript where you can see the use for it in TS as well.
So you have 4 options:
Disable the strict rule from never to as in your lint config
Disable the rule on the cast line as this one would be totally safe
Use the filter function I shared with you previously to not disable the rule
function getPBRs(materials: Material[]): PBRMaterial[] {
return materials.filter(
(item): item is PBRMaterial => item.getClassName() === 'PBRMaterial',
);
}
for (let m of getPBRs(scene.materials)) {
m.ambientColor = null;
}
Create your own typeguard test to not disable the rule
function isPBR(material: Material): material is PBRMaterial {
return material.getClassName() === "PBRMaterial";
}
for (let m of scene.materials) {
if (isPBR(m)) {
m.ambientColor = null;
}
}
Cause it is totally different, you do not have an array of one or the other but it could be an array of mixed materials: (Material | PBRMaterial | âŚ)[]
And what about ShaderMaterial, GridMaterial, BackgroundMaterial and all the custom ones ? as well as the one Babylon does not provide but are provided externally ?
Also please note that even with the union type, you would need to cast at some points to specify yours is PBR.
Your issue should not be solved in the Babylon code itself as the framework needs to handle any kind of materials and the Material type here is the correct one.