Skip to content

Commit 95841d3

Browse files
committed
Update server_raycast.h
1 parent 8a308c3 commit 95841d3

File tree

1 file changed

+81
-64
lines changed

1 file changed

+81
-64
lines changed

Network/server_raycast.h

Lines changed: 81 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -44,33 +44,39 @@ inline Float3 Scale(const Float3& v, float s)
4444
}
4545

4646
//-----------------------------------------------------------------------------
47-
// ClosestPointOnSegment — returns t in [0,1]
47+
// RaySphere — test ray against a sphere
48+
//
49+
// Returns true if hit, outT = entry distance along ray.
4850
//-----------------------------------------------------------------------------
49-
inline float ClosestTOnSegment(const Float3& segA, const Float3& segB,
50-
const Float3& point)
51+
inline bool RaySphere(const Float3& origin, const Float3& dir,
52+
const Float3& center, float radius, float& outT)
5153
{
52-
Float3 ab = Sub(segB, segA);
53-
Float3 ap = Sub(point, segA);
54-
float denom = Dot(ab, ab);
55-
if (denom < 1e-8f) return 0.0f;
56-
float t = Dot(ap, ab) / denom;
57-
if (t < 0.0f) t = 0.0f;
58-
if (t > 1.0f) t = 1.0f;
59-
return t;
54+
Float3 oc = Sub(origin, center);
55+
float a = Dot(dir, dir);
56+
float h = Dot(oc, dir);
57+
float c = Dot(oc, oc) - radius * radius;
58+
float disc = h * h - a * c;
59+
if (disc < 0.0f) return false;
60+
float sqrtDisc = sqrtf(disc);
61+
float t = (-h - sqrtDisc) / a;
62+
if (t < 0.0f) t = (-h + sqrtDisc) / a;
63+
if (t < 0.0f) return false;
64+
outT = t;
65+
return true;
6066
}
6167

6268
//-----------------------------------------------------------------------------
63-
// RayCapsule — test ray against a capsule (two hemispheres + cylinder)
69+
// RayCapsule — test ray against a capsule (cylinder + two hemispheres)
6470
//
65-
// Returns true if the ray hits the capsule, and outT is the hit distance.
66-
// Uses the "closest approach between two lines" method:
67-
// Ray line: P = origin + t * dir
68-
// Capsule segment: Q = segA + s * (segB - segA)
69-
// Find (t, s) that minimize |P - Q|, check if distance <= radius.
71+
// Decomposes the capsule into:
72+
// 1. Infinite cylinder (clamped to segment extent)
73+
// 2. Bottom hemisphere (sphere at segA)
74+
// 3. Top hemisphere (sphere at segB)
75+
// Returns the closest hit among all three.
7076
//
7177
// Parameters:
72-
// origin — ray start position (eye position)
73-
// dir — ray direction (normalized)
78+
// origin — ray start position (eye position)
79+
// dir — ray direction (normalized)
7480
// capBottom — capsule foot position (bottom of capsule)
7581
// capHeight — total height of capsule
7682
// capRadius — capsule radius
@@ -83,67 +89,78 @@ inline bool RayCapsule(const Float3& origin, const Float3& dir,
8389
const Float3& capBottom, float capHeight, float capRadius,
8490
float& outT, float maxRange = 200.0f)
8591
{
86-
// Capsule segment: A = bottom + (0, radius, 0), B = bottom + (0, height - radius, 0)
92+
// Capsule segment endpoints (sphere centers)
8793
Float3 segA = { capBottom.x, capBottom.y + capRadius, capBottom.z };
8894
Float3 segB = { capBottom.x, capBottom.y + capHeight - capRadius, capBottom.z };
89-
90-
// Line-segment closest approach
91-
// Ray: P(t) = origin + t * dir
92-
// Seg: Q(s) = segA + s * segDir, where segDir = segB - segA
9395
Float3 segDir = Sub(segB, segA);
94-
Float3 w0 = Sub(origin, segA);
95-
96-
float a = Dot(dir, dir); // always 1 if dir is normalized
97-
float b = Dot(dir, segDir);
98-
float c = Dot(segDir, segDir);
99-
float d = Dot(dir, w0);
100-
float e = Dot(segDir, w0);
96+
float segLenSq = Dot(segDir, segDir);
10197

102-
float denom = a * c - b * b;
98+
float bestT = maxRange + 1.0f;
99+
bool hasHit = false;
103100

104-
float t, s;
105-
106-
if (denom < 1e-6f)
107-
{
108-
// Lines are nearly parallel
109-
s = 0.0f;
110-
t = -d / a;
111-
}
112-
else
101+
// 1. Test ray against infinite cylinder, clamp to segment extent
102+
if (segLenSq > 1e-8f)
113103
{
114-
s = (b * d - a * e) / denom;
115-
t = (c * d - b * e) / denom;
104+
float segLen = sqrtf(segLenSq);
105+
Float3 axis = Scale(segDir, 1.0f / segLen);
106+
Float3 oc = Sub(origin, segA);
107+
108+
float dDotAxis = Dot(dir, axis);
109+
float ocDotAxis = Dot(oc, axis);
110+
111+
// Project out the capsule axis component
112+
Float3 dPerp = Sub(dir, Scale(axis, dDotAxis));
113+
Float3 ocPerp = Sub(oc, Scale(axis, ocDotAxis));
114+
115+
float a = Dot(dPerp, dPerp);
116+
float b = Dot(dPerp, ocPerp);
117+
float c = Dot(ocPerp, ocPerp) - capRadius * capRadius;
118+
119+
float disc = b * b - a * c;
120+
if (disc >= 0.0f && a > 1e-8f)
121+
{
122+
float sqrtDisc = sqrtf(disc);
123+
float t = (-b - sqrtDisc) / a;
124+
if (t < 0.0f) t = (-b + sqrtDisc) / a;
125+
126+
if (t >= 0.0f && t <= maxRange)
127+
{
128+
float hitOnAxis = ocDotAxis + t * dDotAxis;
129+
if (hitOnAxis >= 0.0f && hitOnAxis <= segLen)
130+
{
131+
bestT = t;
132+
hasHit = true;
133+
}
134+
}
135+
}
116136
}
117137

118-
// Clamp s to [0, 1] (capsule segment) and recompute t
119-
if (s < 0.0f)
138+
// 2. Test against bottom hemisphere (sphere at segA)
139+
float tSphere;
140+
if (RaySphere(origin, dir, segA, capRadius, tSphere))
120141
{
121-
s = 0.0f;
122-
t = -d / a; // dot(dir, origin - segA) / dot(dir, dir)
142+
if (tSphere <= maxRange && tSphere < bestT)
143+
{
144+
bestT = tSphere;
145+
hasHit = true;
146+
}
123147
}
124-
else if (s > 1.0f)
148+
149+
// 3. Test against top hemisphere (sphere at segB)
150+
if (RaySphere(origin, dir, segB, capRadius, tSphere))
125151
{
126-
s = 1.0f;
127-
Float3 w1 = Sub(origin, segB);
128-
t = -Dot(dir, w1) / a;
152+
if (tSphere <= maxRange && tSphere < bestT)
153+
{
154+
bestT = tSphere;
155+
hasHit = true;
156+
}
129157
}
130158

131-
// t must be positive (ray goes forward) and within range
132-
if (t < 0.0f) t = 0.0f;
133-
if (t > maxRange) return false;
134-
135-
// Compute closest points
136-
Float3 closestOnRay = Add(origin, Scale(dir, t));
137-
Float3 closestOnSeg = Add(segA, Scale(segDir, s));
138-
Float3 diff = Sub(closestOnRay, closestOnSeg);
139-
float distSq = Dot(diff, diff);
140-
141-
if (distSq <= capRadius * capRadius)
159+
if (hasHit)
142160
{
143-
outT = t;
161+
outT = bestT;
144162
return true;
145163
}
146-
147164
return false;
148165
}
149166

0 commit comments

Comments
 (0)