/// <summary> /// Initializes a new instance of the <see cref="ProceduralTerrainCreator"/> class. /// </summary> /// <param name="seed">The seed for the random number generator (e.g. 7777).</param> /// <param name="permutationTableSize"> /// The size of the permutation table (e.g. 256). Must be a power of two. /// </param> /// <param name="mu"> /// The constant µ that defines the exponential distribution. Use a value of 1 to get standard /// Perlin noise. Use a value greater than 1 (e.g. 1.02) to get Perlin noise with exponentially /// distributed gradients. For a <paramref name="permutationTableSize"/> of 256, µ should be in /// the range [1, 1.16]. /// </param> public ProceduralTerrainCreator(int seed, int permutationTableSize, float mu) { if (!MathHelper.IsPowerOf2(permutationTableSize)) throw new ArgumentException("The permutation table size must be a power of 2 (e.g. 256)."); _maskB = permutationTableSize - 1; var random = new Random(seed); // Create table of random gradient vectors (normalized). _gradients = new Vector2F[permutationTableSize]; for (int i = 0; i < _gradients.Length; i++) { var direction = random.NextVector2F(-1, 1); if (!direction.TryNormalize()) direction = new Vector2F(1, 0); _gradients[i] = direction; } // Create table with a permutation of the values 0 to permutationTableSize. _permutations = new int[permutationTableSize]; for (int i = 0; i < _permutations.Length; i++) _permutations[i] = i; for (int i = _permutations.Length - 1; i > 0; i--) MathHelper.Swap(ref _permutations[i], ref _permutations[random.NextInteger(0, i)]); // Create table with gradient magnitudes. _magnitudes = new float[permutationTableSize]; float s = 1; // First magnitude. for (int i = 0; i < _magnitudes.Length; i++) { _magnitudes[i] = s; s /= mu; } }
private float GetConcavity(int vertexLimit, bool sampleVertices, bool sampleCenters) { // Initially we assume that the new vertices are simply the union of the islands' vertices. Vertices = IslandA.Vertices.Union(IslandB.Vertices).ToArray(); try { // Create hull mesh. // Incremental hull building. // Note: Commented out because this building the hull this way is less stable. //bool rebuild = true; //try //{ // if (IslandA.ConvexHullBuilder != null) // { // if (IslandB.ConvexHullBuilder == null || IslandA.Vertices.Length > IslandB.Vertices.Length) // { // ConvexHullBuilder = IslandA.ConvexHullBuilder.Clone(); // ConvexHullBuilder.Grow(IslandB.Vertices, vertexLimit, 0); // } // else // { // ConvexHullBuilder = IslandB.ConvexHullBuilder.Clone(); // ConvexHullBuilder.Grow(IslandA.Vertices, vertexLimit, 0); // } // rebuild = false; // } // else if (IslandB.ConvexHullBuilder != null) // { // ConvexHullBuilder = IslandB.ConvexHullBuilder.Clone(); // ConvexHullBuilder.Grow(IslandA.Vertices, vertexLimit, 0); // rebuild = false; // } //} //catch (GeometryException) //{ // rebuild = true; //} //if (rebuild) { try { ConvexHullBuilder = new ConvexHullBuilder(); ConvexHullBuilder.Grow(Vertices, vertexLimit, 0); } catch (GeometryException) { // Hull building failed. Try again with a randomized order. var random = new Random(1234567); // Fisher-Yates shuffle: for (int i = Vertices.Length - 1; i >= 1; i--) { var v = Vertices[i]; var j = random.NextInteger(0, i); Vertices[i] = Vertices[j]; Vertices[j] = v; } } } var hullMesh = ConvexHullBuilder.Mesh.ToTriangleMesh(); // Now, we have a reduced set of vertices. Vertices = hullMesh.Vertices.ToArray(); // For larger meshes we create an AabbTree as acceleration structure. AabbTree<int> partition = null; if (hullMesh.NumberOfTriangles > 12) { partition = new AabbTree<int> { GetAabbForItem = i => hullMesh.GetTriangle(i).Aabb, BottomUpBuildThreshold = 0, }; for (int i = 0; i < hullMesh.NumberOfTriangles; i++) partition.Add(i); partition.Update(true); } Aabb aabb = Aabb; float aabbExtent = aabb.Extent.Length; // Note: For a speed-up we could skip some ray tests in the next loop and only sample // a few vertices if there would be a lot of tests. // The next loop performs ray casts against the hull mesh to determine the maximum // concavity. We ensure that we make only one ray cast per vertex even if a vertex // is shared by many triangles. float maxConcavity = 0; foreach (var triangle in IslandA.Triangles.Union(IslandB.Triangles)) { if (sampleVertices) { for (int i = 0; i < triangle.Vertices.Length; i++) { // Each vertex can be shared by several triangles of the current islands. // Therefore, we check the edges that contain this vertex. If an edge neighbor // is in the same island, we make sure that the vertex concavity is computed only once // in the triangle with the smallest Id. var neighbor0 = triangle.Neighbors[(i + 1) % 3]; var neighbor1 = triangle.Neighbors[(i + 2) % 3]; if (neighbor0 != null && (neighbor0.Island == IslandA || neighbor0.Island == IslandB) && triangle.Id > neighbor0.Id) { // No need to test: The neighbor is in the same islands and this triangle Id is larger. continue; } if (neighbor1 != null && (neighbor1.Island == IslandA || neighbor1.Island == IslandB) && triangle.Id > neighbor1.Id) { // No need to test: The neighbor is in the same islands and this triangle Id is larger. continue; } var position = triangle.Vertices[i]; var normal = triangle.VertexNormals[i]; // Degenerate triangles are ignored. if (normal.IsNumericallyZero) continue; // Shoot a ray from outside the hull mesh to the vertex. float hitDistance; Vector3F rayOrigin = position + normal * aabbExtent; float rayLength = (position - rayOrigin).Length; var ray = new Ray(rayOrigin, -normal, rayLength); if (partition != null) { // Use AABB tree for better performance. foreach (var triangleIndex in partition.GetOverlaps(ray)) { var candidateTriangle = hullMesh.GetTriangle(triangleIndex); var hit = GeometryHelper.GetContact(ray, candidateTriangle, false, out hitDistance); if (hit) { // The concavity is the distance from the hull to the vertex. float concavity = rayLength - hitDistance; maxConcavity = Math.Max(maxConcavity, concavity); break; } } } else { // No AABB tree. var hit = GeometryHelper.GetContact(hullMesh, ray, out hitDistance); if (hit) { float concavity = rayLength - hitDistance; maxConcavity = Math.Max(maxConcavity, concavity); } } } } if (sampleCenters) { // Test: Also shoot from the triangle centers. var center = (triangle.Vertices[0] + triangle.Vertices[1] + triangle.Vertices[2]) / 3; var normal = triangle.Normal; // Degenerate triangles are ignored. if (normal.IsNumericallyZero) continue; // Shoot a ray from outside the hull mesh to the vertex. float hitDistance; Vector3F rayOrigin = center + normal * aabbExtent; float rayLength = (center - rayOrigin).Length; var ray = new Ray(rayOrigin, -normal, rayLength); if (partition != null) { // Use AABBTree for better performance. foreach (var triangleIndex in partition.GetOverlaps(ray)) { var candidateTriangle = hullMesh.GetTriangle(triangleIndex); var hit = GeometryHelper.GetContact(ray, candidateTriangle, false, out hitDistance); if (hit) { // The concavity is the distance from the hull to the vertex. float concavity = rayLength - hitDistance; maxConcavity = Math.Max(maxConcavity, concavity); break; } } } else { // No AABBTree. var hit = GeometryHelper.GetContact(hullMesh, ray, out hitDistance); if (hit) { float concavity = rayLength - hitDistance; maxConcavity = Math.Max(maxConcavity, concavity); } } } } return maxConcavity; } catch (GeometryException) { // Ouch, the convex hull generation failed. This can happen for degenerate inputs // and numerical problems in the convex hull builder. ConvexHullBuilder = null; return 0; } }
public CollisionFilterSample(Microsoft.Xna.Framework.Game game) : base(game) { SampleFramework.IsMouseVisible = false; GraphicsScreen.ClearBackground = true; GraphicsScreen.BackgroundColor = Color.Gray; GraphicsScreen.DrawReticle = true; SetCamera(new Vector3F(0, 0, 10), 0, 0); // ----- Initialize collision detection system. // We use one collision domain that manages all objects. var domain = new CollisionDomain(); // Let's set a filter which disables collision between object in the same collision group. // We can use a broad phase or a narrow phase filter: // Option A) Broad phase filter // The collision detection broad phase computes bounding box overlaps. // A broad phase filter is best used if the filtering rules are simple and do not change // during the runtime of your application. //domain.BroadPhase.Filter = new DelegatePairFilter<CollisionObject>( // pair => pair.First.CollisionGroup != pair.Second.CollisionGroup); // Option B) Narrow phase filter // The collision detection narrow phase computes contacts between objects where the broad // phase has detected a bounding box overlap. Use a narrow phase filter if the filtering rules // are complex or can change during the runtime of your application. var filter = new CollisionFilter(); // Disable collision between objects in the same groups. filter.Set(0, 0, false); filter.Set(1, 1, false); filter.Set(2, 2, false); filter.Set(3, 3, false); domain.CollisionDetection.CollisionFilter = filter; // Create a random list of points. var points = new List<Vector3F>(); for (int i = 0; i < 100; i++) points.Add(RandomHelper.Random.NextVector3F(-1.5f, 1.5f)); // Add lots of spheres to the collision domain. Assign spheres to different collision groups. var random = new Random(); var sphereShape = new SphereShape(0.7f); for (int i = 0; i < 20; i++) { var randomPosition = new Vector3F(random.NextFloat(-6, 6), random.NextFloat(-3, 3), 0); var geometricObject = new GeometricObject(sphereShape, new Pose(randomPosition)); var collisionObject = new CollisionObject(geometricObject) { // A collision group is simply an integer. We can assign collision objects to collision // groups to control the collision filtering. CollisionGroup = random.NextInteger(0, 3), }; domain.CollisionObjects.Add(collisionObject); } // Compute collisions. (The objects do not move in this sample. Therefore, we only have to // call Update once.) domain.Update(0); // Draw objects. The color depends on the collision group. var debugRenderer = GraphicsScreen.DebugRenderer; debugRenderer.Clear(); foreach (var collisionObject in domain.CollisionObjects) { Color color; switch (collisionObject.CollisionGroup) { case 0: color = Color.LightBlue; break; case 1: color = Color.Yellow; break; case 2: color = Color.Orange; break; default: color = Color.LightGreen; break; } debugRenderer.DrawObject(collisionObject.GeometricObject, color, false, false); } debugRenderer.DrawContacts(domain.ContactSets, 0.1f, Color.Red, true); }
public void RandomizedMassSceneUpdate2() { const int NumberOfSceneNodes = 10; const int NumberOfSteps = 10000; const float WorldSize = 1000; var random = new Random(123457); var scene = new Scene(); scene.EnableMultithreading = false; var nodes = new TestSceneNode[NumberOfSceneNodes]; // Create random nodes. for (int i = 0; i < NumberOfSceneNodes; i++) { var node = new TestSceneNode(); nodes[i] = node; var position = random.NextVector3F(0, WorldSize); var orientation = random.NextQuaternionF(); node.PoseLocal = new Pose(position, orientation); float p = random.NextFloat(0, 100); if (p < 0.1f) node.Shape = Shape.Empty; else if (p < 0.2f) node.Shape = Shape.Infinite; else if (p < 0.6f) node.Shape = new BoxShape(random.NextVector3F(0, WorldSize)); else node.Shape = new SphereShape(random.NextFloat(0, WorldSize)); } var projection = new PerspectiveProjection(); projection.SetFieldOfView(0.8f, 1, WorldSize / 10000, WorldSize); var camera = new Camera(projection); var cameraNode = new CameraNode(camera); for (int updateIndex = 0; updateIndex < NumberOfSteps; updateIndex++) { int actionsPerFrame = random.NextInteger(0, 100); for (int i = 0; i < actionsPerFrame; i++) { var node = nodes[random.Next(0, NumberOfSceneNodes)]; const int numberOfActions = 100; int action = random.Next(0, numberOfActions); //scene.Validate(); if (action == 0) { // Add if (node.Parent == null) { scene.Children.Add(node); //scene.Validate(); } } else if (action == 1) { // Remove if (node.Parent != null) { node.Parent.Children.Remove(node); //scene.Validate(); } } else if (action == 2) { // Move var pose = node.PoseWorld; pose.Position = random.NextVector3F(0, WorldSize); node.PoseWorld = pose; //scene.Validate(); } else if (action == 3) { // Very small Move var pose = node.PoseWorld; const float maxDistance = WorldSize / 10000; pose.Position += random.NextVector3F(-maxDistance, maxDistance); node.PoseWorld = pose; //scene.Validate(); } else if (action == 4) { // Small Move var pose = node.PoseWorld; const float maxDistance = WorldSize / 100; pose.Position += random.NextVector3F(-maxDistance, maxDistance); node.PoseWorld = pose; //scene.Validate(); } else if (action == 5) { // Scale node.ScaleLocal = random.NextVector3F(0.0f, 10f); //scene.Validate(); } else if (action == 6) { // Rotate node.PoseWorld = new Pose(node.PoseWorld.Position, random.NextQuaternionF()); //scene.Validate(); } else if (action == 7) { // Query var query = scene.Query<CameraFrustumQuery>(cameraNode, null); //Debug.WriteLine("Camera queried nodes: " + query.SceneNodes.Count); //scene.Validate(); } else if (action == 8) { // Move camera. cameraNode.PoseWorld = new Pose(random.NextVector3F(0, WorldSize), random.NextQuaternionF()); //scene.Validate(); } else if (action == 9) { // Change shape. int type = random.NextInteger(0, 5); if (type == 0) node.Shape = new BoxShape(random.NextVector3F(0, WorldSize)); else if (type == 1) node.Shape = new SphereShape(random.NextFloat(0, WorldSize)); else if (type == 2) node.Shape = new BoxShape(new Vector3F(Single.MaxValue)); else if (type == 3) node.Shape = new BoxShape(new Vector3F(0)); else if (type == 4) node.Shape = Shape.Empty; //scene.Validate(); } else if (action == 10) { // Add to random parent. if (node.Parent == null) { var randomParent = nodes[random.NextInteger(0, NumberOfSceneNodes - 1)]; // Avoid loops: bool isLoop = node.GetSubtree().Contains(randomParent); if (!isLoop) { if (randomParent.Children == null) randomParent.Children = new SceneNodeCollection(); randomParent.Children.Add(node); } } } else if (action == 11) { if (node.Parent != null && node.Parent != scene) node.Parent.Children.Remove(node); } else if (action == 13) { if (random.NextInteger(0, 100) < 5) scene.Children.Clear(); } else if (action == 14) { if (random.NextInteger(0, 100) < 5) scene.Children = new SceneNodeCollection(); } } //Debug.WriteLine("Number of nodes in scene: " + scene.GetDescendants().Count()); //scene.Validate(); scene.Update(TimeSpan.FromSeconds(0.016666666f)); //scene.Validate(); } }