public void QueryVisibleRenderers(IDrawDevice device, RawList<ICmpRenderer> targetList) { // Empty the cached list of visible renderers targetList.Count = 0; targetList.Reserve(this.totalRendererCount); // Copy references to all renderers that are visible to the target device int visibleCount = 0; ICmpRenderer[] targetData = targetList.Data; foreach (var pair in this.renderersByType) { ICmpRenderer[] data = pair.Value.Data; for (int i = 0; i < data.Length; i++) { if (i >= pair.Value.Count) break; if ((data[i] as Component).Active && data[i].IsVisible(device)) { targetData[visibleCount] = data[i]; visibleCount++; } } } targetList.Count = visibleCount; }
public void QueryVisibleRenderers(IDrawDevice device, RawList <ICmpRenderer> targetList) { // Empty the cached list of visible renderers targetList.Count = 0; targetList.Reserve(this.totalRendererCount); // Copy references to all renderers that are visible to the target device int visibleCount = 0; ICmpRenderer[] targetData = targetList.Data; foreach (var pair in this.renderersByType) { ICmpRenderer[] data = pair.Value.Data; for (int i = 0; i < data.Length; i++) { if (i >= pair.Value.Count) { break; } if ((data[i] as Component).Active && data[i].IsVisible(device)) { targetData[visibleCount] = data[i]; visibleCount++; } } } targetList.Count = visibleCount; }
public void QueryVisibleRenderers(DrawDevice device, RawList <ICmpRenderer> visibleRenderers) { int activeCount = this.cullingInfo.Count; CullingInfo[] cullingData = this.cullingInfo.Data; ICmpRenderer[] rendererData = this.cullingRenderers.Data; visibleRenderers.Clear(); visibleRenderers.Reserve(activeCount); ICmpRenderer[] visibleRendererData = visibleRenderers.Data; int visibleCount = 0; VisibilityFlag mask = device.VisibilityMask; for (int i = 0; i < activeCount; i++) { // Check group and overlay / world visibility if ((cullingData[i].Visibility & VisibilityFlag.AllGroups & mask) == VisibilityFlag.None) { continue; } if ((cullingData[i].Visibility & VisibilityFlag.ScreenOverlay) != (mask & VisibilityFlag.ScreenOverlay)) { continue; } // Check spatial visibility if (!device.IsSphereInView(cullingData[i].Position, cullingData[i].Radius)) { continue; } // Add renderer to visible result list visibleRendererData[visibleCount] = rendererData[i]; visibleCount++; } visibleRenderers.Count = visibleCount; }
private static void GenerateCollisionShapes(TileEdgeMap edgeMap, Vector2 origin, Vector2 tileSize, bool roundedCorners, IList <ShapeInfo> shapeList) { // Traverse the edge map and gradually create chain / loop // shapes until all edges have been used. RawList <Point2> currentChain = new RawList <Point2>(); RawList <Vector2> vertexBuffer = new RawList <Vector2>(); while (true) { // Begin a new continuous chain of nodes currentChain.Clear(); // Find a starting node for our current chain. // If there is none, we found and handled all edges. Point2 start = edgeMap.FindNonEmpty(); if (start == new Point2(-1, -1)) { break; } // Traverse the current chain node-by-node from the start we found Point2 current = start; while (true) { // Add the current node to our continuous chain currentChain.Add(current); // Find the next node that connects to the current one. // If there is none, our current chain is done. Point2 next = edgeMap.GetClockwiseNextFrom(current); if (next == new Point2(-1, -1)) { break; } // Remove the edge we used to get to the next node edgeMap.RemoveEdge(current, next); // Use the next node as origin for traversing further current = next; } // Generate a shape from the current chain bool isLoop = (start == currentChain[currentChain.Count - 1]); if (isLoop) { currentChain.RemoveAt(currentChain.Count - 1); } vertexBuffer.Clear(); // Rounded corners if (roundedCorners && currentChain.Count >= 3) { vertexBuffer.Reserve(currentChain.Count * 2); vertexBuffer.Count = 0; for (int i = 0; i < currentChain.Count; i++) { int prevIndex = (i - 1 + currentChain.Count) % currentChain.Count; int nextIndex = (i + 1) % currentChain.Count; Vector2 currentVert = origin + tileSize * (Vector2)currentChain[i]; Vector2 prevVert = origin + tileSize * (Vector2)currentChain[prevIndex]; Vector2 nextVert = origin + tileSize * (Vector2)currentChain[nextIndex]; if (nextVert - currentVert != currentVert - prevVert) { if (!isLoop && (i == 0 || i == currentChain.Count - 1)) { vertexBuffer.Add(currentVert); } else { vertexBuffer.Add(currentVert + (prevVert - currentVert).Normalized * tileSize * 0.2f); vertexBuffer.Add(currentVert + (nextVert - currentVert).Normalized * tileSize * 0.2f); } } } } // Sharp corners else { vertexBuffer.Reserve(currentChain.Count); vertexBuffer.Count = 0; for (int i = 0; i < currentChain.Count; i++) { int prevIndex = (i - 1 + currentChain.Count) % currentChain.Count; int nextIndex = (i + 1) % currentChain.Count; Vector2 currentVert = origin + tileSize * (Vector2)currentChain[i]; Vector2 prevVert = origin + tileSize * (Vector2)currentChain[prevIndex]; Vector2 nextVert = origin + tileSize * (Vector2)currentChain[nextIndex]; if (nextVert - currentVert != currentVert - prevVert) { vertexBuffer.Add(currentVert); } } } Vector2[] vertices = new Vector2[vertexBuffer.Count]; vertexBuffer.CopyTo(vertices, 0); shapeList.Add(isLoop ? (ShapeInfo) new LoopShapeInfo(vertices) : (ShapeInfo) new ChainShapeInfo(vertices)); } }
private void UpdateComponents <T>(Action <T> updateAction) where T : class { Profile.TimeUpdateSceneComponents.BeginMeasure(); // Gather a list of updatable Components RawList <Component> updatableComponents = new RawList <Component>(256); RawList <UpdateEntry> updateMap = new RawList <UpdateEntry>(); foreach (var pair in this.componentsByType) { // Skip Component types that aren't updatable anyway Component sampleComponent = pair.Value.FirstOrDefault(); if (!(sampleComponent is T)) { continue; } int oldCount = updatableComponents.Count; // Collect Components updatableComponents.Reserve(updatableComponents.Count + pair.Value.Count); for (int i = 0; i < pair.Value.Count; i++) { updatableComponents.Add(pair.Value[i]); } // Keep in mind how many Components of each type we have in what order if (updatableComponents.Count - oldCount > 0) { updateMap.Add(new UpdateEntry { Type = pair.Key, Count = updatableComponents.Count - oldCount, Profiler = Profile.RequestCounter <TimeCounter>(Profile.TimeUpdateScene.FullName + @"\" + pair.Key.Name) }); } } // Update all Components. They're still sorted by type. { int updateMapIndex = -1; int updateMapBegin = -1; TimeCounter activeProfiler = null; Component[] data = updatableComponents.Data; UpdateEntry[] updateData = updateMap.Data; for (int i = 0; i < data.Length; i++) { if (i >= updatableComponents.Count) { break; } // Manage profilers per Component type if (i == 0 || i - updateMapBegin >= updateData[updateMapIndex].Count) { // Note: // Since we're doing this based on index-count ranges, this needs to be // done before skipping inactive Components, so we don't run out of sync. updateMapIndex++; updateMapBegin = i; if (activeProfiler != null) { activeProfiler.EndMeasure(); } activeProfiler = updateData[updateMapIndex].Profiler; activeProfiler.BeginMeasure(); } // Skip inactive, disposed and detached Components if (!data[i].Active) { continue; } // Invoke the Component's update action updateAction(data[i] as T); } if (activeProfiler != null) { activeProfiler.EndMeasure(); } } Profile.TimeUpdateSceneComponents.EndMeasure(); }
/// <summary> /// Draws the tile layer of the <see cref="TilesetView"/>. /// </summary> /// <param name="e"></param> protected virtual void OnPaintTiles(PaintEventArgs e) { Tileset tileset = this.tileset.Res; // Early-out if there are no tiles to draw if (tileset == null) { return; } if (this.totalTileCount == 0) { return; } // Determine which tiles are visible in the current viewport, so not all of them are drawn needlessly // Note: Not using clip rectangle here, because the multicolumn rendering algorithm assumes that // we always start at the beginning. int firstIndex = this.PickTileIndexAt(0, 0, true, true); int lastIndex = this.PickTileIndexAt(this.ClientSize.Width - 1, this.ClientSize.Height - 1, true, true); Point firstItemPos = this.GetTileIndexLocation(firstIndex); if (lastIndex < firstIndex) { return; } Size texSize = new Size( MathF.NextPowerOfTwo(this.tileBitmap.Width), MathF.NextPowerOfTwo(this.tileBitmap.Height)); // ToDo: Cleanup the below algorithm, it's hard to understand and has far too many // special cases. If performance doesn't suffer too much, maybe even do a 2D grid draw // and query tile indices using PickTileIndexAt? // Determine rendering data for all visible tile items paintTileBuffer.Count = 0; paintTileBuffer.Reserve(lastIndex - firstIndex); { Point basePos = firstItemPos; Point curPos = basePos; int itemsPerRow = (this.multiColumnMode == MultiColumnMode.Vertical) ? this.multiColumnLength : this.tileCount.X; int skipItemsPerRow = (firstIndex % itemsPerRow); int itemsPerRenderedRow = itemsPerRow - skipItemsPerRow; int itemsInCurrentRow = 0; for (int i = firstIndex; i <= lastIndex; i++) { Rectangle tileRect = new Rectangle(curPos.X, curPos.Y, this.displayedTileSize.Width, this.displayedTileSize.Height); // If the tile is actually visible, add the required data to the paint buffer if (e.ClipRectangle.IntersectsWith(tileRect)) { Point2 atlasTilePos; Point2 atlasTileSize; tileset.LookupTileSourceRect(this.displayedConfigIndex, i, out atlasTilePos, out atlasTileSize); paintTileBuffer.Count++; paintTileBuffer.Data[paintTileBuffer.Count - 1] = new TilesetViewPaintTileData { TileIndex = i, SourceRect = new Rectangle(atlasTilePos.X, atlasTilePos.Y, atlasTileSize.X, atlasTileSize.Y), ViewRect = tileRect }; } itemsInCurrentRow++; bool isLastIndexInRow = (itemsInCurrentRow == itemsPerRenderedRow); bool isLastIndexInHorizontalMultiColumn = (this.multiColumnMode == MultiColumnMode.Horizontal) && (i % (this.tileCount.X * this.multiColumnLength)) == (this.tileCount.X * this.multiColumnLength - 1); if (isLastIndexInHorizontalMultiColumn) { // Determine how many tiles we need to skip to the next horizontal multicolumn int baseIndexInRow = firstIndex % this.tileCount.X; int tilesToNextMultiColumn = this.tileCount.X - baseIndexInRow; // Switch to the next horizontal multicolumn itemsInCurrentRow = 0; basePos.X += tilesToNextMultiColumn * (this.displayedTileSize.Width + this.spacing.Width); curPos = basePos; i = this.PickTileIndexAt(curPos.X, curPos.Y, true, false); if (i == -1) { break; } // Recalculate regular rowskip values because we switched to the next horizontal multicolumn // If we previously rendered the last part of a row, this no longer applies. Since we are // now back at the beginning of a row, we don't skip any items prior and render full rows instead. // (Yes, this could be optimized, but there's no need right now) skipItemsPerRow = 0; itemsPerRenderedRow = itemsPerRow - skipItemsPerRow; i--; } else if (isLastIndexInRow) { curPos.X = basePos.X; curPos.Y += this.displayedTileSize.Height + this.spacing.Height; itemsInCurrentRow = 0; i += skipItemsPerRow; if (this.multiColumnMode == MultiColumnMode.Vertical) { i = this.PickTileIndexAt(curPos.X, curPos.Y, true, false); if (i == -1) { break; } // Recalculate regular rowskip values because we might have switched to the next // vertical multicolumn and might need to skip some tiles at the end because they // don't map to a valid model index int maxValidItemsInThisRow = this.tileCount.X - this.GetTilePos(i).X; skipItemsPerRow = itemsPerRow - Math.Min(this.multiColumnLength, maxValidItemsInThisRow); itemsPerRenderedRow = itemsPerRow - skipItemsPerRow; i--; } } else { curPos.X += this.displayedTileSize.Width + this.spacing.Width; } } } // Set the interpolation mode based on whether we're scaling up or down Vector2 scaleFactor = new Vector2(this.displayedTileSize.Width, this.displayedTileSize.Height) / tileset.TileSize; bool scalingUpClean = scaleFactor.X > 1.0f && scaleFactor.X == scaleFactor.Y && scaleFactor.X == (int)scaleFactor.X && scaleFactor.Y == (int)scaleFactor.Y; if (scalingUpClean) { e.Graphics.InterpolationMode = InterpolationMode.NearestNeighbor; } else { e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; } // Draw the previously determined visible tiles accordingly TilesetViewPaintTileData[] rawPaintData = paintTileBuffer.Data; int paintedTileCount = paintTileBuffer.Count; for (int i = 0; i < rawPaintData.Length; i++) { if (i >= paintedTileCount) { break; } // Adjust the image rect by half the scale factor in pixels, // because for some reason the nearest-neighbor-filtered image // might end up too small otherwise. Rectangle imageRect = rawPaintData[i].ViewRect; if (scalingUpClean) { imageRect.Width += MathF.RoundToInt(scaleFactor.X / 2.0f); imageRect.Height += MathF.RoundToInt(scaleFactor.Y / 2.0f); } e.Graphics.DrawImage( this.tileBitmap, imageRect, rawPaintData[i].SourceRect, GraphicsUnit.Pixel); } e.Graphics.InterpolationMode = InterpolationMode.Default; // Invoke the event handler if (this.PaintTiles != null) { this.PaintTiles(this, new TilesetViewPaintTilesEventArgs( e.Graphics, e.ClipRectangle, tileset, this.tileBitmap, paintTileBuffer)); } }
private static void GenerateCollisionShapes(TileEdgeMap edgeMap, Vector2 origin, Vector2 tileSize, bool roundedCorners, IList<ShapeInfo> shapeList) { // Traverse the edge map and gradually create chain / loop // shapes until all edges have been used. RawList<Point2> currentChain = new RawList<Point2>(); RawList<Vector2> vertexBuffer = new RawList<Vector2>(); while (true) { // Begin a new continuous chain of nodes currentChain.Clear(); // Find a starting node for our current chain. // If there is none, we found and handled all edges. Point2 start = edgeMap.FindNonEmpty(); if (start == new Point2(-1, -1)) break; // Traverse the current chain node-by-node from the start we found Point2 current = start; while (true) { // Add the current node to our continuous chain currentChain.Add(current); // Find the next node that connects to the current one. // If there is none, our current chain is done. Point2 next = edgeMap.GetClockwiseNextFrom(current); if (next == new Point2(-1, -1)) break; // Remove the edge we used to get to the next node edgeMap.RemoveEdge(current, next); // Use the next node as origin for traversing further current = next; } // Generate a shape from the current chain bool isLoop = (start == currentChain[currentChain.Count - 1]); if (isLoop) currentChain.RemoveAt(currentChain.Count - 1); vertexBuffer.Clear(); // Rounded corners if (roundedCorners && currentChain.Count >= 3) { vertexBuffer.Reserve(currentChain.Count * 2); vertexBuffer.Count = 0; for (int i = 0; i < currentChain.Count; i++) { int prevIndex = (i - 1 + currentChain.Count) % currentChain.Count; int nextIndex = (i + 1) % currentChain.Count; Vector2 currentVert = origin + tileSize * (Vector2)currentChain[i]; Vector2 prevVert = origin + tileSize * (Vector2)currentChain[prevIndex]; Vector2 nextVert = origin + tileSize * (Vector2)currentChain[nextIndex]; if (nextVert - currentVert != currentVert - prevVert) { if (!isLoop && (i == 0 || i == currentChain.Count - 1)) { vertexBuffer.Add(currentVert); } else { vertexBuffer.Add(currentVert + (prevVert - currentVert).Normalized * tileSize * 0.2f); vertexBuffer.Add(currentVert + (nextVert - currentVert).Normalized * tileSize * 0.2f); } } } } // Sharp corners else { vertexBuffer.Reserve(currentChain.Count); vertexBuffer.Count = 0; for (int i = 0; i < currentChain.Count; i++) { int prevIndex = (i - 1 + currentChain.Count) % currentChain.Count; int nextIndex = (i + 1) % currentChain.Count; Vector2 currentVert = origin + tileSize * (Vector2)currentChain[i]; Vector2 prevVert = origin + tileSize * (Vector2)currentChain[prevIndex]; Vector2 nextVert = origin + tileSize * (Vector2)currentChain[nextIndex]; if (nextVert - currentVert != currentVert - prevVert) vertexBuffer.Add(currentVert); } } shapeList.Add(isLoop ? (ShapeInfo)new LoopShapeInfo(vertexBuffer) : (ShapeInfo)new ChainShapeInfo(vertexBuffer)); } }