/// <summary> /// Generates scene graph updates for renderer. /// </summary> public async IAsyncEnumerable <IRenderCommand> Process(AssertionBatch assertionBatch) { var knownUpdateObjects = new List <ObjectIdentityBase>(); foreach (var assertion in assertionBatch.Assertions) { if (assertion is GetPickingInfo getPickingInfo) { yield return(new RenderCommands.RequestPickingInfo(getPickingInfo.ScreenX, getPickingInfo.ScreenY)); } else if (assertion is Core.Assertions.ClearColor clearColor) { _sceneGraph.RgbClearColor = new (clearColor.Red, clearColor.Green, clearColor.Blue, 1.0f); yield return(new RenderCommands.ClearColor(_sceneGraph.RgbClearColor)); } else if (assertion is CameraProjectionPerspective projectionPerspective) { // Projection matrix is set in uniforms yield return(new RenderCommands.SetProjectionMatrix(_sceneGraph, projectionPerspective.FieldOfViewVerticalDegrees, projectionPerspective.NearPlane, projectionPerspective.FarPlane)); } else if (assertion is CameraOrientation cameraOrientation) { // View matrix is set in uniforms _sceneGraph.ViewMatrix = cameraOrientation.ToMatrix(); } else if (assertion is Core.Assertions.Vertex assertionVertex) { if (!_sceneGraph.Vertices.TryGetValue(assertionVertex.VertexId, out Core.Scene.Vertex vertex)) { vertex = new Core.Scene.Vertex(_sceneGraph, assertionVertex.VertexId); _sceneGraph.Vertices[assertionVertex.VertexId] = vertex; } if (assertionVertex.Coordinates != default) { vertex.Coordinates = assertionVertex.Coordinates; knownUpdateObjects.Add(vertex); } } else if (assertion is Core.Assertions.Triangle triangle) { var newTriangle = new Core.Scene.Triangle(_sceneGraph, triangle.TriangleId) { VertexIds = triangle.VertexIds }; _partialObjectsWatchList.AddLast(newTriangle); _sceneGraph.Triangles[triangle.TriangleId] = newTriangle; } else if (assertion is Core.Assertions.GrabScreenshot) { yield return(new RenderCommands.GrabScreenshot(_renderWindow, _cancellationTokenManager)); } else if (assertion is Core.Assertions.Texture texture) { // Pre-load texture to avoid doing this in the function setting OpenGL state var loadedImage = await _imageLoader.LoadImage(new Uri(texture.Uri)); _sceneGraph.Textures[texture.TextureId] = new Core.Scene.Texture(_sceneGraph, texture.TextureId, loadedImage.BufferRgba, loadedImage.Width, loadedImage.Height); } else if (assertion is TexCoords texCoords) { _sceneGraph.SurfaceVertexTexCoords[texCoords.ObjectIdentifier] = texCoords; } else if (assertion is SurfaceColor surfaceColor) { _sceneGraph.SurfaceVertexColors[surfaceColor.ObjectIdentifier] = surfaceColor; } else { throw new ArgumentException($"Unknown assertion type: {assertion.GetType()}"); } } // Split objects into new and update lists var newRenderTriangles = new List <Core.Scene.Triangle>(); // Get known complete geometry LinkedListNode <Core.Scene.Triangle> currentNode = _partialObjectsWatchList.First; while (currentNode != null) { if (_bufferManager.HasExistingTriangleBuffer(currentNode.Value)) { knownUpdateObjects.Add(currentNode.Value); currentNode = currentNode.Next; continue; } if (!currentNode.Value.HasMinimumRenderInfo) { currentNode = currentNode.Next; continue; } newRenderTriangles.Add(currentNode.Value); var next = currentNode.Next; _partialObjectsWatchList.Remove(currentNode); currentNode = next; } if (newRenderTriangles.Any()) { yield return(_bufferManager.CreateRenderCommands(newRenderTriangles)); } if (knownUpdateObjects.Any()) { foreach (var reloadCommand in _bufferManager.CreateReloadCommands(knownUpdateObjects)) { yield return(reloadCommand); } } }