blog games developers documentation portfolio gallery

Documentation:

Running in background thread

If you want to create LOD levels at runtime and don't want the screen to freeze, you have the option to run the process in a background thread.

For this you can use the GameObject extension functions:

public static IEnumerator SetUpLODLevelsWithLODSwitcherInBackground(
float[] lodScreenSizes, float[] maxWeights, bool recalcNormals, float removeSmallParts)
public static IEnumerator GetSimplifiedMeshInBackground(float maxWeight, bool recalcNormals, float removeSmallParts, int nrOfSteps = 1, System.Action result)


Don't forget to put

using OrbCreationExtensions;
in your code file to include the extension.

To execute these functions you do this:

public void MakeLodsInBackground() {
StartCoroutine(myGameObject.SetUpLODLevelsWithLODSwitcherInBackground(
new float[3] {0.4f, 0.2f, 0.1f}, // relative screen sizes to switch lods
new float[3] {0.3f, 0.8f, 1.5f}, // compression values
true,
1f));
StartCoroutine(myGameObject.GetSimplifiedMeshInBackground(
0.8f, // compression value
true, // recalculate normals
1f, // remove small parts
2, // in 2 steps
retval => decimatedMesh = retval // catch the mesh
));
}


Here's a full example of importing an OBJ model with SimpleOBJ and then deciding if it is too big for the game and decimating it. All run in the background to interfere with the game as little as possible.


using UnityEngine;
using System.Collections;

using OrbCreationExtensions;

public class ImportInBackground : MonoBehaviour {

private string path = "http://orbcreation.com/SimpleObj/mushroom.obj";
private GameObject importedGameObject;

void Start() {
StartCoroutine(DownloadAndImportFile(path));
}

private IEnumerator DownloadAndImportFile(string url) {
Quaternion rotate = Quaternion.identity;
Vector3 scale = Vector3.one;
Vector3 translate = Vector3.one;
bool gameObjectPerGrp = false;
bool subMeshPerGrp = false;
bool usesRightHandedCoordinates = false;

string objString = null;
string mtlString = null;
Hashtable textures = null;

yield return StartCoroutine(DownloadFile(url, retval => objString = retval));
yield return StartCoroutine(DownloadFile(url.Substring(0,url.Length-4)+".mtl", retval => mtlString = retval));
if(mtlString!=null && mtlString.Length>0) {
string path = url;
int lastSlash = path.LastIndexOf('/',path.Length-1);
if(lastSlash>=0) path = path.Substring(0,lastSlash+1);
Hashtable[] mtls = ObjImporter.ImportMaterialSpecs(mtlString);
for(int i=0;i<mtls.Length;i++) {
if(mtls[i].ContainsKey("mainTexName")) {
Texture2D texture = null;
string texUrl = path+mtls[i]["mainTexName"];
yield return StartCoroutine(DownloadTexture(texUrl, retval => texture = retval));
if(texture != null) {
if(textures == null) textures = new Hashtable();
textures[mtls[i]["mainTexName"]] = texture;
}
}
}
}

if(objString!=null && objString.Length>0) {

yield return StartCoroutine(ObjImporter.ImportInBackground(objString, mtlString, textures, rotate, scale, translate, retval => importedGameObject = retval, gameObjectPerGrp, subMeshPerGrp, usesRightHandedCoordinates));

Debug.Log("Done importing model");
if(importedGameObject!=null) {
// rename the object if needed
if(importedGameObject.name == "Imported OBJ file") {
string[] path = url.Split(new char[] {'/', '.'});
if(path.Length > 1) importedGameObject.name = path[path.Length - 2];
}

// place the bottom on the floor
Bounds overallBounds = GetBounds(importedGameObject);
importedGameObject.transform.position = new Vector3(0, overallBounds.min.y * -1f, 0);

int meshCount, subMeshCount, vertexCount, triangleCount;
GetModelInfo(importedGameObject, out meshCount, out subMeshCount, out vertexCount, out triangleCount);
if(meshCount > 1) { // multiple meshes, let's merge them into 1
importedGameObject.CombineMeshes();
}

if(vertexCount > 10000) {
// Let's add some LOD levels
yield return StartCoroutine(importedGameObject.SetUpLODLevelsWithLODSwitcherInBackground(
new float[3] {0.4f, 0.2f, 0.1f}, // relative screen sizes to switch lods
new float[3] {0.3f, 0.8f, 1.5f}, // compression values
false));
}
}
}
}

private Bounds GetBounds(GameObject go) {
Bounds goBounds = new Bounds(Vector3.zero, Vector3.zero);
Renderer[] renderers = go.GetComponentsInChildren<Renderer>();
foreach (Renderer r in renderers) {
Bounds bounds = r.bounds;
goBounds.Encapsulate(bounds);
}
return goBounds;
}

private void GetModelInfo(GameObject go, out int meshCount, out int subMeshCount, out int vertexCount, out int triangleCount) {
meshCount = 0;
subMeshCount = 0;
vertexCount = 0;
triangleCount = 0;
MeshFilter[] meshFilters = go.GetComponentsInChildren<MeshFilter>();
if(meshFilters!=null) meshCount = meshFilters.Length;
foreach (MeshFilter mf in meshFilters) {
Mesh mesh = mf.mesh;
subMeshCount += mesh.subMeshCount;
vertexCount += mesh.vertices.Length;
triangleCount += mesh.triangles.Length / 3;
}
}

private IEnumerator DownloadFile(string url, System.Action<string> result) {
Debug.Log("Downloading "+url);
WWW www = new WWW(url);
yield return www;
if(www.error!=null) {
Debug.Log(www.error);
} else {
Debug.Log("Downloaded "+www.bytesDownloaded+" bytes");
}
result(www.text);
}

private IEnumerator DownloadTexture(string url, System.Action<Texture2D> result) {
Debug.Log("Downloading "+url);
WWW www = new WWW(url);
yield return www;
if(www.error!=null) {
Debug.Log(www.error);
} else {
Debug.Log("Downloaded "+www.bytesDownloaded+" bytes");
}
result(www.texture);
}


}
































follow us