diff --git a/API/oursin/__init__.py b/API/oursin/__init__.py index 7774cb5e..2bde2b57 100644 --- a/API/oursin/__init__.py +++ b/API/oursin/__init__.py @@ -24,6 +24,7 @@ from . import text from . import volumes from . import texture +from . import custom # load the colors from . import colors diff --git a/API/oursin/custom.py b/API/oursin/custom.py new file mode 100644 index 00000000..a4ef48c1 --- /dev/null +++ b/API/oursin/custom.py @@ -0,0 +1,70 @@ +"""Custom 3D Objects""" + +from . import client +from . import utils +import json + +count = 0 + +class CustomMesh: + """Custom 3D object + """ + + def __init__(self, vertices, triangles, normals = None): + """Create a Custom 3D object based on a set of vertices and triangle faces + + Parameters + ---------- + vertices : list of vector3 + Vertex coordinates + triangles : list of vector3 + Triangle vertices + normals : list of vector3, optional + Normal directions, by default None + """ + global count + + self.id = str(count) + count += 1 + + data = {} + data['ID'] = self.id + data['vertices'] = vertices + data['triangles'] = triangles + + if not normals is None: + data['normals'] = normals + + client.sio.emit('CustomMeshCreate', json.dumps(data)) + + self.in_unity = True + + def delete(self): + """Destroy this object in the renderer scene + """ + client.sio.emit('CustomMeshDelete', self.id) + self.in_unity = False + + def set_position(self, position = [0,0,0], use_reference = True): + """Set the position relative to the reference coordinate + + Parameters + ---------- + position : vector3 + AP/ML/DV coordinate relative to the reference (defaults to [0,0,0] when unset) + use_reference : bool, optional + whether to use the reference coordinate, by default True + """ + position = utils.sanitize_vector3(position) + + data = {} + data['ID'] = self.id + data['Position'] = position + data['UseReference'] = use_reference + + client.sio.emit('CustomMeshPosition', json.dumps(data)) + +def clear(): + """Clear all custom meshes + """ + client.sio.emit('Clear','custommesh') \ No newline at end of file diff --git a/API/oursin/utils.py b/API/oursin/utils.py index 9cfde5a4..0ee78f58 100644 --- a/API/oursin/utils.py +++ b/API/oursin/utils.py @@ -124,4 +124,14 @@ def rgb_to_hex(rgb): return '#%02x%02x%02x' % rgb def rgba_to_hex(rgba): - return '#%02x%02x%02x%02x' % rgba \ No newline at end of file + return '#%02x%02x%02x%02x' % rgba + +def list_of_list2vector3(list_of_list): + """Convert a list of lists to a list of vector3 {"x","y","z"} objects + + Parameters + ---------- + list_of_list : list of length 3 lists + _description_ + """ + return [{"x":str(data[0]), "y":str(data[1]), "z":str(data[2])} for data in list_of_list] diff --git a/UnityClient/Packages/vbl.urchin/Scripts/API/Client_SocketIO.cs b/UnityClient/Packages/vbl.urchin/Scripts/API/Client_SocketIO.cs index 73c6fb98..833147db 100644 --- a/UnityClient/Packages/vbl.urchin/Scripts/API/Client_SocketIO.cs +++ b/UnityClient/Packages/vbl.urchin/Scripts/API/Client_SocketIO.cs @@ -71,6 +71,7 @@ void Start() Start_LineRenderer(); Start_PrimitiveMeshRenderer(); Start_FOV(); + Start_CustomMesh(); // Misc manager.Socket.On("Clear", Clear); @@ -261,6 +262,16 @@ private void Start_FOV() manager.Socket.On>("SetFOVVisibility", x => SetFOVVisibility.Invoke(x)); } + public static Action CustomMeshCreate; + public static Action CustomMeshDestroy; + public static Action CustomMeshPosition; + + private void Start_CustomMesh() + { + manager.Socket.On("CustomMeshCreate", x => CustomMeshCreate.Invoke(JsonUtility.FromJson(x))); + manager.Socket.On("CustomMeshDestroy", x => CustomMeshDestroy.Invoke(JsonUtility.FromJson(x))); + manager.Socket.On("CustomMeshPosition", x => CustomMeshPosition.Invoke(JsonUtility.FromJson(x))); + } #endregion @@ -273,8 +284,9 @@ private void Start_FOV() public static Action ClearParticles; public static Action ClearMeshes; public static Action ClearFOV; + public static Action ClearCustomMeshes; public static List ClearAll = new List { ClearProbes, ClearAreas, ClearVolumes, - ClearText, ClearParticles, ClearMeshes, ClearFOV}; + ClearText, ClearParticles, ClearMeshes, ClearFOV, ClearCustomMeshes}; private void Clear(string val) { switch (val) @@ -304,6 +316,9 @@ private void Clear(string val) case "texture": ClearFOV.Invoke(); break; + case "custommesh": + ClearCustomMeshes.Invoke(); + break; } } #endregion diff --git a/UnityClient/Packages/vbl.urchin/Scripts/JSON/CustomMesh.meta b/UnityClient/Packages/vbl.urchin/Scripts/JSON/CustomMesh.meta new file mode 100644 index 00000000..455170fb --- /dev/null +++ b/UnityClient/Packages/vbl.urchin/Scripts/JSON/CustomMesh.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8373d87ca58a1d24f9459fd161430562 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityClient/Packages/vbl.urchin/Scripts/JSON/CustomMesh/CustomMeshData.cs b/UnityClient/Packages/vbl.urchin/Scripts/JSON/CustomMesh/CustomMeshData.cs new file mode 100644 index 00000000..83e5b304 --- /dev/null +++ b/UnityClient/Packages/vbl.urchin/Scripts/JSON/CustomMesh/CustomMeshData.cs @@ -0,0 +1,9 @@ +using UnityEngine; + +public struct CustomMeshData +{ + public string ID; + public Vector3[] vertices; + public Vector3Int[] triangles; + public Vector3[] normals; +} diff --git a/UnityClient/Packages/vbl.urchin/Scripts/JSON/CustomMesh/CustomMeshData.cs.meta b/UnityClient/Packages/vbl.urchin/Scripts/JSON/CustomMesh/CustomMeshData.cs.meta new file mode 100644 index 00000000..bde7ab6f --- /dev/null +++ b/UnityClient/Packages/vbl.urchin/Scripts/JSON/CustomMesh/CustomMeshData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 97af1d97294ef1d4a8e84351e08cfbea +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityClient/Packages/vbl.urchin/Scripts/JSON/CustomMesh/CustomMeshDestroy.cs b/UnityClient/Packages/vbl.urchin/Scripts/JSON/CustomMesh/CustomMeshDestroy.cs new file mode 100644 index 00000000..3d9e17f9 --- /dev/null +++ b/UnityClient/Packages/vbl.urchin/Scripts/JSON/CustomMesh/CustomMeshDestroy.cs @@ -0,0 +1,5 @@ + +public struct CustomMeshDestroy +{ + public string ID; +} diff --git a/UnityClient/Packages/vbl.urchin/Scripts/JSON/CustomMesh/CustomMeshDestroy.cs.meta b/UnityClient/Packages/vbl.urchin/Scripts/JSON/CustomMesh/CustomMeshDestroy.cs.meta new file mode 100644 index 00000000..857be586 --- /dev/null +++ b/UnityClient/Packages/vbl.urchin/Scripts/JSON/CustomMesh/CustomMeshDestroy.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dad09aaad5f5c434980c743dc0c23342 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityClient/Packages/vbl.urchin/Scripts/JSON/CustomMesh/CustomMeshPosition.cs b/UnityClient/Packages/vbl.urchin/Scripts/JSON/CustomMesh/CustomMeshPosition.cs new file mode 100644 index 00000000..85407e67 --- /dev/null +++ b/UnityClient/Packages/vbl.urchin/Scripts/JSON/CustomMesh/CustomMeshPosition.cs @@ -0,0 +1,8 @@ +using UnityEngine; + +public struct CustomMeshPosition +{ + public string ID; + public Vector3 Position; + public bool UseReference; +} diff --git a/UnityClient/Packages/vbl.urchin/Scripts/JSON/CustomMesh/CustomMeshPosition.cs.meta b/UnityClient/Packages/vbl.urchin/Scripts/JSON/CustomMesh/CustomMeshPosition.cs.meta new file mode 100644 index 00000000..e94f218f --- /dev/null +++ b/UnityClient/Packages/vbl.urchin/Scripts/JSON/CustomMesh/CustomMeshPosition.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8950578b8395333409066ae3b2f00eef +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityClient/Packages/vbl.urchin/Scripts/Managers/CustomMeshManager.cs b/UnityClient/Packages/vbl.urchin/Scripts/Managers/CustomMeshManager.cs index e437d21b..3026c3a8 100644 --- a/UnityClient/Packages/vbl.urchin/Scripts/Managers/CustomMeshManager.cs +++ b/UnityClient/Packages/vbl.urchin/Scripts/Managers/CustomMeshManager.cs @@ -1,21 +1,89 @@ +using BrainAtlas; using System.Collections; using System.Collections.Generic; +using Unity.Entities.UniversalDelegates; using UnityEngine; +using Urchin.API; namespace Urchin.Managers { public class CustomMeshManager : MonoBehaviour { - // Start is called before the first frame update - void Start() + #region Serialized + [SerializeField] private Transform _customMeshParentT; + #endregion + + #region Private + Dictionary _customMeshGOs; + #endregion + + private void Start() + { + _customMeshGOs = new(); + + Client_SocketIO.CustomMeshCreate += Create; + Client_SocketIO.CustomMeshDestroy += Destroy; + Client_SocketIO.CustomMeshPosition += SetPosition; + + Client_SocketIO.ClearCustomMeshes += Clear; + } + + public void Create(CustomMeshData data) + { + GameObject go = new GameObject(data.ID); + go.transform.SetParent(_customMeshParentT); + + Mesh mesh = new Mesh(); + mesh.vertices = data.vertices; + + int[] triangles = new int[data.triangles.Length * 3]; + for (int i = 0; i < data.triangles.Length; i++) + { + triangles[i + 0] = data.triangles[i].x; + triangles[i + 1] = data.triangles[i].y; + triangles[i + 2] = data.triangles[i].z; + } + mesh.triangles = triangles; + + if (data.normals != null) + mesh.normals = data.normals; + + go.AddComponent().mesh = mesh; + go.AddComponent().material = MaterialManager.MeshMaterials["default"]; + + _customMeshGOs.Add(data.ID, go); + } + + public void Destroy(CustomMeshDestroy data) + { + if (_customMeshGOs.ContainsKey(data.ID)) + { + Destroy(_customMeshGOs[data.ID]); + _customMeshGOs.Remove(data.ID); + } + else + Client_SocketIO.LogWarning($"Custom mesh {data.ID} does not exist in the scene, cannot destroy"); + + } + + public void SetPosition(CustomMeshPosition data) { + if (_customMeshGOs.ContainsKey(data.ID)) + { + Transform transform = _customMeshGOs[data.ID].transform; + transform.position = BrainAtlasManager.ActiveReferenceAtlas.Atlas2World(data.Position, data.UseReference? data.UseReference : true); + } + else + Client_SocketIO.LogWarning($"Custom mesh {data.ID} does not exist in the scene, cannot set position"); } - // Update is called once per frame - void Update() + public void Clear() { + foreach (var kvp in _customMeshGOs) + Destroy(kvp.Value); + _customMeshGOs.Clear(); } }