Start working on z-clipping

This commit is contained in:
Luke Benstead 2018-06-06 08:33:41 +01:00
parent 5b05039dfd
commit 1baeba1c59
4 changed files with 198 additions and 2 deletions

View File

@ -32,3 +32,69 @@ ClipResult clipLineToNearZ(const float* v1, const float* v2, const float dist, f
return CLIP_RESULT_ALL_ON_PLANE; return CLIP_RESULT_ALL_ON_PLANE;
} }
} }
TriangleClipResult clipTriangleToNearZ(
const float plane_dist,
const unsigned short triangle_n, const pvr_vertex_t* v1, const pvr_vertex_t* v2, const pvr_vertex_t *v3,
pvr_vertex_t* v1out, pvr_vertex_t* v2out, pvr_vertex_t* v3out, pvr_vertex_t* v4out
) {
/* Fast out. Let's just see if everything is in front of the clip plane (and as in OpenGL Z comes out of the screen
* we check to see if they are all < -dist
*/
typedef unsigned char uint8;
uint8 visible = ((uint8) v1->z < plane_dist) | ((uint8) v2->z < plane_dist) << 1 | ((uint8) v3->z < plane_dist) << 2;
switch(visible) {
case 0b000:
/* If behind is zero, then none of the vertices are visible */
return TRIANGLE_CLIP_RESULT_DROP_TRIANGLE;
case 0b111:
/* If behind is zero, then none of the vertices are visible */
return TRIANGLE_CLIP_RESULT_NO_CHANGE;
case 0b101:
case 0b110:
case 0b011: {
/* Two vertices are visible */
/* Tricky case. If two vertices are visible then manipulating the other one is going to change the shape of the
* triangle. So we have to clip both lines, and output a new vertex.
*/
return TRIANGLE_CLIP_RESULT_ALTERED_AND_CREATED_VERTEX;
} break;
default: {
/* One vertex is visible */
/* This is the "easy" case, we simply find the vertex which is visible, and clip the lines to the other 2 against the plane */
pvr_vertex_t tmp1, tmp2;
float t1, t2;
if(visible == 0b001) {
ClipResult l1 = clipLineToNearZ(&v1->x, &v2->x, plane_dist, &tmp1, &t1);
ClipResult l2 = clipLineToNearZ(&v1->x, &v3->x, plane_dist, &tmp2, &t2);
*v1out = *v1;
*v2out = tmp1;
*v3out = tmp2;
} else if(visible == 0b010) {
ClipResult l1 = clipLineToNearZ(&v2->x, &v1->x, plane_dist, &tmp1, &t1);
ClipResult l2 = clipLineToNearZ(&v2->x, &v3->x, plane_dist, &tmp2, &t2);
*v1out = tmp1;
*v2out = *v2;
*v3out = tmp2;
} else {
ClipResult l1 = clipLineToNearZ(&v3->x, &v1->x, plane_dist, &tmp1, &t1);
ClipResult l2 = clipLineToNearZ(&v3->x, &v2->x, plane_dist, &tmp2, &t2);
*v1out = tmp1;
*v2out = tmp2;
*v3out = *v3;
}
return TRIANGLE_CLIP_RESULT_ALTERED_VERTICES;
}
}
}

View File

@ -5,6 +5,22 @@
extern "C" { extern "C" {
#endif #endif
/* If we're not on the Dreamcast then define pvr_vertex_t
* (this if for testing only)
*/
#ifndef _arch_dreamcast
typedef struct {
uint32 flags; /**< \brief TA command (vertex flags) */
float x; /**< \brief X coordinate */
float y; /**< \brief Y coordinate */
float z; /**< \brief Z coordinate */
float u; /**< \brief Texture U coordinate */
float v; /**< \brief Texture V coordinate */
uint32 argb; /**< \brief Vertex color */
uint32 oargb; /**< \brief Vertex offset color */
} pvr_vertex_t;
#endif
typedef enum { typedef enum {
CLIP_RESULT_ALL_IN_FRONT, CLIP_RESULT_ALL_IN_FRONT,
CLIP_RESULT_ALL_BEHIND, CLIP_RESULT_ALL_BEHIND,
@ -15,6 +31,35 @@ typedef enum {
ClipResult clipLineToNearZ(const float* v1, const float* v2, const float dist, float* vout, float* t); ClipResult clipLineToNearZ(const float* v1, const float* v2, const float dist, float* vout, float* t);
/* There are 4 possible situations we'll hit when clipping triangles:
*
* 1. The entire triangle was in front of the near plane, so we do nothing
* 2. The entire triangle was behind the near plane, so we drop it completely
* 3. One vertex was behind the clip plane. In this case we need to create a new vertex and an additional triangle
* 4. Two vertices were behind the clip plane, we can simply move them so the triangle no longer intersects
*/
typedef enum {
TRIANGLE_CLIP_RESULT_NO_CHANGE,
TRIANGLE_CLIP_RESULT_DROP_TRIANGLE,
TRIANGLE_CLIP_RESULT_ALTERED_AND_CREATED_VERTEX,
TRIANGLE_CLIP_RESULT_ALTERED_VERTICES
} TriangleClipResult;
/* Clips a triangle from a triangle strip to a near-z plane. Alternating triangles in a strip switch vertex ordering
* so the number of the triangle must be passed in (vn - 2).
*
* Note that clipping a triangle with a plane may create a quadrilateral, so this function must have
* a space to output the 4th vertex.
*
* The outputs can be the same as the inputs.
*/
TriangleClipResult clipTriangleToNearZ(
const float plane_dist,
const unsigned short triangle_n, const pvr_vertex_t* v1, const pvr_vertex_t* v2, const pvr_vertex_t *v3,
pvr_vertex_t* v1out, pvr_vertex_t* v2out, pvr_vertex_t* v3out, pvr_vertex_t* v4out
);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -190,12 +190,23 @@ inline void transformNormalToEyeSpace(GLfloat* normal) {
mat_trans_normal3(normal[0], normal[1], normal[2]); mat_trans_normal3(normal[0], normal[1], normal[2]);
} }
/* If this has a value other than zero, it must be negative! */
#define NEAR_DEPTH 0.0f
static void submitVertices(GLenum mode, GLsizei first, GLsizei count, GLenum type, const GLvoid* indices) { static void submitVertices(GLenum mode, GLsizei first, GLsizei count, GLenum type, const GLvoid* indices) {
static GLfloat normal[3] = {0.0f, 0.0f, -1.0f}; static GLfloat normal[3] = {0.0f, 0.0f, -1.0f};
static GLfloat eye_P[3]; static GLfloat eye_P[3];
static GLfloat eye_N[3]; static GLfloat eye_N[3];
/* When clipping triangle strips we need to keep a stash of the last two vertices *before*
* they were manipulated, because, imagine this scenario:
* - A triangle has 2 vertices hidden
* - We manipulate those 2 vertices to coincide with the near plane
* - We end that triangle strip, then start a new one with the next triangle
* - That triangle needs to be formed with the original vertices
*/
static pvr_vertex_t clip_stash[2];
if(!(ENABLED_VERTEX_ATTRIBUTES & VERTEX_ENABLED_FLAG)) { if(!(ENABLED_VERTEX_ATTRIBUTES & VERTEX_ENABLED_FLAG)) {
return; return;
} }
@ -235,8 +246,10 @@ static void submitVertices(GLenum mode, GLsizei first, GLsizei count, GLenum typ
GLboolean lighting_enabled = isLightingEnabled(); GLboolean lighting_enabled = isLightingEnabled();
GLushort i, last_vertex; GLushort i, last_vertex
for(i = first; i < count; ++i) { GLshort rel; // Has to be signed as we drop below zero so we can re-enter the loop at 1.
for(rel = 0, i = first; i < count; ++i, ++rel) {
pvr_vertex_t* vertex = (pvr_vertex_t*) dst; pvr_vertex_t* vertex = (pvr_vertex_t*) dst;
vertex->u = vertex->v = 0.0f; vertex->u = vertex->v = 0.0f;
vertex->argb = 0; vertex->argb = 0;
@ -308,6 +321,8 @@ static void submitVertices(GLenum mode, GLsizei first, GLsizei count, GLenum typ
} }
_applyRenderMatrix(); /* Apply the Render Matrix Stack */ _applyRenderMatrix(); /* Apply the Render Matrix Stack */
// FIXME: Don't perspective divide!
transformVertex(&vertex->x, &vertex->x, &vertex->y, &vertex->z); transformVertex(&vertex->x, &vertex->x, &vertex->y, &vertex->z);
/* The PVR doesn't support quads, only triangle strips, so we need to /* The PVR doesn't support quads, only triangle strips, so we need to
@ -325,6 +340,74 @@ static void submitVertices(GLenum mode, GLsizei first, GLsizei count, GLenum typ
*(vertex - 1) = tmp; *(vertex - 1) = tmp;
} }
// Store this for the clip stash
pvr_vertex_t original_vertex = *dst;
if(rel >= 2) {
/* We have at least one complete triangle, let's start clipping! */
pvr_vertex_t* v1 = &clip_stash[0];
pvr_vertex_t* v2 = &clip_stash[1];
pvr_vertex_t* v3 = dst;
pvr_vertex_t* v1out = dst - 2;
pvr_vertex_t* v2out = dst - 1;
pvr_vertex_t* v3out = v3;
pvr_vertex_t v4out;
TriangleClipResult ret = clipTriangleToNearZ(
NEAR_DEPTH,
rel - 2,
v1, v2, v3,
v1out, v2out, v3out, &v4out
);
if(ret == TRIANGLE_CLIP_RESULT_DROP_TRIANGLE) {
/* If we're here, then none of the 3 points were visible, that means we drop the entire triangle and start
* anew with the next one. We rollback rel as we always need 3 vertices and if this was the first triangle in the strip
* we need to build up the stash again
* we point dst back at the first vertex. We would actually have access space at the end of the array
* so we move that back too (this won't reallocate, we never reallocate on shrink unless we call vector_shrink_to_fit
*/
dst = dst - 3; // This might seem like we're going back too many, but we increment further down
rel = rel - 3; // This might drop down to -1, but the next loop will go up to 0 again
aligned_vector_resize(
&activePolyList()->vector,
activePolyList->size - 3
);
} else if(ret == TRIANGLE_CLIP_RESULT_ALTERED_VERTICES) {
/*
* Two vertices were behind the clip plane, we just manipulated them.
we have to end the triangle strip here and pick up next vertex */
v3out->flags = PVR_CMD_VERTEX_EOL;
/* Now we push back the original 2 vertices, so that the next triangle strip will be properly
* formed (or dropped) next time around */
aligned_vector_resize(
&activePolyList()->vector,
activePolyList->size + 2
);
*(++dst) = clip_stash[1];
*(++dst) = original_vertex;
} else if(ret == TRIANGLE_CLIP_RESULT_ALTERED_AND_CREATED_VERTEX) {
/* One vertex was behind the clip plane, we need to create another triangle */
/* We need to push back v4 and then deal with a possible reallocation by updating dst */
} else {
/* OK nothing changed, don't do anything */
}
}
/* Update the clip stash */
clip_stash[0] = clip_stash[1];
clip_stash[1] = original_vertex;
//FIXME: Peform perspective division
++dst; ++dst;
} }
} }

View File

@ -4,7 +4,9 @@
#include "../include/gl.h" #include "../include/gl.h"
#include "../containers/aligned_vector.h" #include "../containers/aligned_vector.h"
#include "../containers/named_array.h" #include "../containers/named_array.h"
#include "./clip.h" #include "./clip.h"
#include "./pvr.h"
#define TRACE_ENABLED 0 #define TRACE_ENABLED 0
#define TRACE() if(TRACE_ENABLED) {fprintf(stderr, "%s\n", __func__);} #define TRACE() if(TRACE_ENABLED) {fprintf(stderr, "%s\n", __func__);}