Всё что написано это так сказать последовательность моих действий. Мне самому будет потом интересно всё пересмотреть, систематизировать, переделать в более читаемый вид.
В данный момент пока двигаюсь в аудио. Делаю свой аудио движок. Конвертирование форматов, изменение samplerate. 3д звук сделан (на сойдёт). Есть что рассказать по этой теме. Хотелось бы реализовать реверберацию, но пока надо начать с delay. Надеюсь работать с FFT не потребуется. Молюсь чтобы не понадобилось. Хотя, вот если сделать такие эффекты как например в Half-Life, там же офигенная реверберация. Неужели это всё FFT и прочая высшая математика? Если да то придётся раскошелится на профи. 50000-100000 заплачу за реализацию, я считаю оно того стоит. Ну или сам всё изучу. "Do you think we can fly? Well, I do."
Нужно где-то хранить размеры, например, ширину и высоту. И ещё надо хранить 4 значения обозначающих прямоугольную область. Нужны
Код
bqPoint bqRect
Куда их сунуть? Где написать? Добавлю новый .h файл inc/badcoiq/common/bqBasicTypes.h Напишу туда.
Код
template<typename T> class bqPoint_t { public: bqPoint_t() {} bqPoint_t(T X, T Y) : x(X), y(Y) {}
T x = 0; T y = 0; };
using bqPoint = bqPoint_t<int32_t>; using bqPointf = bqPoint_t<float>;
template<typename T> class bqRect_t { public: bqRect_t() {} bqRect_t(T Left, T Top, T Right, T Bottom) : left(Left), top(Top), right(Right), bottom(Bottom) {}
T left = 0; T top = 0; T right = 0; T bottom = 0; };
using bqRect = bqRect_t<int32_t>; using bqRectf = bqRect_t<float>;
Окно
При создании окна ничего указывать не надо. Просто Призовём окно. Все настройки будем делать методами. Окну нужен коллбэк, оно будет вызывать методы когда с окном что-то случиться.
inc/badcoiq/system/bqWindow.h
Код
// Когда с окном что-то происходит, оно будет вызывать эти коллбэки // При создании окна мы обязаны послать указатель на коллбэк class bqWindowCallback { public: bqWindowCallback() {} virtual ~bqWindowCallback() {}
// Когда переключились к этому окну virtual void OnActivate(bqWindow*) {} // Когда переключились к другому окну virtual void OnDeactivate(bqWindow*) {}
// После изменении размера virtual void OnSize(bqWindow*) {} // Пока изменяем размер virtual void OnSizing(bqWindow*) {} // when resizing
// Свернули окно virtual void OnMinimize(bqWindow*) {} // Развернули на весь монитор virtual void OnMaximize(bqWindow*) {} // Восстановили (из Maximized и Minimized в обычное) virtual void OnRestore(bqWindow*) {}
// Когда окно перерисовывается virtual void OnDraw(bqWindow*) {}
// Когда двигается virtual void OnMove(bqWindow*) {}
// Когда кликается кнопка закрытия virtual void OnClose(bqWindow*) {} };
Окна для разных ОС имеют что-то общее. Нужно сунуть общие данные в структуру.
Код
struct bqWindowCommonData { // тут хранится указатель на коллбэк bqWindowCallback* m_cb = 0;
// размер рамки bqPoint m_borderSize;
// минимальный размер окна bqPoint m_sizeMinimum;
// текущий размер окна (клиентской области) bqPoint m_sizeCurrent;
// окно видимо или нет bool m_isVisible = false;
// данные специфичные для ОС void* m_implementation = 0; };
Данные специфичные для ОС могут понадобиться. Надо добавить ещё одну структуру, которая видна только для Windows платформы.
Надо добавить отдельный .h файл, для Windows данных, так как нужно подключать windows.h, а лишний раз это делать не нужно (я за скорость компиляции). inc/badcoiq/system/bqWindowWin32.h
// Установить заголовок void SetTitle(const char*);
// Скрыть\показать void SetVisible(bool v);
// Узнать, видимо ли окно. // Можно скипать рисование если окно например свёрнуто. bool IsVisible() { return m_data.m_isVisible; }
// Развернуть в полный экран void Maximize(); // Свернуть void Minimize(); // Восстановить оконное состояние (ну типа оконное, не полный экран) void Restore();
// Тут, указатели для того чтобы получить их и сохранить где-то, и // не вызывать эти методы каждый раз, так как в таком случае потребуется // вызывать постоянно. // Получить минимальный размер окна. bqPoint* GetSizeMinimum() { return &m_data.m_sizeMinimum; } // Получить размер рамки bqPoint* GetBorderSize() { return &m_data.m_borderSize; }
// Получить данные bqWindowCommonData* GetData() { return &m_data; }
// Установить позицию и размер void SetPositionAndSize(int x, int y, int sx, int sy); };
void bqWindow::SetPositionAndSize(int x, int y, int sx, int sy) { BQ_ASSERT_ST(m_data.m_implementation); #ifdef BQ_PLATFORM_WINDOWS bqWindowWin32* w32 = (bqWindowWin32*)m_data.m_implementation;
Добавлено (06 Июля 2023, 18:45) --------------------------------------------- Delta time Вычисление Delta Time реализую в bqFramework::Update
Код
static clock_t then = 0; clock_t now = clock();
g_framework->m_deltaTime = (float)(now - then) / CLOCKS_PER_SEC; then = now;
для clock_t надо подключить <time.h>
User Data Бывает объектам нужно добавить поля GetUserData SetUserData Добавлю базовый класс.
inc/badcoiq/common/bqUserData.h
Код
// Если для какого-то класса нужен user data, то можно наследовать этот класс class bqUserData { void* m_data = 0; public: bqUserData() {} virtual ~bqUserData() {}
Добавлено (07 Июля 2023, 22:40) --------------------------------------------- Математика
Хочу использовать заранее вычисленные значения для sin cos atan2 и т.д. Нецелесообразно вычислять большой диапазон. Функции принимают значения например от -1 до +1, от -Пи до +Пи и т.д.
// Expect value from -PI to +PI // It will do this // if (v > 3.141f) v = 3.141f; // if (v < -3.141f) v = -3.141f; static float Cos(float); static double Cos(double); static float Sin(float); static double Sin(double); static float Tan(float); static double Tan(double);
// wikipedia: // Imprecise method, which does not guarantee v = v1 when t = 1, due to floating-point arithmetic error. // This method is monotonic. This form may be used when the hardware has a native fused multiply-add instruction. // // типа, Lerp1 не гарантирует что x станет равным y (...Lerp2 тоже) // ... имеет 1 умножение. Lerp2 имеет 2 умножения. static float Lerp1(float x, float y, float t); static double Lerp1(double x, double y, double t);
// wikipedia: // Precise method, which guarantees v = v1 when t = 1. This method is monotonic only when v0 * v1 < 0. // Lerping between same values might not produce the same value static float Lerp2(float x, float y, float t); static double Lerp2(double x, double y, double t);
// Находится ли точка в прямоугольной области static bool PointInRect(const bqPoint&, const bqRect&); };
Опять класс со статическими методами(...очень удобно). Постепенно будут добавлены множество других методов когда появятся вектор, матрица, кватернион.
template<typename T2> bqVec4_t<T2> operator*(T2 v)const { bqVec4_t<T2> r; r.x = x * v; r.y = y * v; r.z = z * v; r.w = w * v; return r; }
template<typename T2> bqVec4_t<T2> operator+(const bqVec4_t<T2>& v)const { bqVec4_t<T2> r; r.x = x + v.x; r.y = y + v.y; r.z = z + v.z; r.w = w + v.w; return r; } ... T x = static_cast<T>(0); T y = static_cast<T>(0); T z = static_cast<T>(0); T w = static_cast<T>(0); T* Data() { return &x; } };
В bqForward.h добавил
Код
template<typename T> class bqVec2_t; template<typename T> class bqVec3_t; template<typename T> class bqVec4_t; template<typename T> class bqMatrix4_t;
using bqVec2 = bqVec2_t<bqReal>; using bqVec2f = bqVec2_t<float>; using bqVec2i = bqVec2_t<int32_t>; using bqVec3 = bqVec3_t<bqReal>; using bqVec3f = bqVec3_t<float>; using bqVec3i = bqVec3_t<int32_t>; using bqVec4 = bqVec4_t<bqReal>; using bqVec4f = bqVec4_t<float>; using bqVec4i = bqVec4_t<int32_t>; using bqMat4 = bqMatrix4_t<bqReal>;
так-же некоторые функции для векторов добавил в класс bqMath, чтобы не нагружать bqVector.h
Матрица Матрица для 3D мира это четыре 4D вектора
Код
vec4 matrix[4];
Ещё называют матрица 4на4
Есть матрица 3на3 Матрица 3на3 является как бы частью матрицы 4на4 Та часть что 3 на 3 это базис. Он отвечает за вращение. Раньше делали игры на матрицах 3на3. ХЗ как сейчас, везде 4 на 4. Необходима позиция, например, объект на сцене перемещается изменяя позицию. В матрице 4на4 указывается эта позиция. А при использовании матрицы 3на3 видимо посылали позицию отдельно, я честно не изучал это.
и в памяти он должен занимать это так-же, вот почему вектр должен НЕ содержать ничего лишнего.
Вот эта часть (помечено единичками) отвечает за положение точки в пространстве, ориентация, всмысле вращение относительно центра, так-же там находится так называемый масштаб
Если поставить 2 то моделька увеличиться\координата сдвинеться. Если 0.5 то уменьшится\сдвинеться в обратную сторону. Если там будут нули и мы рисуем модель то модельки не будет видно.
В углу должна стоять единица, для математической магии.
Matrix4x4 Position = Matrix4x4( 0,0,0,0, 0,0,0,0, 0,0,0,0, x,y,z,1 );
Matrix4x4 World = Position * Rotation * Scale; // справа на лево - масштабируем, вращаем, и устанавливаем на позицию.
Матрица нужна для преобразования 3D координаты... например позиция [132.25][2556.23][1.52] ...в 2D координаты куда идёт рисование (напрямую на экран, или в текстуру)...назавём "холст" а координатная система холста от -1 до +1
Позиция умножается на матрицу - получается нужное значение.
Нужно создать удобный класс.
Код
// кратко template<typename T> class bqMatrix4_t { public: bqMatrix4_t() { Identity(); }
Что такое кватернион не объяснить, ибо это высшая математика, комплексное число. Читайте википедию.
Простыми словами, кватернион хранит вращение, наверно более корректно - ориентацию. Указываем углы, получаем кватернион, преобразуем в матрицу вращения. Вращать объект кватернионом можно в любое направление. Главное я покажу в будущем, во "Вращение объектов"
Добавлено (07 Июля 2023, 22:49) --------------------------------------------- Кватернионы можно интерполировать. Обычно такая функция называется Slerp
на форуме тупизм, невозможно продолжить публиковать сообщения пока кто-то другой не напишет статейки пишутся в текстовый документ, будут опубликованы позже, тут или где-то ещё.
Игрострой это не только сюжет, дизайн и т.д. Это ещё и технологии, то что скрыто под капотом.
Ну вот если вспомнить детство, во что играли, денди, сега, кто-то играл на более древних консолях. Я лично всегда задавался вопросом "как сделано то, как сделано это".
Когда ковыряешься в этих вещах, приходят мысли "с этой вещью можно сделать то-то". Изучаешь всё это, и рамки, границы возможностей расширяются.
Моя граница упёрлась в физику. Всё сделано по списку, осталось только она. Я хочу переписать всё, документируя что возможно, и написать свою физику. И я опишу начальные шаги, и все свои ошибки, и 100% всё сделаю, с оптимизациями. Книжку прочитал, оказывается это элементарщина. Будет интересно реализовать.
Как только будет реализовано (всё по списку), буду делать игрульку. Ресурсы есть. Главное не напрягаться и заниматься тем что нравится. "Do you think we can fly? Well, I do."
Все те действия что были описаны выше НЕОБХОДИМЫ. Ошибки надо показывать, для этого нужна своя строка (ибо херня из STL однажды может дать 0, где нибудь в конвертации юникода). Строку нужно написать чтобы удовлетворяла, например чтобы тупо можно было добавить число. Для строки нужны функции для работы с памятью.
Это минимум который должен быть.
Перед тем как продолжить, надо добавить ещё один .h файл inc/badcoiq/common/bqForward.h
Там будут forward declarations.
Код
class bqWindow; class bqWindowCallback;
bqForward.h так-же должен быть указан в badcoiq.h
Что делает forward declarations? Если мы делаем класс и в нём есть объект
Код
class CClass{ ... MyObject m_object; };
То компилятору нужно знать сколько памяти выделить для этого объекта. Компилятор должен видеть описание MyObject. Можно вставить #include, но в итоге время компиляции увеличится. Давно я делал ДВИЖЁК где все include были в одном файле. Это плохой подход.
Нужно использовать указатель.
Код
class MyObject; //forward declaration class CClass{ ... MyObject* m_object = 0; };
Компилятор знает размер указателя и проблем не будет. Нужно всего лишь вставить forward declaration - добавить class ИмяКласса.
В будущем все классы которые не будут входить в минимальный набор (указанный в badcoiq.h) будут добавляться в forward declaration.
Теперь ДВИЖЁК
Framework
Будет внутренний класс, в котором будут находиться все объекты необходимые для работы ДВИЖКА. Управление фреймворком будет происходить через класс со статическими методами
+ inc/badcoiq/framework/bqFramework.h
Код
// Предполагаю что фреймворк должен посылать какие-то сообщения. // Я это ещё не реализовал class bqFrameworkCallback { public: bqFrameworkCallback() {} virtual ~bqFrameworkCallback() {}
virtual void OnMessage() = 0; };
// API для фреймворка. class bqFramework { public: static void Start(bqFrameworkCallback*); static void Stop(); static void Update();
Когда нам нужно запустить ДВИЖЁК, вызываем Start. Он выделит память для внутреннего класса. Stop для завершения работы, он освободит ту память. Update - например, он вычислит DeltaTime, обновит состояния ввода, GUI, окна. Delta Time - время между двумя вызывами Update. Update надо вызывать каждую итерацию главного цикла. Соответственно, это время затраченное на одну итерацию(так-же можно сказать время на рисование 1го кадра). SummonWindow - создать окно. Имя CreateWindow занято WinAPI, как и многие другие. Я решил использовать слово Summon(призвать) вместо Create(создать).
Теперь реализация. src/badcoiq/framework/bqFramework.cpp
Класс который будет хранить всё необходимое.
Код
class bqFrameworkImpl { public: bqFrameworkImpl() {} ~bqFrameworkImpl() {}
Есть шанс что Stop не будет вызван. Надо добавить класс который при закрытии программы в деструкторе вызовет Stop
Код
class bqFrameworkDestroyer { public: bqFrameworkDestroyer() {} ~bqFrameworkDestroyer() { if (g_framework) bqFramework::Stop(); } }; // При закрытии программы этот объект будет уничтожен - будет вызван деструктор // в котором будет вызван bqFramework::Stop(); bqFrameworkDestroyer g_frameworkDestroyer;
Ну вот и всё. Далее необходимо создать окно, и реализовать Update
В своём assert я хотел иметь возможность использовать дефолтную Visual Studio функцию _wassert, но она требует wchar_t строку. Когда-то я замарочился и сделал 100% рабочий конвертер UTF-8\16\32 Я подумал и решил, что не нужен мне сам конвертер. Мне нужна UTF-32 строка. Эта строка должна иметь методы для конвертации.
Эту строку лучше использовать для текста который мы будем рисовать.
Для внутреннего использования необходимы более простые строки. Начну с них.
Полностью описывать классы будет очень долго. В коде есть комментарии. В общем всё элементарно. Можно объяснить основные принципы.
Класс строки содержит массив символов + 1 символ для завершающего нуля. При создании объекта надо заранее выделить немного памяти. При добавлении символов, если необходимо, перевыделяем память.
Главное реализовать функцию push_back. Все остальные функции (append assign operator=) и конструкторы в итоге будут вызывать push_back.
Код
template<typename other_type> bqString_base(const other_type* str) { _reallocate(m_allocated); assign(str); // просто присвоение } ... // присвоить содержимое другой строки template<typename other_type> void assign(other_type str) { clear(); if (str) append(str); } ... template<typename other_type> void append(const other_type* str) { append(str, bqStringLen(str)); } ... // добавить в конец template<typename other_type> void append(const other_type* str, size_t l) { for (size_t i = 0; i < l; ++i) { push_back((char_type)str[i]); } } ... // добавить в конец символ void push_back(char_type c) { size_t new_size = m_size + 1u; if ((new_size + 1u) > m_allocated) _reallocate((new_size + 1u) + (1 + (uint32_t)(m_size * 0.5f))); m_data[m_size] = c; m_size = new_size; m_data[m_size] = 0; }
Очень необходимо реализовать добавление чисел. Например float:
Код
void append(float f) { char buf[32]; int nob = sprintf_s(buf, 32, "%.4f", f); if (nob) { for (int i = 0; i < nob; ++i) { push_back((char_type)buf[i]); } } }
Полный код шаблонной строки не влезает в сообщение. Реализовано куча полезных методов.
Код
// Функция чтобы получить длинну строки template<typename other_type> size_t bqStringLen(const other_type* str) { // Считаем пока не встретится ноль unsigned int len = 0u; if (str[0] != 0) { const other_type* p = &str[0u]; while ((size_t)*p++) ++len; } return len; }
// Традиционный строковый класс template<typename char_type> class bqString_base { char_type* m_data = 0; // массив size_t m_allocated = 10; // количество символов которое влезает в m_data учитывая завершающий ноль size_t m_size = 0; // текущее количество символов
// если количество символов m_size равно или больше m_allocated то надо вызвать это // указав новый m_allocated void _reallocate(size_t new_allocated) { // выделение новой памяти, копирование содержимого, освобождение старой, сохранение нового указателя char_type* new_data = (char_type*)bqMemory::malloc(new_allocated * sizeof(char_type)); if (m_data) { memcpy(new_data, m_data, m_size * sizeof(char_type)); bqMemory::free(m_data); } else { memset(new_data, 0, new_allocated); } m_data = new_data; m_allocated = new_allocated; }
public: // в конструкторах надо вызвать _reallocate() чтобы инициализировать массив m_data bqString_base() { _reallocate(m_allocated); }
Для конвертации в UTF-8 и UTF-16 LE я использую таблицу, заранее вычисленную.
Код
struct slUnicodeCharNode { uint32_t m_utf8; // код для UTF-8 uint32_t m_utf16;// код для UTF-16 }; static slUnicodeCharNode g_UnicodeChars[0x32000] = { #include "UnicodeChars.inl" // коды здесь };
Добавление char* и wchar_t* строк. По сути конвертация UTF-8 и UTF-16 в UTF-32
Код
// UTF-8 to UTF-32 void bqString::append(const char* str) { size_t len = bqStringLen(str);
// для to_utf8 to_utf16. union UC { uint8_t bytes[4]; uint16_t shorts[2]; uint32_t integer; }; ... void bqString::to_utf8(bqStringA& stra) const { stra.clear(); UC uc; auto ut = &g_UnicodeChars[0]; for (size_t i = 0; i < m_size; ++i) { char32_t c = m_data[i]; if (c >= 0x32000) c = '?';
uc.integer = ut[c].m_utf8;
if (uc.bytes[3]) stra.push_back(uc.bytes[3]); if (uc.bytes[2]) stra.push_back(uc.bytes[2]); if (uc.bytes[1]) stra.push_back(uc.bytes[1]); if (uc.bytes[0]) stra.push_back(uc.bytes[0]); } }
void bqString::to_utf16(bqStringW& strw) const { strw.clear(); UC uc; auto ut = &g_UnicodeChars[0]; for (size_t i = 0; i < m_size; ++i) { char32_t c = m_data[i]; if (c >= 0x32000) c = '?';
uc.integer = ut[c].m_utf16;
if (uc.shorts[1]) strw.push_back(uc.shorts[1]); if (uc.shorts[0]) strw.push_back(uc.shorts[0]); } }
Полный код не влезает в сообщение. В общем-то там много дублирования простой строки. Ещё есть дополнительные методы типа to_upper.
Почему-то корректно работает только со статическими библиотеками.
inc/badcoiq/system/bqStacktracer.h
Код
class bqStackTracer { public: static void Print(); };
Реализация src/badcoiq/system/bqStacktracer.cpp
Код взят из Havok Постарался чуть изменить. В общем ничего особенного нет в плане авторского права, одни вызовы системных функций.
Грузим системные библиотеки, получаем указатели на функции, вызываем эти функции. Детали слишком специфичны для ОС, не буду их описывать. Если почитать MSDN то всё станет понятно.
int bqStackTracerImpl::GetStackTrace(size_t* trace, int maxtrace, int framesToSkip) { _init(); if (m_RtlCaptureStackBackTrace) return m_RtlCaptureStackBackTrace(framesToSkip, maxtrace, (PVOID*)trace, NULL); return 0; }
Сама реализация находится в отдельном классе (bqStackTracerImpl). Пока чисто Windows версия. Имеем глобальный объект (g_stackTracer). Статический метод(bqStackTracer::Print) использует его.
Можно создать тестовую программу:
Код
// не забыть указать inc и libs папки в параметрах проекта #include "badcoiq.h"
Добавлено (02 Июля 2023, 19:25) --------------------------------------------- Logging
Надо выводить текстовую информацию, ошибки и предупреждения. Нужно сделать так чтобы можно было указать свою функцию для вывода. Например, чтобы выводить в файл, или в игровую консоль.
+ inc/badcoiq/common/bqLog.h
Код
// Класс для вывода текстового сообщения // Можно установить коллбэки class bqLog { public: static void Print(const char* s, ...); static void Print(const wchar_t* s, ...);
Добавлю файл, который будет подключать стандартные файлы, и в котором будут макросы. Что такое макрос и для чего они нужны.
Макросы это то что создаётся используя слово define
Код
#define ABC
Они могут быть в виде функций.
Их используют чтобы добиться мультиплатформенного кода, для краткости написания кода и для много чего. Вставляешь макрос, и на его месте будет то что было справа от него.
Возможно ненужная вещь. Пережиток прошлого и всё такое. Многие библиотеки используют это. `inline` не гарантирует что функция будет inline. Предположу что "не гарантирует" если функция написана в .c .cpp файлах(или файл инклюдится в них), ведь такие функции не встраиваемые, по умолчанию. Вместо inline теперь надо писать BQ_FORCEINLINE
Добавлено (02 Июля 2023, 09:14) --------------------------------------------- Память
Я всегда делал DLL библиотеки, и требовалось чтобы выделение и освобождение памяти происходило в одном месте. Если в DLL память была выделена используя new, а в EXE она будет delete, то может так случиться что эти new и delete будут из разных мест (различные Run Time). От этого произойдёт ошибка и программа упадёт. Решение проблемы - создать место которое будет выделять\освобождать память.
Возможно для статических библиотек это не проблема. Но я планирую использовать .dll как плагины. Поэтому надо это создать и использовать только эти функции.
#ifdef BQ_PLATFORM_WINDOWS #define WIN32_LEAN_AND_MEAN #include <Windows.h> // Windows функции для работы с памятью требуют Process Heap // Ничего страшного в глобальной переменной нет. static HANDLE g_processHeap = GetProcessHeap(); #endif
Данные функции неудобно использовать для замены new и delete Потому что для объектов классов нужно вызывать конструктор и деструктор. Добавлю две функции которые будут выделять\освобождать память и вызывать конструктор\деструктор.
Добавлено (02 Июля 2023, 11:22) --------------------------------------------- DLL
Изначально был план дать возможность использовать библиотеку как DLL. Добавил функции для работы с DLL. Потом отказался от поддержки DLL версии, так как много возни. Но сами функции пригодятся в будущем.
Новый include файл
inc/badcoiq/system/bqDLL.h
Подобно bqMemory, это класс со статическими методами.
Код
// надо завести свои имена для хэндла и указателя на функцию typedef void* bqDLLHandle; // HMODULE в Windows typedef void* bqDLLFunction;
class bqDLL { public:
// Загрузить динамическую библиотеку // Вернётся указатель если загрузилось. // При завершении работы надо вызвать free и передать bqDLLHandle static bqDLLHandle load(const char* libraryName);
// Получить функцию из библиотеки // Вернёт NULL если была какая-то ошибка. static bqDLLFunction get_proc(bqDLLHandle library, const char* functionName); };
Если ошибка произойдёт, то в функцию передадутся выражение используемое в if (#expression), имя файла и строка где находится BQ_ASSERT.
assert работает только в debug режиме. Его нужно добавлять в код везде где можно, в релизной версии там будет пустота.
BQ_ASSERT вызвет функцию bq::internal::OnAssert() Эта функция должна быть реализована в библиотеке. Она должна использовать указатель на функцию - дефолтная OnAssert Дефолтную функцию можно изменить, послав свой указатель функцией SetOnAssert. ...этот код должен быть выше макросов BQ_ASSERT
Код
// Скрою в неймспейсах вспомогательные функции namespace bq { namespace internal { enum flag_onAssert { // использовать системное окно которое показывается как при использовании assert() flag_onAssert_useDefaultSystemWindow = 0x1,
// использовать stack tracer flag_onAssert_useStackTrace = 0x2, };
Что необходимо сделать чтобы получился ДВИЖОК? Сначала надо освоить создание библиотек. Как только получится вызвать функцию из библиотеки, сразу придёт понимание что такое этот ваш ДВИЖОК - это просто библиотека, или их набор.
Библиотеки могут быть динамическими или статическими. Динамическую библиотеку можно использовать во множестве языков программирования. Она имеет вид отдельного файла.
Статические библиотеки это набор кодов, которые становятся частью программы, они вшиваются в исполняемый файл. Эти библиотеки нельзя использовать в иных ЯП. Наверно.
Здесь, я буду использовать статические библиотеки. Почему: - всё быстро компилируется - их проще подключить (для Visual Studio)
Минус статической библиотеки в том что нужно использовать конкретную версию компилятора, это касается VS, на GCC их не писал. Поэтому при начале работы, лучше скомпилировать библиотеки тем компилятором что есть.
Мысли о коде Я не имею опыт современного C++. Даже не знаю есть ли C++23 и далее, и вообще что там было после C++17, даже не интересно. Что-то увижу полезное, приму. Но никаких концептов, модулей, жёсткого STL и т.д.
* Запрещено использовать STL в API. Необходимо написать свои вещи.
* Общий стиль кода должен быть простым в стиле СИ. Имена функций и пользовательских типов должны иметь префикс, в моём случае я буду использовать `bq`. Сишные префиксы гораздо удобнее использования namespace.
* Вообще namespace даёт кучу неудобностей, поэтому запрещено использовать его в API. Допускается его использование для внутренних вещей.
Код
namespace bq { namespace internal { } }
* Фигурные скобки тел функций и пользовательских типов должны быть на уникальной строке. * Тела if for и прочих могут находится на той-же строке что и инструкция.
* Имена для API нужно писать в CamelCase. Код проще редактировать, не нужно тратить время на ввод симола `_` Получается, имена будут выглядить так: bqPolygonMesh
* Использование имён_такого_типа допускается только в классах которые схожи с классами в STL.
* В коде лучше использовать сишные\сырые массивы. Ненужно использовать динамические если без них не обойтись.
* Допускается использование макросов, но не сложных. Например, запрещено использовать макросы для генерации функций
* Использовать строки char* в API или свой класс bqString. Использовать wchar_t* для открытия файла не имеет смысла. char* работает подобно UTF-8.
* Использование uint8_t и подобных имён для стандартных типов.
* Запрещено кидать исключения.
* В пользовательских типах, имена переменных должны иметь префикс 'm_', имена открытых методов должны начинаться с Большой буквы, имена закрытых методов должны начинаться с символа `_`.
* Наследование используется только для классов.
Мысль об архитектуре Не нужно писать супер класс который всё сможет сделать. Лучше написать простой класс, потом поверх него чуть сложнее, потом ещё сложнее если надо. Типа слой за слоем. И эти сложные слои должны находиться на стороне программы. Например, работа со сценой, рисование теней и т.д. Всё должно делаться на стороне программы.
Создаваемые вещи должны быть максимально независимыми. Если делаем рендер с уникальными технологиями, то более простые рендеры всё равно должны работать.
Шаблоны проектирования под сомнением. Их не будет до той поры пока я не осознаю их пользу.
Внутри основной библиотеки будет главный класс Framework. Он будет содержать все необходимые вещи, будет инициализировать библиотеки, иметь загрузчики ресурсов и т.д. Для простоты будет глобальный указатель на объект данного класса. Простая глобальная переменная. И ненужны никакие заумные вещи.
В общем, главное простота, а не выпендрёж.
Для простых вещей будут использоваться классы со статическими методами.
Структура проекта Я использую Visual Studio 2022. Буду использовать его возможности.
Папки: `build` - там лежат файлы проектов `inc` - #include файлы движка `libs` - туда прилетят скомпилированные библиотеки `src` - тут лежат исходные коды движка и сторонних библиотек.
По ходу прогресса появятся папки `data` - ресурсы для демки `bin32\bin64` - демка и инструмены `tools` - исходные коды для инструментов
Создание проекта Visual Studio Я всегда создаю пустые (Empty) проекты, и настраиваю их. Некоторые проекты я просто копирую в проводнике, редактирую в Notepad и кидаю в Visual Studio.
После создания проекта, надо добавить какой нибудь файл с исходным кодом. В папке 'src' надо создать папку для исходного кода движка. Далее создать текстовый файл и переименовать его установив расширение .cpp Далее кидаем этот файл в Visual Studio.
Это надо сделать, потому что Visual Studio не показывает C/C++ параметры если в проекте нет файла с исходным кодом.
Содержимое данного файла
Код
/* BSD 2-Clause License
Copyright (c) 2023, badcoiq All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
#include "badcoiq.h"
В каждом файле (.h .c .c++ .cxx и т.д.) должна быть указана лицензия. Так-же практически в каждом файле (.c .cpp) потребуется минимальный набор include файлов. Все эти файлы указаны в `badcoiq.h`
Надо создать badcoiq.h Он будет находится в папке `inc`. Просто копирую badcoiq.cpp и изменю расширение.
Теперь настрою параметры проекта.
Параметр Target Name. В результате имя библиотеки получиться типа такое
badcoiq_v143_x86_Debug.lib
Укажу include папку
Для Release укажу оптимизацию
Можно изменить Floating Point Model Fast даст чуть скорости.
Указываю NODEFAULTLIB Возможно они и так будут игнориться, но укажу на всякий случай.
Всё, настройка завершена.
Далее нажимаю в редакторе правой кнопкой по badcoiq.h и выбираю Go To Document Для быстроты я привык нажимать просто Ctrl+Shift+G
Откроется badcoiq.h и можно приступить к его редактированию.
В данном моменте там должна быть лицензия и include guard
Код
#pragma once #ifndef __BQ_BADCOIQ_H__ #define __BQ_BADCOIQ_H__
#endif
Указываю #pragma once тоже, первее include guard
Всё, можно строить.
В папке libs будут такие файлы
В Visual Studio можно включить кнопку для построения. Правой кнопкой по GUI и выбираем Build. Добавлю кнопку Compile
Осталось отредактировать .gitignore и сделать коммит
Хочу написать библиотеку которую буду использовать как для игр так и для простого и не очень ПО. В данной теме буду публиковать детальное описание своим действиям.
Главная цель это показать действия тем людям кто хочет написать своё и не знает с чего начать. Как говорят, кто ничего в жизни не добился, идут в учителя. Но тем не менее я считаю себя достаточно опытным.
Всё уже написано по миллиону раз. Это будет финальная итерация.
Код и архитектура не претендует на эталон. Но я напишу определённые моменты к которым я пришёл, и я считаю их истинно верными.
Какой результат я ожидаю? Хочу сделать минимальный набор функций и набор редакторов (написанные на этой библиотеке). Именно набор а не одну единственную программу всё-в-одном. Графика будет в последнюю очередь, я её даже не включил в список дел.
Ниже будут ссылки на сообщения в данной теме. В каждом сообщении будет находится ссылка на репозиторий той версии когда было закончено написание сообщения. Всё написано последовательно, от старого к новому. Если в списке чего-то нет, это не значит что этого не будет вообще.
* Начало * Макросы, память, DLL, assert * String, Stacktracer, Log * Framework * Окно, Delta time * Ввод * Математика * Вектр * UID * Графическая Система * D3D11 * Цвет * Матрица * Кватернион * Рисование 3D линии * Камера * AABB * Луч * 3D Mesh * Mesh creator * Массив * Список * Загрузка OBJ * GPU Mesh * Image * Загрузка BMP * Текстура * Загрузка из архива * Загрузка PNG * Загрузка JPG * Загрузка TGA * Render Target Texture * GUI: шейдер * GUI: рисование прямоугольника * GUI: шрифт * GUI: текст * GUI: база для GUI элементов * GUI: GUI окно * GUI: кнопка * GUI: выравнивание * GUI: текст для кнопки * GUI: дефолтный шрифт * GUI: скроллинг * GUI: checkbox и radio button * GUI: редактор текста * GUI: listbox * GUI: slider * Нормальная демо программа * Ray-triangle intersection * Спрайты * Билборды * Камера для редактора * Текст в 3D * Генерация куба * Генерация сферы * Генерация UV (planar mapping) * Frustum culling * Вращение объектов * Occlusion culling * Скелетная анимация - основа * Чтение SMD * Скелетная анимация - проигрывание анимации * Физика * Аудио * Ландшафт * ???
Необходимо время для того чтобы я написал Начало... "Do you think we can fly? Well, I do."
Сообщение отредактировал BADCOIQ - Понедельник, 27 Мая 2024, 22:25
Матрица модели(точнее объекта который визуализирует модель) (так называемая мировая матрица) это есть матрица перемещения * матрица вращения * матрица масштаба, и её надо обновлять постоянно как что-то изменилось (изменили масштаб, ориентацию или позицию).
На CPU нужно вычислить WVP, потому что на GPU эти вычисления будут идти на каждую вершину, а вершин очень много.
Код
Matrix4 W = GetWorldMatrix(); Matrix4 V = GetViewMatrix(); Matrix4 P = GetProjectionMatrix(); Matrix4 WVP = P * V * W; посылаем в шейдер WVP
Шейдер
Код
#version 330 core layout(location = 0) in vec3 lay_position; void main(){ gl_Position = WVP * vec4( lay_position, 1.0f ); // перемещаем текущую вершину на нужную позицию }
"Do you think we can fly? Well, I do."
Сообщение отредактировал BADCOIQ - Четверг, 26 Июля 2018, 16:17
Как так делают? Видел в unity, что в редакторе можно запустить проект игры и играть в самом редакторе, то же самое видел и unreal engine, как такое можно сделать, не пойму?
Представь, в редакторе, есть камера, через которую человек видит виртуальный мир. Есть другие объекты на сцене, дорога, стена. У всех объектов есть параметры, позиция, физические параметры. Что мешает сделать кнопку, по нажатии на которую, инициализировался бы физический движок, активировалась нужная камера, включались бы нужные кнопки ввода? Все данные о виртуальном мире сохраняются, скрипты доступны и они как бы переходят в рабочий режим.
Если часть игры вынесена в другие модули (.dll .so), то для правильной работы создают общие методы, интерфейсы. А значит, каждый модуль должен реализовывать их (например, инициализация, завершение работы).
Цитатаafq ()
DivES, есть функция void (*loop)(); она получает ссылку на функцию с помощью dlsym, но она не получает её, потому что если сделать такую проверку, то ничего не будет.
При загрузки библиотеки нужно проверить, загружена ли она. Если загружена то Указатель должен быть инициализирован нулём или nullptr. Если получить адрес на функцию не удалось, то указатель так и останется нулевым. Значит, была допущена ошибка либо при экспорте функции, либо при получении указателя на неё (ошибка в имени, ошибка в объявлении указателя).
При загрузки библиотеки лучше сразу проверить на наличие все функции. Если какой-то нет то ошибка, старая .dll .so. Если всё есть, то проверки можно убрать, или использовать какой-нибудь флаг (библиотека была загружена или нет). "Do you think we can fly? Well, I do."
Буду(или будем) делать игру по моей идее (первый вариант).
Главная мысль которая у меня была в тот момент когда я её придумывал, сделать игру для геймпада, чтобы в развалочку сидя/лёжа на чём-то, нажимать минимум кнопок, чтобы было легко по умолчанию, чтобы игра расслабляла.
Протестировал тени и освещение.
Осталось сделать только освещение от частиц. Думал, сделаю вершинным светом, но, думаю проще рендерить частицы и накладывать сверху, как освещение для героя. "Do you think we can fly? Well, I do."
Сообщение отредактировал BADCOIQ - Суббота, 21 Июля 2018, 12:07
1 никому нельзя верить и вы не исключение, где гарантия что вы не кинете людей? 2 идеи как таковой нет, и вы ссылаетесь на то что решит группа(если соберется), а если не будет единогласного мнения? что тогда? 3 группа не соберется полностью (допустим присоединится один только музыкант) что тогда? заставите всю остальную работу его делать? сами будете делать? 4 как будете контролировать группу? энтузиазм слишком быстро заканчивается тоесть к примеру через неделю пол группы скажет "ой все я устал, я ухожу" что делать остальным которые отнеслись к делу серьезно? на свалку только зря время потратили? 5 что будет если команда все таки соберется вы создали проект но он провалился? команда разбегается? создавать новый? 6 возможно эгоистичный вопрос, но почему поровну? к примеру если художник создал намного больший объем работы чем скажем музыкант, получается музыкант схалявил, а художник в пролете, разве это справедливо?
мда
хватит держать людей за идиотов (я объяснять не буду, но вы это делаете, задумайтесь и поймёте)
______
Я вам больше не отвечу.
Это какой-то треш.
Будут посты только по теме. "Do you think we can fly? Well, I do."
Сообщение отредактировал BADCOIQ - Вторник, 17 Июля 2018, 16:54
У вас одно желание - услышать оправдания от человека.
Люди бывают разные, такова жизнь.
Как я и писал, господа, не обращайте на меня внимания, я не достоин вас. Именно вас я и отталкиваю ибо знаю суть форумных жителей. Я пришел не к вам, а к обычным игроделам ЛЮБИТЕЛЯМ которые не треплятся на форуме, потешая своё чувство собственной важности, набивая 2000+ сообщений и т.д.
Вот, вы прекратите делать из людей, не подс***вших вам, не подлизавших, идиотов. Это моё ощущение, видение быта в интернет сообществах, и это норма, к сожалению. Просто я принципиальный, не имею стадного инстинкта, гнобить слабых, льстить `важным особам` и т.д.
Есть идея, нужны люди которым будет интересно сообща делать что-то, от этого у всех будет стимул работать, особенно у меня. Раскрывать даже некоторые свои карты, естественно, нет желания, ибо крыс сейчас полно. Вести диспуты, подобные этим, не вызывает у меня интереса, а только наводит на тоску.
Пожалуйста, если вы так хотите, пофилософствовать о смыслах написанного и ненаписанного, создайте отдельную тему в этой категории -> флейм Поверьте, не все люди имеют такое же мировозрение как у вас. Им безразлично то, на что вы обращаете внимание. Вас что-то не устраивает - игнорируйте, делов-то.
______________ Столько мусора, кроме первых 2х сообщений. Всё нормально я объясняю. Просто нужны не тормозящие люди.
Добавлено (17 Июля 2240, час икс) --------------------------------------------- Днём запилю демку с 2д графикой. "Do you think we can fly? Well, I do."
Сообщение отредактировал BADCOIQ - Вторник, 17 Июля 2018, 00:59
Я никого не заставляю. Есть желающие - добро пожаловать. Как я написал, у меня есть идеи, и мне нужна помощь в их проработке, быть может, кто-то хочет реализовать что-то иное. Так то я отвечаю за код (непонимаю, зачем я должен всё на пальцах объяснять, по первому посту это можно было понять). Ну, если никого не будет, ничего страшного, буду таки сам делать.
Если и нанимать то только квалифицированных профессионалов. Какое тогда удовольствие от разработки? От процесса? Лучше сам буду, да пару иностранцев, пакистанцев, непальцев найду.
Прошу задавать вопросы, ответы которых ещё более проясняют ситуацию. Быть может я что-то не написал, не выразил какую-то мысль. Объясню, дополню. Только давайте без вечных вопросов (зачем, почему бы не и т.д.).
______________________
>показать почему идея крутая. Вместе с командой создадим свою крутую идею. Главное - не тормозить.
Добавлено (16 Июля 2018, 22:50) ---------------------------------------------
ЦитатаTLT ()
А движок больше трёхмерный... Или имеется в виду двухмерный (фиксированный) вид на трёхмерную сцену?
Демонстрацию не сделал пока. Но есть спрайты с анимацией, 2D камера. Будет именно в двух плоскостях. Всё остальное рисуется и собирается в редакторе. "Do you think we can fly? Well, I do."
Сообщение отредактировал BADCOIQ - Понедельник, 16 Июля 2018, 22:45