diff --git a/CMakeLists.txt b/CMakeLists.txt index 048f894..e51c14d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -209,6 +209,7 @@ gen_sample(scissor samples/scissor/main.c) gen_sample(polymark samples/polymark/main.c) gen_sample(cubes samples/cubes/main.cpp) gen_sample(zclip_test tests/zclip/main.cpp) +gen_sample(primitive_modes samples/primitive_modes/main.c) if(PLATFORM_DREAMCAST) gen_sample(trimark samples/trimark/main.c) diff --git a/GL/draw.c b/GL/draw.c index 0a3a1bd..d54e775 100644 --- a/GL/draw.c +++ b/GL/draw.c @@ -435,21 +435,147 @@ GL_FORCE_INLINE void genTriangleStrip(Vertex* output, GLuint count) { output[count - 1].flags = GPU_CMD_VERTEX_EOL; } -static void genTriangleFan(Vertex* output, GLuint count) { - gl_assert(count <= 255); +#define QUADSTRIP_COUNT(count) (((count) - 2) * 2) +static GL_NO_INLINE void genQuadStrip(Vertex* output, GLuint count) { + Vertex* dst = output + QUADSTRIP_COUNT(count) - 1; + Vertex* src = output + count;//(count - 1); - Vertex* dst = output + (((count - 2) * 3) - 1); - Vertex* src = output + (count - 1); + for (; count > 2; count -= 2) { + // Have to copy because of src/dst overlapping on first quad + Vertex src1 = src[-1], src2 = src[-2], src3 = src[-3], src4 = src[-4]; - GLubyte i = count - 2; - while(i--) { - *dst = *src--; + *dst = src3; + (*dst--).flags = GPU_CMD_VERTEX_EOL; + *dst-- = src4; + *dst-- = src1; + *dst-- = src2; + src -= 2; + } +} + +#define TRIFAN_COUNT(count) (((count) - 2) * 3) +static GL_NO_INLINE void genTriangleFan(Vertex* output, GLuint count) { + Vertex* dst = output + TRIFAN_COUNT(count) - 1; + Vertex* src = output + count - 1; + + // Triangles generated as {first vertex, prior vertex, current vertex} + // e.g. {v1, v2, v3, v4} produces {v1, v2, v3}, {v1, v3, v4} + for (; count > 2; count--) { + *dst = *src--; (*dst--).flags = GPU_CMD_VERTEX_EOL; *dst-- = *src; *dst-- = *output; } } +#define POINTS_COUNT(count) ((count) * 4) +static GL_NO_INLINE void genPoints(Vertex* output, GLuint count) { + Vertex* dst = output + POINTS_COUNT(count) - 1; + Vertex* src = output + count - 1; + float half_size = HALF_POINT_SIZE; + + // Expands v to { v + (-S/2,S/2), v + (S/2,S/2), v + (-S/2,S/2), (S/2,-S/2) } + for (; count > 0; count--, src--) { + *dst = *src; + dst->flags = GPU_CMD_VERTEX_EOL; + dst->xyz[0] -= half_size; dst->xyz[1] -= half_size; + dst--; + + *dst = *src; + dst->xyz[0] += half_size; dst->xyz[1] -= half_size; + dst--; + + *dst = *src; + dst->xyz[0] -= half_size; dst->xyz[1] += half_size; + dst--; + + *dst = *src; + dst->xyz[0] += half_size; dst->xyz[1] += half_size; + dst--; + } +} + +// Heavily based on the pvrline example by jnmartin84 +// Which is based on https://devcry.heiho.net/html/2017/20170820-opengl-line-drawing.html +static Vertex* draw_line(Vertex* dst, Vertex* v1, Vertex* v2) { + Vertex ov1 = *v1; + Vertex ov2 = *v2; + // TODO don't copy unless dst might overlap v1/v2 + + // Essentially "expands" a line into a quad by + // 1) Calculating normal of the line from v1 to v2 + // 2) Scaling normal by the line width + // 3) Offseting the endpoints wrt the scaled normal + float dx = ov2.xyz[0] - ov1.xyz[0]; + float dy = ov2.xyz[1] - ov1.xyz[1]; + + float inverse_mag = fast_rsqrt((dx*dx) + (dy*dy)) * HALF_LINE_WIDTH; + float nx = -dy * inverse_mag; + float ny = dx * inverse_mag; + + // Expand first endpoint both "up" and "down" + *dst = ov1; + dst->flags = GPU_CMD_VERTEX_EOL; + dst->xyz[0] += nx; + dst->xyz[1] += ny; + dst--; + + *dst = ov1; + dst->xyz[0] -= nx; + dst->xyz[1] -= ny; + dst--; + + // Expand second endpoint both "up" and "down" + *dst = ov2; + dst->xyz[0] += nx; + dst->xyz[1] += ny; + dst--; + + *dst = ov2; + dst->xyz[0] -= nx; + dst->xyz[1] -= ny; + dst--; + + return dst; +} + +#define LINES_COUNT(count) (((count) / 2) * 4) +static GL_NO_INLINE void genLines(Vertex* output, GLuint count) { + Vertex* dst = output + LINES_COUNT(count) - 1; + Vertex* src = output + count - 1; + + // Draws line using two vertices + for (; count >= 2; count -= 2, src -= 2) { + dst = draw_line(dst, src, src - 1); + } +} + +#define LINE_STRIP_COUNT(count) (((count) - 1) * 4) +static GL_NO_INLINE void genLineStrip(Vertex* output, GLuint count) { + Vertex* dst = output + LINE_STRIP_COUNT(count) - 1; + Vertex* src = output + count - 1; + + // Draws line using current and prior vertex + for (; count > 1; count--, src--) { + dst = draw_line(dst, src, src - 1); + } +} + +#define LINE_LOOP_COUNT(count) ((count) * 4) +static GL_NO_INLINE void genLineLoop(Vertex* output, GLuint count) { + Vertex* dst = output + LINE_LOOP_COUNT(count) - 1; + Vertex* src = output + count - 1; + Vertex last = *src, first = *output; + + // Draws line using current and prior vertex + for (; count > 1; count--, src--) { + dst = draw_line(dst, src, src - 1); + } + + // Connect first and last vertex + draw_line(dst, &first, &last); +} + typedef void (*ReadPositionFunc)(const GLubyte*, GLubyte*); typedef void (*ReadDiffuseFunc)(const GLubyte*, GLubyte*); typedef void (*ReadUVFunc)(const GLubyte*, GLubyte*); @@ -891,11 +1017,28 @@ static void generate(SubmissionTarget* target, const GLenum mode, const GLsizei case GL_QUADS: genQuads(it, count); break; + case GL_TRIANGLE_STRIP: + genTriangleStrip(it, count); + break; + + case GL_QUAD_STRIP: + genQuadStrip(it, count); + break; case GL_TRIANGLE_FAN: genTriangleFan(it, count); break; - case GL_TRIANGLE_STRIP: - genTriangleStrip(it, count); + + case GL_POINTS: + genPoints(it, count); + break; + case GL_LINES: + genLines(it, count); + break; + case GL_LINE_STRIP: + genLineStrip(it, count); + break; + case GL_LINE_LOOP: + genLineLoop(it, count); break; default: gl_assert(0 && "Not Implemented"); @@ -1093,6 +1236,23 @@ void _glInitSubmissionTarget() { target->extras = &VERTEX_EXTRAS; } +GL_FORCE_INLINE GLuint calcFinalVertices(GLenum mode, GLuint count) { + switch (mode) { + case GL_POINTS: + return POINTS_COUNT(count); + case GL_LINE_LOOP: + return LINE_LOOP_COUNT(count); + case GL_LINE_STRIP: + return LINE_STRIP_COUNT(count); + case GL_LINES: + return LINES_COUNT(count); + case GL_TRIANGLE_FAN: + return TRIFAN_COUNT(count); + case GL_QUAD_STRIP: + return QUADSTRIP_COUNT(count); + } + return count; +} GL_FORCE_INLINE void submitVertices(GLenum mode, GLsizei first, GLuint count, GLenum type, const GLvoid* indices) { @@ -1102,14 +1262,10 @@ GL_FORCE_INLINE void submitVertices(GLenum mode, GLsizei first, GLuint count, GL TRACE(); /* Do nothing if vertices aren't enabled */ - if(!(ENABLED_VERTEX_ATTRIBUTES & VERTEX_ENABLED_FLAG)) { - return; - } + if(!(ENABLED_VERTEX_ATTRIBUTES & VERTEX_ENABLED_FLAG)) return; /* No vertices? Do nothing */ - if(!count) { - return; - } + if(!count) return; /* Polygons are treated as triangle fans, the only time this would be a * problem is if we supported glPolygonMode(..., GL_LINE) but we don't. @@ -1131,14 +1287,6 @@ GL_FORCE_INLINE void submitVertices(GLenum mode, GLsizei first, GLuint count, GL } } - if(mode == GL_LINE_STRIP || mode == GL_LINES) { - fprintf(stderr, "Line drawing is currently unsupported\n"); - return; - } - - // We don't handle this any further, so just make sure we never pass it down */ - gl_assert(mode != GL_POLYGON); - target->output = _glActivePolyList(); gl_assert(target->output); gl_assert(extras); @@ -1147,7 +1295,7 @@ GL_FORCE_INLINE void submitVertices(GLenum mode, GLsizei first, GLuint count, GL GLboolean header_required = (vector_size == 0) || _glGPUStateIsDirty(); - target->count = (mode == GL_TRIANGLE_FAN) ? ((count - 2) * 3) : count; + target->count = calcFinalVertices(mode, count); target->header_offset = vector_size; target->start_offset = target->header_offset + (header_required ? 1 : 0); diff --git a/GL/private.h b/GL/private.h index 31a7872..8230f01 100644 --- a/GL/private.h +++ b/GL/private.h @@ -25,6 +25,7 @@ extern void* memcpy4 (void *dest, const void *src, size_t count); #define GL_NO_INSTRUMENT inline __attribute__((no_instrument_function)) #define GL_INLINE_DEBUG GL_NO_INSTRUMENT __attribute__((always_inline)) #define GL_FORCE_INLINE static GL_INLINE_DEBUG +#define GL_NO_INLINE __attribute__((noinline)) #define _GL_UNUSED(x) (void)(x) #define _PACK4(v) ((v * 0xF) / 0xFF) @@ -259,6 +260,12 @@ do { \ memcpy_vertex(b, &c); \ } while(0) +#ifdef __DREAMCAST__ +#define fast_rsqrt(x) frsqrt(x) +#else +#define fast_rsqrt(x) (1.0f / __builtin_sqrtf(x)) +#endif + /* ClipVertex doesn't have room for these, so we need to parse them * out separately. Potentially 'w' will be housed here if we support oargb */ typedef struct { @@ -320,6 +327,9 @@ void _glMatrixLoadModelViewProjection(); extern GLfloat DEPTH_RANGE_MULTIPLIER_L; extern GLfloat DEPTH_RANGE_MULTIPLIER_H; +extern GLfloat HALF_LINE_WIDTH; +extern GLfloat HALF_POINT_SIZE; + Matrix4x4* _glGetProjectionMatrix(); Matrix4x4* _glGetModelViewMatrix(); diff --git a/GL/state.c b/GL/state.c index ef7780f..fd28b2a 100644 --- a/GL/state.c +++ b/GL/state.c @@ -3,7 +3,8 @@ #include #include "private.h" - +GLfloat HALF_LINE_WIDTH = 1.0f / 2.0f; +GLfloat HALF_POINT_SIZE = 1.0f / 2.0f; static struct { GLboolean is_dirty; @@ -773,7 +774,11 @@ GLAPI void APIENTRY glAlphaFunc(GLenum func, GLclampf ref) { } void glLineWidth(GLfloat width) { - _GL_UNUSED(width); + HALF_LINE_WIDTH = width / 2.0f; +} + +void glPointSize(GLfloat size) { + HALF_POINT_SIZE = size / 2.0f; } void glPolygonOffset(GLfloat factor, GLfloat units) { diff --git a/include/GL/gl.h b/include/GL/gl.h index 61d434b..d3914de 100644 --- a/include/GL/gl.h +++ b/include/GL/gl.h @@ -552,6 +552,10 @@ GLAPI GLvoid APIENTRY glRecti(GLint x1, GLint y1, GLint x2, GLint y2); GLAPI GLvoid APIENTRY glRectiv(const GLint *v1, const GLint *v2); #define glRectsv glRectiv +/* Primitive configuration */ +GLAPI void APIENTRY glLineWidth(GLfloat width); +GLAPI void APIENTRY glPointSize(GLfloat size); + /* Enable / Disable Capability */ /* Currently Supported Capabilities: GL_TEXTURE_2D @@ -742,7 +746,6 @@ GLAPI GLenum APIENTRY glGetError(void); /* Non Operational Stubs for portability */ GLAPI void APIENTRY glAlphaFunc(GLenum func, GLclampf ref); -GLAPI void APIENTRY glLineWidth(GLfloat width); GLAPI void APIENTRY glPolygonMode(GLenum face, GLenum mode); GLAPI void APIENTRY glPolygonOffset(GLfloat factor, GLfloat units); GLAPI void APIENTRY glGetTexParameterfv(GLenum target, GLenum pname, GLfloat *params); diff --git a/samples/primitive_modes/main.c b/samples/primitive_modes/main.c new file mode 100644 index 0000000..db793cc --- /dev/null +++ b/samples/primitive_modes/main.c @@ -0,0 +1,189 @@ +#include +#include +#include + +#ifdef SDL2_BUILD +#include +static SDL_Window* win_handle; +#else +#include +#endif + +static void DrawLineLoop(float y) { + glBegin(GL_LINE_LOOP); + glColor3f(1.0f, 0.0f, 0.0f); glVertex2f(410.0f, y + 10.0f); + glColor3f(0.0f, 1.0f, 0.0f); glVertex2f(490.0f, y + 10.0f); + glColor3f(0.0f, 0.0f, 1.0f); glVertex2f(490.0f, y + 90.0f); + glColor3f(1.0f, 1.0f, 1.0f); glVertex2f(410.0f, y + 90.0f); + glEnd(); +} + +static void DrawLineStrip(float y) { + glBegin(GL_LINE_STRIP); + glColor3f(1.0f, 0.0f, 0.0f); glVertex2f(310.0f, y + 10.0f); + glColor3f(0.0f, 1.0f, 0.0f); glVertex2f(390.0f, y + 10.0f); + glColor3f(0.0f, 0.0f, 1.0f); glVertex2f(390.0f, y + 90.0f); + glColor3f(1.0f, 1.0f, 1.0f); glVertex2f(310.0f, y + 90.0f); + glEnd(); +} + +static void DrawLine(float y) { + glBegin(GL_LINES); + glColor3f(1.0f, 0.0f, 0.0f); glVertex2f(210.0f, y + 10.0f); + glColor3f(0.0f, 1.0f, 0.0f); glVertex2f(290.0f, y + 10.0f); + glColor3f(0.0f, 0.0f, 1.0f); glVertex2f(290.0f, y + 90.0f); + glColor3f(1.0f, 1.0f, 1.0f); glVertex2f(210.0f, y + 90.0f); + + glColor3f(1.0f, 0.0f, 0.0f); glVertex2f(230.0f, y + 25.0f); + glColor3f(0.0f, 1.0f, 0.0f); glVertex2f(250.0f, y + 75.0f); + glColor3f(0.0f, 0.0f, 1.0f); glVertex2f(260.0f, y + 75.0f); + glColor3f(1.0f, 1.0f, 1.0f); glVertex2f(280.0f, y + 45.0f); + glEnd(); +} + +static void DrawPoint(float y) { + glBegin(GL_POINTS); + glColor3f(1.0f, 0.0f, 0.0f); glVertex2f(110.0f, y + 10.0f); + glColor3f(0.0f, 1.0f, 0.0f); glVertex2f(190.0f, y + 10.0f); + glColor3f(0.0f, 0.0f, 1.0f); glVertex2f(190.0f, y + 90.0f); + glColor3f(1.0f, 1.0f, 1.0f); glVertex2f(110.0f, y + 90.0f); + glEnd(); +} + +static void DrawQuad(float y) { + glBegin(GL_QUADS); + glColor3f(1.0f, 0.0f, 0.0f); glVertex2f(10.0f, y + 10.0f); + glColor3f(0.0f, 1.0f, 0.0f); glVertex2f(90.0f, y + 10.0f); + glColor3f(0.0f, 0.0f, 1.0f); glVertex2f(90.0f, y + 90.0f); + glColor3f(1.0f, 1.0f, 1.0f); glVertex2f(10.0f, y + 90.0f); + + glColor3f(1.0f, 0.0f, 0.0f); glVertex2f(110.0f, y + 10.0f); + glColor3f(0.0f, 1.0f, 0.0f); glVertex2f(190.0f, y + 10.0f); + glColor3f(0.0f, 0.0f, 1.0f); glVertex2f(190.0f, y + 90.0f); + glColor3f(1.0f, 1.0f, 1.0f); glVertex2f(110.0f, y + 90.0f); + glEnd(); +} + +static void DrawQuadStrip(float y) { + glBegin(GL_QUAD_STRIP); + glColor3f(1.0f, 0.0f, 0.0f); glVertex2f(210.0f, y + 10.0f); + glColor3f(0.0f, 1.0f, 0.0f); glVertex2f(290.0f, y + 10.0f); + glColor3f(0.0f, 0.0f, 1.0f); glVertex2f(290.0f, y + 90.0f); + glColor3f(1.0f, 1.0f, 1.0f); glVertex2f(210.0f, y + 90.0f); + + glColor3f(1.0f, 0.0f, 0.0f); glVertex2f(310.0f, y + 10.0f); + glColor3f(0.0f, 1.0f, 0.0f); glVertex2f(390.0f, y + 10.0f); + glColor3f(0.0f, 0.0f, 1.0f); glVertex2f(390.0f, y + 90.0f); + glColor3f(1.0f, 1.0f, 1.0f); glVertex2f(310.0f, y + 90.0f); + glEnd(); +} + +static void DrawTriList(float y) { + glBegin(GL_TRIANGLES); + glColor3f(1.0f, 0.0f, 0.0f); glVertex2f( 10.0f, y + 10.0f); + glColor3f(0.0f, 1.0f, 0.0f); glVertex2f( 90.0f, y + 10.0f); + glColor3f(0.0f, 0.0f, 1.0f); glVertex2f( 90.0f, y + 90.0f); + + glColor3f(1.0f, 0.0f, 0.0f); glVertex2f(110.0f, y + 10.0f); + glColor3f(0.0f, 1.0f, 0.0f); glVertex2f(190.0f, y + 10.0f); + glColor3f(0.0f, 0.0f, 1.0f); glVertex2f(190.0f, y + 90.0f); + glEnd(); +} + +static void DrawTriStrip(float y) { + glBegin(GL_TRIANGLE_STRIP); + glColor3f(1.0f, 0.0f, 0.0f); glVertex2f(210.0f, y + 10.0f); + glColor3f(0.0f, 1.0f, 0.0f); glVertex2f(290.0f, y + 10.0f); + glColor3f(0.0f, 0.0f, 1.0f); glVertex2f(290.0f, y + 90.0f); + glColor3f(1.0f, 1.0f, 1.0f); glVertex2f(210.0f, y + 90.0f); + + glColor3f(1.0f, 0.0f, 0.0f); glVertex2f(310.0f, y + 10.0f); + glColor3f(0.0f, 1.0f, 0.0f); glVertex2f(390.0f, y + 10.0f); + glColor3f(0.0f, 0.0f, 1.0f); glVertex2f(390.0f, y + 90.0f); + glColor3f(1.0f, 1.0f, 1.0f); glVertex2f(310.0f, y + 90.0f); + glEnd(); +} + + +static void DrawTriFan(float y) { + glBegin(GL_TRIANGLE_FAN); + glColor3f(1.0f, 0.0f, 0.0f); glVertex2f(410.0f, y + 10.0f); + glColor3f(0.0f, 1.0f, 0.0f); glVertex2f(490.0f, y + 10.0f); + glColor3f(0.0f, 0.0f, 1.0f); glVertex2f(445.0f, y + 90.0f); + + glColor3f(0.0f, 1.0f, 0.0f); glVertex2f(590.0f, y + 10.0f); + glColor3f(1.0f, 1.0f, 1.0f); glVertex2f(510.0f, y + 45.0f); + glEnd(); +} + +int main(int argc, char *argv[]) { +#ifdef SDL2_BUILD + SDL_Init(SDL_INIT_EVERYTHING); + win_handle = SDL_CreateWindow("Shapes", 0, 0, 640, 480, SDL_WINDOW_OPENGL); + SDL_GL_CreateContext(win_handle); +#else + glKosInit(); +#endif + + glClearColor(0.5f, 0.5f, 0.5f, 1); + glViewport(0, 0, 640, 480); + + float mat[4][4] = { 0 }; + float L = 0, T = 0, R = 640, B = 480, N = -100, F = 100; + mat[0][0] = 2.0f / (R - L); + mat[1][1] = 2.0f / (T - B); + mat[2][2] = -2.0f / (F - N); + mat[3][0] = -(R + L) / (R - L); + mat[3][1] = -(T + B) / (T - B); + mat[3][2] = -(F + N) / (F - N); + mat[3][3] = 1; + glLoadMatrixf(mat); + + int running = 1; + + while (running) { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + +#ifdef SDL2_BUILD + SDL_Event event; + while (SDL_PollEvent(&event)) { + if(event.type == SDL_QUIT) running = 0; + } +#endif + + DrawTriStrip(300); + DrawTriFan(300); + DrawTriList(300); + + DrawQuadStrip(200); + DrawQuad(200); + + glPointSize(5); + DrawPoint(100); + glLineWidth(0.5f); + DrawLineLoop(100); + glLineWidth(2); + DrawLineStrip(100); + glLineWidth(10); + DrawLine(100); + + glPointSize(1); + glLineWidth(1); + DrawPoint(0); + DrawLineLoop(0); + DrawLineStrip(0); + DrawLine(0); + + glShadeModel(GL_FLAT); + DrawQuad(400); + DrawTriStrip(400); + glShadeModel(GL_SMOOTH); + +#ifdef SDL2_BUILD + SDL_GL_SwapWindow(win_handle); +#else + glKosSwapBuffers(); +#endif + } + return 0; +}