PDA

Просмотр полной версии : [Starling] Загрузка текстур в риалтайме


AlexCooper
09.10.2015, 04:29
Здравствуйте.
Для большей гибкости при обновлении, реализовываю поддержку текстур в качестве отдельных png-файлов.

override public function run():void {

types = model.types;

EmbeddedAssets.initialize();

var loader:Load = new Load(types.assetPath(types.logo));
loader.addEventListener(Event.COMPLETE, completeHandler );
loader.start();
}

protected function completeHandler(event:Event):void
{
var loader:Load = event.currentTarget as Load;

AssetManager.instance.uploadByteArray( types.logo, loader.data );

AssetManager.instance.loadQueue( release );
}

package net.alexscript.worker.file
{
import flash.events.EventDispatcher;
import flash.net.URLRequest;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.events.Event;
import flash.events.ProgressEvent;
import flash.events.IOErrorEvent;
import flash.utils.ByteArray;

public class Load extends EventDispatcher {


private var _file:String;

public function Load(file:String) {
_file = file;

}

public var data:ByteArray = new ByteArray();

public function start():void{

trace('start load: '+_file);
var request:URLRequest = new URLRequest(_file);

var urlLoader:URLLoader = new URLLoader(request);
urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
urlLoader.addEventListener(IOErrorEvent.IO_ERROR, completeError);
urlLoader.addEventListener(ProgressEvent.PROGRESS, progressHandler );
urlLoader.addEventListener(Event.COMPLETE, onLoadedAndSave);
}

private function completeError(e:IOErrorEvent):void {
trace('error load: '+_file);
dispatchEvent(new Event(Event.COMPLETE));

}

private function onLoadedAndSave(e:Event):void {
data.writeBytes(e.currentTarget.data,0,e.currentTarget.data.length);
trace('complete load: '+_file);
dispatchEvent(new Event(Event.COMPLETE));
}

private function progressHandler(e:ProgressEvent):void {
dispatchEvent(new ProgressEvent(ProgressEvent.PROGRESS,false,false,e.bytesLoaded+data.length,e.bytesTotal));
}
}
}

package com.state.model
{
import flash.events.EventDispatcher;
import flash.utils.ByteArray;

import starling.textures.Texture;
import starling.utils.AssetManager;

public class AssetManager extends EventDispatcher
{

public static function get instance():com.state.model.AssetManager
{
if (!_instance) _instance = new com.state.model.AssetManager();

return _instance;
}

private static var _instance:com.state.model.AssetManager;

public function AssetManager()
{
if (!_instance) {
super();
_instance = this;
png = new PNGDecoder();
}
}

public function uploadByteArray( key:String, byteArray:ByteArray ):void {
assetsMgr.addTexture( key, Texture.fromBitmapData(png.decode(byteArray)) );
}

public function loadQueue(_callback:Function):void
{
assetsMgr.loadQueue(function(ratio:Number):void
{
Debug.log(' texture load '+ratio);
if (ratio == 1.0)
_callback();
});
}

public function get assetsMgr():starling.utils.AssetManager {

if (!assets) assets = new starling.utils.AssetManager();

return assets;
}

private var assets:starling.utils.AssetManager;
private var png:PNGDecoder;

}
}

package
{
import flash.display.BitmapData;
import flash.utils.ByteArray;
import flash.geom.Matrix;
import flash.geom.Rectangle;

/*******************************
PNGDecoder
Author: Jerion

A class that decodes png byte arrays and generates a bitmapdata.
This is my first attempt at decoding images, so there are a lot of
things that are not implemented.

So far, it can only decode truecolour with alpha png images with
no interlacing, no filter, and bit depth of 8.
********************************/

public class PNGDecoder {
private const IHDR:uint = 0x49484452;
private const PLTE:uint = 0x504c5445;
private const IDAT:uint = 0x49444154;
private const IEND:uint = 0x49454e44;

private var imgWidth:uint = 0;
private var imgHeight:uint = 0;

//file info, but not used yet.
private var bitDepth:uint = 0;
private var colourType:uint = 0;
private var compressionMethod:uint = 0;
private var filterMethod:uint = 0;
private var interlaceMethod:uint = 0;

private var chunks:Array;
private var input:ByteArray;
private var output:ByteArray;

//recieves the bytearray and returns a bitmapdata
public function decode(ba:ByteArray):BitmapData {
chunks = new Array();
input = new ByteArray();
output = new ByteArray();

input = ba;

input.position = 0;

if (!readSignature()) throw new Error("wrong signature");

getChunks();

for (var i:int = 0; i < chunks.length; ++i) {
switch(chunks[i].type) {
case IHDR: processIHDR(i); break;
//case PLTE: processPLTE(i); break;
case IDAT: processIDAT(i); break;
//case IEND: processIEND(i); break;
}
}

//Since the image is inverted in x and y, I have to flip it using a Matrix object. There should be a better solution for this..
var bd0:BitmapData = new BitmapData(imgWidth, imgHeight);
var bd1:BitmapData = new BitmapData(imgWidth, imgHeight, true, 0xffffff);

if (output.length > 0 && (imgWidth * imgHeight * 4) == output.length) {
output.position = 0;
bd0.setPixels(new Rectangle(0,0,imgWidth,imgHeight), output);

var mat:Matrix = new Matrix();
mat.scale(-1,-1);
mat.translate(imgWidth, imgHeight);

bd1.draw(bd0, mat);
}

return bd1;
}

//read the header of the image
private function processIHDR(index:uint):void {
input.position = chunks[index].position;

imgWidth = input.readUnsignedInt();
imgHeight = input.readUnsignedInt();

//file info, but is not used yet
bitDepth = input.readUnsignedByte();
colourType = input.readUnsignedByte();
compressionMethod = input.readUnsignedByte();
filterMethod = input.readUnsignedByte();
interlaceMethod = input.readUnsignedByte();
}

//This can't handle multiple IDATs yet, and it can only decode filter 0 scanlines.
private function processIDAT(index:uint):void {
var tmp:ByteArray = new ByteArray();

var pixw:uint = imgWidth * 4;

tmp.writeBytes(input, chunks[index].position, chunks[index].length);
tmp.uncompress();

for (var i:int = tmp.length - 1; i > 0; --i) {
if (i % (pixw + 1) != 0) {
var a:uint = tmp[i];
var b:uint = tmp[i-1];
var g:uint = tmp[i-2];
var r:uint = tmp[i-3];

output.writeByte(a);
output.writeByte(r);
output.writeByte(g);
output.writeByte(b);

i -= 3;
}
}
}

private function getChunks():void {
var pos:uint = 0;
var len:uint = 0;
var type:uint = 0;

var loopEnd:int = input.length;

while (input.position < loopEnd) {
len = input.readUnsignedInt();
type = input.readUnsignedInt();
pos = input.position;

input.position += len;
input.position += 4; //crc block. It is ignored right now, but if you want to retrieve it, replace this line with "input.readUnsignedInt()"

chunks.push({position: pos, length: len, type: type});
}
}

private function readSignature():Boolean {
return (input.readUnsignedInt() == 0x89504e47 && input.readUnsignedInt() == 0x0D0A1A0A);
}

//transform the chunk type to a string representation
private function fixType(num:uint):String {
var ret:String = "";
var str:String = num.toString(16);

while (str.length < 8) str = "0" + str;

ret += String.fromCharCode(parseInt(str.substr(0,2), 16));
ret += String.fromCharCode(parseInt(str.substr(2,2), 16));
ret += String.fromCharCode(parseInt(str.substr(4,2), 16));
ret += String.fromCharCode(parseInt(str.substr(6,2), 16));

return ret;
}

}
}

Вопрос в том, на сколько это оправданно со сторонны поддержки проекта, или же стоит смотреть в сторону ATF, без этих всех велосипедов.

caseyryan
09.10.2015, 06:39
или же стоит смотреть в сторону ATF
ATF вообще какая-то лажа. Нормальных инструментов для создания нет, все на уровне командной строки, при этом надо еще и в степени двойки все сохранять сразу. Долго с ним ковырялся, пришел к выводу, что png лучше. Ради экономии нескольких мегабайт памяти не стоит оно того.

По поводу вопроса: зачем вообще это нужно? Чем старлинговский ассет менеджер не устраивает? Отличная штука. Я всегда пользуюсь только им и все отлично. Так же можно хоть когда по запросу подгрузить нужную текстуру

Zebestov
09.10.2015, 11:15
ATF, если я не ошибаюсь (а я могу!), шикарен тем, что в GPU хранится сжатым(?).
Но если это не так (ответа в свое время в "ленивом режиме" я так и не нашел), то да, ATF — вообще какая-то лажа.

illuzor
09.10.2015, 16:09
Нормальных инструментов для создания нетЧто понимается под нормальными инструментами? adobe даёт отличные тулзы с подробной документацией, которые с лёгкостью оборачиваются в bat/ant/gradle (нужное подчеркнуть). К тому же, TexturePacker давно умеет экспортировать в atf.
ATF, если я не ошибаюсь (а я могу!), шикарен тем, что в GPU хранится сжатым(?).Размер atf файла меньше, чем размер аналогичного png, он грузится в gpu память быстрей, чем png и рендерится тоже быстрей. И да, он занимает меньше памяти gpu. Если для проектов с парой атласов это не критично, то для игр с большим количеством графики выгода очень заметна. Вплоть до того, что игра с png, вылетающая на слабых девайсах, при использовании atf работает нормально.
Также atf использует разные "родные" типы сжатия для разных платформ. Можно включить в файлы только версии для используемых платформ, что ещё уменьшит вес файла.

Zebestov
09.10.2015, 16:22
Вот! Если таки да, то гут. Иначе — фигня какая-то битая.

AlexCooper
09.10.2015, 17:27
Нормальных инструментов для создания нет, все на уровне командной строки
Командная строка тоже программируется) Была мысль поднять на ейре автобилдер для ане с селектором sdk, вернее батник на msdos'e уже написал, только Воздух подключить, но для кмд у меня фикс есть и она мне не страшна)

Чем старлинговский ассет менеджер не устраивает?Если оставлю png, байтеррей нужен не по прямому назначению.


ATF — вообще какая-то лажа. Есть какие-то особенности?

Zebestov
09.10.2015, 17:31
Выше написали про плюс — компактность размещения в памяти GPU.
Минус — как минимум джипежность. Целые семинары о том, как делать так, чтобы ATF-картинку мылом не покрыло.

AlexCooper
09.10.2015, 17:45
TexturePacker Как на меня он шикарен ( единственно что напрягает - пустые места )

Zebestov
09.10.2015, 17:57
В смысле "пустые места"?

AlexCooper
09.10.2015, 18:29
В смысле "пустые места"?

Как мувиклипах, при не парном количестве кадров ( возможно рукикрюки )

Zebestov
09.10.2015, 18:36
Все равно не понял. Там просто есть padding, border, ты об этом? Или о том, что в атласе так или иначе зачастую остается неиспользованное место?

caseyryan
09.10.2015, 18:53
Что понимается под нормальными инструментами? adobe даёт отличные тулзы с подробной документацией, которые с лёгкостью оборачиваются в bat/ant/gradle (нужное подчеркнуть)
Ты называешь эту хрень нормальными инструментами?
Для меня нормальные инструменты - это фотошоп или иллюстратор. Сделал картинку, сохранил в нужном формате. А всё, что работает через разные командные строки и энты - это уже не нормальные. Терпеть не могу все эти заморочки. Чтобы сохранить картинку с нужном формате надо полдня с бубном танцевать и кучу документации перекурить. Это как раз одна из причин по которым мне не нравится линукс. GUI рулит.

illuzor
09.10.2015, 19:03
Чтобы сохранить картинку с нужном формате надо полдня с бубном танцевать и кучу документации перекурить.Один час чтения документации и экспериментов - и готов универсальный ant конфиг, который конвертит все png в atf по одному клику. И вообще, консольные утилиты часто сильно удобней gui. Тот же консольный вариант TexturePacker сильно упрощает жизнь.

GUI рулит. -> TexturePacker давно умеет экспортировать в atf.

AlexCooper
10.10.2015, 00:50
GUI рулит.
Как по мне, вот что действительно вызывает эндорфины
;set ADT_PATH=D:\sdk\air13+flex4.6\bin\adt
;set ANDROID_PLATFORM=-platform Android-ARM -C android .
;set DEFAULT_PLATFORM=-platform default -C default
;set IOS_PLATFORM=-platform iPhone-ARM -C ios .
;set IOS_SIMULATOR=-platform iPhone-x86 -C iosSimulator .
;set PACKAGE_TARGET=-package -target ane PMANE.ane extension.xml
;set PLATFORM_OPTIONS=-platformoptions platformoptions.xml
;set SWC=-swc PMANE.swc

;set EXTENSION_BINARY=Release
#EXTENSION_BINARY=Debug
;timeout 1
;mkdir android;
;mkdir default;
;mkdir ios;
;mkdir iosSimulator;
;timeout 1
;unzip.exe -o PMANE.swc
;timeout 1
;copy library.swf android/library.swf /y
;copy library.swf default/library.swf /y
;copy library.swf ios/library.swf /y
;copy library.swf iosSimulator/library.swf /y
;copy lib.jar android/lib.jar /y
;timeout 1
;call %ADT_PATH%%PACKAGE_TARGET%%SWC%%ANDROID_PLATFORM%%IOS_PLATFORM%%IOS_SIMULATOR%%DEFAULT_PLATFORM%

;del catalog.xml

;del library.swf
;timeout 1
;RMDIR android /s /q
;RMDIR default /s /q
;RMDIR ios /s /q
;RMDIR iosSimulator /s /q
;timeout 3
;exit

Добавлено через 49 минут
Или о том, что в атласе так или иначе зачастую остается неиспользованное место?
Да. Это как то меня слегка напрягает. Когда размеры текстур идентичны и можно разложить в линию или столбцем то пустого не будет, в TexturePacker не получилось на "мувиклипе"