Пример #1
0
        internal RoadCollection(RoadTopology topology, AssetLoadContext loadContext, HeightMap heightMap)
            : this()
        {
            // The map stores road segments with no connectivity:
            // - a segment is from point A to point B
            // - with a road type name
            // - and start and end curve types (angled, tight curve, broad curve).

            // The goal is to create road networks of connected road segments,
            // where a network has only a single road type.

            // A road network is composed of 2 or more nodes.
            // A network is a (potentially) cyclic graph.

            // A road node has > 1 and <= 4 edges connected to it.
            // A node can be part of multiple networks.

            // An edge can only exist in one network.

            // TODO: If a node stored in the map has > 4 edges, the extra edges
            // are put into a separate network.

            var networks = topology.BuildNetworks();

            foreach (var network in networks)
            {
                foreach (var edge in network.Edges)
                {
                    var startPosition = edge.Start.TopologyNode.Position;
                    var endPosition   = edge.End.TopologyNode.Position;

                    startPosition.Z += heightMap.GetHeight(startPosition.X, startPosition.Y);
                    endPosition.Z   += heightMap.GetHeight(endPosition.X, endPosition.Y);

                    _roads.Add(AddDisposable(new Road(
                                                 loadContext,
                                                 heightMap,
                                                 edge.TopologyEdge.Template,
                                                 startPosition,
                                                 endPosition)));
                }
            }
        }
Пример #2
0
        internal Road(
            ContentManager contentManager,
            HeightMap heightMap,
            RoadTemplate template,
            Vector3 startPosition,
            Vector3 endPosition)
        {
            const float heightBias = 1f;
            const float createNewVerticesHeightDeltaThreshold = 0.002f;

            var distance              = Vector3.Distance(startPosition, endPosition);
            var direction             = Vector3.Normalize(endPosition - startPosition);
            var centerToEdgeDirection = Vector3.Cross(Vector3.UnitZ, direction);
            var up = Vector3.Cross(direction, centerToEdgeDirection);

            var halfWidth = template.RoadWidth / 2;

            var textureAtlasSplit = 1 / 3f;

            var vertices = new List <RoadVertex>();

            // Step along road segment in units of 10. If the delta between
            // (a) the straight line from previous point to finish and
            // (b) the actual height of the terrain at this point
            // is > a threshold, create extra vertices.
            // TODO: I don't know if this is the right algorithm.

            void AddVertexPair(in Vector3 position, float distanceAlongRoad)
            {
                var u = distanceAlongRoad / 50;

                var p0 = position - centerToEdgeDirection * halfWidth;

                p0.Z += heightBias;

                vertices.Add(new RoadVertex
                {
                    Position = p0,
                    Normal   = up,
                    UV       = new Vector2(u, 0)
                });

                var p1 = position + centerToEdgeDirection * halfWidth;

                p1.Z += heightBias;

                vertices.Add(new RoadVertex
                {
                    Position = p1,
                    Normal   = up,
                    UV       = new Vector2(u, textureAtlasSplit)
                });
            }

            AddVertexPair(startPosition, 0);

            var previousPoint         = startPosition;
            var previousPointDistance = 0;

            for (var currentDistance = 10; currentDistance < distance; currentDistance += 10)
            {
                var position           = startPosition + direction * currentDistance;
                var actualHeight       = heightMap.GetHeight(position.X, position.Y);
                var interpolatedHeight = MathUtility.Lerp(previousPoint.Z, endPosition.Z, (currentDistance - previousPointDistance) / distance);

                if (Math.Abs(actualHeight - interpolatedHeight) > createNewVerticesHeightDeltaThreshold)
                {
                    AddVertexPair(position, currentDistance);
                    previousPoint         = position;
                    previousPointDistance = currentDistance;
                }
            }

            // Add last chunk.
            AddVertexPair(endPosition, distance);

            _boundingBox = BoundingBox.CreateFromPoints(vertices.Select(x => x.Position));

            _vertexBuffer = AddDisposable(contentManager.GraphicsDevice.CreateStaticBuffer(
                                              vertices.ToArray(),
                                              BufferUsage.VertexBuffer));

            var indices = new List <ushort>();

            for (var i = 0; i < vertices.Count - 2; i += 2)
            {
                indices.Add((ushort)(i + 0));
                indices.Add((ushort)(i + 1));
                indices.Add((ushort)(i + 2));

                indices.Add((ushort)(i + 1));
                indices.Add((ushort)(i + 2));
                indices.Add((ushort)(i + 3));
            }

            _numIndices = (uint)indices.Count;

            _indexBuffer = AddDisposable(contentManager.GraphicsDevice.CreateStaticBuffer(
                                             indices.ToArray(),
                                             BufferUsage.IndexBuffer));

            _material = AddDisposable(new RoadMaterial(
                                          contentManager,
                                          contentManager.EffectLibrary.Road));

            var texture = contentManager.Load <Texture>(Path.Combine("Art", "Textures", template.Texture));

            _material.SetTexture(texture);
        }