hush
This commit is contained in:
197
src/triclip.cpp
Normal file
197
src/triclip.cpp
Normal file
@@ -0,0 +1,197 @@
|
||||
#include "triclip.hh"
|
||||
|
||||
namespace psxsplash {
|
||||
|
||||
// ============================================================================
|
||||
// Screen-space Sutherland-Hodgman clipping
|
||||
// ============================================================================
|
||||
|
||||
// Interpolate between two ClipVertex at parameter t (0..256 = 0.0..1.0, fp8).
|
||||
// Uses fp8 to avoid overflow with int16 coordinates (int16 * 256 fits int32).
|
||||
static ClipVertex lerpClip(const ClipVertex& a, const ClipVertex& b, int32_t t) {
|
||||
ClipVertex r;
|
||||
r.x = (int16_t)(a.x + (((int32_t)(b.x - a.x) * t) >> 8));
|
||||
r.y = (int16_t)(a.y + (((int32_t)(b.y - a.y) * t) >> 8));
|
||||
r.z = (int16_t)(a.z + (((int32_t)(b.z - a.z) * t) >> 8));
|
||||
r.u = (uint8_t)(a.u + (((int)(b.u) - (int)(a.u)) * t >> 8));
|
||||
r.v = (uint8_t)(a.v + (((int)(b.v) - (int)(a.v)) * t >> 8));
|
||||
r.r = (uint8_t)(a.r + (((int)(b.r) - (int)(a.r)) * t >> 8));
|
||||
r.g = (uint8_t)(a.g + (((int)(b.g) - (int)(a.g)) * t >> 8));
|
||||
r.b = (uint8_t)(a.b + (((int)(b.b) - (int)(a.b)) * t >> 8));
|
||||
return r;
|
||||
}
|
||||
|
||||
// Clip a polygon (in[] with inCount vertices) against a single edge.
|
||||
// Edge is defined by axis (0=X, 1=Y), sign (+1 or -1), and threshold.
|
||||
// Output written to out[], returns output vertex count.
|
||||
static int clipEdge(const ClipVertex* in, int inCount,
|
||||
ClipVertex* out, int axis, int sign, int16_t threshold) {
|
||||
if (inCount == 0) return 0;
|
||||
int outCount = 0;
|
||||
|
||||
for (int i = 0; i < inCount; i++) {
|
||||
const ClipVertex& cur = in[i];
|
||||
const ClipVertex& next = in[(i + 1) % inCount];
|
||||
|
||||
int16_t curVal = (axis == 0) ? cur.x : cur.y;
|
||||
int16_t nextVal = (axis == 0) ? next.x : next.y;
|
||||
|
||||
bool curInside = (sign > 0) ? (curVal <= threshold) : (curVal >= threshold);
|
||||
bool nextInside = (sign > 0) ? (nextVal <= threshold) : (nextVal >= threshold);
|
||||
|
||||
if (curInside) {
|
||||
if (outCount < 8) out[outCount++] = cur;
|
||||
if (!nextInside) {
|
||||
// Exiting: compute intersection
|
||||
int32_t den = (int32_t)nextVal - (int32_t)curVal;
|
||||
if (den != 0) {
|
||||
int32_t t = ((int32_t)(threshold - curVal) << 8) / den;
|
||||
if (t < 0) t = 0;
|
||||
if (t > 256) t = 256;
|
||||
if (outCount < 8) out[outCount++] = lerpClip(cur, next, t);
|
||||
}
|
||||
}
|
||||
} else if (nextInside) {
|
||||
// Entering: compute intersection
|
||||
int32_t den = (int32_t)nextVal - (int32_t)curVal;
|
||||
if (den != 0) {
|
||||
int32_t t = ((int32_t)(threshold - curVal) << 8) / den;
|
||||
if (t < 0) t = 0;
|
||||
if (t > 256) t = 256;
|
||||
if (outCount < 8) out[outCount++] = lerpClip(cur, next, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
return outCount;
|
||||
}
|
||||
|
||||
int clipTriangle(const ClipVertex& v0, const ClipVertex& v1, const ClipVertex& v2,
|
||||
ClipResult& result) {
|
||||
// Working buffers for Sutherland-Hodgman (max 8 vertices after 4 clips).
|
||||
ClipVertex bufA[8], bufB[8];
|
||||
bufA[0] = v0; bufA[1] = v1; bufA[2] = v2;
|
||||
int count = 3;
|
||||
|
||||
// Clip against 4 edges: left, right, top, bottom.
|
||||
count = clipEdge(bufA, count, bufB, 0, -1, CLIP_LEFT); // X >= CLIP_LEFT
|
||||
count = clipEdge(bufB, count, bufA, 0, +1, CLIP_RIGHT); // X <= CLIP_RIGHT
|
||||
count = clipEdge(bufA, count, bufB, 1, -1, CLIP_TOP); // Y >= CLIP_TOP
|
||||
count = clipEdge(bufB, count, bufA, 1, +1, CLIP_BOTTOM); // Y <= CLIP_BOTTOM
|
||||
|
||||
if (count < 3) return 0;
|
||||
|
||||
// Triangulate the convex polygon into a fan from vertex 0.
|
||||
int triCount = count - 2;
|
||||
if (triCount > MAX_CLIP_TRIS) triCount = MAX_CLIP_TRIS;
|
||||
|
||||
for (int i = 0; i < triCount; i++) {
|
||||
result.verts[i * 3 + 0] = bufA[0];
|
||||
result.verts[i * 3 + 1] = bufA[i + 1];
|
||||
result.verts[i * 3 + 2] = bufA[i + 2];
|
||||
}
|
||||
return triCount;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Near-plane (3D view-space) clipping
|
||||
// ============================================================================
|
||||
|
||||
ViewVertex lerpViewVertex(const ViewVertex& a, const ViewVertex& b, int32_t t) {
|
||||
ViewVertex r;
|
||||
r.x = a.x + (int32_t)(((int64_t)(b.x - a.x) * t) >> 12);
|
||||
r.y = a.y + (int32_t)(((int64_t)(b.y - a.y) * t) >> 12);
|
||||
r.z = a.z + (int32_t)(((int64_t)(b.z - a.z) * t) >> 12);
|
||||
r.u = (uint8_t)(a.u + (((int)(b.u) - (int)(a.u)) * t >> 12));
|
||||
r.v = (uint8_t)(a.v + (((int)(b.v) - (int)(a.v)) * t >> 12));
|
||||
r.r = (uint8_t)(a.r + (((int)(b.r) - (int)(a.r)) * t >> 12));
|
||||
r.g = (uint8_t)(a.g + (((int)(b.g) - (int)(a.g)) * t >> 12));
|
||||
r.b = (uint8_t)(a.b + (((int)(b.b) - (int)(a.b)) * t >> 12));
|
||||
r.pad = 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline int32_t nearPlaneT(int32_t zA, int32_t zB) {
|
||||
constexpr int32_t nearZ = (int32_t)NEAR_PLANE_Z << 12;
|
||||
int32_t num = nearZ - zA;
|
||||
int32_t den = zB - zA;
|
||||
if (den == 0) return 0;
|
||||
int32_t absNum = num < 0 ? -num : num;
|
||||
int shift = 0;
|
||||
while (absNum > 0x7FFFF) {
|
||||
absNum >>= 1;
|
||||
shift++;
|
||||
}
|
||||
if (shift > 0) {
|
||||
num >>= shift;
|
||||
den >>= shift;
|
||||
if (den == 0) return num > 0 ? 4096 : 0;
|
||||
}
|
||||
return (num << 12) / den;
|
||||
}
|
||||
|
||||
static inline bool isBehind(int32_t z) {
|
||||
return z < ((int32_t)NEAR_PLANE_Z << 12);
|
||||
}
|
||||
|
||||
int nearPlaneClip(const ViewVertex& v0, const ViewVertex& v1, const ViewVertex& v2,
|
||||
NearClipResult& result) {
|
||||
bool b0 = isBehind(v0.z);
|
||||
bool b1 = isBehind(v1.z);
|
||||
bool b2 = isBehind(v2.z);
|
||||
int behindCount = (int)b0 + (int)b1 + (int)b2;
|
||||
|
||||
if (behindCount == 3) {
|
||||
result.triCount = 0;
|
||||
return 0;
|
||||
}
|
||||
if (behindCount == 0) {
|
||||
result.triCount = 1;
|
||||
result.verts[0] = v0;
|
||||
result.verts[1] = v1;
|
||||
result.verts[2] = v2;
|
||||
return 1;
|
||||
}
|
||||
if (behindCount == 1) {
|
||||
const ViewVertex* A;
|
||||
const ViewVertex* B;
|
||||
const ViewVertex* C;
|
||||
if (b0) { A = &v0; B = &v1; C = &v2; }
|
||||
else if (b1) { A = &v1; B = &v2; C = &v0; }
|
||||
else { A = &v2; B = &v0; C = &v1; }
|
||||
|
||||
int32_t tAB = nearPlaneT(A->z, B->z);
|
||||
int32_t tAC = nearPlaneT(A->z, C->z);
|
||||
ViewVertex AB = lerpViewVertex(*A, *B, tAB);
|
||||
ViewVertex AC = lerpViewVertex(*A, *C, tAC);
|
||||
|
||||
result.triCount = 2;
|
||||
result.verts[0] = AB;
|
||||
result.verts[1] = *B;
|
||||
result.verts[2] = *C;
|
||||
result.verts[3] = AB;
|
||||
result.verts[4] = *C;
|
||||
result.verts[5] = AC;
|
||||
return 2;
|
||||
}
|
||||
{
|
||||
const ViewVertex* A;
|
||||
const ViewVertex* B;
|
||||
const ViewVertex* C;
|
||||
if (!b0) { A = &v0; B = &v1; C = &v2; }
|
||||
else if (!b1) { A = &v1; B = &v2; C = &v0; }
|
||||
else { A = &v2; B = &v0; C = &v1; }
|
||||
|
||||
int32_t tBA = nearPlaneT(B->z, A->z);
|
||||
int32_t tCA = nearPlaneT(C->z, A->z);
|
||||
ViewVertex BA = lerpViewVertex(*B, *A, tBA);
|
||||
ViewVertex CA = lerpViewVertex(*C, *A, tCA);
|
||||
|
||||
result.triCount = 1;
|
||||
result.verts[0] = *A;
|
||||
result.verts[1] = BA;
|
||||
result.verts[2] = CA;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace psxsplash
|
||||
Reference in New Issue
Block a user