diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b08891..dd69e25 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,6 +80,7 @@ set( GL/matrix.c GL/state.c GL/texture.c + GL/tnl_effects.c GL/util.c GL/alloc/alloc.c ${CMAKE_CURRENT_BINARY_DIR}/version.c @@ -212,6 +213,7 @@ 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) +gen_sample(tnl_effects samples/tnl_effects/main.c) if(PLATFORM_DREAMCAST) gen_sample(trimark samples/trimark/main.c) diff --git a/GL/attributes.c b/GL/attributes.c index aedb27e..3864c21 100644 --- a/GL/attributes.c +++ b/GL/attributes.c @@ -338,7 +338,7 @@ static ReadAttributeFunc calcReadDiffuseFunc(void) { static void _fillZero2f(const GLubyte* __restrict__ input, GLubyte* __restrict__ out) { _GL_UNUSED(input); //memset(out, 0, sizeof(float) * 2); - // memset does 8 byte writes - faster to manually write as uint32 + // memset does 8 individual byte writes - faster to manually write as uint32 uint32_t* dst = (uint32_t*)out; dst[0] = 0; dst[1] = 0; diff --git a/GL/draw.c b/GL/draw.c index f39e00e..5807ce1 100644 --- a/GL/draw.c +++ b/GL/draw.c @@ -367,7 +367,6 @@ static void generateElements( Vertex* output = _glSubmissionTargetStart(target); VertexExtra* ve = aligned_vector_at(target->extras, 0); - float pos[3], w = 1.0f; uint32_t i = first; uint32_t idx = 0; @@ -598,37 +597,6 @@ static void generate(SubmissionTarget* target, const GLenum mode, const GLsizei } } -static void transform(SubmissionTarget* target) { - TRACE(); - - /* Perform modelview transform, storing W */ - Vertex* it = _glSubmissionTargetStart(target); - int count = target->count; - - for(int i = 0; i < count; ++i, ++it) { - TransformVertex(it->xyz[0], it->xyz[1], it->xyz[2], it->w, - it->xyz, &it->w); - } -} - -static void mat_transform_normal3(VertexExtra* extra, const uint32_t count) { - ITERATE(count) { - TransformNormalNoMod(extra->nxyz, extra->nxyz); - extra++; - } -} - -static void light(SubmissionTarget* target) { - /* Perform lighting calculations and manipulate the colour */ - Vertex* vertex = _glSubmissionTargetStart(target); - VertexExtra* extra = aligned_vector_at(target->extras, 0); - - _glMatrixLoadNormal(); - mat_transform_normal3(extra, target->count); - - _glPerformLighting(vertex, extra, target->count); -} - GL_FORCE_INLINE int _calc_pvr_face_culling() { if(!_glIsCullingEnabled()) { return GPU_CULLING_SMALL; @@ -850,28 +818,11 @@ GL_FORCE_INLINE void submitVertices(GLenum mode, GLsizei first, GLuint count, GL _glGPUStateMarkClean(); } - /* If we're lighting, then we need to do some work in - * eye-space, so we only transform vertices by the modelview - * matrix, and then later multiply by projection. - * - * If we're not doing lighting though we can optimise by taking - * vertices straight to clip-space */ - - if(_glIsLightingEnabled()) { - _glMatrixLoadModelView(); - } else { - _glMatrixLoadModelViewProjection(); - } + _glTnlLoadMatrix(); generate(target, mode, first, count, (GLubyte*) indices, type); - if(_glIsLightingEnabled()){ - light(target); - - /* OK eye-space work done, now move into clip space */ - _glMatrixLoadProjection(); - transform(target); - } + _glTnlApplyEffects(target); // /* // Now, if multitexturing is enabled, we want to send exactly the same vertices again, except: diff --git a/GL/matrix.c b/GL/matrix.c index 452fae9..8d6ba45 100644 --- a/GL/matrix.c +++ b/GL/matrix.c @@ -19,7 +19,7 @@ static Matrix4x4 __attribute__((aligned(32))) VIEWPORT_MATRIX; static Matrix4x4 __attribute__((aligned(32))) PROJECTION_MATRIX; static GLenum MATRIX_MODE = GL_MODELVIEW; -static GLubyte MATRIX_IDX = 0; +static Stack* MATRIX_CUR; static GLboolean NORMAL_DIRTY, PROJECTION_DIRTY; static const Matrix4x4 __attribute__((aligned(32))) IDENTITY = { @@ -31,29 +31,45 @@ static const Matrix4x4 __attribute__((aligned(32))) IDENTITY = { GLfloat NEAR_PLANE_DISTANCE = 0.0f; +Matrix4x4* _glGetModelViewMatrix() { + return (Matrix4x4*) stack_top(&MATRIX_STACKS[0]); +} + Matrix4x4* _glGetProjectionMatrix() { return (Matrix4x4*) stack_top(&MATRIX_STACKS[1]); } -Matrix4x4* _glGetModelViewMatrix() { - return (Matrix4x4*) stack_top(&MATRIX_STACKS[0]); +Matrix4x4* _glGetTextureMatrix() { + return (Matrix4x4*) stack_top(MATRIX_STACKS + (GL_TEXTURE & 0xF)); +} + +Matrix4x4* _glGetColorMatrix() { + return (Matrix4x4*) stack_top(MATRIX_STACKS + (GL_COLOR & 0xF)); } GLenum _glGetMatrixMode() { return MATRIX_MODE; } +GLboolean _glIsIdentity(const Matrix4x4* m) { + return memcmp(m, IDENTITY, sizeof(Matrix4x4)) == 0; +} + void _glInitMatrices() { init_stack(&MATRIX_STACKS[0], sizeof(Matrix4x4), 32); init_stack(&MATRIX_STACKS[1], sizeof(Matrix4x4), 32); init_stack(&MATRIX_STACKS[2], sizeof(Matrix4x4), 32); + init_stack(&MATRIX_STACKS[3], sizeof(Matrix4x4), 32); stack_push(&MATRIX_STACKS[0], IDENTITY); stack_push(&MATRIX_STACKS[1], IDENTITY); stack_push(&MATRIX_STACKS[2], IDENTITY); + stack_push(&MATRIX_STACKS[3], IDENTITY); MEMCPY4(NORMAL_MATRIX, IDENTITY, sizeof(Matrix4x4)); + MATRIX_CUR = MATRIX_STACKS + (GL_MODELVIEW & 0xF); + const VideoMode* vid_mode = GetVideoMode(); glDepthRange(0.0f, 1.0f); @@ -114,37 +130,49 @@ static void UpdateNormalMatrix() { } static void OnMatrixChanged() { - if(MATRIX_MODE == GL_MODELVIEW) NORMAL_DIRTY = true; - if(MATRIX_MODE == GL_PROJECTION) PROJECTION_DIRTY = true; + switch (MATRIX_MODE) { + case GL_MODELVIEW: + NORMAL_DIRTY = true; + return; + case GL_PROJECTION: + PROJECTION_DIRTY = true; + return; + case GL_TEXTURE: + _glTnlUpdateTextureMatrix(); + return; + case GL_COLOR: + _glTnlUpdateColorMatrix(); + return; + } } void APIENTRY glMatrixMode(GLenum mode) { MATRIX_MODE = mode; - MATRIX_IDX = mode & 0xF; + MATRIX_CUR = MATRIX_STACKS + (mode & 0xF); } void APIENTRY glPushMatrix() { - void* top = stack_top(MATRIX_STACKS + MATRIX_IDX); + void* top = stack_top(MATRIX_CUR); assert(top); - void* ret = stack_push(MATRIX_STACKS + MATRIX_IDX, top); + void* ret = stack_push(MATRIX_CUR, top); (void) ret; assert(ret); OnMatrixChanged(); } void APIENTRY glPopMatrix() { - stack_pop(MATRIX_STACKS + MATRIX_IDX); + stack_pop(MATRIX_CUR); OnMatrixChanged(); } void APIENTRY glLoadIdentity() { - stack_replace(MATRIX_STACKS + MATRIX_IDX, IDENTITY); + stack_replace(MATRIX_CUR, IDENTITY); OnMatrixChanged(); } void GL_FORCE_INLINE _glMultMatrix(const Matrix4x4* mat) { - void* top = stack_top(MATRIX_STACKS + MATRIX_IDX); + void* top = stack_top(MATRIX_CUR); UploadMatrix4x4(top); MultiplyMatrix4x4(mat); @@ -220,7 +248,7 @@ void APIENTRY glLoadMatrixf(const GLfloat *m) { static Matrix4x4 __attribute__((aligned(32))) TEMP; memcpy(TEMP, m, sizeof(float) * 16); - stack_replace(MATRIX_STACKS + MATRIX_IDX, TEMP); + stack_replace(MATRIX_CUR, TEMP); OnMatrixChanged(); } @@ -309,7 +337,7 @@ void glLoadTransposeMatrixf(const GLfloat *m) { TEMP[M14] = m[11]; TEMP[M15] = m[15]; - stack_replace(MATRIX_STACKS + MATRIX_IDX, TEMP); + stack_replace(MATRIX_CUR, TEMP); OnMatrixChanged(); } @@ -433,10 +461,6 @@ void gluLookAt(GLdouble eyex, GLdouble eyey, GLdouble eyez, GLdouble centerx, DownloadMatrix4x4(stack_top(MATRIX_STACKS + (GL_MODELVIEW & 0xF))); } -void _glMatrixLoadTexture() { - UploadMatrix4x4((const Matrix4x4*) stack_top(MATRIX_STACKS + (GL_TEXTURE & 0xF))); -} - void _glMatrixLoadModelView() { UploadMatrix4x4((const Matrix4x4*) stack_top(MATRIX_STACKS + (GL_MODELVIEW & 0xF))); } diff --git a/GL/private.h b/GL/private.h index db8fe47..b890b05 100644 --- a/GL/private.h +++ b/GL/private.h @@ -321,7 +321,6 @@ void _glInitSubmissionTarget(); void _glMatrixLoadNormal(); void _glMatrixLoadModelView(); void _glMatrixLoadProjection(); -void _glMatrixLoadTexture(); void _glMatrixLoadModelViewProjection(); extern GLfloat DEPTH_RANGE_MULTIPLIER_L; @@ -332,7 +331,10 @@ extern GLfloat HALF_POINT_SIZE; Matrix4x4* _glGetProjectionMatrix(); Matrix4x4* _glGetModelViewMatrix(); +Matrix4x4* _glGetTextureMatrix(); +Matrix4x4* _glGetColorMatrix(); GLenum _glGetMatrixMode(); +GLboolean _glIsIdentity(const Matrix4x4* m); void _glWipeTextureOnFramebuffers(GLuint texture); @@ -485,6 +487,13 @@ void _glGPUStateMarkDirty(); #define SCENE_AMBIENT_MASK 16 +void _glTnlLoadMatrix(void); +void _glTnlApplyEffects(SubmissionTarget* target); + +void _glTnlUpdateLighting(void); +void _glTnlUpdateTextureMatrix(void); +void _glTnlUpdateColorMatrix(void); + /* This is from KOS pvr_buffers.c */ #define PVR_MIN_Z 0.0001f diff --git a/GL/state.c b/GL/state.c index cac3fba..88aaad0 100644 --- a/GL/state.c +++ b/GL/state.c @@ -428,7 +428,6 @@ void _glInitContext() { glDisable(GL_DEPTH_TEST); glDisable(GL_TEXTURE_2D); glDisable(GL_FOG); - glDisable(GL_LIGHTING); GLubyte i; for(i = 0; i < MAX_GLDC_LIGHTS; ++i) { @@ -472,7 +471,7 @@ GLAPI void APIENTRY glEnable(GLenum cap) { case GL_LIGHTING: { if(GPUState.lighting_enabled != GL_TRUE) { GPUState.lighting_enabled = GL_TRUE; - GPUState.is_dirty = GL_TRUE; + _glTnlUpdateLighting(); } } break; case GL_FOG: @@ -583,7 +582,7 @@ GLAPI void APIENTRY glDisable(GLenum cap) { case GL_LIGHTING: { if(GPUState.lighting_enabled != GL_FALSE) { GPUState.lighting_enabled = GL_FALSE; - GPUState.is_dirty = GL_TRUE; + _glTnlUpdateLighting(); } } break; case GL_FOG: @@ -979,6 +978,13 @@ void APIENTRY glGetFloatv(GLenum pname, GLfloat* params) { case GL_MODELVIEW_MATRIX: MEMCPY4(params, _glGetModelViewMatrix(), sizeof(float) * 16); break; + case GL_TEXTURE_MATRIX: + MEMCPY4(params, _glGetTextureMatrix(), sizeof(float) * 16); + break; + case GL_COLOR_MATRIX: + MEMCPY4(params, _glGetColorMatrix(), sizeof(float) * 16); + break; + case GL_POLYGON_OFFSET_FACTOR: *params = GPUState.offset_factor; break; diff --git a/GL/texture.c b/GL/texture.c index 2de56fb..2f34ab7 100644 --- a/GL/texture.c +++ b/GL/texture.c @@ -1477,14 +1477,14 @@ static bool _glTexImage2DValidate(GLenum target, GLint level, GLint internalForm if(format != GL_COLOR_INDEX4_EXT && format != GL_COLOR_INDEX4_TWID_KOS) { /* Abuse determineStride to see if type is valid */ if(_determineStride(GL_RGBA, type) == -1) { - INFO_MSG(""); + INFO_MSG("Unsupported type"); _glKosThrowError(GL_INVALID_ENUM, __func__); return false; } } if(_cleanInternalFormat(internalFormat) == -1) { - INFO_MSG(""); + INFO_MSG("Unsupported internal format"); _glKosThrowError(GL_INVALID_VALUE, __func__); return false; } @@ -1494,7 +1494,7 @@ static bool _glTexImage2DValidate(GLenum target, GLint level, GLint internalForm if(level == 0){ if((w < 8 || (w & -w) != w)) { /* Width is not a power of two. Must be!*/ - INFO_MSG(""); + INFO_MSG("Unsupported width"); _glKosThrowError(GL_INVALID_VALUE, __func__); return false; } @@ -1502,7 +1502,7 @@ static bool _glTexImage2DValidate(GLenum target, GLint level, GLint internalForm if((h < 8 || (h & -h) != h)) { /* height is not a power of two. Must be!*/ - INFO_MSG(""); + INFO_MSG("Unsupported height"); _glKosThrowError(GL_INVALID_VALUE, __func__); return false; } @@ -1517,7 +1517,7 @@ static bool _glTexImage2DValidate(GLenum target, GLint level, GLint internalForm } if(level < 0) { - INFO_MSG(""); + INFO_MSG("Level must be >= 0"); _glKosThrowError(GL_INVALID_VALUE, __func__); return false; } @@ -1530,7 +1530,7 @@ static bool _glTexImage2DValidate(GLenum target, GLint level, GLint internalForm } if(border) { - INFO_MSG(""); + INFO_MSG("Border not allowed"); _glKosThrowError(GL_INVALID_VALUE, __func__); return false; } diff --git a/GL/tnl_effects.c b/GL/tnl_effects.c new file mode 100644 index 0000000..c63722c --- /dev/null +++ b/GL/tnl_effects.c @@ -0,0 +1,139 @@ +#include +#include +#include +#include +#include +#include + +#include "private.h" +#include "platform.h" + +static int TNL_EFFECTS, TNL_LIGHTING, TNL_TEXTURE, TNL_COLOR; + +#define ITERATE(count) \ + GLuint i = count; \ + while(i--) + +void _glTnlLoadMatrix(void) { + /* If we're lighting, then we need to do some work in + * eye-space, so we only transform vertices by the modelview + * matrix, and then later multiply by projection. + * + * If we're not doing lighting though we can optimise by taking + * vertices straight to clip-space */ + + if(TNL_LIGHTING) { + _glMatrixLoadModelView(); + } else { + _glMatrixLoadModelViewProjection(); + } +} + +static void updateEffects(void) { + TNL_EFFECTS = TNL_LIGHTING | TNL_TEXTURE | TNL_COLOR; +} + +static void transformVertices(SubmissionTarget* target) { + TRACE(); + + /* Perform modelview transform, storing W */ + Vertex* it = _glSubmissionTargetStart(target); + uint32_t count = target->count; + + ITERATE(count) { + TransformVertex(it->xyz[0], it->xyz[1], it->xyz[2], it->w, + it->xyz, &it->w); + it++; + } +} + + +static void mat_transform_normal3(VertexExtra* extra, const uint32_t count) { + ITERATE(count) { + TransformNormalNoMod(extra->nxyz, extra->nxyz); + extra++; + } +} + +static void lightingEffect(SubmissionTarget* target) { + /* Perform lighting calculations and manipulate the colour */ + Vertex* vertex = _glSubmissionTargetStart(target); + VertexExtra* extra = aligned_vector_at(target->extras, 0); + + _glMatrixLoadNormal(); + mat_transform_normal3(extra, target->count); + + _glPerformLighting(vertex, extra, target->count); +} + +void _glTnlUpdateLighting(void) { + TNL_LIGHTING = _glIsLightingEnabled(); + updateEffects(); +} + + +static void textureEffect(SubmissionTarget* target) { + Matrix4x4* m = _glGetTextureMatrix(); + UploadMatrix4x4(m); + float coords[4]; + float* ptr = (float*)m; + + Vertex* it = _glSubmissionTargetStart(target); + uint32_t count = target->count; + + ITERATE(count) { + TransformVertex(it->uv[0], it->uv[1], 0.0f, 1.0f, coords, &coords[3]); + it->uv[0] = coords[0]; + it->uv[1] = coords[1]; + it++; + } +} + +void _glTnlUpdateTextureMatrix(void) { + Matrix4x4* m = _glGetTextureMatrix(); + TNL_TEXTURE = !_glIsIdentity(m); + updateEffects(); +} + + +static void colorEffect(SubmissionTarget* target) { + Matrix4x4* m = _glGetColorMatrix(); + UploadMatrix4x4(m); + float coords[4]; + + Vertex* it = _glSubmissionTargetStart(target); + uint32_t count = target->count; + + ITERATE(count) { + TransformVertex(it->bgra[2], it->bgra[1], it->bgra[0], it->bgra[3], coords, &coords[3]); + it->bgra[2] = clamp(coords[0], 0, 255); + it->bgra[1] = clamp(coords[1], 0, 255); + it->bgra[0] = clamp(coords[2], 0, 255); + it->bgra[3] = clamp(coords[3], 0, 255); + it++; + } +} + +void _glTnlUpdateColorMatrix(void) { + Matrix4x4* m = _glGetColorMatrix(); + TNL_COLOR = !_glIsIdentity(m); + updateEffects(); +} + + +void _glTnlApplyEffects(SubmissionTarget* target) { + if (!TNL_EFFECTS) return; + + if (TNL_LIGHTING) + lightingEffect(target); + if (TNL_TEXTURE) + textureEffect(target); + if (TNL_COLOR) + colorEffect(target); + + if (TNL_LIGHTING) { + /* OK eye-space work done, now move into clip space */ + _glMatrixLoadProjection(); + transformVertices(target); + } +} diff --git a/include/GL/gl.h b/include/GL/gl.h index c26f456..f6c7f4a 100644 --- a/include/GL/gl.h +++ b/include/GL/gl.h @@ -69,10 +69,12 @@ __BEGIN_DECLS #define GL_MODELVIEW 0x1700 #define GL_PROJECTION 0x1701 #define GL_TEXTURE 0x1702 +#define GL_COLOR 0x1703 /* NOTE: Not the usual value */ #define GL_MODELVIEW_MATRIX 0x0BA6 #define GL_PROJECTION_MATRIX 0x0BA7 #define GL_TEXTURE_MATRIX 0x0BA8 +#define GL_COLOR_MATRIX 0x80B1 /* Depth buffer */ #define GL_NEVER 0x0200 diff --git a/samples/tnl_effects/main.c b/samples/tnl_effects/main.c new file mode 100644 index 0000000..4f063d5 --- /dev/null +++ b/samples/tnl_effects/main.c @@ -0,0 +1,138 @@ +#include +#include +#include + +#ifndef _arch_dreamcast +#include +static SDL_Window* win_handle; +#else +#include +#include +#endif + +static void DrawQuad(float x, float y) { + glBegin(GL_QUADS); + x -= 1.0f; + y -= 1.0f; + glColor3f(0.5f, 0.5f, 0.5f); glTexCoord2f(0.0f, 0.0f); glVertex2f(x + 0.0f, y + 0.0f); + glColor3f(0.5f, 0.5f, 0.5f); glTexCoord2f(1.0f, 0.0f); glVertex2f(x + 0.3f, y + 0.0f); + glColor3f(0.5f, 0.5f, 0.5f); glTexCoord2f(1.0f, 1.0f); glVertex2f(x + 0.3f, y + 0.3f); + glColor3f(0.5f, 0.5f, 0.5f); glTexCoord2f(0.0f, 1.0f); glVertex2f(x + 0.0f, y + 0.3f); + glEnd(); +} + +static void sample_init() { +#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); + SDL_GL_SetSwapInterval(1); +#else + glKosInit(); +#endif +} + +static int sample_should_exit() { +#ifndef _arch_dreamcast + SDL_Event event; + while (SDL_PollEvent(&event)) { + if(event.type == SDL_QUIT) return 1; + } + return 0; +#else + maple_device_t *cont = maple_enum_type(0, MAPLE_FUNC_CONTROLLER); + if (!cont) return 0; + + cont_state_t *state = (cont_state_t *)maple_dev_status(cont); + if (!state) return 0; + + return state->buttons & CONT_START; +#endif +} + +#define IMG_SIZE 8 +int main(int argc, char *argv[]) { + sample_init(); + glClearColor(0.2f, 0.2f, 0.2f, 1); + glViewport(0, 0, 640, 480); + glEnable(GL_TEXTURE_2D); + + GLint tex; + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, tex); + + static GLubyte texData[IMG_SIZE * IMG_SIZE * 4]; + for (int y = 0, i = 0; y < IMG_SIZE; y++) + for (int x = 0; x < IMG_SIZE; x++, i += 4) + { + int a = x + y; + texData[i + 0] = (a & 1) ? 0xFF : 0; + texData[i + 1] = (a & 2) ? 0xFF : 0; + texData[i + 2] = (a & 4) ? 0xFF : 0; + texData[i + 3] = 0xFF; + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, IMG_SIZE, IMG_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, texData); + float time = 0.0f; + + while (!sample_should_exit()) { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + DrawQuad(0.1f, 0.1f); + + glMatrixMode(GL_TEXTURE); + glTranslatef(time, 0, 0); + DrawQuad(0.5f, 0.1f); + glLoadIdentity(); + + glMatrixMode(GL_TEXTURE); + glTranslatef(0, time, 0); + DrawQuad(0.9f, 0.1f); + glLoadIdentity(); + + glMatrixMode(GL_TEXTURE); + glScalef(time, time * 0.5f, 0); + DrawQuad(1.3f, 0.1f); + glLoadIdentity(); + + glMatrixMode(GL_TEXTURE); + glRotatef(time * 100, 1, 0, 0); + DrawQuad(1.7f, 0.1f); + glLoadIdentity(); + + glMatrixMode(GL_COLOR); + glTranslatef(time, 0, 0); + DrawQuad(0.1f, 0.5f); + glLoadIdentity(); + + glMatrixMode(GL_COLOR); + glTranslatef(0, time, 0); + DrawQuad(0.5f, 0.5f); + glLoadIdentity(); + + glMatrixMode(GL_COLOR); + glTranslatef(0, 0, time); + DrawQuad(0.9f, 0.5f); + glLoadIdentity(); + + glMatrixMode(GL_COLOR); + glScalef(time, time, time); + DrawQuad(1.3f, 0.5f); + glLoadIdentity(); + + glMatrixMode(GL_COLOR); + glRotatef(time * 100, 1, 0, 0); + DrawQuad(0.1f, 0.5f); + glLoadIdentity(); + +#ifdef SDL2_BUILD + SDL_GL_SwapWindow(win_handle); +#else + glKosSwapBuffers(); +#endif + time += 0.001f; + } + return 0; +}