diff --git a/src/external/rlsw.h b/src/external/rlsw.h index 55205d057..6af7fcf84 100644 --- a/src/external/rlsw.h +++ b/src/external/rlsw.h @@ -22,1888 +22,2061 @@ * SOFTWARE. */ - #ifndef RLSW_H - #define RLSW_H - - #include - #include - - #ifndef SW_MALLOC - # define SW_MALLOC(sz) malloc(sz) - #endif - - #ifndef SW_FREE - # define SW_FREE(ptr) free(ptr) - #endif - - #ifndef SW_MAX_PROJECTION_STACK_SIZE - # define SW_MAX_PROJECTION_STACK_SIZE 2 - #endif - - #ifndef SW_MAX_MODELVIEW_STACK_SIZE - # define SW_MAX_MODELVIEW_STACK_SIZE 8 - #endif - - #ifndef SW_MAX_TEXTURE_STACK_SIZE - # define SW_MAX_TEXTURE_STACK_SIZE 4 - #endif - - #ifndef SW_MAX_TEXTURES - # define SW_MAX_TEXTURES 128 - #endif - - #ifndef SW_MAX_CLIPPED_POLYGON_VERTICES - # define SW_MAX_CLIPPED_POLYGON_VERTICES 12 - #endif - - #ifndef SW_CLIP_EPSILON - # define SW_CLIP_EPSILON 1e-4f - #endif - - typedef enum { - SW_PROJECTION, - SW_MODELVIEW, - SW_TEXTURE - } SWmatrix; - - typedef enum { - SW_VERTEX_ARRAY, - SW_TEXTURE_COORD_ARRAY, - SW_NORMAL_ARRAY, - SW_COLOR_ARRAY - } SWarray; - - typedef enum { - SW_POINTS, - SW_LINES, - SW_TRIANGLES, - SW_QUADS, - } SWfill; - - typedef enum { - SW_CULL_FRONT, - SW_CULL_BACK, - } SWcull; - - typedef enum { - SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE = 1, // 8 bit per pixel (no alpha) - SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA, // 8*2 bpp (2 channels) - SW_PIXELFORMAT_UNCOMPRESSED_R5G6B5, // 16 bpp - SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8, // 24 bpp - SW_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1, // 16 bpp (1 bit alpha) - SW_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4, // 16 bpp (4 bit alpha) - SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, // 32 bpp - SW_PIXELFORMAT_UNCOMPRESSED_R32, // 32 bpp (1 channel - float) - SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32, // 32*3 bpp (3 channels - float) - SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32, // 32*4 bpp (4 channels - float) - SW_PIXELFORMAT_UNCOMPRESSED_R16, // 16 bpp (1 channel - half float) - SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16, // 16*3 bpp (3 channels - half float) - SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16, // 16*4 bpp (4 channels - half float) - SW_PIXELFORMAT_COMPRESSED_DXT1_RGB, // 4 bpp (no alpha) - SW_PIXELFORMAT_COMPRESSED_DXT1_RGBA, // 4 bpp (1 bit alpha) - SW_PIXELFORMAT_COMPRESSED_DXT3_RGBA, // 8 bpp - SW_PIXELFORMAT_COMPRESSED_DXT5_RGBA, // 8 bpp - SW_PIXELFORMAT_COMPRESSED_ETC1_RGB, // 4 bpp - SW_PIXELFORMAT_COMPRESSED_ETC2_RGB, // 4 bpp - SW_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA, // 8 bpp - SW_PIXELFORMAT_COMPRESSED_PVRT_RGB, // 4 bpp - SW_PIXELFORMAT_COMPRESSED_PVRT_RGBA, // 4 bpp - SW_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA, // 8 bpp - SW_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA // 2 bpp - } SWpixelformat; - - typedef enum { - SW_NEAREST, - SW_LINEAR, - SW_NEAREST_MIPMAP_NEAREST, - SW_NEAREST_MIPMAP_LINEAR, - SW_LINEAR_MIPMAP_NEAREST, - SW_LINEAR_MIPMAP_LINEAR - } SWfilter; - - typedef enum { - SW_REPEAT, - SW_CLAMP_TO_EDGE, - SW_MIRRORED_REPEAT - } SWwrap; - - typedef enum { - SW_TEXTURE_MIN_FILTER, - SW_TEXTURE_MAG_FILTER, - SW_TEXTURE_WRAP_S, - SW_TEXTURE_WRAP_T - } SWtexparam; - - typedef enum { - SW_NO_ERROR, - SW_INVALID_ENUM, - SW_INVALID_VALUE, - SW_STACK_OVERFLOW, - SW_STACK_UNDERFLOW, - SW_INVALID_OPERATION, - SW_ERROR_OUT_OF_MEMORY - } SWerrcode; - - /* === Public API === */ - - void swInit(int w, int h); - void swClose(void); - - void* swGetColorBuffer(int* w, int* h); - - void swMatrixMode(SWmatrix mode); - void swPushMatrix(void); - void swPopMatrix(void); - void swLoadIdentity(void); - void swTranslatef(float x, float y, float z); - void swRotatef(float angle, float x, float y, float z); - void swScalef(float x, float y, float z); - void swMultMatrixf(const float* mat); - void swFrustum(float left, float right, float bottom, float top, float znear, float zfar); - void swOrtho(float left, float right, float bottom, float top, float znear, float zfar); - - void swViewport(int x, int y, int width, int height); - - void swClearColor(float r, float g, float b, float a); - void swClear(void); - - void swBegin(SWfill mode); - void swEnd(void); - - void swVertex2i(int x, int y); - void swVertex2f(float x, float y); - void swVertex2fv(const float* v); - void swVertex3i(int x, int y, int z); - void swVertex3f(float x, float y, float z); - void swVertex3fv(const float* v); - void swVertex4i(int x, int y, int z, int w); - void swVertex4f(float x, float y, float z, float w); - void swVertex4fv(const float* v); - - void swColor1ui(uint32_t color); - void swColor3ub(uint8_t r, uint8_t g, uint8_t b); - void swColor3ubv(const uint8_t* v); - void swColor3us(uint16_t r, uint16_t g, uint16_t b); - void swColor3usv(const uint16_t* v); - void swColor3ui(uint32_t r, uint32_t g, uint32_t b); - void swColor3uiv(const uint32_t* v); - void swColor3f(float r, float g, float b); - void swColor3fv(const float* v); - void swColor4ub(uint8_t r, uint8_t g, uint8_t b, uint8_t a); - void swColor4ubv(const uint8_t* v); - void swColor4us(uint16_t r, uint16_t g, uint16_t b, uint16_t a); - void swColor4usv(const uint16_t* v); - void swColor4ui(uint32_t r, uint32_t g, uint32_t b, uint32_t a); - void swColor4uiv(const uint32_t* v); - void swColor4f(float r, float g, float b, float a); - void swColor4fv(const float* v); - - void swTexCoord2f(float u, float v); - void swTexCoordfv(const float* v); - - void swNormal3f(float x, float y, float z); - void swNormal3fv(const float* v); - - void swBindArray(SWarray type, void *buffer); - void swDrawArrays(SWfill mode, int offset, int count); - - uint32_t swLoadTexture(const void *data, int width, int height, int format, int mipmapCount); - void swUnloadTexture(uint32_t id); - - void swTextureParameters(uint32_t id, int param, int value); - void swBindTexture(uint32_t id); - - #endif // RLSW_H - - - - #ifdef RLSW_IMPL - - #include - #include - - /* === Defines and Macros === */ - - #define SW_PI 3.14159265358979323846f - #define SW_DEG2RAD (SW_PI/180.0f) - #define SW_RAD2DEG (180.0f/SW_PI) - - /* === Internal Structs === */ - - typedef float sw_matrix_t[4*4]; - typedef uint16_t sw_half_t; - - typedef struct { - - float position[4]; // Position coordinates - float normal[3]; // Normal vector - float texcoord[2]; // Texture coordinates - float color[4]; // Color - - float homogeneous[4]; // Homogeneous coordinates - float screen[2]; // Screen coordinates - - } sw_vertex_t; - - typedef struct { - - const void* pixels; - int width; - int height; - int format; - - SWfilter minFilter; - SWfilter magFilter; - - SWwrap sWrap; - SWwrap tWrap; - - float tx; - float ty; - - } sw_texture_t; - - typedef struct { - uint8_t *color; // 32-bit RGBA color buffer - uint16_t *depth; // 16-bit fixed fract buffer - int width, height; - } sw_framebuffer_t; - - typedef struct { - - sw_framebuffer_t framebuffer; - uint8_t clearColor[4]; // Color used to clear the screen - uint16_t clearDepth; // Depth value used to clear the screen - - uint32_t currentTexture; - sw_matrix_t *currentMatrix; - - uint32_t blendFunction; - uint32_t depthFunction; - - int vpPos[2]; // Represents the top-left corner of the viewport - int vpDim[2]; // Represents the dimensions of the viewport (minus one) - int vpMin[2]; // Represents the minimum renderable point of the viewport (top-left) - int vpMax[2]; // Represents the maximum renderable point of the viewport (bottom-right) - - struct { - float* positions; - float* texcoords; - float* normals; - uint8_t* colors; - } array; - - sw_vertex_t vertexBuffer[4]; // Buffer used for storing primitive vertices, used for processing and rendering - int vertexCounter; // Number of vertices in 'ctx.vertexBuffer' - - SWfill fillMode; // Current polygon filling mode (e.g., lines, triangles) - float pointSize; // Rasterized point size - float lineWidth; // Rasterized line width - - sw_matrix_t matProjection; // Projection matrix, user adjustable - sw_matrix_t matTexture; // Texture matrix, user adjustable - sw_matrix_t matModel; // Model matrix, user adjustable (the one used if we push in SW_MODELVIEW mode) - sw_matrix_t matView; // View matrix, user adjustable (the default one used in SW_MODELVIEW mode) - sw_matrix_t matMVP; // Model view projection matrix, calculated and used internally - - sw_matrix_t stackProjection[SW_MAX_PROJECTION_STACK_SIZE]; // Projection matrix stack for push/pop operations - sw_matrix_t stackModelview[SW_MAX_MODELVIEW_STACK_SIZE]; // Modelview matrix stack for push/pop operations - sw_matrix_t stackTexture[SW_MAX_TEXTURE_STACK_SIZE]; // Texture matrix stack for push/pop operations - uint32_t stackProjectionCounter; // Counter for matrix stack operations - uint32_t stackModelviewCounter; // Counter for matrix stack operations - uint32_t stackTextureCounter; // Counter for matrix stack operations - - SWmatrix currentMatrixMode; // Current matrix mode (e.g., sw_MODELVIEW, sw_PROJECTION) - bool modelMatrixUsed; // Flag indicating if the model matrix is used - - SWcull cullFace; // Faces to cull - SWerrcode errCode; // Last error code - - sw_texture_t* loadedTextures; - int loadedTextureCount; - - uint32_t* freeTextureIds; - int freeTextureIdCount; - - } sw_data_t; - - - /* === Global Data === */ - - static sw_data_t RLSW = { 0 }; - - - /* === Helper Functions === */ - - static inline void sw_matrix_id(sw_matrix_t dst) - { - dst[0] = 1, dst[1] = 0, dst[2] = 0, dst[3] = 0; - dst[4] = 0, dst[5] = 1, dst[6] = 0, dst[7] = 0; - dst[8] = 0, dst[9] = 0, dst[10] = 1, dst[11] = 0; - dst[12] = 0, dst[13] = 0, dst[14] = 0, dst[15] = 1; - } - - static inline void sw_matrix_mul(sw_matrix_t dst, const sw_matrix_t left, const sw_matrix_t right) - { - sw_matrix_t result; - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - float sum = 0.0; - for (int k = 0; k < 4; k++) { - sum += left[i * 4 + k] * right[k * 4 + j]; - } - result[i * 4 + j] = sum; - } - } - for (int i = 0; i < 16; i++) { - dst[i] = result[i]; - } - } - - static inline void sw_vec4_transform(float dst[4], const float v[4], const sw_matrix_t mat) - { - float tmp[4] = { - mat[0] * v[0] + mat[4] * v[1] + mat[8] * v[2] + mat[12] * v[3], - mat[1] * v[0] + mat[5] * v[1] + mat[9] * v[2] + mat[13] * v[3], - mat[2] * v[0] + mat[6] * v[1] + mat[10] * v[2] + mat[14] * v[3], - mat[3] * v[0] + mat[7] * v[1] + mat[11] * v[2] + mat[15] * v[3] - }; - - for (int i = 0; i < 4; i++) { - dst[i] = tmp[i]; - } - } - - static inline float sw_lerp(float a, float b, float t) - { - return a + t * (b - a); - } - - static inline sw_vertex_t sw_lerp_vertex(const sw_vertex_t* a, const sw_vertex_t* b, float t) - { - sw_vertex_t result; - for (int i = 0; i < sizeof(sw_vertex_t) / sizeof(float); i++) { - ((float*)&result)[i] = sw_lerp(((float*)a)[i], ((float*)b)[i], t); - } - return result; - } - - static inline uint32_t sw_cvt_hf_ui(uint16_t h) - { - uint32_t s = (uint32_t)(h & 0x8000) << 16; - int32_t em = h & 0x7fff; - - // bias exponent and pad mantissa with 0; 112 is relative exponent bias (127-15) - int32_t r = (em + (112 << 10)) << 13; - - // denormal: flush to zero - r = (em < (1 << 10)) ? 0 : r; - - // infinity/NaN; note that we preserve NaN payload as a byproduct of unifying inf/nan cases - // 112 is an exponent bias fixup; since we already applied it once, applying it twice converts 31 to 255 - r += (em >= (31 << 10)) ? (112 << 23) : 0; - - return s | r; - } - - static inline float sw_cvt_hf(sw_half_t y) - { - union { float f; uint32_t i; } v = { - .i = sw_cvt_hf_ui(y) - }; - return v.f; - } - - static inline void sw_get_pixel_grayscale(float* color, const void* pixels, uint32_t offset) - { - float gray = (float)((uint8_t*)pixels)[offset] / 255; - - color[0] = gray; - color[1] = gray; - color[2] = gray; - color[3] = 1.0f; - } - - static inline void sw_get_pixel_red_16(float* color, const void* pixels, uint32_t offset) - { - float value = sw_cvt_hf(((sw_half_t*)pixels)[offset]); - - color[0] = value; - color[1] = value; - color[2] = value; - color[3] = 1.0f; - } - - static inline void sw_get_pixel_red_32(float* color, const void* pixels, uint32_t offset) - { - float value = ((float*)pixels)[offset]; - - color[0] = value; - color[1] = value; - color[2] = value; - color[3] = 1.0f; - } - - static inline void sw_get_pixel_grayscale_alpha(float* color, const void* pixels, uint32_t offset) - { - float gray = (float)((uint8_t*)pixels)[2 * offset] / 255; - float alpha = (float)((uint8_t*)pixels)[2 * offset + 1] / 255; - - color[0] = gray; - color[1] = gray; - color[2] = gray; - color[3] = alpha; - } - - static inline void sw_get_pixel_rgb_565(float* color, const void* pixels, uint32_t offset) - { - uint16_t pixel = ((uint16_t*)pixels)[offset]; - - color[0] = (float)((pixel & 0xF800) >> 11) / 31; - color[1] = (float)((pixel & 0x7E0) >> 5) / 63; - color[2] = (float)(pixel & 0x1F) / 31; - color[3] = 1.0f; - } - - static inline void sw_get_pixel_rgb_888(float* color, const void* pixels, uint32_t offset) - { - const uint8_t* pixel = (uint8_t*)pixels + 3 * offset; - - color[0] = (float)pixel[0] / 255; - color[1] = (float)pixel[1] / 255; - color[2] = (float)pixel[2] / 255; - color[3] = 1.0f; - } - - static inline void sw_get_pixel_rgb_161616(float* color, const void* pixels, uint32_t offset) - { - const sw_half_t *pixel = (sw_half_t*)pixels + 3 * offset; - - color[0] = sw_cvt_hf(pixel[0]); - color[1] = sw_cvt_hf(pixel[1]); - color[2] = sw_cvt_hf(pixel[2]); - color[3] = 1.0f; - } - - static inline void sw_get_pixel_rgb_323232(float* color, const void* pixels, uint32_t offset) - { - const float *pixel = (float*)pixels + 3 * offset; - - color[0] = pixel[0]; - color[1] = pixel[1]; - color[2] = pixel[2]; - color[3] = 1.0f; - } - - static inline void sw_get_pixel_rgba_5551(float* color, const void* pixels, uint32_t offset) - { - uint16_t pixel = ((uint16_t*)pixels)[offset]; - - color[0] = (float)((pixel & 0xF800) >> 11) / 31; - color[1] = (float)((pixel & 0x7C0) >> 6) / 31; - color[2] = (float)((pixel & 0x3E) >> 1) / 31; - color[3] = (float)(pixel & 0x1); - } - - static inline void sw_get_pixel_rgba_4444(float* color, const void* pixels, uint32_t offset) - { - uint16_t pixel = ((uint16_t*)pixels)[offset]; - - color[0] = (float)((pixel & 0xF000) >> 12) / 15; - color[1] = (float)((pixel & 0xF00) >> 8) / 15; - color[2] = (float)((pixel & 0xF0) >> 4) / 15; - color[3] = (float)(pixel & 0xF) / 15; - } - - static inline void sw_get_pixel_rgba_8888(float* color, const void* pixels, uint32_t offset) - { - const uint8_t *pixel = (uint8_t*)pixels + 4 * offset; - - color[0] = (float)pixel[0] / 255; - color[1] = (float)pixel[1] / 255; - color[2] = (float)pixel[2] / 255; - color[3] = (float)pixel[3] / 255; - } - - static inline void sw_get_pixel_rgba_16161616(float* color, const void* pixels, uint32_t offset) - { - const sw_half_t *pixel = (sw_half_t*)pixels + 4 * offset; - - color[0] = sw_cvt_hf(pixel[0]); - color[1] = sw_cvt_hf(pixel[1]); - color[2] = sw_cvt_hf(pixel[2]); - color[3] = sw_cvt_hf(pixel[3]); - } - - static inline void sw_get_pixel_rgba_32323232(float* color, const void* pixels, uint32_t offset) - { - const float *pixel = (float*)pixels + 4 * offset; - - color[0] = pixel[0]; - color[1] = pixel[1]; - color[2] = pixel[2]; - color[3] = pixel[3]; - } - - static inline void sw_get_pixel(float* color, const void* pixels, uint32_t offset, SWpixelformat format) - { - switch (format) { - - case SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: - sw_get_pixel_grayscale(color, pixels, offset); - break; - - case SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: - sw_get_pixel_grayscale_alpha(color, pixels, offset); - break; - - case SW_PIXELFORMAT_UNCOMPRESSED_R5G6B5: - sw_get_pixel_rgb_565(color, pixels, offset); - break; - - case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8: - sw_get_pixel_rgb_888(color, pixels, offset); - break; - - case SW_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: - sw_get_pixel_rgba_5551(color, pixels, offset); - break; - - case SW_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: - sw_get_pixel_rgba_4444(color, pixels, offset); - break; - - case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: - sw_get_pixel_rgba_8888(color, pixels, offset); - break; - - case SW_PIXELFORMAT_UNCOMPRESSED_R32: - sw_get_pixel_red_32(color, pixels, offset); - break; - - case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32: - sw_get_pixel_rgb_323232(color, pixels, offset); - break; - - case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: - sw_get_pixel_rgba_32323232(color, pixels, offset); - break; - - case SW_PIXELFORMAT_UNCOMPRESSED_R16: - sw_get_pixel_red_16(color, pixels, offset); - break; - - case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16: - sw_get_pixel_rgb_161616(color, pixels, offset); - break; - - case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: - sw_get_pixel_rgba_16161616(color, pixels, offset); - break; - - case SW_PIXELFORMAT_COMPRESSED_DXT1_RGB: - case SW_PIXELFORMAT_COMPRESSED_DXT1_RGBA: - case SW_PIXELFORMAT_COMPRESSED_DXT3_RGBA: - case SW_PIXELFORMAT_COMPRESSED_DXT5_RGBA: - case SW_PIXELFORMAT_COMPRESSED_ETC1_RGB: - case SW_PIXELFORMAT_COMPRESSED_ETC2_RGB: - case SW_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA: - case SW_PIXELFORMAT_COMPRESSED_PVRT_RGB: - case SW_PIXELFORMAT_COMPRESSED_PVRT_RGBA: - case SW_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: - case SW_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: - break; - - } - } - - static inline void sw_map_repeat(int* out, float in, int max) - { - // Upscale to nearest texture coordinates - // NOTE: We use '(int)(x+0.5)' although this is incorrect - // regarding the direction of rounding in case of negative values - // and also less accurate than roundf, but it remains so much more - // efficient that it is preferable for now to opt for this option. - - *out = abs((int)((in - (int)in) * (max - 1) + 0.5f)); - } - - static inline void sw_map_clamp_to_edge(int* out, float in, int max) - { - in = (in > 1.0f) ? 1.0f : ((in < 0.0f) ? 0.0f : in); - *out = (int)(in * (max - 1) + 0.5f); - } - - static inline void sw_map_mirrored_repeat(int* out, float in, int max) - { - in = fmodf(fabsf(in), 2); - if (in > 1.0f) in = 1.0f - (in - 1.0f); - *out = (int)(in * (max - 1) + 0.5f); - } - - static inline void sw_map(int* out, float in, int max, SWwrap mode) - { - switch (mode) { - case SW_REPEAT: - sw_map_repeat(out, in, max); - break; - case SW_CLAMP_TO_EDGE: - sw_map_clamp_to_edge(out, in, max); - break; - case SW_MIRRORED_REPEAT: - sw_map_mirrored_repeat(out, in, max); - break; - } - } - - static inline void sw_sample_texture_nearest(float* color, const sw_texture_t* tex, float u, float v) - { - int x, y; - sw_map(&x, u, tex->width, tex->sWrap); - sw_map(&y, v, tex->height, tex->tWrap); - sw_get_pixel(color, tex->pixels, y * tex->width + x, tex->format); - } - - static inline void sw_sample_texture_bilinear(float* color, const sw_texture_t* tex, float u, float v) - { - int x0, y0, x1, y1; - sw_map(&x0, u, tex->width, tex->sWrap); - sw_map(&y0, v, tex->height, tex->tWrap); - sw_map(&x1, u + tex->tx, tex->width, tex->sWrap); - sw_map(&y1, v + tex->ty, tex->height, tex->tWrap); - - float fx = u * (tex->width - 1) - x0; - float fy = v * (tex->height - 1) - y0; - - float c00[4], c10[4], c01[4], c11[4]; - sw_get_pixel(c00, tex->pixels, y0 * tex->width + x0, tex->format); - sw_get_pixel(c10, tex->pixels, y0 * tex->width + x1, tex->format); - sw_get_pixel(c01, tex->pixels, y1 * tex->width + x0, tex->format); - sw_get_pixel(c11, tex->pixels, y1 * tex->width + x1, tex->format); - - float c0[4], c1[4]; - for (int i = 0; i < 4; i++) { - float a = sw_lerp(c00[i], c10[i], fx); - float b = sw_lerp(c01[i], c11[i], fx); - color[i] = sw_lerp(a, b, fy); - } - } - - static inline void sw_sample_texture(float* color, const sw_texture_t* tex, float u, float v, - float xDu, float yDu, float xDv, float yDv) - { - // TODO: It seems there are some incorrect detections depending on the context - // This is probably due to the fact that the fractions are obtained - // at the wrong moment during rasterization. It would be worth reviewing - // this, although the scanline method complicates things. - - // Calculate the derivatives for each axis - float du = sqrtf(xDu * xDu + yDu * yDu); - float dv = sqrtf(xDv * xDv + yDv * yDv); - float L = (du > dv) ? du : dv; - - // Select the filter based on the size of the footprint - if (L > 1.0f) { - // Minification - if (tex->minFilter == SW_NEAREST) { - sw_sample_texture_nearest(color, tex, u, v); - } else if (tex->minFilter == SW_LINEAR) { - sw_sample_texture_bilinear(color, tex, u, v); - } - } else { - // Magnification - if (tex->magFilter == SW_NEAREST) { - sw_sample_texture_nearest(color, tex, u, v); - } else if (tex->magFilter == SW_LINEAR) { - sw_sample_texture_bilinear(color, tex, u, v); - } - } - } - - static inline bool sw_clip_polygon_w(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES], int* vertexCounter) - { - sw_vertex_t input[SW_MAX_CLIPPED_POLYGON_VERTICES]; - for (int i = 0; i < SW_MAX_CLIPPED_POLYGON_VERTICES; i++) { - input[i] = polygon[i]; - } - - int inputCounter = *vertexCounter; - *vertexCounter = 0; - - const sw_vertex_t *prevVt = &input[inputCounter-1]; - char prevDot = (prevVt->homogeneous[3] < SW_CLIP_EPSILON) ? -1 : 1; - - for (int i = 0; i < inputCounter; i++) { - char currDot = (input[i].homogeneous[3] < SW_CLIP_EPSILON) ? -1 : 1; - if (prevDot*currDot < 0) { - polygon[(*vertexCounter)++] = sw_lerp_vertex(prevVt, &input[i], - (SW_CLIP_EPSILON - prevVt->homogeneous[3]) / (input[i].homogeneous[3] - prevVt->homogeneous[3])); - } - if (currDot > 0) { - polygon[(*vertexCounter)++] = input[i]; - } - prevDot = currDot; - prevVt = &input[i]; - } - - return *vertexCounter > 0; - } - - static inline bool sw_clip_polygon_xyz(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES], int* vertexCounter) - { - for (int iAxis = 0; iAxis < 3; iAxis++) - { - if (*vertexCounter == 0) return false; - - sw_vertex_t input[SW_MAX_CLIPPED_POLYGON_VERTICES]; - int inputCounter; - - const sw_vertex_t *prevVt; - char prevDot; - - // Clip against first plane - - for (int i = 0; i < SW_MAX_CLIPPED_POLYGON_VERTICES; i++) { - input[i] = polygon[i]; - } - inputCounter = *vertexCounter; - *vertexCounter = 0; - - prevVt = &input[inputCounter-1]; - prevDot = (prevVt->homogeneous[iAxis] <= prevVt->homogeneous[3]) ? 1 : -1; - - for (int i = 0; i < inputCounter; i++) { - char currDot = (input[i].homogeneous[iAxis] <= input[i].homogeneous[3]) ? 1 : -1; - if (prevDot * currDot <= 0) { - polygon[(*vertexCounter)++] = sw_lerp_vertex(prevVt, &input[i], (prevVt->homogeneous[3] - prevVt->homogeneous[iAxis]) / - ((prevVt->homogeneous[3] - prevVt->homogeneous[iAxis]) - (input[i].homogeneous[3] - input[i].homogeneous[iAxis]))); - } - if (currDot > 0) { - polygon[(*vertexCounter)++] = input[i]; - } - prevDot = currDot; - prevVt = &input[i]; - } - - if (*vertexCounter == 0) return false; - - // Clip against opposite plane - - for (int i = 0; i < SW_MAX_CLIPPED_POLYGON_VERTICES; i++) { - input[i] = polygon[i]; - } - inputCounter = *vertexCounter; - *vertexCounter = 0; - - prevVt = &input[inputCounter-1]; - prevDot = (-prevVt->homogeneous[iAxis] <= prevVt->homogeneous[3]) ? 1 : -1; - - for (int i = 0; i < inputCounter; i++) { - char currDot = (-input[i].homogeneous[iAxis] <= input[i].homogeneous[3]) ? 1 : -1; - if (prevDot*currDot <= 0) { - polygon[(*vertexCounter)++] = sw_lerp_vertex(prevVt, &input[i], (prevVt->homogeneous[3] + prevVt->homogeneous[iAxis]) / - ((prevVt->homogeneous[3] + prevVt->homogeneous[iAxis]) - (input[i].homogeneous[3] + input[i].homogeneous[iAxis]))); - } - if (currDot > 0) { - polygon[(*vertexCounter)++] = input[i]; - } - prevDot = currDot; - prevVt = &input[i]; - } - } - - return *vertexCounter > 0; - } - - void sw_project_and_clip_triangle(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES], int* vertexCounter) - { - for (int i = 0; i < *vertexCounter; i++) { - sw_vertex_t *v = polygon + i; - for (int j = 0; j < 4; j++) v->homogeneous[j] = v->position[j]; - sw_vec4_transform(v->homogeneous, v->homogeneous, RLSW.matMVP); - } - - if (sw_clip_polygon_w(polygon, vertexCounter) && sw_clip_polygon_xyz(polygon, vertexCounter)) { - for (int i = 0; i < *vertexCounter; i++) { - sw_vertex_t *v = polygon + i; - - // Calculation of the reciprocal of W for normalization - // as well as perspective correct attributes - v->homogeneous[3] = 1.0f / v->homogeneous[3]; - - // Division of XYZ coordinates by weight - v->homogeneous[0] *= v->homogeneous[3]; - v->homogeneous[1] *= v->homogeneous[3]; - v->homogeneous[2] *= v->homogeneous[3]; - - // Division of texture coordinates (perspective correct) - v->texcoord[0] *= v->homogeneous[3]; - v->texcoord[1] *= v->homogeneous[3]; - - // Transform to screen space - v->screen[0] = RLSW.vpPos[0] + (v->homogeneous[0] + 1.0f) * 0.5f * RLSW.vpDim[0]; - v->screen[1] = RLSW.vpPos[1] + (v->homogeneous[1] + 1.0f) * 0.5f * RLSW.vpDim[1]; - } - } - } - - void sw_raster_scanline(const sw_texture_t* tex, const sw_vertex_t* start, const sw_vertex_t* end, float yDu, float yDv) - { - // Calculate the horizontal width and avoid division by zero - float dx = end->screen[0] - start->screen[0]; - if (fabsf(dx) < 1e-4f) return; - - // Convert and center the screen coordinates - int xStart = (int)(start->screen[0] + 0.5f); - int xEnd = (int)(end->screen[0] + 0.5f); - int y = (int)(start->screen[1] + 0.5f); - - // Calculate the initial interpolation parameter and its increment - float dt = 1.0f / dx; - float t = (xStart - start->screen[0]) * dt; - - // Calculate the horizontal gradients for UV coordinates - float xDu = (end->texcoord[0] - start->texcoord[0]) * dt; - float xDv = (end->texcoord[1] - start->texcoord[1]) * dt; - - // Pre-calculate the color differences for interpolation - float dcol[4]; - for (int i = 0; i < 4; i++) { - dcol[i] = end->color[i] - start->color[i]; - } - - // Pre-calculate the differences in Z and W (for depth testing and perspective correction) - float dz = end->homogeneous[2] - start->homogeneous[2]; - float dw = end->homogeneous[3] - start->homogeneous[3]; - - // Initialize the interpolated texture coordinates - float u = start->texcoord[0] + t * xDu; - float v = start->texcoord[1] + t * xDv; - - // Pre-calculate the starting pointer for the color framebuffer row - uint8_t* row_ptr = (uint8_t*)((uint32_t*)RLSW.framebuffer.color + y * RLSW.framebuffer.width); - uint8_t* dst = row_ptr + xStart * 4; - - // Pre-calculate the pointer for the depth buffer row - uint16_t* depth_row = RLSW.framebuffer.depth + y * RLSW.framebuffer.width + xStart; - uint16_t* dptr = depth_row; - - // Scanline rasterization loop - for (int x = xStart; x < xEnd; x++) { - // Interpolate Z and W for depth testing and perspective correction - float w = 1.0f / (start->homogeneous[3] + t * dw); - float z = start->homogeneous[2] + t * dz; - - // Depth testing with direct access to the depth buffer - // TODO: Implement different depth funcs? - float depth = (float)(*dptr) / UINT16_MAX; - if (z > depth) goto discard; - - // Update the depth buffer - *dptr = (uint16_t)(z * UINT16_MAX); - - // Sample the texture - float texColor[4]; - sw_sample_texture(texColor, tex, u * w, v * w, xDu, yDu, xDv, yDv); - - // Interpolate the color and modulate by the texture color - for (int i = 0; i < 4; i++) { - float lerp = start->color[i] + t * dcol[i]; - float finalColor = texColor[i] * lerp; - // Inline clamp to keep the value between 0 and 1 - // NOTE: The need for clamp, the colors could be a sign of problem during interpolation (?) - finalColor = (finalColor < 0.0f) ? 0.0f : (finalColor > 1.0f ? 1.0f : finalColor); - dst[i] = (uint8_t)(finalColor * 255.0f); - } - - // Increment the interpolation parameter, UVs, and pointers - discard: - t += dt; - u += xDu; - v += xDv; - dst += 4; - dptr++; - } - } - - void sw_raster_triangle(const sw_vertex_t* v0, const sw_vertex_t* v1, const sw_vertex_t* v2, const sw_texture_t* tex) - { - // Swap vertices by increasing y - if (v0->screen[1] > v1->screen[1]) { const sw_vertex_t* tmp = v0; v0 = v1; v1 = tmp; } - if (v1->screen[1] > v2->screen[1]) { const sw_vertex_t* tmp = v1; v1 = v2; v2 = tmp; } - if (v0->screen[1] > v1->screen[1]) { const sw_vertex_t* tmp = v0; v0 = v1; v1 = tmp; } - - // Extracting coordinates from the sorted vertices - float x0 = v0->screen[0], y0 = v0->screen[1]; - float x1 = v1->screen[0], y1 = v1->screen[1]; - float x2 = v2->screen[0], y2 = v2->screen[1]; - - // Reject degenerate triangles - float height = y2 - y0; - if (height < 1e-4f) return; - - // Global calculation of vertical texture gradients for the triangle - float yDu = (v2->texcoord[0] - v0->texcoord[0]) / height; - float yDv = (v2->texcoord[1] - v0->texcoord[1]) / height; - - // Precompute the inverse of the triangle height and - // edge lengths with checks to avoid division by zero. - float inv_height = 1.0f / height; - float inv_y1y0 = (y1 - y0 > 1e-4f) ? 1.0f / (y1 - y0) : 0.0f; - float inv_y2y1 = (y2 - y1 > 1e-4f) ? 1.0f / (y2 - y1) : 0.0f; - - // Pre-calculation of slopes (dx/dy) - float dx02 = (x2 - x0) * inv_height; - float dx01 = (x1 - x0) * inv_y1y0; - float dx12 = (x2 - x1) * inv_y2y1; - - // Y bounds (vertical clipping) - int yTop = (int)(y0 + 0.5f); - int yMiddle = (int)(y1 + 0.5f); - int yBottom = (int)(y2 + 0.5f); - - // Initializing scanline variables - float xLeft = x0, xRight = x0; - sw_vertex_t start, end; - - // Scanline for the upper part of the triangle - for (int y = yTop; y < yMiddle; y++) { - float dy = (float)y - y0; - float t1 = dy * inv_height; - float t2 = dy * inv_y1y0; - - // Optimized interpolation - start = sw_lerp_vertex(v0, v2, t1); - end = sw_lerp_vertex(v0, v1, t2); - start.screen[0] = xLeft; - start.screen[1] = (float)y; - end.screen[0] = xRight; - end.screen[1] = (float)y; - - if (xLeft > xRight) { sw_vertex_t tmp = start; start = end; end = tmp; } - sw_raster_scanline(tex, &start, &end, yDu, yDv); - - // Incremental update - xLeft += dx02; - xRight += dx01; - } - - // Scanline for the lower part of the triangle - xRight = x1; // Restart the right side from the second vertex - for (int y = yMiddle; y < yBottom; y++) { - float dy = (float)y - y0; - float t1 = dy * inv_height; - float t2 = (float)(y - y1) * inv_y2y1; - - // Optimized interpolation - start = sw_lerp_vertex(v0, v2, t1); - end = sw_lerp_vertex(v1, v2, t2); - start.screen[0] = xLeft; - start.screen[1] = (float)y; - end.screen[0] = xRight; - end.screen[1] = (float)y; - - if (xLeft > xRight) { sw_vertex_t tmp = start; start = end; end = tmp; } - sw_raster_scanline(tex, &start, &end, yDu, yDv); - - // Incremental update - xLeft += dx02; - xRight += dx12; - } - } - - void sw_render_triangle(const sw_vertex_t* v0, const sw_vertex_t* v1, const sw_vertex_t* v2) - { - int vertexCounter = 3; - - sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES]; - polygon[0] = *v0; - polygon[1] = *v1; - polygon[2] = *v2; - - sw_project_and_clip_triangle(polygon, &vertexCounter); - - if (vertexCounter < 3) { - return; - } - - for (int_fast8_t i = 0; i < vertexCounter - 2; i++) { - sw_raster_triangle( - &polygon[0], &polygon[i + 1], &polygon[i + 2], - &RLSW.loadedTextures[RLSW.currentTexture] - ); - } - } - - static inline bool sw_is_texture_id_valid(uint32_t id) - { - bool valid = true; - - if (id == 0) valid = false; - else if (id >= SW_MAX_TEXTURES) valid = false; - else if (RLSW.loadedTextures[id].pixels == 0) valid = false; - - return true; - } - - static inline bool sw_is_texture_filter_valid(int filter) - { - return (filter == SW_NEAREST || filter == SW_LINEAR); - } - - static inline bool sw_is_texture_wrap_valid(int wrap) - { - return (wrap == SW_REPEAT || wrap == SW_CLAMP_TO_EDGE || SW_MIRRORED_REPEAT); - } - - /* === Public Implementation === */ - - void swInit(int w, int h) - { - swViewport(0, 0, w, h); - - RLSW.framebuffer.color = SW_MALLOC(4 * w * h); - RLSW.framebuffer.depth = SW_MALLOC(2 * w * h); - - RLSW.framebuffer.width = w; - RLSW.framebuffer.height = h; - - RLSW.loadedTextures = SW_MALLOC(SW_MAX_TEXTURES); - RLSW.freeTextureIds = SW_MALLOC(SW_MAX_TEXTURES); - - RLSW.clearColor[0] = 0; - RLSW.clearColor[1] = 0; - RLSW.clearColor[2] = 0; - RLSW.clearColor[3] = 255; - RLSW.clearDepth = UINT16_MAX; - - RLSW.currentMatrixMode = SW_MODELVIEW; - RLSW.currentMatrix = &RLSW.matView; - - sw_matrix_id(RLSW.matProjection); - sw_matrix_id(RLSW.matTexture); - sw_matrix_id(RLSW.matModel); - sw_matrix_id(RLSW.matView); - - RLSW.vertexBuffer[0].color[0] = 1.0f; - RLSW.vertexBuffer[0].color[1] = 1.0f; - RLSW.vertexBuffer[0].color[2] = 1.0f; - RLSW.vertexBuffer[0].color[3] = 1.0f; - - RLSW.vertexBuffer[0].texcoord[0] = 0.0f; - RLSW.vertexBuffer[0].texcoord[1] = 0.0f; - - RLSW.vertexBuffer[0].normal[0] = 0.0f; - RLSW.vertexBuffer[0].normal[1] = 0.0f; - RLSW.vertexBuffer[0].normal[2] = 1.0f; - - static const float defTex[3*2*2] = - { - 1.0f, 1.0f, 1.0f, - 1.0f, 1.0f, 1.0f, - 1.0f, 1.0f, 1.0f, - 1.0f, 1.0f, 1.0f, - }; - - RLSW.loadedTextures[0].pixels = defTex; - RLSW.loadedTextures[0].width = 2; - RLSW.loadedTextures[0].height = 2; - RLSW.loadedTextures[0].format = SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32; - RLSW.loadedTextures[0].minFilter = SW_NEAREST; - RLSW.loadedTextures[0].magFilter = SW_NEAREST; - RLSW.loadedTextures[0].sWrap = SW_REPEAT; - RLSW.loadedTextures[0].tWrap = SW_REPEAT; - RLSW.loadedTextures[0].tx = 0.5f; - RLSW.loadedTextures[0].ty = 0.5f; - - RLSW.loadedTextureCount = 1; - } - - void swClose(void) - { - SW_FREE(RLSW.framebuffer.color); - SW_FREE(RLSW.framebuffer.depth); - - SW_FREE(RLSW.loadedTextures); - SW_FREE(RLSW.freeTextureIds); - } - - void* swGetColorBuffer(int* w, int* h) - { - if (w) *w = RLSW.framebuffer.width; - if (h) *h = RLSW.framebuffer.height; - - return RLSW.framebuffer.color; - } - - void swMatrixMode(SWmatrix mode) - { - switch (mode) { - case SW_PROJECTION: - RLSW.currentMatrix = &RLSW.matProjection; - break; - case SW_MODELVIEW: - RLSW.currentMatrix = RLSW.modelMatrixUsed - ? &RLSW.matModel : &RLSW.matView; - break; - case SW_TEXTURE: - RLSW.currentMatrix = &RLSW.matTexture; - break; - default: - RLSW.errCode = SW_INVALID_ENUM; - return; - } - - RLSW.currentMatrixMode = mode; - } - - void swPushMatrix(void) - { - switch (RLSW.currentMatrixMode) { - - case SW_PROJECTION: - if (RLSW.stackProjectionCounter >= SW_MAX_PROJECTION_STACK_SIZE) { - RLSW.errCode = SW_STACK_OVERFLOW; - return; - } - for (int i = 0; i < 16; i++) { - RLSW.stackProjection[RLSW.stackProjectionCounter][i] = RLSW.matProjection[i]; - } - RLSW.stackProjectionCounter++; - break; - - case SW_MODELVIEW: - if (RLSW.stackModelviewCounter >= SW_MAX_MODELVIEW_STACK_SIZE) { - RLSW.errCode = SW_STACK_OVERFLOW; - return; - } - if (RLSW.modelMatrixUsed) { - for (int i = 0; i < 16; i++) { - RLSW.stackModelview[RLSW.stackModelviewCounter][i] = RLSW.matModel[i]; - } - RLSW.stackModelviewCounter++; - } else { - RLSW.currentMatrix = &RLSW.matModel; - RLSW.modelMatrixUsed = true; - } - break; - - case SW_TEXTURE: - if (RLSW.stackTextureCounter >= SW_MAX_TEXTURE_STACK_SIZE) { - RLSW.errCode = SW_STACK_OVERFLOW; - return; - } - for (int i = 0; i < 16; i++) { - RLSW.stackTexture[RLSW.stackTextureCounter][i] = RLSW.matTexture[i]; - } - RLSW.stackTextureCounter++; - break; - - } - } - - void swPopMatrix(void) - { - switch (RLSW.currentMatrixMode) { - - case SW_PROJECTION: - if (RLSW.stackProjectionCounter <= 0) { - RLSW.errCode = SW_STACK_UNDERFLOW; - return; - } - RLSW.stackProjectionCounter--; - for (int i = 0; i < 16; i++) { - RLSW.matProjection[i] = RLSW.stackProjection[RLSW.stackProjectionCounter][i]; - } - break; - - case SW_MODELVIEW: - if (RLSW.stackModelviewCounter == 0) { - if (!RLSW.modelMatrixUsed) { - RLSW.errCode = SW_STACK_UNDERFLOW; - return; - } - sw_matrix_id(RLSW.matModel); - RLSW.currentMatrix = &RLSW.matView; - RLSW.modelMatrixUsed = false; - } else { - RLSW.stackModelviewCounter--; - for (int i = 0; i < 16; i++) { - RLSW.matModel[i] = RLSW.stackModelview[RLSW.stackModelviewCounter][i]; - } - } - break; - - case SW_TEXTURE: - if (RLSW.stackTextureCounter <= 0) { - RLSW.errCode = SW_STACK_UNDERFLOW; - return; - } - RLSW.stackTextureCounter--; - for (int i = 0; i < 16; i++) { - RLSW.matTexture[i] = RLSW.stackTexture[RLSW.stackTextureCounter][i]; - } - break; - - } - } - - void swLoadIdentity(void) - { - sw_matrix_id(*RLSW.currentMatrix); - } - - void swTranslatef(float x, float y, float z) - { - sw_matrix_t mat; - sw_matrix_id(mat); - - mat[12] = x; - mat[13] = y; - mat[14] = z; - - sw_matrix_mul(*RLSW.currentMatrix, mat, *RLSW.currentMatrix); - } - - void swRotatef(float angle, float x, float y, float z) - { - angle *= SW_DEG2RAD; - - sw_matrix_t mat; - sw_matrix_id(mat); - - float lengthSq = x*x + y*y + z*z; - - if (lengthSq != 1.0f && lengthSq != 0.0f) { - float invLenght = 1.0f / lengthSq; - x *= invLenght; - y *= invLenght; - z *= invLenght; - } - - float sinres = sinf(angle); - float cosres = cosf(angle); - float t = 1.0f - cosres; - - mat[0] = x*x*t + cosres; - mat[1] = y*x*t + z*sinres; - mat[2] = z*x*t - y*sinres; - - mat[4] = x*y*t - z*sinres; - mat[5] = y*y*t + cosres; - mat[6] = z*y*t + x*sinres; - - mat[8] = x*z*t + y*sinres; - mat[9] = y*z*t - x*sinres; - mat[10] = z*z*t + cosres; - - sw_matrix_mul(*RLSW.currentMatrix, mat, *RLSW.currentMatrix); - } - - void swScalef(float x, float y, float z) - { - sw_matrix_t mat; - - mat[0] = x, mat[1] = 0, mat[2] = 0, mat[3] = 0; - mat[4] = 0, mat[5] = y, mat[6] = 0, mat[7] = 0; - mat[8] = 0, mat[9] = 0, mat[10] = z, mat[11] = 0; - mat[12] = 0, mat[13] = 0, mat[14] = 0, mat[15] = 1; - - sw_matrix_mul(*RLSW.currentMatrix, mat, *RLSW.currentMatrix); - } - - void swMultMatrixf(const float* mat) - { - sw_matrix_mul(*RLSW.currentMatrix, *RLSW.currentMatrix, mat); - } - - void swFrustum(float left, float right, float bottom, float top, float znear, float zfar) - { - sw_matrix_t mat = { 0 }; - - float rl = right - left; - float tb = top - bottom; - float fn = zfar - znear; - - mat[0] = (znear * 2.0f) / rl; - mat[5] = (znear * 2.0f) / tb; - - mat[8] = (right + left) / rl; - mat[9] = (top + bottom) / tb; - mat[10] = -(zfar + znear) / fn; - mat[11] = -1.0f; - - mat[14] = -(zfar * znear * 2.0f) / fn; - - sw_matrix_mul(*RLSW.currentMatrix, *RLSW.currentMatrix, mat); - } - - void swOrtho(float left, float right, float bottom, float top, float znear, float zfar) - { - sw_matrix_t mat = { 0 }; - - float rl = (right - left); - float tb = (top - bottom); - float fn = (zfar - znear); - - mat[0] = 2.0f / rl; - mat[5] = 2.0f / tb; - - mat[10] = -2.0f / fn; - mat[11] = 0.0f; - mat[12] = -(left + right) / rl; - mat[13] = -(top + bottom) / tb; - - mat[14] = -(zfar + znear) / fn; - mat[15] = 1.0f; - - sw_matrix_mul(*RLSW.currentMatrix, *RLSW.currentMatrix, mat); - } - - void swViewport(int x, int y, int width, int height) - { - if (x <= -width || y <= -height) { - RLSW.errCode = SW_INVALID_OPERATION; - return; - } - - RLSW.vpPos[0] = x; - RLSW.vpPos[1] = y; - - RLSW.vpDim[0] = width - 1; - RLSW.vpDim[1] = height - 1; - - RLSW.vpMin[0] = (x < 0) ? 0 : x; - RLSW.vpMin[1] = (y < 0) ? 0 : y; - - int fbW = RLSW.framebuffer.width - 1; - int fbH = RLSW.framebuffer.height - 1; - - int vpMaxX = x + width; - int vpMaxY = y + height; - - RLSW.vpMax[0] = (vpMaxX < fbW) ? vpMaxX : fbW; - RLSW.vpMax[1] = (vpMaxY < fbH) ? vpMaxY : fbH; - } - - void swClearColor(float r, float g, float b, float a) - { - RLSW.clearColor[0] = r * 255; - RLSW.clearColor[1] = g * 255; - RLSW.clearColor[2] = b * 255; - RLSW.clearColor[3] = a * 255; - } - - void swClear(void) - { - int size = RLSW.framebuffer.width * RLSW.framebuffer.height; - - for (int i = 0; i < size; i++) { - ((uint32_t*)RLSW.framebuffer.color)[i] = *((uint32_t*)RLSW.clearColor); - RLSW.framebuffer.depth[i] = RLSW.clearDepth; - } - } - - void swBegin(SWfill mode) - { - if (mode < SW_POINTS || mode > SW_QUADS) { - RLSW.errCode = SW_INVALID_ENUM; - return; - } - RLSW.vertexCounter = 0; - RLSW.fillMode = mode; - } - - void swEnd(void) - { - RLSW.vertexCounter = 0; - } - - void swVertex2i(int x, int y) - { - float v[4] = { (float)x, (float)y, 0.0f, 1.0f }; - swVertex4fv(v); - } - - void swVertex2f(float x, float y) - { - float v[4] = { x, y, 0.0f, 1.0f }; - swVertex4fv(v); - } - - void swVertex2fv(const float* v) - { - float v4[4] = { v[0], v[1], 0.0f, 1.0f }; - swVertex4fv(v4); - } - - void swVertex3i(int x, int y, int z) - { - float v[4] = { (float)x, (float)y, (float)z, 1.0f }; - swVertex4fv(v); - } - - void swVertex3f(float x, float y, float z) - { - float v[4] = { x, y, z, 1.0f }; - swVertex4fv(v); - } - - void swVertex3fv(const float* v) - { - float v4[4] = { v[0], v[1], v[2], 1.0f }; - swVertex4fv(v4); - } - - void swVertex4i(int x, int y, int z, int w) - { - float v[4] = { (float)x, (float)y, (float)z, (float)w }; - swVertex4fv(v); - } - - void swVertex4f(float x, float y, float z, float w) - { - float v[4] = { x, y, z, w }; - swVertex4fv(v); - } - - void swVertex4fv(const float* v) - { - for (int i = 0; i < 4; i++) { - RLSW.vertexBuffer[RLSW.vertexCounter].position[i] = v[i]; - } - RLSW.vertexCounter++; - - int neededVertices = 0; - switch (RLSW.fillMode) { - case SW_POINTS: - neededVertices = 1; - break; - case SW_LINES: - neededVertices = 2; - break; - case SW_TRIANGLES: - neededVertices = 3; - break; - case SW_QUADS: - neededVertices = 4; - break; - } - - if (RLSW.vertexCounter == neededVertices) { - - // TODO: Optimize MVP calculation - sw_matrix_mul(RLSW.matMVP, RLSW.matModel, RLSW.matView); - sw_matrix_mul(RLSW.matMVP, RLSW.matMVP, RLSW.matProjection); - - switch (RLSW.fillMode) { - case SW_POINTS: - break; - case SW_LINES: - neededVertices = 2; - break; - case SW_TRIANGLES: - sw_render_triangle( - &RLSW.vertexBuffer[0], - &RLSW.vertexBuffer[1], - &RLSW.vertexBuffer[2] - ); - break; - case SW_QUADS: - sw_render_triangle( - &RLSW.vertexBuffer[0], - &RLSW.vertexBuffer[1], - &RLSW.vertexBuffer[2] - ); - sw_render_triangle( - &RLSW.vertexBuffer[2], - &RLSW.vertexBuffer[3], - &RLSW.vertexBuffer[0] - ); - break; - } - - RLSW.vertexBuffer[0] = RLSW.vertexBuffer[neededVertices - 1]; - RLSW.vertexCounter = 0; - } - else { - RLSW.vertexBuffer[RLSW.vertexCounter] = RLSW.vertexBuffer[RLSW.vertexCounter - 1]; - } - } - - void swColor1ui(uint32_t color) - { - union { - uint32_t v; - uint8_t a[4]; - } c = { .v = color }; - - float cv[4]; - cv[0] = (float)c.a[0] / 255; - cv[1] = (float)c.a[1] / 255; - cv[2] = (float)c.a[2] / 255; - cv[3] = (float)c.a[3] / 255; - - swColor4fv(cv); - } - - void swColor3ub(uint8_t r, uint8_t g, uint8_t b) - { - float cv[4]; - cv[0] = (float)r / 255; - cv[1] = (float)g / 255; - cv[2] = (float)b / 255; - cv[3] = 1.0f; - - swColor4fv(cv); - } - - void swColor3ubv(const uint8_t* v) - { - float cv[4]; - cv[0] = (float)v[0] / 255; - cv[1] = (float)v[1] / 255; - cv[2] = (float)v[2] / 255; - cv[3] = 1.0f; - - swColor4fv(cv); - } - - void swColor3us(uint16_t r, uint16_t g, uint16_t b) - { - float cv[4]; - cv[0] = (float)((uint8_t)(r >> 8)) / 255; - cv[1] = (float)((uint8_t)(g >> 8)) / 255; - cv[2] = (float)((uint8_t)(b >> 8)) / 255; - cv[3] = 1.0f; - - swColor4fv(cv); - } - - void swColor3usv(const uint16_t* v) - { - float cv[4]; - cv[0] = (float)((uint8_t)(v[0] >> 8)) / 255; - cv[1] = (float)((uint8_t)(v[1] >> 8)) / 255; - cv[2] = (float)((uint8_t)(v[2] >> 8)) / 255; - cv[3] = 1.0f; - - swColor4fv(cv); - } - - void swColor3ui(uint32_t r, uint32_t g, uint32_t b) - { - float cv[4]; - cv[0] = (float)((uint8_t)(r >> 24)) / 255; - cv[1] = (float)((uint8_t)(g >> 24)) / 255; - cv[2] = (float)((uint8_t)(b >> 24)) / 255; - cv[3] = 1.0f; - - swColor4fv(cv); - } - - void swColor3uiv(const uint32_t* v) - { - float cv[4]; - cv[0] = (float)((uint8_t)(v[0] >> 24)) / 255; - cv[1] = (float)((uint8_t)(v[1] >> 24)) / 255; - cv[2] = (float)((uint8_t)(v[2] >> 24)) / 255; - cv[3] = 1.0f; - - swColor4fv(cv); - } - - void swColor3f(float r, float g, float b) - { - float cv[4]; - cv[0] = r; - cv[1] = g; - cv[2] = b; - cv[3] = 1.0f; - - swColor4fv(cv); - } - - void swColor3fv(const float* v) - { - float cv[4]; - cv[0] = v[0]; - cv[1] = v[1]; - cv[2] = v[2]; - cv[3] = 1.0f; - - swColor4fv(cv); - } - - void swColor4ub(uint8_t r, uint8_t g, uint8_t b, uint8_t a) - { - float cv[4]; - cv[0] = (float)r / 255; - cv[1] = (float)g / 255; - cv[2] = (float)b / 255; - cv[3] = (float)a / 255; - - swColor4fv(cv); - } - - void swColor4ubv(const uint8_t* v) - { - float cv[4]; - cv[0] = (float)v[0] / 255; - cv[1] = (float)v[1] / 255; - cv[2] = (float)v[2] / 255; - cv[3] = (float)v[3] / 255; - - swColor4fv(cv); - } - - void swColor4us(uint16_t r, uint16_t g, uint16_t b, uint16_t a) - { - float cv[4]; - cv[0] = (float)((uint8_t)(r >> 8)) / 255; - cv[1] = (float)((uint8_t)(g >> 8)) / 255; - cv[2] = (float)((uint8_t)(b >> 8)) / 255; - cv[3] = (float)((uint8_t)(a >> 8)) / 255; - - swColor4fv(cv); - } - - void swColor4usv(const uint16_t* v) - { - float cv[4]; - cv[0] = (float)((uint8_t)(v[0] >> 8)) / 255; - cv[1] = (float)((uint8_t)(v[1] >> 8)) / 255; - cv[2] = (float)((uint8_t)(v[2] >> 8)) / 255; - cv[3] = (float)((uint8_t)(v[3] >> 8)) / 255; - - swColor4fv(cv); - } - - void swColor4ui(uint32_t r, uint32_t g, uint32_t b, uint32_t a) - { - float cv[4]; - cv[0] = (float)((uint8_t)(r >> 24)) / 255; - cv[1] = (float)((uint8_t)(g >> 24)) / 255; - cv[2] = (float)((uint8_t)(b >> 24)) / 255; - cv[3] = (float)((uint8_t)(a >> 24)) / 255; - - swColor4fv(cv); - } - - void swColor4uiv(const uint32_t* v) - { - float cv[4]; - cv[0] = (float)((uint8_t)(v[0] >> 24)) / 255; - cv[1] = (float)((uint8_t)(v[1] >> 24)) / 255; - cv[2] = (float)((uint8_t)(v[2] >> 24)) / 255; - cv[3] = (float)((uint8_t)(v[3] >> 24)) / 255; - - swColor4fv(cv); - } - - void swColor4f(float r, float g, float b, float a) - { - float cv[4]; - cv[0] = r; - cv[1] = g; - cv[2] = b; - cv[3] = a; - - swColor4fv(cv); - } - - void swColor4fv(const float* v) - { - for (int i = 0; i < 4; i++) { - RLSW.vertexBuffer[RLSW.vertexCounter].color[i] = v[i]; - } - } - - void swTexCoord2f(float u, float v) - { - float s = RLSW.matTexture[0]*u + RLSW.matTexture[4]*v + RLSW.matTexture[12]; - float t = RLSW.matTexture[1]*u + RLSW.matTexture[5]*v + RLSW.matTexture[13]; - - RLSW.vertexBuffer[RLSW.vertexCounter].texcoord[0] = s; - RLSW.vertexBuffer[RLSW.vertexCounter].texcoord[1] = t; - } - - void swTexCoordfv(const float* v) - { - float s = RLSW.matTexture[0]*v[0] + RLSW.matTexture[4]*v[1] + RLSW.matTexture[12]; - float t = RLSW.matTexture[1]*v[0] + RLSW.matTexture[5]*v[1] + RLSW.matTexture[13]; - - RLSW.vertexBuffer[RLSW.vertexCounter].texcoord[0] = s; - RLSW.vertexBuffer[RLSW.vertexCounter].texcoord[1] = t; - } - - void swNormal3f(float x, float y, float z) - { - RLSW.vertexBuffer[RLSW.vertexCounter].normal[0] = x; - RLSW.vertexBuffer[RLSW.vertexCounter].normal[1] = y; - RLSW.vertexBuffer[RLSW.vertexCounter].normal[2] = z; - } - - void swNormal3fv(const float* v) - { - RLSW.vertexBuffer[RLSW.vertexCounter].normal[0] = v[0]; - RLSW.vertexBuffer[RLSW.vertexCounter].normal[1] = v[1]; - RLSW.vertexBuffer[RLSW.vertexCounter].normal[2] = v[2]; - } - - void swBindArray(SWarray type, void *buffer) - { - switch (type) { - case SW_VERTEX_ARRAY: - RLSW.array.positions = buffer; - break; - case SW_TEXTURE_COORD_ARRAY: - RLSW.array.texcoords = buffer; - break; - case SW_NORMAL_ARRAY: - RLSW.array.normals = buffer; - break; - case SW_COLOR_ARRAY: - RLSW.array.colors = buffer; - break; - default: - break; - } - } - - void swDrawArrays(SWfill mode, int offset, int count) - { - if (RLSW.array.positions == 0) { - RLSW.errCode = SW_INVALID_OPERATION; - return; - } - - swBegin(mode); - - for (int i = offset; i < count; i++) { - if (RLSW.array.texcoords) { - swTexCoordfv(RLSW.array.texcoords + 2 * i); - } - if (RLSW.array.normals) { - swNormal3fv(RLSW.array.normals + 3 * i); - } - if (RLSW.array.colors) { - swColor4ubv(RLSW.array.colors + 4 * i); - } - swVertex3fv(RLSW.array.positions + 3 * i); - } - - swEnd(); - } - - uint32_t swLoadTexture(const void *data, int width, int height, int format, int mipmapCount) - { - if (RLSW.loadedTextureCount >= SW_MAX_TEXTURES) { - RLSW.errCode = SW_ERROR_OUT_OF_MEMORY; - return 0; - } - - sw_texture_t texture = { 0 }; - texture.pixels = data; - texture.width = width; - texture.height = height; - texture.format = format; - texture.minFilter = SW_NEAREST; - texture.magFilter = SW_NEAREST; - texture.sWrap = SW_REPEAT; - texture.tWrap = SW_REPEAT; - texture.tx = 1.0f / width; - texture.ty = 1.0f / height; - (void)mipmapCount; - - uint32_t id = 0; - if (RLSW.freeTextureIdCount > 0) { - id = RLSW.freeTextureIds[--RLSW.freeTextureIdCount]; - } - else { - id = RLSW.loadedTextureCount++; - } - - RLSW.loadedTextures[id] = texture; - - return id; - } - - void swUnloadTexture(uint32_t id) - { - if (!sw_is_texture_id_valid(id)) { - RLSW.errCode = SW_INVALID_VALUE; - return; - } - - RLSW.loadedTextures[id].pixels = 0; - RLSW.freeTextureIds[RLSW.freeTextureIdCount++] = id; - } - - void swTextureParameters(uint32_t id, int param, int value) - { - if (!sw_is_texture_id_valid(id)) { - RLSW.errCode = SW_INVALID_VALUE; - return; - } - - sw_texture_t* texture = &RLSW.loadedTextures[id]; - - switch (param) { - - case SW_TEXTURE_MIN_FILTER: - if (!sw_is_texture_filter_valid(value)) { - RLSW.errCode = SW_INVALID_ENUM; - return; - } - texture->minFilter = value; - break; - - case SW_TEXTURE_MAG_FILTER: - if (!sw_is_texture_filter_valid(value)) { - RLSW.errCode = SW_INVALID_ENUM; - return; - } - texture->magFilter = value; - break; - - case SW_TEXTURE_WRAP_S: - if (!sw_is_texture_wrap_valid(value)) { - RLSW.errCode = SW_INVALID_ENUM; - return; - } - texture->sWrap = value; - break; - - case SW_TEXTURE_WRAP_T: - if (!sw_is_texture_wrap_valid(value)) { - RLSW.errCode = SW_INVALID_ENUM; - return; - } - texture->tWrap = value; - break; - - default: - RLSW.errCode = SW_INVALID_ENUM; - return; - - } - } - - void swBindTexture(uint32_t id) - { - if (id >= SW_MAX_TEXTURES) { - RLSW.errCode = SW_INVALID_VALUE; - return; - } - - if (id > 0 && RLSW.loadedTextures[id].pixels == 0) { - RLSW.errCode = SW_INVALID_OPERATION; - return; - } - - RLSW.currentTexture = id; - } - - #endif // RLSW_IMPL - \ No newline at end of file +#ifndef RLSW_H +#define RLSW_H + +#include +#include + + +/* === RLSW Definition And Macros === */ + +#ifndef SW_MALLOC +# define SW_MALLOC(sz) malloc(sz) +#endif + +#ifndef SW_FREE +# define SW_FREE(ptr) free(ptr) +#endif + +#ifndef SW_MAX_PROJECTION_STACK_SIZE +# define SW_MAX_PROJECTION_STACK_SIZE 2 +#endif + +#ifndef SW_MAX_MODELVIEW_STACK_SIZE +# define SW_MAX_MODELVIEW_STACK_SIZE 8 +#endif + +#ifndef SW_MAX_TEXTURE_STACK_SIZE +# define SW_MAX_TEXTURE_STACK_SIZE 4 +#endif + +#ifndef SW_MAX_TEXTURES +# define SW_MAX_TEXTURES 128 +#endif + +#ifndef SW_MAX_CLIPPED_POLYGON_VERTICES +# define SW_MAX_CLIPPED_POLYGON_VERTICES 12 +#endif + +#ifndef SW_CLIP_EPSILON +# define SW_CLIP_EPSILON 1e-4f +#endif + + +/* === OpenGL Definitions === */ + +#define GL_TEXTURE_2D 0x0DE1 +#define GL_DEPTH_TEST 0x0B71 +#define GL_CULL_FACE 0x0B44 + +#define GL_MODELVIEW 0x1700 +#define GL_PROJECTION 0x1701 +#define GL_TEXTURE 0x1702 + +#define GL_VERTEX_ARRAY 0x8074 +#define GL_NORMAL_ARRAY 0x8075 +#define GL_COLOR_ARRAY 0x8076 +//#define GL_INDEX_ARRAY 0x8077 +#define GL_TEXTURE_COORD_ARRAY 0x8078 + +#define GL_POINTS 0x0000 +#define GL_LINES 0x0001 +//#define GL_LINE_LOOP 0x0002 +//#define GL_LINE_STRIP 0x0003 +#define GL_TRIANGLES 0x0004 +//#define GL_TRIANGLE_STRIP 0x0005 +//#define GL_TRIANGLE_FAN 0x0006 +#define GL_QUADS 0x0007 +//#define GL_QUAD_STRIP 0x0008 +//#define GL_POLYGON 0x0009 + +//#define GL_CW 0x0900 +//#define GL_CCW 0x0901 + +#define GL_FRONT 0x0404 +#define GL_BACK 0x0405 + +#define GL_NEAREST 0x2600 +#define GL_LINEAR 0x2601 + +#define GL_REPEAT 0x2901 +#define GL_CLAMP_TO_EDGE 0x812F //< (OpenGL 1.2) +#define GL_MIRRORED_REPEAT 0x8370 //< (OpenGL 2.0) + +#define GL_TEXTURE_MAG_FILTER 0x2800 +#define GL_TEXTURE_MIN_FILTER 0x2801 + +#define GL_TEXTURE_WRAP_S 0x2802 +#define GL_TEXTURE_WRAP_T 0x2803 + +#define GL_NO_ERROR 0 +#define GL_INVALID_ENUM 0x0500 +#define GL_INVALID_VALUE 0x0501 +#define GL_INVALID_OPERATION 0x0502 +#define GL_STACK_OVERFLOW 0x0503 +#define GL_STACK_UNDERFLOW 0x0504 +#define GL_OUT_OF_MEMORY 0x0505 + + +/* === RLSW Enums === */ + +typedef enum { + SW_TEXTURE_2D = GL_TEXTURE_2D, + SW_DEPTH_TEST = GL_DEPTH_TEST, + SW_CULL_FACE = GL_CULL_FACE +} SWstate; + +typedef enum { + SW_PROJECTION = GL_PROJECTION, + SW_MODELVIEW = GL_MODELVIEW, + SW_TEXTURE = GL_TEXTURE +} SWmatrix; + +typedef enum { + SW_VERTEX_ARRAY = GL_VERTEX_ARRAY, + SW_TEXTURE_COORD_ARRAY = GL_TEXTURE_COORD_ARRAY, + SW_NORMAL_ARRAY = GL_NORMAL_ARRAY, + SW_COLOR_ARRAY = GL_COLOR_ARRAY +} SWarray; + +typedef enum { + SW_POINTS = GL_POINTS, + SW_LINES = GL_LINES, + SW_TRIANGLES = GL_TRIANGLES, + SW_QUADS = GL_QUADS, +} SWfill; + +typedef enum { + SW_FRONT = GL_FRONT, + SW_BACK = GL_BACK, +} SWface; + +typedef enum { + SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE = 1, // 8 bit per pixel (no alpha) + SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA, // 8*2 bpp (2 channels) + SW_PIXELFORMAT_UNCOMPRESSED_R5G6B5, // 16 bpp + SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8, // 24 bpp + SW_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1, // 16 bpp (1 bit alpha) + SW_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4, // 16 bpp (4 bit alpha) + SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, // 32 bpp + SW_PIXELFORMAT_UNCOMPRESSED_R32, // 32 bpp (1 channel - float) + SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32, // 32*3 bpp (3 channels - float) + SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32, // 32*4 bpp (4 channels - float) + SW_PIXELFORMAT_UNCOMPRESSED_R16, // 16 bpp (1 channel - half float) + SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16, // 16*3 bpp (3 channels - half float) + SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16, // 16*4 bpp (4 channels - half float) + SW_PIXELFORMAT_COMPRESSED_DXT1_RGB, // 4 bpp (no alpha) + SW_PIXELFORMAT_COMPRESSED_DXT1_RGBA, // 4 bpp (1 bit alpha) + SW_PIXELFORMAT_COMPRESSED_DXT3_RGBA, // 8 bpp + SW_PIXELFORMAT_COMPRESSED_DXT5_RGBA, // 8 bpp + SW_PIXELFORMAT_COMPRESSED_ETC1_RGB, // 4 bpp + SW_PIXELFORMAT_COMPRESSED_ETC2_RGB, // 4 bpp + SW_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA, // 8 bpp + SW_PIXELFORMAT_COMPRESSED_PVRT_RGB, // 4 bpp + SW_PIXELFORMAT_COMPRESSED_PVRT_RGBA, // 4 bpp + SW_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA, // 8 bpp + SW_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA // 2 bpp +} SWpixelformat; + +typedef enum { + SW_NEAREST = GL_NEAREST, + SW_LINEAR = GL_LINEAR +} SWfilter; + +typedef enum { + SW_REPEAT = GL_REPEAT, + SW_CLAMP_TO_EDGE = GL_CLAMP_TO_EDGE, + SW_MIRRORED_REPEAT = GL_MIRRORED_REPEAT +} SWwrap; + +typedef enum { + SW_TEXTURE_MIN_FILTER = GL_TEXTURE_MIN_FILTER, + SW_TEXTURE_MAG_FILTER = GL_TEXTURE_MAG_FILTER, + SW_TEXTURE_WRAP_S = GL_TEXTURE_WRAP_S, + SW_TEXTURE_WRAP_T = GL_TEXTURE_WRAP_T +} SWtexparam; + +typedef enum { + SW_NO_ERROR = GL_NO_ERROR, + SW_INVALID_ENUM = GL_INVALID_ENUM, + SW_INVALID_VALUE = GL_INVALID_VALUE, + SW_STACK_OVERFLOW = GL_STACK_OVERFLOW, + SW_STACK_UNDERFLOW = GL_STACK_UNDERFLOW, + SW_INVALID_OPERATION = GL_INVALID_OPERATION, +} SWerrcode; + +/* === Public API === */ + +void swInit(int w, int h); +void swClose(void); + +void swEnable(SWstate state); +void swDisable(SWstate state); + +void* swGetColorBuffer(int* w, int* h); + +void swMatrixMode(SWmatrix mode); +void swPushMatrix(void); +void swPopMatrix(void); +void swLoadIdentity(void); +void swTranslatef(float x, float y, float z); +void swRotatef(float angle, float x, float y, float z); +void swScalef(float x, float y, float z); +void swMultMatrixf(const float* mat); +void swFrustum(float left, float right, float bottom, float top, float znear, float zfar); +void swOrtho(float left, float right, float bottom, float top, float znear, float zfar); + +void swViewport(int x, int y, int width, int height); + +void swClearColor(float r, float g, float b, float a); +void swClear(void); + +void swBegin(SWfill mode); +void swEnd(void); + +void swVertex2i(int x, int y); +void swVertex2f(float x, float y); +void swVertex2fv(const float* v); +void swVertex3i(int x, int y, int z); +void swVertex3f(float x, float y, float z); +void swVertex3fv(const float* v); +void swVertex4i(int x, int y, int z, int w); +void swVertex4f(float x, float y, float z, float w); +void swVertex4fv(const float* v); + +void swColor1ui(uint32_t color); +void swColor3ub(uint8_t r, uint8_t g, uint8_t b); +void swColor3ubv(const uint8_t* v); +void swColor3us(uint16_t r, uint16_t g, uint16_t b); +void swColor3usv(const uint16_t* v); +void swColor3ui(uint32_t r, uint32_t g, uint32_t b); +void swColor3uiv(const uint32_t* v); +void swColor3f(float r, float g, float b); +void swColor3fv(const float* v); +void swColor4ub(uint8_t r, uint8_t g, uint8_t b, uint8_t a); +void swColor4ubv(const uint8_t* v); +void swColor4us(uint16_t r, uint16_t g, uint16_t b, uint16_t a); +void swColor4usv(const uint16_t* v); +void swColor4ui(uint32_t r, uint32_t g, uint32_t b, uint32_t a); +void swColor4uiv(const uint32_t* v); +void swColor4f(float r, float g, float b, float a); +void swColor4fv(const float* v); + +void swTexCoord2f(float u, float v); +void swTexCoordfv(const float* v); + +void swNormal3f(float x, float y, float z); +void swNormal3fv(const float* v); + +void swBindArray(SWarray type, void *buffer); +void swDrawArrays(SWfill mode, int offset, int count); + +uint32_t swLoadTexture(const void *data, int width, int height, int format, int mipmapCount); +void swUnloadTexture(uint32_t id); + +void swTextureParameters(uint32_t id, int param, int value); +void swBindTexture(uint32_t id); + +#endif // RLSW_H + + +#ifdef RLSW_IMPL + +#include +#include + +/* === Defines and Macros === */ + +#define SW_PI 3.14159265358979323846f +#define SW_DEG2RAD (SW_PI/180.0f) +#define SW_RAD2DEG (180.0f/SW_PI) + +#define SW_STATE_TEXTURE_2D (1 << 0) +#define SW_STATE_DEPTH_TEST (1 << 1) +#define SW_STATE_CULL_FACE (1 << 2) + +/* === Internal Structs === */ + +typedef float sw_matrix_t[4*4]; +typedef uint16_t sw_half_t; + +typedef struct { + + float position[4]; // Position coordinates + float normal[3]; // Normal vector + float texcoord[2]; // Texture coordinates + float color[4]; // Color + + float homogeneous[4]; // Homogeneous coordinates + float screen[2]; // Screen coordinates + +} sw_vertex_t; + +typedef struct { + + const void* pixels; + int width; + int height; + int format; + + SWfilter minFilter; + SWfilter magFilter; + + SWwrap sWrap; + SWwrap tWrap; + + float tx; + float ty; + +} sw_texture_t; + +typedef struct { + uint8_t *color; // 32-bit RGBA color buffer + uint16_t *depth; // 16-bit fixed fract buffer + int width, height; +} sw_framebuffer_t; + +typedef struct { + + sw_framebuffer_t framebuffer; + uint8_t clearColor[4]; // Color used to clear the screen + uint16_t clearDepth; // Depth value used to clear the screen + + uint32_t currentTexture; + sw_matrix_t *currentMatrix; + + uint32_t blendFunction; + uint32_t depthFunction; + + int vpPos[2]; // Represents the top-left corner of the viewport + int vpDim[2]; // Represents the dimensions of the viewport (minus one) + int vpMin[2]; // Represents the minimum renderable point of the viewport (top-left) + int vpMax[2]; // Represents the maximum renderable point of the viewport (bottom-right) + + struct { + float* positions; + float* texcoords; + float* normals; + uint8_t* colors; + } array; + + sw_vertex_t vertexBuffer[4]; // Buffer used for storing primitive vertices, used for processing and rendering + int vertexCounter; // Number of vertices in 'ctx.vertexBuffer' + + SWfill fillMode; // Current polygon filling mode (e.g., lines, triangles) + float pointSize; // Rasterized point size + float lineWidth; // Rasterized line width + + sw_matrix_t matProjection; // Projection matrix, user adjustable + sw_matrix_t matTexture; // Texture matrix, user adjustable + sw_matrix_t matModel; // Model matrix, user adjustable (the one used if we push in SW_MODELVIEW mode) + sw_matrix_t matView; // View matrix, user adjustable (the default one used in SW_MODELVIEW mode) + sw_matrix_t matMVP; // Model view projection matrix, calculated and used internally + + sw_matrix_t stackProjection[SW_MAX_PROJECTION_STACK_SIZE]; // Projection matrix stack for push/pop operations + sw_matrix_t stackModelview[SW_MAX_MODELVIEW_STACK_SIZE]; // Modelview matrix stack for push/pop operations + sw_matrix_t stackTexture[SW_MAX_TEXTURE_STACK_SIZE]; // Texture matrix stack for push/pop operations + uint32_t stackProjectionCounter; // Counter for matrix stack operations + uint32_t stackModelviewCounter; // Counter for matrix stack operations + uint32_t stackTextureCounter; // Counter for matrix stack operations + + SWmatrix currentMatrixMode; // Current matrix mode (e.g., sw_MODELVIEW, sw_PROJECTION) + bool modelMatrixUsed; // Flag indicating if the model matrix is used + + SWface cullFace; // Faces to cull + SWerrcode errCode; // Last error code + + sw_texture_t* loadedTextures; + int loadedTextureCount; + + uint32_t* freeTextureIds; + int freeTextureIdCount; + + uint32_t stateFlags; + +} sw_data_t; + + +/* === Global Data === */ + +static sw_data_t RLSW = { 0 }; + + +/* === Helper Functions === */ + +static inline void sw_matrix_id(sw_matrix_t dst) +{ + dst[0] = 1, dst[1] = 0, dst[2] = 0, dst[3] = 0; + dst[4] = 0, dst[5] = 1, dst[6] = 0, dst[7] = 0; + dst[8] = 0, dst[9] = 0, dst[10] = 1, dst[11] = 0; + dst[12] = 0, dst[13] = 0, dst[14] = 0, dst[15] = 1; +} + +static inline void sw_matrix_mul(sw_matrix_t dst, const sw_matrix_t left, const sw_matrix_t right) +{ + sw_matrix_t result; + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + float sum = 0.0; + for (int k = 0; k < 4; k++) { + sum += left[i * 4 + k] * right[k * 4 + j]; + } + result[i * 4 + j] = sum; + } + } + for (int i = 0; i < 16; i++) { + dst[i] = result[i]; + } +} + +static inline void sw_vec4_transform(float dst[4], const float v[4], const sw_matrix_t mat) +{ + float tmp[4] = { + mat[0] * v[0] + mat[4] * v[1] + mat[8] * v[2] + mat[12] * v[3], + mat[1] * v[0] + mat[5] * v[1] + mat[9] * v[2] + mat[13] * v[3], + mat[2] * v[0] + mat[6] * v[1] + mat[10] * v[2] + mat[14] * v[3], + mat[3] * v[0] + mat[7] * v[1] + mat[11] * v[2] + mat[15] * v[3] + }; + + for (int i = 0; i < 4; i++) { + dst[i] = tmp[i]; + } +} + +static inline float sw_lerp(float a, float b, float t) +{ + return a + t * (b - a); +} + +static inline sw_vertex_t sw_lerp_vertex(const sw_vertex_t* a, const sw_vertex_t* b, float t) +{ + sw_vertex_t result; + for (int i = 0; i < sizeof(sw_vertex_t) / sizeof(float); i++) { + ((float*)&result)[i] = sw_lerp(((float*)a)[i], ((float*)b)[i], t); + } + return result; +} + +static inline uint32_t sw_cvt_hf_ui(uint16_t h) +{ + uint32_t s = (uint32_t)(h & 0x8000) << 16; + int32_t em = h & 0x7fff; + + // bias exponent and pad mantissa with 0; 112 is relative exponent bias (127-15) + int32_t r = (em + (112 << 10)) << 13; + + // denormal: flush to zero + r = (em < (1 << 10)) ? 0 : r; + + // infinity/NaN; note that we preserve NaN payload as a byproduct of unifying inf/nan cases + // 112 is an exponent bias fixup; since we already applied it once, applying it twice converts 31 to 255 + r += (em >= (31 << 10)) ? (112 << 23) : 0; + + return s | r; +} + +static inline float sw_cvt_hf(sw_half_t y) +{ + union { float f; uint32_t i; } v = { + .i = sw_cvt_hf_ui(y) + }; + return v.f; +} + +static inline void sw_get_pixel_grayscale(float* color, const void* pixels, uint32_t offset) +{ + float gray = (float)((uint8_t*)pixels)[offset] / 255; + + color[0] = gray; + color[1] = gray; + color[2] = gray; + color[3] = 1.0f; +} + +static inline void sw_get_pixel_red_16(float* color, const void* pixels, uint32_t offset) +{ + float value = sw_cvt_hf(((sw_half_t*)pixels)[offset]); + + color[0] = value; + color[1] = value; + color[2] = value; + color[3] = 1.0f; +} + +static inline void sw_get_pixel_red_32(float* color, const void* pixels, uint32_t offset) +{ + float value = ((float*)pixels)[offset]; + + color[0] = value; + color[1] = value; + color[2] = value; + color[3] = 1.0f; +} + +static inline void sw_get_pixel_grayscale_alpha(float* color, const void* pixels, uint32_t offset) +{ + float gray = (float)((uint8_t*)pixels)[2 * offset] / 255; + float alpha = (float)((uint8_t*)pixels)[2 * offset + 1] / 255; + + color[0] = gray; + color[1] = gray; + color[2] = gray; + color[3] = alpha; +} + +static inline void sw_get_pixel_rgb_565(float* color, const void* pixels, uint32_t offset) +{ + uint16_t pixel = ((uint16_t*)pixels)[offset]; + + color[0] = (float)((pixel & 0xF800) >> 11) / 31; + color[1] = (float)((pixel & 0x7E0) >> 5) / 63; + color[2] = (float)(pixel & 0x1F) / 31; + color[3] = 1.0f; +} + +static inline void sw_get_pixel_rgb_888(float* color, const void* pixels, uint32_t offset) +{ + const uint8_t* pixel = (uint8_t*)pixels + 3 * offset; + + color[0] = (float)pixel[0] / 255; + color[1] = (float)pixel[1] / 255; + color[2] = (float)pixel[2] / 255; + color[3] = 1.0f; +} + +static inline void sw_get_pixel_rgb_161616(float* color, const void* pixels, uint32_t offset) +{ + const sw_half_t *pixel = (sw_half_t*)pixels + 3 * offset; + + color[0] = sw_cvt_hf(pixel[0]); + color[1] = sw_cvt_hf(pixel[1]); + color[2] = sw_cvt_hf(pixel[2]); + color[3] = 1.0f; +} + +static inline void sw_get_pixel_rgb_323232(float* color, const void* pixels, uint32_t offset) +{ + const float *pixel = (float*)pixels + 3 * offset; + + color[0] = pixel[0]; + color[1] = pixel[1]; + color[2] = pixel[2]; + color[3] = 1.0f; +} + +static inline void sw_get_pixel_rgba_5551(float* color, const void* pixels, uint32_t offset) +{ + uint16_t pixel = ((uint16_t*)pixels)[offset]; + + color[0] = (float)((pixel & 0xF800) >> 11) / 31; + color[1] = (float)((pixel & 0x7C0) >> 6) / 31; + color[2] = (float)((pixel & 0x3E) >> 1) / 31; + color[3] = (float)(pixel & 0x1); +} + +static inline void sw_get_pixel_rgba_4444(float* color, const void* pixels, uint32_t offset) +{ + uint16_t pixel = ((uint16_t*)pixels)[offset]; + + color[0] = (float)((pixel & 0xF000) >> 12) / 15; + color[1] = (float)((pixel & 0xF00) >> 8) / 15; + color[2] = (float)((pixel & 0xF0) >> 4) / 15; + color[3] = (float)(pixel & 0xF) / 15; +} + +static inline void sw_get_pixel_rgba_8888(float* color, const void* pixels, uint32_t offset) +{ + const uint8_t *pixel = (uint8_t*)pixels + 4 * offset; + + color[0] = (float)pixel[0] / 255; + color[1] = (float)pixel[1] / 255; + color[2] = (float)pixel[2] / 255; + color[3] = (float)pixel[3] / 255; +} + +static inline void sw_get_pixel_rgba_16161616(float* color, const void* pixels, uint32_t offset) +{ + const sw_half_t *pixel = (sw_half_t*)pixels + 4 * offset; + + color[0] = sw_cvt_hf(pixel[0]); + color[1] = sw_cvt_hf(pixel[1]); + color[2] = sw_cvt_hf(pixel[2]); + color[3] = sw_cvt_hf(pixel[3]); +} + +static inline void sw_get_pixel_rgba_32323232(float* color, const void* pixels, uint32_t offset) +{ + const float *pixel = (float*)pixels + 4 * offset; + + color[0] = pixel[0]; + color[1] = pixel[1]; + color[2] = pixel[2]; + color[3] = pixel[3]; +} + +static inline void sw_get_pixel(float* color, const void* pixels, uint32_t offset, SWpixelformat format) +{ + switch (format) { + + case SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: + sw_get_pixel_grayscale(color, pixels, offset); + break; + + case SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: + sw_get_pixel_grayscale_alpha(color, pixels, offset); + break; + + case SW_PIXELFORMAT_UNCOMPRESSED_R5G6B5: + sw_get_pixel_rgb_565(color, pixels, offset); + break; + + case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8: + sw_get_pixel_rgb_888(color, pixels, offset); + break; + + case SW_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: + sw_get_pixel_rgba_5551(color, pixels, offset); + break; + + case SW_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: + sw_get_pixel_rgba_4444(color, pixels, offset); + break; + + case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: + sw_get_pixel_rgba_8888(color, pixels, offset); + break; + + case SW_PIXELFORMAT_UNCOMPRESSED_R32: + sw_get_pixel_red_32(color, pixels, offset); + break; + + case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32: + sw_get_pixel_rgb_323232(color, pixels, offset); + break; + + case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: + sw_get_pixel_rgba_32323232(color, pixels, offset); + break; + + case SW_PIXELFORMAT_UNCOMPRESSED_R16: + sw_get_pixel_red_16(color, pixels, offset); + break; + + case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16: + sw_get_pixel_rgb_161616(color, pixels, offset); + break; + + case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: + sw_get_pixel_rgba_16161616(color, pixels, offset); + break; + + case SW_PIXELFORMAT_COMPRESSED_DXT1_RGB: + case SW_PIXELFORMAT_COMPRESSED_DXT1_RGBA: + case SW_PIXELFORMAT_COMPRESSED_DXT3_RGBA: + case SW_PIXELFORMAT_COMPRESSED_DXT5_RGBA: + case SW_PIXELFORMAT_COMPRESSED_ETC1_RGB: + case SW_PIXELFORMAT_COMPRESSED_ETC2_RGB: + case SW_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA: + case SW_PIXELFORMAT_COMPRESSED_PVRT_RGB: + case SW_PIXELFORMAT_COMPRESSED_PVRT_RGBA: + case SW_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: + case SW_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: + break; + + } +} + +static inline void sw_map_repeat(int* out, float in, int max) +{ + // Upscale to nearest texture coordinates + // NOTE: We use '(int)(x+0.5)' although this is incorrect + // regarding the direction of rounding in case of negative values + // and also less accurate than roundf, but it remains so much more + // efficient that it is preferable for now to opt for this option. + + *out = abs((int)((in - (int)in) * (max - 1) + 0.5f)); +} + +static inline void sw_map_clamp_to_edge(int* out, float in, int max) +{ + in = (in > 1.0f) ? 1.0f : ((in < 0.0f) ? 0.0f : in); + *out = (int)(in * (max - 1) + 0.5f); +} + +static inline void sw_map_mirrored_repeat(int* out, float in, int max) +{ + in = fmodf(fabsf(in), 2); + if (in > 1.0f) in = 1.0f - (in - 1.0f); + *out = (int)(in * (max - 1) + 0.5f); +} + +static inline void sw_map(int* out, float in, int max, SWwrap mode) +{ + switch (mode) { + case SW_REPEAT: + sw_map_repeat(out, in, max); + break; + case SW_CLAMP_TO_EDGE: + sw_map_clamp_to_edge(out, in, max); + break; + case SW_MIRRORED_REPEAT: + sw_map_mirrored_repeat(out, in, max); + break; + } +} + +static inline void sw_sample_texture_nearest(float* color, const sw_texture_t* tex, float u, float v) +{ + int x, y; + sw_map(&x, u, tex->width, tex->sWrap); + sw_map(&y, v, tex->height, tex->tWrap); + sw_get_pixel(color, tex->pixels, y * tex->width + x, tex->format); +} + +static inline void sw_sample_texture_bilinear(float* color, const sw_texture_t* tex, float u, float v) +{ + int x0, y0, x1, y1; + sw_map(&x0, u, tex->width, tex->sWrap); + sw_map(&y0, v, tex->height, tex->tWrap); + sw_map(&x1, u + tex->tx, tex->width, tex->sWrap); + sw_map(&y1, v + tex->ty, tex->height, tex->tWrap); + + float fx = u * (tex->width - 1) - x0; + float fy = v * (tex->height - 1) - y0; + + float c00[4], c10[4], c01[4], c11[4]; + sw_get_pixel(c00, tex->pixels, y0 * tex->width + x0, tex->format); + sw_get_pixel(c10, tex->pixels, y0 * tex->width + x1, tex->format); + sw_get_pixel(c01, tex->pixels, y1 * tex->width + x0, tex->format); + sw_get_pixel(c11, tex->pixels, y1 * tex->width + x1, tex->format); + + float c0[4], c1[4]; + for (int i = 0; i < 4; i++) { + float a = sw_lerp(c00[i], c10[i], fx); + float b = sw_lerp(c01[i], c11[i], fx); + color[i] = sw_lerp(a, b, fy); + } +} + +static inline void sw_sample_texture(float* color, const sw_texture_t* tex, float u, float v, + float xDu, float yDu, float xDv, float yDv) +{ + // TODO: It seems there are some incorrect detections depending on the context + // This is probably due to the fact that the fractions are obtained + // at the wrong moment during rasterization. It would be worth reviewing + // this, although the scanline method complicates things. + + // Calculate the derivatives for each axis + float du = sqrtf(xDu * xDu + yDu * yDu); + float dv = sqrtf(xDv * xDv + yDv * yDv); + float L = (du > dv) ? du : dv; + + // Select the filter based on the size of the footprint + if (L > 1.0f) { + // Minification + if (tex->minFilter == SW_NEAREST) { + sw_sample_texture_nearest(color, tex, u, v); + } else if (tex->minFilter == SW_LINEAR) { + sw_sample_texture_bilinear(color, tex, u, v); + } + } else { + // Magnification + if (tex->magFilter == SW_NEAREST) { + sw_sample_texture_nearest(color, tex, u, v); + } else if (tex->magFilter == SW_LINEAR) { + sw_sample_texture_bilinear(color, tex, u, v); + } + } +} + +static inline bool sw_clip_polygon_w(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES], int* vertexCounter) +{ + sw_vertex_t input[SW_MAX_CLIPPED_POLYGON_VERTICES]; + for (int i = 0; i < SW_MAX_CLIPPED_POLYGON_VERTICES; i++) { + input[i] = polygon[i]; + } + + int inputCounter = *vertexCounter; + *vertexCounter = 0; + + const sw_vertex_t *prevVt = &input[inputCounter-1]; + char prevDot = (prevVt->homogeneous[3] < SW_CLIP_EPSILON) ? -1 : 1; + + for (int i = 0; i < inputCounter; i++) { + char currDot = (input[i].homogeneous[3] < SW_CLIP_EPSILON) ? -1 : 1; + if (prevDot*currDot < 0) { + polygon[(*vertexCounter)++] = sw_lerp_vertex(prevVt, &input[i], + (SW_CLIP_EPSILON - prevVt->homogeneous[3]) / (input[i].homogeneous[3] - prevVt->homogeneous[3])); + } + if (currDot > 0) { + polygon[(*vertexCounter)++] = input[i]; + } + prevDot = currDot; + prevVt = &input[i]; + } + + return *vertexCounter > 0; +} + +static inline bool sw_clip_polygon_xyz(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES], int* vertexCounter) +{ + for (int iAxis = 0; iAxis < 3; iAxis++) + { + if (*vertexCounter == 0) return false; + + sw_vertex_t input[SW_MAX_CLIPPED_POLYGON_VERTICES]; + int inputCounter; + + const sw_vertex_t *prevVt; + char prevDot; + + // Clip against first plane + + for (int i = 0; i < SW_MAX_CLIPPED_POLYGON_VERTICES; i++) { + input[i] = polygon[i]; + } + inputCounter = *vertexCounter; + *vertexCounter = 0; + + prevVt = &input[inputCounter-1]; + prevDot = (prevVt->homogeneous[iAxis] <= prevVt->homogeneous[3]) ? 1 : -1; + + for (int i = 0; i < inputCounter; i++) { + char currDot = (input[i].homogeneous[iAxis] <= input[i].homogeneous[3]) ? 1 : -1; + if (prevDot * currDot <= 0) { + polygon[(*vertexCounter)++] = sw_lerp_vertex(prevVt, &input[i], (prevVt->homogeneous[3] - prevVt->homogeneous[iAxis]) / + ((prevVt->homogeneous[3] - prevVt->homogeneous[iAxis]) - (input[i].homogeneous[3] - input[i].homogeneous[iAxis]))); + } + if (currDot > 0) { + polygon[(*vertexCounter)++] = input[i]; + } + prevDot = currDot; + prevVt = &input[i]; + } + + if (*vertexCounter == 0) return false; + + // Clip against opposite plane + + for (int i = 0; i < SW_MAX_CLIPPED_POLYGON_VERTICES; i++) { + input[i] = polygon[i]; + } + inputCounter = *vertexCounter; + *vertexCounter = 0; + + prevVt = &input[inputCounter-1]; + prevDot = (-prevVt->homogeneous[iAxis] <= prevVt->homogeneous[3]) ? 1 : -1; + + for (int i = 0; i < inputCounter; i++) { + char currDot = (-input[i].homogeneous[iAxis] <= input[i].homogeneous[3]) ? 1 : -1; + if (prevDot*currDot <= 0) { + polygon[(*vertexCounter)++] = sw_lerp_vertex(prevVt, &input[i], (prevVt->homogeneous[3] + prevVt->homogeneous[iAxis]) / + ((prevVt->homogeneous[3] + prevVt->homogeneous[iAxis]) - (input[i].homogeneous[3] + input[i].homogeneous[iAxis]))); + } + if (currDot > 0) { + polygon[(*vertexCounter)++] = input[i]; + } + prevDot = currDot; + prevVt = &input[i]; + } + } + + return *vertexCounter > 0; +} + +void sw_project_and_clip_triangle(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES], int* vertexCounter) +{ + for (int i = 0; i < *vertexCounter; i++) { + sw_vertex_t *v = polygon + i; + for (int j = 0; j < 4; j++) v->homogeneous[j] = v->position[j]; + sw_vec4_transform(v->homogeneous, v->homogeneous, RLSW.matMVP); + } + + if (sw_clip_polygon_w(polygon, vertexCounter) && sw_clip_polygon_xyz(polygon, vertexCounter)) { + for (int i = 0; i < *vertexCounter; i++) { + sw_vertex_t *v = polygon + i; + + // Calculation of the reciprocal of W for normalization + // as well as perspective correct attributes + v->homogeneous[3] = 1.0f / v->homogeneous[3]; + + // Division of XYZ coordinates by weight + v->homogeneous[0] *= v->homogeneous[3]; + v->homogeneous[1] *= v->homogeneous[3]; + v->homogeneous[2] *= v->homogeneous[3]; + + // Division of texture coordinates (perspective correct) + v->texcoord[0] *= v->homogeneous[3]; + v->texcoord[1] *= v->homogeneous[3]; + + // Transform to screen space + v->screen[0] = RLSW.vpPos[0] + (v->homogeneous[0] + 1.0f) * 0.5f * RLSW.vpDim[0]; + v->screen[1] = RLSW.vpPos[1] + (v->homogeneous[1] + 1.0f) * 0.5f * RLSW.vpDim[1]; + } + } +} + +#define DEFINE_RASTER_SCANLINE(FUNC_NAME, ENABLE_TEXTURE, ENABLE_DEPTH_TEST) \ +void FUNC_NAME(const sw_texture_t* tex, const sw_vertex_t* start, \ + const sw_vertex_t* end, float yDu, float yDv) \ +{ \ + /* Calculate the horizontal width and avoid division by zero */ \ + float dx = end->screen[0] - start->screen[0]; \ + if (fabsf(dx) < 1e-4f) return; \ + \ + /* Convert and center the screen coordinates */ \ + int xStart = (int)(start->screen[0] + 0.5f); \ + int xEnd = (int)(end->screen[0] + 0.5f); \ + int y = (int)(start->screen[1] + 0.5f); \ + \ + /* Calculate the initial interpolation parameter and its increment */ \ + float dt = 1.0f / dx; \ + float t = (xStart - start->screen[0]) * dt; \ + \ + float xDu, xDv; \ + if (ENABLE_TEXTURE) { \ + /* Calculate the horizontal gradients for UV coordinates */ \ + xDu = (end->texcoord[0] - start->texcoord[0]) * dt; \ + xDv = (end->texcoord[1] - start->texcoord[1]) * dt; \ + } \ + \ + /* Pre-calculate the color differences for interpolation */ \ + float dcol[4]; \ + for (int i = 0; i < 4; i++) { \ + dcol[i] = end->color[i] - start->color[i]; \ + } \ + \ + /* Pre-calculate the differences in Z and W \ + (for depth testing and perspective correction) */ \ + float dz = end->homogeneous[2] - start->homogeneous[2]; \ + float dw = end->homogeneous[3] - start->homogeneous[3]; \ + \ + float u, v; \ + if (ENABLE_TEXTURE) { \ + /* Initialize the interpolated texture coordinates */ \ + u = start->texcoord[0] + t * xDu; \ + v = start->texcoord[1] + t * xDv; \ + } \ + \ + /* Pre-calculate the starting pointer for the color framebuffer row */ \ + uint8_t* row_ptr = (uint8_t*)((uint32_t*)RLSW.framebuffer.color + y * RLSW.framebuffer.width); \ + uint8_t* dst = row_ptr + xStart * 4; \ + \ + /* Pre-calculate the pointer for the depth buffer row */ \ + uint16_t* depth_row = RLSW.framebuffer.depth + y * RLSW.framebuffer.width + xStart; \ + uint16_t* dptr = depth_row; \ + \ + /* Scanline rasterization loop */ \ + for (int x = xStart; x < xEnd; x++) { \ + /* Interpolate Z and W for depth testing and perspective correction */ \ + float w = 1.0f / (start->homogeneous[3] + t * dw); \ + float z = start->homogeneous[2] + t * dz; \ + \ + if (ENABLE_DEPTH_TEST) { \ + /* Depth testing with direct access to the depth buffer */ \ + /* TODO: Implement different depth funcs? */ \ + float depth = (float)(*dptr) / UINT16_MAX; \ + if (z > depth) goto discard; \ + } \ + \ + /* Update the depth buffer */ \ + *dptr = (uint16_t)(z * UINT16_MAX); \ + \ + if (ENABLE_TEXTURE) \ + { \ + /* Sample the texture */ \ + float texColor[4]; \ + sw_sample_texture(texColor, tex, u * w, v * w, xDu, yDu, xDv, yDv); \ + \ + /* Interpolate the color and modulate by the texture color */ \ + for (int i = 0; i < 4; i++) { \ + float lerp = start->color[i] + t * dcol[i]; \ + float finalColor = texColor[i] * lerp; \ + /* Inline clamp to keep the value between 0 and 1 */ \ + /* NOTE: The need for clamp the colors could be a sign of problem during interpolation (?) */ \ + finalColor = (finalColor < 0.0f) ? 0.0f : (finalColor > 1.0f ? 1.0f : finalColor); \ + dst[i] = (uint8_t)(finalColor * 255.0f); \ + } \ + } \ + else \ + { \ + /* Interpolate the color */ \ + for (int i = 0; i < 4; i++) { \ + float finalColor = start->color[i] + t * dcol[i]; \ + /* Inline clamp to keep the value between 0 and 1 */ \ + /* NOTE: The need for clamp the colors could be a sign of problem during interpolation (?) */ \ + finalColor = (finalColor < 0.0f) ? 0.0f : (finalColor > 1.0f ? 1.0f : finalColor); \ + dst[i] = (uint8_t)(finalColor * 255.0f); \ + } \ + } \ + \ + /* Increment the interpolation parameter, UVs, and pointers */ \ + discard: \ + t += dt; \ + dst += 4; \ + dptr++; \ + if (ENABLE_TEXTURE) { \ + u += xDu; \ + v += xDv; \ + } \ + } \ +} + +#define DEFINE_RASTER_TRIANGLE(FUNC_NAME, FUNC_SCANLINE, ENABLE_TEXTURE) \ +void FUNC_NAME(const sw_vertex_t* v0, const sw_vertex_t* v1, const sw_vertex_t* v2, \ + const sw_texture_t* tex) \ +{ \ + /* Swap vertices by increasing y */ \ + if (v0->screen[1] > v1->screen[1]) { const sw_vertex_t* tmp = v0; v0 = v1; v1 = tmp; } \ + if (v1->screen[1] > v2->screen[1]) { const sw_vertex_t* tmp = v1; v1 = v2; v2 = tmp; } \ + if (v0->screen[1] > v1->screen[1]) { const sw_vertex_t* tmp = v0; v0 = v1; v1 = tmp; } \ + \ + /* Extracting coordinates from the sorted vertices */ \ + float x0 = v0->screen[0], y0 = v0->screen[1]; \ + float x1 = v1->screen[0], y1 = v1->screen[1]; \ + float x2 = v2->screen[0], y2 = v2->screen[1]; \ + \ + /* Reject degenerate triangles */ \ + float height = y2 - y0; \ + if (height < 1e-4f) return; \ + \ + /* Precompute the inverse of the triangle height and */ \ + /* edge lengths with checks to avoid division by zero. */ \ + float inv_height = 1.0f / height; \ + float inv_y1y0 = (y1 - y0 > 1e-4f) ? 1.0f / (y1 - y0) : 0.0f; \ + float inv_y2y1 = (y2 - y1 > 1e-4f) ? 1.0f / (y2 - y1) : 0.0f; \ + \ + /* Pre-calculation of slopes (dx/dy) */ \ + float dx02 = (x2 - x0) * inv_height; \ + float dx01 = (x1 - x0) * inv_y1y0; \ + float dx12 = (x2 - x1) * inv_y2y1; \ + \ + /* Y bounds (vertical clipping) */ \ + int yTop = (int)(y0 + 0.5f); \ + int yMiddle = (int)(y1 + 0.5f); \ + int yBottom = (int)(y2 + 0.5f); \ + \ + /* Global calculation of vertical texture gradients for the triangle */ \ + float yDu, yDv; \ + if (ENABLE_TEXTURE) { \ + yDu = (v2->texcoord[0] - v0->texcoord[0]) * inv_height; \ + yDv = (v2->texcoord[1] - v0->texcoord[1]) * inv_height; \ + } \ + \ + /* Initializing scanline variables */ \ + float xLeft = x0, xRight = x0; \ + sw_vertex_t start, end; \ + \ + /* Scanline for the upper part of the triangle */ \ + for (int y = yTop; y < yMiddle; y++) { \ + float dy = (float)y - y0; \ + float t1 = dy * inv_height; \ + float t2 = dy * inv_y1y0; \ + \ + /* Vertex interpolation */ \ + start = sw_lerp_vertex(v0, v2, t1); \ + end = sw_lerp_vertex(v0, v1, t2); \ + start.screen[0] = xLeft; \ + start.screen[1] = (float)y; \ + end.screen[0] = xRight; \ + end.screen[1] = (float)y; \ + \ + if (xLeft > xRight) { sw_vertex_t tmp = start; start = end; end = tmp; } \ + FUNC_SCANLINE(tex, &start, &end, yDu, yDv); \ + \ + /* Incremental update */ \ + xLeft += dx02; \ + xRight += dx01; \ + } \ + \ + /* Scanline for the lower part of the triangle */ \ + xRight = x1; /* Restart the right side from the second vertex */ \ + for (int y = yMiddle; y < yBottom; y++) { \ + float dy = (float)y - y0; \ + float t1 = dy * inv_height; \ + float t2 = (float)(y - y1) * inv_y2y1; \ + \ + /* Vertex interpolation */ \ + start = sw_lerp_vertex(v0, v2, t1); \ + end = sw_lerp_vertex(v1, v2, t2); \ + start.screen[0] = xLeft; \ + start.screen[1] = (float)y; \ + end.screen[0] = xRight; \ + end.screen[1] = (float)y; \ + \ + if (xLeft > xRight) { sw_vertex_t tmp = start; start = end; end = tmp; } \ + FUNC_SCANLINE(tex, &start, &end, yDu, yDv); \ + \ + /* Incremental update */ \ + xLeft += dx02; \ + xRight += dx12; \ + } \ +} + +DEFINE_RASTER_SCANLINE(sw_raster_scanline, false, false) +DEFINE_RASTER_SCANLINE(sw_raster_scanline_tex, true, false) +DEFINE_RASTER_SCANLINE(sw_raster_scanline_depth, false, true) +DEFINE_RASTER_SCANLINE(sw_raster_scanline_tex_depth, true, true) + +DEFINE_RASTER_TRIANGLE(sw_raster_triangle, sw_raster_scanline, false) +DEFINE_RASTER_TRIANGLE(sw_raster_triangle_tex, sw_raster_scanline_tex, true) +DEFINE_RASTER_TRIANGLE(sw_raster_triangle_depth, sw_raster_scanline_depth, false) +DEFINE_RASTER_TRIANGLE(sw_raster_triangle_tex_depth, sw_raster_scanline_tex_depth, true) + +void sw_render_triangle(const sw_vertex_t* v0, const sw_vertex_t* v1, const sw_vertex_t* v2) +{ + int vertexCounter = 3; + + sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES]; + polygon[0] = *v0; + polygon[1] = *v1; + polygon[2] = *v2; + + sw_project_and_clip_triangle(polygon, &vertexCounter); + + if (vertexCounter < 3) { + return; + } + + if ((RLSW.stateFlags & SW_STATE_TEXTURE_2D) && (RLSW.stateFlags & SW_STATE_DEPTH_TEST)) { + for (int_fast8_t i = 0; i < vertexCounter - 2; i++) { + sw_raster_triangle_tex_depth( + &polygon[0], &polygon[i + 1], &polygon[i + 2], + &RLSW.loadedTextures[RLSW.currentTexture] + ); + } + } + else if (RLSW.stateFlags & SW_STATE_TEXTURE_2D) { + for (int_fast8_t i = 0; i < vertexCounter - 2; i++) { + sw_raster_triangle_tex( + &polygon[0], &polygon[i + 1], &polygon[i + 2], + &RLSW.loadedTextures[RLSW.currentTexture] + ); + } + } + else if (RLSW.stateFlags & SW_STATE_DEPTH_TEST) { + for (int_fast8_t i = 0; i < vertexCounter - 2; i++) { + sw_raster_triangle_depth( + &polygon[0], &polygon[i + 1], &polygon[i + 2], + &RLSW.loadedTextures[RLSW.currentTexture] + ); + } + } + else { + for (int_fast8_t i = 0; i < vertexCounter - 2; i++) { + sw_raster_triangle( + &polygon[0], &polygon[i + 1], &polygon[i + 2], + &RLSW.loadedTextures[RLSW.currentTexture] + ); + } + } +} + +static inline bool sw_is_texture_id_valid(uint32_t id) +{ + bool valid = true; + + if (id == 0) valid = false; + else if (id >= SW_MAX_TEXTURES) valid = false; + else if (RLSW.loadedTextures[id].pixels == 0) valid = false; + + return true; +} + +static inline bool sw_is_texture_filter_valid(int filter) +{ + return (filter == SW_NEAREST || filter == SW_LINEAR); +} + +static inline bool sw_is_texture_wrap_valid(int wrap) +{ + return (wrap == SW_REPEAT || wrap == SW_CLAMP_TO_EDGE || SW_MIRRORED_REPEAT); +} + +/* === Public Implementation === */ + +void swInit(int w, int h) +{ + swViewport(0, 0, w, h); + + RLSW.framebuffer.color = SW_MALLOC(4 * w * h); + RLSW.framebuffer.depth = SW_MALLOC(2 * w * h); + + RLSW.framebuffer.width = w; + RLSW.framebuffer.height = h; + + RLSW.loadedTextures = SW_MALLOC(SW_MAX_TEXTURES); + RLSW.freeTextureIds = SW_MALLOC(SW_MAX_TEXTURES); + + RLSW.clearColor[0] = 0; + RLSW.clearColor[1] = 0; + RLSW.clearColor[2] = 0; + RLSW.clearColor[3] = 255; + RLSW.clearDepth = UINT16_MAX; + + RLSW.currentMatrixMode = SW_MODELVIEW; + RLSW.currentMatrix = &RLSW.matView; + + sw_matrix_id(RLSW.matProjection); + sw_matrix_id(RLSW.matTexture); + sw_matrix_id(RLSW.matModel); + sw_matrix_id(RLSW.matView); + + RLSW.vertexBuffer[0].color[0] = 1.0f; + RLSW.vertexBuffer[0].color[1] = 1.0f; + RLSW.vertexBuffer[0].color[2] = 1.0f; + RLSW.vertexBuffer[0].color[3] = 1.0f; + + RLSW.vertexBuffer[0].texcoord[0] = 0.0f; + RLSW.vertexBuffer[0].texcoord[1] = 0.0f; + + RLSW.vertexBuffer[0].normal[0] = 0.0f; + RLSW.vertexBuffer[0].normal[1] = 0.0f; + RLSW.vertexBuffer[0].normal[2] = 1.0f; + + static const float defTex[3*2*2] = + { + 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + }; + + RLSW.loadedTextures[0].pixels = defTex; + RLSW.loadedTextures[0].width = 2; + RLSW.loadedTextures[0].height = 2; + RLSW.loadedTextures[0].format = SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32; + RLSW.loadedTextures[0].minFilter = SW_NEAREST; + RLSW.loadedTextures[0].magFilter = SW_NEAREST; + RLSW.loadedTextures[0].sWrap = SW_REPEAT; + RLSW.loadedTextures[0].tWrap = SW_REPEAT; + RLSW.loadedTextures[0].tx = 0.5f; + RLSW.loadedTextures[0].ty = 0.5f; + + RLSW.loadedTextureCount = 1; +} + +void swClose(void) +{ + SW_FREE(RLSW.framebuffer.color); + SW_FREE(RLSW.framebuffer.depth); + + SW_FREE(RLSW.loadedTextures); + SW_FREE(RLSW.freeTextureIds); +} + +void swEnable(SWstate state) +{ + switch (state) { + case SW_TEXTURE_2D: + RLSW.stateFlags |= SW_STATE_TEXTURE_2D; + break; + case SW_DEPTH_TEST: + RLSW.stateFlags |= SW_STATE_DEPTH_TEST; + break; + case SW_CULL_FACE: + RLSW.stateFlags |= SW_STATE_CULL_FACE; + break; + default: + RLSW.errCode = SW_INVALID_ENUM; + break; + } +} + +void swDisable(SWstate state) +{ + switch (state) { + case SW_TEXTURE_2D: + RLSW.stateFlags &= ~SW_STATE_TEXTURE_2D; + break; + case SW_DEPTH_TEST: + RLSW.stateFlags &= ~SW_STATE_DEPTH_TEST; + break; + case SW_CULL_FACE: + RLSW.stateFlags &= ~SW_STATE_CULL_FACE; + break; + default: + RLSW.errCode = SW_INVALID_ENUM; + break; + } +} + +void* swGetColorBuffer(int* w, int* h) +{ + if (w) *w = RLSW.framebuffer.width; + if (h) *h = RLSW.framebuffer.height; + + return RLSW.framebuffer.color; +} + +void swMatrixMode(SWmatrix mode) +{ + switch (mode) { + case SW_PROJECTION: + RLSW.currentMatrix = &RLSW.matProjection; + break; + case SW_MODELVIEW: + RLSW.currentMatrix = RLSW.modelMatrixUsed + ? &RLSW.matModel : &RLSW.matView; + break; + case SW_TEXTURE: + RLSW.currentMatrix = &RLSW.matTexture; + break; + default: + RLSW.errCode = SW_INVALID_ENUM; + return; + } + + RLSW.currentMatrixMode = mode; +} + +void swPushMatrix(void) +{ + switch (RLSW.currentMatrixMode) { + + case SW_PROJECTION: + if (RLSW.stackProjectionCounter >= SW_MAX_PROJECTION_STACK_SIZE) { + RLSW.errCode = SW_STACK_OVERFLOW; + return; + } + for (int i = 0; i < 16; i++) { + RLSW.stackProjection[RLSW.stackProjectionCounter][i] = RLSW.matProjection[i]; + } + RLSW.stackProjectionCounter++; + break; + + case SW_MODELVIEW: + if (RLSW.stackModelviewCounter >= SW_MAX_MODELVIEW_STACK_SIZE) { + RLSW.errCode = SW_STACK_OVERFLOW; + return; + } + if (RLSW.modelMatrixUsed) { + for (int i = 0; i < 16; i++) { + RLSW.stackModelview[RLSW.stackModelviewCounter][i] = RLSW.matModel[i]; + } + RLSW.stackModelviewCounter++; + } else { + RLSW.currentMatrix = &RLSW.matModel; + RLSW.modelMatrixUsed = true; + } + break; + + case SW_TEXTURE: + if (RLSW.stackTextureCounter >= SW_MAX_TEXTURE_STACK_SIZE) { + RLSW.errCode = SW_STACK_OVERFLOW; + return; + } + for (int i = 0; i < 16; i++) { + RLSW.stackTexture[RLSW.stackTextureCounter][i] = RLSW.matTexture[i]; + } + RLSW.stackTextureCounter++; + break; + + } +} + +void swPopMatrix(void) +{ + switch (RLSW.currentMatrixMode) { + + case SW_PROJECTION: + if (RLSW.stackProjectionCounter <= 0) { + RLSW.errCode = SW_STACK_UNDERFLOW; + return; + } + RLSW.stackProjectionCounter--; + for (int i = 0; i < 16; i++) { + RLSW.matProjection[i] = RLSW.stackProjection[RLSW.stackProjectionCounter][i]; + } + break; + + case SW_MODELVIEW: + if (RLSW.stackModelviewCounter == 0) { + if (!RLSW.modelMatrixUsed) { + RLSW.errCode = SW_STACK_UNDERFLOW; + return; + } + sw_matrix_id(RLSW.matModel); + RLSW.currentMatrix = &RLSW.matView; + RLSW.modelMatrixUsed = false; + } else { + RLSW.stackModelviewCounter--; + for (int i = 0; i < 16; i++) { + RLSW.matModel[i] = RLSW.stackModelview[RLSW.stackModelviewCounter][i]; + } + } + break; + + case SW_TEXTURE: + if (RLSW.stackTextureCounter <= 0) { + RLSW.errCode = SW_STACK_UNDERFLOW; + return; + } + RLSW.stackTextureCounter--; + for (int i = 0; i < 16; i++) { + RLSW.matTexture[i] = RLSW.stackTexture[RLSW.stackTextureCounter][i]; + } + break; + + } +} + +void swLoadIdentity(void) +{ + sw_matrix_id(*RLSW.currentMatrix); +} + +void swTranslatef(float x, float y, float z) +{ + sw_matrix_t mat; + sw_matrix_id(mat); + + mat[12] = x; + mat[13] = y; + mat[14] = z; + + sw_matrix_mul(*RLSW.currentMatrix, mat, *RLSW.currentMatrix); +} + +void swRotatef(float angle, float x, float y, float z) +{ + angle *= SW_DEG2RAD; + + sw_matrix_t mat; + sw_matrix_id(mat); + + float lengthSq = x*x + y*y + z*z; + + if (lengthSq != 1.0f && lengthSq != 0.0f) { + float invLenght = 1.0f / lengthSq; + x *= invLenght; + y *= invLenght; + z *= invLenght; + } + + float sinres = sinf(angle); + float cosres = cosf(angle); + float t = 1.0f - cosres; + + mat[0] = x*x*t + cosres; + mat[1] = y*x*t + z*sinres; + mat[2] = z*x*t - y*sinres; + + mat[4] = x*y*t - z*sinres; + mat[5] = y*y*t + cosres; + mat[6] = z*y*t + x*sinres; + + mat[8] = x*z*t + y*sinres; + mat[9] = y*z*t - x*sinres; + mat[10] = z*z*t + cosres; + + sw_matrix_mul(*RLSW.currentMatrix, mat, *RLSW.currentMatrix); +} + +void swScalef(float x, float y, float z) +{ + sw_matrix_t mat; + + mat[0] = x, mat[1] = 0, mat[2] = 0, mat[3] = 0; + mat[4] = 0, mat[5] = y, mat[6] = 0, mat[7] = 0; + mat[8] = 0, mat[9] = 0, mat[10] = z, mat[11] = 0; + mat[12] = 0, mat[13] = 0, mat[14] = 0, mat[15] = 1; + + sw_matrix_mul(*RLSW.currentMatrix, mat, *RLSW.currentMatrix); +} + +void swMultMatrixf(const float* mat) +{ + sw_matrix_mul(*RLSW.currentMatrix, *RLSW.currentMatrix, mat); +} + +void swFrustum(float left, float right, float bottom, float top, float znear, float zfar) +{ + sw_matrix_t mat = { 0 }; + + float rl = right - left; + float tb = top - bottom; + float fn = zfar - znear; + + mat[0] = (znear * 2.0f) / rl; + mat[5] = (znear * 2.0f) / tb; + + mat[8] = (right + left) / rl; + mat[9] = (top + bottom) / tb; + mat[10] = -(zfar + znear) / fn; + mat[11] = -1.0f; + + mat[14] = -(zfar * znear * 2.0f) / fn; + + sw_matrix_mul(*RLSW.currentMatrix, *RLSW.currentMatrix, mat); +} + +void swOrtho(float left, float right, float bottom, float top, float znear, float zfar) +{ + sw_matrix_t mat = { 0 }; + + float rl = (right - left); + float tb = (top - bottom); + float fn = (zfar - znear); + + mat[0] = 2.0f / rl; + mat[5] = 2.0f / tb; + + mat[10] = -2.0f / fn; + mat[11] = 0.0f; + mat[12] = -(left + right) / rl; + mat[13] = -(top + bottom) / tb; + + mat[14] = -(zfar + znear) / fn; + mat[15] = 1.0f; + + sw_matrix_mul(*RLSW.currentMatrix, *RLSW.currentMatrix, mat); +} + +void swViewport(int x, int y, int width, int height) +{ + if (x <= -width || y <= -height) { + RLSW.errCode = SW_INVALID_OPERATION; + return; + } + + RLSW.vpPos[0] = x; + RLSW.vpPos[1] = y; + + RLSW.vpDim[0] = width - 1; + RLSW.vpDim[1] = height - 1; + + RLSW.vpMin[0] = (x < 0) ? 0 : x; + RLSW.vpMin[1] = (y < 0) ? 0 : y; + + int fbW = RLSW.framebuffer.width - 1; + int fbH = RLSW.framebuffer.height - 1; + + int vpMaxX = x + width; + int vpMaxY = y + height; + + RLSW.vpMax[0] = (vpMaxX < fbW) ? vpMaxX : fbW; + RLSW.vpMax[1] = (vpMaxY < fbH) ? vpMaxY : fbH; +} + +void swClearColor(float r, float g, float b, float a) +{ + RLSW.clearColor[0] = r * 255; + RLSW.clearColor[1] = g * 255; + RLSW.clearColor[2] = b * 255; + RLSW.clearColor[3] = a * 255; +} + +void swClear(void) +{ + int size = RLSW.framebuffer.width * RLSW.framebuffer.height; + + for (int i = 0; i < size; i++) { + ((uint32_t*)RLSW.framebuffer.color)[i] = *((uint32_t*)RLSW.clearColor); + RLSW.framebuffer.depth[i] = RLSW.clearDepth; + } +} + +void swBegin(SWfill mode) +{ + if (mode < SW_POINTS || mode > SW_QUADS) { + RLSW.errCode = SW_INVALID_ENUM; + return; + } + RLSW.vertexCounter = 0; + RLSW.fillMode = mode; +} + +void swEnd(void) +{ + RLSW.vertexCounter = 0; +} + +void swVertex2i(int x, int y) +{ + float v[4] = { (float)x, (float)y, 0.0f, 1.0f }; + swVertex4fv(v); +} + +void swVertex2f(float x, float y) +{ + float v[4] = { x, y, 0.0f, 1.0f }; + swVertex4fv(v); +} + +void swVertex2fv(const float* v) +{ + float v4[4] = { v[0], v[1], 0.0f, 1.0f }; + swVertex4fv(v4); +} + +void swVertex3i(int x, int y, int z) +{ + float v[4] = { (float)x, (float)y, (float)z, 1.0f }; + swVertex4fv(v); +} + +void swVertex3f(float x, float y, float z) +{ + float v[4] = { x, y, z, 1.0f }; + swVertex4fv(v); +} + +void swVertex3fv(const float* v) +{ + float v4[4] = { v[0], v[1], v[2], 1.0f }; + swVertex4fv(v4); +} + +void swVertex4i(int x, int y, int z, int w) +{ + float v[4] = { (float)x, (float)y, (float)z, (float)w }; + swVertex4fv(v); +} + +void swVertex4f(float x, float y, float z, float w) +{ + float v[4] = { x, y, z, w }; + swVertex4fv(v); +} + +void swVertex4fv(const float* v) +{ + for (int i = 0; i < 4; i++) { + RLSW.vertexBuffer[RLSW.vertexCounter].position[i] = v[i]; + } + RLSW.vertexCounter++; + + int neededVertices = 0; + switch (RLSW.fillMode) { + case SW_POINTS: + neededVertices = 1; + break; + case SW_LINES: + neededVertices = 2; + break; + case SW_TRIANGLES: + neededVertices = 3; + break; + case SW_QUADS: + neededVertices = 4; + break; + } + + if (RLSW.vertexCounter == neededVertices) { + + // TODO: Optimize MVP calculation + sw_matrix_mul(RLSW.matMVP, RLSW.matModel, RLSW.matView); + sw_matrix_mul(RLSW.matMVP, RLSW.matMVP, RLSW.matProjection); + + switch (RLSW.fillMode) { + case SW_POINTS: + break; + case SW_LINES: + neededVertices = 2; + break; + case SW_TRIANGLES: + sw_render_triangle( + &RLSW.vertexBuffer[0], + &RLSW.vertexBuffer[1], + &RLSW.vertexBuffer[2] + ); + break; + case SW_QUADS: + sw_render_triangle( + &RLSW.vertexBuffer[0], + &RLSW.vertexBuffer[1], + &RLSW.vertexBuffer[2] + ); + sw_render_triangle( + &RLSW.vertexBuffer[2], + &RLSW.vertexBuffer[3], + &RLSW.vertexBuffer[0] + ); + break; + } + + RLSW.vertexBuffer[0] = RLSW.vertexBuffer[neededVertices - 1]; + RLSW.vertexCounter = 0; + } + else { + RLSW.vertexBuffer[RLSW.vertexCounter] = RLSW.vertexBuffer[RLSW.vertexCounter - 1]; + } +} + +void swColor1ui(uint32_t color) +{ + union { + uint32_t v; + uint8_t a[4]; + } c = { .v = color }; + + float cv[4]; + cv[0] = (float)c.a[0] / 255; + cv[1] = (float)c.a[1] / 255; + cv[2] = (float)c.a[2] / 255; + cv[3] = (float)c.a[3] / 255; + + swColor4fv(cv); +} + +void swColor3ub(uint8_t r, uint8_t g, uint8_t b) +{ + float cv[4]; + cv[0] = (float)r / 255; + cv[1] = (float)g / 255; + cv[2] = (float)b / 255; + cv[3] = 1.0f; + + swColor4fv(cv); +} + +void swColor3ubv(const uint8_t* v) +{ + float cv[4]; + cv[0] = (float)v[0] / 255; + cv[1] = (float)v[1] / 255; + cv[2] = (float)v[2] / 255; + cv[3] = 1.0f; + + swColor4fv(cv); +} + +void swColor3us(uint16_t r, uint16_t g, uint16_t b) +{ + float cv[4]; + cv[0] = (float)((uint8_t)(r >> 8)) / 255; + cv[1] = (float)((uint8_t)(g >> 8)) / 255; + cv[2] = (float)((uint8_t)(b >> 8)) / 255; + cv[3] = 1.0f; + + swColor4fv(cv); +} + +void swColor3usv(const uint16_t* v) +{ + float cv[4]; + cv[0] = (float)((uint8_t)(v[0] >> 8)) / 255; + cv[1] = (float)((uint8_t)(v[1] >> 8)) / 255; + cv[2] = (float)((uint8_t)(v[2] >> 8)) / 255; + cv[3] = 1.0f; + + swColor4fv(cv); +} + +void swColor3ui(uint32_t r, uint32_t g, uint32_t b) +{ + float cv[4]; + cv[0] = (float)((uint8_t)(r >> 24)) / 255; + cv[1] = (float)((uint8_t)(g >> 24)) / 255; + cv[2] = (float)((uint8_t)(b >> 24)) / 255; + cv[3] = 1.0f; + + swColor4fv(cv); +} + +void swColor3uiv(const uint32_t* v) +{ + float cv[4]; + cv[0] = (float)((uint8_t)(v[0] >> 24)) / 255; + cv[1] = (float)((uint8_t)(v[1] >> 24)) / 255; + cv[2] = (float)((uint8_t)(v[2] >> 24)) / 255; + cv[3] = 1.0f; + + swColor4fv(cv); +} + +void swColor3f(float r, float g, float b) +{ + float cv[4]; + cv[0] = r; + cv[1] = g; + cv[2] = b; + cv[3] = 1.0f; + + swColor4fv(cv); +} + +void swColor3fv(const float* v) +{ + float cv[4]; + cv[0] = v[0]; + cv[1] = v[1]; + cv[2] = v[2]; + cv[3] = 1.0f; + + swColor4fv(cv); +} + +void swColor4ub(uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + float cv[4]; + cv[0] = (float)r / 255; + cv[1] = (float)g / 255; + cv[2] = (float)b / 255; + cv[3] = (float)a / 255; + + swColor4fv(cv); +} + +void swColor4ubv(const uint8_t* v) +{ + float cv[4]; + cv[0] = (float)v[0] / 255; + cv[1] = (float)v[1] / 255; + cv[2] = (float)v[2] / 255; + cv[3] = (float)v[3] / 255; + + swColor4fv(cv); +} + +void swColor4us(uint16_t r, uint16_t g, uint16_t b, uint16_t a) +{ + float cv[4]; + cv[0] = (float)((uint8_t)(r >> 8)) / 255; + cv[1] = (float)((uint8_t)(g >> 8)) / 255; + cv[2] = (float)((uint8_t)(b >> 8)) / 255; + cv[3] = (float)((uint8_t)(a >> 8)) / 255; + + swColor4fv(cv); +} + +void swColor4usv(const uint16_t* v) +{ + float cv[4]; + cv[0] = (float)((uint8_t)(v[0] >> 8)) / 255; + cv[1] = (float)((uint8_t)(v[1] >> 8)) / 255; + cv[2] = (float)((uint8_t)(v[2] >> 8)) / 255; + cv[3] = (float)((uint8_t)(v[3] >> 8)) / 255; + + swColor4fv(cv); +} + +void swColor4ui(uint32_t r, uint32_t g, uint32_t b, uint32_t a) +{ + float cv[4]; + cv[0] = (float)((uint8_t)(r >> 24)) / 255; + cv[1] = (float)((uint8_t)(g >> 24)) / 255; + cv[2] = (float)((uint8_t)(b >> 24)) / 255; + cv[3] = (float)((uint8_t)(a >> 24)) / 255; + + swColor4fv(cv); +} + +void swColor4uiv(const uint32_t* v) +{ + float cv[4]; + cv[0] = (float)((uint8_t)(v[0] >> 24)) / 255; + cv[1] = (float)((uint8_t)(v[1] >> 24)) / 255; + cv[2] = (float)((uint8_t)(v[2] >> 24)) / 255; + cv[3] = (float)((uint8_t)(v[3] >> 24)) / 255; + + swColor4fv(cv); +} + +void swColor4f(float r, float g, float b, float a) +{ + float cv[4]; + cv[0] = r; + cv[1] = g; + cv[2] = b; + cv[3] = a; + + swColor4fv(cv); +} + +void swColor4fv(const float* v) +{ + for (int i = 0; i < 4; i++) { + RLSW.vertexBuffer[RLSW.vertexCounter].color[i] = v[i]; + } +} + +void swTexCoord2f(float u, float v) +{ + float s = RLSW.matTexture[0]*u + RLSW.matTexture[4]*v + RLSW.matTexture[12]; + float t = RLSW.matTexture[1]*u + RLSW.matTexture[5]*v + RLSW.matTexture[13]; + + RLSW.vertexBuffer[RLSW.vertexCounter].texcoord[0] = s; + RLSW.vertexBuffer[RLSW.vertexCounter].texcoord[1] = t; +} + +void swTexCoordfv(const float* v) +{ + float s = RLSW.matTexture[0]*v[0] + RLSW.matTexture[4]*v[1] + RLSW.matTexture[12]; + float t = RLSW.matTexture[1]*v[0] + RLSW.matTexture[5]*v[1] + RLSW.matTexture[13]; + + RLSW.vertexBuffer[RLSW.vertexCounter].texcoord[0] = s; + RLSW.vertexBuffer[RLSW.vertexCounter].texcoord[1] = t; +} + +void swNormal3f(float x, float y, float z) +{ + RLSW.vertexBuffer[RLSW.vertexCounter].normal[0] = x; + RLSW.vertexBuffer[RLSW.vertexCounter].normal[1] = y; + RLSW.vertexBuffer[RLSW.vertexCounter].normal[2] = z; +} + +void swNormal3fv(const float* v) +{ + RLSW.vertexBuffer[RLSW.vertexCounter].normal[0] = v[0]; + RLSW.vertexBuffer[RLSW.vertexCounter].normal[1] = v[1]; + RLSW.vertexBuffer[RLSW.vertexCounter].normal[2] = v[2]; +} + +void swBindArray(SWarray type, void *buffer) +{ + switch (type) { + case SW_VERTEX_ARRAY: + RLSW.array.positions = buffer; + break; + case SW_TEXTURE_COORD_ARRAY: + RLSW.array.texcoords = buffer; + break; + case SW_NORMAL_ARRAY: + RLSW.array.normals = buffer; + break; + case SW_COLOR_ARRAY: + RLSW.array.colors = buffer; + break; + default: + break; + } +} + +void swDrawArrays(SWfill mode, int offset, int count) +{ + if (RLSW.array.positions == 0) { + RLSW.errCode = SW_INVALID_OPERATION; + return; + } + + swBegin(mode); + + for (int i = offset; i < count; i++) { + if (RLSW.array.texcoords) { + swTexCoordfv(RLSW.array.texcoords + 2 * i); + } + if (RLSW.array.normals) { + swNormal3fv(RLSW.array.normals + 3 * i); + } + if (RLSW.array.colors) { + swColor4ubv(RLSW.array.colors + 4 * i); + } + swVertex3fv(RLSW.array.positions + 3 * i); + } + + swEnd(); +} + +uint32_t swLoadTexture(const void *data, int width, int height, int format, int mipmapCount) +{ + if (RLSW.loadedTextureCount >= SW_MAX_TEXTURES) { + RLSW.errCode = SW_STACK_OVERFLOW; //< Out of memory, not really stack overflow + return 0; + } + + sw_texture_t texture = { 0 }; + texture.pixels = data; + texture.width = width; + texture.height = height; + texture.format = format; + texture.minFilter = SW_NEAREST; + texture.magFilter = SW_NEAREST; + texture.sWrap = SW_REPEAT; + texture.tWrap = SW_REPEAT; + texture.tx = 1.0f / width; + texture.ty = 1.0f / height; + (void)mipmapCount; + + uint32_t id = 0; + if (RLSW.freeTextureIdCount > 0) { + id = RLSW.freeTextureIds[--RLSW.freeTextureIdCount]; + } + else { + id = RLSW.loadedTextureCount++; + } + + RLSW.loadedTextures[id] = texture; + + return id; +} + +void swUnloadTexture(uint32_t id) +{ + if (!sw_is_texture_id_valid(id)) { + RLSW.errCode = SW_INVALID_VALUE; + return; + } + + RLSW.loadedTextures[id].pixels = 0; + RLSW.freeTextureIds[RLSW.freeTextureIdCount++] = id; +} + +void swTextureParameters(uint32_t id, int param, int value) +{ + if (!sw_is_texture_id_valid(id)) { + RLSW.errCode = SW_INVALID_VALUE; + return; + } + + sw_texture_t* texture = &RLSW.loadedTextures[id]; + + switch (param) { + + case SW_TEXTURE_MIN_FILTER: + if (!sw_is_texture_filter_valid(value)) { + RLSW.errCode = SW_INVALID_ENUM; + return; + } + texture->minFilter = value; + break; + + case SW_TEXTURE_MAG_FILTER: + if (!sw_is_texture_filter_valid(value)) { + RLSW.errCode = SW_INVALID_ENUM; + return; + } + texture->magFilter = value; + break; + + case SW_TEXTURE_WRAP_S: + if (!sw_is_texture_wrap_valid(value)) { + RLSW.errCode = SW_INVALID_ENUM; + return; + } + texture->sWrap = value; + break; + + case SW_TEXTURE_WRAP_T: + if (!sw_is_texture_wrap_valid(value)) { + RLSW.errCode = SW_INVALID_ENUM; + return; + } + texture->tWrap = value; + break; + + default: + RLSW.errCode = SW_INVALID_ENUM; + return; + + } +} + +void swBindTexture(uint32_t id) +{ + if (id >= SW_MAX_TEXTURES) { + RLSW.errCode = SW_INVALID_VALUE; + return; + } + + if (id > 0 && RLSW.loadedTextures[id].pixels == 0) { + RLSW.errCode = SW_INVALID_OPERATION; + return; + } + + RLSW.currentTexture = id; +} + +#endif // RLSW_IMPL