Tasharen Entertainment Forum
Support => NGUI 3 Support => Topic started by: ChrisPruett on May 29, 2012, 11:55:09 AM
-
Sorry if this has been posed before--I searched the forums but didn't see it.
What is the correct way to change the mainTexture--but not the material--of a UIDrawTexture widget?
uiTexture.material.mainTexture = foo; isn't correct because UIWidget doesn't capture the change and does not mark the draw call for that material as dirty. Changing the entire material works because it causes the entire widget to be removed and re-added to its parent panel, which causes the draw call to be recomputed. There doesn't appear to be an accessor that updates mTex and marks the material as changed, which makes sense for other widgets (very rare to change the atlas texture, I guess) but not for UITexture.
Some detail for context: I have implemented an infinitely scrolling list of UITextures that represent contents loaded from the network. They are all instantiated from a prefab but get a unique material per UITexture so that there's no sharing. This material initially points at a "loading..." texture, which is then replaced by the bits that come over the wire sometime later. If I swap the material when the new texture arrives, or if I force the material to be dirtied by disabling and then re-enabling the UITexture (to force a call to OnEnable()), my new texture data shows up. If not, the texture in the material is changed but the draw call is not recreated and so the previous loading texture continues to show.
It seems like this is a bug, but it might be that I've just missed the correct way to talk to UITexture in this case.
Thanks!
Chris
-
uiTexture.material.mainTexture = yourNewTexture;
uiTexture.MarkAsChanged();
-
Thanks for the reply.
I played with MarkAsChanged() before posting but wasn't able to get it to produce accurate results. I've just tried again now, and the code flow appears correct (mChanged is toggled, which later causes UpdateGeometry() to return true, causing the material to be marked as dirty, etc). But when I run this code I get inconsistent results. The last material in the list changes correctly, but the remainder stick to the first texture I assign. As I scroll around in my list (causing images to load and unload), some UITextures update and others do not. This *looks* like a race condition in which mChanged somehow gets dropped when lots of things are changed in quick succession.
In contrast, if I allocate two materials, assign textures to them once, and then just swap materials when I want the texture to change, the results are 100% correct and predictable.
My use case isn't complicated. I'm setting the texture (or material), loading some bits via www, allocating a new texture, and assigning it. If I call MarkAsChanged() after every texture assignment, the results are inconsistent and mostly wrong (some assignments don't take). If I omit MarkAsChanged but alter the material pointer instead, it's all correct.
Cheers,
Chris
-
Not sure why it "wouldn't take" as you say. You're swapping the material's texture. Why would material still reference an old texture? You're the one who swapped it. Do you have objects marked as static or something?
-
Not sure why it "wouldn't take" as you say. You're swapping the material's texture. Why would material still reference an old texture? You're the one who swapped it. Do you have objects marked as static or something?
Nothing is static--I considered that too. I didn't follow the UIWidget code all the way through but I assume that it generates some verts and packages up a material to pass down for rendering every frame. I assume that this draw call package holds its own references to the materials it is initialized with, which is why a method like MarkAsChanged() is required to dirty the call. The behavior I am seeing looks like the changed flag is being overwritten before the call is updated, or possibly the changes are lost at some other point; in any case the result is that the material is changed but the widget continues to draw with whatever it had before. And since it's inconsistent and hard to catch in the debugger (I tried a bit today), my guess was a race condition.
But that's all just conjecture. For now I can work around the issue by swapping materials rather than textures. If there's anywhere else I should poke please let me know.
Cheers,
Chris
-
The way I'm doing this, if you want to display a "loading sprite", is that in my row prefab (the row of the list), I have :
- a UISprite (with the loading image)
- a UITexture that is "empty"
Then when I receive the texture from the network I just do :
NGUITools.SetActive(m_loadingSprite.gameObject, false);
And set the texture, using a method similar to the one you can find at NGUI/Examples/Scripts/Other/DownloadTexture.cs
I don't know if it helps, but I hope so!
-
I am experiencing the same issue as ChrisPruett. I made a scroll list that reuses its items, so I need to reset the mainTexture of UITexture widgets. When I reset the texture, I see in the inspector that it is changed, but in the game window it does not always change. I called MaskAsChanged() and it did not work. However, I noticed (ChrisPruett as well), that it works properly if you disable and enable the widget. This way, I am doing a hack for now:
sprite.enabled = false;
StartCoroutine("ReEnableUITextureSprite"); // Routine re-enables the sprite!
Anyway, I hope we can figure out what the issue is.
-
There was a bug in an earlier version of NGUI that cause the drawcall to not update properly.
What version are you using?
As a dirty hack, you can switch on depthPass and switch it back off in the same frame - that will force a refresh (twice).
-
Cool, I'll try the depthPass hack. I am using the version 2.2.3.
-
You need to upgrade. Keep in mind old versions are not supported, and 2.2.3 is very, very old.