I'm having issues trying to create a capture of a set of object for my menu (like the Icon Generator Icon Generator - Core Documentation), but the capture is running before some of the more complex materials load when I run it in multiplayer testing. The single player preview mode works fine. After the asset is loaded once, it starts working every time I re-generate a capture. So, this appears to be an issue with the materials not being preloaded prior to spawning and the assets, and the capture running far quicker than it can load.
The code is pretty simple, and runs when the player connects on the client side:
local asset = World.SpawnAsset(ITEM_TEMPLATE, { parent = CONTAINER })
previewCapture = CAMERA:Capture(CameraCaptureResolution.LARGE)
ITEM_IMAGE:SetCameraCapture(previewCapture)
asset:Destroy()
The Items are not all the same and some are combinations of multiple templates parented in sequence or loaded from player configurations, so I can't pre-load the assets or images for every player and combination.
I tried:
- adding a Task.Wait() with various intervals between loading and spawning, but it doesn't really change anything since the materials still haven't been used.
- adding Task.Wait() between spawning the asset taking the capture, but that just spawns everything on top of each other since Wait() is non-blocking
- adding a queue, then spawning them in order with a Wait(), but that takes too long since I don't know how long to wait for each thing and end up waiting too long for everything (and looks awful for a UI)
I'm basically looking for something simple, like ":WaitForObject()" from CoreObjectReference, but for World.SpawnAsset(). Maybe I'm just thinking about it wrong, but it doesn't seem like it should be that hard to say "don't do THIS until THAT is actually done". If I can't do that, can I force it to preload materials? Do I have to spawn a bunch of invisible templates first?
Here are some other suggestions to try:
Are you sure the object is being spawned directly on the client side and not propagated from the server, i.e. have you tried changing your first line to be:
local asset = World.SpawnAsset(ITEM_TEMPLATE, { parent = CONTAINER, networkContext = NetworkContextType.CLIENT_CONTEXT })
This will force network enabled templates into a client only context. If your spawn code is in a client context this should not be an issue.
You can have multiple camera capture boxes, so you can set them up in parallel to speed the process of capturing many images even with Task.Wait().
Have you considered having dynamic properties on the item to flag when your material update scripts have completed and using those to green light the capture activation? I expect what is happening is that you have code on your templates that is changing materials on spawn and this code has not completed before the capture - perhaps I have misunderstood?
I kind of have a hack in place for this now. I just put everything that I wanted loaded in a group under the client context called "Preload" and the first step I do in the client side script is to destroy that. With that loading the materials first, everything is ready by the time it captures the first time. The only problem there is that it will be messy to maintain that as I add other objects and materials in the future. I suppose I can script that to walk through the templates and load them, but this feels like an excessive hack for this problem.
I did try setting it to CLIENT_CONTEXT, but it was already set that way since it was spawned from a client script and into a client only object. I added some debug output just to check:
[BOT_Bot1][2022.06.24-09.54.29]item 1 isNetworked: false
[BOT_Bot1][2022.06.24-09.54.29]item 1 isClientOnly: true
For the Materials, they're just regular Core materials with no changes. I didn't even get to the point where I can update the materials yet in the script, since these ones aren't loading as is. I wanted to get the basics working before I get any further. At this point though, I feel that changing the material now might be better, since that would be a blocking action in line with the script before the capture. Is there any way to pull a status on the material or any object to see if it's actually loaded and ready? I can't use Object:IsValid() or .visibility, since that is just for the Static Mesh. I tried walking the ancestors for the template and checking all the mesh material slots, but the results just show what should be there even when it isn't loaded yet.
But, as an example of what I mean by complex materials, most of the objects are something simple like Plastic Matte or Metals with a color, and those load fine. The ones that don't have either an effect (Emissive Glow Transparent or Bubble Glass Opaque) or are layered (Corrugated Metal or Greebles).
I don't think there is any way to validate the state of a material, particularly as you say it is shown as being assigned to the mesh. All of the problematic materials that you mention are the ones that don't play nicely with merged model either. I wonder if they are dependent upon some additional shader code that is somehow not run fast enough for the capture. Sorry I don't have a real solution for you, I'll add a link to this on the discord feedback channel. Discord
The preload hack seems to be working pretty well. I used a handful of other material types and made much more complicated templates, and it still loads up fine for the capture. There is no noticeable delay in loading the menu scene either.
Since I am using a Data Table to store all my item info, I'm just going to add it to the startup script to walk this table, load and destroy each item at launch. It's not a pretty fix, but it's working for now.