public override MeshDraw CreateDebugPrimitive(GraphicsDevice device) { var verts = new VertexPositionNormalTexture[pointsList.Count]; for (var i = 0; i < pointsList.Count; i++) { verts[i].Position = pointsList[i]; verts[i].TextureCoordinate = Vector2.Zero; verts[i].Normal = Vector3.Zero; } var intIndices = indicesList.Select(x => (int)x).ToArray(); ////calculate basic normals ////todo verify, winding order might be wrong? for (var i = 0; i < indicesList.Count; i += 3) { var i1 = intIndices[i]; var i2 = intIndices[i + 1]; var i3 = intIndices[i + 2]; var a = verts[i1]; var b = verts[i2]; var c = verts[i3]; var n = Vector3.Cross((b.Position - a.Position), (c.Position - a.Position)); n.Normalize(); verts[i1].Normal = verts[i2].Normal = verts[i3].Normal = n; } var meshData = new GeometricMeshData <VertexPositionNormalTexture>(verts, intIndices, false); return(new GeometricPrimitive(device, meshData).ToMeshDraw()); }
public ConvexHullColliderShape(IReadOnlyList <Vector3> points, IEnumerable <uint> indices) { Type = ColliderShapeTypes.ConvexHull; Is2D = false; InternalShape = new BulletSharp.ConvexHullShape(points); if (!PhysicsEngine.Singleton.CreateDebugPrimitives) { return; } var verts = new VertexPositionNormalTexture[points.Count]; for (var i = 0; i < points.Count; i++) { verts[i].Position = points[i]; verts[i].TextureCoordinate = Vector2.Zero; verts[i].Normal = Vector3.Zero; } var intIndices = indices.Select(x => (int)x).ToArray(); var meshData = new GeometricMeshData <VertexPositionNormalTexture>(verts, intIndices, false); DebugPrimitive = new GeometricPrimitive(PhysicsEngine.Singleton.DebugGraphicsDevice, meshData); DebugPrimitiveScaling = Matrix.Scaling(new Vector3(1, 1, 1) * 1.01f); }
/// <summary> /// Generates triangles for an infinite plane that will completely intersect the given bounding box /// </summary> private NavigationMeshInputBuilder BuildPlaneGeometry(Plane plane, BoundingBox boundingBox) { Vector3 maxSize = boundingBox.Maximum - boundingBox.Minimum; float maxDiagonal = Math.Max(maxSize.X, Math.Max(maxSize.Y, maxSize.Z)); // Generate source plane triangles Vector3[] planePoints; int[] planeInds; NavigationMeshBuildUtils.BuildPlanePoints(ref plane, maxDiagonal, out planePoints, out planeInds); Vector3 tangent, bitangent; NavigationMeshBuildUtils.GenerateTangentBinormal(plane.Normal, out tangent, out bitangent); // Calculate plane offset so that the plane always covers the whole range of the bounding box Vector3 planeOffset = Vector3.Dot(boundingBox.Center, tangent) * tangent; planeOffset += Vector3.Dot(boundingBox.Center, bitangent) * bitangent; VertexPositionNormalTexture[] vertices = new VertexPositionNormalTexture[planePoints.Length]; for (int i = 0; i < planePoints.Length; i++) { vertices[i] = new VertexPositionNormalTexture(planePoints[i] + planeOffset, Vector3.UnitY, Vector2.Zero); } GeometricMeshData <VertexPositionNormalTexture> meshData = new GeometricMeshData <VertexPositionNormalTexture>(vertices, planeInds, true); NavigationMeshInputBuilder inputBuilder = new NavigationMeshInputBuilder(); inputBuilder.AppendMeshData(meshData, Matrix.Identity); return(inputBuilder); }
/// <summary> /// Appends local mesh data transformed with and object transform /// </summary> /// <param name="meshData"></param> /// <param name="objectTransform"></param> public void AppendMeshData(GeometricMeshData <VertexPositionNormalTexture> meshData, Matrix objectTransform) { // Transform box points int vbase = Points.Count; for (int i = 0; i < meshData.Vertices.Length; i++) { VertexPositionNormalTexture point = meshData.Vertices[i]; point.Position = Vector3.Transform(point.Position, objectTransform).XYZ(); Points.Add(point.Position); BoundingBox.Merge(ref BoundingBox, ref point.Position, out BoundingBox); } if (meshData.IsLeftHanded) { // Copy indices with offset applied for (int i = 0; i < meshData.Indices.Length; i += 3) { Indices.Add(meshData.Indices[i] + vbase); Indices.Add(meshData.Indices[i + 2] + vbase); Indices.Add(meshData.Indices[i + 1] + vbase); } } else { // Copy indices with offset applied for (int i = 0; i < meshData.Indices.Length; i++) { Indices.Add(meshData.Indices[i] + vbase); } } }
public override MeshDraw CreateDebugPrimitive(GraphicsDevice device) { if (cachedDebugPrimitive != null) return cachedDebugPrimitive; var verts = new VertexPositionNormalTexture[pointsList.Count]; for (var i = 0; i < pointsList.Count; i++) { verts[i].Position = pointsList[i]; verts[i].TextureCoordinate = Vector2.Zero; verts[i].Normal = Vector3.Zero; } var intIndices = indicesList.Select(x => (int)x).ToArray(); ////calculate basic normals ////todo verify, winding order might be wrong? for (var i = 0; i < indicesList.Count; i += 3) { var i1 = intIndices[i]; var i2 = intIndices[i + 1]; var i3 = intIndices[i + 2]; var a = verts[i1]; var b = verts[i2]; var c = verts[i3]; var n = Vector3.Cross((b.Position - a.Position), (c.Position - a.Position)); n.Normalize(); verts[i1].Normal = verts[i2].Normal = verts[i3].Normal = n; } var meshData = new GeometricMeshData<VertexPositionNormalTexture>(verts, intIndices, false); cachedDebugPrimitive = new GeometricPrimitive(device, meshData).ToMeshDraw(); return cachedDebugPrimitive; }
public GeometryData(float size, float tessellationX, float tessellationY, VertexPositionNormalTexture[] vertices, int[] indices, bool isLeftHanded) : base(vertices, indices, isLeftHanded) { Data = new GeometricMeshData <VertexPositionNormalTexture>(vertices, indices, isLeftHanded); Size = size; TessellationX = tessellationX; TessellationY = tessellationY; }
public override MeshDraw CreateDebugPrimitive(GraphicsDevice device) { var verts = new VertexPositionNormalTexture[vertices.Length]; for (int i = 0; i < vertices.Length; i++) { verts[i].Position = vertices[i]; } var meshData = new GeometricMeshData <VertexPositionNormalTexture>(verts, indices, false); return(new GeometricPrimitive(device, meshData).ToMeshDraw()); }
public static void CopyFromGeometricPrimitive(GeometricMeshData <VertexPositionNormalTexture> primitiveData, ref VertexPositionTexture[] vertices, ref int[] indices) { for (int i = 0; i < vertices.Length; ++i) { vertices[i].Position = primitiveData.Vertices[i].Position; vertices[i].TextureCoordinate = primitiveData.Vertices[i].TextureCoordinate; } for (int i = 0; i < indices.Length; ++i) { indices[i] = primitiveData.Indices[i]; } }
public static Model FromGeometricMeshData(GraphicsDevice graphicsDevice, GeometricMeshData <VertexPositionNormalTangentMultiTexture> geometryMesh, string effectName = "Default") { var vertices = geometryMesh.Vertices; // compute the bounding box of the primitive var boundingBox = BoundingBox.Empty; for (int i = 0; i < vertices.Length; i++) { BoundingBox.Merge(ref boundingBox, ref vertices[i].Position, out boundingBox); } return(FromGeometricMeshData(graphicsDevice, geometryMesh, boundingBox, VertexPositionNormalTangentMultiTexture.Layout, effectName)); }
public static Model FromGeometricMeshData <T>(GraphicsDevice graphicsDevice, GeometricMeshData <T> geometryMesh, BoundingBox boundingBox, VertexDeclaration layout, string effectName = "Default") where T : struct, IVertex { var meshDraw = new MeshDraw(); var vertices = geometryMesh.Vertices; var indices = geometryMesh.Indices; if (indices.Length < 0xFFFF) { var indicesShort = new ushort[indices.Length]; for (int i = 0; i < indicesShort.Length; i++) { indicesShort[i] = (ushort)indices[i]; } meshDraw.IndexBuffer = new IndexBufferBinding(Buffer.Index.New(graphicsDevice, indicesShort).RecreateWith(indicesShort), false, indices.Length); } else { if (graphicsDevice.Features.Profile <= GraphicsProfile.Level_9_3) { throw new InvalidOperationException("Cannot generate more than 65535 indices on feature level HW <= 9.3"); } meshDraw.IndexBuffer = new IndexBufferBinding(Buffer.Index.New(graphicsDevice, indices).RecreateWith(indices), true, indices.Length); } meshDraw.VertexBuffers = new[] { new VertexBufferBinding(Buffer.Vertex.New(graphicsDevice, vertices).RecreateWith(vertices), layout, vertices.Length) }; meshDraw.DrawCount = indices.Length; meshDraw.PrimitiveType = PrimitiveType.TriangleList; var mesh = new Mesh { Draw = meshDraw, BoundingBox = boundingBox }; mesh.Parameters.Set(RenderingParameters.RenderLayer, RenderLayers.RenderLayerAll); var model = new Model { BoundingBox = boundingBox }; model.Add(mesh); return(model); }
/// <summary> /// Appends local mesh data transformed with and object transform /// </summary> /// <param name="meshData"></param> /// <param name="objectTransform"></param> public void AppendMeshData(GeometricMeshData<VertexPositionNormalTexture> meshData, Matrix objectTransform) { // Transform box points int vbase = Points.Count; for (int i = 0; i < meshData.Vertices.Length; i++) { VertexPositionNormalTexture point = meshData.Vertices[i]; point.Position = Vector3.Transform(point.Position, objectTransform).XYZ(); Points.Add(point.Position); BoundingBox.Merge(ref BoundingBox, ref point.Position, out BoundingBox); } // Copy indices with offset applied for (int i = 0; i < meshData.Indices.Length; i++) { Indices.Add(meshData.Indices[i] + vbase); } }
/// <summary> /// Initializes a new instance of the <see cref="GeometricPrimitive{T}"/> class. /// </summary> /// <param name="graphicsDevice">The graphics device.</param> /// <param name="geometryMesh">The geometry mesh.</param> /// <exception cref="System.InvalidOperationException">Cannot generate more than 65535 indices on feature level HW <= 9.3</exception> public GeometricPrimitive(GraphicsDevice graphicsDevice, GeometricMeshData <T> geometryMesh) { GraphicsDevice = graphicsDevice; PipelineState = new MutablePipelineState(graphicsDevice); var vertices = geometryMesh.Vertices; var indices = geometryMesh.Indices; if (geometryMesh.IsLeftHanded) { ReverseWinding(vertices, indices); } if (indices.Length < 0xFFFF) { var indicesShort = new ushort[indices.Length]; for (int i = 0; i < indicesShort.Length; i++) { indicesShort[i] = (ushort)indices[i]; } IndexBuffer = Buffer.Index.New(graphicsDevice, indicesShort).RecreateWith(indicesShort).DisposeBy(this); } else { if (graphicsDevice.Features.CurrentProfile <= GraphicsProfile.Level_9_3) { throw new InvalidOperationException("Cannot generate more than 65535 indices on feature level HW <= 9.3"); } IndexBuffer = Buffer.Index.New(graphicsDevice, indices).RecreateWith(indices).DisposeBy(this); IsIndex32Bits = true; } // For now it will keep buffers for recreation. // TODO: A better alternative would be to store recreation parameters so that we can reuse procedural code. VertexBuffer = Buffer.Vertex.New(graphicsDevice, vertices).RecreateWith(vertices).DisposeBy(this); VertexBufferBinding = new VertexBufferBinding(VertexBuffer, new T().GetLayout(), vertices.Length); PipelineState.State.SetDefaults(); PipelineState.State.InputElements = VertexBufferBinding.Declaration.CreateInputElements(); PipelineState.State.PrimitiveType = PrimitiveQuad.PrimitiveType; }
private NavigationMeshDebugVisual CreateDebugVisual(NavigationMesh navigationMesh, NavigationMesh previousNavigationMesh) { NavigationMeshDebugVisual ret = new NavigationMeshDebugVisual(); ret.DebugEntity = new Entity($"Debug entity for navigation mesh"); // Create a visual for every layer with a separate color using (var layers = navigationMesh.Layers.GetEnumerator()) { while (layers.MoveNext()) { Model model = new Model(); var currentLayer = layers.Current.Value; var currentId = layers.Current.Key; NavigationMeshDisplayGroup displayGroup; if (!groupDisplaySettings.TryGetValue(currentId, out displayGroup)) { continue; // No display settings for this group } model.Add(displayGroup.Material); model.Add(displayGroup.HighlightMaterial); foreach (var p in currentLayer.Tiles) { bool updated = true; NavigationMeshTile tile = p.Value; // Extract vertex data List <Vector3> tileVertexList = new List <Vector3>(); List <int> tileIndexList = new List <int>(); if (!tile.GetTileVertices(tileVertexList, tileIndexList)) { continue; } // Check if updated NavigationMeshLayer sourceLayer; if (previousNavigationMesh != null && previousNavigationMesh.Layers.TryGetValue(currentId, out sourceLayer)) { NavigationMeshTile oldTile = sourceLayer.FindTile(p.Key); if (oldTile != null && oldTile.Data.SequenceEqual(tile.Data)) { updated = false; } } // Stack layers vertically Vector3 offset = new Vector3(0.0f, LayerHeightMultiplier * displayGroup.Index, 0.0f); // Calculate mesh bounding box from navigation mesh points BoundingBox bb = BoundingBox.Empty; List <VertexPositionNormalTexture> meshVertices = new List <VertexPositionNormalTexture>(); for (int i = 0; i < tileVertexList.Count; i++) { Vector3 position = tileVertexList[i] + offset; BoundingBox.Merge(ref bb, ref position, out bb); VertexPositionNormalTexture vert = new VertexPositionNormalTexture(); vert.Position = position; vert.Normal = Vector3.UnitY; vert.TextureCoordinate = new Vector2(0.5f, 0.5f); meshVertices.Add(vert); } MeshDraw draw; using (var meshData = new GeometricMeshData <VertexPositionNormalTexture>(meshVertices.ToArray(), tileIndexList.ToArray(), true)) { GeometricPrimitive primitive = new GeometricPrimitive(game.GraphicsDevice, meshData); ret.GeneratedDynamicPrimitives.Add(primitive); draw = primitive.ToMeshDraw(); } Mesh mesh = new Mesh { Draw = draw, MaterialIndex = updated ? 1 : 0, BoundingBox = bb }; model.Add(mesh); } // Create an entity per layer var layerEntity = new Entity($"Navigation group {currentId}"); // Add a new model component var modelComponent = new ModelComponent(model); layerEntity.Add(modelComponent); modelComponent.Enabled = displayGroup.IsVisible; ret.ModelComponents.Add(currentId, modelComponent); ret.DebugEntity.AddChild(layerEntity); } } return(ret); }
private void CollectInputGeometry() { // Reset state fullRebuild = false; updatedAreas.Clear(); fullRebuild = oldBuild == null; sceneNavigationMeshInputBuilder = new NavigationMeshInputBuilder(); foreach (var entity in sceneEntities) { StaticColliderComponent collider = entity.Get <StaticColliderComponent>(); bool colliderEnabled = collider != null && ((CollisionFilterGroupFlags)collider.CollisionGroup & asset.IncludedCollisionGroups) != 0 && collider.Enabled; if (colliderEnabled) { // Compose collider shape, mark as disabled if it failed so the respective area gets updated collider.ComposeShape(); if (collider.ColliderShape == null) { colliderEnabled = false; } } if (!colliderEnabled) // Removed or disabled { // Check for old object NavigationMeshCachedBuildObject oldObject = null; if (oldBuild?.Objects.TryGetValue(entity.Id, out oldObject) ?? false) { // This object has been disabled, update the area because it was removed and continue to the next object updatedAreas.Add(oldObject.Data.BoundingBox); } } else if (oldBuild?.IsUpdatedOrNew(entity) ?? true) // Is the entity updated? { TransformComponent entityTransform = entity.Transform; Matrix entityWorldMatrix = entityTransform.WorldMatrix; NavigationMeshInputBuilder entityNavigationMeshInputBuilder = new NavigationMeshInputBuilder(); bool isDeferred = false; // Interate through all the colliders shapes while queueing all shapes in compound shapes to process those as well Queue <ColliderShape> shapesToProcess = new Queue <ColliderShape>(); shapesToProcess.Enqueue(collider.ColliderShape); while (shapesToProcess.Count > 0) { var shape = shapesToProcess.Dequeue(); var shapeType = shape.GetType(); if (shapeType == typeof(BoxColliderShape)) { var box = (BoxColliderShape)shape; var boxDesc = (BoxColliderShapeDesc)box.Description; Matrix transform = box.PositiveCenterMatrix * entityWorldMatrix; var meshData = GeometricPrimitive.Cube.New(boxDesc.Size); entityNavigationMeshInputBuilder.AppendMeshData(meshData, transform); } else if (shapeType == typeof(SphereColliderShape)) { var sphere = (SphereColliderShape)shape; var sphereDesc = (SphereColliderShapeDesc)sphere.Description; Matrix transform = sphere.PositiveCenterMatrix * entityWorldMatrix; var meshData = GeometricPrimitive.Sphere.New(sphereDesc.Radius); entityNavigationMeshInputBuilder.AppendMeshData(meshData, transform); } else if (shapeType == typeof(CylinderColliderShape)) { var cylinder = (CylinderColliderShape)shape; var cylinderDesc = (CylinderColliderShapeDesc)cylinder.Description; Matrix transform = cylinder.PositiveCenterMatrix * entityWorldMatrix; var meshData = GeometricPrimitive.Cylinder.New(cylinderDesc.Height, cylinderDesc.Radius); entityNavigationMeshInputBuilder.AppendMeshData(meshData, transform); } else if (shapeType == typeof(CapsuleColliderShape)) { var capsule = (CapsuleColliderShape)shape; var capsuleDesc = (CapsuleColliderShapeDesc)capsule.Description; Matrix transform = capsule.PositiveCenterMatrix * entityWorldMatrix; var meshData = GeometricPrimitive.Capsule.New(capsuleDesc.Length, capsuleDesc.Radius); entityNavigationMeshInputBuilder.AppendMeshData(meshData, transform); } else if (shapeType == typeof(ConeColliderShape)) { var cone = (ConeColliderShape)shape; var coneDesc = (ConeColliderShapeDesc)cone.Description; Matrix transform = cone.PositiveCenterMatrix * entityWorldMatrix; var meshData = GeometricPrimitive.Cone.New(coneDesc.Radius, coneDesc.Height); entityNavigationMeshInputBuilder.AppendMeshData(meshData, transform); } else if (shapeType == typeof(StaticPlaneColliderShape)) { var plane = (StaticPlaneColliderShape)shape; var planeDesc = (StaticPlaneColliderShapeDesc)plane.Description; Matrix transform = plane.PositiveCenterMatrix * entityWorldMatrix; // Defer infinite planes because their size is not defined yet deferredShapes.Add(new DeferredShape { Description = planeDesc, Transform = transform, Entity = entity, NavigationMeshInputBuilder = entityNavigationMeshInputBuilder }); isDeferred = true; } else if (shapeType == typeof(ConvexHullColliderShape)) { var hull = (ConvexHullColliderShape)shape; Matrix transform = hull.PositiveCenterMatrix * entityWorldMatrix; // Convert hull indices to int int[] indices = new int[hull.Indices.Count]; if (hull.Indices.Count % 3 != 0) { throw new InvalidOperationException("Physics hull does not consist of triangles"); } for (int i = 0; i < hull.Indices.Count; i += 3) { indices[i] = (int)hull.Indices[i]; indices[i + 1] = (int)hull.Indices[i + 1]; indices[i + 2] = (int)hull.Indices[i + 2]; } entityNavigationMeshInputBuilder.AppendArrays(hull.Points.ToArray(), indices, transform); } else if (shapeType == typeof(CompoundColliderShape)) { // Unroll compound collider shapes var compound = (CompoundColliderShape)shape; for (int i = 0; i < compound.Count; i++) { shapesToProcess.Enqueue(compound[i]); } } } if (!isDeferred) { // Store current entity in build cache currentBuild.Add(entity, entityNavigationMeshInputBuilder); // Add (?old) and new bounding box to modified areas sceneNavigationMeshInputBuilder.AppendOther(entityNavigationMeshInputBuilder); NavigationMeshCachedBuildObject oldObject = null; if (oldBuild?.Objects.TryGetValue(entity.Id, out oldObject) ?? false) { updatedAreas.Add(oldObject.Data.BoundingBox); } updatedAreas.Add(entityNavigationMeshInputBuilder.BoundingBox); } } else // Not updated { // Copy old data into vertex buffer NavigationMeshCachedBuildObject oldObject = oldBuild.Objects[entity.Id]; sceneNavigationMeshInputBuilder.AppendOther(oldObject.Data); currentBuild.Add(entity, oldObject.Data); } } // Unload loaded collider shapes foreach (var pair in loadedColliderShapes) { contentManager.Unload(pair.Key); } // Store calculated bounding box if (generateBoundingBox) { globalBoundingBox = sceneNavigationMeshInputBuilder.BoundingBox; } // Process deferred shapes Vector3 maxSize = globalBoundingBox.Maximum - globalBoundingBox.Minimum; float maxDiagonal = Math.Max(maxSize.X, Math.Max(maxSize.Y, maxSize.Z)); foreach (DeferredShape shape in deferredShapes) { StaticPlaneColliderShapeDesc planeDesc = (StaticPlaneColliderShapeDesc)shape.Description; Plane plane = new Plane(planeDesc.Normal, planeDesc.Offset); // Pre-Transform plane parameters plane.Normal = Vector3.TransformNormal(plane.Normal, shape.Transform); float offset = Vector3.Dot(shape.Transform.TranslationVector, plane.Normal); plane.D += offset; // Generate source plane triangles Vector3[] planePoints; int[] planeInds; NavigationMeshBuildUtils.BuildPlanePoints(ref plane, maxDiagonal, out planePoints, out planeInds); Vector3 tangent, bitangent; NavigationMeshBuildUtils.GenerateTangentBinormal(plane.Normal, out tangent, out bitangent); // Calculate plane offset so that the plane always covers the whole range of the bounding box Vector3 planeOffset = Vector3.Dot(globalBoundingBox.Center, tangent) * tangent; planeOffset += Vector3.Dot(globalBoundingBox.Center, bitangent) * bitangent; VertexPositionNormalTexture[] vertices = new VertexPositionNormalTexture[planePoints.Length]; for (int i = 0; i < planePoints.Length; i++) { vertices[i] = new VertexPositionNormalTexture(planePoints[i] + planeOffset, Vector3.UnitY, Vector2.Zero); } GeometricMeshData <VertexPositionNormalTexture> meshData = new GeometricMeshData <VertexPositionNormalTexture>(vertices, planeInds, false); shape.NavigationMeshInputBuilder.AppendMeshData(meshData, Matrix.Identity); sceneNavigationMeshInputBuilder.AppendMeshData(meshData, Matrix.Identity); // Update bounding box after plane generation if (generateBoundingBox) { globalBoundingBox = sceneNavigationMeshInputBuilder.BoundingBox; } // Store deferred shape in build cahce just like normal onesdddd currentBuild.Add(shape.Entity, shape.NavigationMeshInputBuilder); // NOTE: Force a full rebuild when moving unbound shapes such as ininite planes // the alternative is to intersect the old and new plane with the tiles to see which ones changed // although in most cases it will be a horizontal plane and all tiles will be affected anyways fullRebuild = true; } }
public GeometricPrimitive(GraphicsDevice graphicsDevice, GeometricMeshData<VertexPositionNormalTexture> geometryMesh) : base(graphicsDevice, geometryMesh) { }
public GeometricMultiTexcoordPrimitive(GraphicsDevice graphicsDevice, GeometricMeshData <VertexPositionNormalTangentMultiTexture> geometryMesh) : base(graphicsDevice, geometryMesh) { }