├── sGLHelper.h └── sGL.cpp /sGLHelper.h: -------------------------------------------------------------------------------- 1 | struct color_t 2 | { 3 | float r; 4 | float g; 5 | float b; 6 | float a; 7 | 8 | color_t(float rr, float gg, float bb) 9 | { 10 | r = rr; 11 | g = gg; 12 | b = bb; 13 | a = 1.0f; 14 | } 15 | 16 | color_t() 17 | { 18 | color_t(0.0f, 0.0f, 0.0f); 19 | } 20 | }; 21 | 22 | struct in_out_vertex_t 23 | { 24 | float x; 25 | float y; 26 | float z; 27 | float w; 28 | 29 | float u; 30 | float v; 31 | 32 | color_t color; 33 | 34 | in_out_vertex_t(float xx, float yy, float zz, float uu, float vv, float rr, float gg, float bb) 35 | { 36 | x = xx; 37 | y = yy; 38 | z = zz; 39 | w = 1.0f; 40 | u = uu; 41 | v = vv; 42 | color.r = rr; 43 | color.g = gg; 44 | color.b = bb; 45 | } 46 | 47 | in_out_vertex_t() 48 | { 49 | in_out_vertex_t(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f); 50 | } 51 | }; 52 | 53 | 54 | struct in_out_pixel_t 55 | { 56 | int x; 57 | int y; 58 | 59 | float z; 60 | 61 | float u; 62 | float v; 63 | float w; 64 | 65 | color_t color; 66 | }; 67 | 68 | 69 | /* --------------------------- You can safely ignore everything below this line --------------------------*/ 70 | 71 | #include 72 | 73 | 74 | // Just some forward declarations 75 | void sGLInit(int windowWidth, int windowHeight); 76 | void sGLSetPixel(int x, int y, Uint32 color); 77 | void sGLDrawElements(int count); 78 | void sGLGenBuffer(int * outBufferIndex); 79 | void sGLBindBuffer(int bufferID); 80 | void sGLBufferData(int size, in_out_vertex_t * vertexData); 81 | void sGLClearDepth(float zz); 82 | void sGLEnableDepthTest(); 83 | void sGLEnableAlphaTest(); 84 | void sGLDisableAlphaTest(); 85 | void sGLUseVertexShader(in_out_vertex_t(*inVS)(in_out_vertex_t)); 86 | void sGLUsePixelShader(in_out_pixel_t(*inPS)(in_out_pixel_t)); 87 | void sGLUniform1f(float * location, float value); 88 | void sGLSwapBuffers(); 89 | void sGLClear(); 90 | void sGLExit(); 91 | bool isRunning(); 92 | 93 | 94 | struct point2_t 95 | { 96 | int x; 97 | int y; 98 | 99 | float dotProduct(point2_t otherVector) 100 | { 101 | return (float)(x * otherVector.x + y * otherVector.y); 102 | } 103 | 104 | point2_t(int xx, int yy) 105 | { 106 | x = xx; 107 | y = yy; 108 | } 109 | }; 110 | 111 | 112 | // More Globals, WOHOOO! 113 | 114 | SDL_Window * g_SDLWindow; 115 | SDL_Renderer * g_SDLRenderer; 116 | SDL_Texture * g_SDLTexture; 117 | Uint32 * g_SDLBackBuffer; 118 | int g_SDLWidth; 119 | int g_SDLHeight; 120 | 121 | void SDLStart(int windowWidth, int windowHeight) 122 | { 123 | SDL_Init(SDL_INIT_VIDEO); 124 | 125 | g_SDLWidth = windowWidth; 126 | g_SDLHeight = windowHeight; 127 | 128 | g_SDLWindow = SDL_CreateWindow("stupidGL", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, windowWidth, windowHeight, 0); 129 | 130 | g_SDLRenderer = SDL_CreateRenderer(g_SDLWindow, -1, 0); 131 | g_SDLTexture = SDL_CreateTexture(g_SDLRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, windowWidth, windowHeight); 132 | 133 | g_SDLBackBuffer = new Uint32[windowWidth * windowHeight]; 134 | } 135 | 136 | void SDLSwapBuffers(color_t * backbuffer) 137 | { 138 | for (int i = 0; i < g_SDLHeight; ++i) 139 | { 140 | for (int j = 0; j < g_SDLWidth; ++j) 141 | { 142 | // Convert color_t to Uint32 143 | Uint32 iColor = ((uint8_t)(255.0f * backbuffer[i * g_SDLWidth + j].b) << 16) | 144 | ((uint8_t)(255.0f * backbuffer[i * g_SDLWidth + j].g) << 8) | 145 | ((uint8_t)(255.0f * backbuffer[i * g_SDLWidth + j].r)) & 0xffffff; 146 | 147 | g_SDLBackBuffer[i * g_SDLWidth + j] = iColor; 148 | } 149 | } 150 | 151 | SDL_UpdateTexture(g_SDLTexture, NULL, g_SDLBackBuffer, g_SDLWidth * sizeof(Uint32)); 152 | SDL_RenderClear(g_SDLRenderer); 153 | SDL_RenderCopy(g_SDLRenderer, g_SDLTexture, NULL, NULL); 154 | SDL_RenderPresent(g_SDLRenderer); 155 | } 156 | 157 | void SDLEnd() 158 | { 159 | delete[] g_SDLBackBuffer; 160 | 161 | SDL_DestroyTexture(g_SDLTexture); 162 | SDL_DestroyRenderer(g_SDLRenderer); 163 | SDL_DestroyWindow(g_SDLWindow); 164 | SDL_Quit(); 165 | } 166 | 167 | bool isRunning() 168 | { 169 | SDL_Event sEvent; 170 | 171 | SDL_PollEvent(&sEvent); 172 | 173 | switch (sEvent.type) 174 | { 175 | case SDL_QUIT: 176 | 177 | return false; 178 | 179 | break; 180 | } 181 | 182 | return true; 183 | } 184 | 185 | void calculateBarycentric(int pointX, int pointY, in_out_pixel_t * verts, float * outU, float * outV, float * outW) 186 | { 187 | // Taken from http://gamedev.stackexchange.com/questions/23743/whats-the-most-efficient-way-to-find-barycentric-coordinates 188 | point2_t v0 = point2_t(verts[1].x - verts[0].x, verts[1].y - verts[0].y); 189 | point2_t v1 = point2_t(verts[2].x - verts[0].x, verts[2].y - verts[0].y); 190 | point2_t v2 = point2_t(pointX - verts[0].x, pointY - verts[0].y); 191 | float d00 = v0.dotProduct(v0); 192 | float d01 = v0.dotProduct(v1); 193 | float d11 = v1.dotProduct(v1); 194 | float d20 = v2.dotProduct(v0); 195 | float d21 = v2.dotProduct(v1); 196 | float invDenom = 1.0f / (d00 * d11 - d01 * d01); 197 | (*outV) = (d11 * d20 - d01 * d21) * invDenom; 198 | (*outW) = (d00 * d21 - d01 * d20) * invDenom; 199 | (*outU) = 1.0f - (*outV) - (*outW); 200 | } 201 | 202 | void sortAscendingY(int * xx, int * yy) 203 | { 204 | int tX, tY; 205 | 206 | if (yy[1] > yy[0]) 207 | { 208 | if (yy[1] > yy[2]) 209 | { 210 | tX = xx[0]; 211 | tY = yy[0]; 212 | 213 | xx[0] = xx[1]; 214 | yy[0] = yy[1]; 215 | 216 | if (tY > yy[2]) 217 | { 218 | xx[1] = tX; 219 | yy[1] = tY; 220 | } 221 | else 222 | { 223 | xx[1] = xx[2]; 224 | yy[1] = yy[2]; 225 | 226 | xx[2] = tX; 227 | yy[2] = tY; 228 | } 229 | } 230 | else 231 | { 232 | tX = xx[0]; 233 | tY = yy[0]; 234 | 235 | xx[0] = xx[2]; 236 | yy[0] = yy[2]; 237 | 238 | xx[2] = tX; 239 | yy[2] = tY; 240 | } 241 | } 242 | else 243 | { 244 | if (yy[0] > yy[2]) 245 | { 246 | if (yy[1] < yy[2]) 247 | { 248 | tX = xx[1]; 249 | tY = yy[1]; 250 | 251 | xx[1] = xx[2]; 252 | yy[1] = yy[2]; 253 | 254 | xx[2] = tX; 255 | yy[2] = tY; 256 | } 257 | } 258 | else 259 | { 260 | tX = xx[0]; 261 | tY = yy[0]; 262 | 263 | xx[0] = xx[2]; 264 | yy[0] = yy[2]; 265 | 266 | xx[2] = xx[1]; 267 | yy[2] = yy[1]; 268 | 269 | xx[1] = tX; 270 | yy[1] = tY; 271 | } 272 | } 273 | 274 | // Accidently sorted it the wrong way around... 275 | // Too lazy to fix.. So here is the dirty fix: 276 | 277 | tX = xx[0]; 278 | tY = yy[0]; 279 | 280 | xx[0] = xx[2]; 281 | yy[0] = yy[2]; 282 | 283 | xx[2] = tX; 284 | yy[2] = tY; 285 | } -------------------------------------------------------------------------------- /sGL.cpp: -------------------------------------------------------------------------------- 1 | 2 | // You will require the SDL2 Library to Compile 3 | // SDL2 is _ONLY_ used to draw the Backbuffer on the Screen. 4 | // Everything "3D" is done in our Software Rasterizer "sGL" below 5 | 6 | // You can find the Article to this Source Code on Gamasutra: 7 | // http://gamasutra.com/blogs/MichaelKissner/20160112/263097/Writing_a_Game_Engine_from_Scratch__Part_4_Graphics_Library.php 8 | 9 | // NOTE: This is not meant to run at any usable Framerate! 10 | 11 | // - Michael Kissner (@Spellwrath) 12 | 13 | #include 14 | 15 | #include "sGLHelper.h" 16 | 17 | /* --------------------------------------- Our Vertex and Pixel Shader -----------------------------------*/ 18 | 19 | #define WHITE color_t(1.0f, 1.0f, 1.0f) 20 | #define BLACK color_t(0.0f, 0.0f, 0.0f) 21 | #define TEXTURE_SIZE 6 22 | 23 | color_t g_TextureSampler[TEXTURE_SIZE][TEXTURE_SIZE] = { 24 | { WHITE, BLACK, WHITE, WHITE, BLACK, WHITE }, 25 | { WHITE, BLACK, BLACK, BLACK, BLACK, WHITE }, 26 | { WHITE, BLACK, WHITE, WHITE, BLACK, WHITE }, 27 | { WHITE, BLACK, WHITE, WHITE, BLACK, WHITE }, 28 | { WHITE, BLACK, WHITE, WHITE, BLACK, WHITE }, 29 | { BLACK, BLACK, WHITE, WHITE, BLACK, BLACK }, 30 | }; 31 | 32 | // "uniforms" 33 | float fRotation = 0.0f; 34 | 35 | 36 | in_out_vertex_t vertexShader(in_out_vertex_t inVertex) 37 | { 38 | in_out_vertex_t tempV; 39 | in_out_vertex_t out = inVertex; 40 | 41 | // WORLD - Transformation 42 | 43 | // Just a simple rotation of the Vertices 44 | tempV.x = inVertex.z * sin(fRotation) + inVertex.x * cos(fRotation); 45 | tempV.y = inVertex.y; 46 | tempV.z = inVertex.z * cos(fRotation) - inVertex.x * sin(fRotation); 47 | 48 | 49 | 50 | // VIEW - Transformation 51 | 52 | // Move the "Camera" back a bit so that the Triangles are in full view 53 | tempV.z = tempV.z + 0.5f; 54 | 55 | 56 | 57 | // PROJECTION - Transformation 58 | 59 | // Quick and dirty "projection" to get a quasi-perspective look. 60 | float ftan = tan(1.5707f / 2.0f) / (tempV.z); 61 | out.x = tempV.x * ftan; 62 | out.y = tempV.y * ftan; 63 | out.z = tempV.z; 64 | out.w = 1 / out.z; 65 | 66 | // And blend by depth - because we can. 67 | out.color.a = 1.0f - out.z; 68 | 69 | return out; 70 | } 71 | 72 | 73 | in_out_pixel_t pixelShader(in_out_pixel_t inPixel) 74 | { 75 | in_out_pixel_t out = inPixel; 76 | 77 | // We Point "sample" the Texture 78 | int texU = (int)(inPixel.u * TEXTURE_SIZE); 79 | int texV = (int)(inPixel.v * TEXTURE_SIZE); 80 | out.color = g_TextureSampler[texU][texV]; 81 | 82 | // ... and overlay the Vertex Color 83 | out.color.r = out.color.r * inPixel.color.r; 84 | out.color.g = out.color.g * inPixel.color.g; 85 | out.color.b = out.color.b * inPixel.color.b; 86 | 87 | out.color.a = inPixel.color.a; 88 | 89 | return out; 90 | } 91 | 92 | 93 | /* ----------------------------------------------- The Main Loop------------------------------------------*/ 94 | 95 | 96 | int main(int argc, char ** argv) 97 | { 98 | sGLInit(640, 480); 99 | 100 | in_out_vertex_t vertices[6] = 101 | { 102 | in_out_vertex_t(-0.3f, -0.3f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f), 103 | in_out_vertex_t(0.3f, -0.3f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f), 104 | in_out_vertex_t(0.3f, 0.3f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f), 105 | 106 | in_out_vertex_t(-0.3f, 0.3f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f), 107 | in_out_vertex_t(-0.3f, -0.3f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f), 108 | in_out_vertex_t(0.3f, 0.3f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f), 109 | }; 110 | 111 | int bufferID; 112 | 113 | sGLGenBuffer(&bufferID); 114 | sGLBindBuffer(bufferID); 115 | sGLBufferData(6, vertices); 116 | 117 | sGLClearDepth(1.0f); 118 | sGLEnableDepthTest(); 119 | 120 | float rotateScene = 0.0f; 121 | 122 | while (isRunning()) 123 | { 124 | sGLClear(); 125 | 126 | 127 | sGLBindBuffer(bufferID); 128 | 129 | sGLUseVertexShader(vertexShader); 130 | sGLUsePixelShader(pixelShader); 131 | 132 | 133 | 134 | sGLDisableAlphaTest(); 135 | 136 | sGLUniform1f(&fRotation, 0.0f); 137 | 138 | sGLDrawElements(2); 139 | 140 | 141 | 142 | sGLEnableAlphaTest(); 143 | 144 | rotateScene = rotateScene + 0.01f; 145 | sGLUniform1f(&fRotation, rotateScene); 146 | 147 | sGLDrawElements(2); 148 | 149 | 150 | 151 | sGLSwapBuffers(); 152 | } 153 | 154 | sGLExit(); 155 | 156 | return 0; 157 | } 158 | 159 | 160 | /* --------------------------------------------- Our own stupidGL ----------------------------------------*/ 161 | 162 | // Yeah. Globals! Because we can. 163 | // For this, assume that all these things are present on the Graphics Card instead 164 | // For ease of Programming, I've made them all globals 165 | 166 | color_t * g_sGLBackBuffer; 167 | float * g_sGLDepthBuffer; 168 | int g_sGLBackbufferWidth; 169 | int g_sGLBackbufferHeight; 170 | 171 | in_out_vertex_t(*g_sGLVertexShader)(in_out_vertex_t); 172 | in_out_pixel_t(*g_sGLPixelShader)(in_out_pixel_t); 173 | 174 | // Let's fix the maximum amount of Buffers at some random number: 32 175 | in_out_vertex_t *g_sGLVertexBuffer[32]; 176 | int g_sGLNumBuffers = 0; 177 | int g_sGLBoundBuffer = 0; 178 | 179 | bool g_sGLAlphaTest = false; 180 | 181 | bool g_sGLDepthTest = false; 182 | float g_sGLDepthClear = 1.0f; 183 | 184 | 185 | 186 | void sGLInit(int windowWidth, int windowHeight) 187 | { 188 | // Create the SDL things... not of interest 189 | SDLStart(windowWidth, windowHeight); 190 | 191 | // Let's create our Backbuffer 192 | g_sGLBackBuffer = new color_t[windowWidth * windowHeight]; 193 | g_sGLDepthBuffer = new float[windowWidth * windowHeight]; 194 | 195 | g_sGLBackbufferWidth = windowWidth; 196 | g_sGLBackbufferHeight = windowHeight; 197 | } 198 | 199 | void sGLGenBuffer(int * outBufferIndex) 200 | { 201 | if (g_sGLNumBuffers < 32) 202 | { 203 | (*outBufferIndex) = g_sGLNumBuffers; 204 | g_sGLNumBuffers++; 205 | } 206 | else 207 | { 208 | (*outBufferIndex) = -1; 209 | } 210 | } 211 | 212 | void sGLBindBuffer(int bufferID) 213 | { 214 | g_sGLBoundBuffer = bufferID; 215 | } 216 | 217 | void sGLBufferData(int size, in_out_vertex_t * vertexData) 218 | { 219 | g_sGLVertexBuffer[g_sGLBoundBuffer] = new in_out_vertex_t[size]; 220 | 221 | for (int i = 0; i < size; ++i) 222 | { 223 | g_sGLVertexBuffer[g_sGLBoundBuffer][i] = vertexData[i]; 224 | } 225 | } 226 | 227 | void sGLClearDepth(float zz) 228 | { 229 | g_sGLDepthClear = zz; 230 | } 231 | 232 | void sGLEnableDepthTest() 233 | { 234 | g_sGLDepthTest = true; 235 | } 236 | 237 | void sGLEnableAlphaTest() 238 | { 239 | g_sGLAlphaTest = true; 240 | } 241 | 242 | void sGLDisableAlphaTest() 243 | { 244 | g_sGLAlphaTest = false; 245 | } 246 | 247 | 248 | void sGLUseVertexShader(in_out_vertex_t(*inVS)(in_out_vertex_t)) 249 | { 250 | g_sGLVertexShader = inVS; 251 | } 252 | 253 | void sGLUsePixelShader(in_out_pixel_t(*inPS)(in_out_pixel_t)) 254 | { 255 | g_sGLPixelShader = inPS; 256 | } 257 | 258 | void sGLUniform1f(float * location, float value) 259 | { 260 | // Fake setting of uniforms 261 | (*location) = value; 262 | } 263 | 264 | void sGLSetPixel(int x, int y, float z, color_t color) 265 | { 266 | // We check if we are actually setting the Pixel inside the Backbuffer 267 | if (x >= 0 && x < g_sGLBackbufferWidth) 268 | { 269 | if (y >= 0 && y < g_sGLBackbufferHeight) 270 | { 271 | // We check if we violate the Depth Buffer 272 | if (z > 0 && z <= g_sGLDepthBuffer[y * g_sGLBackbufferWidth + x] || g_sGLDepthTest == false) 273 | { 274 | // And finally Alpha blending 275 | 276 | if (g_sGLAlphaTest == true) 277 | { 278 | color_t cCol = g_sGLBackBuffer[y * g_sGLBackbufferWidth + x]; 279 | 280 | cCol.r = cCol.r * (1 - color.a) + color.r * color.a; 281 | cCol.g = cCol.g * (1 - color.a) + color.g * color.a; 282 | cCol.b = cCol.b * (1 - color.a) + color.b * color.a; 283 | 284 | g_sGLBackBuffer[y * g_sGLBackbufferWidth + x] = cCol; 285 | } 286 | else 287 | { 288 | g_sGLBackBuffer[y * g_sGLBackbufferWidth + x] = color; 289 | } 290 | 291 | g_sGLDepthBuffer[y * g_sGLBackbufferWidth + x] = z; 292 | } 293 | } 294 | } 295 | } 296 | 297 | void sGLScanLine(int x0, int x1, int y, in_out_pixel_t * tris) 298 | { 299 | // We determine which Pixels need to be drawn by Scanning each line in the 300 | // Triangle. 301 | 302 | int sx = x0 < x1 ? x0 : x1; 303 | int ex = x0 < x1 ? x1 : x0; 304 | 305 | // sx = left edge --- ex = right edge of the current Scanline 306 | for (int x = sx; x <= ex; ++x) 307 | { 308 | in_out_pixel_t currentPixel; 309 | float lambda1, lambda2, lambda3; 310 | 311 | // For each Pixel we draw, we first Calculate the Barycentric Coordinates 312 | // https://en.wikipedia.org/wiki/Barycentric_coordinate_system 313 | // Which are helpful for interpolation inside the Triangle 314 | 315 | calculateBarycentric(x, y, tris, &lambda1, &lambda2, &lambda3); 316 | 317 | currentPixel.x = x; 318 | currentPixel.y = y; 319 | 320 | // We can then interpolate the Color, uv and z coordinate across the Triangle 321 | currentPixel.color.r = tris[0].color.r * lambda1 + tris[1].color.r * lambda2 + tris[2].color.r * lambda3; 322 | currentPixel.color.g = tris[0].color.g * lambda1 + tris[1].color.g * lambda2 + tris[2].color.g * lambda3; 323 | currentPixel.color.b = tris[0].color.b * lambda1 + tris[1].color.b * lambda2 + tris[2].color.b * lambda3; 324 | currentPixel.color.a = tris[0].color.a * lambda1 + tris[1].color.a * lambda2 + tris[2].color.a * lambda3; 325 | 326 | currentPixel.z = tris[0].z * lambda1 + tris[1].z * lambda2 + tris[2].z * lambda3; 327 | currentPixel.w = tris[0].w * lambda1 + tris[1].w * lambda2 + tris[2].w * lambda3; 328 | 329 | // Moving back to Screen-Affine Space 330 | currentPixel.u = (tris[0].u * lambda1 + tris[1].u * lambda2 + tris[2].u * lambda3) / currentPixel.w; 331 | currentPixel.v = (tris[0].v * lambda1 + tris[1].v * lambda2 + tris[2].v * lambda3) / currentPixel.w; 332 | 333 | 334 | // You might notice that our Texture is "distorted" once the Triangle rotates. This is due 335 | // the fact that we are working with an affine Transformation for our Texture coordinates 336 | // (the typical Playstation 1 look) yet do a perspective Transformation on the triangle itself. 337 | // I've left it affine for simplicity and the fact that we don't do a real Projection (which is needed). 338 | // You can read more about it here: 339 | // https://en.wikipedia.org/wiki/Texture_mapping 340 | 341 | // We feed this Pixel information into the Pixel Shader 342 | currentPixel = g_sGLPixelShader(currentPixel); 343 | 344 | // And finally draw the last Pixel onto the Back Buffer 345 | // Continue on at - sGLSetPixel - 346 | sGLSetPixel(currentPixel.x, currentPixel.y, currentPixel.z, currentPixel.color); 347 | } 348 | } 349 | 350 | void sGLFillTopFlatTriangle(int * xx, int * yy, in_out_pixel_t * tris) 351 | { 352 | // Taken from http://www.sunshine2k.de/coding/java/TriangleRasterization/TriangleRasterization.html 353 | float invslope1 = (float)(xx[2] - xx[0]) / (float)(yy[2] - yy[0]); 354 | float invslope2 = (float)(xx[2] - xx[1]) / (float)(yy[2] - yy[1]); 355 | 356 | float curx1 = (float) xx[2]; 357 | float curx2 = (float) xx[2]; 358 | 359 | for (int scanlineY = yy[2]; scanlineY > yy[0]; scanlineY--) 360 | { 361 | curx1 -= invslope1; 362 | curx2 -= invslope2; 363 | sGLScanLine((int)curx1, (int)curx2, scanlineY, tris); 364 | } 365 | } 366 | 367 | 368 | void sGLFillBottomFlatTriangle(int * xx, int * yy, in_out_pixel_t * tris) 369 | { 370 | // Taken from http://www.sunshine2k.de/coding/java/TriangleRasterization/TriangleRasterization.html 371 | float invslope1 = (float)(xx[1] - xx[0]) / (float)(yy[1] - yy[0]); 372 | float invslope2 = (float)(xx[2] - xx[0]) / (float)(yy[2] - yy[0]); 373 | 374 | float curx1 = (float) xx[0]; 375 | float curx2 = (float) xx[0]; 376 | 377 | for (int scanlineY = yy[0]; scanlineY <= yy[1]; scanlineY++) 378 | { 379 | sGLScanLine((int)curx1, (int)curx2, scanlineY, tris); 380 | curx1 += invslope1; 381 | curx2 += invslope2; 382 | } 383 | } 384 | 385 | 386 | void sGLDrawElements(int count) 387 | { 388 | // We loop each Triangle we need to draw 389 | for (int i = 0; i < count; ++i) 390 | { 391 | in_out_pixel_t verts[3]; 392 | 393 | // First, we Apply the Vertex Shader to each of the 3 Vertices 394 | for (int j = 0; j < 3; ++j) 395 | { 396 | in_out_vertex_t currentVertex; 397 | 398 | currentVertex = g_sGLVertexShader(g_sGLVertexBuffer[g_sGLBoundBuffer][i * 3 + j]); 399 | 400 | // Transform the float coordinates to int's for the screen 401 | verts[j].x = (int)(((currentVertex.x + 1.0f) / 2.0f) * (float)g_sGLBackbufferWidth); 402 | verts[j].y = (int)((1.0f - ((currentVertex.y + 1.0f) / 2.0f)) * (float)g_sGLBackbufferHeight); 403 | 404 | // For perspective correct Texture mapping, we need: (i.e. moving to Object-Affine Space) 405 | verts[j].u = currentVertex.u * currentVertex.w; 406 | verts[j].v = currentVertex.v * currentVertex.w; 407 | 408 | // We can copy the rest directly 409 | verts[j].z = currentVertex.z; 410 | verts[j].w = currentVertex.w; 411 | verts[j].color = currentVertex.color; 412 | 413 | } 414 | 415 | // And then Scanline every Pixel that needs to be drawn 416 | // See - sGLScanLine - for Continuation 417 | 418 | // Scanline Algorithm taken from http://www.sunshine2k.de/coding/java/TriangleRasterization/TriangleRasterization.html 419 | // You can check there for a detailed description of what happens below 420 | 421 | int xx[3]; 422 | int yy[3]; 423 | 424 | xx[0] = verts[0].x; 425 | yy[0] = verts[0].y; 426 | xx[1] = verts[1].x; 427 | yy[1] = verts[1].y; 428 | xx[2] = verts[2].x; 429 | yy[2] = verts[2].y; 430 | 431 | sortAscendingY(xx, yy); 432 | 433 | if (yy[1] == yy[2]) 434 | { 435 | sGLFillBottomFlatTriangle(xx, yy, verts); 436 | } 437 | else if (yy[0] == yy[1]) 438 | { 439 | sGLFillTopFlatTriangle(xx, yy, verts); 440 | } 441 | else 442 | { 443 | int xxTemp[3]; 444 | int yyTemp[3]; 445 | 446 | xxTemp[0] = xx[0]; 447 | yyTemp[0] = yy[0]; 448 | 449 | xxTemp[1] = xx[1]; 450 | yyTemp[1] = yy[1]; 451 | 452 | xxTemp[2] = (int)(xx[0] + ((float)(yy[1] - yy[0]) / (float)(yy[2] - yy[0])) * (xx[2] - xx[0])); 453 | yyTemp[2] = yy[1]; 454 | 455 | sGLFillBottomFlatTriangle(xxTemp, yyTemp, verts); 456 | 457 | xxTemp[0] = xx[1]; 458 | yyTemp[0] = yy[1]; 459 | 460 | xxTemp[1] = xxTemp[2]; 461 | yyTemp[1] = yyTemp[2]; 462 | 463 | xxTemp[2] = xx[2]; 464 | yyTemp[2] = yy[2]; 465 | 466 | sGLFillTopFlatTriangle(xxTemp, yyTemp, verts); 467 | } 468 | } 469 | } 470 | 471 | 472 | void sGLSwapBuffers() 473 | { 474 | // Swap our Backbuffer with the Frontbuffer 475 | SDLSwapBuffers(g_sGLBackBuffer); 476 | } 477 | 478 | 479 | void sGLClear() 480 | { 481 | for (int i = 0; i < g_sGLBackbufferHeight; ++i) 482 | { 483 | for (int j = 0; j < g_sGLBackbufferWidth; ++j) 484 | { 485 | // Let's clear the Backbuffer to a white Color 486 | g_sGLBackBuffer[i * g_sGLBackbufferWidth + j] = color_t(1.0f, 1.0f, 1.0f); 487 | 488 | // And the Depth Buffer to the Set value 489 | g_sGLDepthBuffer[i * g_sGLBackbufferWidth + j] = g_sGLDepthClear; 490 | } 491 | } 492 | } 493 | 494 | 495 | void sGLExit() 496 | { 497 | delete[] g_sGLBackBuffer; 498 | delete[] g_sGLDepthBuffer; 499 | 500 | for (int i = 0; i < g_sGLNumBuffers; ++i) 501 | { 502 | delete[] g_sGLVertexBuffer[i]; 503 | } 504 | 505 | SDLEnd(); 506 | } --------------------------------------------------------------------------------