diff --git a/Assets/IRXRClient/Scripts/IRXRNetManager.cs b/Assets/IRXRClient/Scripts/IRXRNetManager.cs index bf2a6ec..fda4468 100644 --- a/Assets/IRXRClient/Scripts/IRXRNetManager.cs +++ b/Assets/IRXRClient/Scripts/IRXRNetManager.cs @@ -19,11 +19,11 @@ public enum ServerPort { public enum ClientPort { Discovery = 7720, - Service = 7723, - Topic = 7724, + Service = 7730, + Topic = 7731, } -class HostInfo { +public class HostInfo { public string name; public string ip; public List topics = new(); @@ -38,7 +38,7 @@ public class IRXRNetManager : Singleton { public Action ConnectionSpin; private UdpClient _discoveryClient; private string _conncetionID = null; - private HostInfo _serverInfo = null; + public HostInfo _serverInfo = null; private HostInfo _localInfo = new HostInfo(); private List _sockets; @@ -81,16 +81,16 @@ void Awake() { // Default host name if (PlayerPrefs.HasKey("HostName")) { - // The key exists, proceed to get the value - string savedHostName = PlayerPrefs.GetString("HostName"); - _localInfo.name = savedHostName; - Debug.Log($"Find Host Name: {_localInfo.name}"); + // The key exists, proceed to get the value + string savedHostName = PlayerPrefs.GetString("HostName"); + _localInfo.name = savedHostName; + Debug.Log($"Find Host Name: {_localInfo.name}"); } else { - // The key does not exist, handle it accordingly - _localInfo.name = "UnityClient"; - Debug.Log($"Host Name not found, using default name {_localInfo.name}"); + // The key does not exist, handle it accordingly + _localInfo.name = "UnityClient"; + Debug.Log($"Host Name not found, using default name {_localInfo.name}"); } } @@ -101,7 +101,8 @@ void Start() { OnConnectionStart += () => { isConnected = true; }; ConnectionSpin += () => {}; OnDisconnected += StopConnection; - RegisterServiceCallback("ChangeHostName", ChangeHoseName); + lastTimeStamp = -1.0f; + RegisterServiceCallback("ChangeHostName", ChangeHostName); } async void Update() { @@ -231,6 +232,7 @@ public void ServiceRespondSpin() { if (!_resSocket.HasIn) return; string messageReceived = _resSocket.ReceiveFrameString(); string[] messageSplit = messageReceived.Split(":", 2); + Debug.Log($"Received service request {messageSplit[0]}"); if (_serviceCallbacks.ContainsKey(messageSplit[0])) { string response = _serviceCallbacks[messageSplit[0]](messageSplit[1]); _resSocket.SendFrame(response); @@ -320,7 +322,7 @@ private static bool IsInSameSubnet(IPAddress ip1, IPAddress ip2, IPAddress subne return true; } - public string ChangeHoseName(string name) { + public string ChangeHostName(string name) { _localInfo.name = name; PlayerPrefs.SetString("HostName", name); Debug.Log($"Change Host Name to {_localInfo.name}"); @@ -335,4 +337,8 @@ public bool CheckServerService(string serviceName) { return false; } + public HostInfo GetServerInfo() { + return _serverInfo; + } + } \ No newline at end of file diff --git a/Assets/SceneLoader/Scripts/SimData.cs b/Assets/SceneLoader/Scripts/SimData.cs index bbbf316..736668f 100644 --- a/Assets/SceneLoader/Scripts/SimData.cs +++ b/Assets/SceneLoader/Scripts/SimData.cs @@ -22,12 +22,10 @@ public Vector3 GetScale() { } public class SimVisual { - // public string name; public string type; - public string mesh; - public string material; + public SimMesh mesh; + public SimMaterial material; public SimTransform trans; - public List color; } @@ -51,41 +49,28 @@ public class SimAsset { } public class SimMesh : SimAsset { - public string dataHash; + public string hash; public List indicesLayout; - public List verticesLayout; - public List normalsLayout; - public List uvLayout; - [JsonIgnore] - public Mesh compiledMesh; } public class SimMaterial : SimAsset { - public string dataHash; + public string hash; public List color; public List emissionColor; public float specular; public float shininess; public float reflectance; - public string texture; - public List textureSize; - - [JsonIgnore] - public Material compiledMaterial; + public SimTexture texture; } public class SimTexture : SimAsset { - public string dataHash; + public string hash; public int width; - public int height; - public string texureType; - - [JsonIgnore] - public Texture compiledTexture; + public List textureSize; } \ No newline at end of file diff --git a/Assets/SceneLoader/Scripts/SimLoader.cs b/Assets/SceneLoader/Scripts/SimLoader.cs index 795dd5e..28a118b 100644 --- a/Assets/SceneLoader/Scripts/SimLoader.cs +++ b/Assets/SceneLoader/Scripts/SimLoader.cs @@ -1,7 +1,8 @@ using System; +using System.Collections; +using System.Collections.Generic; using UnityEngine; using System.Runtime.InteropServices; -using System.Collections.Generic; using Newtonsoft.Json; using System.Threading.Tasks; using System.Linq; @@ -12,7 +13,6 @@ public class SceneLoader : MonoBehaviour { - private object updateActionLock = new(); private Action updateAction; public Action OnSceneLoaded; @@ -20,101 +20,74 @@ public class SceneLoader : MonoBehaviour { private IRXRNetManager _netManager; private GameObject _simSceneObj; private SimScene _simScene; - private Dictionary _simObjTrans = new Dictionary(); - - private Dictionary _simMeshes = new(); - private Dictionary _simMaterials = new(); - private Dictionary _simTextures = new(); - private Dictionary _cachedMeshes = new(); - private Dictionary _cachedTextures = new(); + private Dictionary _simObjTrans = new (); + private Dictionary>> _pendingMesh = new (); + private Dictionary>> _pendingTexture = new (); void Start() { _netManager = IRXRNetManager.Instance; _netManager.OnDisconnected += ClearScene; - _netManager.OnConnectionStart += DownloadScene; updateAction = () => { }; OnSceneLoaded += () => Debug.Log("Scene Loaded"); OnSceneCleared += () => Debug.Log("Scene Cleared"); + _netManager.RegisterServiceCallback("LoadSimScene", LoadSimScene); + } + + private string LoadSimScene(string simSceneJsonStr) { + ClearScene(); + _simScene = JsonConvert.DeserializeObject(simSceneJsonStr); + updateAction += BuildScene; + Debug.Log("Downloaded scene json and starting to build scene"); + return "Received Scene"; } void BuildScene() { // Don't include System.Diagnostics, Debug becomes disambiguous + // It is more accurate to use System.Diagnostics.Stopwatch, theoretically var local_watch = new System.Diagnostics.Stopwatch(); local_watch.Start(); Debug.Log("Building Scene"); _simSceneObj = CreateObject(gameObject.transform, _simScene.root); local_watch.Stop(); Debug.Log($"Building Scene in {local_watch.ElapsedMilliseconds} ms"); - OnSceneLoaded.Invoke(); + Task.Run(() => DownloadAssets()); } - private void DownloadScene() { - + public void DownloadAssets() { var local_watch = new System.Diagnostics.Stopwatch(); local_watch.Start(); - - if (!_netManager.CheckServerService("Scene")) { - Debug.LogWarning("Scene Service is not found"); - return; + int totalMeshSize = 0; + int totalTextureSize = 0; + foreach (string hash in _pendingMesh.Keys) + { + byte[] meshData = _netManager.RequestBytes("Asset", hash).ToArray(); + foreach (Tuple pair in _pendingMesh[hash]) + { + RunOnMainThread(() => BuildMesh(pair.Item1, pair.Item2, meshData)); + } + totalMeshSize += meshData.Length; } - - string asset_info = _netManager.RequestString("Scene"); - - if (asset_info == "Invalid Service") { - Debug.LogWarning("Invalid Service"); - return; + foreach (string hash in _pendingTexture.Keys) + { + byte[] texData = _netManager.RequestBytes("Asset", hash).ToArray(); + foreach (Tuple pair in _pendingTexture[hash]) + { + RunOnMainThread(() => BuildTexture(pair.Item1, pair.Item2, texData)); + } + totalTextureSize += texData.Length; } - - _simScene = JsonConvert.DeserializeObject(asset_info); - DownloadAssets(_simScene); + double meshSizeMB = Math.Round(totalMeshSize / Math.Pow(2, 20), 2); + double textureSizeMB = Math.Round(totalTextureSize / Math.Pow(2, 20), 2); local_watch.Stop(); - Debug.Log($"Downloaded Scene in {local_watch.ElapsedMilliseconds} ms"); - updateAction += BuildScene; + _pendingMesh.Clear(); + _pendingTexture.Clear(); + // When debug run in the subthread, it will not send the log to the server + RunOnMainThread(() => Debug.Log($"Downloaded {meshSizeMB}MB meshes, {textureSizeMB}MB textures.")); + RunOnMainThread(() => Debug.Log($"Downloading Asset in {local_watch.ElapsedMilliseconds} ms")); + RunOnMainThread(() => OnSceneLoaded.Invoke()); } - public void DownloadAssets(SimScene scene) { - _simMeshes.Clear(); - _simMaterials.Clear(); - _simTextures.Clear(); - - int textureSizeAcc = 0; - int meshSizeAcc = 0; - - scene.meshes.ForEach(mesh => { - meshSizeAcc += DownloadMesh(mesh); - }); - scene.textures.ForEach(texture => { - textureSizeAcc += DownloadTexture(texture); - }); - scene.materials.ForEach(material => _simMaterials.Add(material.name, material)); - - print($"Downloaded {Math.Round(meshSizeAcc / Math.Pow(2, 20), 2)}MB meshes, {Math.Round(textureSizeAcc / Math.Pow(2, 20), 2)}MB textures, {scene.materials.Count} materials"); - } - - private int DownloadMesh(SimMesh mesh) { - int meshSize = 0; - if (!_cachedMeshes.TryGetValue(mesh.dataHash, out mesh.compiledMesh)) { - byte[] data = _netManager.RequestBytes("Asset", mesh.dataHash).ToArray(); - meshSize += data.Length; - RunOnMainThread(() => ProcessMesh(mesh, data)); - } - _simMeshes.TryAdd(mesh.name, mesh); - return meshSize; - } - - private int DownloadTexture(SimTexture texture) { - int textureSize = 0; - if (!_cachedTextures.TryGetValue(texture.dataHash, out texture.compiledTexture)){ - List data = _netManager.RequestBytes("Asset", texture.dataHash); - textureSize = data.Count; - RunOnMainThread(() => ProcessTexture(texture, data)); - } - _simTextures.TryAdd(texture.name, texture); - return textureSize; - } - - void RunOnMainThread(Action action) { lock(updateActionLock) { updateAction += action; @@ -128,75 +101,63 @@ void Update() { } } - void ApplyTransform(Transform utransform, SimTransform trans) { utransform.localPosition = trans.GetPos(); utransform.localRotation = trans.GetRot(); utransform.localScale = trans.GetScale(); } - GameObject CreateObject(Transform root, SimBody body) { GameObject bodyRoot = new GameObject(body.name); bodyRoot.transform.SetParent(root, false); ApplyTransform(bodyRoot.transform, body.trans); - - GameObject VisualContainer = new GameObject("Visuals"); - VisualContainer.transform.SetParent(bodyRoot.transform, false); - - foreach (SimVisual visual in body.visuals) { - GameObject visualObj; - switch (visual.type) { - case "MESH": { - if (!_simMeshes.ContainsKey(visual.mesh)) { - Debug.LogWarning("Mesh not found, " + visual.mesh); - visualObj = GameObject.CreatePrimitive(PrimitiveType.Sphere); - visualObj.transform.localScale = new Vector3(0.01f, 0.01f, 0.01f); + if (body.visuals.Count != 0) { + GameObject VisualContainer = new GameObject($"{body.name}_Visuals"); + VisualContainer.transform.SetParent(bodyRoot.transform, false); + foreach (SimVisual visual in body.visuals) { + GameObject visualObj; + switch (visual.type) { + case "MESH": { + SimMesh simMesh = visual.mesh; + visualObj = new GameObject(simMesh.hash, typeof(MeshFilter), typeof(MeshRenderer)); + if (!_pendingMesh.ContainsKey(simMesh.hash)) { + _pendingMesh[simMesh.hash] = new(); + } + _pendingMesh[simMesh.hash].Add(new Tuple(simMesh, visualObj.GetComponent())); break; } - SimMesh asset = _simMeshes[visual.mesh]; - visualObj = new GameObject(asset.name, typeof(MeshFilter), typeof(MeshRenderer)); - visualObj.GetComponent().mesh = asset.compiledMesh; - break; + case "CUBE": + visualObj = GameObject.CreatePrimitive(PrimitiveType.Cube); + break; + case "PLANE": + visualObj = GameObject.CreatePrimitive(PrimitiveType.Plane); + break; + case "CYLINDER": + visualObj = GameObject.CreatePrimitive(PrimitiveType.Cylinder); + break; + case "CAPSULE": + visualObj = GameObject.CreatePrimitive(PrimitiveType.Capsule); + break; + case "SPHERE": + visualObj = GameObject.CreatePrimitive(PrimitiveType.Sphere); + break; + default: + Debug.LogWarning("Invalid visual, " + visual.type + body.name); + visualObj = GameObject.CreatePrimitive(PrimitiveType.Sphere); + break; } - case "CUBE": - visualObj = GameObject.CreatePrimitive(PrimitiveType.Cube); - break; - case "PLANE": - visualObj = GameObject.CreatePrimitive(PrimitiveType.Plane); - break; - case "CYLINDER": - visualObj = GameObject.CreatePrimitive(PrimitiveType.Cylinder); - break; - case "CAPSULE": - visualObj = GameObject.CreatePrimitive(PrimitiveType.Capsule); - break; - case "SPHERE": - visualObj = GameObject.CreatePrimitive(PrimitiveType.Sphere); - break; - default: - Debug.LogWarning("Invalid visual, " + visual.type + body.name); - visualObj = GameObject.CreatePrimitive(PrimitiveType.Sphere); - break; - } - - Renderer renderer = visualObj.GetComponent(); - if (visual.material != null) { - var material = GetMaterial(visual.material); - if (material.compiledMaterial == null) { - ProcessMaterial(material); + Renderer renderer = visualObj.GetComponent(); + if (visual.material != null) { + renderer.material = BuildMaterial(visual.material, body.name); } - renderer.material = material.compiledMaterial; - } - else { - renderer.material = CreateColorMaterial(visual.color); + else { + Debug.LogWarning($"Material of {body.name}_Visuals not found"); + } + visualObj.transform.SetParent(VisualContainer.transform, false); + ApplyTransform(visualObj.transform, visual.trans); } - - visualObj.transform.SetParent(VisualContainer.transform, false); - ApplyTransform(visualObj.transform, visual.trans); } - body.children.ForEach(body => CreateObject(bodyRoot.transform, body)); _simObjTrans.Add(body.name, bodyRoot.transform); return bodyRoot; @@ -205,88 +166,58 @@ GameObject CreateObject(Transform root, SimBody body) { void ClearScene() { OnSceneCleared.Invoke(); if (_simSceneObj != null) Destroy(_simSceneObj); - _simSceneObj = null; + _pendingMesh.Clear(); + _pendingTexture.Clear(); _simObjTrans.Clear(); - _simMeshes.Clear(); - _simMaterials.Clear(); - _simTextures.Clear(); } - public SimMesh GetMesh(string id) => _simMeshes[id]; - public SimTexture GetTexture(string id) => _simTextures[id]; - public SimMaterial GetMaterial(string id) => _simMaterials[id]; - - public Material CreateColorMaterial(List color) { - var material = new Material(Shader.Find("Standard")); - if (color[3] < 1) + public Material BuildMaterial(SimMaterial simMat, string objName) { + Material mat = new Material(Shader.Find("Standard")); + // Transparency + if (simMat.color[3] < 1) { - material.SetFloat("_Mode", 2); - material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha); - material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha); - material.SetInt("_ZWrite", 0); - material.DisableKeyword("_ALPHATEST_ON"); - material.EnableKeyword("_ALPHABLEND_ON"); - material.DisableKeyword("_ALPHAPREMULTIPLY_ON"); - material.renderQueue = 3000; + mat.SetFloat("_Mode", 2); + mat.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha); + mat.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha); + mat.SetInt("_ZWrite", 0); + mat.DisableKeyword("_ALPHATEST_ON"); + mat.EnableKeyword("_ALPHABLEND_ON"); + mat.DisableKeyword("_ALPHAPREMULTIPLY_ON"); + mat.renderQueue = 3000; + } + mat.SetColor("_Color", new Color(simMat.color[0], simMat.color[1], simMat.color[2], simMat.color[3])); + if (simMat.emissionColor != null){ + mat.SetColor("_emissionColor", new Color(simMat.emissionColor[0], simMat.emissionColor[1], simMat.emissionColor[2], simMat.emissionColor[3])); + } + mat.SetFloat("_specularHighlights", simMat.specular); + mat.SetFloat("_Smoothness", simMat.shininess); + mat.SetFloat("_GlossyReflections", simMat.reflectance); + if (simMat.texture != null) { + Debug.Log($"Texture found for {objName}"); + SimTexture simTex = simMat.texture; + if (!_pendingTexture.ContainsKey(simTex.hash)) { + _pendingTexture[simTex.hash] = new (); + } + _pendingTexture[simTex.hash].Add(new Tuple(simTex, mat)); } - material.SetColor("_Color", new Color(color[0], color[1], color[2], color[3])); - return material; + return mat; } - public void ProcessMesh(SimAsset asset, byte[] data) { - SimMesh mesh = (SimMesh)asset; - - - mesh.compiledMesh = new Mesh{ - name = asset.name, - vertices = MemoryMarshal.Cast(new ReadOnlySpan(data, mesh.verticesLayout[0], mesh.verticesLayout[1] * sizeof(float))).ToArray(), - normals = MemoryMarshal.Cast(new ReadOnlySpan(data, mesh.normalsLayout[0], mesh.normalsLayout[1] * sizeof(float))).ToArray(), - triangles = MemoryMarshal.Cast(new ReadOnlySpan(data, mesh.indicesLayout[0], mesh.indicesLayout[1] * sizeof(int))).ToArray(), - uv = MemoryMarshal.Cast(new ReadOnlySpan(data, mesh.uvLayout[0], mesh.uvLayout[1] * sizeof(float))).ToArray() + public void BuildMesh(SimMesh simMesh, MeshFilter meshFilter, byte[] meshData) { + meshFilter.mesh = new Mesh{ + vertices = MemoryMarshal.Cast(new ReadOnlySpan(meshData, simMesh.verticesLayout[0], simMesh.verticesLayout[1] * sizeof(float))).ToArray(), + normals = MemoryMarshal.Cast(new ReadOnlySpan(meshData, simMesh.normalsLayout[0], simMesh.normalsLayout[1] * sizeof(float))).ToArray(), + triangles = MemoryMarshal.Cast(new ReadOnlySpan(meshData, simMesh.indicesLayout[0], simMesh.indicesLayout[1] * sizeof(int))).ToArray(), + uv = MemoryMarshal.Cast(new ReadOnlySpan(meshData, simMesh.uvLayout[0], simMesh.uvLayout[1] * sizeof(float))).ToArray() }; - - _cachedMeshes[mesh.dataHash] = mesh.compiledMesh; - _simMeshes[mesh.name] = mesh; } - public void ProcessMaterial(SimAsset asset) { - SimMaterial material = (SimMaterial)asset; - - Material mat = new Material(Shader.Find("Standard")); - - mat.SetColor("_Color", new Color(material.color[0], material.color[1], material.color[2], material.color[3])); - mat.SetColor("_emissionColor", new Color(material.emissionColor[0], material.emissionColor[1], material.emissionColor[2], material.emissionColor[3])); - mat.SetFloat("_specularHighlights", material.specular); - mat.SetFloat("_Smoothness", material.shininess); - mat.SetFloat("_GlossyReflections", material.reflectance); - - if (material.texture != null) { - SimTexture texture = _simTextures[material.texture]; - - _simTextures[material.texture] = texture; // Just to be shure the texture is updated (investigate) - - mat.mainTexture = texture.compiledTexture; - mat.mainTextureScale = new Vector2(material.textureSize[0], material.textureSize[1]); - } - - material.compiledMaterial = mat; - _simMaterials[material.name] = material; - } - - public SimAsset ProcessTexture(SimAsset asset, List data) { - SimTexture simTexture = (SimTexture)asset; - - byte[] byteData = data.ToArray(); - data.Clear(); - data = null; - - var tex = new Texture2D(simTexture.width, simTexture.height, TextureFormat.RGB24, false); - tex.LoadRawTextureData(byteData); + public void BuildTexture(SimTexture simTex, Material material, byte[] texData) { + Texture2D tex = new Texture2D(simTex.width, simTex.height, TextureFormat.RGB24, false); + tex.LoadRawTextureData(texData); tex.Apply(); - - simTexture.compiledTexture = tex; - _cachedTextures[simTexture.dataHash] = simTexture.compiledTexture; - return asset; + material.mainTexture = tex; + material.mainTextureScale = new Vector2(simTex.textureSize[0], simTex.textureSize[1]); } public Dictionary GetObjectsTrans() {