diff --git a/GL/clip.c b/GL/clip.c index a5c101d..5951163 100644 --- a/GL/clip.c +++ b/GL/clip.c @@ -1,5 +1,6 @@ #include #include "clip.h" +#include "../containers/aligned_vector.h" ClipResult clipLineToNearZ(const float* v1, const float* v2, const float dist, float* vout, float* t) { if(v1[2] < dist && v2[2] < dist) { @@ -32,3 +33,165 @@ ClipResult clipLineToNearZ(const float* v1, const float* v2, const float dist, f return CLIP_RESULT_ALL_ON_PLANE; } } + +static void interpolateVec2(const float* v1, const float* v2, const float t, float* out) { + /* FIXME: SH4 has an asm instruction for this */ + out[0] = v1[0] + (v2[0] - v1[0]) * t; + out[1] = v1[1] + (v2[1] - v1[1]) * t; +} + +static void interpolateVec3(const float* v1, const float* v2, const float t, float* out) { + /* FIXME: SH4 has an asm instruction for this */ + out[0] = v1[0] + (v2[0] - v1[0]) * t; + out[1] = v1[1] + (v2[1] - v1[1]) * t; + out[2] = v1[2] + (v2[2] - v1[2]) * t; +} + +static void interpolateColour(const uint32_t* c1, const uint32_t* c2, const float t, const uint32_t* out) { + /* FIXME: Needs float casting stuff to actually work */ + +} + +const uint32_t VERTEX_CMD_EOL = 0xf0000000; +const uint32_t VERTEX_CMD = 0xe0000000; + +void clipTriangleStrip(AlignedVector* vertices, AlignedVector* outBuffer) { + + /* Clipping triangle strips is *hard* this is the algorithm we follow: + * + * - Treat each triangle in the strip individually. + * - If we find a triangle that needs clipping, treat it in isolation. + * - End the strip at the triangle + * - Generate a new single-triangle strip for it + * - Begin a new strip for the remainder of the strip + * + * There is probably more efficient way but there are so many different cases to handle that it's + * difficult to even write them down! + */ + + uint32_t i; + + for(i = 2; i < vertices->size; ++i) { + ClipVertex* sourceTriangle[3] = { + aligned_vector_at(vertices, i - 2), + aligned_vector_at(vertices, i - 1), + aligned_vector_at(vertices, i) + }; + + ClipVertex* v1 = sourceTriangle[0]; + ClipVertex* v2 = sourceTriangle[1]; + ClipVertex* v3 = sourceTriangle[2]; + + uint8_t visible = ((v1->xyz[2] <= 0) ? 4 : 0) | ((v2->xyz[2] <= 0) ? 2 : 0) | ((v3->xyz[2] <= 0) ? 1 : 0); + + /* All visible, we're fine! */ + if(visible == 0b111) { + if(i == 2) { + aligned_vector_push_back(outBuffer, v1, 1); + aligned_vector_push_back(outBuffer, v2, 1); + } + + aligned_vector_push_back(outBuffer, v3, 1); + } else if(visible == 0b000) { + /* Do nothing */ + continue; + } else if(visible == 0b100) { + + } else if(visible == 0b010) { + + } else if(visible == 0b001) { + + } else if(visible == 0b110) { + /* Third vertex isn't visible */ + + float t1 = 0, t2 = 0; + + ClipVertex output[4]; + + /* FIXME: Interpolate other values (colors etc.) */ + clipLineToNearZ(v2->xyz, v3->xyz, 0, output[2].xyz, &t1); + clipLineToNearZ(v1->xyz, v3->xyz, 0, output[3].xyz, &t2); + + output[0] = *v1; + output[1] = *v2; + + /* Interpolate normals */ + interpolateVec3(v2->nxyz, v3->nxyz, t1, output[2].nxyz); + interpolateVec3(v1->nxyz, v3->nxyz, t2, output[3].nxyz); + + /* Interpolate texcoords */ + interpolateVec2(v2->uv, v3->uv, t1, output[2].uv); + interpolateVec2(v1->uv, v3->uv, t2, output[3].uv); + + interpolateColour((uint32_t*) &v2->argb, (uint32_t*) &v3->argb, t1, (uint32_t*) &output[2].argb); + interpolateColour((uint32_t*) &v1->argb, (uint32_t*) &v3->argb, t2, (uint32_t*) &output[3].argb); + + output[0].flags = VERTEX_CMD; + output[1].flags = VERTEX_CMD; + output[2].flags = VERTEX_CMD; + output[3].flags = VERTEX_CMD_EOL; + + aligned_vector_push_back(outBuffer, output, 4); + } else if(visible == 0b011) { + /* First vertex isn't visible, so let's clip along the lines to the second and third */ + float t1 = 0, t2 = 0; + + ClipVertex output[4]; + + /* FIXME: Interpolate other values (colors etc.) */ + clipLineToNearZ(v1->xyz, v2->xyz, 0, output[0].xyz, &t1); + clipLineToNearZ(v1->xyz, v3->xyz, 0, output[2].xyz, &t2); + + output[1] = *v2; + output[3] = *v3; + + /* Interpolate normals */ + interpolateVec3(v1->nxyz, v2->nxyz, t1, output[0].nxyz); + interpolateVec3(v1->nxyz, v3->nxyz, t2, output[2].nxyz); + + /* Interpolate texcoords */ + interpolateVec2(v1->uv, v2->uv, t1, output[0].uv); + interpolateVec2(v1->uv, v3->uv, t2, output[2].uv); + + interpolateColour((uint32_t*) &v1->argb, (uint32_t*) &v2->argb, t1, (uint32_t*) &output[0].argb); + interpolateColour((uint32_t*) &v1->argb, (uint32_t*) &v3->argb, t2, (uint32_t*) &output[2].argb); + + output[0].flags = VERTEX_CMD; + output[1].flags = VERTEX_CMD; + output[2].flags = VERTEX_CMD; + output[3].flags = VERTEX_CMD_EOL; + + aligned_vector_push_back(outBuffer, output, 4); + } else if(visible == 0b101) { + /* Second vertex isn't visible */ + float t1 = 0, t2 = 0; + + ClipVertex output[4]; + + /* FIXME: Interpolate other values (colors etc.) */ + clipLineToNearZ(v1->xyz, v2->xyz, 0, output[1].xyz, &t1); + clipLineToNearZ(v3->xyz, v2->xyz, 0, output[2].xyz, &t2); + + output[0] = *v1; + output[3] = *v3; + + /* Interpolate normals */ + interpolateVec3(v1->nxyz, v2->nxyz, t1, output[1].nxyz); + interpolateVec3(v3->nxyz, v2->nxyz, t2, output[2].nxyz); + + /* Interpolate texcoords */ + interpolateVec2(v1->uv, v2->uv, t1, output[1].uv); + interpolateVec2(v3->uv, v2->uv, t2, output[2].uv); + + interpolateColour((uint32_t*) &v1->argb, (uint32_t*) &v2->argb, t1, (uint32_t*) &output[1].argb); + interpolateColour((uint32_t*) &v3->argb, (uint32_t*) &v2->argb, t2, (uint32_t*) &output[2].argb); + + output[0].flags = VERTEX_CMD; + output[1].flags = VERTEX_CMD; + output[2].flags = VERTEX_CMD; + output[3].flags = VERTEX_CMD_EOL; + + aligned_vector_push_back(outBuffer, output, 4); + } + } +} diff --git a/GL/clip.h b/GL/clip.h index c6a496b..40f362b 100644 --- a/GL/clip.h +++ b/GL/clip.h @@ -1,6 +1,10 @@ #ifndef CLIP_H #define CLIP_H +#include + +#include "../containers/aligned_vector.h" + #ifdef __cplusplus extern "C" { #endif @@ -13,8 +17,34 @@ typedef enum { CLIP_RESULT_BACK_TO_FRONT } ClipResult; +typedef struct { + uint8_t a; + uint8_t r; + uint8_t g; + uint8_t b; +} ClipColour; + +/* Note: This structure is the almost the same format as pvr_vertex_t aside from the offet + * (oargb) which is replaced by the floating point w value. This is so that we can + * simply zero it and memcpy the lot into the output */ +typedef struct { + uint32_t flags; + float xyz[3]; + float uv[2]; + ClipColour argb; + float nxyz[3]; + float w; + + float xyzES[3]; /* Coordinate in eye space */ + float nES[3]; /* Normal in eye space */ +} ClipVertex; + ClipResult clipLineToNearZ(const float* v1, const float* v2, const float dist, float* vout, float* t); + +void clipTriangleStrip(AlignedVector* vertices, AlignedVector* outBuffer); + + #ifdef __cplusplus } #endif diff --git a/GL/draw.c b/GL/draw.c index f601edf..c4b3239 100644 --- a/GL/draw.c +++ b/GL/draw.c @@ -178,27 +178,8 @@ inline void transformNormalToEyeSpace(GLfloat* normal) { } -typedef struct { - uint8_t a; - uint8_t r; - uint8_t g; - uint8_t b; -} Colour; -/* Note: This structure is the almost the same format as pvr_vertex_t aside from the offet - * (oargb) which is replaced by the floating point w value. This is so that we can - * simply zero it and memcpy the lot into the output */ -typedef struct { - uint32_t flags; - float xyz[3]; - float uv[2]; - Colour argb; - float nxyz[3]; - float w; - float xyzES[3]; /* Coordinate in eye space */ - float nES[3]; /* Normal in eye space */ -} ClipVertex; static void swapVertex(ClipVertex* v1, ClipVertex* v2) { @@ -317,6 +298,27 @@ static void transform(AlignedVector* vertices) { static void clip(AlignedVector* vertices) { /* Perform clipping, generating new vertices as necessary */ + + static AlignedVector* CLIP_BUFFER = NULL; + + /* First entry into this, allocate the clip buffer */ + if(!CLIP_BUFFER) { + CLIP_BUFFER = (AlignedVector*) malloc(sizeof(AlignedVector)); + aligned_vector_init(CLIP_BUFFER, sizeof(ClipVertex)); + } + + /* Make sure we allocate roughly enough space */ + aligned_vector_reserve(CLIP_BUFFER, vertices->size); + + /* Start from empty */ + aligned_vector_resize(CLIP_BUFFER, 0); + + /* Now perform clipping! */ + clipTriangleStrip(vertices, CLIP_BUFFER); + + /* Copy the clip buffer over the vertices */ + aligned_vector_resize(vertices, CLIP_BUFFER->size); + memcpy(vertices->data, CLIP_BUFFER->data, CLIP_BUFFER->size * CLIP_BUFFER->element_size); } static void mat_transform3(const float* xyz, const float* xyzOut, const uint32_t count, const uint32_t stride) { @@ -385,7 +387,7 @@ static void light(AlignedVector* vertices) { } uint32_t final = PVR_PACK_COLOR(contribution[3], contribution[0], contribution[1], contribution[2]); - vertex->argb = *((Colour*) &final); + vertex->argb = *((ClipColour*) &final); } } @@ -465,8 +467,8 @@ static void submitVertices(GLenum mode, GLsizei first, GLsizei count, GLenum typ generate(buffer, mode, first, count, (GLubyte*) indices, type, vptr, vstride, cptr, cstride, uvptr, uvstride, nptr, nstride); light(buffer); transform(buffer); + clip(buffer); divide(buffer); - push(buffer, activePolyList()); }