Esempio n. 1
0
        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;
        }
Esempio n. 2
0
        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);
        }