/// <summary> /// Iterates over the position of every node, translating it to a widget position /// Returns the offsets of the furthest nodes of the edges of the widget /// To fit the graph in the screen, each offset needs to be as small as possible above 0 /// /// Acquires reader lock /// </summary> /// <param name="graphWidgetSize">Size of the rendering widget</param> /// <param name="graph">Graph being displayed in the widget</param> /// <param name="isPreview">True if preview widget, false if main</param> /// <param name="xoffsets">Furthest from the left and right sides of the widget</param> /// <param name="yoffsets">Furthest from the top and bottom of the widget</param> /// <param name="zoffsets">Furthest from in front of/behind the camera lens in the Z direction</param> /// <returns>true if a meaningful result was returned</returns> public static bool GetWidgetFitOffsets(Vector2 graphWidgetSize, PlottedGraph graph, bool isPreview, out Vector2 xoffsets, out Vector2 yoffsets, out Vector2 zoffsets) { if (GlobalConfig.Settings.Logs.BulkLogging) { Logging.RecordLogEvent($"GetWidgetFitOffsets Start {graph.TID} layout", Logging.LogFilterType.BulkDebugLogFile); } xoffsets = new Vector2(0, 0); yoffsets = new Vector2(0, 0); zoffsets = new Vector2(0, 0); float zoom = isPreview ? graph.CameraState.PreviewCameraZoom : graph.CameraState.MainCameraZoom; float aspectRatio = graphWidgetSize.X / graphWidgetSize.Y; Matrix4x4 translation = isPreview ? graph.CameraState.PreviewCameraTranslation : graph.CameraState.MainCameraTranslation; Matrix4x4 projection = Matrix4x4.CreatePerspectiveFieldOfView(1.0f, aspectRatio, 1, 80000); Matrix4x4 worldView = Matrix4x4.CreateFromAxisAngle(Vector3.UnitY, 0) * translation; Vector2 xlimits = new Vector2(float.MaxValue, float.MinValue); Vector2 ylimits = new Vector2(float.MaxValue, float.MinValue); Vector2 zlimits = new Vector2(float.MaxValue, float.MinValue); Vector2 ev = new Vector2(0, 0); Vector2 xmin = ev, xmax = ev, ymin = ev, ymax = ev; float[] positions = graph.LayoutState.DownloadVRAMPositions(); bool result; if (positions.Length < 4) { result = false; } else { result = true; for (int idx = 0; idx < positions.Length; idx += 4) { float guard = positions[idx + 3]; if (guard is not 1) { break; } float x = positions[idx]; float y = positions[idx + 1]; float z = positions[idx + 2]; Vector3 worldpos = new Vector3(x, y, z); Vector2 ndcPos = GraphicsMaths.WorldToNDCPos(worldpos, worldView, projection); if (ndcPos.X < xlimits.X) { xlimits = new Vector2(ndcPos.X, xlimits.Y); xmin = ndcPos; } if (ndcPos.X > xlimits.Y) { xlimits = new Vector2(xlimits.X, ndcPos.X); xmax = ndcPos; } if (ndcPos.Y < ylimits.X) { ylimits = new Vector2(ndcPos.Y, ylimits.Y); ymin = ndcPos; } if (ndcPos.Y > ylimits.Y) { ylimits = new Vector2(ylimits.X, ndcPos.Y); ymax = ndcPos; } if (worldpos.Z < zlimits.X) { zlimits = new Vector2(worldpos.Z, zlimits.Y); } if (worldpos.Z > zlimits.Y) { zlimits = new Vector2(zlimits.X, worldpos.Z); } } Vector2 minxS = GraphicsMaths.NdcToScreenPos(xmin, graphWidgetSize); Vector2 maxxS = GraphicsMaths.NdcToScreenPos(xmax, graphWidgetSize); Vector2 minyS = GraphicsMaths.NdcToScreenPos(ymin, graphWidgetSize); Vector2 maxyS = GraphicsMaths.NdcToScreenPos(ymax, graphWidgetSize); xoffsets = new Vector2(minxS.X, graphWidgetSize.X - maxxS.X); yoffsets = new Vector2(minyS.Y, graphWidgetSize.Y - maxyS.Y); zoffsets = new Vector2(zlimits.X - zoom, zlimits.Y - zoom); } //Sometimes the position buffer is full of terrible data. //Seems to just be for the preview graph? Only happens at the start so must have gotten hold of uninitialised data if (zoffsets.X > 100000000000 || zoffsets.X < -100000000000) { if (isPreview) { graph.CameraState.PreviewCameraZoom = -60000; } else { graph.CameraState.MainCameraZoom = -60000; } return(false); } if (GlobalConfig.Settings.Logs.BulkLogging) { Logging.RecordLogEvent($"GetWidgetFitOffsets exit", Logging.LogFilterType.BulkDebugLogFile); } return(result); }
/// <summary> /// Update the node attributes compute VRAM buffer (alpha, node size, mouseover details) /// </summary> /// <param name="cl">Thread-specific CommandList</param> /// <param name="graph">ProtoGraph being drawn</param> /// <param name="inputAttributes">Attributes buffer being updated</param> /// <param name="resources">Shader resources ResourceSet</param> /// <param name="delta">Time-delta from the last update</param> /// <param name="mouseoverNodeID">Index of the node the mouse is over</param> /// <param name="useAnimAttribs">Flag to specify the graph is in animated-alpha mode</param> private unsafe void RenderNodeAttribs(CommandList cl, PlottedGraph graph, DeviceBuffer inputAttributes, ResourceSet resources, float delta, int mouseoverNodeID, bool useAnimAttribs) { if (GlobalConfig.Settings.Logs.BulkLogging) { Logging.RecordLogEvent($"RenderNodeAttribs {this.EngineID}", Logging.LogFilterType.BulkDebugLogFile); } AttribShaderParams parms = new AttribShaderParams { delta = delta, hoveredNodeID = mouseoverNodeID, nodeCount = (uint)Math.Min(graph.RenderedNodeCount(), graph.LayoutState.AttributesVRAM1 !.SizeInBytes / 16), MinimumAlpha = GlobalConfig.AnimatedFadeMinimumAlpha, hoverMode = (mouseoverNodeID != -1) ? 1 : 0, isAnimated = useAnimAttribs ? 1: 0 }; graph.GetActiveNodeIndexes(out List <uint> pulseNodes, out List <uint> lingerNodes, out uint[] deactivatedNodes); if (GlobalConfig.Settings.Logs.BulkLogging) { Logging.RecordLogEvent($"RenderNodeAttribs {this.EngineID} updating attribsbuf {inputAttributes.Name}", Logging.LogFilterType.BulkDebugLogFile); } cl.UpdateBuffer(_attribsParamsBuffer, 0, parms); float currentPulseAlpha = Math.Max(GlobalConfig.AnimatedFadeMinimumAlpha, GraphicsMaths.getPulseAlpha()); //todo - merge contiguous regions to reduce command count float[] valArray = new float[3]; foreach (uint idx in pulseNodes) { if (idx >= graph.RenderedNodeCount()) { break; } if (inputAttributes.SizeInBytes <= idx * 4 * sizeof(float) + (2 * sizeof(float))) { break; } valArray[0] = GlobalConfig.NodeSize; //start big valArray[1] = 1.0f; //full alpha valArray[2] = 1.0f; //pulse fixed(float *dataPtr = valArray) { Debug.Assert((idx * 4 * sizeof(float) + valArray.Length * sizeof(float)) < inputAttributes.SizeInBytes); cl.UpdateBuffer(inputAttributes, idx * 4 * sizeof(float), (IntPtr)dataPtr, (uint)valArray.Length * sizeof(float)); } } //make the active node pulse if (graph.IsAnimated) { uint activeNodeIdx = graph.LastAnimatedVert; if (!lingerNodes.Contains(activeNodeIdx)) { valArray[0] = currentPulseAlpha; fixed(float *dataPtr = valArray) { uint nodeAlphaOffset = (activeNodeIdx * 4 * sizeof(float)) + (2 * sizeof(float)); if (nodeAlphaOffset + sizeof(float) <= inputAttributes.SizeInBytes) { cl.UpdateBuffer(inputAttributes, nodeAlphaOffset, (IntPtr)dataPtr, sizeof(float)); } } } } foreach (uint idx in lingerNodes) { if (idx >= graph.RenderedNodeCount()) { break; } if (inputAttributes.SizeInBytes <= idx * 4 * sizeof(float) + (2 * sizeof(float))) { break; } valArray[0] = 2.0f + currentPulseAlpha; fixed(float *dataPtr = valArray) { Debug.Assert((idx * 4 * sizeof(float) + (2 * sizeof(float)) + sizeof(float)) < inputAttributes.SizeInBytes); cl.UpdateBuffer(inputAttributes, idx * 4 * sizeof(float) + (2 * sizeof(float)), (IntPtr)dataPtr, sizeof(float)); } } foreach (uint idx in deactivatedNodes) { if (idx >= graph.RenderedNodeCount()) { break; } if (inputAttributes.SizeInBytes <= idx * 4 * sizeof(float) + (2 * sizeof(float))) { break; } valArray[0] = 0.8f; fixed(float *dataPtr = valArray) { Debug.Assert((idx * 4 * sizeof(float) + (2 * sizeof(float)) + sizeof(float)) < inputAttributes.SizeInBytes); cl.UpdateBuffer(inputAttributes, idx * 4 * sizeof(float) + (2 * sizeof(float)), (IntPtr)dataPtr, sizeof(float)); } } if (graph.HighlightsChanged) { ApplyHighlightAttributes(cl, graph, inputAttributes); } cl.SetPipeline(_nodeAttribComputePipeline); cl.SetComputeResourceSet(0, resources); cl.Dispatch((uint)Math.Ceiling(inputAttributes.SizeInBytes / (256.0 * sizeof(Vector4))), 1, 1); }