Журнал «FPS» №28

К выходу FPS №28 мы решили приурочить открытие сайта-блога http://fps-magazine.blogspot.ru, в котором постепенно собираемся разместить всю информацию из нашего старого, ныне несуществующего сайта (fpsmag.zymichost.com). Надеемся, этот ресурс поможет читателям, не зарегистрированным в Google+, не терять нас на просторах Интернета и быть в курсе обо всех событиях из жизни журнала.

А в этом выпуске FPS вы найдете следующие материалы:

  • Подборка новостей по Blender
  • Интервью с Ларри Гритцем, создателем OSL
  • Рисуем фрактал на D
  • Обзор альтернативных ОС
  • Игровые новости из мира Linux
  • Кому нужен Linux? Правда и вымыслы
  • Культовые игры: The Elder Scrolls V
  • Angry Birds в России
  • Как создавались игры 80-х и 90-х
  • Каково быть гиком в тюрьме? Исповедь Анонимуса

Журнал доступен для онлайн-чтения и загрузки на Документах Google, на Dropbox, а также на Issuu.com.

Последние новости по проекту вы можете узнать в публичной странице журнала в социальной сети Google+: http://gplus.to/fpsmag. Добавляйте нас в круги, оставляйте свои комментарии и отписывайтесь в нашем сообществе.

Архив номеров журнала вы можете найти здесь.

GScript — скриптовый язык для D

В игровом движке трудно обойтись без какого-либо способа динамического задания логики и поведения объектов, поэтому я решил написать для Atrium скриптовый язык. Это очень простой императивный язык с динамической типизацией и (пока) всего одним внутренним типом – float.

Что уже реализовано:

  • Модульная система, как в D;
  • Функции (есть поддержка рекурсии);
  • Локальные переменные;
  • Передача аргументов по значению и по ссылке. Что интересно, передача по ссылке возможна в любую функцию, так как ссылочный тип указывается при конкретном вызове функции, а не при ее объявлении;
  • Условный переход if…else;
  • Цикл while;
  • Возможность расширять язык собственными функциями на D.

Кодогенератор и виртуальная машина к языку пока находятся на стадии прототипа (реализация рабочая, но далека от оптимальной).

Пример кода на GScript:

import myPackage.myModule;

func main()
{
    var x = 10;
    var a, b;

    a = x * 2 + 1;

    while(a > 0)
    {
        a = a - 1;
        b = b + 1;
    }

    writeln(x, a, b);
}

Исходники проекта доступны на GitHub:
https://github.com/gecko0307/gscript
Примеры скриптов

Приветствуются предлолжения и пожелания – какую функциональность вы бы хотели видеть в языке (оговорка: поддержка ООП в ближайшее время не планируется).

Распараллеливание обработки изображений

API dlib.image позволяет создавать фильтры, которые легко распараллеливать на несколько процессоров. Изображение условно разбивается на несколько блоков заданного размера, которые затем обрабатываются фильтром через std.parallelism.

import std.parallelism;
import dlib.functional.range;
import dlib.image.image;

struct Block
{
    uint x1, y1;
    uint x2, y2;
}

alias Range!uint PixRange;

void parallelFilter(
     SuperImage img, 
     void delegate(PixRange blockRow, PixRange blockCol) ffunc, 
     uint bw = 100,
     uint bh = 100)
{
    if (bw > img.width)
        bw = img.width;
    if (bh > img.height)
        bh = img.height;

    uint numBlocksX = img.width / bw + ((img.width % bw) > 0);
    uint numBlocksY = img.height / bh + ((img.height % bh) > 0);

    Block[] blocks = new Block[numBlocksX * numBlocksY];
    foreach(x; 0..numBlocksX)
    foreach(y; 0..numBlocksY)
    {
        uint bx = x * bw;
        uint by = y * bh;

        uint bw1 = bw;
        uint bh1 = bh;

        if ((img.width - bx) < bw)
            bw1 = img.width - bx;
        if ((img.height - by) < bh)
            bh1 = img.height - by;

        blocks[y * numBlocksX + x] = Block(bx, by, bx + bw1, by + bh1);
    }

    foreach(i, ref b; taskPool.parallel(blocks))
    {
        ffunc(range!uint(b.x1, b.x2),
              range!uint(b.y1, b.y2));
    }
}

Пример (закрашивание сплошным цветом):

SuperImage filterTestMultithreaded(SuperImage img)
{
    auto res = img.dup;
    
    img.parallelFilter((PixRange row, PixRange col)
    {
        foreach(x; row)
        foreach(y; col)
        {
            res[x, y] = hsv(180.0f, 1.0f, 0.5f);
        }
    });
    
    return res;
}

Для сравнения — однопоточный вариант:

SuperImage filterTestSinglethreaded(SuperImage img)
{
    auto res = img.dup;
    
    foreach(x; img.row)
    foreach(y; img.col)
    {
        res[x, y] = hsv(180.0f, 1.0f, 0.5f);
    }
   
    return res;
}

На двухъядерном Intel Dual Core T2390 (1.86 ГГц) многопоточный вариант показывает прирост производительности на 70%.

Интернационализация в D

Представляю вашему вниманию i18n.d – простое и минималистичное решение для интернационализации программ на языке D. Работает по принципу GNU gettext и других аналогичных инструментов: для перевода строки, ее нужно обернуть в функцию “_”.
В данный момент модуль имеет поддержку Windows и всех POSIX-систем.

Пример использования:

import std.stdio;
import i18n;

void main()
{
    Locale.readLang("locale", ".lang");

    writeln("Hello, world!"._);
}

Программа будет искать файлы локализации (*.lang) в каталоге locale. Имена файлов должны соответствовать RFC 3066 (в POSIX-варианте). Кодировка – UTF-8.

Вот пример русской локали (ru_RU.lang):

"Hello, world!" = "Привет, мир!"
"Some text" = "Какой-то текст"

Исходный код i18n.d:
https://gist.github.com/gecko0307/8419717

Журнал «FPS» №27

Вышел 27 номер электронного PDF-журнала «FPS», посвященного разработке игр, программированию, компьютерной графике и звуку.

Читайте в этом номере:

> Подборка новостей по Blender
> Тон Розендаль о будущем интерфейса Blender
> GIMP: цветокоррекция на Python
> От мольберта — к дисплею. Заметки о цифровой живописи
> Физический движок своими руками. Часть IV
> Математика в dlib
> Ranges: диапазоны в D
> Игровые новости из мира Linux
> Право на творчество

Номер доступен для онлайн-чтения и загрузки на сервисе Issuu.com, Документах Google и Dropbox.

Последние новости по проекту вы можете узнать в публичной странице журнала в социальной сети Google+: http://gplus.to/fpsmag. Добавляйте нас в круги, оставляйте свои комментарии и отписывайтесь в нашем сообществе.

Архив номеров журнала здесь.

Cook2

В связи с выходом DMD 2.064 с поддержкой пакетного импортирования, была создана новая нестабильная ветка проекта Cook (инкрементальной системы сборки для программ на D).
В Cook2 планируется внесение серьезных изменений без сохранения обратной совместимости: в частности, переписан код обработки опций командной строки (он теперь использует std.getopt), а также удалена устаревшая и ненужная функциональность.
Поддержка пакетных модулей (package.d) уже обеспечена — кроме того, появилась поддержка выборочных и именованных импортов (например, import foo = bar.Foo и import std.stdio: writefln).

Репозиторий проекта:
https://github.com/gecko0307/cook2

Обновление dlib.image

В dlib.image появилась возможность отслеживать прогресс во время работы фильтров. Для этого используется многопоточность – необходимо создать класс-враппер, наследующий от FilteringThread. Прогресс (от 0 до 1) считывается из свойства progress для SuperImage. В данном примере показано, как использовать эту функциональность для вывода прогресса свертки в консоль:

import std.stdio;
import dlib.image.image;
import dlib.image.io.png;
import dlib.image.filters.convolution;
import dlib.image.fthread;

class ConvolutionThread: FilteringThread
{
    float[] kernel;
    
    this(SuperImage img, float[] k)
    {
        super(img);
        kernel = k;
    }
    
    override void run()
    {
        output = image.convolve(kernel);
    }
    
    override void onRunning()
    {
        writef("Convolving %s%%", cast(uint)(image.progress * 100));
        write("r");
        stdout.flush();
    }
    
    override void onFinished()
    {
        writeln();
    }
}

void main()
{
    auto img = loadPNG("test.png");
    img = (new ConvolutionThread(img, Kernel.Emboss)).filtered;
    img.savePNG("output.png");
}

Пример работы с Yahoo! Finance на D

Пример работы с сетевым API Yahoo! Finance: запрос котировок ценных бумаг. Демонстрирует некоторые распространенные идиомы Phobos – например, форматированный ввод, объекты времени и даты. Использует минималистичный HTTP-клиент DHTTPClient. На ввод программа принимает тикер (биржевое обозначение акции) – например, MGNT.ME для акций “Магнит”. Выводит стоимость, дату и время последней сделки.

module main;

import std.stdio;
import std.string;
import std.uri;
import std.format;
import std.datetime;

import dhttpclient;

struct Quote
{
    string symbol;     // s
    string name;       // n
    double lastTrade;  // l1
    string currency;   // c4
    DateTime datetime; // d1 t1
}

Quote getQuote(string sym)
{   
    const request = "snl1c4d1t1";

    const url = 
        "http://finance.yahoo.com/d/quotes.csv?e=.csv"
      ~ "&f=" ~ request
      ~ "&s=" ~ sym.encode;
    
    auto http = new HTTPClient();
    auto data = http.get(url).chomp;

    Quote q;

    with (q)
    {
        uint year, month, day;
        string time;

        formattedRead(data, 
            ""%s","%s",%s,"%s","%s/%s/%s","%s"", 
            &symbol, &name,
            &lastTrade, &currency,
            &month, &day, &year, &time);

        uint hour, minute;
        formattedRead(time, "%s:%s", &hour, &minute);
        if (time[$-2..$] == "pm")
            hour += 12;

        datetime = DateTime(year, month, day, hour, minute);
    }

    return q;
}

void main(string[] args)
{
    string s = "AAPL"; // Apple Inc.
    
    if (args.length > 1)
        s = args[1];

    auto q = getQuote(s);

    writefln("Symbol: %s", q.symbol);
    writefln("Name: %s", q.name);
    writefln("Last trade: %s %s (%s)", 
        q.lastTrade, 
        q.currency, 
        q.datetime);
}

Рефакторинг матриц в dlib

На днях состоялось грандиозное обновление пакета линейной алгебры dlib.math. Изменения коснулись, главным образом, реализации матриц. Если раньше матрицы 2×2, 3×3 и 4×4 имели каждая отдельную независимую реализацию, то теперь все они являются частными случаями обобщенной квадратной матрицы Matrix!(T,N) (где T – тип элементов, N – размерность). Она содержит все необходимые общие методы для матриц любого размера (нахождение определителя, нахождение обратной матрицы, нахождение матрицы миноров и алгебраических дополнений и т.д.), оптимизированные, где это возможно, для размерностей 2, 3 и 4. Таким образом, нынешние специализации Matrix2x2f, Matrix3x3f и Matrix4x4f практически идентичны их прежним аналогам.

Новая реализация создана с учетом обратной совместимости, но все-таки есть несколько критичных изменений:

1. Больше нет шаблонов Matrix2x2!(T), Matrix3x3!(T), Matrix4x4!(T). Используйте вместо них Matrix!(T,2), Matrix!(T,3) и Matrix!(T,4). При этом псевдонимы на специализации типа Matrix2x2f и Matrix4x4d сохранены;

2. Нет доступа к элементам матриц 4×4 через поля m*, t* и h*. Возможен только доступ через поля a*. Это справедливо для матриц любого размера:

a11 a12 a13 a14 .. a1N
a21 a22 a23 a24 .. a2N
a31 a32 a33 a34 .. a3N
a41 a42 a43 a44 .. a4N
 :   :   :   :  .
aN1 aN2 aN3 aN4  ' aNN

2. Все аффинные преобразования (функции rotationMatrix, translationMatrix и др.) и утилитарные функции для матриц вынесены в отдельный модуль dlib.math.affine. Там же находятся функции right, up, forward, translation, scaling, которые раньше были опрелены как методы в Matrix4x4!(T). Благодаря UFCS, их и теперь можно использовать как методы – однако все они теперь представляют собой свойства только для чтения. Пока они определены только для Matrix!(T,4), но в будущем функции базиса (right, up, forwartd) будут доступны и для Matrix!(T,3).

3. В целях обратной совместимости сохраняются модули dlib.math.matrix2x2, dlib.math.matrix3x3, dlib.math.matrix4x4, но они помечены как deprecated. Вместо них импортируйте dlib.math.matrix (и dlib.math.affine, если вам нужны аффинные преобразования)

2. Не рекомендуется использовать identityMatrix3x3!(T) и identityMatrix4x4!(T). Единичные матрицы создаются при помощи статического метода identity: например, Matrix3x3f.identity.

3. Не рекомендуется трансформировать векторы методом transform. Вместо этого лучше использовать умножение вектора на матрицу: Vector3f(1, 2, 3) * myMatrix.

4. Любые матрицы можно создавать при помощи функции-фабрики matrixf, которая автоматически определяет размерность на основе входных данных:

auto m1 = matrixf(
    8, 3, 2, 0,
    4, 0, 2, 0,
    1, 3, 3, 0,
    0, 0, 3, 1
);

Это выражение создаст матрицу типа Matrix!(float,4) и присвоит ее переменной m1.

Убедительная просьба всем пользователям dlib сообщить мне (в Issues в репозитории на GitHub, либо на почту – gecko0307@gmail.com), если будут обнаружены какие-то несостыковки и баги, связанные с данным рефакторингом матриц.

Журнал «FPS» №26

Вышел 26 номер электронного PDF-журнала «FPS», посвященного разработке игр, программированию, компьютерной графике и звуку.

Читайте в этом номере:

> Tube Open Movie. Интервью с Бассамом Курдали
> Обзор дополнений Blender, выпуск 5
> GIMP: ломо-эффект как в Instagram
> Физический движок своими руками, часть III
> Making-of: логическая мини-игра Arrow
> Генерация случайных уровней
> Осваиваемся в SDL2
> Пишем плагин для DeleD на D
> Как я стал D-шником или Путь художника в IT
> Игровые новости из мира СПО и Linux
> «Корпорация зла». Почему у Microsoft нет будущего

Номер доступен для онлайн-чтения и загрузки на сервисе Issuu.com, Документах Google и Dropbox.

Последние новости по проекту вы можете узнать в публичной странице журнала в социальной сети Google+: http://gplus.to/fpsmag. Добавляйте нас в круги, оставляйте свои комментарии и отписывайтесь в нашем сообществе.

Архив номеров журнала здесь.