Skip to content

Commit

Permalink
Merge pull request #193 from notgiven688/np
Browse files Browse the repository at this point in the history
  • Loading branch information
notgiven688 authored Oct 20, 2024
2 parents ed4912f + 3edfb6f commit 3f61a42
Show file tree
Hide file tree
Showing 23 changed files with 246 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@
using Jitter2.Collision.Shapes;
using Jitter2.LinearMath;

namespace Jitter2;
namespace Jitter2.Collision;

public partial class World
public partial class DynamicTree
{
/// <summary>
/// Preliminary result of the ray cast.
Expand Down Expand Up @@ -75,8 +75,6 @@ public Ray(in JVector origin, in JVector direction)
}
}

[ThreadStatic] private static Stack<int>? stack;

/// <summary>
/// Ray cast against the world.
/// </summary>
Expand Down Expand Up @@ -123,11 +121,11 @@ public bool RayCast(JVector origin, JVector direction, float maxFraction, RayCas

private RayCastResult QueryRay(in Ray ray)
{
if (DynamicTree.Root == -1) return new RayCastResult();
if (root == -1) return new RayCastResult();

stack ??= new Stack<int>(256);

stack.Push(DynamicTree.Root);
stack.Push(root);

RayCastResult result = new();
result.Fraction = ray.Lambda;
Expand All @@ -136,7 +134,7 @@ private RayCastResult QueryRay(in Ray ray)
{
int pop = stack.Pop();

ref DynamicTree.Node node = ref DynamicTree.Nodes[pop];
ref Node node = ref Nodes[pop];

if (node.IsLeaf)
{
Expand All @@ -157,8 +155,8 @@ private RayCastResult QueryRay(in Ray ray)
continue;
}

ref DynamicTree.Node lnode = ref DynamicTree.Nodes[node.Left];
ref DynamicTree.Node rnode = ref DynamicTree.Nodes[node.Right];
ref Node lnode = ref Nodes[node.Left];
ref Node rnode = ref Nodes[node.Right];

bool lres = lnode.ExpandedBox.RayIntersect(ray.Origin, ray.Direction, out float enterl);
bool rres = rnode.ExpandedBox.RayIntersect(ray.Origin, ray.Direction, out float enterr);
Expand Down
2 changes: 1 addition & 1 deletion src/Jitter2/Collision/DynamicTree.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ namespace Jitter2.Collision;
/// Represents a dynamic Axis Aligned Bounding Box (AABB) tree. A hashset (refer to <see cref="PairHashSet"/>)
/// maintains a record of potential overlapping pairs.
/// </summary>
public class DynamicTree
public partial class DynamicTree
{
private volatile SlimBag<IDynamicTreeProxy>[] lists = Array.Empty<SlimBag<IDynamicTreeProxy>>();

Expand Down
7 changes: 0 additions & 7 deletions src/Jitter2/Dynamics/Contact.cs
Original file line number Diff line number Diff line change
Expand Up @@ -293,8 +293,6 @@ public struct Contact
public enum Flags
{
NewContact = 1 << 1,
Body1Static = 1 << 2,
Body2Static = 1 << 3
}

public Flags Flag;
Expand Down Expand Up @@ -346,8 +344,6 @@ public void Initialize(ref RigidBodyData b1, ref RigidBodyData b2, in JVector po
if (newContact)
{
Flag = Flags.NewContact;
if (b1.IsStatic || !b1.IsActive) Flag |= Flags.Body1Static;
if (b2.IsStatic || !b2.IsActive) Flag |= Flags.Body2Static;

AccumulatedNormalImpulse = 0;
AccumulatedTangentImpulse1 = 0;
Expand Down Expand Up @@ -421,9 +417,6 @@ public unsafe void PrepareForIteration(ContactData* cd, float idt)
ref var b2 = ref cd->Body2.Data;
bool speculative = cd->IsSpeculative;

if ((Flag & Flags.NewContact) != 0) Flag = Flags.NewContact;
else Flag = 0;

// redundant if contact has just been initialized or updateposition was called
// before <=> it is redundant the first time it is called in world.step <=> it is
// redundant if no sub-stepping is used. but it does not seem to slow anything down,
Expand Down
7 changes: 7 additions & 0 deletions src/Jitter2/Dynamics/RigidBody.cs
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,15 @@ public TimeSpan DeactivationTime
/// </summary>
public (float angular, float linear) DeactivationThreshold
{
get => (MathF.Sqrt(inactiveThresholdAngularSq), MathF.Sqrt(inactiveThresholdLinearSq));
set
{
if (value.linear < 0 || value.angular < 0)
{
throw new ArgumentOutOfRangeException(nameof(value),
"Both linear and angular thresholds must be non-negative.");
}

inactiveThresholdLinearSq = value.linear * value.linear;
inactiveThresholdAngularSq = value.angular * value.angular;
}
Expand Down
12 changes: 11 additions & 1 deletion src/Jitter2/UnmanagedMemory/UnmanagedActiveList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ internal static JHandle<K> AsHandle<K>(JHandle<T> handle) where K : unmanaged
/// </summary>
public sealed unsafe class UnmanagedActiveList<T> : IDisposable where T : unmanaged
{
// this is a mixture of a datastructure and an allocator.
// this is a mixture of a data structure and an allocator.

// layout:
// 0 [ .... ] active [ .... ] count [ .... ] size
Expand Down Expand Up @@ -95,6 +95,11 @@ public UnmanagedActiveList(int maximumSize, int initialSize = 1024)
}
}

/// <summary>
/// Returns the total amount of unmanaged memory allocated in bytes.
/// </summary>
public long TotalBytesAllocated => size * sizeof(T) + maximumSize * sizeof(IntPtr);

/// <summary>
/// Removes the associated native structure from the data structure.
/// </summary>
Expand All @@ -118,6 +123,11 @@ public void Free(JHandle<T> handle)
/// </summary>
public Span<T> Active => new(memory, active);

/// <summary>
/// A span for all elements marked as inactive.
/// </summary>
public Span<T> Inactive => new(&memory[active], Count - active);

/// <summary>
/// A span for all elements.
/// </summary>
Expand Down
156 changes: 156 additions & 0 deletions src/Jitter2/World.Capacity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
using System;
using System.Net;

namespace Jitter2;

public partial class World
{
public struct Capacity : IEquatable<Capacity>
{
// Offsets for customizable counts
private int bodyCountOffset;
private int contactCountOffset;
private int constraintCountOffset;
private int smallConstraintCountOffset;

// Default values for each property
public const int DefaultBodyCount = 32768;
public const int DefaultContactCount = 65536;
public const int DefaultConstraintCount = 32768;
public const int DefaultSmallConstraintCount = 32768;

/// <summary>
/// Gets or sets the total number of bodies.
/// The value will always be at least <see cref="DefaultBodyCount"/>.
/// </summary>
public int BodyCount
{
get => DefaultBodyCount + bodyCountOffset;
set
{
if (value <= 0)
throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(value)} must be a positive integer.");
bodyCountOffset = value - DefaultBodyCount;
}
}

/// <summary>
/// Uses the default values for world creation.
/// </summary>
/// <remarks>
/// The default values are:
/// <list type="bullet">
/// <item>BodyCount: 32,768</item>
/// <item>ContactCount: 65,536</item>
/// <item>ConstraintCount: 32,768</item>
/// <item>SmallConstraintCount: 32,768</item>
/// </list>
/// </remarks>
public static Capacity Default => new Capacity();

/// <summary>
/// Gets or sets the total number of contacts.
/// </summary>
public int ContactCount
{
get => DefaultContactCount + contactCountOffset;
set
{
if (value <= 0)
throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(value)} must be a positive integer.");
contactCountOffset = value - DefaultContactCount;
}
}

/// <summary>
/// Gets or sets the total number of constraints.
/// </summary>
public int ConstraintCount
{
get => DefaultConstraintCount + constraintCountOffset;
set
{
if (value <= 0)
throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(value)} must be a positive integer.");
constraintCountOffset = value - DefaultConstraintCount;
}
}

/// <summary>
/// Gets or sets the total number of small constraints.
/// </summary>
public int SmallConstraintCount
{
get => DefaultSmallConstraintCount + smallConstraintCountOffset;
set
{
if (value <= 0)
throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(value)} must be a positive integer.");
smallConstraintCountOffset = value - DefaultSmallConstraintCount;
}
}

/// <summary>
/// Returns a string representation of the <see cref="Capacity"/>.
/// </summary>
/// <returns>A string that represents the current state of the <see cref="Capacity"/>.</returns>
public override string ToString()
{
return $"BodyCount: {BodyCount}, ContactCount: {ContactCount}, ConstraintCount: {ConstraintCount}, SmallConstraintCount: {SmallConstraintCount}";
}

/// <summary>
/// Determines whether the specified <see cref="Capacity"/> is equal to the current <see cref="Capacity"/>.
/// </summary>
/// <param name="other">The <see cref="Capacity"/> to compare with the current <see cref="Capacity"/>.</param>
/// <returns><c>true</c> if the specified <see cref="Capacity"/> is equal to the current <see cref="Capacity"/>; otherwise, <c>false</c>.</returns>
public bool Equals(Capacity other)
{
return BodyCount == other.BodyCount &&
ContactCount == other.ContactCount &&
ConstraintCount == other.ConstraintCount &&
SmallConstraintCount == other.SmallConstraintCount;
}

/// <summary>
/// Determines whether the specified object is equal to the current <see cref="Capacity"/>.
/// </summary>
/// <param name="obj">The object to compare with the current <see cref="Capacity"/>.</param>
/// <returns><c>true</c> if the specified object is equal to the current <see cref="Capacity"/>; otherwise, <c>false</c>.</returns>
public override bool Equals(object? obj)
{
return obj is Capacity other && Equals(other);
}

/// <summary>
/// Returns a hash code for the current <see cref="Capacity"/>.
/// </summary>
/// <returns>A hash code for the current <see cref="Capacity"/>.</returns>
public override int GetHashCode()
{
return HashCode.Combine(BodyCount, ContactCount, ConstraintCount, SmallConstraintCount);
}

/// <summary>
/// Compares two <see cref="Capacity"/> instances for equality.
/// </summary>
/// <param name="left">The left instance to compare.</param>
/// <param name="right">The right instance to compare.</param>
/// <returns><c>true</c> if the two instances are equal; otherwise, <c>false</c>.</returns>
public static bool operator ==(Capacity left, Capacity right)
{
return left.Equals(right);
}

/// <summary>
/// Compares two <see cref="Capacity"/> instances for inequality.
/// </summary>
/// <param name="left">The left instance to compare.</param>
/// <param name="right">The right instance to compare.</param>
/// <returns><c>true</c> if the two instances are not equal; otherwise, <c>false</c>.</returns>
public static bool operator !=(Capacity left, Capacity right)
{
return !(left == right);
}
}
}
41 changes: 30 additions & 11 deletions src/Jitter2/World.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,29 @@ public SpanData(World world)
this.world = world;
}

/// <summary>
/// Returns the total amount of unmanaged memory allocated in bytes.
/// </summary>
public long TotalBytesAllocated =>
world.memRigidBodies.TotalBytesAllocated +
world.memContacts.TotalBytesAllocated +
world.memConstraints.TotalBytesAllocated +
world.memSmallConstraints.TotalBytesAllocated;

public readonly Span<RigidBodyData> ActiveRigidBodies => world.memRigidBodies.Active;
public readonly Span<RigidBodyData> InactiveRigidBodies => world.memRigidBodies.Inactive;
public readonly Span<RigidBodyData> RigidBodies => world.memRigidBodies.Elements;

public readonly Span<ContactData> ActiveContacts => world.memContacts.Active;
public readonly Span<ContactData> InactiveContacts => world.memContacts.Inactive;
public readonly Span<ContactData> Contacts => world.memContacts.Elements;

public readonly Span<ConstraintData> ActiveConstraints => world.memConstraints.Active;
public readonly Span<ConstraintData> InactiveConstraints => world.memConstraints.Inactive;
public readonly Span<ConstraintData> Constraints => world.memConstraints.Elements;

public readonly Span<SmallConstraintData> ActiveSmallConstraints => world.memSmallConstraints.Active;
public readonly Span<SmallConstraintData> InactiveSmallConstraints => world.memSmallConstraints.Inactive;
public readonly Span<SmallConstraintData> SmallConstraints => world.memSmallConstraints.Elements;
}

Expand Down Expand Up @@ -143,7 +156,7 @@ public static (ulong min, ulong max) RequestId(int count)
/// Access to the <see cref="DynamicTree"/> instance. The instance
/// should only be modified by Jitter.
/// </summary>
public readonly DynamicTree DynamicTree;
public DynamicTree DynamicTree { get; }

/// <summary>
/// A fixed body, pinned to the world. Can be used to create constraints with.
Expand All @@ -157,7 +170,7 @@ public static (ulong min, ulong max) RequestId(int count)
public bool AllowDeactivation { get; set; } = true;

/// <summary>
/// Number of iterations per substep (see <see cref="World.NumberSubsteps"/>).
/// Number of iterations per substep (see <see cref="SubstepCount"/>).
/// </summary>
/// <value></value>
public int SolverIterations
Expand All @@ -179,7 +192,7 @@ public int SolverIterations
/// The number of substeps for each call to <see cref="World.Step(float, bool)"/>.
/// Substepping is deactivated when set to one.
/// </summary>
public int NumberSubsteps
public int SubstepCount
{
get => substeps;
set
Expand Down Expand Up @@ -220,18 +233,24 @@ public JVector Gravity
/// </summary>
public bool UseFullEPASolver { get; set; }

/// <summary>
/// Creates an instance of the <see cref="World"/> class with the default capacity.
/// This initializes the world using default values for the number of bodies, contacts,
/// constraints, and small constraints as defined in <see cref="Capacity.Default"/>.
/// </summary>
/// <seealso cref="World(Capacity)"/>
public World() : this(Capacity.Default) { }

/// <summary>
/// Creates an instance of the World class. As Jitter utilizes a distinct memory model, it is necessary to specify
/// the maximum number of instances for <see cref="RigidBody"/>, <see cref="ContactData"/>, and <see cref="Constraint"/>.
/// the capacity of the world in advance.
/// </summary>
public World(int numBodies = 32768, int numContacts = 65536, int numConstraints = 32768)
public World(Capacity capacity)
{
// int numBodies = 32768, int numContacts = 65536, int numConstraints = 32768
// with this choice (32768 + 65536 + 2 x 32768) x 8 Bytes = 1280 KB are allocated on the heap.
memRigidBodies = new UnmanagedActiveList<RigidBodyData>(numBodies);
memContacts = new UnmanagedActiveList<ContactData>(numContacts);
memConstraints = new UnmanagedActiveList<ConstraintData>(numConstraints);
memSmallConstraints = new UnmanagedActiveList<SmallConstraintData>(numConstraints);
memRigidBodies = new UnmanagedActiveList<RigidBodyData>(capacity.BodyCount);
memContacts = new UnmanagedActiveList<ContactData>(capacity.ContactCount);
memConstraints = new UnmanagedActiveList<ConstraintData>(capacity.ConstraintCount);
memSmallConstraints = new UnmanagedActiveList<SmallConstraintData>(capacity.SmallConstraintCount);

InitParallelCallbacks();

Expand Down
Loading

0 comments on commit 3f61a42

Please sign in to comment.