glMap1
[?] имеет довольно узкий коридор поддержки. Она была введена в OpenGL 1.0 и удалена из поддержки в OpenGL 3.2 Core Profile.wglCreateContext
, система сама решает какой контекст ей для тебя создавать. Это может быть и контекст с версией 4.6, в котором уже нет поддержки функции glMap1f
.wglCreateContextAttribsARB
позволяет задавать атрибуты для создаваемого контекста, среди которых ты можешь обозначить и требуемую версию.// Sample code showing how to create a modern OpenGL window and rendering context on Win32.
#include <windows.h>
#include <gl/gl.h>
#include <stdbool.h>
typedef HGLRC WINAPI wglCreateContextAttribsARB_type(HDC hdc, HGLRC hShareContext,
const int *attribList);
wglCreateContextAttribsARB_type *wglCreateContextAttribsARB;
// See https://www.khronos.org/registry/OpenGL/extensions/ARB/WGL_ARB_create_context.txt for all values
#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
typedef BOOL WINAPI wglChoosePixelFormatARB_type(HDC hdc, const int *piAttribIList,
const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats);
wglChoosePixelFormatARB_type *wglChoosePixelFormatARB;
// See https://www.khronos.org/registry/OpenGL/extensions/ARB/WGL_ARB_pixel_format.txt for all values
#define WGL_DRAW_TO_WINDOW_ARB 0x2001
#define WGL_ACCELERATION_ARB 0x2003
#define WGL_SUPPORT_OPENGL_ARB 0x2010
#define WGL_DOUBLE_BUFFER_ARB 0x2011
#define WGL_PIXEL_TYPE_ARB 0x2013
#define WGL_COLOR_BITS_ARB 0x2014
#define WGL_DEPTH_BITS_ARB 0x2022
#define WGL_STENCIL_BITS_ARB 0x2023
#define WGL_FULL_ACCELERATION_ARB 0x2027
#define WGL_TYPE_RGBA_ARB 0x202B
static void
fatal_error(char *msg)
{
MessageBoxA(NULL, msg, "Error", MB_OK | MB_ICONEXCLAMATION);
exit(EXIT_FAILURE);
}
static void
init_opengl_extensions(void)
{
// Before we can load extensions, we need a dummy OpenGL context, created using a dummy window.
// We use a dummy window because you can only set the pixel format for a window once. For the
// real window, we want to use wglChoosePixelFormatARB (so we can potentially specify options
// that aren't available in PIXELFORMATDESCRIPTOR), but we can't load and use that before we
// have a context.
WNDCLASSA window_class = {
.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
.lpfnWndProc = DefWindowProcA,
.hInstance = GetModuleHandle(0),
.lpszClassName = "Dummy_WGL_djuasiodwa",
};
if (!RegisterClassA(&window_class)) {
fatal_error("Failed to register dummy OpenGL window.");
}
HWND dummy_window = CreateWindowExA(
0,
window_class.lpszClassName,
"Dummy OpenGL Window",
0,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
0,
0,
window_class.hInstance,
0);
if (!dummy_window) {
fatal_error("Failed to create dummy OpenGL window.");
}
HDC dummy_dc = GetDC(dummy_window);
PIXELFORMATDESCRIPTOR pfd = {
.nSize = sizeof(pfd),
.nVersion = 1,
.iPixelType = PFD_TYPE_RGBA,
.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
.cColorBits = 32,
.cAlphaBits = 8,
.iLayerType = PFD_MAIN_PLANE,
.cDepthBits = 24,
.cStencilBits = 8,
};
int pixel_format = ChoosePixelFormat(dummy_dc, &pfd);
if (!pixel_format) {
fatal_error("Failed to find a suitable pixel format.");
}
if (!SetPixelFormat(dummy_dc, pixel_format, &pfd)) {
fatal_error("Failed to set the pixel format.");
}
HGLRC dummy_context = wglCreateContext(dummy_dc);
if (!dummy_context) {
fatal_error("Failed to create a dummy OpenGL rendering context.");
}
if (!wglMakeCurrent(dummy_dc, dummy_context)) {
fatal_error("Failed to activate dummy OpenGL rendering context.");
}
wglCreateContextAttribsARB = (wglCreateContextAttribsARB_type*)wglGetProcAddress(
"wglCreateContextAttribsARB");
wglChoosePixelFormatARB = (wglChoosePixelFormatARB_type*)wglGetProcAddress(
"wglChoosePixelFormatARB");
wglMakeCurrent(dummy_dc, 0);
wglDeleteContext(dummy_context);
ReleaseDC(dummy_window, dummy_dc);
DestroyWindow(dummy_window);
}
static HGLRC
init_opengl(HDC real_dc)
{
init_opengl_extensions();
// Now we can choose a pixel format the modern way, using wglChoosePixelFormatARB.
int pixel_format_attribs[] = {
WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
WGL_COLOR_BITS_ARB, 32,
WGL_DEPTH_BITS_ARB, 24,
WGL_STENCIL_BITS_ARB, 8,
0
};
int pixel_format;
UINT num_formats;
wglChoosePixelFormatARB(real_dc, pixel_format_attribs, 0, 1, &pixel_format, &num_formats);
if (!num_formats) {
fatal_error("Failed to set the OpenGL 3.3 pixel format.");
}
PIXELFORMATDESCRIPTOR pfd;
DescribePixelFormat(real_dc, pixel_format, sizeof(pfd), &pfd);
if (!SetPixelFormat(real_dc, pixel_format, &pfd)) {
fatal_error("Failed to set the OpenGL 3.3 pixel format.");
}
// Specify that we want to create an OpenGL 3.3 core profile context
int gl33_attribs[] = {
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
WGL_CONTEXT_MINOR_VERSION_ARB, 3,
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
0,
};
HGLRC gl33_context = wglCreateContextAttribsARB(real_dc, 0, gl33_attribs);
if (!gl33_context) {
fatal_error("Failed to create OpenGL 3.3 context.");
}
if (!wglMakeCurrent(real_dc, gl33_context)) {
fatal_error("Failed to activate OpenGL 3.3 rendering context.");
}
return gl33_context;
}
static LRESULT CALLBACK
window_callback(HWND window, UINT msg, WPARAM wparam, LPARAM lparam)
{
LRESULT result = 0;
switch (msg) {
case WM_CLOSE:
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
result = DefWindowProcA(window, msg, wparam, lparam);
break;
}
return result;
}
static HWND
create_window(HINSTANCE inst)
{
WNDCLASSA window_class = {
.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
.lpfnWndProc = window_callback,
.hInstance = inst,
.hCursor = LoadCursor(0, IDC_ARROW),
.hbrBackground = 0,
.lpszClassName = "WGL_fdjhsklf",
};
if (!RegisterClassA(&window_class)) {
fatal_error("Failed to register window.");
}
// Specify a desired width and height, then adjust the rect so the window's client area will be
// that size.
RECT rect = {
.right = 1024,
.bottom = 576,
};
DWORD window_style = WS_OVERLAPPEDWINDOW;
AdjustWindowRect(&rect, window_style, false);
HWND window = CreateWindowExA(
0,
window_class.lpszClassName,
"OpenGL",
window_style,
CW_USEDEFAULT,
CW_USEDEFAULT,
rect.right - rect.left,
rect.bottom - rect.top,
0,
0,
inst,
0);
if (!window) {
fatal_error("Failed to create window.");
}
return window;
}
int WINAPI
WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmd_line, int show)
{
HWND window = create_window(inst);
HDC gldc = GetDC(window);
HGLRC glrc = init_opengl(gldc);
ShowWindow(window, show);
UpdateWindow(window);
bool running = true;
while (running) {
MSG msg;
while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) {
running = false;
} else {
TranslateMessage(&msg);
DispatchMessageA(&msg);
}
}
glClearColor(1.0f, 0.5f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Do OpenGL rendering here
SwapBuffers(gldc);
}
return 0;
}
f5
не просто первично объявлена, она и определена по месту объявления дружественности. Ее имя является однозначно неквалифицированным в следствии своего определения.f5
не находится.f5(5);
буквально обречен на ошибку трансляции.f5
доступен только способ с повторным объявлением, т.к. она уже определена.f5
окончательно потеряет всякий смысл быть дружественной для A
и станет просто сильно спрятанной и сбивающей с толку глобальной функцией.f5
относительно A
не пользуется, т.к. среди ее параметров нет ни одного с типом A
.f5
, среди ее параметров обязан быть параметр и с типом A
.f5
, ее надо дополнительно объявить за пределами типа A
в его пространстве имен. num в данном случае представляет собой шестнадцатеричное число, при выводе оно переводится в десятичное число типа double.
If a program attempts to access the stored value of an object through a glvalue whose type is not similar to one of the following types the behavior is undefined:
(11.1) -- the dynamic type of the object,
(11.2) -- a type that is the signed or unsigned type corresponding to the dynamic type of the object, or
(11.3) -- a char, unsigned char, or std::byte type.
unsigned long long
тип double
не обладает ни одним из требуемых стандартом свойств. Отсюда прямой вывод что данный код вносит UB.char
, unsigned char
или std::byte
для C++17.union
. Этого действительно не стоит делать даже при условии того, что все современные трансляторы понимают такие конструкции так, как подозревает писатель.Почему неверно работает, к примеру, такой фрагмент кода:
unsigned long long
в тип double
. В этом случае производится именно стандартное преобразование типа, а не подмена с целью интерпретации представления памяти num
иным образом.0xC068C0C000000000
для типа unsigned long long
означает 13864543383726325760
, при преобразовании этого значения в тип double
значение будет преобразовано в 1.3864543383726326e+19
. В таком виде оно и будет выведено.unsigned long long num = 0xC068C0C000000000;
double representation = {};
memcpy( &representation, &num, sizeof( num ) );
cout << representation;
memcpy
тут. Данный код полностью соответствует стандарту и ожидаемо оптимизируется транслятором до желаемого результата.Мне не понятно, как работает такая конструкция, т.е зачем передавать именно ссылку на переменную num?
auto
и инициализацией списками, понимание цены отказа от std::tie
в пользу структурного связывания (не говоря уже о понимании семантики структурного связывания) и понимание рациональности перехода на структурное связывание в конкретном месте кода, понимание последствий использования автоматического выведения аргументов шаблона типа в месте вызова конструктора и возможные последствия применения этого подхода в конкретном участке кода, понимание семантики работы с this
в замыкании лямбды и отличия этого поведения в более старых стандартах. И так далее. template< typename... TArguments >
void Foo( const TArguments&... arguments )
{
static_assert( sizeof...( TArguments ) > 0, "This function shall not be called without arguments." );
}
glTexImage2D
.data
памяти меньше расчетного размера, который определяет функция исходя из параметров width, height, GL_RGBA, GL_UNSIGNED_BYTE
.glTexImage2D
параметры не соответствуют считанному изображению. А т.к. width
и height
получены напрямую при чтении изображения, вопросы возникать могут только относительно заявленного формата изображения - GL_RGBA, GL_UNSIGNED_BYTE
.Book
является агрегатным.auto
вместо явного указания типа переменной? Зачем писателю потребовалось дважды указать тип, заменив первое указание на auto
? Какова цель инициализации копией для данной переменной? Зачем писатель написал так сложно?entt::registry::view
.ColorComponent
, и этот цвет у тебя может интерполироваться. Значит тебе нужен компонент интерполируемости цвета - ColorInterpolationComponent
. В этом компоненте интерполируемости нужно определить закон интерполяции, начальное и конечное значение, а так же время интерполяции. ColorInterpolationComponent
говорит о том, что все данные ColorComponent
интерполируются по определенному закону между определенными значениями и за определенное время.***InterpolationComponent
у тебя будет своя система, которая делает выборку по целевому компоненту и по компоненту интерполируемости. Таким образом, уже на стадии выборки компонентов у тебя останутся только подходящие сущности, а все остальные - отфильтруются.int
.VkResult result
можно написать int result
в виду того, что перечисление VkResult
является нестрогим.C26812
[?] относится к разделу Enum.3 из C++ Core Guidelines [?] и напрямую указывает на место определения нестрогого перечисления. К строке VkResult result =
оно отношения не имеет.#pragma warning(push, 0)
[?] не делает того, что ты от него ожидаешь.#pragma warning( push )
#pragma warning( disable: 26812 )
#include <GLFW/glfw3.h>
#pragma warning( pop )
const
относится к правой половине спецификации типа до первого модификатора.const char&
- ссылка на константный символ. Квалификатор - const
, модификатор - &
.const char*
- указатель на память константного символа. Квалификатор - const
, модификатор - *
.const
относится ко всей части спецификации типа левее, включая все модификаторы.char* const
- константный указатель на память символа.const char* const
- константный указатель на память константного символа.char* const *
- указатель на память константного указателя на память символа.char& const
существовать не может, т.к. квалификаторы не применяется к ссылкам. Тут будет ошибка трансляции.constexpr
? Просто constexpr
всегда относится только ко всей спецификации типа со всеми модификаторами.const char*
- указатель на память константного символа.constexpr char*
- константный указатель времени компиляции на память символа. Тут нет ошибки, память символа тут считается модифицируемой.constexpr char*
получит характеристику ODR-used [?], то после трансляции кода это будет уже объект с типом char* const
. Вот так.const char[N]
, т.е. статически определенный массив константных символов. Такой тип можно привести только к типу const char*
.constexpr const char*
.const
в этом месте никакого прямого отношения к constexpr
не имеет. template< typename TComponent >
TComponent* const QueryComponent();
void PerformWorkflow( A*, B*, C* );
A
, B
и C
- это типы компонентов.template< typename... TComponents >
void ApplyComponents( void (*workflow)( TComponents*... ) )
{
workflow( QueryComponent<TComponents>()... );
}
each
и типа результата get
. Стоит напомнить что разыменование nullptr
, который может вернуть get
, приводит к неопределенному поведению [?].*QueryComponent<TComponents>()...
, т.е. на лету разыменовывать полученные указатели, то перед запуском функции для твоей entity
следует убедиться что у нее есть все необходимые компоненты. Такая проверка является необходимой для запуска систем в ECS, т.к. система должна обрабатывать только удовлетворяющие своим фильтрам сущности.template <typename... T>
void each(typename identity<std::function<void(T& ...)>>::type func)
{
for(auto &entity: m_entities)
{
if( !( (entity.HasComponent<T>() && ...) ) )
{
continue;
}
func( *entity.get<T>()... );
}
}
T()
может привести к вызову конструктора с пустым std::initializer_list
, к вызову конструктора по умолчанию, а может свестись к инициализации по умолчанию в том случае, если конструктор по умолчанию удален или по своим причинам пропущен в пользовательском типе.class
, т.е. имеет отношение только к сложным типам. Последний пункт описывает поведение инициализации для всех неописанных выше случаев как инициализацию нулем.T()
хорошо подходит для инициализации локальных временных объектов, а для инициализации именованных объектов лучше подходит форма T object {};
.T object = T();
, на самом деле, выполняет инициализацию копией, где в качестве копии используется локальный временный объект T()
.T()
или T{}
может привести к вызову конструктора по умолчанию или вызову конструктора от пустого std::initializer_list
для сложного типа и инициализацию нулем для простого типа.std::string string;
варианту std::string string = std::string();
потому что для std::string
инициализация по умолчанию сделает точно то же самое, будет эффективнее (т.к. во втором случае будет копирование) и будет понятнее.HRESULT res = HRESULT();
стоит заменить на инициализацию одним из базовых значений. Например - кодом S_OK
. Это создаст понимание базового состояния программы в месте определения переменной. Тип HRESULT
имеет очень сильную семантику и сразу о многом говорит подготовленному читателю.HWND window = HWND();
или HWND window{};
на самом деле смотрится к месту, т.к. в WinAPI нет предопределенного начального значения и объект проще провести через стандартную инициализацию значением. Уж этот краевой случай разработчики WinAPI должны предусмотреть. struct Vector3i final
{
int32_t x;
int32_t y;
int32_t z;
};
LineIterator
. Такое имя будет хорошо отражать функциональность.using AxisPointer = int32_t Vector3i::*;
class AxisProxy final
{
public:
AxisProxy( AxisPointer major_axis, AxisPointer middle_axis, AxisPointer minor_axis)
: m_major_axis{ major_axis }
, m_middle_axis{ middle_axis }
, m_minor_axis{ minor_axis }
{}
public:
inline int32_t& AccessMajorAxis( Vector3i& value ) const { return value.*m_major_axis; };
inline int32_t& AccessMiddleAxis( Vector3i& value ) const { return value.*m_middle_axis; };
inline int32_t& AccessMinorAxis( Vector3i& value ) const { return value.*m_minor_axis; };
inline const int32_t& AccessMajorAxis( const Vector3i& value ) const { return value.*m_major_axis; };
inline const int32_t& AccessMiddleAxis( const Vector3i& value ) const { return value.*m_middle_axis; };
inline const int32_t& AccessMinorAxis( const Vector3i& value ) const { return value.*m_minor_axis; };
private:
AxisPointer m_major_axis = &Vector3i::x;
AxisPointer m_middle_axis = &Vector3i::y;
AxisPointer m_minor_axis = &Vector3i::z;
};
class LineIterator final
{
public:
LineIterator( const Vector3i from, const Vector3i to );
public:
const Vector3i& operator ++ ();
const Vector3i operator ++ ( int );
inline const Vector3i& operator * () const { return m_current_point; };
inline const Vector3i* operator -> () const { return &m_current_point; };
private:
static inline const int32_t GetCorrectionStepAxis( const int32_t value ) { return std::abs( value ) << 1; };
static inline const int32_t GetShiftStepAxis( const int32_t value ) { return ( value > 0 ) - ( value < 0 ); };
void PerformLineStep();
private:
Vector3i m_current_point; // Current position at line.
Vector3i m_correction_step; // Values to change the point corrections.
Vector3i m_shift_step; // The shift step for current point in each iteration.
int32_t m_middle_axis_correction; // The marker for middle axis correction.
int32_t m_minor_axis_correction; // The marker for minor axis correction.
AxisProxy m_axis_proxy; // Point fields proxy.
};
const Vector3i& LineIterator::operator ++ ()
{
PerformLineStep();
return m_current_point;
}
const Vector3i LineIterator::operator ++ ( int )
{
Vector3i current_point{ m_current_point };
PerformLineStep();
return current_point;
}
LineIterator::LineIterator( const Vector3i from, const Vector3i to )
: m_current_point{ from }
{
const Vector3i line_delta{ to - from };
m_correction_step = { GetCorrectionStepAxis( line_delta.x ), GetCorrectionStepAxis( line_delta.y ), GetCorrectionStepAxis( line_delta.z ) };
m_shift_step = { GetShiftStepAxis( line_delta.x ), GetShiftStepAxis( line_delta.y ), GetShiftStepAxis( line_delta.z ) };
AxisPointer axis[3] = { &Vector3i::x, &Vector3i::y, &Vector3i::z };
std::sort(
std::begin( axis ), std::end( axis ),
[this]( const AxisPointer left, const AxisPointer right ) -> bool
{
return m_correction_step.*left > m_correction_step.*right;
}
);
m_axis_proxy = { axis[0], axis[1], axis[2] };
m_middle_axis_correction = m_axis_proxy.AccessMiddleAxis( m_correction_step ) - ( m_axis_proxy.AccessMajorAxis( m_correction_step ) >> 1 );
m_minor_axis_correction = m_axis_proxy.AccessMinorAxis( m_correction_step ) - ( m_axis_proxy.AccessMajorAxis( m_correction_step ) >> 1 );
}
m_correction_step
, на базе которого далее производится сортировка указателей на поля осей вектора. Сортируются указатели по убыванию шага их приращения. Именно таким образом определяется главное и вторичное направления. Порядок указателей осей используется для инициализации экземпляра AxisProxy
.void LineIterator::PerformLineStep()
{
if( m_middle_axis_correction > 0 )
{
m_middle_axis_correction -= m_axis_proxy.AccessMajorAxis( m_correction_step );
m_axis_proxy.AccessMiddleAxis( m_current_point ) += m_axis_proxy.AccessMiddleAxis( m_shift_step );
}
if( m_minor_axis_correction > 0 )
{
m_minor_axis_correction -= m_axis_proxy.AccessMajorAxis( m_correction_step );
m_axis_proxy.AccessMinorAxis( m_current_point ) += m_axis_proxy.AccessMinorAxis( m_shift_step );
}
m_middle_axis_correction += m_axis_proxy.AccessMiddleAxis( m_correction_step );
m_minor_axis_correction += m_axis_proxy.AccessMinorAxis( m_correction_step );
m_axis_proxy.AccessMajorAxis( m_current_point ) += m_axis_proxy.AccessMajorAxis( m_shift_step );
}
AxisProxy::m_middle_axis
из кода и удалить весь код, где на него есть ссылки, то весь оставшийся код будет представлять из себя обычный итератор линии для 2D-пространства. В этом случае оси даже сортировать не надо будет, для инициализации прокси можно будет обойтись одним тернарным оператором. ModelMatrix
- это матрица преобразования локального пространства геометрии конкретного объекта.ModelMatrix
.ViewMatrix
- матрица отображения любого внешнего пространства в пространство вида - пространство взора наблюдателя. Наблюдатель (камера) воспринимает объекты только внутри своего собственного локального пространства. Чтобы наблюдатель мог увидеть объект, объект необходимо отобразить из его родного пространства в пространство вида камеры, где он или попадет в область проекции, или не попадет.Projectionmatrix
- матрица отображения видового пространства в пространство проекции вида. Фактически это отображение выполняет проецирование некоторого участка видового пространства на плоскость. Именно отображенная таким образом геометрия становится видимой при презентации кадра.ModelMatrix
.ViewMatrix
.Projectionmatrix
.Projectionmatrix
и ViewMatrix
уникальны для камеры, а вот матрицы ModelMatrix
уникальны уже для каждого объекта отдельно.Projectionmatrix
и ViewMatrix
в процессе презентации кадра являются неизменными, затраты в 48 операций на каждую позицию выглядят как расточительство. Если до презентации выполнить отображение самого пространства вида в пространство проекции, т.е. перемножить ViewMatrix
и Projectionmatrix
в правильном порядке, то с помощью результирующей матрицы viewProjectionMatrix
число операций на одну позицию можно снизить до 32.viewProjectionMatrix
и ModelMatrix
в правильном порядке, то благодаря полученной таким образом MVPMatrix
число операций на одну позицию снизится до изначальных 16.viewProjectionMatrix
и MVPMatrix
просто позволяют снизить трудоемкость презентации кадра. Однако, во время презентации может потребоваться определение положения геометрии в каком-либо промежуточном пространстве, поэтому вдобавок к MVPMatrix
в шейдер принято отдавать и ModelMatrix
, и матрицы камеры.MVPMatrix
бесспорно нужна всегда, то любую другую матрицу в шейдер добавлять стоит только исходя из осмысленной необходимости. Регистры GPU для шейдера не резиновые и могут очень быстро забиться избыточными данными. К тому же, чем меньше на каждом кадре в шейдер передается данных, тем быстрее выполняется презентация кадра.