среда, 6 января 2016 г.

Снова про эти воксельные движки

Все-таки, в заду жжет и хочется изобретать велосипед. Однако, вместо очередной попытки сделать что-то в лоб я принялся за изучение имеющихся API, протоколов и внутренних структур Minecraft как одного из наиболее успешных (хотя, все уверены, что он "неоптимизирован", "криво написан", "тормозит потому что жава"). Узнал много интересного, развеял многие свои заблуждения насчет "да блин, это ж так просто". Смотришь так формат хранения/пересылки чего-то и видишь там много интересных особенностей. И начинаешь думать, можно ли сделать как-то иначе, и как-то в голову сразу ничего не приходит (особенно после опыта собственной "жырноты").

Например, о чем я сразу подумал плохо - это об объемах данных, которые нужно перегнать с сервера на клиент, например, чтобы показать мир в радиусе 80 блоков (то есть 10 чанков - по 5 в каждую сторону).
Примем ради простоты, что у нас каждый чанк состоит из 4096 блоков (16х16х16), и каждый блок у нас ничего кроме статичного ID не описывает. И пусть наш ID будет 32-битным (4 байта), потому что мы щедрые и хотим много блоков (или хранить какие-то дополнительные данные о них).
Если загрузить все полностью (и с этим, в принципе, я справился даже с жирнотой, в которой было больше 4-х байт на блок, просто все не так быстро было), то это 10*10*10 чанков, то есть тыща чанков... и если использовать даже по 4 байта на блок, у вас получается 16 мегабайт данных (4096*4*1000). Вроде-бы не космос какой-то для современных сетей. Но так и 10 чанков это не так много - это средние настройки для Майнкрафта. Максимум там 16 чанков (правда, это не радиус, а диаметр). Примерно 64 мегабайта (4096*4096*4), что чуть больше. 100-мегабитная сеть будет грузить это до 10 секунд. Конечо же, в идеале, нужно догнать и перегнать MC, отрисовывая не 8 чанков перед игроком, что всего-то составляет 128 блоков (128 метров). Размер блоков без снижения детализации позволяет рисовать гораздо дальше, создавая офигительный ландшафт, и надо увеличить это хотя-бы раза в два, но это будет не в 2 раза больше по объему, а в 8 (это ведь 3Д, Карл!), то есть, офигенная половина гигабайта (если быть точным, то 32*32*32*4*4096 байт).

Конечно, вы не хотите передавать от сервера клиенту полгигабайта данных (ну может меньше, за счет сжатия), поэтому нужно делать что-то еще, изобретая невероятные способы уменьшить эти объемы. Например, Minecraft целиком "пустые" куски чанков не передает вообще, совершает ряд манипуляций с blockID, чтобы вместо 2-х байт использовать иногда и один. Нам же придется кроме этого добавить и еще что-то вроде уменьшения числа данных о далеких чанках (такой себе LOD) и умные алгоритмы определения, надо ли их вообще загружать, что я уже делал, хоть и криво). И даже если не хочется очень далеко видеть, это очень нужно чтобы уметь быстро двигаться в мире.

Поэтому, кстати, Minecraft начинает как-то медленнее работать, если в него впихивают моды и ставят кучу блоков из них (хотя есть и другая причина, но о ней в следующий раз). Ну и еще стоит помнить, что кроме передачи этих данных по сети, их нужно на сервере хранить на диске и в памяти, и в памяти клиента тоже.

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