Skip to content

Instantly share code, notes, and snippets.

@Madsy
Created January 12, 2017 00:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Madsy/004a764b1453c8fcc68cf086b7bb4099 to your computer and use it in GitHub Desktop.
Save Madsy/004a764b1453c8fcc68cf086b7bb4099 to your computer and use it in GitHub Desktop.
#include <cmath>
#include <vector>
#include <SDL/SDL.h>
#include <IL/il.h>
#include <math.h>
const float PI = 3.14159265359f;
typedef struct {
unsigned int id;
int width;
int height;
std::vector<unsigned char> pixels;
} Texture2D;
const int FRAMEBUFFER_WIDTH = 1280;
const int FRAMEBUFFER_HEIGHT = 720;
unsigned char* framebuffer = nullptr;
Texture2D* texture_ptr = nullptr;
typedef struct {
float x;
float y;
} Point2f;
typedef struct {
int x;
int y;
} Point2i;
static void swap_int(int* a, int* b){
int tmp = *a;
*a = *b;
*b = tmp;
}
static void swap_float(float* a, float* b){
float tmp = *a;
*a = *b;
*b = tmp;
}
static void swap_point2f(Point2f* a, Point2f* b){
Point2f tmp = *a;
*a = *b;
*b = tmp;
}
Point2f rotate(Point2f* v, float angle){
float M[4];
Point2f vP;
M[0] = std::cos(angle/180.0f*PI);
M[1] = std::sin(angle/180.0f*PI);
M[2] = -std::sin(angle/180.0f*PI);
M[3] = std::cos(angle/180.0f*PI);
vP.x = M[0]*v->x + M[1]*v->y;
vP.y = M[2]*v->x + M[3]*v->y;
return vP;
}
static void blitPixel(int x, int y,
unsigned char r,
unsigned char g,
unsigned char b,
unsigned char a){
if(!a) return;
framebuffer[(x + y * FRAMEBUFFER_WIDTH)*4 + 0] = r; //red
framebuffer[(x + y * FRAMEBUFFER_WIDTH)*4 + 1] = g; //green
framebuffer[(x + y * FRAMEBUFFER_WIDTH)*4 + 2] = b; //blue
framebuffer[(x + y * FRAMEBUFFER_WIDTH)*4 + 3] = 255; //alpha or unused
}
void drawtriangle(Point2f v1, Point2f v2, Point2f v3, Point2f t1, Point2f t2, Point2f t3) {
Point2f delta1, delta2, delta3;
Point2f deltaT1, deltaT2, deltaT3;
float slope1, slope2, slope3;
float slopeU1, slopeU2, slopeU3;
float slopeV1, slopeV2, slopeV3;
float xAccum1, xAccum2;
float uAccum1, uAccum2;
float vAccum1, vAccum2;
int yStart, yEnd;
int xStart, xEnd;
float uStart, uEnd;
float vStart, vEnd;
//sort vertices by increasing y
if(v1.y > v2.y) {
swap_point2f(&v1, &v2);
swap_point2f(&t1, &t2);
}
if(v2.y > v3.y) {
swap_point2f(&v2, &v3);
swap_point2f(&t2, &t3);
}
if(v1.y > v2.y) {
swap_point2f(&v1, &v2);
swap_point2f(&t1, &t2);
}
delta1.x = v3.x - v1.x;
delta1.y = v3.y - v1.y;
delta2.x = v2.x - v1.x;
delta2.y = v2.y - v1.y;
delta3.x = v3.x - v2.x;
delta3.y = v3.y - v2.y;
deltaT1.x = t3.x - t1.x;
deltaT1.y = t3.y - t1.y;
deltaT2.x = t2.x - t1.x;
deltaT2.y = t2.y - t1.y;
deltaT3.x = t3.x - t2.x;
deltaT3.y = t3.y - t2.y;
slope1 = slope2 = slope3 = 0;
slopeU1 = slopeU2 = slopeU3 = 0;
slopeV1 = slopeV2 = slopeV3 = 0;
//Rise over run for each edge
//We know that all the deltas are 0 or greater, because we sorted
//the points before
if(delta1.y > 0) {
slope1 = delta1.x / delta1.y;
slopeU1 = deltaT1.x / delta1.y;
slopeV1 = deltaT1.y / delta1.y;
}
if(delta2.y > 0) {
slope2 = delta2.x / delta2.y;
slopeU2 = deltaT2.x / delta2.y;
slopeV2 = deltaT2.y / delta2.y;
}
if(delta3.y > 0) {
slope3 = delta3.x / delta3.y;
slopeU3 = deltaT3.x / delta3.y;
slopeV3 = deltaT3.y / delta3.y;
}
//Render top part of triangle
yStart = (int)v1.y;
yEnd = (int)v2.y;
xAccum1 = v1.x;
xAccum2 = v1.x;
uAccum1 = t1.x;
uAccum2 = t1.x;
vAccum1 = t1.y;
vAccum2 = t1.y;
for(; yStart < yEnd; yStart++){
float xDelta, uDelta, vDelta, uSlope, vSlope;
xStart = (int)xAccum1;
xEnd = (int)xAccum2;
uStart = uAccum1;
uEnd = uAccum2;
vStart = vAccum1;
vEnd = vAccum2;
//swap if middle point is on the 'wrong' side
if(xStart > xEnd){
swap_int(&xStart, &xEnd);
swap_float(&uStart, &uEnd);
swap_float(&vStart, &vEnd);
}
//the length of the horisontal scanline
//this is the positive length of the "ideal" triangle, not the number of pixels!
xDelta = xEnd - xStart;
//the horisontal delta for U and V
uDelta = uEnd - uStart;
vDelta = vEnd - vStart;
uSlope = vSlope = 0.0f;
//the horisontal slopes for U and V
if(xDelta){
uSlope = uDelta / xDelta;
vSlope = vDelta / xDelta;
}
for(; xStart < xEnd; xStart++){
bool xTest = (xStart > 0) && (xStart < FRAMEBUFFER_WIDTH);
bool yTest = (yStart > 0) && (yStart < FRAMEBUFFER_HEIGHT);
if(xTest && yTest){
int U = uStart * texture_ptr->width;
int V = vStart * texture_ptr->height;
int UVindex = U + V * texture_ptr->width;
unsigned char r = texture_ptr->pixels[UVindex*4 + 0];
unsigned char g = texture_ptr->pixels[UVindex*4 + 1];
unsigned char b = texture_ptr->pixels[UVindex*4 + 2];
unsigned char a = texture_ptr->pixels[UVindex*4 + 3];
blitPixel(xStart, yStart, r,g,b,a);
}
uStart += uSlope;
vStart += vSlope;
}
xAccum1 += slope1;
xAccum2 += slope2;
uAccum1 += slopeU1;
uAccum2 += slopeU2;
vAccum1 += slopeV1;
vAccum2 += slopeV2;
}
//render bottom part of triangle
yStart = (int)v2.y;
yEnd = (int)v3.y;
xAccum1 = v2.x;
xAccum2 = v1.x + slope1*delta2.y;
uAccum1 = t2.x;
uAccum2 = t1.x + slopeU1*delta2.y;
vAccum1 = t2.y;
vAccum2 = t1.y + slopeV1*delta2.y;
for(; yStart < yEnd; yStart++){
float xDelta, uDelta, vDelta, uSlope, vSlope;
xStart = (int)xAccum1;
xEnd = (int)xAccum2;
uStart = uAccum1;
uEnd = uAccum2;
vStart = vAccum1;
vEnd = vAccum2;
//swap if middle point is on the 'wrong' side
if(xStart > xEnd){
swap_int(&xStart, &xEnd);
swap_float(&uStart, &uEnd);
swap_float(&vStart, &vEnd);
}
//the length of the horisontal scanline
//this is the positive length of the "ideal" triangle, not the number of pixels!
xDelta = xEnd - xStart;
//the horisontal delta for U and V
uDelta = uEnd - uStart;
vDelta = vEnd - vStart;
uSlope = vSlope = 0.0f;
//the horisontal slopes for U and V
if(xDelta){
uSlope = uDelta / xDelta;
vSlope = vDelta / xDelta;
}
for(; xStart < xEnd; xStart++){
bool xTest = (xStart > 0) && (xStart < FRAMEBUFFER_WIDTH);
bool yTest = (yStart > 0) && (yStart < FRAMEBUFFER_HEIGHT);
if(xTest && yTest){
int U = uStart * texture_ptr->width;
int V = vStart * texture_ptr->height;
int UVindex = U + V * texture_ptr->width;
unsigned char r = texture_ptr->pixels[UVindex*4 + 0];
unsigned char g = texture_ptr->pixels[UVindex*4 + 1];
unsigned char b = texture_ptr->pixels[UVindex*4 + 2];
unsigned char a = texture_ptr->pixels[UVindex*4 + 3];
blitPixel(xStart, yStart, r,g,b,a);
}
uStart += uSlope;
vStart += vSlope;
}
xAccum1 += slope3;
xAccum2 += slope1;
uAccum1 += slopeU3;
uAccum2 += slopeU1;
vAccum1 += slopeV3;
vAccum2 += slopeV1;
}
};
int main(int argc, char* argv[]){
SDL_Event ev;
SDL_Surface* s;
Texture2D textures[5];
const char* texture_names[5] = {
"F.png",
"l.png",
"e.png",
"c.png",
"k.png"
};
bool running = true;
float w = FRAMEBUFFER_WIDTH;
float h = FRAMEBUFFER_HEIGHT;
float wh = w*0.5f;
float hh = h*0.5f;
Point2f v1,v2,v3,v4;
Point2f t1,t2,t3,t4;
float xSizeHalf = 64.0f;
float ySizeHalf = 64.0f;
v1.x = -xSizeHalf; v1.y = -ySizeHalf;
v2.x = -xSizeHalf; v2.y = ySizeHalf;
v3.x = xSizeHalf; v3.y = ySizeHalf;
v4.x = xSizeHalf; v4.y = -ySizeHalf;
t1.x = 0.0f; t1.y = 0.0;
t2.x = 0.0f; t2.y = 1.0;
t3.x = 1.0f; t3.y = 1.0;
t4.x = 1.0f; t4.y = 0.0;
ilInit();
SDL_Init(SDL_INIT_VIDEO);
s = SDL_SetVideoMode(FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT, 32, SDL_SWSURFACE);
if(!s){
SDL_Quit();
return 1;
}
framebuffer = (unsigned char*)s->pixels;
for(int i = 0; i < 5; i++){
ilGenImages(1, &textures[i].id);
ilBindImage(textures[i].id);
if(!ilLoadImage(texture_names[i])){
return 2;
}
textures[i].width = ilGetInteger(IL_IMAGE_WIDTH);
textures[i].height = ilGetInteger(IL_IMAGE_HEIGHT);
textures[i].pixels.resize(textures[i].width*textures[i].height*4);
ilCopyPixels(0,0,0,textures[i].width, textures[i].height, 1, IL_RGBA, IL_UNSIGNED_BYTE, &textures[i].pixels[0]);
ilDeleteImages(1,&textures[i].id);
}
while(running){
while(SDL_PollEvent(&ev)){
switch(ev.type){
case SDL_KEYDOWN:
case SDL_QUIT:
running = false;
break;
default:
break;
}
}
float t = (float)SDL_GetTicks() * 0.001f;
SDL_FillRect(s, nullptr, 0x00000000);
Point2f v1c,v2c,v3c,v4c;
Point2f t1c,t2c,t3c,t4c;
for(int i = 0; i < 5; i++){
float xOffset = wh + (xSizeHalf*2)*(i-2);
v1c = rotate(&v1, 45.0f * t);
v2c = rotate(&v2, 45.0f * t);
v3c = rotate(&v3, 45.0f * t);
v4c = rotate(&v4, 45.0f * t);
t1c = t1;
t2c = t2;
t3c = t3;
t4c = t4;
v1c.x += xOffset; v1c.y += hh;
v2c.x += xOffset; v2c.y += hh;
v3c.x += xOffset; v3c.y += hh;
v4c.x += xOffset; v4c.y += hh;
texture_ptr = &textures[i];
drawtriangle(v1c, v2c, v3c, t1c, t2c, t3c);
drawtriangle(v1c, v3c, v4c, t1c, t3c, t4c);
}
SDL_UpdateRect(s, 0, 0, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT);
}
SDL_Quit();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment