Публикации

Пилю физический движок и автосимулятор на нём.

Кажется, я уже перерос libgdx и он сейчас больше мешает, чем помогает. Кто-то писал что к godot легко прикрутить свой физический движок — жду в комментариях рассказа как это сделать и ссылок с примерами.

Штуки, которых не было из коробки и я писал их сам:

1. Поддержка руля с force feedback через SDL.
2. Привязка звука к объектам в 3д. Подключил miniaudio, теперь право-лево и ближе-дальше нормально слышно.
3. Нет быстрой ECS. (Тут больше претензии к особенностям JVM, чем к libgdx).

В чём проблема с ECS — в языках типа С++ ECS может разложить все компоненты одного типа просто в один массив, доступ к ним будет линейным чтением из памяти, а в случае java объекты раскиданы как попало и на это никак не повлиять.

В итоге у меня получился самописный франкенштейн, в котором почти всё удалось вынести в компоненты, но один тип пришлось оставить в виде поля в Entity. Потому что физический движок очень часто лезет в компонент для состояния физического тела, где хранятся положение, скорость, момент инерции и аккумулятор для сил. Причём доступ в случайном порядке. Например, для добавления силы от пружины надо посмотреть места двух точек крепления у двух разных нет.

Я сделал два эксперимента и в обоих производительность проседала в разы. В итоге я решил что скорость важнее красоты кода и оставил это поле в Entity.
Эксперимент 1: как и для остальных компоненов, просто использовал HashMap[Entity, PhysicsBody] и вместо обращеня к полю лазил туда. Ужасно медленно.
Эксперимент 2: Заменил тип Entity на Int, для PhysicsBody завёл массив, где по индексу лежал нужный объект.
Производительность просела раза в два-три, и кажется JIT стал хуже справляться, т.к. производительность была очень нестабильной и где-то через десяток секунд устаканаливалась на чём-то стабильном, но раза в два-три более медленном чем с доступом к полю в Entity.

В эту неделю попробовал ради эксперимента сделать в игре модель мотоцикла. И это офигенно получилось — почти сразу после начальных настроек он поехал. Причём, как и настоящий мотоцикл, на скорости он был устойчивый, а рулить надо было контррулением. Это достигается за счёт угла наклона вилки и трейла.

В целом это очень круто и значит, что мой физический движок вполне нормально работает и его можно использовать для чего-то серьёзного. Кажется, что он нормально обрабатывает штуки типа прецессии вращающихся тел, которую аналитически в случае с кучей тел фиг посчитаешь.



Тут могло бы быть видео, но форум не даёт его загрузить файлом.

Проблемы, которые я не решил (и почему хочу перейти к чему-то типа Godot/Unity/UE):
В libgdx нет редактора объектов. Я не могу просто взять сцену, накидать на неё компонентов колес, пружин, амортизаторов, двигателя, коробки передач, дифференциала и в редакторе движка собрать из них транспортное средство. И ассета из этой штуки я тоже не сделаю.

Вместо этого приходится описывать все взаимосвязи кодом, получается очень долго. Вдобавок приходится отвлекаться на всякие штуки типа звука, ввода-вывода, графики, логики движения камеры и т.п. Сейчас это всё самописное, хотелось бы использовать что-то готовое и не отвлекаться.


UPD 2024-12-03


В качестве эксперимента накидал плоскую трассу из линий. Вообще мне понравился такой подход — не надо думать про текстуры и тратить время на детализацию. Можно просто накидать контуры в Blender и закинуть в движок. Этого достаточно, чтобы как-то оценить управляемость машины и сопоставить размеры моделей. Вся карта в obj весит около 50 килобайт и легко парсится самодельным парсером. Стандартный парсер obj от libgdx ожидает увидеть полигоны вместо линий и эти файлики не переваривает.



UPD 2024-12-04


Попробовал загрузить ресурсы из Ассето Корсы. Формат хранения у них бинарный, но довольно простой и на гитхабе уже есть код, который его парсит: github.com/RaduMC/kn5-converter/blob/master/kn5%20converter/Program.cs

Осталось только под свой язык программирования переписать.

Если кратко по формату и организации сцены в формате kn5 — сцена состоит из дерева нодов. У каждой ноды есть имя и список дочерних. Видов нод три — нода с матрицей преобразования, нода с мешем и нода с анимированным мешем. В файлах попадаются пустые ноды — кажется, их ещё используют в качестве зашлушек, чтобы потом по этой позиции добавить колесо или ещё что-то.



Я пока в своём минималистичном стиле нарисовал только линии, без полигонов, каждую ноду своим рандомным цветом. Но кажется у меня какая-то ошибка в индексах — куски земли как-то очень плотно залиты линиями и треугольников не видно.

Так же прямо внутри хранятся текстуры в dds и какие-то параметры материалов для шейдеров. Но сами шейдеры хз где.


UPD 2024-12-13



Поправил загрузку, добавил текстуры.
Много времени потратил на борьбу с libgdx. На мой взгляд в этом движке много оверинжиниринга и какие-то простые вещи делаются сложно, а вдобавок чтобы использовать libgdx, надо знать и как работает openGL, и как работают велосипеды в движке поверх него. А для чего-то сложного движок слабо подходит, например поддержки загрузки dds текстур в движке нет
. Я нашёл какую-то внешнюю библиотеку (gdx-dds), но она на попытке загрузки каких-то текстур кидается исключениями. В итоге я вместо вызова «загрузить текстуру» трачу время.

На мой взгляд пример оверинжиниринга: RenderContext.java — чтобы его использовать, надо целиком прочитать его код, и потом понять что под капотом он что-то запоминает и как-то вызывает openGL

Больше всего я сгорел с бага с текстурными координатами. Попробуйте найти ошибку:

    val mesh = new Mesh(
      true,
      node.verticesData.length,
      node.indices.length,
      new VertexAttribute(VertexAttributes.Usage.Position, 3, ShaderProgram.POSITION_ATTRIBUTE),
      new VertexAttribute(VertexAttributes.Usage.Normal, 3, ShaderProgram.NORMAL_ATTRIBUTE),
      new VertexAttribute(VertexAttributes.Usage.TextureCoordinates, 2, ShaderProgram.TEXCOORD_ATTRIBUTE),
      new VertexAttribute(VertexAttributes.Usage.Tangent, 3, ShaderProgram.TANGENT_ATTRIBUTE),
    )
Ошибка в том, что для текстурный хоординат должно быть так
ShaderProgram.TEXCOORD_ATTRIBUTE + "0"
В движке ни намёка на то, что текстурные координаты должны быть с нулём или ещё какой-то цифрой в конце.


UPD 2024-12-15

По совету Mr F попробовал использовать MSAA и включить alpha to coverage. Это прекрасно! Офигенная, забытая технология древних. С MSAA 16x получилась почти идеальная картинка для полупрозрачных объектов буквально добавлением пары строчек. Артефакты при желании можно увидеть, но усложнять код и использовать более сложные подходы я не хочу. Сейчас у меня осовной упор именно на проработку физики, графика подойдёт почти любая.


Топик о возможностях и проблемах статической типизации

Я тут на скале попробовал написать код, чтобы компилятор знал про размерности как в системе СИ, и догадывался, что скорость, поделённая на время — это ускорение. И не давал его складывать с чем-нибудь другим.
(Сам пост тут, сори мне лень из маркдауна в форумный вид форматировать, может потом попробую, да и статья немножкно не об этом).

Так вот, идея размерностей проиходят на физике в шестом классе. Компиляторы развиваются полвека как минимум.
Но почему, блин, я такую относительно простую концепцию смог записать только в довольно простом виде только сейчас и на немейнстримном языке?
На части языков это невозможно, на части языков придётся через числа Чёрча изобретать арифметику заново и писать нечитаемый код. Ещё на С++ на шаблонах можно, но сам по себе С++ можно назвать одним большим извращением.

А казалось бы задача не сложная — параметризовать шаблонный тип целыми числами и делать арифметические операции в compile time. Всё.