diff --git a/Assets/OBJImport/OBJLoader.cs b/Assets/OBJImport/OBJLoader.cs index 317aa9c..04638ef 100644 --- a/Assets/OBJImport/OBJLoader.cs +++ b/Assets/OBJImport/OBJLoader.cs @@ -19,6 +19,8 @@ public class OBJLoader { public static bool splitByMaterial = false; public static string[] searchPaths = new string[] { "", "%FileName%_Textures" + Path.DirectorySeparatorChar }; + public static Material defaultMaterial; + public static Shader defaultShader; //structures struct OBJFace @@ -38,14 +40,14 @@ public class OBJLoader { System.Diagnostics.Stopwatch s = new System.Diagnostics.Stopwatch(); s.Start(); - LoadOBJFile(pth); + new OBJLoader(pth).LoadOBJFile(); Debug.Log("OBJ load took " + s.ElapsedMilliseconds + "ms"); s.Stop(); } } #endif - public static Vector3 ParseVectorFromCMPS(string[] cmps) + private Vector3 ParseVectorFromCMPS(string[] cmps) { float x = float.Parse(cmps[1]); float y = float.Parse(cmps[2]); @@ -57,7 +59,7 @@ public class OBJLoader return new Vector2(x, y); } - public static Color ParseColorFromCMPS(string[] cmps, float scalar = 1.0f) + private Color ParseColorFromCMPS(string[] cmps, float scalar = 1.0f) { float Kr = float.Parse(cmps[1]) * scalar; float Kg = float.Parse(cmps[2]) * scalar; @@ -83,7 +85,12 @@ public class OBJLoader return null; } - public static Material[] LoadMTLFile(string fn) + public OBJLoader(string filePath) + { + this.filePath = filePath; + } + + private Material[] LoadMTLFile(string fn) { Material currentMaterial = null; List matlList = new List(); @@ -103,8 +110,7 @@ public class OBJLoader matlList.Add(currentMaterial); } - //currentMaterial = new Material(Shader.Find("Standard (Specular setup)")); - currentMaterial = new Material(Shader.Find("Standard")); + currentMaterial = new Material(defaultShader); currentMaterial.name = data; } else if (cmps[0] == "Kd") @@ -174,36 +180,47 @@ public class OBJLoader return matlList.ToArray(); } - public static GameObject LoadOBJFile(string fn) + private string filePath; + + private string meshName; + private bool hasNormals = false; + + //OBJ LISTS + private List vertices = new List(); + private List normals = new List(); + private List uvs = new List(); + + //UMESH LISTS + private List uvertices = new List(); + private List unormals = new List(); + private List uuvs = new List(); + + //MESH CONSTRUCTION + private List materialNames = new List(); + private List objectNames = new List(); + private Dictionary hashtable = new Dictionary(); + private List faceList = new List(); + private string cmaterial = ""; + private string cmesh = "default"; + + //CACHE + private List mtlFiles = new List(); + private List materialCache = new List(); + + public GameObject LoadOBJFile() { - string meshName = Path.GetFileNameWithoutExtension(fn); - bool hasNormals = false; + ParseObjFile(); + GameObject parentObject = BuildUnityObjects(); - //OBJ LISTS - List vertices = new List(); - List normals = new List(); - List uvs = new List(); + return parentObject; + } - //UMESH LISTS - List uvertices = new List(); - List unormals = new List(); - List uuvs = new List(); + public void ParseObjFile() + { + meshName = Path.GetFileNameWithoutExtension(filePath); + FileInfo OBJFileInfo = new FileInfo(filePath); - //MESH CONSTRUCTION - List materialNames = new List(); - List objectNames = new List(); - Dictionary hashtable = new Dictionary(); - List faceList = new List(); - string cmaterial = ""; - string cmesh = "default"; - - //CACHE - Material[] materialCache = null; - - //save this info for later - FileInfo OBJFileInfo = new FileInfo(fn); - - foreach (string ln in File.ReadAllLines(fn)) + foreach (string ln in File.ReadAllLines(filePath)) { if (ln.Length > 0 && ln[0] != '#') { @@ -216,7 +233,7 @@ public class OBJLoader //load cache string pth = OBJGetFilePath(data, OBJFileInfo.Directory.FullName + Path.DirectorySeparatorChar, meshName); if (pth != null) - materialCache = LoadMTLFile(pth); + mtlFiles.Add(pth); } else if ((cmps[0] == "g" || cmps[0] == "o") && splitByMaterial == false) { @@ -345,10 +362,17 @@ public class OBJLoader if (objectNames.Count == 0) objectNames.Add("default"); + } + public GameObject BuildUnityObjects() + { //build objects GameObject parentObject = new GameObject(meshName); - + foreach (var matfile in mtlFiles) + { + materialCache.AddRange(LoadMTLFile(matfile)); + } + foreach (string obj in objectNames) { GameObject subObject = new GameObject(obj); @@ -380,7 +404,7 @@ public class OBJLoader m.subMeshCount = meshMaterialNames.Count; var indexes = faces.SelectMany(f => f.indexes).ToArray(); - + for (int i = 0; i < indexes.Length; i++) { int idx = indexes[i]; @@ -428,14 +452,14 @@ public class OBJLoader { if (materialCache == null) { - processedMaterials[i] = new Material(Shader.Find("Standard (Specular setup)")); + processedMaterials[i] = defaultMaterial; } else { - Material mfn = Array.Find(materialCache, x => x.name == meshMaterialNames[i]); ; + Material mfn = materialCache.Find(x => x.name == meshMaterialNames[i]); if (mfn == null) { - processedMaterials[i] = new Material(Shader.Find("Standard (Specular setup)")); + processedMaterials[i] = defaultMaterial; } else { diff --git a/Assets/RothenburgAR/Scripts/AppInitializerBehaviour.cs b/Assets/RothenburgAR/Scripts/AppInitializerBehaviour.cs index cfeb806..31b4122 100644 --- a/Assets/RothenburgAR/Scripts/AppInitializerBehaviour.cs +++ b/Assets/RothenburgAR/Scripts/AppInitializerBehaviour.cs @@ -27,6 +27,10 @@ namespace RothenburgAR InputManager.Instance.Initialize(); var init = DisplayManager.Instance; + + //OBJLoader.defaultShader = Shader.Find("Standard (Specular setup)") + OBJLoader.defaultShader = Shader.Find("Standard"); + OBJLoader.defaultMaterial = new Material(OBJLoader.defaultShader); } void InitializeData() diff --git a/Assets/RothenburgAR/Scripts/PointOfInterest/PoiApiDataPreloader.cs b/Assets/RothenburgAR/Scripts/PointOfInterest/PoiApiDataPreloader.cs index fa6e4af..92aa099 100644 --- a/Assets/RothenburgAR/Scripts/PointOfInterest/PoiApiDataPreloader.cs +++ b/Assets/RothenburgAR/Scripts/PointOfInterest/PoiApiDataPreloader.cs @@ -45,7 +45,7 @@ namespace RothenburgAR.PointOfInterest var poiIds = exhibits.Values.SelectMany(l => l.SelectMany(e => e.Pois.Select(p => p.Id))).Distinct().ToList(); Dictionary> poisPerLang = new Dictionary>(); - exhibits.Keys.ToList().ForEach(k => + exhibits.Keys.ToList().ForEach(k => poisPerLang.Add(k, exhibits[k].SelectMany(l => l.Pois.Select(p => p)).Distinct().ToList())); foreach (var poiID in poiIds) @@ -75,7 +75,7 @@ namespace RothenburgAR.PointOfInterest if (mediaID != null) { var modelDescription = new PoiModelDescription(); - modelDescription.ModelPath = mediaID; + modelDescription.ModelPath = PathHelper.CombinePaths(PathHelper.MediaPath, mediaID, mediaID + ".obj"); //TODO dont have model size information, find a way to scale model to fit into view? modelDescription.ModelPrefabName = string.Empty; diff --git a/Assets/RothenburgAR/Scripts/PointOfInterest/PoiData.cs b/Assets/RothenburgAR/Scripts/PointOfInterest/PoiData.cs index 742468c..994df5e 100644 --- a/Assets/RothenburgAR/Scripts/PointOfInterest/PoiData.cs +++ b/Assets/RothenburgAR/Scripts/PointOfInterest/PoiData.cs @@ -1,4 +1,7 @@ using RothenburgAR.Common; +using System.Collections; +using System.IO; +using System.Threading; using UnityEngine; namespace RothenburgAR.PointOfInterest @@ -21,6 +24,48 @@ namespace RothenburgAR.PointOfInterest get { return ModelDescription.ModelPath != null; } } + public bool IsModelLoaded + { + get + { + return _modelPrefab != null; + } + } + + public IEnumerator LoadModel() + { + if (!File.Exists(ModelDescription.ModelPath)) + { + throw new FileNotFoundException(ModelDescription.ModelPath + " does not exist;"); + } + + var loader = new OBJLoader(ModelDescription.ModelPath); + + //TODO do this in new thread + ThreadStart start = new ThreadStart(loader.ParseObjFile); + Thread t = new Thread(start); + t.Start(); + + while (t.ThreadState != ThreadState.Stopped) + { + Debug.Log("loading model from subthread"); + yield return null; + } + Debug.Log("loaded model from subthread"); + + //TODO wait for thread finish with yield return null + var model = loader.BuildUnityObjects(); + + if (!model) + { + Debug.LogError("Failed to load Model!"); + } + + + model.SetActive(false); + _modelPrefab = model; + } + public GameObject GetModelPrefab() { if (!HasModelDescription) @@ -28,8 +73,7 @@ namespace RothenburgAR.PointOfInterest if (_modelPrefab != null) return _modelPrefab; - _modelPrefab = new PoiModelLoader().LoadModel(ModelDescription); - _modelPrefab.SetActive(false); + LoadModel(); return _modelPrefab; } @@ -38,7 +82,7 @@ namespace RothenburgAR.PointOfInterest if (!HasModelDescription) return null; var modelPrefab = GetModelPrefab(); - var gameObject = UnityEngine.Object.Instantiate(modelPrefab); + var gameObject = GameObject.Instantiate(modelPrefab); gameObject.SetActive(true); gameObject.transform.localRotation = Quaternion.identity; @@ -46,7 +90,7 @@ namespace RothenburgAR.PointOfInterest var modelGO = gameObject.transform.GetChild(0); modelGO.transform.eulerAngles = ModelDescription.Rotation; - modelGO.gameObject.layer = LayerMask.NameToLayer("UIModel"); + modelGO.gameObject.layer = LayerMask.NameToLayer("UIModel"); return gameObject; } diff --git a/Assets/RothenburgAR/Scripts/PointOfInterest/PoiModelLoader.cs b/Assets/RothenburgAR/Scripts/PointOfInterest/PoiModelLoader.cs deleted file mode 100644 index 678d982..0000000 --- a/Assets/RothenburgAR/Scripts/PointOfInterest/PoiModelLoader.cs +++ /dev/null @@ -1,34 +0,0 @@ -using RothenburgAR.Common; -using System.IO; -using UnityEngine; - -namespace RothenburgAR.PointOfInterest -{ - class PoiModelLoader - { - public GameObject LoadModel(PoiModelDescription description) - { - var path = PathHelper.CombinePaths(PathHelper.MediaPath, description.ModelPath, description.ModelPath + ".obj"); - if (!File.Exists(path)) - { - throw new FileNotFoundException(description.ModelPath + " does not exist;"); - } - - return OBJLoader.LoadOBJFile(path); - - var asset = AssetBundle.LoadFromFile(description.ModelPath); - if (!asset) - { - Debug.Log("Failed to load AssetBundle!"); - return null; - } - - var prefab = asset.LoadAsset(description.ModelPrefabName) as GameObject; - - prefab.transform.localScale = description.Scale; - prefab.transform.localPosition = description.Position; - prefab.transform.Rotate(description.Rotation); - return prefab; - } - } -} \ No newline at end of file diff --git a/Assets/RothenburgAR/Scripts/PointOfInterest/PoiModelLoader.cs.meta b/Assets/RothenburgAR/Scripts/PointOfInterest/PoiModelLoader.cs.meta deleted file mode 100644 index 6e8060f..0000000 --- a/Assets/RothenburgAR/Scripts/PointOfInterest/PoiModelLoader.cs.meta +++ /dev/null @@ -1,12 +0,0 @@ -fileFormatVersion: 2 -guid: 7ba5d1ae3dd7b284a99ab7858988692b -timeCreated: 1505023463 -licenseType: Free -MonoImporter: - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/RothenburgAR/Scripts/PointOfInterest/PoiXmlPreloader.cs b/Assets/RothenburgAR/Scripts/PointOfInterest/PoiXmlPreloader.cs index 26d0cdb..8ec515d 100644 --- a/Assets/RothenburgAR/Scripts/PointOfInterest/PoiXmlPreloader.cs +++ b/Assets/RothenburgAR/Scripts/PointOfInterest/PoiXmlPreloader.cs @@ -70,7 +70,7 @@ namespace RothenburgAR.PointOfInterest var poiModelDescription = new PoiModelDescription { - ModelPath = pathNode.Attributes["value"].Value, + ModelPath = PathHelper.CombinePaths(PathHelper.MediaPath, pathNode.Attributes["value"].Value, pathNode.Attributes["value"].Value + ".obj"), ModelPrefabName = pathNode.Attributes["prefabName"].Value, Scale = GetVector3FromXmlNode(scaleNode).GetValueOrDefault(Vector3.one), Rotation = GetVector3FromXmlNode(rotationNode).GetValueOrDefault(Vector3.zero), diff --git a/Assets/RothenburgAR/Scripts/UI/ARViewBehaviour.cs b/Assets/RothenburgAR/Scripts/UI/ARViewBehaviour.cs index 72582d5..9095c56 100644 --- a/Assets/RothenburgAR/Scripts/UI/ARViewBehaviour.cs +++ b/Assets/RothenburgAR/Scripts/UI/ARViewBehaviour.cs @@ -30,21 +30,20 @@ namespace RothenburgAR.UI if (data.HasModelDescription) DetailsPanel.SetModelFromPoi(data); - + DetailsPanel.UpdateOpeningAnimation(poi.transform.position); - + poi.SetActive(true); ProjectionEffect.SelectedPOI = poi.gameObject; ProjectionEffect.SetActive(true); } - public void HidePoiDetails() { DetailsPanel.gameObject.SetActive(false); ProjectionEffect.SetActive(false); - + if (ProjectionEffect.SelectedPOI != null) { var poi = ProjectionEffect.SelectedPOI.GetComponent(); diff --git a/Assets/RothenburgAR/Scripts/UI/DetailsPanelBehaviour.cs b/Assets/RothenburgAR/Scripts/UI/DetailsPanelBehaviour.cs index de328ac..23583dc 100644 --- a/Assets/RothenburgAR/Scripts/UI/DetailsPanelBehaviour.cs +++ b/Assets/RothenburgAR/Scripts/UI/DetailsPanelBehaviour.cs @@ -5,6 +5,8 @@ using TMPro; using UnityEngine; using UnityEngine.UI; using UnityEngine.EventSystems; +using System.Collections; +using System.Threading; namespace RothenburgAR.UI { @@ -24,6 +26,7 @@ namespace RothenburgAR.UI get { return _currentDisplayedModelGo != null; } } + private string modelToBeDisplayed = null; private GameObject _currentDisplayedModelGo; public void SetText(TextElement el) @@ -34,14 +37,52 @@ namespace RothenburgAR.UI public void SetText(String el) { DetailsText.text = el; - + // Scroll to top DetailsTextScrollRect.normalizedPosition = new Vector2(0, 1); } + public void SetModelFromPoi(PoiData data) + { + if (!data.HasModelDescription) return; + + StartCoroutine("SetModelCoroutine", data); + } + + public IEnumerator SetModelCoroutine(PoiData data) + { + modelToBeDisplayed = data.ID; + + if (!data.IsModelLoaded) + { + Debug.Log("Model not loaded, starting thread..."); + StartCoroutine(data.LoadModel()); + + while (!data.IsModelLoaded) + { + // display loading icon or sth idk + Debug.Log("waiting for data to be loaded.."); + yield return null; + } + + Debug.Log("exited while loop"); + } + + try + { + if (modelToBeDisplayed == data.ID) + SetModel(data.InstantiateModelPrefab()); + } + catch (Exception) + { + // ignore, just do not display an object + _currentDisplayedModelGo = null; + modelToBeDisplayed = null; + } + } + public void SetModel(GameObject modelGo) { - RemoveModel(); modelGo.transform.SetParent(UIManager.Instance.UIObjectCamera.transform, false); modelGo.transform.localPosition += new Vector3(0, 0, 450); @@ -51,24 +92,11 @@ namespace RothenburgAR.UI DetailsModel.gameObject.SetActive(true); } - public void SetModelFromPoi(PoiData data) - { - if (!data.HasModelDescription) return; - try - { - SetModel(data.InstantiateModelPrefab()); - } - catch (Exception) - { - // ignore, just do not display an object - _currentDisplayedModelGo = null; - } - } - public void RemoveModel() { DetailsModel.gameObject.SetActive(false); DetailsModel.CurrentModel = null; + modelToBeDisplayed = null; if (HasModel) { Destroy(_currentDisplayedModelGo);