/// <summary> /// Saves the surface to bytes. Performs also modified child surfaces saving before. /// </summary> /// <remarks> /// Assume this method does not throw exceptions but uses return value as a error code. /// </remarks> /// <returns>True if failed, otherwise false.</returns> public bool Save() { // Save all children modified before saving the current surface for (int i = 0; i < Children.Count; i++) { if (Children[i].IsModified && Children[i].Save()) { return(true); } } Saving?.Invoke(this); // Save surface meta _meta.AddEntry(10, Utils.StructureToByteArray(ref CachedSurfaceMeta)); // Save all nodes meta VisjectSurface.Meta11 meta11; for (int i = 0; i < Nodes.Count; i++) { var node = Nodes[i]; meta11.Position = node.Location; meta11.Selected = false; // don't save selection to prevent stupid binary diffs on asset // TODO: reuse byte[] array for all nodes to reduce dynamic memory allocations node.Meta.AddEntry(11, Utils.StructureToByteArray(ref meta11)); } // Save graph try { // Save graph using (var stream = new MemoryStream()) using (var writer = new BinaryWriter(stream)) { // Save graph to bytes SaveGraph(writer); var bytes = stream.ToArray(); // Send data to the container Context.SurfaceData = bytes; Saved?.Invoke(this); // Clear modification flag _isModified = false; } } catch (Exception ex) { // Error Editor.LogWarning("Saving Visject Surface data failed."); Editor.LogWarning(ex); return(true); } return(false); }
/// <summary> /// Called when node gets loaded and should be added to the surface. Creates node elements from the archetype. /// </summary> /// <param name="node">The node.</param> public virtual void OnNodeLoaded(SurfaceNode node) { // Create child elements of the node based on it's archetype int elementsCount = node.Archetype.Elements?.Length ?? 0; for (int i = 0; i < elementsCount; i++) { // ReSharper disable once PossibleNullReferenceException node.AddElement(node.Archetype.Elements[i]); } // Load metadata var meta = node.Meta.GetEntry(11); if (meta.Data != null) { var meta11 = Utils.ByteArrayToStructure <VisjectSurface.Meta11>(meta.Data); node.Location = meta11.Position; //node.IsSelected = meta11.Selected; } }
/// <summary> /// Loads the surface from bytes. Clears the surface before and uses context source data as a surface bytes source. /// </summary> /// <remarks> /// Assume this method does not throw exceptions but uses return value as a error code. /// </remarks> /// <returns>True if failed, otherwise false.</returns> public bool Load() { Surface._isUpdatingBoxTypes++; try { // Prepare Clear(); Loading?.Invoke(this); // Load bytes var bytes = Context.SurfaceData; if (bytes == null) { throw new Exception("Failed to load surface data."); } // Load graph (empty bytes data means empty graph for simplicity when using subgraphs) if (bytes.Length > 0) { using (var stream = new MemoryStream(bytes)) using (var reader = new BinaryReader(stream)) { LoadGraph(reader); } } // Load surface meta var meta = _meta.GetEntry(10); if (meta.Data != null) { Utils.ByteArrayToStructure(meta.Data, out CachedSurfaceMeta); } else { // Reset view CachedSurfaceMeta.ViewCenterPosition = Vector2.Zero; CachedSurfaceMeta.Scale = 1.0f; } // [Deprecated on 04.07.2019] Load surface comments var commentsData = _meta.GetEntry(666); if (commentsData.Data != null) { using (var stream = new MemoryStream(commentsData.Data)) using (var reader = new BinaryReader(stream)) { var commentsCount = reader.ReadInt32(); for (int i = 0; i < commentsCount; i++) { var title = reader.ReadStr(71); var color = new Color(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); var bounds = new Rectangle(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); var comment = SpawnComment(ref bounds, title, color); if (comment == null) { throw new InvalidOperationException("Failed to create comment."); } OnControlLoaded(comment); } } } // Post load for (int i = 0; i < RootControl.Children.Count; i++) { if (RootControl.Children[i] is SurfaceControl control) { control.OnSurfaceLoaded(); } } RootControl.UnlockChildrenRecursive(); // Update boxes types for nodes that dependant box types based on incoming connections { bool keepUpdating = false; int updateLimit = 100; do { for (int i = 0; i < RootControl.Children.Count; i++) { if (RootControl.Children[i] is SurfaceNode node && !node.HasDependentBoxesSetup) { node.UpdateBoxesTypes(); keepUpdating = true; } } } while (keepUpdating && updateLimit-- > 0); } Loaded?.Invoke(this); // Clear modification flag _isModified = false; } catch (Exception ex) { // Error Editor.LogWarning("Loading Visject Surface data failed."); Editor.LogWarning(ex); return(true); } finally { Surface._isUpdatingBoxTypes--; } return(false); }