| |
| 3D circel tunnel programma's en broncode in C |
© 2010 Hein Pragt
Nog een klassiek stukje grafische code is een 3d tunnel met circels demo. Het wekt de suggestie dat u in een tunnel
van circels kijkt die op u afkomen en u aan alle zijden passeren. Punten die ver weg zijn zullen kleiner zijn en zullen groter worden als ze de
camera (onze kijk positie) naderen. Voor de helderheid geldt hetzelfde, punten die verder van ons vandaan zijn hebben een
minder heldere kleur dan punten die dichtbij zijn. Dit wekt de indruk dat de punten echt op ons afkomen.
Voor de punten gebruiken we een structure waarin we de eigenschappen van de punt opslaan. Dat zijn de x, y en z
positie als float, een 3d coordinaat in de wiskunde, waarmee we de positie horizontaal, verticaal en in de diepte opslaan.
Een groter z positie betekend dus dat het voorwerp ver van ons verwijderd is (in dit wiskundig model) en de grootte
en helderheid kunnen we dan ook berekenen uit de (z) afstand. Verder slaan we deze vector (x,y,z) ook op als int
waarbij ze omgerekend zijn naar een punt op het beeldscherm. Dit is eenvoudig te doen als we toch aan het rekenen
zijn en scheelt weer tijd als we de punten willen schrijven in het venster.
Deze demo verschilt ten opzichte van het starfield, dat hier ook een leuke demo van wat toegepaste wiskunde in
zit. We gebruiken de sinus en cosinus functie, zetten radialen om in graden en voeren een extra transformatie uit zodat
lijkt alsof de tunnel een sinusvirmige beweging maakt in de ruimte. In de init routine laat ik zien hoe je met behulp van een
sinus en cosinus vrij eenvoudig een circel kunt maken.
Omdat het om vrij universele code gaat heb ik de Windows Win32 interface zo minimaal mogelijk gemaakt, dit is
trouwens altijd mijn voorkeur om programma’s zo portable mogelijk te maken en niet te afhankelijk te zijn van
het besturingssysteem.
// ------------------------------------
// Win32 Demo
// © Hein Pragt 2010
// ------------------------------------
#include <windows.h>
#include <windowsx.h>
#include <stdlib.h>
#include <math.h>
// Interface to windows
void PlotPixel(int x, int y, int r, int g, int b);
// -------------------------------------------------------------
// This is our starfield code
// -------------------------------------------------------------
#define NUMPIXELS 1440
struct t_stars {
float x;
float y;
float z;
int sx;
int sy;
int sz;
} stars[NUMPIXELS];
int sWidth, sHeight;
int cnt1;
int cnt2;
DWORD nStartTime;
void DeleteStars(void);
void DrawStar(int x, int y, int z, int color);
void InitStars(int WinWidth, int WinHeight);
void GameLoop(void);
void MoveStars(void);
void PlotStars(void);
// ---------------------------------------
// Call this before at init of the app
// ---------------------------------------
void InitStars(int WinWidth, int WinHeight)
{
int a,an;
float sn,cs;
cnt1 = 0;
cnt2 = 0;
sWidth = WinWidth;
sHeight = WinHeight;
nStartTime = GetTickCount();
srand(GetTickCount()); // Random seed
for (a=0; a<NUMPIXELS; a++) {
an = (a * 4) % 360;
sn = sin((float) an * 3.1428 / 180) * 8;
cs = cos((float) an * 3.1428 / 180) * 8;
stars[a].x = (float) cs;
stars[a].y = (float) sn;
stars[a].z = (float) 100 - (a / 90) * 6;
}
}
// ---------------------------------------
// Call this to keep the engine running
// ---------------------------------------
void GameLoop()
{
if ((GetTickCount() - nStartTime) < 33) {
Sleep(1);
return; // Wait for next frame
}
nStartTime = GetTickCount();
DeleteStars();
MoveStars();
PlotStars();
}
// ---------------------------------------
// Delete alle stars from screen
// ---------------------------------------
void DeleteStars(void)
{
int a;
for (a=0; a<NUMPIXELS; a++)
DrawStar(stars[a].sx, stars[a].sy, stars[a].sz, 0);
}
// ---------------------------------------
// Plot alle stars on screen
// ---------------------------------------
void PlotStars(void)
{
int a, col;
for (a=0; a<NUMPIXELS; a++) {
col = 255 - (int)(stars[a].sz);
DrawStar(stars[a].sx, stars[a].sy, stars[a].sz, col);
}
}
// ---------------------------------------
// Plot single star on screen
// ---------------------------------------
void DrawStar(int x, int y, int z, int color)
{
int a, b, max;
max = 3 - (int)(z/33); // size based on distance from camera
for (a=0; a<max; a++)
for (b=0; b<max; b++)
PlotPixel(x+a, y+b, color, color, color);
}
// ---------------------------------------
// Calculate new position of starts
// ---------------------------------------
void MoveStars(void)
{
int a,an;
float sn,cs;
cnt1 += 2;
cnt2 += 3;
for (a=0; a<NUMPIXELS; a++) {
stars[a].z -= 1;
if (stars[a].z <= 10) {
stars[a].z += 96;
}
an = (cnt1 + (int)stars[a].z) % 360;
sn = sin((float) an * 3.1428 / 180) * 30;
an = cnt2 % 360;
cs = cos((float) an * 3.1428 / 180) * 30;
// Simple perspective transformation from 3D to 2D:
// x2D = x3D / z3D * (screen width) + (half of screen width)
// y2D = y3D / z3D * (screen height) + (half of screen height)
stars[a].sx = (stars[a].x / stars[a].z) * sWidth + (sWidth >> 1) + sn;
stars[a].sy = (stars[a].y / stars[a].z) * sHeight + (sHeight >> 1) + cs;
stars[a].sz = (int) stars[a].z;
}
}
// ---------------------------------------------
// The minimal Window interface code
// ---------------------------------------------
#define WIN_CLASS_NAME "MyGraphClass"
HDC hMainDC;
HINSTANCE hInstance;
HWND hWnd;
int WinWidth = 500;
int WinHeight = 500;
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hprevinstance, LPSTR lpcmdline, int ncmdshow)
{
WNDCLASSEX winclass;
HWND hwnd;
MSG msg;
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_WINLOGO);
winclass.hCursor = LoadCursor(NULL, IDC_ARROW);
winclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
winclass.lpszMenuName = NULL; // no menu
winclass.lpszClassName = (LPCWSTR) WIN_CLASS_NAME;
winclass.hIconSm = LoadIcon(NULL, IDI_WINLOGO);
if (!RegisterClassEx(&winclass))
return(0);
if (!(hwnd = CreateWindowEx(NULL,
WIN_CLASS_NAME, // class identifier
TEXT("Demo"), // window title
WS_OVERLAPPEDWINDOW | WS_VISIBLE, // parameters
CW_USEDEFAULT, CW_USEDEFAULT, // initial position
WinWidth, WinHeight, // Window size
NULL, // handle to parent
NULL, // handle to menu
hinstance, // app handle
NULL)))
return(0);
// Copy to globals
hWnd = hwnd;
hInstance = hinstance;
hMainDC = GetDC(hWnd);
InitStars(WinWidth, WinHeight);
while(TRUE) {
if (PeekMessage(&msg,0,0,0,PM_REMOVE)) {
if (msg.message == WM_QUIT) // exit loop on quit
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
GameLoop(); // keep animation running
}
ReleaseDC(hWnd, hMainDC);
return(msg.wParam);
}
// ---------------------------------------------
// The minimal Window message handler
// ---------------------------------------------
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg,WPARAM wparam, LPARAM lparam)
{
switch(msg) {
case WM_DESTROY:
PostQuitMessage(0);
return(0);
break;
case WM_CLOSE:
break;
}
return(DefWindowProc(hwnd, msg, wparam, lparam));
}
// ---------------------------------------------
// The plot pixel function
// ---------------------------------------------
void PlotPixel(int x, int y, int r, int g, int b)
{
SetPixel(hMainDC, x, y, RGB(r, g, b));
}
Last update: 08-04-2010
|
|