/// <summary> /// Loads the study with the provided index. /// </summary> /// <param name="studyIndex">The index of the study to load.</param> public async void LoadStudy(int studyIndex) { _ = ProgressIndicator.StartProgressIndicator("Loading data..."); // tell other clients to also load the study var command = new MessageLoadStudy(studyIndex); Services.NetworkManager().SendMessage(command.Pack()); // delete all current Visualizations Services.VisManager().DeleteAllVisualizations(false); // load the actual data await Services.DataManager().LoadStudyAsync(studyIndex); StudyChangeBroadcast.Invoke(studyIndex); // set session filter (also remotely) Services.StudyManager().UpdateSessionFilter(new List <int> { 0 }, new List <int> { 0 }); _ = ProgressIndicator.StopProgressIndicator(); }
/// <summary> /// Stops/pauses playback. /// </summary> public void StopPlayback() { TimelineStatus = TimelineStatus.PAUSED; var message = new MessageUpdateTimeline(new TimelineState(TimelineStatus, CurrentTimestamp, MinTimestamp, MaxTimestamp, PlaybackSpeed)); Services.NetworkManager().SendMessage(message.Pack()); TimelineEventBroadcast.Invoke(message.TimelineState); }
/// <summary> /// Sets the playback speed. /// </summary> /// <param name="playbackSpeed">The new playback speed factor.</param> public void SetPlaybackSpeed(float playbackSpeed) { PlaybackSpeed = playbackSpeed; var message = new MessageUpdateTimeline(new TimelineState(TimelineStatus, CurrentTimestamp, MinTimestamp, MaxTimestamp, PlaybackSpeed)); Services.NetworkManager().SendMessage(message.Pack()); TimelineEventBroadcast.Invoke(message.TimelineState); }
// Start is called before the first frame update private void Start() { CurrentTimeFilter.MinTime = 0; CurrentTimeFilter.MaxTime = 1; Services.NetworkManager().RegisterMessageHandler(MessageContainer.MessageType.LOAD_STUDY, OnLoadStudy); Services.NetworkManager().RegisterMessageHandler(MessageContainer.MessageType.UPDATE_TIMELINE, OnTimelineChange); Services.NetworkManager().RegisterMessageHandler(MessageContainer.MessageType.UPDATE_TIME_FILTER, OnTimeFilterChange); Services.NetworkManager().RegisterMessageHandler(MessageContainer.MessageType.UPDATE_SESSION_FILTER, OnSessionFilterChange); }
/// <summary> /// Updates the session filter with the provided <see cref="List{int}">Lists</see> of sessions and conditions. /// </summary> /// <param name="sessions">The indices of the sessions to select.</param> /// <param name="conditions">The indices of the conditions to select.</param> public void UpdateSessionFilter(List <int> sessions, List <int> conditions) { CurrentStudyConditions = conditions; CurrentStudySessions = sessions; UpdateTimestampBounds(); var message = new MessageUpdateSessionFilter(sessions, conditions); Services.NetworkManager().SendMessage(message.Pack()); SessionFilterEventBroadcast.Invoke(); }
/// <summary> /// Updates an existing visualization with the provided <see cref="VisProperties"/>. /// </summary> /// <param name="config">The struct containing the settings for the updated visualization.</param> /// <param name="syncWithRemote">Indicates whether this update should also happen on remote clients.</param> public void UpdateVisualization(VisProperties config, bool syncWithRemote = true) { if (Visualizations.TryGetValue(config.VisId, out AbstractView visualization)) { visualization.UpdateView(config); if (syncWithRemote) { var message = new MessageUpdateVisualization(config); Services.NetworkManager().SendMessage(message.Pack()); } } }
/// <summary> /// Updates the time filter. /// </summary> /// <param name="min">A float between 0 and 1 indicating the lower bound of the filter.</param> /// <param name="max">A float between 0 and 1 indicating the upper bound of the filter.</param> public void UpdateTimeFilter(float min, float max) { CurrentTimeFilter.MinTime = min; CurrentTimeFilter.MaxTime = max; UpdateTimestampBounds(); var message = new MessageUpdateTimeFilter(CurrentTimeFilter); Services.NetworkManager().SendMessage(message.Pack()); TimeFilterEventBroadcast.Invoke(message.TimeFilter); }
/// <summary> /// Unity start function. /// </summary> public void Start() { Services.NetworkManager().RegisterMessageHandler(MessageContainer.MessageType.CREATE_VISUALIZATION, OnCreateVisualization); Services.NetworkManager().RegisterMessageHandler(MessageContainer.MessageType.CREATE_CONTAINER, OnCreateVisContainer); Services.NetworkManager().RegisterMessageHandler(MessageContainer.MessageType.UPDATE_VISUALIZATION, OnUpdateVisualization); Services.NetworkManager().RegisterMessageHandler(MessageContainer.MessageType.DELETE_VISUALIZATION, OnDeleteVisualization); Services.NetworkManager().RegisterMessageHandler(MessageContainer.MessageType.DELETE_ALL_VISUALIZATIONS, OnDeleteAllVisualizations); Services.NetworkManager().RegisterMessageHandler(MessageContainer.MessageType.DELETE_ALL_CONTAINERS, OnDeleteAllContainers); Services.NetworkManager().RegisterMessageHandler(MessageContainer.MessageType.CENTER_DATA, OnCenterData); Services.StudyManager().SessionFilterEventBroadcast.AddListener(OnSessionFilterChange); Services.StudyManager().StudyChangeBroadcast.AddListener(OnStudyLoaded); }
private void SendUserUpdate() { if (cameraTransform != null && message != null) { LocalUserPosition.position = cameraTransform.position; LocalUserPosition.rotation = cameraTransform.rotation; message.Color = Color; message.Position = LocalUserPosition.localPosition; message.Orientation = LocalUserPosition.localRotation; message.Id = id; Services.NetworkManager().SendMessage(message.Pack()); } }
/// <summary> /// Sends the anchor to a client. /// </summary> /// <param name="client">The client connection to send the anchor to.</param> public void SendAnchor(Socket client) { #if UNITY_WSA && !UNITY_EDITOR if (exportingAnchorBytes != null && IsAnchorEstablished) { // Send existing anchor data to clients var Command = new MessageWorldAnchor(exportingAnchorBytes.ToArray()); Services.NetworkManager().Network.SendToClient(Command.Pack(), client); } else { // create new anchor and send it to clients CreateAnchor(); } #endif }
private void Start() { #if UNITY_EDITOR IsAnchorEstablished = true; #elif UNITY_WSA && !(ENABLE_IL2CPP && NET_STANDARD_2_0) if (HolographicSettings.IsDisplayOpaque) { IsAnchorEstablished = true; } else { Services.NetworkManager().RegisterMessageHandler(MessageContainer.MessageType.WORLD_ANCHOR, OnAnchorData); } #else IsAnchorEstablished = true; #endif }
/// <summary> /// Centers or un-centers the data to the origin, based on the average position of the samples. /// </summary> /// <param name="isCentering">Indicated whether to center or to reverse.</param> /// <param name="syncWithRemote">Indicates whether the centering should also happen on remote clients.</param> public void CenterData(bool isCentering, bool syncWithRemote = true) { var GlobalOffset = GameObject.FindGameObjectWithTag("GlobalOffset"); if (GlobalOffset != null) { // center or un-center data if (isCentering == true && GlobalOffset.transform.position == Vector3.zero) { Vector3 averagePosition = Vector3.zero; foreach (var studyObject in Services.DataManager().DataSets.Values) { averagePosition += studyObject.AveragePosition; } averagePosition /= Services.DataManager().DataSets.Values.Count; GameObject.FindGameObjectWithTag("GlobalOffset").transform.localPosition -= averagePosition; // send message to the other clients if (syncWithRemote) { var message = new MessageCenterData(isCentering); Services.NetworkManager().SendMessage(message.Pack()); } // send notification event that the data was centered or un-centered DataCenteringEventBroadcast.Invoke(true); } else { GameObject.FindGameObjectWithTag("GlobalOffset").transform.localPosition = Vector3.zero; // send message to the other clients if (syncWithRemote) { var message = new MessageCenterData(isCentering); Services.NetworkManager().SendMessage(message.Pack()); } // send notification event that the data was centered or un-centered DataCenteringEventBroadcast.Invoke(false); } } }
/// <summary> /// Deletes all existing <see cref="ViewContainer">ViewContainers</see>. /// </summary> /// <param name="syncWithRemote">Indicates whether the containers should also be deleted on remote clients.</param> public void DeleteAllViewContainers(bool syncWithRemote = true) { if (ViewContainers != null) { foreach (var container in ViewContainers) { Destroy(container.Value.gameObject); } ViewContainers.Clear(); } if (syncWithRemote) { var message = new MessageDeleteAllVisContainers(); Services.NetworkManager().SendMessage(message.Pack()); } }
// Start is called before the first frame update private void Start() { SetColor(0); worldAnchor = GameObject.FindGameObjectWithTag("RootWorldAnchor").transform; Services.NetworkManager().RegisterMessageHandler(MessageContainer.MessageType.UPDATE_USER, OnRemoteUserUpdate); Services.NetworkManager().RegisterMessageHandler(MessageContainer.MessageType.ACCEPT_CLIENT, OnAcceptedAsClient); Transform cameraTransform = CameraCache.Main ? CameraCache.Main.transform : null; if (cameraTransform != null) { message = new MessageUpdateUser(cameraTransform.position, cameraTransform.rotation, id, Color); Services.NetworkManager().SendMessage(message.Pack()); LocalUserPosition = new GameObject().transform; LocalUserPosition.position = cameraTransform.position; LocalUserPosition.rotation = cameraTransform.rotation; } }
/// <summary> /// Deletes all existing visualizations from the scene. /// </summary> /// <param name="syncWithRemote">Indicates whether the visualization should also be deleted on remote clients.</param> public void DeleteAllVisualizations(bool syncWithRemote = true) { foreach (var kvp in Visualizations) { var settings = kvp.Value.Settings; kvp.Value.Dispose(); // send notification event that a vis was deleted VisualizationDeletedEventBroadcast.Invoke(settings); } Visualizations.Clear(); if (syncWithRemote) { var message = new MessageDeleteAllVisualizations(); Services.NetworkManager().SendMessage(message.Pack()); } }
/// <summary> /// Deletes a single visualization with the provided <see cref="Guid"/>. /// </summary> /// <param name="visId">The <see cref="Guid"/> of the visualization.</param> /// <param name="syncWithRemote">Indicates whether the visualization should also be deleted on remote clients.</param> /// <returns>A bool indicating whether a visualization was successfully deleted. /// Returns <see langword="false"/> when there is no visualization with the provided <see cref="Guid"/>.</returns> public bool DeleteVisualization(Guid visId, bool syncWithRemote = true) { if (Visualizations.ContainsKey(visId)) { var settings = Visualizations[visId].Settings; Visualizations[visId].Dispose(); Visualizations.Remove(visId); if (syncWithRemote) { var message = new MessageDeleteVisualization(visId); Services.NetworkManager().SendMessage(message.Pack()); } // send notification event that a vis was deleted VisualizationDeletedEventBroadcast.Invoke(settings); return(true); } return(false); }
/// <summary> /// Called when serializing an anchor is complete. /// </summary> /// <param name="status">If the serialization succeeded.</param> private void ExportComplete(SerializationCompletionReason status) { if (status == SerializationCompletionReason.Succeeded && exportingAnchorBytes.Count > MinTrustworthySerializedAnchorDataSize) { AnchorName = exportingAnchorName; anchorData = exportingAnchorBytes.ToArray(); ////createdAnchor = true; Debug.Log("Anchor ready " + exportingAnchorBytes.Count); IsAnchorEstablished = true; // Send anchor data to clients var command = new MessageWorldAnchor(exportingAnchorBytes.ToArray()); Services.NetworkManager().SendMessage(command.Pack()); } else { Debug.Log("Create anchor failed " + status + " " + exportingAnchorBytes.Count); exportingAnchorBytes.Clear(); DestroyImmediate(ObjectToAnchor.GetComponent <WorldAnchor>()); CreateAnchor(); } }
/// <summary> /// Creates a new <see cref="ViewContainer"/> and adds it to the list of containers. /// </summary> /// <param name="container">The <see cref="VisContainer"/> object representing the settings for the new <see cref="ViewContainer"/>.</param> /// <param name="syncWithRemote">Indicates whether the container should also be created on remote clients.</param> public void CreateViewContainer(VisContainer container, bool syncWithRemote = true) { // add to list of containers or update list if (ViewContainers.ContainsKey(container.Id)) { // already in list, update ViewContainers[container.Id].Init(container); } else { // not in list, create and add Transform worldAnchor = GameObject.FindGameObjectWithTag("VisRootAnchor").transform; var placeholder = Instantiate(VisPlaceholderPrefab, worldAnchor); // instantiate placeholder prefab, set the World Anchor as parent, to make sure every client sees the same placeholder.Init(container); ViewContainers.Add(container.Id, placeholder.GetComponent <ViewContainer>()); // add to list } if (syncWithRemote) { var message = new MessageCreateVisContainer(container); Services.NetworkManager().SendMessage(message.Pack()); } }
private async Task OnLoadStudy(MessageContainer obj) { Debug.Log("Loading Study"); Services.NetworkManager().Pause(); MessageLoadStudy message = MessageLoadStudy.Unpack(obj); if (message != null) { _ = ProgressIndicator.StartProgressIndicator("Loading data..."); // delete all current Visualizations Services.VisManager().DeleteAllVisualizations(false); // load the actual data await Services.DataManager().LoadStudyAsync(message.StudyIndex); StudyChangeBroadcast.Invoke(message.StudyIndex); _ = ProgressIndicator.StopProgressIndicator(); } Services.NetworkManager().Unpause(); Debug.Log("Loading Study - Completed"); }
// Start is called before the first frame update private void Start() { worldAnchor = GameObject.FindGameObjectWithTag("VisRootAnchor").transform; Services.NetworkManager().RegisterMessageHandler(MessageContainer.MessageType.UPDATE_ANNOTATION, OnAnnotationUpdated); }
/// <summary> /// This method adds a new annotation. /// </summary> public void AddAnnotation() { if (!AnnotationPrefab) { return; } Transform cameraTransform = CameraCache.Main ? CameraCache.Main.transform : null; foreach (var source in CoreServices.InputSystem.DetectedInputSources) { // Only look for hands if (source.SourceType == Microsoft.MixedReality.Toolkit.Input.InputSourceType.Hand) { foreach (var pointer in source.Pointers) { if (pointer is IMixedRealityNearPointer || true) { Vector3 position; if (pointer.Result != null) { position = pointer.Result.Details.Point; } else { position = pointer.Position; } if (cameraTransform != null) { if ((cameraTransform.position - position).magnitude > 3) { position = cameraTransform.position + ((position - cameraTransform.position) * 0.5f); } } var annotation = Instantiate(AnnotationPrefab, worldAnchor); annotation.transform.position = position; annotation.Id = Guid.NewGuid(); annotation.SetColor(Services.UserManager().Color); annotationList.Add(annotation.Id, annotation); var message = new MessageUpdateAnnotation(annotation.Id, annotation.transform.localPosition, annotation.Color); Services.NetworkManager().SendMessage(message.Pack()); return; // only take first valid pointer } } } } if (cameraTransform != null) { Vector3 position = cameraTransform.position; var annotation = Instantiate(AnnotationPrefab, worldAnchor); annotation.transform.position = position; annotation.Id = Guid.NewGuid(); annotation.SetColor(Services.UserManager().Color); annotationList.Add(annotation.Id, annotation); var message = new MessageUpdateAnnotation(annotation.Id, annotation.transform.localPosition, annotation.Color); Services.NetworkManager().SendMessage(message.Pack()); return; // only take first valid pointer } }
/// <summary> /// Creates a visualization from a <see cref="VisProperties"/>. /// </summary> /// <param name="settings">The struct containing the settings for the visualization.</param> /// <param name="syncWithRemote">Indicates whether the visualization should also be created on other clients.</param> /// <returns>A <see cref="GameObject"/> with the visualization.</returns> public GameObject CreateVisualization(VisProperties settings, bool syncWithRemote = true) { try { if (Visualizations.ContainsKey(settings.VisId)) { throw new Exception("VisId " + settings.VisId + " has been used twice!"); } // if the vis id is empty, create a new, non-colliding GUID if (settings.VisId == Guid.Empty) { settings.VisId = Guid.NewGuid(); // This is extremely unlikely to ever happen while (Visualizations.ContainsKey(settings.VisId)) { Debug.LogWarning("GUID collision! Probably someone made a mistake. If not, you should start playing the lottery!"); settings.VisId = Guid.NewGuid(); } } // special case: timeline control view & coordinate system GameObject vis; if (settings.VisType == VisType.TimelineControl) { var timelineControl = Instantiate(TimelineControlPrefab); timelineControl.Init(settings); vis = timelineControl.gameObject; timelineController = timelineControl; } else if (settings.VisType == VisType.CoordinateSystem3D) { var coordinateSystem = Instantiate(CoordinateSystemPrefab); coordinateSystem.Init(settings); vis = coordinateSystem.gameObject; coordinateSystemVis = coordinateSystem; } // get correct prefab AbstractView visPrefab = null; foreach (var prefab in VisualizationPrefabs) { if (prefab.VisType == settings.VisType) { visPrefab = prefab; break; } } if (visPrefab == null) { throw new Exception("Attempt to spawn unknown visualization! Type: " + settings.VisType); } // instantiate prefab var visInstance = Instantiate(visPrefab); visInstance.Init(settings); vis = visInstance.gameObject; if (settings.AnchorId != -1 && ViewContainers.ContainsKey(settings.AnchorId)) { // anchor is available ViewContainers[settings.AnchorId].AttachVis(vis.GetComponent <AbstractView>()); } else if (settings.VisType == VisType.TimelineControl) { // anchor not available, put at world anchor Transform worldAnchor = GameObject.FindGameObjectWithTag("RootWorldAnchor").transform; vis.transform.SetParent(worldAnchor, false); } else { // anchor not available, put at world anchor Transform worldAnchor = GameObject.FindGameObjectWithTag("VisRootAnchor").transform; vis.transform.SetParent(worldAnchor, false); } Visualizations[settings.VisId] = vis.GetComponent <AbstractView>(); // at this point, creation was successful; send message to the other clients if needed if (syncWithRemote) { var message = new MessageCreateVisualization(settings); Services.NetworkManager().SendMessage(message.Pack()); } // send notification event that a new vis was created VisualizationCreatedEventBroadcast.Invoke(settings); return(vis); } catch (Exception e) { Debug.LogWarning("Creation of requested Visualization failed."); Debug.LogError(e.Message); Debug.LogError(e.StackTrace); return(new GameObject("Creation Failed")); } }