Here are the C source files to one of my very early attempts at using graphics in C code, it uses GDI to render the stuff and isnt an exactly super effects 3d engine but it was back in 2004 when coded it and I had no real idea wtf I was doing

I wouldnt suggest using this code as is for an engine but it might help a few people learn something if only how not to code

Headerfile - VectorEngine.h
//-----------------------------------------------------------------------------------------------------
// 3d Flat Vector Engine (real basic one)
// by Joncom2000
// Dec 2004
//-----------------------------------------------------------------------------------------------------
#pragma once
#include <windows.h> // include all the windows headers
#include <math.h>
//-----------------------------------------------------------------------------------------------------
// Setup Constants and Arrays
//-----------------------------------------------------------------------------------------------------
#define MAXPOINTS 10000
#define MAXFACES 10000
float fVX; // X rotation.
float fVY; // Y rotation.
float fVZ; // Z rotation.
int iPoints; // The amount of points in the Object
int iFaces; // The Amount of faces in the Object
float fOriginx[MAXPOINTS]; // Original X co-ordinate store
float fOriginy[MAXPOINTS]; // Original Y co-ordinate store
float fOriginz[MAXPOINTS]; // Original Z co-ordinate store
int iTransx[MAXPOINTS]; // Transformed X co-ordinate store
int iTransy[MAXPOINTS]; // Transformed Y co-ordinate store
float fTransz[MAXPOINTS]; // Transforned Z co-ordinate store
int iF1[MAXFACES]; // Connections definition
int iF2[MAXFACES]; // Connections definition
int iF3[MAXFACES]; // Connections definition
int iRd[MAXFACES]; // Face red amount
int iGr[MAXFACES]; // Face green amount
int iBl[MAXFACES]; // Face blue amount
//---------------------------------------------------------------------------------
// Z Sort stuff
//---------------------------------------------------------------------------------
int iRenderlist[MAXFACES]; // contains face number
float fRenderzmin[MAXFACES]; // Minimum Z use in color code
//---------------------------------------------------------------------------------
// Polydraw Setup
//---------------------------------------------------------------------------------
COLORREF TempCol;
POINT point_list[3];
// Function prototypes ?
void Rotate3dpoints(int iDistance=3,int iHalfx=320,int iHalfy=240);
int CreateRenderlist();
int Quicksort(int l,int r);
void DrawSortedObject(HDC hDC,int iNumberrendered,int iLight=30);
void TriangleFill(HDC hDC,int tmx1,int tmy1,int tmx2,int tmy2,int tmx3,int tmy3,int r,int g,int b);
void Cls(HDC hDC,int r=0,int g=0,int b=0,int x=640,int y=480);
void ReadModel();
void readstr(FILE *f,char *string);
Engine Code - VectorEngine.cpp
//-----------------------------------------------------------------------------------------------------
// 3d Flat Vector Engine (real basic one)
// by Joncom2000
// Dec 2004
//-----------------------------------------------------------------------------------------------------
#include <stdio.h> // Header File For Standard Input/Output
#include "VectorEngine.h"
//-------------------------------------------------------------------
// NAME : Rotate3dpoints()
// PURPOSE : This function rotates the points in 3d.
// INPUTS : None.
// RETURNS : Nothing.
//-------------------------------------------------------------------
void Rotate3dpoints(int iDistance,int iHalfx,int iHalfy)
{
float cosx,cosy,cosz,sinx,siny,sinz,x3d,y3d,z3d,tx,ty,tz,ox;
cosx=cos((float)(fVX * 0.0174532925));
sinx=sin((float)(fVX * 0.0174532925));
cosy=cos((float)(fVY * 0.0174532925));
siny=sin((float)(fVY * 0.0174532925));
cosz=cos((float)(fVZ * 0.0174532925));
sinz=sin((float)(fVZ * 0.0174532925));
for(int n=0;n<iPoints+1;++n)
{
x3d = fOriginx[n];
y3d = fOriginy[n];
z3d = fOriginz[n];
ty = (y3d * cosx) - (z3d * sinx);
tz = (y3d * sinx) + (z3d * cosx);
tx = (x3d * cosy) - (tz * siny);
tz = (x3d * siny) + (tz * cosy);
ox = tx;
tx = (tx * cosz) - (ty * sinz);
ty = (ox * sinz) + (ty * cosz);
iTransx[n]=(int((tx / (iDistance-tz))*512))+iHalfx;
iTransy[n]=(int((ty / (iDistance-tz))*512))+iHalfy;
fTransz[n]=tz;
}
}
//-------------------------------------------------------------------
// NAME : CreateRenderlist()
// PURPOSE : Determine which faces to render
// INPUTS : renderbank=bank to draw clear
// RETURNS : None.
//-------------------------------------------------------------------
int CreateRenderlist()
{
int vx1,vx2,vy1,vy2,n;
int iNumberrendered=0;
for(int a=0;a<iFaces+1;++a)
{
vx1= iTransx[iF1[a]]-iTransx[iF2[a]];
vy1= iTransy[iF1[a]]-iTransy[iF2[a]];
vx2= iTransx[iF3[a]]-iTransx[iF2[a]];
vy2= iTransy[iF3[a]]-iTransy[iF2[a]];
n=vx1*vy2-vx2*vy1;
if(n>0)// ;If Not removed by backface cull Then add To renderlist And renderz#
{
iRenderlist[iNumberrendered]=a;
fRenderzmin[iNumberrendered]=fTransz[iF1[a]];
if(fTransz[iF2[a]]<fRenderzmin[iNumberrendered])
{
fRenderzmin[iNumberrendered]=fTransz[iF2[a]];
}
if(fTransz[iF3[a]]<fRenderzmin[iNumberrendered])
{
fRenderzmin[iNumberrendered]=fTransz[iF3[a]];
}
iNumberrendered=iNumberrendered+1;
}
}
iNumberrendered=iNumberrendered-1;
return(iNumberrendered);
}
//-------------------------------------------------------------------
// NAME : Quicksort()
// PURPOSE : Sort visable faces into order
// INPUTS : l & r
// RETURNS : true :).
//-------------------------------------------------------------------
int Quicksort(int l,int r)
{
int p,q,h1,a;
float h2,z;
p=l;
q=r;
z=fRenderzmin[(l+r)/2];
while(true)
{
while(fRenderzmin[p]<z)
{
p=p+1;
}
while(z<fRenderzmin[q])
{
q=q-1;
}
if(p>q)
{
break;
}
//SWAP------------------
h1=iRenderlist[q];
h2=fRenderzmin[q];
iRenderlist[q]=iRenderlist[p];
fRenderzmin[q]=fRenderzmin[p];
iRenderlist[p]=h1;
fRenderzmin[p]=h2;
//----------------------
p=p+1;
q=q-1;
if(q<0)
{
break;
}
}
if(l<q)
{
a=Quicksort(l,q);
}
if(p<r)
{
a=Quicksort(p,r);
}
return(1);
}
//-------------------------------------------------------------------
// NAME : DrawSortedObject()
// PURPOSE : Render visable faces in order
// INPUTS : renderbank=bank to draw clear
// RETURNS : None.
//-------------------------------------------------------------------
void DrawSortedObject(HDC hDC,int iNumberrendered,int iLight)
{
int r,g,b;
for(int a=0;a<iNumberrendered+1;++a)
{
r=iRd[iRenderlist[a]]+int(fRenderzmin[a]*iLight);
g=iGr[iRenderlist[a]]+int(fRenderzmin[a]*iLight);
b=iBl[iRenderlist[a]]+int(fRenderzmin[a]*iLight);
if(r<0)
{
r=0;
}
if(r>255)
{
r=255;
}
if(g<0)
{
g=0;
}
if(g>255)
{
g=255;
}
if(b<0)
{
b=0;
}
if(b>255)
{
b=255;
}
TriangleFill(hDC,iTransx[iF1[iRenderlist[a]]],iTransy[iF1[iRenderlist[a]]],iTransx[iF2[iRenderlist[a]]],iTransy[iF2[iRenderlist[a]]],iTransx[iF3[iRenderlist[a]]],iTransy[iF3[iRenderlist[a]]],r,g,b);
}
}
//------------------------------------------------------------------
// NAME : ReadModel()
// PURPOSE : Reads in a models data (uses. Model.txt for filename :(
// INPUTS : None
// RETURNS : Nothing
//------------------------------------------------------------------
void ReadModel()
{
float x, y, z;
int f1,f2,f3;
FILE *filein;
char oneline[255];
filein = fopen("Data.ttd", "rt"); // File To Load model Data From
readstr(filein,oneline);
sscanf(oneline, "%d\n", &iPoints);
readstr(filein,oneline);
sscanf(oneline, "%d\n", &iFaces);
for (int loop = 0; loop < iPoints; loop++)
{
readstr(filein,oneline);
sscanf(oneline, "%f %f %f", &x, &y, &z);
fOriginx[loop]=x;
fOriginy[loop]=y;
fOriginz[loop]=z;
}
for (int loop = 0; loop < iFaces; loop++)
{
readstr(filein,oneline);
sscanf(oneline, "%d %d %d", &f1, &f2, &f3);
iF1[loop]=f1;
iF2[loop]=f2;
iF3[loop]=f3;
iRd[loop]=128; // Face red amount
iGr[loop]=128; // Face green amount
iBl[loop]=128;
}
fclose(filein);
}
void readstr(FILE *f,char *string)
{
do
{
fgets(string, 255, f);
} while ((string[0] == '/') || (string[0] == '\n'));
return;
}
//------------------------------------------------------------------
// NAME : Cls(hDC,r,g,b,x,y)
// PURPOSE : Quick and dirty cls command
// INPUTS : r,g,b & x,y size
// RETURNS : Nothing
//------------------------------------------------------------------
void Cls(HDC hDC,int r,int g,int b,int x,int y)
{
HPEN pen_color;
HBRUSH brush_color;
// set colors for drawing
TempCol=RGB(r,g,b);
pen_color=CreatePen(PS_SOLID,1,TempCol);
brush_color=CreateSolidBrush(TempCol);
// select them into dc
SelectObject(hDC,pen_color);
SelectObject(hDC,brush_color);
// Draw a rectangle
Rectangle(hDC,0,0,x,y);
// Do not forget to clean up.
DeleteObject(pen_color);
DeleteObject(brush_color);
}
//------------------------------------------------------------------
// NAME : TriangleFill(hdc,tmx1,tmy1,tmx2,tmy2,tmx3,tmy3,r,g,b)
// PURPOSE : fills a triangle
// INPUTS : three sets of points and seperate rgb values
// RETURNS : Nothing
//------------------------------------------------------------------
void TriangleFill(HDC hDC,int tmx1,int tmy1,int tmx2,int tmy2,int tmx3,int tmy3,int r,int g,int b)
{
HPEN pen_color;
HBRUSH brush_color;
// set colors for polygon
TempCol=RGB(r,g,b);
pen_color=CreatePen(PS_SOLID,1,TempCol);
brush_color=CreateSolidBrush(TempCol);
// select them into dc
SelectObject(hDC,pen_color);
SelectObject(hDC,brush_color);
// create array of points
point_list[0].x=tmx1;
point_list[0].y=tmy1;
point_list[1].x=tmx2;
point_list[1].y=tmy2;
point_list[2].x=tmx3;
point_list[2].y=tmy3;
// draw the polygon
Polygon(hDC, point_list, 3);
// Do not forget to clean up.
DeleteObject(pen_color);
DeleteObject(brush_color);
}
Example - GDIenginemain.cpp
// INCLUDES ///////////////////////////////////////////////
#define WIN32_LEAN_AND_MEAN // just say no to MFC
#include <windows.h> // include all the windows headers
#include <strsafe.h> // This enables us to use sprintf
#include <math.h>
#include "VectorEngine.h"
// DEFINES ////////////////////////////////////////////////
// defines for windows
#define WINDOW_CLASS_NAME "WINCLASS1"
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480
// MACROS /////////////////////////////////////////////////
#define KEYDOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
// GLOBALS ////////////////////////////////////////////////
HWND main_window_handle = NULL; // globally track main window
HINSTANCE hinstance_app = NULL; // globally track hinstance
HDC hOffscreenDC= NULL;
HBITMAP hOffscreenBitmap= NULL;
BOOL bFullScreen = FALSE; // Create a boolean and set it to false. If we choose full screen, set it to TRUE
void EffectSetup();
int EffectMain(HDC hdc);
void GetFPS2(HDC hDC);
float fDelta=1;
// FUNCTIONS //////////////////////////////////////////////
// Fullscreen
void ChangeToFullScreen(int width, int height)
{
DEVMODE dmSettings; // Device Mode variable - Needed to change modes
memset(&dmSettings,0,sizeof(dmSettings)); // Makes Sure Memory's Cleared
// Get current settings -- This function fills our the settings
// This makes sure NT and Win98 machines change correctly
if(!EnumDisplaySettings(NULL,ENUM_CURRENT_SETTINGS,&dmSettings))
{
// Display error message if we couldn't get display settings
MessageBox(NULL, "Could Not Enum Display Settings", "Error", MB_OK);
return;
}
dmSettings.dmPelsWidth = width; // Set the desired Screen Width
dmSettings.dmPelsHeight = height; // Set the desired Screen Height
dmSettings.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT; // Set the flags saying we're changing the Screen Width and Height
// This function actually changes the screen to full screen
// CDS_FULLSCREEN Gets Rid Of Start Bar.
// We always want to get a result from this function to check if we failed
int result = ChangeDisplaySettings(&dmSettings,CDS_FULLSCREEN);
// Check if we didn't recieved a good return message From the function
if(result != DISP_CHANGE_SUCCESSFUL)
{
// Display the error message and quit the program
MessageBox(NULL, "Display Mode Not Compatible", "Error", MB_OK);
PostQuitMessage(0);
}
}
LRESULT CALLBACK WindowProc(HWND hwnd,
UINT msg,
WPARAM wparam,
LPARAM lparam)
{
// this is the main message handler of the system
PAINTSTRUCT ps; // used in WM_PAINT
HDC hdc; // handle to a device context
// what is the message
switch(msg)
{
case WM_CREATE:
{
// do initialization stuff here
// return success
return(0);
} break;
case WM_PAINT:
{
// simply validate the window
hdc = BeginPaint(hwnd,&ps);
// end painting
EndPaint(hwnd,&ps);
// return success
return(0);
} break;
case WM_DESTROY:
{
// kill the application, this sends a WM_QUIT message
PostQuitMessage(0);
// return success
return(0);
} break;
default:break;
} // end switch
// process any messages that we didn't take care of
return (DefWindowProc(hwnd, msg, wparam, lparam));
} // end WinProc
// WINMAIN ////////////////////////////////////////////////
int WINAPI WinMain( HINSTANCE hinstance,
HINSTANCE hprevinstance,
LPSTR lpcmdline,
int ncmdshow)
{
WNDCLASSEX winclass; // this will hold the class we create
HWND hwnd; // generic window handle
MSG msg; // generic message
HDC hdc; // graphics device context
// first fill in the window class stucture
winclass.cbSize = sizeof(WNDCLASSEX);
winclass.style = CS_DBLCLKS | CS_OWNDC |
CS_HREDRAW | CS_VREDRAW;
winclass.lpfnWndProc = WindowProc;
winclass.cbClsExtra = 0;
winclass.cbWndExtra = 0;
winclass.hInstance = hinstance;
winclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
winclass.hCursor = LoadCursor(NULL, IDC_ARROW);
winclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
winclass.lpszMenuName = NULL;
winclass.lpszClassName = WINDOW_CLASS_NAME;
winclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
// save hinstance in global
hinstance_app = hinstance;
// register the window class
if (!RegisterClassEx(&winclass))
return(0);
// Fullscreen ?
DWORD dwStyle;
if(MessageBox(NULL, "Click Yes to go to full screen", "Options", MB_YESNO | MB_ICONQUESTION) == IDNO)
{
dwStyle = WS_POPUPWINDOW | WS_CAPTION | WS_MINIMIZEBOX; // If we don't want full screen, open a simple window
}
else // If we chose YES
{
bFullScreen = TRUE; // Set our boolean to TRUE, we wanted fullscreen
// This is the style that we need our window to have in order to be windowless fullscreen
dwStyle = WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
ChangeToFullScreen(WINDOW_WIDTH,WINDOW_HEIGHT); // This changes our screen to full screen with our desired resolution
}
// Create the window
if (!bFullScreen)
{
hwnd = CreateWindowEx(NULL, // extended style
WINDOW_CLASS_NAME, // class
"Joncom2000 GDI Engine v0.1", // title
WS_POPUPWINDOW | WS_CAPTION | WS_MINIMIZEBOX| WS_VISIBLE,
0,0, // initial x,y
WINDOW_WIDTH+6, // initial width
WINDOW_HEIGHT+32,// initial height
NULL, // handle to parent
NULL, // handle to menu
hinstance,// instance of this application
NULL); // extra creation parms
}
else
{
hwnd = CreateWindowEx(NULL, // extended style
WINDOW_CLASS_NAME, // class
"Joncom2000 GDI Engine v0.1", // title
WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN| WS_VISIBLE,
0,0, // initial x,y
WINDOW_WIDTH, // initial width
WINDOW_HEIGHT,// initial height
NULL, // handle to parent
NULL, // handle to menu
hinstance,// instance of this application
NULL); // extra creation parms
ShowCursor(FALSE);
}
if (!hwnd)
return(0);
// save main window handle
main_window_handle = hwnd;
// get the graphics device context
hdc = GetDC(hwnd);
// Create the offscreen device context and bitmap
hOffscreenDC = CreateCompatibleDC(GetDC(main_window_handle));
hOffscreenBitmap = CreateCompatibleBitmap(GetDC(main_window_handle),640,480);
SelectObject(hOffscreenDC, hOffscreenBitmap);
EffectSetup();
SetBkMode(hOffscreenDC,OPAQUE);
SetBkColor(hOffscreenDC,RGB(0,0,0));
SetTextColor(hOffscreenDC, RGB(255, 255, 255));
// enter main event loop, but this time we use PeekMessage()
// instead of GetMessage() to retrieve messages
while(TRUE)
{
// test if there is a message in queue, if so get it
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
// test if this is a quit
if (msg.message == WM_QUIT)
{
break;
}
// translate any accelerator keys
TranslateMessage(&msg);
// send the message to the window proc
DispatchMessage(&msg);
} // end if
// main game processing goes here
if (KEYDOWN(VK_ESCAPE))
{
SendMessage(hwnd, WM_CLOSE, 0,0);
}
EffectMain(hOffscreenDC);
// Blit the offscreen bitmap to the game screen
BitBlt(hdc, 0, 0, WINDOW_WIDTH,WINDOW_HEIGHT,hOffscreenDC, 0, 0, SRCCOPY);
//GetFPS();
} // end while
if(bFullScreen) // If we went to full screen mode
{ // Calling the same function with NULL and 0 reset the screen settings and resolution
ChangeDisplaySettings(NULL, 0);
ShowCursor(TRUE);
}
// release the device context
ReleaseDC(hwnd,hdc);
// Cleanup the offscreen device context and bitmap
DeleteObject(hOffscreenBitmap);
DeleteDC(hOffscreenDC);
// return to Windows like this
return(int(msg.wParam));
} // end WinMain
void GetFPS2(HDC hDC)
{
// Statics are used so that values exsist after the function has
// ended so the next time it is called the values are still there.
// I really don't like using globals so I don't when I can.
// This will hold the number of frames that will go in the title bar.
// Lets initialize this to 0;
static char titleBar[25] = {0};
static RECT rect = { 0, 0, 70, 20 };
// This will hold the number of frames per second in between seconds.
static float FPS = 0.0f;
// This will be used to check if a second has passed.
float nextSecond = 0.0f;
// This is the last second that occured.
static float prevSecond = 0.0f;
// Add to the frames per second every time this function is called.
FPS++;
// Get the second in millisecond then multiply it by 0.001 to convert this to
// seconds.
nextSecond = GetTickCount() * 0.001f;
// If the time we have now substracted from the previous second is greater than
// or equal to 1 (i.e. if a second has passed) then we display the FPS number.
if(nextSecond - prevSecond > 1.0f)
{
// Make the second we just got the previous second so we can used this the
// next time the function is called.
prevSecond = nextSecond;
// print the FPS in the titlebar array. Don't forget to cast the FPS so
// what is printed is a whole number. Or you can just print only 2 or
// even no decimal places after the number. What ever turns you on.
StringCbPrintf(titleBar,25,"%d FPS ", int(FPS));
if(FPS>1)
{
fDelta=float((85.0/FPS));
}
// Reset the FPS counter.
FPS = 0;
}
DrawText(hDC, titleBar, -1, &rect, DT_SINGLELINE | DT_RIGHT | DT_VCENTER);
}
void EffectSetup()
{
ReadModel();
}
int EffectMain(HDC hDC)
{
int iNumberrendered;
Cls(hDC);
fVX=fVX+float(1*fDelta);
fVY=fVY+float(0.5*fDelta);
fVZ=fVZ+float(0.25*fDelta);
if (fVX>360)
{
fVX=fVX-360;
}
if (fVY>360)
{
fVY=fVY-360;
}
if (fVZ>360)
{
fVZ=fVZ-360;
}
Rotate3dpoints(250,320,240);
iNumberrendered=CreateRenderlist();
Quicksort(0,iNumberrendered);
DrawSortedObject(hDC,iNumberrendered,1);
GetFPS2(hDC);
return (0);
}
The code was compiled originally in VC++ 2003.net but it does compile under VC 2008 express, not sure about DevC++ but only non-standard include is for fps display so can probably be worked around

Attached is a rar with the sourcecode, compiled exe and a test data file for a object.
Cheers
Jon