- Home /
Loadimage Texture at runtime
Hi,
I want to update an image of a texture at runtime, but not in start(). I receive some image by socket, and I want to load this image in a texture. How can I do ? There is an error, Unity want I call this function in stat() or MainThread. WebCamTexture do this, so I hope I can.
Laurent
private void ReceiveCallback(IAsyncResult AR)
{
//Check how much bytes are recieved and call EndRecieve to finalize handshake
int recieved = _clientSocket.EndReceive(AR);
if (recieved <= 0)
return;
//Copy the recieved data into new buffer , to avoid null bytes
byte[] recData = new byte[recieved];
Buffer.BlockCopy(_recieveBuffer, 0, recData, 0, recieved);
try
{
var base64EncodedBytes = System.Convert.FromBase64String(Encoding.ASCII.GetString(recData, 0, recieved));
Stream str = new MemoryStream(base64EncodedBytes);//, 0, bytesRec);
StartCoroutine("Test", text);
text.LoadImage(base64EncodedBytes);
Mg.mainTexture = text;
//Image img = System.Drawing.Image.FromStream(str);
//pictureBox2.Image = img;
}
catch (Exception ex)
{
//label3.Text = ex.ToString();
}
//Process data here the way you want , all your bytes will be stored in recData
//Start receiving again
_clientSocket.BeginReceive(_recieveBuffer, 0, _recieveBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), null);
byte[] msg = Encoding.ASCII.GetBytes("30");
SendData(msg);
}
Error message :
IsObjectMonoBehaviour can only be called from the main thread. Constructors and field initializers will be executed from the loading thread when loading a scene. Don't use this function in the constructor or field initializers, instead move initialization code to the Awake or Start function. UnityEngine.MonoBehaviour:StartCoroutine(String, Object) socket:ReceiveCallback(IAsyncResult) (at Assets/socket.cs:59) System.Net.Sockets.Worker:Receive()
You should add more informations in the topic, that is what's your current code and what's exactly not working (including eventually the exception message). You're not forced to change the texture of a material in Start, so I suppose you're misunderstanding the exception raised.
in the unity examples i have seen that before using loadtexture they usually make a new small texture first.
before loading the texture try adding:
text = new Texture2D(2,2);
text.LoadImage(your data);
Answer by SkaredCreations · Oct 09, 2018 at 02:44 PM
The exception is raised because you can call LoadImage only in the main thread but your code is running asynchronously through BeginReceive/EndReceive that run in a new thread.
There's a dispatcher on Github posted by a user that can fit your case: click here. Import the file UnityMainThreadDispatcher.cs in your project and attach it to one of your game objects (i.e. the one that contains also your socket script, there should be only one in a scene).
Then pass the local component instance as state object to BeginReceive (last parameter this instead of null):
_clientSocket.BeginReceive(_recieveBuffer, 0, _recieveBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), this);
Finally you will have to cast AR.AsyncState back to your script type and enqueue the logic in UnityMainThreadDispatcher:
YourScriptName me = (YourScriptName)AR.AsyncState;
UnityMainThreadDispatcher.Instance().Enqueue(() =>
{
me.text.LoadImage(base64EncodedBytes);
me.Mg.mainTexture = me.text;
});
Same problem
In fact, I want to call loadimage in a function like update(), not in start() or awake(). How can I do ?
private void ReceiveCallback(IAsyncResult AR) { //Check how much bytes are recieved and call EndRecieve to finalize handshake int recieved = _clientSocket.EndReceive(AR);
if(recieved <= 0)
return;
//Copy the recieved data into new buffer , to avoid null bytes
byte[] recData = new byte[recieved];
Buffer.BlockCopy(_recieveBuffer,0,recData,0,recieved);
try
{
var base64EncodedBytes = System.Convert.FromBase64String(Encoding.ASCII.GetString(recData, 0, recieved));
Stream str = new $$anonymous$$emoryStream(base64EncodedBytes);//, 0, bytesRec);
socket me = (socket)AR.AsyncState;
me.text.LoadImage(base64EncodedBytes);
me.$$anonymous$$g.mainTexture = text;
}
catch(Exception ex)
{
//label3.Text = ex.ToString();
}
_clientSocket.BeginReceive(_recieveBuffer,0,_recieveBuffer.Length,SocketFlags.None,new AsyncCallback(ReceiveCallback),this);
byte[] msg = Encoding.ASCII.GetBytes ("30");
SendData (msg);
}
Same error :
LoadImage can only be called from the main thread. Constructors and field initializers will be executed from the loading thread when loading a scene. Don't use this function in the constructor or field initializers, ins$$anonymous$$d move initialization code to the Awake or Start function. UnityEngine.ImageConversion:LoadImage(Texture2D, Byte[]) socket:ReceiveCallback(IAsyncResult) (at Assets/socket.cs:59) System.Net.Sockets.Worker:Receive()
Ok so the thing is that the method LoadImage (as the exception says) needs to be called on the main thread of Unity, which is not possible for your current code because you're working asynchronously through BeginReceive/EndReceive.
On Github there's a dispatcher that (if it still works, as it was released by a user for Unity 5.x) could be useful for you.
If that dispatcher works then you just need to move the logic into an inline function:
socket me = (socket)AR.AsyncState;
Unity$$anonymous$$ainThreadDispatcher.Instance().Enqueue(() =>
{
me.text.LoadImage(base64EncodedBytes);
me.$$anonymous$$g.mainTexture = text;
});
EDIT Ok I've tested the above dispatcher and it works in 2017.* (and hopefully also in later), here is a small script that I've used to test (pressing 'S' loads the image with the dispatcher from a new thread):
using System.Threading;
using UnityEngine;
public class TestLoadImage : $$anonymous$$onoBehaviour {
public string textureUri;
public Renderer target;
Texture2D tex;
void Start()
{
tex = new Texture2D(2, 2);
target.material.mainTexture = tex;
}
void Update ()
{
if (Input.Get$$anonymous$$eyDown($$anonymous$$eyCode.A))
{
LoadNewImage();
}
if (Input.Get$$anonymous$$eyDown($$anonymous$$eyCode.S))
{
LoadInNewThread();
}
}
void LoadInNewThread()
{
object source = this;
new Thread(() =>
{
Unity$$anonymous$$ainThreadDispatcher.Instance().Enqueue(() =>
{
TestLoadImage me = (TestLoadImage)source;
me.LoadNewImage();
});
}).Start();
}
void LoadNewImage()
{
try
{
var content = System.IO.File.ReadAllBytes(textureUri);
tex.LoadImage(content);
}
catch (System.Exception e)
{
Debug.LogError(e.$$anonymous$$essage);
}
}
}
I have edited my answer with complete explanation.