Unity WebGL freezes when loading files

Hello. I am trying to build a scene that will load an OBJ file and JPG texture file, from Azure Storage, and display in the scene. This code works fine when running in Unity, but when I export it as WebGL and run it in my browser, it freezes the browser and doesn't seem to run. For the setup, I have a virtual Azure Cloud emulator running (Azurite). No error is thrown, it just freezes. Here is my Unity code:

public void RunCode(string key)
{
Console.WriteLine("Running code with key: " + key);
string authorization, date;
Key = key;

try
{
Blobs(out authorization, out date, "MyObj_1.obj");
using (WebClient client = new WebClient())
{
client.Headers.Add("Authorization", authorization);
client.Headers.Add("x-ms-date", date);
client.Headers.Add("x-ms-version", apiversion);
var res = client.DownloadString(objURL + "MyObj_1.obj");

MemoryStream textStream = new MemoryStream(Encoding.UTF8.GetBytes(res));
if (model != null)
{
Destroy(model);
}
model = new OBJLoader().Load(textStream);
model.transform.localScale = new Vector3(-1, 1, 1);
model.transform.localPosition = new Vector3(10, -1164, 530);
LoadTexture();
}

}
catch(Exception e)
{
Console.WriteLine(e);
}
}

private void LoadTexture()
{
string authorization, date;
Blobs(out authorization, out date, "MyImg.jpg");

using (WebClient client = new WebClient())
{
client.Headers.Add("Authorization", authorization);
client.Headers.Add("x-ms-date", date);
client.Headers.Add("x-ms-version", apiversion);
byte[ ] textureData = client.DownloadData(objURL + "MyImg.jpg");
Texture2D texture = new Texture2D(2, 2);
texture.LoadImage(textureData);

Renderer renderer = model.GetComponentInChildren();
if (renderer != null)
{
renderer.material.mainTexture = texture;
}

}
}

private void Blobs(out string auth, out string date, string blob)
{
DateTime dt = DateTime.UtcNow;
string StringToSign = String.Format("GET\n"
+ "\n" // content encoding
+ "\n" // content language
+ "\n" // content length
+ "\n" // content md5
+ "\n" // content type
+ "\n" // date
+ "\n" // if modified since
+ "\n" // if match
+ "\n" // if none match
+ "\n" // if unmodified since
+ "\n" // range
+ "x-ms-date:" + dt.ToString("R") + "\nx-ms-version:" + apiversion + "\n" // headers
+ "/{0}/{0}/{1}/{2}", Account, Container, blob);

string signature = SignThis(StringToSign, Key, Account);

auth = string.Format(
CultureInfo.InvariantCulture,
"{0} {1}:{2}",
"SharedKey",
Account,
signature);

date = dt.ToString("R");
}

static String SignThis(String StringToSign, string Key, string Account)
{
String signature = string.Empty;
byte[ ] unicodeKey = Convert.FromBase64String(Key);
using (HMACSHA256 hmacSha256 = new HMACSHA256(unicodeKey))
{
Byte[ ] dataToHmac = System.Text.Encoding.UTF8.GetBytes(StringToSign);
signature = Convert.ToBase64String(hmacSha256.ComputeHash(dataToHmac));
}

return signature;
}

On the JS side, I then call the function like this:

let GameInstance = null;

window.UnityFunction = () => {
GameInstance.SendMessage("ObjModel", "RunCode", "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==");

};
window.initializeUnity = () => {
var container = document.querySelector("#unity-container");
var canvas = document.querySelector("#unity-canvas");
var loadingBar = document.querySelector("#unity-loading-bar");
var progressBarFull = document.querySelector("#unity-progress-bar-full");
var fullscreenButton = document.querySelector("#unity-fullscreen-button");
var warningBanner = document.querySelector("#unity-warning");

function unityShowBanner(msg, type) {
function updateBannerVisibility() {
warningBanner.style.display = warningBanner.children.length ? 'block' : 'none';
}
var div = document.createElement('div');
div.innerHTML = msg;
warningBanner.appendChild(div);
if (type == 'error') div.style = 'background: red; padding: 10px;';
else {
if (type == 'warning') div.style = 'background: yellow; padding: 10px;';
setTimeout(function () {
warningBanner.removeChild(div);
updateBannerVisibility();
}, 5000);
}
updateBannerVisibility();
}

var buildUrl = "Build/Build";
var loaderUrl = buildUrl + "/Build.loader.js";
var config = {
dataUrl: buildUrl + "/Build.data",
frameworkUrl: buildUrl + "/Build.framework.js",
codeUrl: buildUrl + "/Build.wasm",
streamingAssetsUrl: "StreamingAssets",
companyName: "DefaultCompany",
productName: "AzureRemoteRendering",
productVersion: "0.1",
showBanner: unityShowBanner,
};
if (/iPhone|iPad|iPod|Android/i.test(navigator.userAgent)) {
var meta = document.createElement('meta');
meta.name = 'viewport';
meta.content = 'width=device-width, height=device-height, initial-scale=1.0, user-scalable=no, shrink-to-fit=yes';
document.getElementsByTagName('head')[0].appendChild(meta);
container.className = "unity-mobile";
canvas.className = "unity-mobile";

unityShowBanner('WebGL builds are not supported on mobile devices.');
} else {
canvas.style.width = "960px";
canvas.style.height = "600px";
}

loadingBar.style.display = "block";

var script = document.createElement("script");
script.src = loaderUrl;
script.onload = () => {
createUnityInstance(canvas, config, (progress) => {
progressBarFull.style.width = 100 * progress + "%";
}).then((unityInstance) => {
loadingBar.style.display = "none";
fullscreenButton.onclick = () => {
unityInstance.SetFullscreen(1);
};
GameInstance = unityInstance;
}).catch((message) => {
alert(message);
});
};
document.body.appendChild(script);
};

Nevermind. I moved these methods into a Coroutine and it works well. thanks!

1 Like

Ungh. Unity has UnityWebRequest class for this. You can yield it in a coroutine.

PS: please use code tags next time.