/// <inheritdoc/> internal override void OnDraw(GraphicsDevice graphicsDevice, Rectangle rectangle, Vector2F topLeftPosition, Vector2F bottomRightPosition) { if (Submesh != null) { Submesh.Draw(); } }
//-------------------------------------------------------------- #region Methods //-------------------------------------------------------------- /// <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 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; }
public void GetMesh(Shape shape, out Submesh submesh, out Matrix44F 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 SubmeshSample(Microsoft.Xna.Framework.Game game) : base(game) { SampleFramework.IsMouseVisible = false; var delegateGraphicsScreen = new DelegateGraphicsScreen(GraphicsService) { RenderCallback = Render, }; GraphicsService.Screens.Insert(0, delegateGraphicsScreen); // Add a custom game object which controls the camera. _cameraObject = new CameraObject(Services); GameObjectService.Objects.Add(_cameraObject); var graphicsDevice = GraphicsService.GraphicsDevice; // The MeshHelper class can create submeshes for several basic shapes: _sphere = MeshHelper.CreateUVSphere(graphicsDevice, 20); _torus = MeshHelper.CreateTorus(graphicsDevice, 0.5f, 0.667f, 16); _teapot = MeshHelper.CreateTeapot(graphicsDevice, 1, 8); // MeshHelper.CreateBox() returns a new submesh for a box. Instead we can call // MeshHelper.GetBox(), which returns a shared submesh. - GetBox() will always // return the same instance. _box = MeshHelper.GetBox(GraphicsService); // We can also create a submesh that uses line primitives. _cone = MeshHelper.GetConeLines(GraphicsService); // We use a normal XNA BasicEffect to render the submeshes. _effect = new BasicEffect(graphicsDevice) { PreferPerPixelLighting = true }; _effect.EnableDefaultLighting(); }
public static TriangleMesh ToTriangleMesh(this Submesh submesh) { var triangleMesh = new TriangleMesh(); ToTriangleMesh(submesh, triangleMesh); return(triangleMesh); }
public static Submesh CreateBoxLines(GraphicsDevice graphicsDevice) { if (graphicsDevice == null) throw new ArgumentNullException("graphicsDevice"); var submesh = new Submesh { PrimitiveType = PrimitiveType.LineList, }; var vertices = new[] { new Vector3F(-0.5f, -0.5f, +0.5f), new Vector3F(+0.5f, -0.5f, +0.5f), new Vector3F(+0.5f, +0.5f, +0.5f), new Vector3F(-0.5f, +0.5f, +0.5f), new Vector3F(-0.5f, -0.5f, -0.5f), new Vector3F(+0.5f, -0.5f, -0.5f), new Vector3F(+0.5f, +0.5f, -0.5f), new Vector3F(-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 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 Vector3F[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 Vector3F(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 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; #if MONOGAME graphicsDevice.DrawIndexedPrimitives( submesh.PrimitiveType, submesh.StartVertex, submesh.StartIndex, submesh.PrimitiveCount); #else graphicsDevice.DrawIndexedPrimitives( submesh.PrimitiveType, submesh.StartVertex, 0, submesh.VertexCount, submesh.StartIndex, submesh.PrimitiveCount); #endif } }
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 Vector3F[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 Vector3F(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 GetMesh(IGraphicsService graphicsService, Shape shape, out Submesh submesh, out Matrix44F 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 = Matrix44F.Identity; } }
/// <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); }
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); }
private void CreateMesh(Shape shape, out Submesh submesh, out Matrix44F 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 = Matrix44F.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 * Matrix44F.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 = Matrix44F.Identity; }
public static Submesh CreateCylinder(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>(); // Top cap Vector3 normal = Vector3.UnitY; vertices.Add(new VertexPositionNormal(new Vector3(0, 1, 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, 1, z), normal)); } // Bottom cap normal = -Vector3.UnitY; vertices.Add(new VertexPositionNormal(new Vector3(0, -1, 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, -1, z), normal)); } // Side int baseIndex = vertices.Count; 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; var indices = new List <ushort>(); // Top cap for (int i = 0; i < numberOfSegments - 1; i++) { indices.Add(0); indices.Add((ushort)(i + 2)); indices.Add((ushort)(i + 1)); } // Last triangle of top cap. indices.Add(0); indices.Add(1); indices.Add((ushort)numberOfSegments); // Bottom cap for (int i = 0; i < numberOfSegments - 1; i++) { indices.Add((ushort)(numberOfSegments + 1)); indices.Add((ushort)(numberOfSegments + i + 2)); indices.Add((ushort)(numberOfSegments + i + 3)); } // Last triangle of bottom cap. indices.Add((ushort)(numberOfSegments + 1)); indices.Add((ushort)(numberOfSegments + numberOfSegments + 1)); indices.Add((ushort)(numberOfSegments + 2)); // Side for (int i = 0; i < numberOfSegments - 1; i++) { indices.Add((ushort)(baseIndex + 2 * i)); indices.Add((ushort)(baseIndex + 2 * i + 2)); indices.Add((ushort)(baseIndex + 2 * i + 1)); indices.Add((ushort)(baseIndex + 2 * i + 2)); indices.Add((ushort)(baseIndex + 2 * i + 3)); indices.Add((ushort)(baseIndex + 2 * i + 1)); } // Indices of last 2 triangle. indices.Add((ushort)(baseIndex + numberOfSegments + numberOfSegments - 2)); indices.Add((ushort)(baseIndex)); indices.Add((ushort)(baseIndex + numberOfSegments + numberOfSegments - 1)); indices.Add((ushort)(baseIndex)); indices.Add((ushort)(baseIndex + 1)); indices.Add((ushort)(baseIndex + 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 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 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 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 <Vector3F>(); for (int i = 0; i < numberOfSegments; i++) { float angle = i * ConstantsF.TwoPi / numberOfSegments; vertices.Add(new Vector3F((float)Math.Cos(angle), 0, -(float)Math.Sin(angle))); } // Top vertex of the sphere. var topVertexIndex = vertices.Count; vertices.Add(new Vector3F(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 Vector3F((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++) { Vector3F p = vertices[firstArcIndex + i]; vertices.Add(new Vector3F(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++) { Vector3F p = vertices[firstArcIndex + i]; vertices.Add(new Vector3F(-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++) { Vector3F p = vertices[firstArcIndex + i]; vertices.Add(new Vector3F(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); }
//-------------------------------------------------------------- /// <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 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 <Vector3F>(); 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) { Vector3F previous = flattenedPoints[i - 1]; Vector3F 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) { Vector3F position = key.Point; var roadKey = key as TerrainRoadPathKey; float width = (roadKey != null) ? roadKey.Width : defaultWidth; for (; index < flattenedPoints.Count; index++) { if (Vector3F.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. Vector3F lastOrthonormal = Vector3F.UnitX; aabb = new Aabb(flattenedPoints[0], flattenedPoints[0]); bool isClosed = Vector3F.AreNumericallyEqual(flattenedPoints[0], flattenedPoints[flattenedPoints.Count - 1]); for (int i = 0; i < flattenedPoints.Count; i++) { Vector3F start = flattenedPoints[i]; Vector3F previous; bool isFirstPoint = (i == 0); if (!isFirstPoint) { previous = flattenedPoints[i - 1]; } else if (isClosed && path.SmoothEnds) { previous = flattenedPoints[flattenedPoints.Count - 2]; } else { previous = start; } Vector3F next; bool isLastPoint = (i + 1 == flattenedPoints.Count); if (!isLastPoint) { next = flattenedPoints[i + 1]; } else if (isClosed && path.SmoothEnds) { next = flattenedPoints[1]; } else { next = start; } Vector3F tangent = next - previous; Vector3F orthonormal = new Vector3F(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. Vector3F leftVertex = start - orthonormal * (widths[i] / 2); Vector3F 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()); }
public static Submesh CreateUVSphere(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 slices (horizontal cuts, not including the poles). int numberOfSlices = numberOfSegments / 2 - 1; // The number of rings (horizontal bands of triangles). int numberOfRings = numberOfSegments / 2; int numberOfVertices = numberOfSegments * numberOfSlices + 2; 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 top to bottom and from the x-axis in // clockwise direction (when viewed from top). for (int slice = 0; slice < numberOfSlices; slice++) { float upAngle = angle * (slice + 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)); } } // Bottom vertex. vertices[i++] = new VertexPositionNormal(new Vector3(0, -1, 0), new Vector3(0, -1, 0)); 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 * 2 // Triangles in top and bottom cap. + numberOfSegments * 2 * (numberOfRings - 2); 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 - 1; 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); } } } // Index of first vertex on last slice. int baseIndex = numberOfVertices - numberOfSegments - 1; // Indices for bottom cap. for (int segment = 0; segment < numberOfSegments; segment++) { indices[i++] = (ushort)(numberOfVertices - 1); if (segment + 1 < numberOfSegments) indices[i++] = (ushort)(baseIndex + segment + 1); else indices[i++] = (ushort)baseIndex; // Wrap around to first vertex. indices[i++] = (ushort)(baseIndex + 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 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 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); }
//-------------------------------------------------------------- #region Properties & Events //-------------------------------------------------------------- #endregion //-------------------------------------------------------------- #region Creation & Cleanup //-------------------------------------------------------------- /// <summary> /// Initializes a new instance of the <see cref="FogSphereRenderer"/> class. /// </summary> /// <param name="graphicsService">The graphics service.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="graphicsService"/> is <see langword="null"/>. /// </exception> public FogSphereRenderer(IGraphicsService graphicsService) { if (graphicsService == null) throw new ArgumentNullException("graphicsService"); // Load effect. _effect = graphicsService.Content.Load<Effect>("FogSphere"); _parameterViewportSize = _effect.Parameters["ViewportSize"]; _parameterWorld = _effect.Parameters["World"]; _parameterWorldInverse = _effect.Parameters["WorldInverse"]; _parameterView = _effect.Parameters["View"]; _parameterProjection = _effect.Parameters["Projection"]; _parameterCameraPosition = _effect.Parameters["CameraPosition"]; _parameterCameraFar = _effect.Parameters["CameraFar"]; _parameterGBuffer0 = _effect.Parameters["GBuffer0"]; _parameterColor = _effect.Parameters["Color"]; _parameterBlendMode = _effect.Parameters["BlendMode"]; _parameterDensity = _effect.Parameters["Density"]; _parameterFalloff = _effect.Parameters["Falloff"]; _parameterIntersectionSoftness = _effect.Parameters["IntersectionSoftness"]; // Get a sphere mesh. _submesh = MeshHelper.CreateIcosphere(graphicsService.GraphicsDevice, 2); }
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 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; }
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<Vector3F>(); for (int i = 0; i < numberOfSegments; i++) { float angle = i * ConstantsF.TwoPi / numberOfSegments; vertices.Add(new Vector3F((float)Math.Cos(angle), 0, -(float)Math.Sin(angle))); } // Top vertex of the sphere. var topVertexIndex = vertices.Count; vertices.Add(new Vector3F(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 Vector3F((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++) { Vector3F p = vertices[firstArcIndex + i]; vertices.Add(new Vector3F(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++) { Vector3F p = vertices[firstArcIndex + i]; vertices.Add(new Vector3F(-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++) { Vector3F p = vertices[firstArcIndex + i]; vertices.Add(new Vector3F(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; }
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; }
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; }
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); }
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 CreateConeLines(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 <Vector3F>(); // Base 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 Vector3F(x, 0, z)); } // Tip vertices.Add(new Vector3F(0, 1, 0)); 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>(); // Base 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 base circle. indices.Add((ushort)(numberOfSegments - 1)); indices.Add(0); // Side represented by 4 lines. for (int i = 0; i < 4; i++) { indices.Add((ushort)(i * numberOfSegments / 4)); indices.Add((ushort)numberOfSegments); } 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 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<Vector3F>(); // Top circle. for (int i = 0; i < numberOfSegments; i++) { float angle = i * ConstantsF.TwoPi / numberOfSegments; vertices.Add(new Vector3F((float)Math.Cos(angle), 1, -(float)Math.Sin(angle))); } // Bottom circle. for (int i = 0; i < numberOfSegments; i++) { Vector3F p = vertices[i]; vertices.Add(new Vector3F(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; }
private static OutlineItem CreateOutlineItem(Submesh submesh) { var item = new OutlineItem { Text = "Submesh", Icon = MultiColorGlyphs.Mesh, ToolTip = ToolTipSceneNode, UserData = submesh, }; return item; }
public static Mesh CreateMesh(IGraphicsService graphicsService, Texture2D texture) { if (graphicsService == null) throw new ArgumentNullException("graphicsService"); if (texture == null) throw new ArgumentNullException("texture"); List<Vector3> positions = new List<Vector3>(); List<ushort> indices = new List<ushort>(); // Create two rings of vertices around the top and bottom of the cylinder. for (int i = 0; i < CylinderSegments; i++) { float angle = ConstantsF.TwoPi * i / CylinderSegments; float x = (float)Math.Cos(angle) * CylinderSize; float z = (float)Math.Sin(angle) * CylinderSize; positions.Add(new Vector3(x, CylinderSize * 5 / 12, z)); positions.Add(new Vector3(x, -CylinderSize * 5 / 12, z)); } // Create two center vertices, used for closing the top and bottom. positions.Add(new Vector3(0, CylinderSize, 0)); positions.Add(new Vector3(0, -CylinderSize, 0)); // Create the individual triangles that make up our skydome. List<VertexPositionTexture> vertices = new List<VertexPositionTexture>(); ushort index = 0; for (int i = 0; i < CylinderSegments; i++) { int j = (i + 1) % CylinderSegments; // Calculate texture coordinates for this segment of the cylinder. float u1 = (float)i / (float)CylinderSegments; float u2 = (float)(i + 1) / (float)CylinderSegments; // Two triangles form a quad, one side segment of the cylinder. vertices.Add(new VertexPositionTexture(positions[i * 2], new Vector2(u1, TexCoordTop))); indices.Add(index++); vertices.Add(new VertexPositionTexture(positions[j * 2], new Vector2(u2, TexCoordTop))); indices.Add(index++); vertices.Add(new VertexPositionTexture(positions[i * 2 + 1], new Vector2(u1, TexCoordBottom))); indices.Add(index++); vertices.Add(new VertexPositionTexture(positions[j * 2], new Vector2(u2, TexCoordTop))); indices.Add(index++); vertices.Add(new VertexPositionTexture(positions[j * 2 + 1], new Vector2(u2, TexCoordBottom))); indices.Add(index++); vertices.Add(new VertexPositionTexture(positions[i * 2 + 1], new Vector2(u1, TexCoordBottom))); indices.Add(index++); // Triangle fanning inward to fill the top above this segment. vertices.Add(new VertexPositionTexture(positions[CylinderSegments * 2], new Vector2(u1, 0))); indices.Add(index++); vertices.Add(new VertexPositionTexture(positions[j * 2], new Vector2(u2, TexCoordTop))); indices.Add(index++); vertices.Add(new VertexPositionTexture(positions[i * 2], new Vector2(u1, TexCoordTop))); indices.Add(index++); // Triangle fanning inward to fill the bottom below this segment. vertices.Add(new VertexPositionTexture(positions[CylinderSegments * 2 + 1], new Vector2(u1, 1))); indices.Add(index++); vertices.Add(new VertexPositionTexture(positions[i * 2 + 1], new Vector2(u1, TexCoordBottom))); indices.Add(index++); vertices.Add(new VertexPositionTexture(positions[j * 2 + 1], new Vector2(u2, TexCoordBottom))); indices.Add(index++); } // Create the vertex buffer. VertexBuffer vertexBuffer = new VertexBuffer( graphicsService.GraphicsDevice, VertexPositionTexture.VertexDeclaration, vertices.Count, BufferUsage. None); vertexBuffer.SetData(vertices.ToArray()); // Create the index buffer. IndexBuffer indexBuffer = new IndexBuffer( graphicsService.GraphicsDevice, IndexElementSize.SixteenBits, indices.Count, BufferUsage.None); indexBuffer.SetData(indices.ToArray()); // Create a submesh, which is a set of primitives which can be rendered in // one draw call. Submesh submesh = new Submesh { PrimitiveCount = indices.Count / 3, PrimitiveType = PrimitiveType.TriangleList, StartIndex = 0, StartVertex = 0, VertexCount = vertices.Count, VertexBuffer = vertexBuffer, IndexBuffer = indexBuffer, }; // Create a mesh (which is collection of submeshes and materials). Mesh mesh = new Mesh { Name = "Sky", BoundingShape = new CylinderShape(CylinderSize, 2 * CylinderSize), }; mesh.Submeshes.Add(submesh); // Create a BasicEffectBinding which wraps the XNA BasicEffect. // An EffectBinding connects an effect with effect parameter values ("effect // parameter binding"). Some of these parameter values are defined here. Others, // like World matrices, light parameters, etc. are automatically updated by // the graphics engine in each frame. BasicEffectBinding effectBinding = new BasicEffectBinding(graphicsService, null) { LightingEnabled = false, TextureEnabled = true, VertexColorEnabled = false }; effectBinding.Set("Texture", texture); effectBinding.Set("SpecularColor", new Vector3(0, 0, 0)); // Create a material, which is a collection of effect bindings - one effect // binding for each "render pass". The sky mesh should be rendered in the // "Sky" render pass. This render pass name is an arbitrary string that is // used in SampleGraphicsScreen.cs. Material material = new Material(); material.Add("Sky", effectBinding); // Assign the material to the submesh. submesh.SetMaterial(material); return mesh; }
public static Submesh CreateBoxLines(GraphicsDevice graphicsDevice) { if (graphicsDevice == null) { throw new ArgumentNullException("graphicsDevice"); } var submesh = new Submesh { PrimitiveType = PrimitiveType.LineList, }; var vertices = new[] { new Vector3F(-0.5f, -0.5f, +0.5f), new Vector3F(+0.5f, -0.5f, +0.5f), new Vector3F(+0.5f, +0.5f, +0.5f), new Vector3F(-0.5f, +0.5f, +0.5f), new Vector3F(-0.5f, -0.5f, -0.5f), new Vector3F(+0.5f, -0.5f, -0.5f), new Vector3F(+0.5f, +0.5f, -0.5f), new Vector3F(-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); }
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 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; }
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 <Vector3F>(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((Vector3F)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((Vector3F)positions[i]); triangleMesh.Vertices.Add((Vector3F)positions[i + 1]); triangleMesh.Vertices.Add((Vector3F)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 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 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<Vector3F>(); 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) { Vector3F previous = flattenedPoints[i - 1]; Vector3F 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) { Vector3F position = key.Point; var roadKey = key as TerrainRoadPathKey; float width = (roadKey != null) ? roadKey.Width : defaultWidth; for (; index < flattenedPoints.Count; index++) { if (Vector3F.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. Vector3F lastOrthonormal = Vector3F.UnitX; aabb = new Aabb(flattenedPoints[0], flattenedPoints[0]); bool isClosed = Vector3F.AreNumericallyEqual(flattenedPoints[0], flattenedPoints[flattenedPoints.Count - 1]); for (int i = 0; i < flattenedPoints.Count; i++) { Vector3F start = flattenedPoints[i]; Vector3F previous; bool isFirstPoint = (i == 0); if (!isFirstPoint) previous = flattenedPoints[i - 1]; else if (isClosed && path.SmoothEnds) previous = flattenedPoints[flattenedPoints.Count - 2]; else previous = start; Vector3F next; bool isLastPoint = (i + 1 == flattenedPoints.Count); if (!isLastPoint) next = flattenedPoints[i + 1]; else if (isClosed && path.SmoothEnds) next = flattenedPoints[1]; else next = start; Vector3F tangent = next - previous; Vector3F orthonormal = new Vector3F(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. Vector3F leftVertex = start - orthonormal * (widths[i] / 2); Vector3F 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()); }