mirror of
https://github.com/MapMakersAndProgrammers/alternativa3d-archive.git
synced 2025-10-26 09:49:07 -07:00
285 lines
9.8 KiB
ActionScript
285 lines
9.8 KiB
ActionScript
package alternativa.engine3d.loaders {
|
||
import alternativa.engine3d.*;
|
||
import alternativa.types.Map;
|
||
import alternativa.utils.ColorUtils;
|
||
|
||
import flash.display.Bitmap;
|
||
import flash.display.BitmapData;
|
||
import flash.display.Loader;
|
||
import flash.events.ErrorEvent;
|
||
import flash.events.Event;
|
||
import flash.events.EventDispatcher;
|
||
import flash.events.IOErrorEvent;
|
||
import flash.events.SecurityErrorEvent;
|
||
import flash.geom.Point;
|
||
import flash.net.URLLoader;
|
||
import flash.net.URLRequest;
|
||
import flash.system.LoaderContext;
|
||
|
||
use namespace alternativa3d;
|
||
|
||
/**
|
||
* @private
|
||
* Загрузчик библиотеки материалов из файлов в формате MTL material format (Lightwave, OBJ).
|
||
* <p>
|
||
* На данный момент обеспечивается загрузка цвета, прозрачности и диффузной текстуры материала.
|
||
*/
|
||
internal class LoaderMTL extends EventDispatcher {
|
||
|
||
private static const COMMENT_CHAR:String = "#";
|
||
private static const CMD_NEW_MATERIAL:String = "newmtl";
|
||
private static const CMD_DIFFUSE_REFLECTIVITY:String = "Kd";
|
||
private static const CMD_DISSOLVE:String = "d";
|
||
private static const CMD_MAP_DIFFUSE:String = "map_Kd";
|
||
|
||
private static const REGEXP_TRIM:RegExp = /^\s*(.*?)\s*$/;
|
||
private static const REGEXP_SPLIT_FILE:RegExp = /\r*\n/;
|
||
private static const REGEXP_SPLIT_LINE:RegExp = /\s+/;
|
||
|
||
// Загрузчик файла MTL
|
||
private var fileLoader:URLLoader;
|
||
// Загрузчик файлов текстур
|
||
private var bitmapLoader:Loader;
|
||
// Контекст загрузки для bitmapLoader
|
||
private var loaderContext:LoaderContext;
|
||
// Базовый URL файла MTL
|
||
private var baseUrl:String;
|
||
|
||
// Библиотека загруженных материалов
|
||
private var _library:Map;
|
||
// Список материалов, имеющих диффузные текстуры
|
||
private var diffuseMaps:Map;
|
||
// Имя текущего материала
|
||
private var materialName:String;
|
||
// параметры текущего материала
|
||
private var currentMaterialInfo:MaterialInfo = new MaterialInfo();
|
||
|
||
alternativa3d static var stubBitmapData:BitmapData;
|
||
|
||
/**
|
||
* Создаёт новый экземпляр класса.
|
||
*/
|
||
public function LoaderMTL() {
|
||
}
|
||
|
||
/**
|
||
* Прекращение текущей загрузки.
|
||
*/
|
||
public function close():void {
|
||
try {
|
||
fileLoader.close();
|
||
} catch (e:Error) {
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Библиотека материалов. Ключами являются наименования материалов, значениями -- объекты, наследники класса
|
||
* <code>alternativa.engine3d.loaders.MaterialInfo</code>.
|
||
* @see alternativa.engine3d.loaders.MaterialInfo
|
||
*/
|
||
public function get library():Map {
|
||
return _library;
|
||
}
|
||
|
||
/**
|
||
* Метод выполняет загрузку файла материалов, разбор его содержимого, загрузку текстур при необходимости и
|
||
* формирование библиотеки материалов. После окончания работы метода посылается сообщение
|
||
* <code>Event.COMPLETE</code> и становится доступна библиотека материалов через свойство <code>library</code>.
|
||
* <p>
|
||
* При возникновении ошибок, связанных с вводом-выводом или с безопасностью, посылаются сообщения <code>IOErrorEvent.IO_ERROR</code> и
|
||
* <code>SecurityErrorEvent.SECURITY_ERROR</code> соответственно.
|
||
* <p>
|
||
* Если происходит ошибка при загрузке файла текстуры, то соответствующая текстура заменяется на текстуру-заглушку.
|
||
* <p>
|
||
* @param url URL MTL-файла
|
||
* @param loaderContext LoaderContext для загрузки файлов текстур
|
||
*
|
||
* @see #library
|
||
*/
|
||
public function load(url:String, loaderContext:LoaderContext = null):void {
|
||
this.loaderContext = loaderContext;
|
||
baseUrl = url.substring(0, url.lastIndexOf("/") + 1);
|
||
|
||
if (fileLoader == null) {
|
||
fileLoader = new URLLoader();
|
||
fileLoader.addEventListener(Event.COMPLETE, parseMTLFile);
|
||
fileLoader.addEventListener(IOErrorEvent.IO_ERROR, onError);
|
||
fileLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
|
||
|
||
bitmapLoader = new Loader();
|
||
bitmapLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onBitmapLoadComplete);
|
||
bitmapLoader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onBitmapLoadComplete);
|
||
bitmapLoader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onBitmapLoadComplete);
|
||
}
|
||
|
||
try {
|
||
fileLoader.close();
|
||
bitmapLoader.close();
|
||
} catch (e:Error) {
|
||
// Пропуск ошибки при попытке закрытия неактивных загрузчиков
|
||
}
|
||
|
||
fileLoader.load(new URLRequest(url));
|
||
}
|
||
|
||
/**
|
||
* Разбор содержимого загруженного файла материалов.
|
||
*/
|
||
private function parseMTLFile(e:Event = null):void {
|
||
var lines:Array = fileLoader.data.split(REGEXP_SPLIT_FILE);
|
||
_library = new Map();
|
||
diffuseMaps = new Map();
|
||
for each (var line:String in lines) {
|
||
parseLine(line);
|
||
}
|
||
defineMaterial();
|
||
|
||
if (diffuseMaps.isEmpty()) {
|
||
// Текстур нет, загрузка окончена
|
||
dispatchEvent(new Event(Event.COMPLETE));
|
||
} else {
|
||
// Загрузка файлов текстур
|
||
loadNextBitmap();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Разбор строки файла.
|
||
*
|
||
* @param line строка файла
|
||
*/
|
||
private function parseLine(line:String):void {
|
||
line = line.replace(REGEXP_TRIM,"$1")
|
||
if (line.length == 0 || line.charAt(0) == COMMENT_CHAR) {
|
||
return;
|
||
}
|
||
var parts:Array = line.split(REGEXP_SPLIT_LINE);
|
||
switch (parts[0]) {
|
||
case CMD_NEW_MATERIAL:
|
||
defineMaterial(parts);
|
||
break;
|
||
case CMD_DIFFUSE_REFLECTIVITY:
|
||
readDiffuseReflectivity(parts);
|
||
break;
|
||
case CMD_DISSOLVE:
|
||
readAlpha(parts);
|
||
break;
|
||
case CMD_MAP_DIFFUSE:
|
||
parseDiffuseMapLine(parts);
|
||
break;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Определение нового материала.
|
||
*/
|
||
private function defineMaterial(parts:Array = null):void {
|
||
if (materialName != null) {
|
||
_library[materialName] = currentMaterialInfo;
|
||
}
|
||
if (parts != null) {
|
||
materialName = parts[1];
|
||
currentMaterialInfo = new MaterialInfo();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Чтение коэффициентов диффузного отражения. Считываются только коэффициенты, заданные в формате r g b. Для текущей
|
||
* версии движка данные коэффициенты преобразуются в цвет материала.
|
||
*/
|
||
private function readDiffuseReflectivity(parts:Array):void {
|
||
var r:Number = Number(parts[1]);
|
||
// Проверка, заданы ли коэффициенты в виде r g b
|
||
if (!isNaN(r)) {
|
||
var g:Number = Number(parts[2]);
|
||
var b:Number = Number(parts[3]);
|
||
currentMaterialInfo.color = ColorUtils.rgb(255 * r, 255 * g, 255 * b);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Чтение коэффициента непрозрачности. Считывается только коэффициент, заданный числом
|
||
* (не поддерживается параметр -halo).
|
||
*/
|
||
private function readAlpha(parts:Array):void {
|
||
var alpha:Number = Number(parts[1]);
|
||
if (!isNaN(alpha)) {
|
||
currentMaterialInfo.alpha = alpha;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Разбор строки, задающей текстурную карту для диффузного отражения.
|
||
*/
|
||
private function parseDiffuseMapLine(parts:Array):void {
|
||
var info:MTLTextureMapInfo = MTLTextureMapInfo.parse(parts);
|
||
diffuseMaps[materialName] = info;
|
||
}
|
||
|
||
/**
|
||
* Загрузка файла следующей текстуры.
|
||
*/
|
||
private function loadNextBitmap():void {
|
||
// Установка имени текущего текстурного материала, для которого выполняется загрузка текстуры
|
||
for (materialName in diffuseMaps) {
|
||
break;
|
||
}
|
||
bitmapLoader.load(new URLRequest(baseUrl + diffuseMaps[materialName].fileName), loaderContext);
|
||
}
|
||
|
||
/**
|
||
*
|
||
*/
|
||
private function createStubBitmap():void {
|
||
if (stubBitmapData == null) {
|
||
var size:uint = 10;
|
||
stubBitmapData = new BitmapData(size, size, false, 0);
|
||
for (var i:uint = 0; i < size; i++) {
|
||
for (var j:uint = 0; j < size; j+=2) {
|
||
stubBitmapData.setPixel((i % 2) ? j : (j+1), i, 0xFF00FF);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Обработка результата загрузки файла текстуры.
|
||
*/
|
||
private function onBitmapLoadComplete(e:Event):void {
|
||
var bmd:BitmapData;
|
||
|
||
if (e is ErrorEvent) {
|
||
if (stubBitmapData == null) {
|
||
createStubBitmap();
|
||
}
|
||
bmd = stubBitmapData;
|
||
} else {
|
||
bmd = Bitmap(bitmapLoader.content).bitmapData;
|
||
}
|
||
|
||
var mtlInfo:MTLTextureMapInfo = diffuseMaps[materialName];
|
||
delete diffuseMaps[materialName];
|
||
var info:MaterialInfo = _library[materialName];
|
||
|
||
info.bitmapData = bmd;
|
||
info.repeat = mtlInfo.repeat;
|
||
info.mapOffset = new Point(mtlInfo.offsetU, mtlInfo.offsetV);
|
||
info.mapSize = new Point(mtlInfo.sizeU, mtlInfo.sizeV);
|
||
info.textureFileName = mtlInfo.fileName;
|
||
|
||
if (diffuseMaps.isEmpty()) {
|
||
dispatchEvent(new Event(Event.COMPLETE));
|
||
} else {
|
||
loadNextBitmap();
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* @param e
|
||
*/
|
||
private function onError(e:IOErrorEvent):void {
|
||
dispatchEvent(e);
|
||
}
|
||
}
|
||
} |