private void UpdateRigidBody(RigidBody body, int sectorX, int sectorY) { Sector sector = this.sectors[sectorX, sectorY]; // Determine collision checksum this.tempCollisionData.Clear(); int newChecksum = this.MergeCollisionData(sectorX, sectorY, this.tempCollisionData); // If it differs from our previous value, update collision shapes if (sector.Checksum != newChecksum) { // Clean up old shapes if (sector.Shapes != null) { foreach (ShapeInfo shape in sector.Shapes) { body.RemoveShape(shape); } sector.Shapes.Clear(); } else { sector.Shapes = new List <ShapeInfo>(); } // Generate new shapes { // Determine general working data Tilemap tilemap = this.referenceTilemap; Tileset tileset = tilemap != null ? tilemap.Tileset.Res : null; Vector2 tileSize = tileset != null ? tileset.TileSize : Tileset.DefaultTileSize; Point2 sectorBaseTile = new Point2( sectorX * SectorSize, sectorY * SectorSize); Vector2 sectorBasePos = sectorBaseTile * tileSize; // Clear the temporary edge map first this.tempEdgeMap.Clear(); // Populate the edge map with fence and block geometry AddFenceCollisionEdges(this.tempCollisionData, this.tempEdgeMap); AddBlockCollisionEdges(this.tempCollisionData, this.tempEdgeMap, sectorBaseTile, this.tileCount); if (this.solidOuterEdges) { AddBorderCollisionEdges(this.tempEdgeMap, sectorBaseTile, this.tileCount); } // Now traverse the edge map and gradually create chain / loop // shapes until all edges have been used. Rect localRect = Rect.Align(this.origin, 0, 0, this.tileCount.X * tileSize.X, this.tileCount.Y * tileSize.Y); GenerateCollisionShapes(this.tempEdgeMap, localRect.TopLeft + sectorBasePos, tileSize, this.roundedCorners, sector.Shapes); // Add all the generated shapes to the target body foreach (ShapeInfo shape in sector.Shapes) { body.AddShape(shape); shape.Friction = this.shapeFriction; shape.Restitution = this.shapeRestitution; } } sector.Checksum = newChecksum; } this.sectors[sectorX, sectorY] = sector; }
public override void Draw(IDrawDevice device) { // Determine basic working data Tilemap tilemap = this.ActiveTilemap; Tileset tileset = tilemap != null ? tilemap.Tileset.Res : null; Point2 tileCount = tilemap != null ? tilemap.Size : new Point2(1, 1); Vector2 tileSize = tileset != null ? tileset.TileSize : Tileset.DefaultTileSize; // Early-out, if insufficient if (tilemap == null) { return; } if (tileset == null) { return; } // Determine the total size and origin of the rendered Tilemap Vector2 renderTotalSize = tileCount * tileSize; Vector2 renderOrigin = Vector2.Zero; this.origin.ApplyTo(ref renderOrigin, ref renderTotalSize); MathF.TransformCoord(ref renderOrigin.X, ref renderOrigin.Y, this.GameObj.Transform.Angle, this.GameObj.Transform.Scale); // Determine Tile visibility TilemapCulling.TileInput cullingIn = new TilemapCulling.TileInput { // Remember: All these transform values are in world space TilemapPos = this.GameObj.Transform.Pos + new Vector3(renderOrigin), TilemapScale = this.GameObj.Transform.Scale, TilemapAngle = this.GameObj.Transform.Angle, TileCount = tileCount, TileSize = tileSize }; TilemapCulling.TileOutput cullingOut = TilemapCulling.GetVisibleTileRect(device, cullingIn); int renderedTileCount = cullingOut.VisibleTileCount.X * cullingOut.VisibleTileCount.Y; // Determine rendering parameters Material material = (tileset != null ? tileset.RenderMaterial : null) ?? Material.Checkerboard.Res; ColorRgba mainColor = this.colorTint; // Determine and adjust data for Z offset generation float depthPerTile = -cullingIn.TileSize.Y * cullingIn.TilemapScale * this.tileDepthScale; if (this.tileDepthMode == TileDepthOffsetMode.Flat) { depthPerTile = 0.0f; } float originDepthOffset = Rect.Align(this.origin, 0, 0, 0, tileCount.Y * depthPerTile).Y; if (this.tileDepthMode == TileDepthOffsetMode.World) { originDepthOffset += (this.GameObj.Transform.Pos.Y / (float)tileSize.Y) * depthPerTile; } float renderBaseOffset = this.offset + this.tileDepthOffset * depthPerTile + originDepthOffset; // Prepare vertex generation data Vector2 tileXStep = cullingOut.XAxisWorld * cullingIn.TileSize.X; Vector2 tileYStep = cullingOut.YAxisWorld * cullingIn.TileSize.Y; Vector3 renderPos = cullingOut.RenderOriginWorld; float renderOffset = renderBaseOffset; Point2 tileGridPos = cullingOut.VisibleTileStart; // Reserve the required space for vertex data in our locally cached buffer const int MaxVerticesPerBatch = 65532; if (this.vertices == null) { this.vertices = new RawList <VertexC1P3T2>(); } this.vertices.Count = Math.Min(renderedTileCount * 4, MaxVerticesPerBatch); VertexC1P3T2[] vertexData = this.vertices.Data; // Prepare vertex data array for batch-submitting IReadOnlyGrid <Tile> tiles = tilemap.Tiles; TileInfo[] tileData = tileset.TileData.Data; int submittedTileCount = 0; int submittedBatchCount = 0; int vertexBaseIndex = 0; for (int tileIndex = 0; tileIndex < renderedTileCount; tileIndex++) { Tile tile = tiles[tileGridPos.X, tileGridPos.Y]; if (tile.Index < tileData.Length) { Rect uvRect = tileData[tile.Index].TexCoord0; bool visualEmpty = tileData[tile.Index].IsVisuallyEmpty; int tileBaseOffset = tileData[tile.Index].DepthOffset; float localDepthOffset = (tile.DepthOffset + tileBaseOffset) * depthPerTile; if (!visualEmpty) { vertexData[vertexBaseIndex + 0].Pos.X = renderPos.X; vertexData[vertexBaseIndex + 0].Pos.Y = renderPos.Y; vertexData[vertexBaseIndex + 0].Pos.Z = renderPos.Z; vertexData[vertexBaseIndex + 0].DepthOffset = renderOffset + localDepthOffset; vertexData[vertexBaseIndex + 0].TexCoord.X = uvRect.X; vertexData[vertexBaseIndex + 0].TexCoord.Y = uvRect.Y; vertexData[vertexBaseIndex + 0].Color = mainColor; vertexData[vertexBaseIndex + 1].Pos.X = renderPos.X + tileYStep.X; vertexData[vertexBaseIndex + 1].Pos.Y = renderPos.Y + tileYStep.Y; vertexData[vertexBaseIndex + 1].Pos.Z = renderPos.Z; vertexData[vertexBaseIndex + 1].DepthOffset = renderOffset + localDepthOffset + depthPerTile; vertexData[vertexBaseIndex + 1].TexCoord.X = uvRect.X; vertexData[vertexBaseIndex + 1].TexCoord.Y = uvRect.Y + uvRect.H; vertexData[vertexBaseIndex + 1].Color = mainColor; vertexData[vertexBaseIndex + 2].Pos.X = renderPos.X + tileXStep.X + tileYStep.X; vertexData[vertexBaseIndex + 2].Pos.Y = renderPos.Y + tileXStep.Y + tileYStep.Y; vertexData[vertexBaseIndex + 2].Pos.Z = renderPos.Z; vertexData[vertexBaseIndex + 2].DepthOffset = renderOffset + localDepthOffset + depthPerTile; vertexData[vertexBaseIndex + 2].TexCoord.X = uvRect.X + uvRect.W; vertexData[vertexBaseIndex + 2].TexCoord.Y = uvRect.Y + uvRect.H; vertexData[vertexBaseIndex + 2].Color = mainColor; vertexData[vertexBaseIndex + 3].Pos.X = renderPos.X + tileXStep.X; vertexData[vertexBaseIndex + 3].Pos.Y = renderPos.Y + tileXStep.Y; vertexData[vertexBaseIndex + 3].Pos.Z = renderPos.Z; vertexData[vertexBaseIndex + 3].DepthOffset = renderOffset + localDepthOffset; vertexData[vertexBaseIndex + 3].TexCoord.X = uvRect.X + uvRect.W; vertexData[vertexBaseIndex + 3].TexCoord.Y = uvRect.Y; vertexData[vertexBaseIndex + 3].Color = mainColor; bool vertical = tileData[tile.Index].IsVertical; if (vertical) { vertexData[vertexBaseIndex + 0].DepthOffset += depthPerTile; vertexData[vertexBaseIndex + 3].DepthOffset += depthPerTile; } submittedTileCount++; vertexBaseIndex += 4; } } tileGridPos.X++; renderPos.X += tileXStep.X; renderPos.Y += tileXStep.Y; if ((tileGridPos.X - cullingOut.VisibleTileStart.X) >= cullingOut.VisibleTileCount.X) { tileGridPos.X = cullingOut.VisibleTileStart.X; tileGridPos.Y++; renderPos = cullingOut.RenderOriginWorld; renderPos.X += tileYStep.X * (tileGridPos.Y - cullingOut.VisibleTileStart.Y); renderPos.Y += tileYStep.Y * (tileGridPos.Y - cullingOut.VisibleTileStart.Y); renderOffset = renderBaseOffset + tileGridPos.Y * depthPerTile; } // If we reached the maximum number of vertices per batch, submit early and restart if (vertexBaseIndex >= MaxVerticesPerBatch) { device.AddVertices( material, VertexMode.Quads, vertexData, vertexBaseIndex); vertexBaseIndex = 0; submittedBatchCount++; } } // Submit the final batch will all remaining vertices if (vertexBaseIndex > 0) { device.AddVertices( material, VertexMode.Quads, vertexData, vertexBaseIndex); submittedBatchCount++; } Profile.AddToStat(@"Duality\Stats\Render\Tilemaps\NumTiles", renderedTileCount); Profile.AddToStat(@"Duality\Stats\Render\Tilemaps\NumVertices", submittedTileCount * 4); Profile.AddToStat(@"Duality\Stats\Render\Tilemaps\NumBatches", submittedBatchCount); }