What is the easiest way to change the texture of a TCastleScene? For example, I have two files: red.jpg and yellow.jpg. I want to change an athlete’s jersey.
Note that TCastleScene represents a 3D model with potentially multiple shapes, each one can have material that can use multiple textures (for various purposes – base texture, metallic/roughness, normal map etc.).
The general way to replace all occurrences of red.jpg → yellow.jpg is to find all TImageTextureNode instances inside your Scene.RootNode, and change their FdUrl value. The FdUrl is a list of URLs from which the texture is loaded (usually just 1 URL, but in general case it can list fallbacks).
Do something like this:
procedure TMyView.ReplaceTexture;
begin
MyScene.RootNode.EnumerateNodes(TImageTextureNode,
{$ifdef FPC}@{$endif} HandleImageTextureNode, false);
end;
procedure TMyView.HandleImageTextureNode(Node: TX3DNode);
var
ImageTexture: TImageTextureNode;
begin
ImageTexture := Node as TImageTextureNode;
// log texture found
WritelnLog('Found TImageTextureNode with %d URLs:', [ImageTexture.FdUrl.Count]);
for I := 0 to ImageTexture.FdUrl.Count - 1 do
WritelnLog('URL %d is %s', [I, ImageTexture.FdUrl[I]]);
if ImageTexture.FdUrl.Items.IndexOf('red.jpg') <> -1 then
ImageTexture.SetUrl(['yellow.jpg']);
end;
Hope this helps
As you see, I added above logging of the texture URLs actually found – you should check the log, what URLs are actually used by your model, and potentially adjust the replacement code to detect/set it correctly.
Please note that I didn’t test whether the above code compiles now, it’s possible I made some small mistakes
If anything is unclear or doesn’t work, let me know.
I’ll try to explain myself better: imagine a Formula 1 game. The cars are 3D models to which you need to apply a texture (MyCar.LoadTexture(FilePath + MyTexture.bmp)). Or imagine a soccer game (MyPlayer.LoadTexture(FilePath + jersey.bmp)). I searched through all the documentation and there doesn’t seem to be a way in Castle Game Engine to load textures on the fly.
The key part is exactly here:
This works when your model already contains a texture like red.jpg. CGE loads it automatically, so you can later find it by name and replace it.
As @michalis mentioned a TCastleScene can contain many shapes and many textures. Which means, simple MyPlayer.LoadTexture(FilePath + jersey.bmp) can not be implemented. Searching by filename guarantees you replace the correct one.
In case when your model has exactly one texture, you could skip the loop and use index 0. However, as soon as you add more textures (e.g., roughness, metallic, normal maps), this breaks. More troubles than benefit in my opinion.
I myself use models with multiple change-able textures - for example birch or oak wooden cupboard can also have silver or black hinges. To replace the textures, I use algorithm very similar to the one above. It’s very simple.
I may be wrong but it sounds like the model can be texture-less (ie. exported with no any textures) and you need to apply ‘red.jpg’, ‘yellow.jpg’, etc. only at runtime. In that scenario things are considerably less straight forward.
In such case the model is loaded as is, with empty texture list, so you’ll have an extra job setting things up manually.
The easiest workflow is to include a dummy texture in the model (even a 1x1 pixel file), or use the ‘red.jpg’. Once the model has any texture, CGE loads it, and then you can replace it at runtime using the same code as above.
This avoids manually creating texture nodes in code.