[C/C++] Простейший 3D мир
Волк-1024
Дата: Воскресенье, 18.01.2015, 19:40 | Сообщение # 1
Авторитетный
Зарегистрирован: 24.07.2011
Группа: Модераторы
Сообщений: 463
Статус: Offline
С недавних пор я занялся изучением программированием 3D графики, посему выкладываю свои недописанные наброски на С.
В интернете достаточно много примеров, но они все слишком громоздкие и труднопонимаемые. Я же постарался сделать всё кратко.
Изначально всё писалось с использованием библиотеки Freeglut.dll, но чтобы не отступать от своих принципов, сделал всё на голых вызовах и WinApi)
Из недописанного по причине лени(выкинуто из кода): стрейф камеры, загрузка текстур и свет.
Если данный пример вас заинтересует, то в следующий раз я реализую недописанное, возможно на Делфи.
Управление: мышь + wasd, выход на Esc.
Код
#include <windows.h>
#include <gl/glu.h>
#include <gl/gl.h>
#include <math.h>
#define VK_W 0x057
#define VK_S 0x053
#define VK_A 0x041
#define VK_D 0x044
typedef struct TVector3D
{
GLdouble x;
GLdouble y;
GLdouble z;
}
TVector3D;
typedef struct TCameraObject
{
TVector3D Pos; // Вектор позиции камеры.
TVector3D View; // Вектор направления камеры.
TVector3D Up; // Вертикальная ось.
}
TCameraObject;
HDC hDC;
HGLRC hRC;
HWND hMainWindow;
int AppWidth, AppHeight;
TCameraObject Camera = {{0.0, 2.5, 5.0}, {0.0, 2.5, 0.0}, {0.0, 1.0, 0.0}}; // А вот такого в Делфи нельзя
TVector3D NormalizeVector(TVector3D Vector)
{
TVector3D Result;
double Length = sqrt((Vector.x * Vector.x) + (Vector.y * Vector.y) + (Vector.z * Vector.z));
Result.x = Vector.x / Length;
Result.y = Vector.y / Length;
Result.z = Vector.z / Length;
return Result;
}
TVector3D CrossVectors(TVector3D Vector1, TVector3D Vector2)
{
TVector3D Result;
Result.x = ((Vector1.y * Vector2.z) - (Vector1.z * Vector2.y));
Result.y = ((Vector1.z * Vector2.x) - (Vector1.x * Vector2.z));
Result.z = ((Vector1.x * Vector2.y) - (Vector1.y * Vector2.x));
return Result;
}
void RotateCamera(double Speed)
{
TVector3D Vector;
Vector.x = Camera.View.x - Camera.Pos.x;
Vector.z = Camera.View.z - Camera.Pos.z;
Camera.View.z = Camera.Pos.z + sin(Speed) * Vector.x + cos(Speed) * Vector.z;
Camera.View.x = Camera.Pos.x + cos(Speed) * Vector.x - sin(Speed) * Vector.z;
}
void MoveCamera(double Speed)
{
TVector3D Vector = {0};
/*
Получаем вектора(направление) взгляда
*/
Vector.x = Camera.View.x - Camera.Pos.x;
Vector.z = Camera.View.z - Camera.Pos.z;
Vector = NormalizeVector(Vector);
/*
Полученное направление, помноженное на скорость, прибавляем к позиции камеры и к позиции взгляда.
Тем самым передвигая камеру вперёд или, если Speed отрицательное - назад.
*/
Camera.Pos.x += Vector.x * Speed;
Camera.Pos.z += Vector.z * Speed;
Camera.View.x += Vector.x * Speed;
Camera.View.z += Vector.z * Speed;
}
void DrawGrid()
{
for (float i = -50; i <= 100; i += 5)
{
glBegin(GL_LINES);
glVertex3f(-50, 0, i);
glVertex3f(100, 0, i);
glVertex3f(i, 0,-50);
glVertex3f(i, 0, 100);
glEnd();
}
}
void RenderScene(HDC hDC)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Очищаем буфер цвета и буфер глубины.
glLoadIdentity();
/*
Устанавливаем вектора камеры.
*/
gluLookAt(Camera.Pos.x, Camera.Pos.y, Camera.Pos.z,
Camera.View.x, Camera.View.y, Camera.View.z,
Camera.Up.x, Camera.Up.y, Camera.Up.z);
DrawGrid(); // Рисуем сетку.
glTranslatef(0.0, 30.0, 0.0); // Вторую сетку поднимим на 30 чего-то там по Y)
DrawGrid();;
SwapBuffers(hDC);
}
void MouseMove(int xMouse, int yMouse)
{
double xAngle = (double)((AppWidth >> 1) - xMouse) * 0.0005;
double yAngle = (double)((AppHeight >> 1) - yMouse) * 0.0010;
if (Camera.View.y > 8.0) Camera.View.y = 8.0;
if (Camera.View.y < -3.0) Camera.View.y = -3.0;
Camera.View.y += yAngle;
RotateCamera(-xAngle);
SetCursorPos(AppWidth >> 1, AppHeight >> 1);
RenderScene(hDC);
}
void KeyboardInput(int Key)
{
switch (Key)
{
case VK_W: MoveCamera(0.5); // Движние вперёд.
break;
case VK_S: MoveCamera(-0.5); // Назад.
break;
case VK_D: RotateCamera(0.025); // Вправо.
break;
case VK_A: RotateCamera(-0.025); // Влево
break;
case VK_ESCAPE:
{
PostQuitMessage(0);
}
}
}
void ResizeScene(int nWidth, int nHeight)
{
AppWidth = nWidth;
AppHeight = nHeight;
glViewport(0, 0, nWidth, nHeight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0, nWidth / nHeight, 0.1, 0); // Устнавливаем поле обзора в 45 градусов, соотношение сторон, передний и задний план.
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void EnableOpenGL(HWND hWindow, HDC* hDC, HGLRC* hRC)
{
PIXELFORMATDESCRIPTOR PixelFormat = {0};
PixelFormat.nSize = sizeof(PIXELFORMATDESCRIPTOR);
PixelFormat.nVersion = 1;
PixelFormat.dwFlags = PFD_DRAW_TO_WINDOW |
PFD_SUPPORT_OPENGL |
PFD_DOUBLEBUFFER;
PixelFormat.iPixelType = PFD_TYPE_RGBA;
PixelFormat.cColorBits = 24; // Глубина цвета.
PixelFormat.cDepthBits = 16; // Размер буфера глубины.
PixelFormat.iLayerType = PFD_MAIN_PLANE; // Тип плоскости.
if (!(*hDC = GetDC(hWindow)))
return;
if (!(SetPixelFormat(*hDC, (ChoosePixelFormat(*hDC, &PixelFormat)), &PixelFormat)))
return;
if (!(*hRC = wglCreateContext(*hDC)))
return;
if (!(wglMakeCurrent(*hDC, *hRC)))
return;
}
LRESULT CALLBACK WindowProc(HWND hWindow, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_SIZE : ResizeScene(LOWORD(lParam), (HIWORD(lParam))); // Обработка изменения размеров главного окна.
break;
case WM_PAINT : RenderScene(hDC); // Отрисовка кадра.
break;
case WM_CREATE : EnableOpenGL(hWindow, &hDC, &hRC); // Инициализация OpenGL.
break;
case WM_KEYDOWN : KeyboardInput(wParam); // Обработка нажатий клавиш на клавиатуре.
break;
case WM_MOUSEMOVE : MouseMove(LOWORD(lParam), (HIWORD(lParam))); // Обработка движения мыши.
break;
default:
return DefWindowProc(hWindow, uMsg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG Msg;
WNDCLASSEX WindowClass;
WindowClass.cbSize = sizeof(WNDCLASSEX);
WindowClass.style = CS_OWNDC;
WindowClass.lpfnWndProc = WindowProc;
WindowClass.cbClsExtra = 0;
WindowClass.cbWndExtra = 0;
WindowClass.hInstance = hInstance;
WindowClass.hIcon = LoadIcon(0, IDI_APPLICATION);
WindowClass.hCursor = LoadCursor(0, IDC_ARROW);
WindowClass.hbrBackground = 0;
WindowClass.lpszMenuName = 0;
WindowClass.lpszClassName = "GLClass";
WindowClass.hIconSm = LoadIcon(0, IDI_APPLICATION);
if (!RegisterClassEx(&WindowClass))
{
MessageBox(0, "Невозможно зарегистрировать класс окна.", "Шайссе!", MB_ICONERROR);
return 0;
}
hMainWindow = CreateWindowEx(0,
"GLClass",
"OpenGL 3D",
WS_POPUPWINDOW, // Окно создастся без оконной рамки.
0,
0,
800, // Ширина окна.
600, // Высота.
0,
0,
hInstance,
0);
if (!hMainWindow)
{
MessageBox(0, "Невозможно создать окно.", "Шайссе!", MB_ICONERROR);
return 0;
}
ShowWindow(hMainWindow, SW_MAXIMIZE);
ShowCursor(0);
while (Msg.message != WM_QUIT)
{
if (PeekMessage(&Msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
else
SendMessage(hMainWindow, WM_PAINT, 0, 0); // Посылаем окну сообщение на отрисовку следующего кадра. (Лучше сделать в отдельном потоке)
}
return Msg.wParam;
}
Волк-1024 (с)
Pascal, C\C++, Assembler, Python
Сообщение отредактировал Волк-1024 - Воскресенье, 18.01.2015, 22:55
xXxSh@dowxXx
Дата: Понедельник, 19.01.2015, 21:13 | Сообщение # 2
Авторитетный
Зарегистрирован: 22.01.2012
Группа: Модераторы
Сообщений: 702
Статус: Offline
Очень даже неплохо, да можно конечно многое сказать, но для начала очень даже, поздравляю!
XSPY
Дата: Среда, 21.01.2015, 20:04 | Сообщение # 3
Продвинутый
Зарегистрирован: 28.01.2010
Группа: Пользователи
Сообщений: 248
Статус: Offline
круто, пиши еще, но в 2-х форматах: СИ и Делфи =) я подписываюсь на тему!
Я не крекер,а программист!
Я не преступник-я свободный человек!
Лучше один раз накодить,чем сто раз качать билды!
Волк-1024
Дата: Пятница, 23.01.2015, 19:46 | Сообщение # 4
Авторитетный
Зарегистрирован: 24.07.2011
Группа: Модераторы
Сообщений: 463
Статус: Offline
Вот, на скорую руку перегнал код на Делфи:
Код
Program Test3D;
{$hints off}
{$apptype gui}
{$codepage utf8}
uses
Windows,
GlExt,
Glu,
Gl;
const
VK_W = $057;
VK_S = $053;
VK_A = $041;
VK_D = $044;
type
TVector3D = record
x, y, z: Double;
end;
TCamera = record
Pos : TVector3D;
View : TVector3D;
Up : TVector3D;
end;
var
Msg: TMsg;
Camera: TCamera;
WindowClass: TWndClassEx;
hWindow, hDC, hRC: THandle;
AppWidth, AppHeight: Integer;
procedure DrawGrid();
var
i: Double;
begin
i := -50;
while i <= 100 do
begin
i := i + 5;
glBegin(GL_LINES);
glVertex3f(-50, 0, i);
glVertex3f(100, 0, i);
glVertex3f(i, 0,-50);
glVertex3f(i, 0, 100);
glEnd();
end;
end;
procedure RenderScene(hDC: THandle);
begin
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
gluLookAt(Camera.Pos.x, Camera.Pos.y, Camera.Pos.z,
Camera.View.x, Camera.View.y, Camera.View.z,
Camera.Up.x, Camera.Up.y, Camera.Up.z);
DrawGrid();
glTranslatef(0.0, 30.0, 0.0);
DrawGrid();;
SwapBuffers(hDC);
end;
function NormalizeVector(Vector: TVector3D): TVector3D;
var
Length: Double;
begin
Length := sqrt((Vector.x * Vector.x) + (Vector.y * Vector.y) + (Vector.z * Vector.z));
Result.x := Vector.x / Length;
Result.y := Vector.y / Length;
Result.z := Vector.z / Length;
end;
procedure RotateCamera(Speed: Double);
var
Vector: TVector3D;
begin
Vector.x := Camera.View.x - Camera.Pos.x;
Vector.z := Camera.View.z - Camera.Pos.z;
Camera.View.z := Camera.Pos.z + sin(Speed) * Vector.x + cos(Speed) * Vector.z;
Camera.View.x := Camera.Pos.x + cos(Speed) * Vector.x - sin(Speed) * Vector.z;
end;
procedure MoveCamera(Speed: Double);
var
Vector: TVector3D;
begin
Vector.x := Camera.View.x - Camera.Pos.x;
Vector.y := 0;
Vector.z := Camera.View.z - Camera.Pos.z;
Vector := NormalizeVector(Vector);
Camera.Pos.x := Camera.Pos.x + Vector.x * Speed;
Camera.Pos.z := Camera.Pos.z + Vector.z * Speed;
Camera.View.x := Camera.View.x + Vector.x * Speed;
Camera.View.z := Camera.View.z + Vector.z * Speed;
end;
procedure MoveMouse(xMouse, yMouse: Integer);
var
xAngle, yAngle: Double;
begin
xAngle := Double((AppWidth shr 1) - xMouse) * 0.0005;
yAngle := Double((AppHeight shr 1) - yMouse) * 0.0010;
if Camera.View.y > 8.0 then
Camera.View.y := 8.0;
if Camera.View.y < -3.0 then
Camera.View.y := -3.0;
Camera.View.y := Camera.View.y + yAngle;
RotateCamera(-xAngle);
SetCursorPos(AppWidth shr 1, AppHeight shr 1);
RenderScene(hDC);
end;
procedure KeyboardInput(Key: Integer);
begin
case Key of
VK_W: MoveCamera(0.5);
VK_S: MoveCamera(-0.5);
VK_D: RotateCamera(0.025);
VK_A: RotateCamera(-0.025);
VK_ESCAPE: PostQuitMessage(0);
end;
end;
procedure ResizeScene(nWidth, nHeight: Integer);
begin
AppWidth := nWidth;
AppHeight := nHeight;
glViewport(0, 0, nWidth, nHeight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0, nWidth / nHeight, 0.1, 0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
end;
procedure EnableOpenGL(hWindow: THandle; var hDC, hRC: THandle);
var
PixelFormat: TPixelFormatDescriptor;
begin
ZeroMemory(@PixelFormat, (SizeOf(PIXELFORMATDESCRIPTOR)));
PixelFormat.nSize := SizeOf(PIXELFORMATDESCRIPTOR);
PixelFormat.nVersion := 1;
PixelFormat.dwFlags := PFD_DRAW_TO_WINDOW or
PFD_SUPPORT_OPENGL or
PFD_DOUBLEBUFFER;
PixelFormat.iPixelType := PFD_TYPE_RGBA;
PixelFormat.cColorBits := 24;
PixelFormat.cDepthBits := 16;
PixelFormat.iLayerType := PFD_MAIN_PLANE;
hDC := GetDC(hWindow);
if hDC = 0 then
Exit;
if not SetPixelFormat(hDC, (ChoosePixelFormat(hDC, @PixelFormat)), @PixelFormat) then
Exit;
hRC := wglCreateContext(hDC);
if hRC = 0 then
Exit;
if not wglMakeCurrent(hDC, hRC) then
Exit;
ZeroMemory(@Camera, (SizeOf(TCamera)));
Camera.Pos.y := 2.5;
Camera.Pos.z := 5.0;
Camera.View.y := 2.5;
Camera.Up.y := 1.0;
end;
function WindowProc(hWindow: THandle; uMsg: Cardinal; wParam: WParam; lParam: LParam): LResult; stdcall;
begin
case uMsg of
WM_SIZE : ResizeScene(LOWORD(lParam), (HIWORD(lParam)));
WM_PAINT : RenderScene(hDC);
WM_CREATE : EnableOpenGL(hWindow, hDC, hRC);
WM_KEYDOWN : KeyboardInput(wParam);
WM_MOUSEMOVE : MoveMouse(LOWORD(lParam), (HIWORD(lParam)));
end;
Result := DefWindowProc(hWindow, uMsg, wParam, lParam);
end;
begin
WindowClass.cbSize := SizeOf(WindowClass);
WindowClass.style := CS_OWNDC;
WindowClass.lpfnWndProc := @WindowProc;
WindowClass.cbClsExtra := 0;
WindowClass.cbWndExtra := 0;
WindowClass.hInstance := hInstance;
WindowClass.hIcon := LoadIcon(0, IDI_APPLICATION);
WindowClass.hCursor := LoadCursor(0, IDC_ARROW);
WindowClass.hbrBackground := 0;
WindowClass.lpszMenuName := nil;
WindowClass.lpszClassName := 'GLClass';
WindowClass.hIconSm := LoadIcon(0, IDI_APPLICATION);
if RegisterClassEx(@WindowClass) = 0 then
begin
MessageBox(0, 'Невозможно зарегистрировать класс окна.', 'Шайссе!', MB_ICONERROR);
Exit;
end;
hWindow := CreateWindowEx(0,
'GLClass',
'OpenGL 3D',
WS_POPUPWINDOW,
0,
0,
800,
600,
0,
0,
hInstance,
nil);
if hWindow = 0 then
begin
MessageBox(0, 'Невозможно создать окно.', 'Шайссе!', MB_ICONERROR);
Exit;
end;
ShowWindow(hWindow, SW_MAXIMIZE);
ShowCursor(false);
ZeroMemory(@Msg, (SizeOf(TMsg)));
while (Msg.message <> WM_QUIT) do
begin
if PeekMessage(@Msg, 0, 0, 0, PM_REMOVE) then
begin
TranslateMessage(@Msg);
DispatchMessage(@Msg);
end
else SendMessage(hWindow, WM_PAINT, 0, 0);
end;
Halt(Msg.wParam);
end.
Pascal, C\C++, Assembler, Python
Сообщение отредактировал Волк-1024 - Пятница, 23.01.2015, 19:56