Класс для генерации текстурных атласов

Переписываю сейчас свой движок для поддержки Stage3D. Появилась необходимость создавать текстурные атласы. Поискал утилиты в интеренете, либо платные и могут не всё, что надо, либо кривые и вообще не могут то, что надо.

Решил писать свой генератор атласов. И самое важное тут — правильно расположить текстуры, чтобы они занимали как можно меньше места. Поиск выдал статью, где описывается алгоритм и даже даётся псевдокод (автор, убей себя за такой невнятный псевдокод). Эта статья и легла в основу моего класса (передрал подчистую, как только понял, что имел в виду автор своим кривым псевдокодом).

Хочу поделиться с вами этим классом, возможно он сэкономит кому-то время и нервы.

package  {
  import flash.geom.Rectangle;
  /**
  * ...
  * @author Makar Osokin (elmortem@gmail.com)
  */
  public class AtlasNode {
    public var childs:Vector.<AtlasNode>;
    public var rect:Rectangle;
    public var data:Object;
                
    public function AtlasNode() {
      childs = new Vector.<AtlasNode>();
      rect = new Rectangle();
      data = null;
    }
                
    public function insert(width:Number, height:Number, data:Object):AtlasNode {
      if(childs.length > 0) {
        var newNode:AtlasNode = childs[0].insert(width, height, data);
        if (newNode != null) return newNode;
        
        return childs[1].insert(width, height, data);
      } else {
        if (this.data != null) return null;
        
        if (width > rect.width || height > rect.height) return null;
                                
        if (width == rect.width && height == rect.height) {
          this.data = data;
          return this;
        }
        
        childs.push(new AtlasNode());
        childs.push(new AtlasNode());
        
        var dw:Number = rect.width - width;
        var dh:Number = rect.height - height;
        
        if (dw > dh) {
          childs[0].rect = new Rectangle(rect.left, rect.top, width, rect.height);
          childs[1].rect = new Rectangle(rect.left + width, rect.top, rect.width-width, rect.height);
        } else {
          childs[0].rect = new Rectangle(rect.left, rect.top, rect.width, height);
          childs[1].rect = new Rectangle(rect.left, rect.top + height, rect.width, rect.height-height);
        }
                                
        return childs[0].insert(width, height, data);
      }
    }
  }

}


Используется данный класс очень просто:
var atlas:AtlasNode = new AtlasNode(); // создаём наш атлас
atlas.rect.width = 1024; // задаём размер
atlas.rect.height = 1024;
// финальный массив для хранения областей на атласе
var atlasArr:Array/*AtlasNode*/ = [];
// массив картинок или любых других объектов у которых есть размер
var bmpArr:Array/*BitmapData*/... 
// сортируем для лучшего качества упаковки
bmpArr.sortOn(['width', 'height'], Array.NUMERIC | Array.DESCENDING);

for(var i:int = 0; i < bmpArr.length; i++) {
  // пытаемся вставить картинку в атлас
  var n:AtlasNode = atlas.insert(bmpArr[i].width, bmpArr[i].height, bmpArr[i]);
  if(n != null) {
    // нам это удалось
    atlasArr.push(n); // сохраняем область в финальный массив
  } else {
    // места не хватает
    throw("Размер атласа надо увеличить!");
  }
}

Теперь нам осталось только отрисовать все картинки из atlasArr[x].data в координаты atlasArr[x].rect.

Если надо сделать бордюры между текстурами, то просто вставляйте объект добавляя к его ширине и высоте двойной размер бордюра, а затем при отрисовке картинок в атлас смещайте картинки на размер бордюра.

P.S. Stage3D рулез! (:
  • +6

Комментарии (21)

0

Тоже использую бинарную упаковку (для шрифтов)
0
можно по подробней об этом.
0
это для приложений написанных с использованием OpenGl и DirectX'а
+1
спасибо.
0
Т.е. ты предпочел создавать атлас в рантайме?..

Из утилит для создания атласа самая нормальная у nVidia. Называется texture atlas tool. Только результат выдает в dds, но это несложно поправить…
0
Нет, написал свою тулзу для генерации атласов и информации по сабтекстурам.
+1
Можно еще вот этим воспользоваться было:
www.texturepacker.com
0
Я его пощупал. Мало того что он глючит, так ещё и не умеет делать того, что мне нужно. Ну и его нельзя было через WebMoney или MoneyBookers купить.
0
Для владельцев блогов — бесплатно.
0
Я не владелец блога, да и я уже написал свою тулзу, которая мне удобней. Так что всё ок. (:
0
Его можно не покупать.
0
Не можно. Фришная версия на некоторые сабтекстуры какашку рисует. Либо я не понял, как её активировать, чтобы и бесплатно и всё работало. Плюс не нашёл там возможности генерить несколько атласов сразу и сотни картинок (может только в прошной версии, х3).
0
Вполне вероятно. Я пользуюсь фришной версией для сборки простейших спрайтов и все работает как часики.
0
Может ты скачал какую-то хитрую версию, которую бесплатно раздавали. Повезло. (:
0
Да не. Просто по ссылке на оф. сайте не так давно. При загрузке постоянно всплывает предложение «прокачать». Игнорирую, работаю.
0
Ну у меня он ещё и несколько сабтекстур портит надписями «купите про-версию». Ну и других недостатков хватило, чтобы написать свою тулзу.
0
Программа не портит надписями текстуру, если не использовать pro-возможности — т.е. особые алгоритмы расположения спрайтов и пр. Она должна была сообщить, какие именно, разве нет?
0
Не стал особо разбираться. Прога у меня всё равно глючит. Ну и мне нужна генерация нескольких атласов из пачки картинок, с возможностью явно указать какие картинки в каком атласе располагаются, для обеспечения правильного батчинга графики. В своей тулзе это реализовал.
0
если перед упаковкой сделать вот это
bmpArr.sortOn(['width', 'height'], Array.NUMERIC | Array.DESCENDING);

то получим очень неплохой плюс к качеству упаковки, а также стабильность, впритык забитый атлас не будет периодически рушиться из за разного порядка упаковки.
0
В своей тулзе так и сделал. Вообще для оптимизации можно по разному сортировать, чтобы плотнее упаковывать. Добавил в пост, чтобы понятнее было, спасибо.
+1
Когда понадобилось утолкать по максимуму, я просто бегал по каждому пикселю и пытался вставить в него картинку.
Не очень быстро (до нескольких минут), зато просто, поддерживает дырки и очень плотно.
  • ryzed
  • ryzed
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.