diff --git a/GL/flush.c b/GL/flush.c index 0c16baf..a2f7030 100644 --- a/GL/flush.c +++ b/GL/flush.c @@ -11,6 +11,8 @@ #include "flush.h" +#define CLIP_DEBUG 1 + #define TA_SQ_ADDR (unsigned int *)(void *) \ (0xe0000000 | (((unsigned long)0x10000000) & 0x03ffffe0)) @@ -20,10 +22,85 @@ static PolyList TR_LIST; static const int STRIDE = sizeof(Vertex) / sizeof(GLuint); +#define CLIP_TO_PLANE(vert1, vert2) \ + do { \ + float t = _glClipLineToNearZ((vert1), (vert2), out); \ + interpolateVec2((vert1)->uv, (vert2)->uv, t, out->uv); \ + interpolateVec3((vert1)->nxyz, (vert2)->nxyz, t, out->nxyz); \ + interpolateVec2((vert1)->st, (vert2)->st, t, out->st); \ + interpolateColour((vert1)->bgra, (vert2)->bgra, t, out->bgra); \ + } while(0); \ + + +GL_FORCE_INLINE float _glClipLineToNearZ(const Vertex* v1, const Vertex* v2, Vertex* vout) { + const float d0 = v1->w; + const float d1 = v2->w; + + assert(isVisible(v1) ^ isVisible(v2)); + +#if 0 + /* FIXME: Disabled until determined necessary */ + + /* We need to shift 't' a little, to avoid the possibility that a + * rounding error leaves the new vertex behind the near plane. We shift + * according to the direction we're clipping across the plane */ + const float epsilon = (d0 < d1) ? 0.000001 : -0.000001; +#else + const float epsilon = 0; +#endif + + float t = MATH_Fast_Divide(d0, (d0 - d1))+ epsilon; + + vout->xyz[0] = MATH_fmac(v2->xyz[0] - v1->xyz[0], t, v1->xyz[0]); + vout->xyz[1] = MATH_fmac(v2->xyz[1] - v1->xyz[1], t, v1->xyz[1]); + vout->xyz[2] = MATH_fmac(v2->xyz[2] - v1->xyz[2], t, v1->xyz[2]); + vout->w = MATH_fmac(v2->w - v1->w, t, v1->w); + +#if CLIP_DEBUG + printf( + "(%f, %f, %f, %f) -> %f -> (%f, %f, %f, %f) = (%f, %f, %f, %f)\n", + v1->xyz[0], v1->xyz[1], v1->xyz[2], v1->w, t, + v2->xyz[0], v2->xyz[1], v2->xyz[2], v2->w, + vout->xyz[0], vout->xyz[1], vout->xyz[2], vout->w + ); +#endif + + return t; +} + +GL_FORCE_INLINE void interpolateFloat(const float v1, const float v2, const float t, float* out) { + *out = MATH_fmac(v2 - v1,t, v1); +} + +GL_FORCE_INLINE void interpolateVec2(const float* v1, const float* v2, const float t, float* out) { + interpolateFloat(v1[0], v2[0], t, &out[0]); + interpolateFloat(v1[1], v2[1], t, &out[1]); +} + +GL_FORCE_INLINE void interpolateVec3(const float* v1, const float* v2, const float t, float* out) { + interpolateFloat(v1[0], v2[0], t, &out[0]); + interpolateFloat(v1[1], v2[1], t, &out[1]); + interpolateFloat(v1[2], v2[2], t, &out[2]); +} + +GL_FORCE_INLINE void interpolateVec4(const float* v1, const float* v2, const float t, float* out) { + interpolateFloat(v1[0], v2[0], t, &out[0]); + interpolateFloat(v1[1], v2[1], t, &out[1]); + interpolateFloat(v1[2], v2[2], t, &out[2]); + interpolateFloat(v1[3], v2[3], t, &out[3]); +} + +GL_FORCE_INLINE void interpolateColour(const uint8_t* v1, const uint8_t* v2, const float t, uint8_t* out) { + out[0] = v1[0] + (uint32_t) (((float) (v2[0] - v1[0])) * t); + out[1] = v1[1] + (uint32_t) (((float) (v2[1] - v1[1])) * t); + out[2] = v1[2] + (uint32_t) (((float) (v2[2] - v1[2])) * t); + out[3] = v1[3] + (uint32_t) (((float) (v2[3] - v1[3])) * t); +} + static Vertex* interpolate_vertex(const Vertex* v0, const Vertex* v1, Vertex* out) { /* If v0 is in front of the near plane, and v1 is behind the near plane, this * generates a vertex *on* the near plane */ - + CLIP_TO_PLANE(v0, v1); return out; } @@ -31,6 +108,7 @@ GL_FORCE_INLINE ListIterator* header_reset(ListIterator* it, Vertex* header) { it->it = header; it->visibility = 0; it->triangle_count = 0; + it->stack_idx = -1; return it; } @@ -40,11 +118,19 @@ GL_FORCE_INLINE Vertex* current_postinc(ListIterator* it) { } it->remaining--; - return it->current++; + Vertex* current = it->current; + it->current++; + return current; } GL_FORCE_INLINE Vertex* push_stack(ListIterator* it) { - return &it->stack[it->stack_idx++]; +#if CLIP_DEBUG + printf("Using stack: %d\n", it->stack_idx + 1); +#endif + + assert(it->stack_idx + 1 < MAX_STACK); + + return &it->stack[++it->stack_idx]; } GL_FORCE_INLINE GLboolean shift(ListIterator* it, Vertex* new_vertex) { @@ -67,6 +153,21 @@ GL_FORCE_INLINE GLboolean shift(ListIterator* it, Vertex* new_vertex) { } ListIterator* _glIteratorNext(ListIterator* it) { + /* None remaining in the list, and the stack is empty */ + if(!it->remaining && it->stack_idx == -1) { + return NULL; + } + + /* Return any vertices we generated */ + if(it->stack_idx > -1) { +#if CLIP_DEBUG + printf("Yielding stack: %d\n", it->stack_idx); +#endif + + it->it = &it->stack[it->stack_idx--]; + return it; + } + if(!isVertex(it->current)) { return header_reset(it, current_postinc(it)); } else { @@ -80,6 +181,7 @@ ListIterator* _glIteratorNext(ListIterator* it) { /* We reached the end so just * return the oldest until they're gone */ it->it = it->triangle[0]; + printf("Bailing early!\n"); return (it->it) ? it : NULL; } } @@ -92,18 +194,34 @@ ListIterator* _glIteratorNext(ListIterator* it) { it->it = it->triangle[0]; return it; break; - case B100: + case B100: { /* First visible only */ - it->triangle[1] = interpolate_vertex(it->triangle[0], it->triangle[1], push_stack(it)); - it->triangle[2] = interpolate_vertex(it->triangle[0], it->triangle[2], push_stack(it)); + Vertex* gen2 = push_stack(it); + Vertex* gen1 = push_stack(it); + + /* Make sure we transfer the flags.. we don't + * want to disrupt the strip */ + gen1->flags = it->triangle[1]->flags; + gen2->flags = it->triangle[2]->flags; + + interpolate_vertex(it->triangle[0], it->triangle[1], gen1); + interpolate_vertex(it->triangle[0], it->triangle[2], gen2); it->visibility = B111; /* All visible now, yay! */ - assert(isVisible(it->triangle[1])); - assert(isVisible(it->triangle[2])); + assert(isVisible(gen1)); + assert(isVisible(gen2)); + assert(isVertex(gen1)); + assert(isVertex(gen2)); it->it = it->triangle[0]; + + /* We're returning v0, and we've pushed + * v1 and v2 to the stack, so next time + * around we'll need to consume and shift + * the next vertex from the source list */ + it->triangle_count--; return it; - break; + } break; } } diff --git a/GL/flush.h b/GL/flush.h index 623d7a1..ef68759 100644 --- a/GL/flush.h +++ b/GL/flush.h @@ -28,22 +28,25 @@ typedef struct { Vertex* it; /* < 8. Bitmask of the last 3 vertices */ - uint8_t visibility; - uint8_t triangle_count; Vertex* triangle[3]; /* Stack of temporary vertices */ Vertex stack[MAX_STACK]; int8_t stack_idx; + uint8_t visibility; + uint8_t triangle_count; + uint8_t padding; } ListIterator; inline ListIterator* _glIteratorBegin(void* src, int n) { ListIterator* it = (ListIterator*) malloc(sizeof(ListIterator)); - it->remaining = n; - it->current = (Vertex*) src; + it->remaining = n - 1; + it->it = (Vertex*) src; + it->current = it->it + 1; it->stack_idx = -1; it->triangle_count = 0; it->visibility = 0; + it->triangle[0] = it->triangle[1] = it->triangle[2] = NULL; return (n) ? it : NULL; } @@ -56,6 +59,7 @@ GL_FORCE_INLINE GLboolean isVertex(const Vertex* vertex) { GL_FORCE_INLINE GLboolean isVisible(const Vertex* vertex) { if(!vertex) return GL_FALSE; + printf("Z: %f, W: %f\n", vertex->xyz[2], vertex->w); return vertex->w >= 0 && vertex->xyz[2] >= -vertex->w; } diff --git a/GL/private.h b/GL/private.h index 8e4dbbe..61b6e63 100644 --- a/GL/private.h +++ b/GL/private.h @@ -297,7 +297,6 @@ typedef enum { #define G8IDX 1 #define B8IDX 0 -float _glClipLineToNearZ(const Vertex* v1, const Vertex* v2, Vertex* vout); void _glClipTriangleStrip(SubmissionTarget* target, uint8_t fladeShade); PolyList *_glActivePolyList(); diff --git a/tests/test_nearz_clipping.h b/tests/test_nearz_clipping.h index 29094ae..8b4b0e2 100644 --- a/tests/test_nearz_clipping.h +++ b/tests/test_nearz_clipping.h @@ -2,32 +2,52 @@ #include "../utils/test.h" #include "../GL/flush.h" +#include "../containers/aligned_vector.h" namespace { struct VertexBuilder { + VertexBuilder() { + aligned_vector_init(&list_, sizeof(Vertex)); + } + + ~VertexBuilder() { + aligned_vector_clear(&list_); + } + VertexBuilder& add_header() { Vertex v; v.flags = 100; // I dunno what this bit of memory would be - list_.push_back(std::move(v)); + + aligned_vector_push_back(&list_, &v, 1); + return *this; } VertexBuilder& add(float x, float y, float z, float w) { Vertex v; + v.flags = PVR_CMD_VERTEX; v.xyz[0] = x; v.xyz[1] = y; v.xyz[2] = z; v.w = w; + aligned_vector_push_back(&list_, &v, 1); return *this; } - std::vector done() { - return list_; + VertexBuilder& add_last(float x, float y, float z, float w) { + add(x, y, z, w); + Vertex* back = (Vertex*) aligned_vector_back(&list_); + back->flags = PVR_CMD_VERTEX_EOL; + return *this; + } + + std::pair done() { + return std::make_pair((Vertex*) aligned_vector_at(&list_, 0), list_.size); } private: - std::vector list_; + AlignedVector list_; }; class NearZClippingTests : public gldc::test::GLdcTestCase { @@ -39,31 +59,34 @@ public: VertexBuilder builder; - std::vector list = builder. + auto list = builder. add_header(). add(1, 1, 2, 1). add(1, 0, 2, -1). - add(0, 1, 2, -1).done(); + add_last(0, 1, 2, -1).done(); - ListIterator* it = _glIteratorBegin(&list[0], list.size()); - Vertex* v0 = it->current; + ListIterator* it = _glIteratorBegin(list.first, list.second); + Vertex* v0 = it->it; assert_is_not_null(v0); assert_false(isVertex(v0)); // Should be a header it = _glIteratorNext(it); assert_is_not_null(it); - Vertex* v1 = it->current; + Vertex* v1 = it->it; assert_is_not_null(v1); + assert_true(isVertex(v1)); it = _glIteratorNext(it); assert_is_not_null(it); - Vertex* v2 = it->current; + Vertex* v2 = it->it; assert_is_not_null(v2); + assert_true(isVertex(v2)); it = _glIteratorNext(it); assert_is_not_null(it); - Vertex* v3 = it->current; + Vertex* v3 = it->it; assert_is_not_null(v3); + assert_true(isVertex(v3)); it = _glIteratorNext(it); assert_is_null(it);