Showing posts with label web. Show all posts
Showing posts with label web. Show all posts

28/04/2020

Web (3): Daft Jong — простая игра на JS/WebGL

В этой заметке я представляю простейшую версию (обкаточную реализацию) игры типа «пасьянс», а именно — трёхмерную разновидность известного MahJong'а (его пасьянсной вариации). Хотя, скорее, у меня смесь головоломки и пасьянса.

Технологии надо описывать, а в игру — играть. Поиграть вы можете, пройдя по ссылке внизу. А здесь я вкратце опишу какие технологии я применял и какие решения принимал. Игра написана на WebGL, с использованием Three.js. Цель игры — разобрать куб, попарно убирая одинаковые фишки (кубики).

Правила
«Правилами» я здесь называю пары узоров, так как именно по ним пользователь играет. Поскольку выбирать фишки нужно попарно, перед созданием объектов на сцене, нужно составить какую-то структуру, содержащую (описывающую) эти правила и гарантирующую «сходимость» головоломки. Для этого реализован алгоритм, создающий такую структуру, учитывающий выбранный размер головоломки, и потом, уже по этой структуре генерируется сама головоломка, её представление — соответствие расположения «правил» в пространстве с узорами.

Отрисовка фишек
В этой игре 35, или уже более, разных «правил». Узоры на фишках я решил отрисовать алгоритмом. Во-первых, потому что рисование такого количества картинок руками у меня заняло бы больше времени (а это не входило в мои планы), а написав алгоритмы отрисовки нескольких примитивных элементов, основанных на линиях и кругах, я смог их комбинировать и получить большое количество «правил». И, на данный момент, ещё не все комбинации употреблены. Во-вторых, генерация узоров на стороне клиента даёт ещё три бонуса. Первый серьёзный — возможность изменения картинок на лету. Пока реализовано только изменение цветов. Но можно реализовать случайную генерацию узоров, что не позволит игрокам «привыкнуть» к «правилам». Второй бонус заключается в возможности контролировать качество графики на стороне клиента, играя размером текстуры и соотношением логического пикселя экрана к аппаратному — DPR. Эту возможность, я может быть использую в реализации алгоритма адаптивного подбора нагрузки на CPU/GPU. Третий бонус, по началу не столь важный — трафик. Но если «правил» — разных цветов и комбинаций, будет больше, то исключение ожидания загрузки картинок с сервера будет более желательно, а рисовать руками придётся ещё больше и скучнее.

Выбор объектов
Так как в игре нужно разбирать головоломку, выбирая попарно фишки, технически нам нужен механизм выбора объектов на экране. Два подхода к решению этой задачи я описывал в статье Web (1): RayCasting vs GPU Color Picking (на примере Three.js). Для этого приложения я выбрал метод трассировки лучом (RayCasting) потому что объектов на сцене немного, а прозрачность текстур не используется.

NB
У меня игра лучше всего работает на телефонах — хоть и греет устройство и батарею расходует, но играется идеально гладко. На всех моих компьютерах — Linux Netbook, MacBook и даже большой Linux на Core i7 c nVidia GPU, работает гораздо хуже — компьютеры шумят, греются, а игра сильно тормозит. А в Safari на MacOS вообще едва дышит, так как аппаратная OpenGL не используется, якобы для экономии батареи.

P.S.
В игре так же реализована несложная система авторизации, её я здесь не описываю. Про это будет отдельная статья.

Ссылка на игру: Daft Jong

Как играть: выбираете один элемент, он выделяется, ищете второй такой же, выбираете его, выбранные фишки удаляются с экрана. Таким образом надо разобрать весь куб. В конце выводится время, за которое вы разложили игру и количество ошибочных ходов (попыток выбрать неодинаковые кубики). На мобильных устройствах вращение куба осуществляется одним пальцем, приближение/отдаление — жестом двумя пальцами. На больших системах куб вращается движением мыши с зажатой правой кнопкой, а приближение/отдаление— колесом мыши.


06/04/2020

Web (2): Watcher.js — компонент для просмотра log-файлов

Задача
Иногда, чаще при разработке встраиваемых систем, возникает необходимость просматривать журнальный файл (лог) какого-либо приложения или демона, работающего на целевой системе или на «железке». Каждый раз ходить на устройство по SSH/Telnet/UART'у бывает не очень удобно. Особенно, когда система уже работает и имеются средства удалённой загрузки, допустим, конфигураций, прошивок или самого ПО, на устройство через систему управления (которая так же может быть Web-приложением).
Можно было бы просто запрашивать интересующий файл через Web-сервер. Но тут есть два нюанса. Первое — Web-сервер, в целях безопасности, не отдаст клиенту файл, расположенный вне DocumentRoot, по простому HTTP-запросу. Второе — даже если настроить сервер так, чтобы он отдавал файл (можно разрешить доступ к каталогу с логами или сделать ссылки на интересующие файлы в DocumentRoot), не будет автоматического обновления дописываемых в лог строк — придётся каждый раз обновлять страницу и ждать пока файл загрузится заново и проматывать вниз. Здесь казалось бы, можно просто написать CGI, которая будет делать 
tail -f /path/to/file
но на Web'е это работать не будет, потому что сервер отдаёт ответ от CGI только по завершению процесса, а tail -f не завершится никогда. Страница на стороне клиента зависнет в вечном состоянии загрузки.

Решение
Именно эти две задачи и решает мой компонент (Web-приложение), который я представляю в этой статье — компонент для просмотра текстовых файлов через Web-интерфейс, Watcher.
Watcher позволяет просматривать любые текстовые файлы на файловой системе, к которым у CGI-скриптов, запускаемых Web-сервером, есть доступ на чтение. Я на своей системе смог просмотреть даже /etc/fstab и /proc/cpuinfo. (А вот /var/log/messages без изменения прав доступа не получилось загрузить, он оказался под запретом даже для чтения.) Строки грузятся кусками и один раз, то есть вы можете читать информацию, не дожидаясь её полной прогрузки и без повторных загрузок файла.

Алгоритм, по которому работает Watcher
Сперва запрашивается размер файла, выраженный в количестве строк. На этом же этапе происходит определение ошибки доступа или отсутствия указанного файла. Если обнаружена ошибка, выводится сообщение и дальнейшая работа прекращается. Если ошибки не произошло, Watcher кусками подгружает строки из файла от нулевой до того размера, который был получен в начале. Затем запускается циклический таймер, который опрашивает изменение размера файла (в строках). Если файл увеличился, запрашивается (кусками) новое содержимое и отображается. Данные (количество строк и прогресс) получаемые на первом этапе отображаются в левом поле (Fetch). Данные, подгружаемые в последствии, отображаются в центральном поле (Watch).

Управление Watcher'ом
Имя файла задаётся в параметре адресной строки (?File=). То есть, вы вводите адрес (например, 127.0.0.1/Watcher.html?File=../log) и нажимаете enter. Всё, Watcher начинает работать по загрузке страницы. Если вам удобно чтобы при подгрузке новых строк, компонент автоматически прокручивался на них, есть переключатель Auto Scroll.
Иногда бывает нужно сбросить лог, очистить его содержимое. Для этого есть кнопка Clear Log. Эта функция очищает файл (при условии соответствующих прав доступа) на устройстве и перезапускает Watcher.

Ограничения и пояснения
Я долго соображал, что последняя, пустая строка, которую мы видим в текстовых редакторах, на самом деле не существует — символ новой строки добавляется в конце последней строки — той, которая нам кажется предыдущей. То есть, символ новой строки является частью последней строки, а не отдельной строкой. Поэтому, когда вы видите в редакторе или в выводе cat пустую строку снизу — это особенности вывода. Эта строка не выводится в Watcher как отдельная, пустая. Отсюда же следствие — если вы допишете строку в конец файла без символа новой строки, она не подхватится Watcher'ом.
В следствие особенностей алгоритма (работает только со строками и по строкам), который я разработал в этом компоненте есть некоторые ограничения:
  • нельзя удалять строки из файла — Watcher не отреагирует на это, более того — алгоритм вовсе собьётся
  • не имеет смысла изменять строки в файле — однажды загрузив содержимое файла, Watcher не следит за изменениями этого содержимого
  • CGI Watcher'а написан на Python 2.x — на Python 3.x пока не работает
  • у меня в проекте своя структура (расположение файлов) — вам придётся настроить свой httpd так, чтобы работал Python-скрипт из моего каталога (CGI) или переместить его в каталог с вашими CGI
Чтобы воспользоваться:
git clone https://gitlab.com/daftsoft/watcher.js.git



18/02/2020

Web (1): RayCasting vs GPU Color Picking (на примере Three.js)

В определённый момент, перед разработчиком графических приложений с использованием OpenGL/WebGL встаёт задача взаимодействия пользователя с объектами. Будь то элементы двухмерного интерфейса (частный случай трёхмерного пространства) или объекты трёхмерной сцены, существует два подхода к решению этой задачи. Первый заключается в поиске объекта, на который указывает мышь математическим (тригонометрическим) расчётом.  Второй — поиск объекта по цвету пикселя, на который пользователь указал мышью. Первый кажется логичным, понятным и красивым, второй — на первый взгляд кажется странным, но у обоих методов есть свои плюсы и минусы и свои области применения.

В этой статье мы рассмотрим два метода выбора объекта на сцене (OpenGL/WebGL на примере Three.js) — RayCasting и GPU Color Pick.

RayCasting — это технология определения пересечения луча с объектом. При определении выбранного объекта — объекта, на который навели мышь, например, выбирается первый объект, с которым произошло пересечение. На самом деле собираются все объекты, с которыми пересёкся луч, а уже мы используем только первый. RayCasting работает на стороне CPU, то есть, не задействует специфические вычислительные мощности GPU. 

В Three.js для этих целей имеется уже разработанный объект THREE.Raycaster. Пользоваться им достаточно просто, в примере, которым я завершу эту статью, этот метод будет представлен. Описывать его механику смысла не вижу — это обычная тригонометрия.

У RayCasting’а есть два минуса:

  1. Он работает медленнее, так как работает на CPU, и в случае, когда у нас много объектов, это может быть очень тяжёлой задачей для центрального процессора. А в современной графике с использованием актуальных методов создания карт (т.н. сцен) количество объектов может быть очень большим.
  2. Он не учитывает факторы текстуры — прозрачность, и просто «выбирает» объекты, которые встречаются лучу (в нашем случае — выбор объекта мышью, луч рассчитывается от камеры), то есть работает исключительно геометрия (математика).

Технология GPU Color Pick заключается в следующем. При создании сцены, все объекты дублируются в другую сцену, обычно называемую PickingScene и связываются некоторыми ID реальных объектов с цветами объектов в PickingScene. Для исключения излишней нагрузки на GPU, PickingScene, как правило, делается очень маленькой (выходной буфер рендеринга очень маленькой площади, это в некоторой степени сокращает время на рендеринг). В нашем случае, по наведению мыши, вызывается функция поиска объекта в PickingScene и затем выбирается ID объекта в реальной сцене. В силу того, что технология GPU Color Pick работает по цвету, она лишена второго минуса RayCasting’а — мы можем использовать альфа-канал в текстурах объектов. Это значит, что мы можем дать пользователю возможность «выбирать» объекты стоящие «за», сквозь «прозрачные» области стоящих «перед». В силу того, что технология GPU Color Pick работает на GPU, а он, в свою очередь очень «заточен» под такие задачи, эта технология работает гораздо быстрее и почти полностью разгружает CPU (на нём остаётся лишь задача соотнести цвета и ID объектов).

У GPU Color Pick (как ни странно) есть два минуса:

  1. Мы не можем получить координаты места «встречи» указателя (здесь это не луч и не вектор) и объекта — только сам объект, так как объект выбирается не геометрией, а цветом.
  2. Подгружается GPU. Это хорошо тем, что разгружается CPU, но может быть плохо, если вычислительные мощности GPU для нас критичны. Плюс, в момент процесса самого Picking’а, нам надо ждать на CPU пока GPU выполнит задачу поиска объекта по цвету.


Все эти технологии, конечно же применяются и на Bare WebGL и на чистом OpenGL.

Чтобы поиграться (работает без Web-сервера):

git clone https://gitlab.com/daftsoft/gpu-color-pick-vs-raycasting