/// <inheritdoc/> internal override void OnDraw(GraphicsDevice graphicsDevice, Rectangle rectangle, Vector2F topLeftPosition, Vector2F bottomRightPosition) { if (Submesh != null) { Submesh.Draw(); } }
/// <summary> /// Sets the road mesh and related properties. /// </summary> /// <param name="submesh">The submesh that represents the road.</param> /// <param name="aabb">The axis-aligned bounding box of the mesh.</param> /// <param name="roadLength">The length of the road in world space units.</param> /// <param name="disposeWithRoadLayer"> /// <see langword="true" /> to automatically dispose of the mesh when the /// <see cref="TerrainRoadLayer"/> is disposed of; otherwise, <see langword="false"/>. /// </param> /// <remarks> /// <see cref="CreateMesh"/> can be used to create a suitable mesh. /// </remarks> public void SetMesh(Submesh submesh, Aabb aabb, float roadLength, bool disposeWithRoadLayer) { Submesh = submesh; Aabb = aabb; RoadLength = roadLength; _disposeMesh = disposeWithRoadLayer; }
public void GetMesh(Shape shape, out Submesh submesh, out Matrix matrix) { ThrowIfDisposed(); if (shape == null) throw new ArgumentNullException("shape"); var index = GetCacheIndex(shape); if (index >= 0) { // Found cache entry! var entry = _cache[index]; Debug.Assert(entry.ShapeWeak.Target == shape, "ShapeMeshCache.GetCacheIndex() returned wrong index."); // Get submesh from strong or weak reference. submesh = entry.Submesh ?? (Submesh)entry.SubmeshWeak.Target; matrix = entry.Matrix; if (submesh != null) { // Recreate submesh if number of triangles in TriangleMeshShape has changed. var triangleMeshShape = shape as TriangleMeshShape; if (triangleMeshShape != null && triangleMeshShape.Mesh.NumberOfTriangles != submesh.PrimitiveCount) { DisposeMesh(entry); submesh = null; } } // Recreate submesh if necessary. if (submesh == null) CreateMesh(shape, out submesh, out matrix); _cache[index].Submesh = submesh; _cache[index].Matrix = matrix; _cache[index].Age = 0; } else { // No cache entry found. // GetCacheIndex returns the bitwise complement of the next index. index = ~index; // No submesh in cache. CreateMesh(shape, out submesh, out matrix); var entry = new CacheEntry(shape) { HashCode = _tempEntry.HashCode, Age = 0, Submesh = submesh, Matrix = matrix, }; _cache.Insert(index, entry); // If shape changes, we must invalidate the cache entry: shape.Changed += OnCachedShapeChanged; } }
public static Submesh CreateCircleLines(GraphicsDevice graphicsDevice, int numberOfSegments) { if (graphicsDevice == null) { throw new ArgumentNullException("graphicsDevice"); } if (numberOfSegments < 3) { throw new ArgumentOutOfRangeException("numberOfSegments", "numberOfSegments must be greater than 2"); } var submesh = new Submesh { PrimitiveType = PrimitiveType.LineList, }; // Create vertices for a circle on the floor. var vertices = new Vector3[numberOfSegments]; for (int i = 0; i < numberOfSegments; i++) { float angle = i * ConstantsF.TwoPi / numberOfSegments; float x = (float)Math.Cos(angle); float y = (float)Math.Sin(angle); vertices[i] = new Vector3(x, y, 0); } submesh.VertexBuffer = new VertexBuffer( graphicsDevice, VertexPosition.VertexDeclaration, vertices.Length, BufferUsage.None); submesh.VertexBuffer.SetData(vertices); submesh.VertexCount = submesh.VertexBuffer.VertexCount; // Create indices for base circle. var indices = new ushort[2 * numberOfSegments]; for (int i = 0; i < numberOfSegments; i++) { indices[2 * i] = (ushort)i; indices[2 * i + 1] = (ushort)(i + 1); } // Correct last index to be 0 to close circle. indices[2 * numberOfSegments - 1] = 0; submesh.IndexBuffer = new IndexBuffer( graphicsDevice, IndexElementSize.SixteenBits, indices.Length, BufferUsage.None); submesh.IndexBuffer.SetData(indices); submesh.PrimitiveCount = indices.Length / 2; return(submesh); }
public static Submesh CreateIcosphere(GraphicsDevice graphicsDevice, int numberOfSubdivisions) { if (graphicsDevice == null) { throw new ArgumentNullException("graphicsDevice"); } if (numberOfSubdivisions < 1) { throw new ArgumentOutOfRangeException("numberOfSubdivisions", "numberOfSegments must be greater than 0"); } var submesh = new Submesh { PrimitiveType = PrimitiveType.TriangleList, }; var mesh = GeometryHelper.CreateIcosphere(numberOfSubdivisions, false); int numberOfVertices = mesh.Vertices.Count; var vertexData = new VertexPositionNormal[numberOfVertices]; for (int i = 0; i < numberOfVertices; i++) { Vector3 v = (Vector3)mesh.Vertices[i]; vertexData[i] = new VertexPositionNormal(v, v); } submesh.VertexBuffer = new VertexBuffer( graphicsDevice, VertexPositionNormal.VertexDeclaration, vertexData.Length, BufferUsage.None); submesh.VertexBuffer.SetData(vertexData); submesh.VertexCount = submesh.VertexBuffer.VertexCount; int numberOfTriangles = mesh.NumberOfTriangles; int numberOfIndices = mesh.Indices.Count; var indexData = new ushort[numberOfIndices]; for (int i = 0; i < numberOfTriangles; i++) { // Flip vertex order. (DigitalRune Geometry uses CCW, XNA uses CW.) indexData[3 * i + 0] = (ushort)mesh.Indices[3 * i + 0]; indexData[3 * i + 2] = (ushort)mesh.Indices[3 * i + 1]; indexData[3 * i + 1] = (ushort)mesh.Indices[3 * i + 2]; } submesh.IndexBuffer = new IndexBuffer( graphicsDevice, IndexElementSize.SixteenBits, indexData.Length, BufferUsage.None); submesh.IndexBuffer.SetData(indexData); submesh.PrimitiveCount = indexData.Length / 3; return(submesh); }
public static void SetMaterial(this Submesh submesh, Material material) { if (submesh == null) throw new ArgumentNullException("submesh"); var mesh = submesh.Mesh; if (mesh == null) throw new InvalidOperationException("Cannot add material to submesh. Submesh needs to be added to Mesh first."); if (material == null) throw new ArgumentNullException("material"); var oldMaterial = GetMaterial(submesh); if (oldMaterial == material) return; // Find out if the old or new material is used by other submeshes which belong // to the same mesh. bool oldMaterialStillInUse = false; bool newMaterialAlreadyInUse = false; foreach (var otherSubmesh in mesh.Submeshes) { if (otherSubmesh != submesh) { Material otherMaterial = GetMaterial(otherSubmesh); if (otherMaterial == oldMaterial) oldMaterialStillInUse = true; else if (otherMaterial == material) newMaterialAlreadyInUse = true; } } // Remove old material from parent mesh material collection if it is not used by // any submesh. int oldIndex = submesh.MaterialIndex; if (!oldMaterialStillInUse && oldMaterial != null) { mesh.Materials.RemoveAt(oldIndex); // One material was removed --> Update indices of meshes. foreach (var otherSubmesh in mesh.Submeshes) if (otherSubmesh.MaterialIndex > oldIndex) otherSubmesh.MaterialIndex--; } // Add new material to parent mesh material collection if this is the first submesh // that uses this material. if (newMaterialAlreadyInUse) { // Get index of the new material. submesh.MaterialIndex = mesh.Materials.IndexOf(material); } else { // Add new material to the mesh at the end of the material collection. mesh.Materials.Add(material); submesh.MaterialIndex = mesh.Materials.Count - 1; } }
public static Material GetMaterial(this Submesh submesh) { if (submesh == null) throw new ArgumentNullException("submesh"); var mesh = submesh.Mesh; if (mesh == null) return null; var index = submesh.MaterialIndex; if (0 <= index && index < mesh.Materials.Count) return mesh.Materials[index]; return null; }
public static void GetMesh(IGraphicsService graphicsService, Shape shape, out Submesh submesh, out Matrix matrix) { // Update submesh. var graphicsManager = graphicsService as GraphicsManager; if (graphicsManager != null) { graphicsManager.ShapeMeshCache.GetMesh(shape, out submesh, out matrix); } else { // This happens if the user has implemented his own graphics manager - // which is very unlikely. submesh = MeshHelper.CreateSubmesh(graphicsService.GraphicsDevice, shape.GetMesh(0.05f, 4), MathHelper.ToRadians(70)); matrix = Matrix.Identity; } }
public static void Draw(this Submesh submesh) { if (submesh == null) throw new ArgumentNullException("submesh"); Debug.Assert(!submesh.HasMorphTargets, "Submesh without morph targets expected."); var vertexBuffer = submesh.VertexBuffer; if (vertexBuffer == null || submesh.VertexCount <= 0) return; // VertexBuffer.GraphicsDevice is set to null when VertexBuffer is disposed of. // Check VertexBuffer.IsDisposed to avoid NullReferenceException. if (vertexBuffer.IsDisposed) throw new ObjectDisposedException("VertexBuffer", "Cannot draw mesh. The vertex buffer has already been disposed of."); var graphicsDevice = vertexBuffer.GraphicsDevice; graphicsDevice.SetVertexBuffer(vertexBuffer); var indexBuffer = submesh.IndexBuffer; if (indexBuffer == null) { graphicsDevice.DrawPrimitives( submesh.PrimitiveType, submesh.StartVertex, submesh.PrimitiveCount); } else { graphicsDevice.Indices = indexBuffer; graphicsDevice.DrawIndexedPrimitives( submesh.PrimitiveType, submesh.StartVertex, submesh.StartIndex, submesh.PrimitiveCount); #else graphicsDevice.DrawIndexedPrimitives( submesh.PrimitiveType, submesh.StartVertex, 0, submesh.VertexCount, submesh.StartIndex, submesh.PrimitiveCount); } }
/// <inheritdoc/> protected override void Dispose(bool disposing) { if (!IsDisposed) { if (disposing) { // Dispose managed resources. if (_disposeMesh) { Submesh.SafeDispose(); } } // Release unmanaged resources. } base.Dispose(disposing); }
public static Submesh CreateTeapot(GraphicsDevice graphicsDevice, float size, int tessellation) { if (graphicsDevice == null) { throw new ArgumentNullException("graphicsDevice"); } if (tessellation < 1) { throw new ArgumentOutOfRangeException("tessellation"); } var submesh = new Submesh { PrimitiveType = PrimitiveType.TriangleList, }; var teapot = new Teapot(size, tessellation); submesh.VertexBuffer = new VertexBuffer( graphicsDevice, VertexPositionNormal.VertexDeclaration, teapot.Vertices.Length, BufferUsage.None); submesh.VertexBuffer.SetData(teapot.Vertices); submesh.VertexCount = submesh.VertexBuffer.VertexCount; submesh.IndexBuffer = new IndexBuffer( graphicsDevice, IndexElementSize.SixteenBits, teapot.Indices.Length, BufferUsage.None); submesh.IndexBuffer.SetData(teapot.Indices); submesh.PrimitiveCount = teapot.Indices.Length / 3; return(submesh); }
private void CreateMesh(Shape shape, out Submesh submesh, out Matrix matrix) { // Use a special shared submesh for box shapes. var boxShape = shape as BoxShape; if (boxShape != null) { if (_boxSubmesh == null) _boxSubmesh = MeshHelper.GetBox(_graphicsService); submesh = _boxSubmesh; matrix = Matrix.CreateScale(boxShape.Extent); return; } var transformedShape = shape as TransformedShape; boxShape = (transformedShape != null) ? transformedShape.Child.Shape as BoxShape : null; if (boxShape != null) { if (_boxSubmesh == null) _boxSubmesh = MeshHelper.GetBox(_graphicsService); submesh = _boxSubmesh; matrix = transformedShape.Child.Pose * Matrix.CreateScale(transformedShape.Child.Scale * boxShape.Extent); return; } // Create the submesh. Return EmptySubmesh if the MeshHelper returns null. var newSubmesh = MeshHelper.CreateSubmesh( _graphicsService.GraphicsDevice, shape.GetMesh(MeshRelativeError, MeshIterationLimit), NormalAngleLimit); submesh = newSubmesh ?? EmptySubmesh; matrix = Matrix.Identity; }
public static Submesh CreateCone(GraphicsDevice graphicsDevice, int numberOfSegments) { if (graphicsDevice == null) { throw new ArgumentNullException("graphicsDevice"); } if (numberOfSegments < 3) { throw new ArgumentOutOfRangeException("numberOfSegments", "numberOfSegments must be greater than 2"); } var submesh = new Submesh { PrimitiveType = PrimitiveType.TriangleList, }; var vertices = new List <VertexPositionNormal>(); // Base Vector3 normal = -Vector3.UnitY; vertices.Add(new VertexPositionNormal(new Vector3(0, 0, 0), normal)); for (int i = 0; i < numberOfSegments; i++) { float angle = i * ConstantsF.TwoPi / numberOfSegments; float x = (float)Math.Cos(angle); float z = -(float)Math.Sin(angle); vertices.Add(new VertexPositionNormal(new Vector3(x, 0, z), normal)); } // Side vertices.Add(new VertexPositionNormal(new Vector3(0, 1, 0), new Vector3(0, 1, 0))); for (int i = 0; i < numberOfSegments; i++) { float angle = i * ConstantsF.TwoPi / numberOfSegments; const float cos45 = 0.707106781f; // cos(45°) float x = (float)Math.Cos(angle); float z = -(float)Math.Sin(angle); vertices.Add(new VertexPositionNormal(new Vector3(x, 0, z), new Vector3(x * cos45, cos45, z * cos45))); } submesh.VertexBuffer = new VertexBuffer( graphicsDevice, VertexPositionNormal.VertexDeclaration, vertices.Count, BufferUsage.None); submesh.VertexBuffer.SetData(vertices.ToArray()); submesh.VertexCount = submesh.VertexBuffer.VertexCount; var indices = new List <ushort>(); // Base for (int i = 0; i < numberOfSegments - 1; i++) { indices.Add(0); indices.Add((ushort)(i + 1)); indices.Add((ushort)(i + 2)); } // Last base triangle. indices.Add(0); indices.Add((ushort)numberOfSegments); indices.Add(1); // Side triangle. for (int i = 0; i < numberOfSegments - 1; i++) { indices.Add((ushort)(numberOfSegments + 1)); indices.Add((ushort)(numberOfSegments + i + 3)); indices.Add((ushort)(numberOfSegments + i + 2)); } // Last side triangle. indices.Add((ushort)(numberOfSegments + 1)); indices.Add((ushort)(numberOfSegments + 2)); indices.Add((ushort)(numberOfSegments + numberOfSegments + 1)); submesh.IndexBuffer = new IndexBuffer( graphicsDevice, IndexElementSize.SixteenBits, indices.Count, BufferUsage.None); submesh.IndexBuffer.SetData(indices.ToArray()); submesh.PrimitiveCount = indices.Count / 3; return(submesh); }
public static void ToTriangleMesh(this Submesh submesh, TriangleMesh triangleMesh) { // This method is similar to TriangleMesh.FromModel(). if (submesh == null) throw new ArgumentNullException("submesh"); if (triangleMesh == null) throw new ArgumentNullException("triangleMesh"); if (submesh.PrimitiveType != PrimitiveType.TriangleList) throw new NotSupportedException("All submeshes must be triangle lists. ToTriangleMesh() does not support other primitive types."); if (submesh.VertexBuffer == null) return; if (triangleMesh.Vertices == null) triangleMesh.Vertices = new List<Vector3>(submesh.VertexCount); if (triangleMesh.Indices == null) triangleMesh.Indices = new List<int>(submesh.PrimitiveCount * 3); // Get vertex element info. var vertexDeclaration = submesh.VertexBuffer.VertexDeclaration; var vertexElements = vertexDeclaration.GetVertexElements(); // Get the vertex positions. var positionElement = vertexElements.First(e => e.VertexElementUsage == VertexElementUsage.Position); if (positionElement.VertexElementFormat != VertexElementFormat.Vector3) throw new NotSupportedException("For vertex positions only VertexElementFormat.Vector3 is supported."); Vector3[] positions = new Vector3[submesh.VertexCount]; submesh.VertexBuffer.GetData( submesh.StartVertex * vertexDeclaration.VertexStride + positionElement.Offset, positions, 0, submesh.VertexCount, vertexDeclaration.VertexStride); if (submesh.IndexBuffer != null) { // Remember the number of vertices already in the mesh. int vertexCount = triangleMesh.Vertices.Count; // Add the vertices of the current modelMeshPart. foreach (Vector3 p in positions) triangleMesh.Vertices.Add((Vector3)p); // Get indices. int indexElementSize = (submesh.IndexBuffer.IndexElementSize == IndexElementSize.SixteenBits) ? 2 : 4; if (indexElementSize == 2) { ushort[] indices = new ushort[submesh.PrimitiveCount * 3]; submesh.IndexBuffer.GetData( submesh.StartIndex * 2, indices, 0, submesh.PrimitiveCount * 3); // Add indices to triangle mesh. for (int i = 0; i < submesh.PrimitiveCount; i++) { // The three indices of the next triangle. // We add 'vertexCount' because the triangleMesh already contains other mesh parts. int i0 = indices[i * 3 + 0] + vertexCount; int i1 = indices[i * 3 + 1] + vertexCount; int i2 = indices[i * 3 + 2] + vertexCount; triangleMesh.Indices.Add(i0); triangleMesh.Indices.Add(i2); // DigitalRune Geometry uses other winding order! triangleMesh.Indices.Add(i1); } } else { Debug.Assert(indexElementSize == 4); int[] indices = new int[submesh.PrimitiveCount * 3]; submesh.IndexBuffer.GetData( submesh.StartIndex * 4, indices, 0, submesh.PrimitiveCount * 3); // Add indices to triangle mesh. for (int i = 0; i < submesh.PrimitiveCount; i++) { // The three indices of the next triangle. // We add 'vertexCount' because the triangleMesh already contains other mesh parts. int i0 = indices[i * 3 + 0] + vertexCount; int i1 = indices[i * 3 + 1] + vertexCount; int i2 = indices[i * 3 + 2] + vertexCount; triangleMesh.Indices.Add(i0); triangleMesh.Indices.Add(i2); // DigitalRune Geometry uses other winding order! triangleMesh.Indices.Add(i1); } } } else { // No index buffer: int vertexCount = triangleMesh.Vertices.Count; for (int i = 0; i < submesh.VertexCount; i += 3) { triangleMesh.Vertices.Add((Vector3)positions[i]); triangleMesh.Vertices.Add((Vector3)positions[i + 1]); triangleMesh.Vertices.Add((Vector3)positions[i + 2]); triangleMesh.Indices.Add(i + vertexCount); triangleMesh.Indices.Add(i + 2 + vertexCount); // DigitalRune Geometry uses other winding order! triangleMesh.Indices.Add(i + 1 + vertexCount); } } }
public static TriangleMesh ToTriangleMesh(this Submesh submesh) { var triangleMesh = new TriangleMesh(); ToTriangleMesh(submesh, triangleMesh); return triangleMesh; }
private static void MergeSubmeshes(List<MergeJob> mergeJobs, Mesh mergedMesh, List<VertexDeclaration> vertexDeclarations, List<int> totalVertexCounts, int totalIndexCount) { var graphicsDevice = mergeJobs[0].Submesh.VertexBuffer.GraphicsDevice; VertexBuffer vertexBuffer = null; IndexBuffer indexBuffer = null; if (totalIndexCount > ushort.MaxValue) indexBuffer = new IndexBuffer(graphicsDevice, IndexElementSize.ThirtyTwoBits, totalIndexCount, BufferUsage.None); else if (totalIndexCount > 0) indexBuffer = new IndexBuffer(graphicsDevice, IndexElementSize.SixteenBits, totalIndexCount, BufferUsage.None); Submesh submesh = null; int vertexDeclarationIndex = -1; int vertexCount = 0; int indexCount = 0; uint sortKey = 0; for (int jobIndex = 0; jobIndex < mergeJobs.Count; jobIndex++) { var job = mergeJobs[jobIndex]; // As long as the sort key is equal we can merge into the current submesh. if (submesh != null && job.SortKey != sortKey) submesh = null; // We have to begin a new vertex buffer if the vertex declaration has changed. if (job.VertexDeclarationIndex != vertexDeclarationIndex) vertexBuffer = null; if (vertexBuffer == null) { vertexDeclarationIndex = job.VertexDeclarationIndex; vertexCount = 0; vertexBuffer = new VertexBuffer( graphicsDevice, vertexDeclarations[vertexDeclarationIndex], totalVertexCounts[vertexDeclarationIndex], BufferUsage.None); } if (submesh == null) { sortKey = job.SortKey; submesh = new Submesh { VertexBuffer = vertexBuffer, StartVertex = vertexCount, StartIndex = indexCount, PrimitiveType = job.Submesh.PrimitiveType, MaterialIndex = job.MergedMaterialIndex, IndexBuffer = (job.Submesh.IndexBuffer != null) ? indexBuffer : null, }; mergedMesh.Submeshes.Add(submesh); } // ----- Merge indices if (job.Submesh.IndexBuffer != null) { Debug.Assert(indexBuffer != null); int submeshIndexCount = GetNumberOfIndices(job.Submesh.PrimitiveType, job.Submesh.PrimitiveCount); if (job.Submesh.IndexBuffer.IndexElementSize == IndexElementSize.SixteenBits) { ushort[] indices16 = new ushort[submeshIndexCount]; job.Submesh.IndexBuffer.GetData(job.Submesh.StartIndex * 2, indices16, 0, submeshIndexCount); if (indexBuffer.IndexElementSize == IndexElementSize.SixteenBits) { for (int i = 0; i < indices16.Length; i++) indices16[i] = (ushort)(indices16[i] + submesh.VertexCount); indexBuffer.SetData(indexCount * 2, indices16, 0, indices16.Length); } else { uint[] indices32 = new uint[submeshIndexCount]; for (int i = 0; i < indices16.Length; i++) indices32[i] = (uint)(indices16[i] + submesh.VertexCount); indexBuffer.SetData(indexCount * 4, indices32, 0, indices32.Length); } } else { uint[] indices32 = new uint[submeshIndexCount]; job.Submesh.IndexBuffer.GetData(job.Submesh.StartIndex * 4, indices32, 0, submeshIndexCount); if (indexBuffer.IndexElementSize == IndexElementSize.SixteenBits) { ushort[] indices16 = new ushort[submeshIndexCount]; for (int i = 0; i < indices32.Length; i++) indices16[i] = (ushort)(indices32[i] + submesh.VertexCount); indexBuffer.SetData(indexCount * 2, indices16, 0, indices16.Length); } else { for (int i = 0; i < indices32.Length; i++) indices32[i] = (uint)(indices32[i] + submesh.VertexCount); indexBuffer.SetData(indexCount * 4, indices32, 0, indices32.Length); } } indexCount += submeshIndexCount; } // ----- Merge vertices var vertexDeclaration = vertexBuffer.VertexDeclaration; int vertexStride = vertexDeclaration.VertexStride; // Get the whole vertex buffer as byte array. byte[] buffer = new byte[job.Submesh.VertexBuffer.VertexCount * vertexStride]; job.Submesh.VertexBuffer.GetData(buffer); // Transform position and normals. TransformVertices(buffer, job.Submesh.StartVertex, job.Submesh.VertexCount, vertexDeclaration, job.Scale, job.Pose); vertexBuffer.SetData( vertexCount * vertexStride, buffer, job.Submesh.StartVertex * vertexStride, job.Submesh.VertexCount * vertexStride, 1); submesh.VertexCount += job.Submesh.VertexCount; vertexCount += job.Submesh.VertexCount; submesh.PrimitiveCount += job.Submesh.PrimitiveCount; } }
public static Submesh CreateTorus(GraphicsDevice graphicsDevice, float radius, float thickness, int numberOfSegments) { if (graphicsDevice == null) { throw new ArgumentNullException("graphicsDevice"); } if (numberOfSegments < 3) { throw new ArgumentOutOfRangeException("numberOfSegments", "numberOfSegments must be greater than 2"); } var submesh = new Submesh { PrimitiveType = PrimitiveType.TriangleList, }; var vertices = new List <VertexPositionNormal>(); var indices = new List <ushort>(); for (int i = 0; i < numberOfSegments; i++) { float outerAngle = i * MathHelper.TwoPi / numberOfSegments; // Create a transformation that will align geometry to slice perpendicularly // though the current ring position. Matrix transform = Matrix.CreateTranslation(radius, 0, 0) * Matrix.CreateRotationY(outerAngle); // Now loop along the other axis, around the side of the tube. for (int j = 0; j < numberOfSegments; j++) { float innerAngle = j * MathHelper.TwoPi / numberOfSegments; float dx = (float)Math.Cos(innerAngle); float dy = (float)Math.Sin(innerAngle); // Create a vertex. Vector3 normal = new Vector3(dx, dy, 0); Vector3 position = normal * thickness / 2.0f; position = Vector3.Transform(position, transform); normal = Vector3.TransformNormal(normal, transform); vertices.Add(new VertexPositionNormal(position, normal)); // And create indices for two triangles. int nextI = (i + 1) % numberOfSegments; int nextJ = (j + 1) % numberOfSegments; indices.Add((ushort)(i * numberOfSegments + j)); indices.Add((ushort)(i * numberOfSegments + nextJ)); indices.Add((ushort)(nextI * numberOfSegments + j)); indices.Add((ushort)(i * numberOfSegments + nextJ)); indices.Add((ushort)(nextI * numberOfSegments + nextJ)); indices.Add((ushort)(nextI * numberOfSegments + j)); } } submesh.VertexBuffer = new VertexBuffer( graphicsDevice, VertexPositionNormal.VertexDeclaration, vertices.Count, BufferUsage.None); submesh.VertexBuffer.SetData(vertices.ToArray()); submesh.VertexCount = submesh.VertexBuffer.VertexCount; submesh.IndexBuffer = new IndexBuffer( graphicsDevice, IndexElementSize.SixteenBits, indices.Count, BufferUsage.None); submesh.IndexBuffer.SetData(indices.ToArray()); submesh.PrimitiveCount = indices.Count / 3; return(submesh); }
public static Submesh CreateHemisphere(GraphicsDevice graphicsDevice, int numberOfSegments) { if (graphicsDevice == null) { throw new ArgumentNullException("graphicsDevice"); } if (numberOfSegments < 3) { throw new ArgumentOutOfRangeException("numberOfSegments", "numberOfSegments must be greater than 2"); } var submesh = new Submesh { PrimitiveType = PrimitiveType.TriangleList, }; // The number of rings. int numberOfRings = numberOfSegments / 4; int numberOfVertices = numberOfSegments * numberOfRings + 1; var vertices = new VertexPositionNormal[numberOfVertices]; // Create rings. float angle = ConstantsF.TwoPi / numberOfSegments; // Next free index in vertices. int i = 0; // Top vertex. vertices[i++] = new VertexPositionNormal(new Vector3(0, 1, 0), new Vector3(0, 1, 0)); // Compute vertices for rings from pole to equator and from the x-axis in // counterclockwise direction (when viewed from top). for (int ring = 0; ring < numberOfRings; ring++) { float upAngle = angle * (ring + 1); float y = (float)Math.Cos(upAngle); float ringRadius = (float)Math.Sin(upAngle); for (int segment = 0; segment < numberOfSegments; segment++) { float x = ringRadius * (float)Math.Cos(angle * segment); float z = ringRadius * (float)Math.Sin(angle * segment); vertices[i++] = new VertexPositionNormal(new Vector3(x, y, z), new Vector3(x, y, z)); } } Debug.Assert(i == numberOfVertices); submesh.VertexBuffer = new VertexBuffer( graphicsDevice, VertexPositionNormal.VertexDeclaration, vertices.Length, BufferUsage.None); submesh.VertexBuffer.SetData(vertices); submesh.VertexCount = submesh.VertexBuffer.VertexCount; // Build array of indices. int numberOfTriangles = numberOfSegments // Triangles in top cap. + numberOfSegments * 2 * (numberOfRings - 1); int numberOfIndices = 3 * numberOfTriangles; var indices = new ushort[numberOfIndices]; i = 0; // Indices for top cap. for (int segment = 0; segment < numberOfSegments; segment++) { indices[i++] = 0; indices[i++] = (ushort)(segment + 1); if (segment + 1 < numberOfSegments) { indices[i++] = (ushort)(segment + 2); } else { indices[i++] = 1; // Wrap around to first vertex of the first ring. } } // Indices for rings between the caps. for (int ring = 1; ring < numberOfRings; ring++) { for (int segment = 0; segment < numberOfSegments; segment++) { // Each segment has 2 triangles. if (segment + 1 < numberOfSegments) { indices[i++] = (ushort)(1 + (ring - 1) * numberOfSegments + segment); indices[i++] = (ushort)(1 + ring * numberOfSegments + segment); indices[i++] = (ushort)(1 + ring * numberOfSegments + segment + 1); indices[i++] = (ushort)(1 + ring * numberOfSegments + segment + 1); indices[i++] = (ushort)(1 + (ring - 1) * numberOfSegments + segment + 1); indices[i++] = (ushort)(1 + (ring - 1) * numberOfSegments + segment); } else { // Handle wrap around. indices[i++] = (ushort)(1 + (ring - 1) * numberOfSegments + segment); indices[i++] = (ushort)(1 + ring * numberOfSegments + segment); indices[i++] = (ushort)(1 + ring * numberOfSegments); indices[i++] = (ushort)(1 + ring * numberOfSegments); indices[i++] = (ushort)(1 + (ring - 1) * numberOfSegments); indices[i++] = (ushort)(1 + (ring - 1) * numberOfSegments + segment); } } } Debug.Assert(i == numberOfIndices); submesh.IndexBuffer = new IndexBuffer( graphicsDevice, IndexElementSize.SixteenBits, indices.Length, BufferUsage.None); submesh.IndexBuffer.SetData(indices); submesh.PrimitiveCount = indices.Length / 3; return(submesh); }
public static Submesh CreateBox(GraphicsDevice graphicsDevice) { if (graphicsDevice == null) { throw new ArgumentNullException("graphicsDevice"); } var submesh = new Submesh { PrimitiveType = PrimitiveType.TriangleList, }; var vertices = new List <VertexPositionNormal>(); var indices = new List <ushort>(); var p0 = new Vector3(-0.5f, -0.5f, -0.5f); var p1 = new Vector3(-0.5f, -0.5f, +0.5f); var p2 = new Vector3(-0.5f, +0.5f, -0.5f); var p3 = new Vector3(-0.5f, +0.5f, +0.5f); var p4 = new Vector3(+0.5f, -0.5f, -0.5f); var p5 = new Vector3(+0.5f, -0.5f, +0.5f); var p6 = new Vector3(+0.5f, +0.5f, -0.5f); var p7 = new Vector3(+0.5f, +0.5f, +0.5f); var normal = Vector3.UnitX; vertices.Add(new VertexPositionNormal(p4, normal)); vertices.Add(new VertexPositionNormal(p5, normal)); vertices.Add(new VertexPositionNormal(p6, normal)); vertices.Add(new VertexPositionNormal(p7, normal)); indices.Add(0); indices.Add(1); indices.Add(2); indices.Add(1); indices.Add(3); indices.Add(2); normal = Vector3.UnitY; vertices.Add(new VertexPositionNormal(p6, normal)); vertices.Add(new VertexPositionNormal(p7, normal)); vertices.Add(new VertexPositionNormal(p2, normal)); vertices.Add(new VertexPositionNormal(p3, normal)); indices.Add(4); indices.Add(5); indices.Add(6); indices.Add(5); indices.Add(7); indices.Add(6); normal = Vector3.UnitZ; vertices.Add(new VertexPositionNormal(p5, normal)); vertices.Add(new VertexPositionNormal(p1, normal)); vertices.Add(new VertexPositionNormal(p7, normal)); vertices.Add(new VertexPositionNormal(p3, normal)); indices.Add(8); indices.Add(9); indices.Add(10); indices.Add(9); indices.Add(11); indices.Add(10); normal = -Vector3.UnitX; vertices.Add(new VertexPositionNormal(p1, normal)); vertices.Add(new VertexPositionNormal(p0, normal)); vertices.Add(new VertexPositionNormal(p3, normal)); vertices.Add(new VertexPositionNormal(p2, normal)); indices.Add(12); indices.Add(13); indices.Add(14); indices.Add(13); indices.Add(15); indices.Add(14); normal = -Vector3.UnitY; vertices.Add(new VertexPositionNormal(p4, normal)); vertices.Add(new VertexPositionNormal(p0, normal)); vertices.Add(new VertexPositionNormal(p5, normal)); vertices.Add(new VertexPositionNormal(p1, normal)); indices.Add(16); indices.Add(17); indices.Add(18); indices.Add(17); indices.Add(19); indices.Add(18); normal = -Vector3.UnitZ; vertices.Add(new VertexPositionNormal(p0, normal)); vertices.Add(new VertexPositionNormal(p4, normal)); vertices.Add(new VertexPositionNormal(p2, normal)); vertices.Add(new VertexPositionNormal(p6, normal)); indices.Add(20); indices.Add(21); indices.Add(22); indices.Add(21); indices.Add(23); indices.Add(22); submesh.VertexBuffer = new VertexBuffer( graphicsDevice, VertexPositionNormal.VertexDeclaration, vertices.Count, BufferUsage.None); submesh.VertexBuffer.SetData(vertices.ToArray()); submesh.VertexCount = submesh.VertexBuffer.VertexCount; submesh.IndexBuffer = new IndexBuffer( graphicsDevice, IndexElementSize.SixteenBits, indices.Count, BufferUsage.None); submesh.IndexBuffer.SetData(indices.ToArray()); submesh.PrimitiveCount = indices.Count / 3; return(submesh); }
public static Submesh CreateCylinderLines(GraphicsDevice graphicsDevice, int numberOfSegments) { if (graphicsDevice == null) { throw new ArgumentNullException("graphicsDevice"); } if (numberOfSegments < 3) { throw new ArgumentOutOfRangeException("numberOfSegments", "numberOfSegments must be greater than 2"); } var submesh = new Submesh { PrimitiveType = PrimitiveType.LineList, }; var vertices = new List <Vector3>(); // Top circle. for (int i = 0; i < numberOfSegments; i++) { float angle = i * ConstantsF.TwoPi / numberOfSegments; vertices.Add(new Vector3((float)Math.Cos(angle), 1, -(float)Math.Sin(angle))); } // Bottom circle. for (int i = 0; i < numberOfSegments; i++) { Vector3 p = vertices[i]; vertices.Add(new Vector3(p.X, -1, p.Z)); } submesh.VertexBuffer = new VertexBuffer( graphicsDevice, VertexPosition.VertexDeclaration, vertices.Count, BufferUsage.None); submesh.VertexBuffer.SetData(vertices.ToArray()); submesh.VertexCount = submesh.VertexBuffer.VertexCount; var indices = new List <ushort>(); // Top circle. for (int i = 0; i < numberOfSegments - 1; i++) { indices.Add((ushort)i); // Line start (= same as previous line end) indices.Add((ushort)(i + 1)); // Line end } // Last line of top circle. indices.Add((ushort)(numberOfSegments - 1)); indices.Add(0); // Bottom circle. for (int i = 0; i < numberOfSegments - 1; i++) { indices.Add((ushort)(numberOfSegments + i)); // Line start (= same as previous line end) indices.Add((ushort)(numberOfSegments + i + 1)); // Line end } // Last line of bottom circle. indices.Add((ushort)(numberOfSegments + numberOfSegments - 1)); indices.Add((ushort)(numberOfSegments)); // Side (represented by 4 lines). for (int i = 0; i < 4; i++) { indices.Add((ushort)(i * numberOfSegments / 4)); indices.Add((ushort)(numberOfSegments + i * numberOfSegments / 4)); } submesh.IndexBuffer = new IndexBuffer( graphicsDevice, IndexElementSize.SixteenBits, indices.Count, BufferUsage.None); submesh.IndexBuffer.SetData(indices.ToArray()); submesh.PrimitiveCount = indices.Count / 2; return(submesh); }
public static Submesh CreateUncappedCylinder(GraphicsDevice graphicsDevice, int numberOfSegments) { if (graphicsDevice == null) { throw new ArgumentNullException("graphicsDevice"); } if (numberOfSegments < 3) { throw new ArgumentOutOfRangeException("numberOfSegments", "numberOfSegments must be greater than 2"); } var submesh = new Submesh { PrimitiveType = PrimitiveType.TriangleList, }; // ----- Vertices var vertices = new List <VertexPositionNormal>(); for (int i = 0; i < numberOfSegments; i++) { float angle = i * ConstantsF.TwoPi / numberOfSegments; float x = (float)Math.Cos(angle); float z = -(float)Math.Sin(angle); vertices.Add(new VertexPositionNormal(new Vector3(x, 1, z), new Vector3(x, 0, z))); vertices.Add(new VertexPositionNormal(new Vector3(x, -1, z), new Vector3(x, 0, z))); } submesh.VertexBuffer = new VertexBuffer( graphicsDevice, VertexPositionNormal.VertexDeclaration, vertices.Count, BufferUsage.None); submesh.VertexBuffer.SetData(vertices.ToArray()); submesh.VertexCount = submesh.VertexBuffer.VertexCount; // ----- Indices var indices = new List <ushort>(); for (int i = 0; i < numberOfSegments - 1; i++) { indices.Add((ushort)(2 * i)); indices.Add((ushort)(2 * i + 2)); indices.Add((ushort)(2 * i + 1)); indices.Add((ushort)(2 * i + 2)); indices.Add((ushort)(2 * i + 3)); indices.Add((ushort)(2 * i + 1)); } // Indices of last 2 triangle. indices.Add((ushort)(numberOfSegments + numberOfSegments - 2)); indices.Add(0); indices.Add((ushort)(numberOfSegments + numberOfSegments - 1)); indices.Add(0); indices.Add(1); indices.Add((ushort)(numberOfSegments + numberOfSegments - 1)); submesh.IndexBuffer = new IndexBuffer( graphicsDevice, IndexElementSize.SixteenBits, indices.Count, BufferUsage.None); submesh.IndexBuffer.SetData(indices.ToArray()); submesh.PrimitiveCount = indices.Count / 3; return(submesh); }
internal static Submesh CreateGrid(GraphicsDevice graphicsDevice, float widthX, float widthY, int numberOfCellsX, int numberOfCellsY) { if (graphicsDevice == null) { throw new ArgumentNullException("graphicsDevice"); } if (widthX <= 0) { throw new ArgumentOutOfRangeException("widthX", "The number of cells must be greater than 0."); } if (widthY <= 0) { throw new ArgumentOutOfRangeException("widthY", "The number of cells must be greater than 0."); } if (numberOfCellsX < 1) { throw new ArgumentOutOfRangeException("numberOfCellsX", "The number of cells must be greater than 0."); } if (numberOfCellsY < 1) { throw new ArgumentOutOfRangeException("numberOfCellsY", "The number of cells must be greater than 0."); } var submesh = new Submesh { PrimitiveType = PrimitiveType.TriangleList, }; int numberOfVertices = (numberOfCellsX + 1) * (numberOfCellsY + 1); var vertices = new VertexPositionNormalTexture[numberOfVertices]; int index = 0; for (int y = 0; y <= numberOfCellsY; y++) { for (int x = 0; x <= numberOfCellsX; x++) { vertices[index++] = new VertexPositionNormalTexture( new Vector3(x * widthX / numberOfCellsX, y * widthY / numberOfCellsY, 0), new Vector3(0, 0, 1), new Vector2(x / (float)numberOfCellsX, y / (float)numberOfCellsY)); } } submesh.VertexBuffer = new VertexBuffer( graphicsDevice, VertexPositionNormalTexture.VertexDeclaration, vertices.Length, BufferUsage.None); submesh.VertexBuffer.SetData(vertices); submesh.VertexCount = submesh.VertexBuffer.VertexCount; // Build array of indices. int numberOfTriangles = numberOfCellsX * numberOfCellsY * 2; int numberOfIndices = 3 * numberOfTriangles; if (numberOfIndices < ushort.MaxValue) { var indices = new ushort[numberOfIndices]; index = 0; for (int y = 0; y < numberOfCellsY; y++) { for (int x = 0; x < numberOfCellsX; x++) { int baseIndex = y * (numberOfCellsX + 1) + x; indices[index++] = (ushort)baseIndex; indices[index++] = (ushort)(baseIndex + numberOfCellsX + 1); indices[index++] = (ushort)(baseIndex + 1); indices[index++] = (ushort)(baseIndex + 1); indices[index++] = (ushort)(baseIndex + numberOfCellsX + 1); indices[index++] = (ushort)(baseIndex + numberOfCellsX + 2); } } submesh.IndexBuffer = new IndexBuffer( graphicsDevice, IndexElementSize.SixteenBits, indices.Length, BufferUsage.None); submesh.IndexBuffer.SetData(indices); submesh.PrimitiveCount = indices.Length / 3; } else { var indices = new int[numberOfIndices]; index = 0; for (int y = 0; y < numberOfCellsY; y++) { for (int x = 0; x < numberOfCellsX; x++) { int baseIndex = y * (numberOfCellsX + 1) + x; indices[index++] = baseIndex; indices[index++] = (baseIndex + numberOfCellsX + 1); indices[index++] = (baseIndex + 1); indices[index++] = (baseIndex + 1); indices[index++] = (baseIndex + numberOfCellsX + 1); indices[index++] = (baseIndex + numberOfCellsX + 2); } } submesh.IndexBuffer = new IndexBuffer( graphicsDevice, IndexElementSize.ThirtyTwoBits, indices.Length, BufferUsage.None); submesh.IndexBuffer.SetData(indices); submesh.PrimitiveCount = indices.Length / 3; } return(submesh); }
public static Submesh CreateHemisphereLines(GraphicsDevice graphicsDevice, int numberOfSegments) { if (graphicsDevice == null) { throw new ArgumentNullException("graphicsDevice"); } if (numberOfSegments < 3) { throw new ArgumentOutOfRangeException("numberOfSegments", "numberOfSegments must be greater than 2"); } var submesh = new Submesh { PrimitiveType = PrimitiveType.LineList, }; // Create vertices for a circle on the floor. var vertices = new List <Vector3>(); for (int i = 0; i < numberOfSegments; i++) { float angle = i * ConstantsF.TwoPi / numberOfSegments; vertices.Add(new Vector3((float)Math.Cos(angle), 0, -(float)Math.Sin(angle))); } // Top vertex of the sphere. var topVertexIndex = vertices.Count; vertices.Add(new Vector3(0, 1, 0)); // 4 quarter arcs. Each arc starts at the base circle and ends at the top vertex. We already // have the first and last vertex. // Arc from +x to top. int firstArcIndex = vertices.Count; for (int i = 0; i < numberOfSegments / 4 - 1; i++) { float angle = (i + 1) * ConstantsF.TwoPi / numberOfSegments; vertices.Add(new Vector3((float)Math.Cos(angle), (float)Math.Sin(angle), 0)); } // Arc from -z to top. (Copy from first arc.) int secondArcIndex = vertices.Count; for (int i = 0; i < numberOfSegments / 4 - 1; i++) { Vector3 p = vertices[firstArcIndex + i]; vertices.Add(new Vector3(0, p.Y, -p.X)); } // Arc from -x to top. (Copy from first arc.) int thirdArcIndex = vertices.Count; for (int i = 0; i < numberOfSegments / 4 - 1; i++) { Vector3 p = vertices[firstArcIndex + i]; vertices.Add(new Vector3(-p.X, p.Y, 0)); } // Arc from +z to top. (Copy from first arc.) int fourthArcIndex = vertices.Count; for (int i = 0; i < numberOfSegments / 4 - 1; i++) { Vector3 p = vertices[firstArcIndex + i]; vertices.Add(new Vector3(0, p.Y, p.X)); } submesh.VertexBuffer = new VertexBuffer( graphicsDevice, VertexPosition.VertexDeclaration, vertices.Count, BufferUsage.None); submesh.VertexBuffer.SetData(vertices.ToArray()); submesh.VertexCount = submesh.VertexBuffer.VertexCount; var indices = new List <ushort>(); // Create indices for base circle. for (int i = 0; i < numberOfSegments; i++) { indices.Add((ushort)i); // Line start (= same as previous line end) indices.Add((ushort)(i + 1)); // Line end } // Correct last index to be 0 to close circle. indices[(ushort)(2 * numberOfSegments - 1)] = 0; // Indices for first arc. indices.Add(0); // Line start for (int i = 0; i < numberOfSegments / 4 - 1; i++) { indices.Add((ushort)(firstArcIndex + i)); // Line end indices.Add((ushort)(firstArcIndex + i)); // Line start (= same as previous line end) } indices.Add((ushort)topVertexIndex); // Line end // Next arcs indices.Add((ushort)(numberOfSegments / 4)); for (int i = 0; i < numberOfSegments / 4 - 1; i++) { indices.Add((ushort)(secondArcIndex + i)); indices.Add((ushort)(secondArcIndex + i)); } indices.Add((ushort)topVertexIndex); indices.Add((ushort)(2 * numberOfSegments / 4)); for (int i = 0; i < numberOfSegments / 4 - 1; i++) { indices.Add((ushort)(thirdArcIndex + i)); indices.Add((ushort)(thirdArcIndex + i)); } indices.Add((ushort)topVertexIndex); indices.Add((ushort)(3 * numberOfSegments / 4)); for (int i = 0; i < numberOfSegments / 4 - 1; i++) { indices.Add((ushort)(fourthArcIndex + i)); indices.Add((ushort)(fourthArcIndex + i)); } indices.Add((ushort)topVertexIndex); submesh.IndexBuffer = new IndexBuffer( graphicsDevice, IndexElementSize.SixteenBits, indices.Count, BufferUsage.None); submesh.IndexBuffer.SetData(indices.ToArray()); submesh.PrimitiveCount = indices.Count / 2; return(submesh); }
public static Submesh CreateBoxLines(GraphicsDevice graphicsDevice) { if (graphicsDevice == null) { throw new ArgumentNullException("graphicsDevice"); } var submesh = new Submesh { PrimitiveType = PrimitiveType.LineList, }; var vertices = new[] { new Vector3(-0.5f, -0.5f, +0.5f), new Vector3(+0.5f, -0.5f, +0.5f), new Vector3(+0.5f, +0.5f, +0.5f), new Vector3(-0.5f, +0.5f, +0.5f), new Vector3(-0.5f, -0.5f, -0.5f), new Vector3(+0.5f, -0.5f, -0.5f), new Vector3(+0.5f, +0.5f, -0.5f), new Vector3(-0.5f, +0.5f, -0.5f) }; submesh.VertexBuffer = new VertexBuffer( graphicsDevice, VertexPosition.VertexDeclaration, vertices.Length, BufferUsage.None); submesh.VertexBuffer.SetData(vertices); submesh.VertexCount = submesh.VertexBuffer.VertexCount; var indices = new ushort[] { 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 }; submesh.IndexBuffer = new IndexBuffer( graphicsDevice, IndexElementSize.SixteenBits, indices.Length, BufferUsage.None); submesh.IndexBuffer.SetData(indices); submesh.PrimitiveCount = indices.Length / 2; return(submesh); }
public static void CreateMesh(GraphicsDevice graphicsDevice, Path3F path, float defaultWidth, int maxNumberOfIterations, float tolerance, out Submesh submesh, out Aabb aabb, out float roadLength) { if (graphicsDevice == null) { throw new ArgumentNullException("graphicsDevice"); } if (path == null) { throw new ArgumentNullException("path"); } // Compute list of line segments. (2 points per line segment!) var flattenedPoints = new List <Vector3>(); path.Flatten(flattenedPoints, maxNumberOfIterations, tolerance); // Abort if path is empty. int numberOfLineSegments = flattenedPoints.Count / 2; if (numberOfLineSegments <= 0) { submesh = null; aabb = new Aabb(); roadLength = 0; return; } // Compute accumulated lengths. (One entry for each entry in flattenedPoints.) float[] accumulatedLengths = new float[flattenedPoints.Count]; accumulatedLengths[0] = 0; for (int i = 1; i < flattenedPoints.Count; i += 2) { Vector3 previous = flattenedPoints[i - 1]; Vector3 current = flattenedPoints[i]; float length = (current - previous).Length; accumulatedLengths[i] = accumulatedLengths[i - 1] + length; if (i + 1 < flattenedPoints.Count) { accumulatedLengths[i + 1] = accumulatedLengths[i]; } } // Total road length. roadLength = accumulatedLengths[accumulatedLengths.Length - 1]; // Create a mapping between accumulatedLength and the path key widths. // (accumulatedLength --> TerrainRoadPathKey.Width) var widthKeys = new List <Pair <float, float> >(); { int index = 0; foreach (var key in path) { Vector3 position = key.Point; var roadKey = key as TerrainRoadPathKey; float width = (roadKey != null) ? roadKey.Width : defaultWidth; for (; index < flattenedPoints.Count; index++) { if (Vector3.AreNumericallyEqual(position, flattenedPoints[index])) { widthKeys.Add(new Pair <float, float>(accumulatedLengths[index], width)); break; } bool isLastLineSegment = (index + 2 == flattenedPoints.Count); if (!isLastLineSegment) { index++; } } index++; } } // Create a list of interpolated road widths. (One entry for each entry in flattenedPoints.) var widths = new float[flattenedPoints.Count]; int previousKeyIndex = 0; var previousKey = widthKeys[0]; var nextKey = widthKeys[1]; widths[0] = widthKeys[0].Second; for (int i = 1; i < flattenedPoints.Count; i += 2) { if (accumulatedLengths[i] > nextKey.First) { previousKey = nextKey; previousKeyIndex++; nextKey = widthKeys[previousKeyIndex + 1]; } float p = (accumulatedLengths[i] - previousKey.First) / (nextKey.First - previousKey.First); widths[i] = InterpolationHelper.Lerp(previousKey.Second, nextKey.Second, p); if (i + 1 < flattenedPoints.Count) { widths[i + 1] = widths[i]; } } // Compute vertices and indices. var vertices = new List <TerrainLayerVertex>(numberOfLineSegments * 2 + 2); var indices = new List <int>(numberOfLineSegments * 6); // Two triangles per line segment. Vector3 lastOrthonormal = Vector3.UnitX; aabb = new Aabb(flattenedPoints[0], flattenedPoints[0]); bool isClosed = Vector3.AreNumericallyEqual(flattenedPoints[0], flattenedPoints[flattenedPoints.Count - 1]); for (int i = 0; i < flattenedPoints.Count; i++) { Vector3 start = flattenedPoints[i]; Vector3 previous; bool isFirstPoint = (i == 0); if (!isFirstPoint) { previous = flattenedPoints[i - 1]; } else if (isClosed && path.SmoothEnds) { previous = flattenedPoints[flattenedPoints.Count - 2]; } else { previous = start; } Vector3 next; bool isLastPoint = (i + 1 == flattenedPoints.Count); if (!isLastPoint) { next = flattenedPoints[i + 1]; } else if (isClosed && path.SmoothEnds) { next = flattenedPoints[1]; } else { next = start; } Vector3 tangent = next - previous; Vector3 orthonormal = new Vector3(tangent.Z, 0, -tangent.X); if (!orthonormal.TryNormalize()) { orthonormal = lastOrthonormal; } // Add indices to add two triangles between the current and the next vertices. if (!isLastPoint) { int baseIndex = vertices.Count; // 2 3 // x----x // |\ | ^ // | \ | | road // | \ | | direction // | \| | // x----x // 0 1 indices.Add(baseIndex); indices.Add(baseIndex + 1); indices.Add(baseIndex + 2); indices.Add(baseIndex + 1); indices.Add(baseIndex + 3); indices.Add(baseIndex + 2); } // Add two vertices. Vector3 leftVertex = start - orthonormal * (widths[i] / 2); Vector3 rightVertex = start + orthonormal * (widths[i] / 2); vertices.Add(new TerrainLayerVertex(new Vector2(leftVertex.X, leftVertex.Z), new Vector2(0, accumulatedLengths[i]))); vertices.Add(new TerrainLayerVertex(new Vector2(rightVertex.X, rightVertex.Z), new Vector2(1, accumulatedLengths[i]))); // Grow AABB aabb.Grow(leftVertex); aabb.Grow(rightVertex); lastOrthonormal = orthonormal; // The flattened points list contains 2 points per line segment, which means that there // are duplicate intermediate points, which we skip. bool isLastLineSegment = (i + 2 == flattenedPoints.Count); if (!isLastLineSegment) { i++; } } Debug.Assert(vertices.Count == (numberOfLineSegments * 2 + 2)); Debug.Assert(indices.Count == (numberOfLineSegments * 6)); // The road is projected onto the terrain, therefore the computed y limits are not correct. // (unless the terrain was clamped to the road). aabb.Minimum.Y = 0; aabb.Maximum.Y = 0; // Convert to submesh. submesh = new Submesh { PrimitiveCount = indices.Count / 3, PrimitiveType = PrimitiveType.TriangleList, VertexCount = vertices.Count, VertexBuffer = new VertexBuffer(graphicsDevice, TerrainLayerVertex.VertexDeclaration, vertices.Count, BufferUsage.WriteOnly), IndexBuffer = new IndexBuffer(graphicsDevice, IndexElementSize.ThirtyTwoBits, indices.Count, BufferUsage.WriteOnly) }; submesh.VertexBuffer.SetData(vertices.ToArray()); submesh.IndexBuffer.SetData(indices.ToArray()); }
internal static Submesh CreateSpherePatch( GraphicsDevice graphicsDevice, float originRadius, float sphereRadius, int numberOfDivisions) { if (graphicsDevice == null) { throw new ArgumentNullException("graphicsDevice"); } if (numberOfDivisions < 1 || numberOfDivisions > 255) { throw new ArgumentOutOfRangeException("numberOfDivisions", "numberOfDivisions must be in the range [0, 255]."); } var submesh = new Submesh { PrimitiveType = PrimitiveType.TriangleList, }; int numberOfVertices = (numberOfDivisions + 1) * (numberOfDivisions + 1); var vertices = new VertexPositionNormalTexture[numberOfVertices]; float groundPlaneSize = 2 * (float)Math.Sqrt(sphereRadius * sphereRadius - originRadius * originRadius); int index = 0; for (int i = 0; i <= numberOfDivisions; i++) { for (int j = 0; j <= numberOfDivisions; j++) { float x = -groundPlaneSize / 2 + groundPlaneSize / numberOfDivisions * j; float z = -groundPlaneSize / 2 + groundPlaneSize / numberOfDivisions * i; //float groundDist2 = x * x + z * z; var direction = new Vector3(x, originRadius, z); direction.Normalize(); var p = direction * sphereRadius - new Vector3(0, originRadius, 0); var t = new Vector2( j / (float)numberOfDivisions, i / (float)numberOfDivisions); vertices[index++] = new VertexPositionNormalTexture(p, new Vector3(0, -1, 0), t); } } submesh.VertexBuffer = new VertexBuffer( graphicsDevice, VertexPositionNormalTexture.VertexDeclaration, vertices.Length, BufferUsage.None); submesh.VertexBuffer.SetData(vertices); submesh.VertexCount = submesh.VertexBuffer.VertexCount; // Build array of indices. int numberOfTriangles = numberOfDivisions * numberOfDivisions * 2; int numberOfIndices = 3 * numberOfTriangles; var indices = new ushort[numberOfIndices]; index = 0; for (int i = 0; i < numberOfDivisions; i++) { for (int j = 0; j < numberOfDivisions; j++) { int baseIndex = i * (numberOfDivisions + 1) + j; indices[index++] = (ushort)baseIndex; indices[index++] = (ushort)(baseIndex + numberOfDivisions + 1); indices[index++] = (ushort)(baseIndex + 1); indices[index++] = (ushort)(baseIndex + 1); indices[index++] = (ushort)(baseIndex + numberOfDivisions + 1); indices[index++] = (ushort)(baseIndex + numberOfDivisions + 2); } } submesh.IndexBuffer = new IndexBuffer( graphicsDevice, IndexElementSize.SixteenBits, indices.Length, BufferUsage.None); submesh.IndexBuffer.SetData(indices); submesh.PrimitiveCount = indices.Length / 3; return(submesh); }