[C/C++] Простейший 3D мир
Волк-1024 Дата: Воскресенье, 18.01.2015, 19:40 | Сообщение # 1
Авторитетный
Зарегистрирован: 24.07.2011
Группа: Модераторы
Сообщений: 469
Статус: 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
Группа: Пользователи
Сообщений: 263
Статус: Offline
круто, пиши еще, но в 2-х форматах: СИ и Делфи =) я подписываюсь на тему!
Я не крекер,а программист! Я не преступник-я свободный человек! Лучше один раз накодить,чем сто раз качать билды!
Волк-1024 Дата: Пятница, 23.01.2015, 19:46 | Сообщение # 4
Авторитетный
Зарегистрирован: 24.07.2011
Группа: Модераторы
Сообщений: 469
Статус: 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