Files
alternativa3d-archive/Alternativa3D5/5.4/alternativa/engine3d/loaders/LoaderMTL.as
Pyogenics c58621fb99 Add A3D5
2024-09-28 17:29:26 +01:00

285 lines
9.8 KiB
ActionScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);
}
}
}