//******************************************************************************************************************************************** //*********************************** Whirld - by Aubrey Falconer **************************************************************************** //**** http://AubreyFalconer.com **** http://web.archive.org/web/20120519040400/http://www.unifycommunity.com/wiki/index.php?title=Whirld **** //******************************************************************************************************************************************** using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Runtime.CompilerServices; //using Ionic.Zlib; using System.IO.Compression; using UnityEngine; [Serializable] public enum WhirldInStatus { Idle, Working, Success, WWWError, SyntaxError } [Serializable] public class WhirldIn : System.Object { public WhirldInStatus status = WhirldInStatus.Idle; public string statusTxt = ""; public float progress = 0.00f; public string info = ""; public string url = ""; public string data; public GameObject world; public GameObject whirldBuffer; public string worldName = "World"; public string urlPath; public Hashtable worldParams = new Hashtable(); public Hashtable threads = new Hashtable(); public int threadAssetBundles = 0; public int threadTextures = 0; public int maxThreads = 5; public List loadedAssetBundles = new List(); public Hashtable objects = new Hashtable(); public Hashtable textures = new Hashtable(); public Hashtable meshMaterials = new Hashtable(); public Hashtable meshMatLibs = new Hashtable(); public MonoBehaviour monoBehaviour; //Needed for attaching Coroutines too public int readChr = 0; public void Load() { whirldBuffer = new GameObject("WhirldBuffer"); monoBehaviour = (MonoBehaviour)whirldBuffer.AddComponent(typeof(MonoBehaviourScript)); monoBehaviour.StartCoroutine(Generate()); } public void Cleanup() { //We are still loading the world if ((bool)whirldBuffer && (bool)monoBehaviour) { monoBehaviour.StopAllCoroutines(); GameObject.Destroy(whirldBuffer); } //Unload AssetBundles if (loadedAssetBundles.Count > 0) { foreach (AssetBundle ab in loadedAssetBundles) { ab.Unload(true); } loadedAssetBundles.Clear(); } } public IEnumerator Generate() { status = WhirldInStatus.Working; if (url != "") { //Download Whirld File statusTxt = "Downloading World Definition"; info = ""; urlPath = url.Substring(0, url.LastIndexOf("/") + 1); WWW www = new WWW(url); while (!www.isDone) { progress = www.progress; yield return new WaitForSeconds(0.1f); } progress = 1f; //Verify Successful Download if (www.error != null) { info = "Failed to download Whirld definition file: " + url + " (" + www.error + ")\n"; status = WhirldInStatus.WWWError; yield break; } data = www.data; } //Init readChr = 0; world = GameObject.Find("World"); if (world) GameObject.Destroy(world); world = new GameObject("World"); statusTxt = "Parsing World Definition"; //Sanity Check if ( data == null || data.Length < 10 || (data[0] != '[' && data[0] != '{')) { status = WhirldInStatus.SyntaxError; yield break; } //Read Whirld Headers String n = null; String v = null; while (true) { //Read next char char s = data[readChr]; readChr++; //Incorrectly nested header []s if (readChr >= data.Length) { status = WhirldInStatus.SyntaxError; yield break; } //Ignore Newlines and Tabs else if (s == '\n' || s == '\t') continue; else if (s == '{') break; //Finished reading headers else if (s == '[') //Beginning new header { n = ""; v = ""; } //Header name read, read value else if (s == ':' && n == "") { n = v; v = ""; } //Header ended else if (s == ']') { //[name] header if (n == "") { n = v; v = ""; } //AssetBundle if (n == "ab") monoBehaviour.StartCoroutine_Auto(LoadAssetBundle(v)); //StreamedScene if (n == "ss") monoBehaviour.StartCoroutine_Auto(LoadStreamedScene(v)); //Skybox else if (n == "rndSkybox") monoBehaviour.StartCoroutine_Auto(LoadSkybox(v)); //Texture else if (n == "txt") monoBehaviour.StartCoroutine_Auto(LoadTexture(v)); //Mesh else if (n == "msh") monoBehaviour.StartCoroutine_Auto(LoadMesh(v)); //Terrain else if (n == "trn") monoBehaviour.StartCoroutine_Auto(LoadTerrain(v)); //Rendering Settings else if ( n == "rndFogColor" || n == "rndFogDensity" || n == "rndAmbientLight" || n == "rndHaloStrength" || n == "rndFlareStrength") { String[] vS = v.Split(","[0]); if (n == "rndFogColor") { RenderSettings.fogColor = new Color( float.Parse(vS[0]), float.Parse(vS[1]), float.Parse(vS[2]), 1); } else if (n == "rndFogDensity") { RenderSettings.fogDensity = float.Parse(v); } else if (n == "rndAmbientLight") { RenderSettings.ambientLight = new Color( float.Parse(vS[0]), float.Parse(vS[1]), float.Parse(vS[2]), float.Parse(vS[3])); } else if (n == "rndHaloStrength") { RenderSettings.haloStrength = float.Parse(v); } else if (n == "rndFlareStrength") { RenderSettings.flareStrength = float.Parse(v); } } //Arbitrary Data else worldParams.Add(n, v); } //Header char read else v += s; } statusTxt = "Downloading World Assets"; //Wait for all "threads" to finish working while (threads.Count > 0) { yield return null; } //Generate World statusTxt = "Initializing World"; ReadObject(world.transform); //Add TerrainControllers to Terrain objects foreach (Terrain trn in GameObject.FindObjectsOfType(typeof(Terrain))) { ((TerrainController)trn.gameObject.AddComponent(typeof(TerrainController))).trnDat = trn.terrainData; } //Cleanup GameObject.Destroy(whirldBuffer); //Send Scene Generation Notice to each object foreach (GameObject go in GameObject.FindObjectsOfType(typeof(GameObject))) { go.SendMessage( "OnSceneGenerated", SendMessageOptions.DontRequireReceiver); } //Success! status = WhirldInStatus.Success; statusTxt = "World Loaded Successfully"; if (info != "") { Debug.Log("Whirld Loading Info: " + info); } } public IEnumerator LoadAssetBundle(string p) { threadAssetBundles++; while (threads.Count >= maxThreads) yield return null; //Don't overwhelm the computer by doing too many things @ once //Presets String thread = System.IO.Path.GetFileNameWithoutExtension(p); threads.Add(thread, ""); String url = p; //Download StreamedScene url = GetURL(url); WWW www = new WWW(url); while (!www.isDone) { threads[thread] = www.progress; yield return null; } if (www.error != null || !www.assetBundle) { if (!www.assetBundle) info += "Referenced file is not an AssetBundle: " + url + "\n"; else info += "Failed to download asset file: " + url + " (" + www.error + ")\n"; threads.Remove(thread); threadAssetBundles--; yield break; } //Load AssetBundle threads[thread] = "Initializing Bundle"; loadedAssetBundles.Add(www.assetBundle); //Success threads.Remove(thread); threadAssetBundles--; } public IEnumerator LoadStreamedScene(string p) { while (threads.Count >= maxThreads) yield return null; //Don't overwhelm the computer by doing too many things @ once //Presets String thread = "SceneData"; threads.Add(thread, ""); String nme = "World"; String url = "Whirld.unity3d"; //Object Parameters if (p != "") //[ss:sceneName,url] { String[] pS = p.Split(","[0]); if (pS[0] != null) nme = pS[0]; if (pS[1] != null) url = pS[1]; } //Download StreamedScene url = GetURL(url); WWW www = new WWW(url); while (!www.isDone) { threads[thread] = www.progress; yield return null; } if (www.error != null || !www.assetBundle) { if (!www.assetBundle) info += "StreamedScene file contains no scenes: " + url + "\n"; else info += "Failed to download asset file: " + url + " (" + www.error + ")\n"; threads.Remove(thread); yield break; } //Wait for all AssetBundles to load threads[thread] = "Loading Asset Dependencies"; while (threadAssetBundles > 0) yield return null; threads.Remove(thread); thread = "SceneInit"; threads.Add(thread, "..."); //Load StreamedScene AssetBundle blah = www.assetBundle; AsyncOperation async = Application.LoadLevelAdditiveAsync(nme); float tme = Time.time; while (!async.isDone) { threads[thread] = (Time.time - tme) + "..."; yield return null; } //Success loadedAssetBundles.Add(www.assetBundle); threads.Remove(thread); } public IEnumerator LoadTexture(string p) //[txt:name,url,wrapMode,anisoLevel] { threadTextures++; //Don't overwhelm the computer by doing too many things @ once while (threads.Count >= maxThreads) yield return null; String[] vS = p.Split(","[0]); String thread = "Txt" + threadTextures + " - " + vS[0]; threads.Add(thread, ""); String url = GetURL(vS[1]); WWW www = new WWW(url); while (!www.isDone) { threads[thread] = www.progress; yield return null; } if (www.error != null) { info += "Failed to download texture: " + url + " (" + www.error + ")\n"; threads.Remove(thread); threadTextures--; yield break; } threads[thread] = "Initializing"; //Texture2D txt = www.texture; Texture2D txt = new Texture2D( 4, 4, TextureFormat.DXT1, true); www.LoadImageIntoTexture(txt); txt.wrapMode = ( (vS[2] == null || float.Parse(vS[2]) == 0f) ? TextureWrapMode.Clamp : TextureWrapMode.Repeat); txt.anisoLevel = (vS[3] != null ? int.Parse(vS[3]) : 1); txt.Apply(true); txt.Compress(true); textures.Add(vS[0], txt); threads.Remove(thread); threadTextures--; } public IEnumerator LoadMeshTexture(string url, string materialName) { threadTextures++; //Don't overwhelm the computer by doing too many things @ once while (threads.Count >= maxThreads) yield return null; String thread = "MshTxt" + threadTextures + " - " + materialName; threads.Add(thread, ""); url = GetURL(url); WWW www = new WWW(url); while (!www.isDone) { threads[thread] = www.progress; yield return null; } if (www.error != null) { info += "Failed to download mesh texture: " + url + " (" + www.error + ")\n"; threads.Remove(thread); threadTextures--; yield break; } threads[thread] = "Initializing"; Texture2D mshTxt = new Texture2D( 4, 4, TextureFormat.DXT1, true); www.LoadImageIntoTexture(mshTxt); mshTxt.wrapMode = TextureWrapMode.Repeat; mshTxt.Apply(true); mshTxt.Compress(true); ((Material)meshMaterials[materialName]).mainTexture = mshTxt; threads.Remove(thread); threadTextures--; } public IEnumerator LoadMesh(string v) //[msh:name,url] { Mesh msh = new Mesh(); List verts = new List(); List norms = new List(); List uvs = new List(); List tris = new List(); List> triangles = new List>(); List mats = new List(); //Don't overwhelm the computer by doing too many things @ once while (threads.Count >= maxThreads) yield return null; //Init Thread String[] vS = v.Split(","[0]); String thread = vS[0]; threads.Add(thread, ""); //Download Mesh Object int hasCollider = (vS.Length > 2 ? int.Parse(vS[2]) : 0); WWW www = new WWW(GetURL(vS[1])); while (!www.isDone) { threads[thread] = www.progress; yield return null; } if (www.error != null) { info += "Failed to download mesh: " + url + " (" + www.error + ")\n"; threads.Remove(thread); yield break; } //Download All Textures Before Generating Mesh //threads[thread] = "Loading Textures"; //while(threadTextures > 0) yield return null; //Uncompress as necessary... threads[thread] = "Decompressing"; yield return null; //Rebuild GUI as we may be working for a while int lastDot = vS[1].LastIndexOf("."); String data; if (vS[1].Substring(lastDot + 1) == "gz") { //data = GZipStream.UncompressString(www.bytes); GZipStream gz = new GZipStream(new MemoryStream(www.bytes), CompressionMode.Decompress); byte[] buf = new byte[www.bytes.Length]; gz.Read(buf, 0, buf.Length); data = buf.ToString(); vS[1] = vS[1].Substring(0, lastDot); } else data = www.data; threads[thread] = "Generating"; lastDot = vS[1].LastIndexOf("."); String ext = vS[1].Substring(lastDot + 1); //Binary UnityMesh Object if (ext == "utm") { //MeshSerializer has been depricated - it's totally nonstandard, and it didn't support submeshes anyway //Mesh msh = MeshSerializer.ReadMesh(www.bytes); } //.obj File else if (ext == "obj") { float timer = Time.time + 0.1f; String[] file = data.Split("\n"[0]); foreach (String str in file) { if (str == "") continue; String[] l = str.Split(" "[0]); if (l[0] == "v") { verts.Add(new Vector3( -float.Parse(l[1]), float.Parse(l[2]), float.Parse(l[3]))); } else if (l[0] == "vn") { norms.Add(new Vector3( float.Parse(l[1]), float.Parse(l[2]), float.Parse(l[3]))); } else if (l[0] == "vt") { uvs.Add(new Vector2( float.Parse(l[1]), float.Parse(l[2]))); } else if (l[0] == "f") { if (l.Length == 4) { tris.Add(int.Parse(l[2].Substring( 0, l[2].IndexOf("/"))) - 1); tris.Add(int.Parse(l[1].Substring( 0, l[2].IndexOf("/"))) - 1); tris.Add(int.Parse(l[3].Substring( 0, l[2].IndexOf("/"))) - 1); } //Attempt to triangulate face - hardly works, could use better routine here... else { int i; for (i = 2; i < l.Length; i++) { tris.Add(int.Parse(l[i].Substring( 0, l[i].IndexOf("/"))) - 1); if (i % 2 == 0) { tris.Add(int.Parse(l[1].Substring( 0, l[1].IndexOf("/"))) - 1); } } while (tris.Count % 3 != 0) { tris.Add(int.Parse(l[i = 2].Substring( 0, l[i - 2].IndexOf("/"))) - 1); } } } else if (l[0] == "usemtl") { if (meshMaterials.ContainsKey(l[1])) { mats.Add((Material)meshMaterials[l[1]]); } else { info += "Mesh Material Missing: " + l[1] + "\n"; mats.Add(null); } if (tris.Count > 0) { triangles.Add(tris); tris = new List(); } } else if (l[0] == "mtllib") //Time to load a material library! { if (!meshMatLibs.ContainsKey(l[1])) { //Only load a material library once, even if it is referenced by multiple meshes meshMatLibs.Add(l[1], true); www = new WWW(GetURL(l[1])); while (!www.isDone) { threads[thread] = "Downloading Material Library (" + Mathf.RoundToInt(www.progress * 100) + "%)"; //yield return null; } if (www.error != null) { info += "Mesh Material Library Undownloadable: " + GetURL(l[1]) + " (" + www.error + ")\n"; } else { threads[thread] = "Initializing " + vS[0] + ""; //yield return null; String[] meshlib = www.data.Split("\n"[0]); Material curMat = null; int offset = -1; while (true) { offset = www.data.IndexOf("map_Ka", offset + 1); if (offset == -1) break; } foreach (String meshline in meshlib) { String[] ml = meshline.Split(" "[0]); if (ml[0] == "newmtl") //Beginning of new material { if (curMat) //Save current material { meshMaterials.Add(curMat.name, curMat); } curMat = new Material(Shader.Find("VertexLit")); curMat.name = ml[1]; } else if (ml[0] == "#Shader") //Set shader of current material { String shdr = meshline.Substring(8).Replace("Diffuse", "VertexLit"); if (shdr != "VertexLit" && shdr != "VertexLit Fast") { curMat.shader = Shader.Find(shdr); } } else if (ml[0] == "Ka") //Set color of current material { curMat.color = new Color( float.Parse(ml[1]), float.Parse(ml[2]), float.Parse(ml[3]), 1f); } else if (ml[0] == "Kd") { curMat.SetColor("_Emission", new Color( float.Parse(ml[1]), float.Parse(ml[2]), float.Parse(ml[3]), 1f)); } else if (ml[0] == "Ks") { curMat.SetColor("_SpecColor", new Color( float.Parse(ml[1]), float.Parse(ml[2]), float.Parse(ml[3]), 1f)); } else if (ml[0] == "Ns") { curMat.SetFloat("_Shininess", float.Parse(ml[1])); } else if (ml[0] == "map_Ka") //Set texture of current material { curMat.mainTextureOffset = new Vector2( float.Parse(ml[2]), float.Parse(ml[3])); curMat.mainTextureScale = new Vector2( float.Parse(ml[5]), float.Parse(ml[6])); monoBehaviour.StartCoroutine_Auto(LoadMeshTexture( ml[7], curMat.name)); } else if (ml[0] == "d") //Set alpha cutoff of current material { //curMat.shader = Shader.Find("Transparent/Cutout/VertexLit"); //curMat.SetFloat("_Cutoff", float.Parse(ml[1])); } } if (curMat) //Save last material (others get saved as file is read) { meshMaterials.Add(curMat.name, curMat); } } } } if (Time.time > timer) //Refresh GUI 10 times per second to keep the user entertained { timer = Time.time + 0.1f; yield return null; } } threads[thread] = "Initializing"; msh.vertices = verts.ToArray(); msh.normals = norms.ToArray(); msh.uv = uvs.ToArray(); if (triangles.Count > 0) { triangles.Add(tris); msh.subMeshCount = triangles.Count; for (int i = 0; i < triangles.Count; i++) { msh.SetTriangles(triangles[i].ToArray(), i); } } else msh.triangles = tris.ToArray(); } //Unknown File Type else info += "Mesh Type Unrecognized: " + vS[0] + " " + vS[1] + " (." + ext + ")\n"; if (hasCollider == 1) //This mesh is being created, and it has a renderer { GameObject mshObj = new GameObject(vS[0]); mshObj.AddComponent(typeof(MeshFilter)); ((MeshFilter)mshObj.GetComponent(typeof(MeshFilter))).mesh = msh; mshObj.AddComponent(typeof(MeshRenderer)); ((MeshRenderer)mshObj.GetComponent(typeof(MeshRenderer))).materials = mats.ToArray(); if (hasCollider != -1) //This mesh has a collider, and it is the same as it's rendered mesh { mshObj.AddComponent(typeof(MeshCollider)); ((MeshCollider)mshObj.GetComponent(typeof(MeshCollider))).sharedMesh = msh; } if (msh.uv.Length < 1) TextureObject(mshObj); objects.Add(vS[0], mshObj); mshObj.transform.parent = whirldBuffer.transform; } else //This mesh has a custom collider { if (objects.ContainsKey(vS[0])) //This mesh already exists, add a custom collider to it { GameObject mshObj = new GameObject(vS[0]); mshObj.AddComponent(typeof(MeshCollider)); ((MeshCollider)mshObj.GetComponent(typeof(MeshCollider))).sharedMesh = msh; objects.Add(vS[0], mshObj); mshObj.transform.parent = whirldBuffer.transform; } } msh.Optimize(); threads.Remove(thread); } //v = "name;r:width,height,length,heightmapResolution //,detailResolution,controlResolution,textureResolution;h:heightMapUrl"; public IEnumerator LoadTerrain(string v) { String[] vS2 = v.Split(";"[0]); String tName = vS2[0]; String[] tRes = null; String tHtmp = null; String tLtmp = null; String tSpmp = null; String tSpmp2 = null; String[] tTxts = null; // /*UNUSED*/ String tDtmp = null; for (int i2 = 1; i2 < vS2.Length; i2++) { String[] str = vS2[i2].Split(":"[0]); if (str[0] == "r") tRes = str[1].Split(","[0]); else if (str[0] == "h") tHtmp = GetURL(str[1]); else if (str[0] == "l") tLtmp = GetURL(str[1]); else if (str[0] == "s") tSpmp = GetURL(str[1]); else if (str[0] == "s2") tSpmp2 = GetURL(str[1]); else if (str[0] == "t") tTxts = str[1].Split(","[0]); //else if (str[0] == "d") tDtmp = GetURL(str[1]); } String thread = tName; threads.Add(thread, ""); WWW www = new WWW(tHtmp); while (!www.isDone) { threads[thread] = www.progress; yield return null; } if (www.error != null) { info += "Terrain Undownloadable: " + tName + " " + tHtmp + " (" + www.error + ")\n"; } else { threads[thread] = "Initializing"; //yield return null; int tWidth = int.Parse(tRes[0]); int tHeight = int.Parse(tRes[1]); int tLength = int.Parse(tRes[2]); int tHRes = int.Parse(tRes[3]); TerrainData trnDat = new TerrainData(); //Heights trnDat.heightmapResolution = tHRes; float[,] hmap = trnDat.GetHeights(0, 0, tHRes, tHRes); System.IO.BinaryReader br; if (true) //Terrain RAW file is compressed { GZipStream gz = new GZipStream(new MemoryStream(www.bytes), CompressionMode.Decompress); byte[] buf = new byte[www.bytes.Length]; gz.Read(buf, 0, buf.Length); br = new System.IO.BinaryReader(new System.IO.MemoryStream( buf)); } //else br = new System.IO.BinaryReader(new System.IO.MemoryStream(www.bytes)); for (int x = 0; x < tHRes; x++) { for (int y = 0; y < tHRes; y++) { hmap[x, y] = br.ReadUInt16() / 65535.00000000f; } } trnDat.SetHeights(0, 0, hmap); trnDat.size = new Vector3(tWidth, tHeight, tLength); //Textures SplatPrototype[] splatPrototypes = null; if (tTxts != null) { splatPrototypes = new SplatPrototype[tTxts.Length]; for (int i = 0; i < tTxts.Length; i++) { String[] splatTxt = tTxts[i].Split("="[0]); String[] splatTxtSize = splatTxt[1].Split("x"[0]); www = new WWW(GetURL(splatTxt[0])); while (!www.isDone) { //threads[thread] = "Initializing"; //yield return new WaitForSeconds(0.1f); } if (www.error != null) { info += "Terrain Texture Undownloadable: #" + (i + 1) + " (" + splatTxt[0] + ")\n"; } else { //yield return null; splatPrototypes[i] = new SplatPrototype(); splatPrototypes[i].texture = new Texture2D( 4, 4, TextureFormat.DXT1, true); www.LoadImageIntoTexture(splatPrototypes[i].texture); splatPrototypes[i].texture.Apply(true); splatPrototypes[i].texture.Compress(true); splatPrototypes[i].tileSize = new Vector2( int.Parse(splatTxtSize[0]), int.Parse(splatTxtSize[1])); } } } trnDat.splatPrototypes = splatPrototypes; //Lightmap if (tLtmp != null) { //whirld.statusTxt = "Downloading Terrain Lightmap (" + tName + ")"; www = new WWW(tLtmp); while (!www.isDone) { //whirld.progress = www.progress; //yield return new WaitForSeconds(0.1f); } if (www.error != null) { info += "Terrain Lightmap Undownloadable: " + tName + " " + tLtmp + " (" + www.error + ")\n"; } else { trnDat.lightmap = www.texture; } } //Splatmap if (tSpmp != null) { Color[] mapColors2 = null; if (tSpmp2 != null) { //whirld.statusTxt = "Downloading Augmentative Terrain Texturemap (" + tName + ")"; www = new WWW(tSpmp2); while (!www.isDone) { //whirld.progress = www.progress; //yield return new WaitForSeconds(0.1f); } mapColors2 = www.texture.GetPixels(); } //whirld.statusTxt = "Downloading Terrain Texturemap (" + tName + ")"; www = new WWW(tSpmp); while (!www.isDone) { //whirld.progress = www.progress; //yield return new WaitForSeconds(0.1f); } //whirld.statusTxt = "Mapping Terrain Textures..."; //yield return null; if (www.error != null) { info += "Terrain Texturemap Undownloadable: " + tName + " " + tLtmp + " (" + www.error + ")\n"; } else { trnDat.alphamapResolution = www.texture.width; float[, ,] splatmapData = trnDat.GetAlphamaps( 0, 0, www.texture.width, www.texture.width); Color[] mapColors = www.texture.GetPixels(); int ht = www.texture.height; int wd = www.texture.width; for (int y = 0; y < ht; y++) { for (int x = 0; x < wd; x++) { for (int z = 0; z < trnDat.alphamapLayers; z++) { if (z < 4) { splatmapData[x, y, z] = mapColors[x * wd + y][z]; } else splatmapData[x, y, z] = mapColors2[x * wd + y][z - 4]; } } } trnDat.SetAlphamaps(0, 0, splatmapData); } } //Go ! GameObject trnObj = new GameObject(tName); trnObj.AddComponent(typeof(Terrain)); ((Terrain)trnObj.GetComponent(typeof(Terrain))).terrainData = trnDat; trnObj.AddComponent(typeof(TerrainCollider)); ((TerrainCollider)trnObj.GetComponent(typeof(TerrainCollider))).terrainData = trnDat; objects.Add(tName, trnObj); //Delete this temporary terrain object AFTER world is fully loaded trnObj.transform.parent = whirldBuffer.transform; } threads.Remove(thread); } public IEnumerator LoadSkyboxTexture(string url, int dest) { threadTextures++; //Don't overwhelm the computer by doing too many things @ once while (threads.Count >= maxThreads) yield return null; //Presets String thread = "Skybox" + dest; threads.Add(thread, ""); //Download Skybox Image url = GetURL(url); WWW www = new WWW(url); while (!www.isDone) { threads[thread] = www.progress; yield return null; } threads.Remove(thread); threadTextures--; if (www.error != null) { info += "Failed to download skybox # " + dest + ": " + url + " (" + www.error + ")\n"; yield break; ; } Texture2D txt = new Texture2D( 4, 4, TextureFormat.DXT1, true); www.LoadImageIntoTexture(txt); txt.wrapMode = TextureWrapMode.Clamp; txt.Apply(true); txt.Compress(true); //Wait for everything else to load while (threads.Count > 0) yield return null; //Assign Texture to Skybox! if (dest == 0 || dest == 1) { RenderSettings.skybox.SetTexture("_FrontTex", txt); } if (dest == 0 || dest == 2) { RenderSettings.skybox.SetTexture("_BackTex", txt); } if (dest == 0 || dest == 3) { RenderSettings.skybox.SetTexture("_LeftTex", txt); } if (dest == 0 || dest == 4) { RenderSettings.skybox.SetTexture("_RightTex", txt); } if (dest == 0 || dest == 5) { RenderSettings.skybox.SetTexture("_UpTex", txt); } if (dest == 0 || dest == 6) { RenderSettings.skybox.SetTexture("_DownTex", txt); } } public IEnumerator LoadSkybox(string v) { String[] vS = v.Split(","[0]); //Multiple Image Skybox if (vS.Length > 5) { //Material skyMat = RenderSettings.skybox; //RenderSettings.skybox = new Material(); //RenderSettings.skybox.CopyPropertiesFromMaterial(skymat); LoadSkyboxTexture(vS[0], 1); LoadSkyboxTexture(vS[1], 2); LoadSkyboxTexture(vS[2], 3); LoadSkyboxTexture(vS[3], 4); LoadSkyboxTexture(vS[4], 5); LoadSkyboxTexture(vS[5], 6); //Wait for everything else to load while (threads.Count > 0) yield return null; if (vS.Length > 6) { RenderSettings.skybox.SetColor("_Tint", new Color( float.Parse(vS[6]), float.Parse(vS[7]), float.Parse(vS[8]), 0.5f)); } } //Single JPG image for all sides else if (vS[0].Substring(vS[0].LastIndexOf(".") + 1) == "jpg") { LoadSkyboxTexture(vS[0], 0); //Wait for everything else to load while (threads.Count > 0) yield return null; if (vS.Length > 1) { RenderSettings.skybox.SetColor("_Tint", new Color( float.Parse(vS[1]), float.Parse(vS[2]), float.Parse(vS[3]), 0.5f)); } } //AssetBundle Material Skybox else { //Wait for everything else to load while (threads.Count > 0) yield return null; RenderSettings.skybox = (Material)GetAsset(v); //, Material if (!RenderSettings.skybox) { info += "Skybox not found: " + v + "\n"; } } } public UnityEngine.Object GetAsset(string str) { if (loadedAssetBundles.Count > 0) { foreach (AssetBundle ab in loadedAssetBundles) { if (ab.Contains(str)) return ab.Load(str); } } return null; } public void ReadObject(Transform parent) { // /*UNUSED*/ string c = null; //Character int i = 0; //Index of param string n = ""; //Param name we are reading data for string v = ""; //Value we are building List d = new List(); //Array of all values in current param data GameObject obj = null; //Object we have created GameObject goP = default(GameObject); WhirldObject whirldObject = default(WhirldObject); Light lightSource = default(Light); while (true) { if (readChr >= data.Length) return; //Get Char char s = data[readChr]; //Ignore spaces if (s == ' ' || s == '\n' || s == '\t') { ; } //Name fully read, begin collecting param value(s) else if (s == ':') { n = v; v = ""; } //Move to next section of value else if (s == ',') { d.Add(v); v = ""; } //Move to next section of value else if (s == '{') { readChr++; ReadObject(obj.transform); //Continue to next obj once the child "thread" we just launched has finished parsing objects at it's level continue; } //Assign current value to object, Begin reading new value else if (s == ';' || s == '}') { //Object name just read, create object if (!obj) { if (objects.ContainsKey(v)) { if (objects[v] != null) { goP = (GameObject)objects[v]; } else { Debug.Log("Whirld: Objects[" + v + "] is null"); } //else goP = gameObject.Find(); } else { goP = (GameObject)Resources.Load(v); if ((bool)goP) objects.Add(v, goP); } if ((bool)goP) { obj = (GameObject)GameObject.Instantiate(goP); obj.name = v; } else { obj = new GameObject(v); objects.Add(v, obj); } if ( obj.name != "Base" && obj.name != "Sea" && obj.name != "JumpPoint" && obj.name != "Light") { obj.transform.parent = parent; } whirldObject = (WhirldObject)obj.GetComponent(typeof(WhirldObject)); if ((bool)whirldObject) { whirldObject.parameters = new Hashtable(); } lightSource = (Light)obj.GetComponent(typeof(Light)); } //Object already created, assign property to object else { if ( (n == "p" || (n == "" && i == 1)) && d.Count == 2) { obj.transform.localPosition = new Vector3( float.Parse(d[0]), float.Parse(d[1]), float.Parse(v)); } else if ( n == "p" || (n == "" && i == 1)) { obj.transform.localPosition = Vector3.one * float.Parse(v); } else if ( (n == "r" || (n == string.Empty && i == 2)) && d.Count == 3) { obj.transform.rotation = new Quaternion( float.Parse(d[0]), float.Parse(d[1]), float.Parse(d[2]), float.Parse(v)); } else if ( (n == "r" || (n == string.Empty && i == 2)) && d.Count == 2) { obj.transform.rotation = Quaternion.Euler( float.Parse(d[0]), float.Parse(d[1]), float.Parse(v)); } else if ( (n == "r" || (n == string.Empty && i == 2)) && d.Count == 0) { obj.transform.rotation = Quaternion.identity; } else if ( (n == "s" || (n == string.Empty && i == 3)) && d.Count == 0) { obj.transform.localScale = Vector3.one * float.Parse(v); } else if ( n == "s" || (n == "" && i == 3)) { obj.transform.localScale = new Vector3( float.Parse(d[0]), float.Parse(d[1]), float.Parse(v)); } else if (n == "cc") { obj.AddComponent(typeof(CombineChildren)); worldParams["ccc"] = 1; } else if (n == "m") { //d.Add(v); //ReadMesh(obj, d); info += "Inline Whirld mesh generation not supported\n"; } else if ((bool)lightSource && n == "color") { Color lsc = lightSource.color; lsc.r = float.Parse(d[0]); lsc.g = float.Parse(d[1]); lsc.b = float.Parse(v); lightSource.color = lsc; } else if ((bool)lightSource && n == "intensity") { lightSource.intensity = float.Parse(v); } else { if ((bool)whirldObject) { //Object Reference if (v.Substring(0, 1) == "#") { whirldObject.parameters.Add( n, GetAsset(v.Substring(1))); } //Text else { whirldObject.parameters.Add(n, v); } } else if (n != "") { Debug.Log( obj.name + " Unknown Param: " + n + " > " + v); } } } //Reset properties v = ""; n = ""; if (d.Count > 0) d = new List(); i++; //Done reading this object if (s == '}') { //Finish up this object if ( obj.name == "cube" || obj.name == "pyramid" || obj.name == "cone" || obj.name == "mesh") { TextureObject(obj); } //Increment ReadChar readChr++; //Handle spaces while ( readChr < data.Length && ( data[readChr] == ' ' || data[readChr] == '\n' || data[readChr] == '\t')) { readChr++; } //Read the next object if (readChr < data.Length && data[readChr] == '{') { readChr++; ReadObject(parent); return; } //Done reading objects at this level of recursion else return; } } //Assign char to property we are reading else { if (n != null) v += s; else n += s; } readChr++; } } public void TextureObject(GameObject go) { MeshFilter mf = (MeshFilter)go.GetComponent(typeof(MeshFilter)); if (!mf) return; Mesh mesh = mf.mesh; Vector2[] uvs = new Vector2[mesh.vertices.Length]; int[] tris = mesh.triangles; for (int i = 0; i < tris.Length; i += 3) { Vector3 a = go.transform.TransformPoint(mesh.vertices[tris[i]]); Vector3 b = go.transform.TransformPoint(mesh.vertices[tris[i+1]]); Vector3 c = go.transform.TransformPoint(mesh.vertices[tris[i+2]]); Vector3 n = Vector3.Cross(a-c, b-c).normalized; if ( Vector3.Dot(Vector3.up, n) >= 0.5f || (Vector3.Dot(-Vector3.up, n) >= 0.5f)) { uvs[tris[i]] = new Vector2(a.x, a.z); uvs[tris[i+1]] = new Vector2(b.x, b.z); uvs[tris[i+2]] = new Vector2(c.x, c.z); } else if ( Vector3.Dot(Vector3.right, n) >= 0.5f || (Vector3.Dot(Vector3.left, n) >= 0.5f)) { uvs[tris[i]] = new Vector2(a.y, a.z); uvs[tris[i+1]] = new Vector2(b.y, b.z); uvs[tris[i+2]] = new Vector2(c.y, c.z); } else { uvs[tris[i]] = new Vector2(a.y, a.x); uvs[tris[i + 1]] = new Vector2(b.y, b.x); uvs[tris[i + 2]] = new Vector2(c.y, c.x); } } mesh.uv = uvs; } public String GetURL(String url) { if (url.Substring(0, 4) != "http") url = urlPath + url; return url; } }