ソースを参照

fix and improve polygon clipping functions

pull/4832/head
Bigfoot71 2週間前
コミット
793d56c701
1個のファイルの変更122行の追加115行の削除
  1. +122
    -115
      src/external/rlsw.h

+ 122
- 115
src/external/rlsw.h ファイルの表示

@ -1320,144 +1320,151 @@ static inline void sw_project_ndc_to_screen(float screen[2], const float ndc[4])
/* === Triangle Rendering Part === */
static inline bool sw_triangle_clip_w(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES], int* vertexCounter)
{
sw_vertex_t input[SW_MAX_CLIPPED_POLYGON_VERTICES];
for (int i = 0; i < SW_MAX_CLIPPED_POLYGON_VERTICES; i++) {
input[i] = polygon[i];
}
int inputCounter = *vertexCounter;
*vertexCounter = 0;
const sw_vertex_t *prevVt = &input[inputCounter-1];
char prevDot = (prevVt->homogeneous[3] < SW_CLIP_EPSILON) ? -1 : 1;
for (int i = 0; i < inputCounter; i++) {
char currDot = (input[i].homogeneous[3] < SW_CLIP_EPSILON) ? -1 : 1;
if (prevDot*currDot < 0) {
polygon[(*vertexCounter)++] = sw_lerp_vertex_PNTCH(prevVt, &input[i],
(SW_CLIP_EPSILON - prevVt->homogeneous[3]) / (input[i].homogeneous[3] - prevVt->homogeneous[3]));
}
if (currDot > 0) {
polygon[(*vertexCounter)++] = input[i];
}
prevDot = currDot;
prevVt = &input[i];
#define DEFINE_CLIP_FUNC(name, FUNC_IS_INSIDE, FUNC_COMPUTE_T) \
static inline int sw_clip_##name( \
sw_vertex_t output[SW_MAX_CLIPPED_POLYGON_VERTICES], \
const sw_vertex_t input[SW_MAX_CLIPPED_POLYGON_VERTICES], \
int n) \
{ \
const sw_vertex_t *prev = &input[n - 1]; \
int prevInside = FUNC_IS_INSIDE(prev->homogeneous); \
int outputCount = 0; \
\
for (int i = 0; i < n; i++) { \
const sw_vertex_t *curr = &input[i]; \
int currInside = FUNC_IS_INSIDE(curr->homogeneous); \
\
/* If transition between interior/exterior, calculate intersection point */ \
if (prevInside != currInside) { \
float t = FUNC_COMPUTE_T(prev->homogeneous, curr->homogeneous); \
output[outputCount++] = sw_lerp_vertex_PNTCH(prev, curr, t); \
} \
\
/* If current vertex inside, add it */ \
if (currInside) { \
output[outputCount++] = *curr; \
} \
\
prev = curr; \
prevInside = currInside; \
} \
\
return outputCount; \
}
#define IS_INSIDE_PLANE_W(h) ((h)[3] >= SW_CLIP_EPSILON)
#define IS_INSIDE_PLANE_X_POS(h) ((h)[0] <= (h)[3])
#define IS_INSIDE_PLANE_X_NEG(h) (-(h)[0] <= (h)[3])
#define IS_INSIDE_PLANE_Y_POS(h) ((h)[1] <= (h)[3])
#define IS_INSIDE_PLANE_Y_NEG(h) (-(h)[1] <= (h)[3])
#define IS_INSIDE_PLANE_Z_POS(h) ((h)[2] <= (h)[3])
#define IS_INSIDE_PLANE_Z_NEG(h) (-(h)[2] <= (h)[3])
#define COMPUTE_T_PLANE_W(hPrev, hCurr) ((SW_CLIP_EPSILON - (hPrev)[3]) / ((hCurr)[3] - (hPrev)[3]))
#define COMPUTE_T_PLANE_X_POS(hPrev, hCurr) (((hPrev)[3] - (hPrev)[0]) / (((hPrev)[3] - (hPrev)[0]) - ((hCurr)[3] - (hCurr)[0])))
#define COMPUTE_T_PLANE_X_NEG(hPrev, hCurr) (((hPrev)[3] + (hPrev)[0]) / (((hPrev)[3] + (hPrev)[0]) - ((hCurr)[3] + (hCurr)[0])))
#define COMPUTE_T_PLANE_Y_POS(hPrev, hCurr) (((hPrev)[3] - (hPrev)[1]) / (((hPrev)[3] - (hPrev)[1]) - ((hCurr)[3] - (hCurr)[1])))
#define COMPUTE_T_PLANE_Y_NEG(hPrev, hCurr) (((hPrev)[3] + (hPrev)[1]) / (((hPrev)[3] + (hPrev)[1]) - ((hCurr)[3] + (hCurr)[1])))
#define COMPUTE_T_PLANE_Z_POS(hPrev, hCurr) (((hPrev)[3] - (hPrev)[2]) / (((hPrev)[3] - (hPrev)[2]) - ((hCurr)[3] - (hCurr)[2])))
#define COMPUTE_T_PLANE_Z_NEG(hPrev, hCurr) (((hPrev)[3] + (hPrev)[2]) / (((hPrev)[3] + (hPrev)[2]) - ((hCurr)[3] + (hCurr)[2])))
DEFINE_CLIP_FUNC(w, IS_INSIDE_PLANE_W, COMPUTE_T_PLANE_W)
DEFINE_CLIP_FUNC(x_pos, IS_INSIDE_PLANE_X_POS, COMPUTE_T_PLANE_X_POS)
DEFINE_CLIP_FUNC(x_neg, IS_INSIDE_PLANE_X_NEG, COMPUTE_T_PLANE_X_NEG)
DEFINE_CLIP_FUNC(y_pos, IS_INSIDE_PLANE_Y_POS, COMPUTE_T_PLANE_Y_POS)
DEFINE_CLIP_FUNC(y_neg, IS_INSIDE_PLANE_Y_NEG, COMPUTE_T_PLANE_Y_NEG)
DEFINE_CLIP_FUNC(z_pos, IS_INSIDE_PLANE_Z_POS, COMPUTE_T_PLANE_Z_POS)
DEFINE_CLIP_FUNC(z_neg, IS_INSIDE_PLANE_Z_NEG, COMPUTE_T_PLANE_Z_NEG)
static inline bool sw_triangle_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES], int* vertexCounter)
{
sw_vertex_t tmp[SW_MAX_CLIPPED_POLYGON_VERTICES];
int n = *vertexCounter;
#define CLIP_AGAINST_PLANE(FUNC_CLIP) \
{ \
n = FUNC_CLIP(tmp, polygon, n); \
if (n == 0) return false; \
for (int i = 0; i < n; i++) { \
polygon[i] = tmp[i]; \
} \
}
return *vertexCounter > 0;
}
static inline bool sw_triangle_clip_xyz(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES], int* vertexCounter)
{
for (int iAxis = 0; iAxis < 3; iAxis++)
{
if (*vertexCounter == 0) return false;
sw_vertex_t input[SW_MAX_CLIPPED_POLYGON_VERTICES];
int inputCounter;
const sw_vertex_t *prevVt;
char prevDot;
// Clip against first plane
for (int i = 0; i < SW_MAX_CLIPPED_POLYGON_VERTICES; i++) {
input[i] = polygon[i];
}
inputCounter = *vertexCounter;
*vertexCounter = 0;
prevVt = &input[inputCounter-1];
prevDot = (prevVt->homogeneous[iAxis] <= prevVt->homogeneous[3]) ? 1 : -1;
for (int i = 0; i < inputCounter; i++) {
char currDot = (input[i].homogeneous[iAxis] <= input[i].homogeneous[3]) ? 1 : -1;
if (prevDot * currDot <= 0) {
polygon[(*vertexCounter)++] = sw_lerp_vertex_PNTCH(prevVt, &input[i], (prevVt->homogeneous[3] - prevVt->homogeneous[iAxis]) /
((prevVt->homogeneous[3] - prevVt->homogeneous[iAxis]) - (input[i].homogeneous[3] - input[i].homogeneous[iAxis])));
}
if (currDot > 0) {
polygon[(*vertexCounter)++] = input[i];
}
prevDot = currDot;
prevVt = &input[i];
}
if (*vertexCounter == 0) return false;
// Clip against opposite plane
for (int i = 0; i < SW_MAX_CLIPPED_POLYGON_VERTICES; i++) {
input[i] = polygon[i];
}
inputCounter = *vertexCounter;
*vertexCounter = 0;
prevVt = &input[inputCounter-1];
prevDot = (-prevVt->homogeneous[iAxis] <= prevVt->homogeneous[3]) ? 1 : -1;
CLIP_AGAINST_PLANE(sw_clip_w);
CLIP_AGAINST_PLANE(sw_clip_x_pos);
CLIP_AGAINST_PLANE(sw_clip_x_neg);
CLIP_AGAINST_PLANE(sw_clip_y_pos);
CLIP_AGAINST_PLANE(sw_clip_y_neg);
CLIP_AGAINST_PLANE(sw_clip_z_pos);
CLIP_AGAINST_PLANE(sw_clip_z_neg);
for (int i = 0; i < inputCounter; i++) {
char currDot = (-input[i].homogeneous[iAxis] <= input[i].homogeneous[3]) ? 1 : -1;
if (prevDot*currDot <= 0) {
polygon[(*vertexCounter)++] = sw_lerp_vertex_PNTCH(prevVt, &input[i], (prevVt->homogeneous[3] + prevVt->homogeneous[iAxis]) /
((prevVt->homogeneous[3] + prevVt->homogeneous[iAxis]) - (input[i].homogeneous[3] + input[i].homogeneous[iAxis])));
}
if (currDot > 0) {
polygon[(*vertexCounter)++] = input[i];
}
prevDot = currDot;
prevVt = &input[i];
}
}
*vertexCounter = n;
return o">*vertexCounter > 0;
return n > 0;
}
static inline void sw_triangle_project_and_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES], int* vertexCounter)
{
// Step 1: MVP projection for all vertices
for (int i = 0; i < *vertexCounter; i++) {
sw_vertex_t *v = polygon + i;
sw_vec4_transform(v->homogeneous, v->position, RLSW.matMVP);
sw_vec4_transform(polygon[i].homogeneous, polygon[i].position, RLSW.matMVP);
}
// Step 2: Face culling - discard triangles facing away
if (RLSW.stateFlags & SW_STATE_CULL_FACE) {
float x0 = polygon[0].homogeneous[0], y0 = polygon[0].homogeneous[1];
float x1 = polygon[1].homogeneous[0], y1 = polygon[1].homogeneous[1];
float x2 = polygon[2].homogeneous[0], y2 = polygon[2].homogeneous[1];
float sgnArea = (x1 - x0) * (y2 - y0) - (x2 - x0) * (y1 - y0);
if ((RLSW.cullFace == SW_FRONT && sgnArea >= 0) || (RLSW.cullFace == SW_BACK && sgnArea <= 0)) {
// NOTE: Face culling is done before clipping to avoid unnecessary computations.
// However, culling requires NDC coordinates, while clipping must be done
// in homogeneous space to correctly interpolate newly generated vertices.
// This means we need to compute 1/W twice:
// - Once before clipping for face culling.
// - Again after clipping for the new vertices.
const float invW0 = 1.0f / polygon[0].homogeneous[3];
const float invW1 = 1.0f / polygon[1].homogeneous[3];
const float invW2 = 1.0f / polygon[2].homogeneous[3];
// Compute the signed 2D area (cross product in Z)
const float x0 = polygon[0].homogeneous[0] * invW0, y0 = polygon[0].homogeneous[1] * invW0;
const float x1 = polygon[1].homogeneous[0] * invW1, y1 = polygon[1].homogeneous[1] * invW1;
const float x2 = polygon[2].homogeneous[0] * invW2, y2 = polygon[2].homogeneous[1] * invW2;
const float sgnArea = (x1 - x0) * (y2 - y0) - (x2 - x0) * (y1 - y0);
// Discard the triangle if it faces the culled direction
if ((RLSW.cullFace == SW_FRONT) ? (sgnArea >= 0) : (sgnArea <= 0)) {
*vertexCounter = 0;
return;
}
}
// Step 3: Clipping and perspective projection
if (sw_triangle_clip(polygon, vertexCounter) && *vertexCounter >= 3) {
if (sw_triangle_clip_w(polygon, vertexCounter) && sw_triangle_clip_xyz(polygon, vertexCounter)) {
// Transformation to screen space and normalization
for (int i = 0; i < *vertexCounter; i++) {
sw_vertex_t *v = polygon + i;
sw_vertex_t *v = o">&polygon[i]; // Use &polygon[i] instead of polygon + i
// Calculation of the reciprocal of W for normalization
// as well as perspective correct attributes
v->homogeneous[3] = 1.0f / v->homogeneous[3];
// as well as perspective-correct attributes
const float invW = 1.0f / v->homogeneous[3];
v->homogeneous[3] = invW;
// Division of XYZ coordinates by weight
v->homogeneous[0] *= v->homogeneous[3];
v->homogeneous[1] *= v->homogeneous[3];
v->homogeneous[2] *= v->homogeneous[3];
// Division of texture coordinates (perspective correct)
v->texcoord[0] *= v->homogeneous[3];
v->texcoord[1] *= v->homogeneous[3];
// Division of colors (perspective correct)
v->color[0] *= v->homogeneous[3];
v->color[1] *= v->homogeneous[3];
v->color[2] *= v->homogeneous[3];
v->color[3] *= v->homogeneous[3];
// Transform to screen space
v->homogeneous[0] *= invW;
v->homogeneous[1] *= invW;
v->homogeneous[2] *= invW;
// Division of texture coordinates (perspective class="o">-correct)
v->texcoord[0] *= invW;
v->texcoord[1] *= invW;
// Division of colors (perspective class="o">-correct)
v->color[0] *= invW;
v->color[1] *= invW;
v->color[2] *= invW;
v->color[3] *= invW;
// Transformation to screen space
sw_project_ndc_to_screen(v->screen, v->homogeneous);
}
}

読み込み中…
キャンセル
保存