Skip to content

Commit

Permalink
Feature/better hover detection (#24)
Browse files Browse the repository at this point in the history
* Better hover detection, unoptimised

* optimised by caching the matrix

* cleanup

* formatting
  • Loading branch information
naam00 authored Jul 22, 2024
1 parent 0a2d5e2 commit f3466fb
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 2 deletions.
34 changes: 32 additions & 2 deletions Assets/Scripts/model/core/MMesh.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ public partial class MMesh
/// </summary>
public Bounds bounds { get; private set; }

/// <summary>
/// Bounds of this mesh in local Mesh coordinates.
/// </summary>
public Bounds localBounds { get; private set; }

/// <summary>
/// ID of the group to which this mesh belongs, or GROUP_NONE if this mesh is ungrouped.
/// Meshes with the same groupId belong to the same group and stay together during
Expand Down Expand Up @@ -139,7 +144,7 @@ public MMesh(int id, Vector3 offset, Quaternion rotation,

public MMesh(int id, Vector3 offset, Quaternion rotation,
Dictionary<int, Vertex> verticesById, Dictionary<int, Face> facesById,
Bounds bounds, Dictionary<int, HashSet<int>> reverseTable, int groupId = GROUP_NONE,
Bounds bounds, Bounds localBounds, Dictionary<int, HashSet<int>> reverseTable, int groupId = GROUP_NONE,
HashSet<string> remixIds = null)
{
_id = id;
Expand All @@ -153,6 +158,7 @@ public MMesh(int id, Vector3 offset, Quaternion rotation,
this.verticesById = verticesById;
this.facesById = facesById;
this.bounds = bounds;
this.localBounds = localBounds;
this.reverseTable = reverseTable;
this.groupId = groupId;
this.remixIds = remixIds;
Expand Down Expand Up @@ -186,6 +192,7 @@ public MMesh Clone()
verticesCloned,
facesCloned,
bounds,
localBounds,
reverseTableCloned,
groupId,
remixIdsCloned);
Expand Down Expand Up @@ -220,6 +227,7 @@ public MMesh CloneWithNewIdAndGroup(int newId, int newGroupId)
new Dictionary<int, Vertex>(verticesById),
facesCloned,
bounds,
localBounds,
new Dictionary<int, HashSet<int>>(reverseTable),
newGroupId,
remixIds == null ? null : new HashSet<string>(remixIds));
Expand Down Expand Up @@ -300,12 +308,21 @@ private void FinishOperation()
/// <summary>
/// Get the bounds for this mesh.
/// </summary>
/// <returns>The bounds, in model coordinates.</returns>
/// <returns>The bounds, in model (ie: world) coordinates.</returns>
public Bounds GetBounds()
{
return bounds;
}

/// <summary>
/// Get the local bounds for this mesh.
/// </summary>
/// <returns>The bounds, in mesh coordinates.</returns>
public Bounds GetLocalBounds()
{
return localBounds;
}

/// <summary>
/// Calculates and returns the bounds of a face in this mesh, in model space.
/// </summary>
Expand Down Expand Up @@ -462,19 +479,32 @@ public void RecalcBounds()
// is an extreme hotspot.
float minX = float.MaxValue, minY = float.MaxValue, minZ = float.MaxValue;
float maxX = float.MinValue, maxY = float.MinValue, maxZ = float.MinValue;
float minXL = float.MaxValue, minYL = float.MaxValue, minZL = float.MaxValue;
float maxXL = float.MinValue, maxYL = float.MinValue, maxZL = float.MinValue;
foreach (Vertex vert in verticesById.Values)
{
Vector3 loc = MeshCoordsToModelCoords(vert.loc);
Vector3 locL = vert.loc;
minX = Mathf.Min(minX, loc.x);
minY = Mathf.Min(minY, loc.y);
minZ = Mathf.Min(minZ, loc.z);
maxX = Mathf.Max(maxX, loc.x);
maxY = Mathf.Max(maxY, loc.y);
maxZ = Mathf.Max(maxZ, loc.z);

minXL = Mathf.Min(minXL, locL.x);
minYL = Mathf.Min(minYL, locL.y);
minZL = Mathf.Min(minZL, locL.z);
maxXL = Mathf.Max(maxXL, locL.x);
maxYL = Mathf.Max(maxYL, locL.y);
maxZL = Mathf.Max(maxZL, locL.z);
}
bounds = new Bounds(
/* center */ new Vector3((minX + maxX) / 2, (minY + maxY) / 2, (minZ + maxZ) / 2),
/* size */ new Vector3(maxX - minX, maxY - minY, maxZ - minZ));
localBounds = new Bounds(
/* center */ new Vector3((minXL + maxXL) / 2, (minYL + maxYL) / 2, (minZL + maxZL) / 2),
/* size */ new Vector3(maxXL - minXL, maxYL - minYL, maxZL - minZL));
}

/// <summary>
Expand Down
30 changes: 30 additions & 0 deletions Assets/Scripts/model/core/SpatialIndex.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ public class SpatialIndex
private Dictionary<FaceKey, FaceInfo> faceInfo;
private Dictionary<EdgeKey, EdgeInfo> edgeInfo;

private Dictionary<int, Matrix4x4> worldToLocalMatrices;
private Dictionary<int, Bounds> meshBoundsLocal;

// A reference to the model, which is the single point of truth as to whether an item exists, despite the fact
// that this spatial index contains collections of meshes and other items.
// The reason we wish to treat the model as a single point of truth is that changes to the model happen on the
Expand Down Expand Up @@ -115,6 +118,9 @@ private void Setup(Bounds bounds)

faceInfo = new Dictionary<FaceKey, FaceInfo>();
edgeInfo = new Dictionary<EdgeKey, EdgeInfo>();

worldToLocalMatrices = new Dictionary<int, Matrix4x4>();
meshBoundsLocal = new Dictionary<int, Bounds>();
}

/// <summary>
Expand Down Expand Up @@ -174,6 +180,14 @@ public void RemoveMesh(MMesh mesh)
{
meshBounds.Remove(mesh.id);
}
if (worldToLocalMatrices.ContainsKey(mesh.id))
{
worldToLocalMatrices.Remove(mesh.id);
}
if (meshBoundsLocal.ContainsKey(mesh.id))
{
meshBoundsLocal.Remove(mesh.id);
}
if (meshes.HasItem(mesh.id))
{
meshes.Remove(mesh.id);
Expand Down Expand Up @@ -249,6 +263,7 @@ public bool FindMeshesClosestTo(Vector3 point, float radius, out List<DistancePa
public bool FindNearestMeshTo(Vector3 point, float radius, out int? nearestMesh,
bool ignoreHiddenMeshes = false)
{
Vector4 point4 = new Vector4(point.x, point.y, point.z, 1); // we need this for the matrix multiplication later
Bounds searchBounds = new Bounds(point, Vector3.one * radius * 2);
nearestMesh = null;
HashSet<int> meshIds;
Expand Down Expand Up @@ -277,6 +292,17 @@ public bool FindNearestMeshTo(Vector3 point, float radius, out int? nearestMesh,
{
continue;
}

// we've probed the world-aligned bounds, but meshes can be rotated. So we check now
// if the point lies within the rotated and offset bounds.
Bounds localBounds = meshBoundsLocal[meshId];
Vector3 localPoint = worldToLocalMatrices[meshId] * point4;
if (!localBounds.Contains(localPoint))
{
continue;
}


float distanceToPoint = Vector3.Distance(bounds.center, point);
if (distanceToPoint < minDistance)
{
Expand Down Expand Up @@ -846,6 +872,10 @@ private void LoadMeshIntoIndex(MMesh mesh,
}
meshes.Add(mesh.id, mesh.bounds);
meshBounds.Add(mesh.id, mesh.bounds);

worldToLocalMatrices.Add(mesh.id, Matrix4x4.TRS(mesh.offset, mesh.rotation, Vector3.one).inverse);
meshBoundsLocal.Add(mesh.id, mesh.localBounds);

foreach (KeyValuePair<FaceKey, FaceInfo> pair in faceInfos)
{
faces.Add(pair.Key, pair.Value.bounds);
Expand Down

0 comments on commit f3466fb

Please sign in to comment.