mirror of
				https://github.com/MapMakersAndProgrammers/alternativa3d-archive.git
				synced 2025-10-30 17:05:17 -07:00 
			
		
		
		
	
		
			
				
	
	
		
			285 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			ActionScript
		
	
	
	
	
	
			
		
		
	
	
			285 lines
		
	
	
		
			10 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);
 | ||
| 		}
 | ||
| 	}
 | ||
| } | 
