// ______________________________________________________________________ BITMAP FILE MATERIAL package org.papervision3d.materials { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Graphics; import flash.display.Loader; import flash.events.*; import flash.net.URLRequest; import flash.utils.Dictionary; import flash.geom.Matrix; import org.papervision3d.Papervision3D; import org.papervision3d.core.Number3D; import org.papervision3d.core.geom.Face3D; import org.papervision3d.core.geom.Vertex2D; import org.papervision3d.core.proto.MaterialObject3D; import org.papervision3d.events.FileLoadEvent; import org.papervision3d.objects.DisplayObject3D; /** * The ShadedBitmapFileMaterial class creates a shaded texture by loading a bitmap from an external file. * * Materials collect data about how objects appear when rendered. */ public class ShadedBitmapFileMaterial extends BitmapMaterial { // ___________________________________________________________________ PUBLIC public var light:Number3D; /** * The URL that has been requested. */ public var url :String = ""; /** * Whether or not the texture has been loaded. */ public var loaded :Boolean; /** * Function to call when the last image has loaded. */ static public var callback :Function; /** * The color to use in materials before loading has finished. */ //static public var LOADING_COLOR :int = MaterialObject3D.DEFAULT_COLOR; static public var LOADING_COLOR :int = 0xEEEEEE; /** * A texture object. */ override public function get texture():Object { return this._texture; } /** * @private */ override public function set texture( asset:Object ):void { if( asset is String == false ) { Papervision3D.log("Error: ShadedBitmapFileMaterial.texture requires a String for the texture"); return; } bitmap = createBitmapFromURL( String(asset) ); _texture = asset; } // ___________________________________________________________________ NEW /** * The ShadedBitmapFileMaterial class creates a shaded texture by loading a bitmap from an external file. * * @param url The URL of the requested bitmap file. * @param initObject [optional] - An object that contains additional properties with which to populate the newly created material. */ public function ShadedBitmapFileMaterial( url :String="" ) { // light this.light = new Number3D(0, 0, 100); // save URL reference this.url = url; // set the loaded flag this.loaded = false; // Loading color this.fillAlpha = 1; this.fillColor = LOADING_COLOR; // face normals needsFaceNormals = true; // start the loading by setting the texture if( url.length > 0 ) texture = url; } // ___________________________________________________________________ CREATE BITMAP /** * [internal-use] * * @param asset * @return */ protected function createBitmapFromURL( asset:String ):BitmapData { // Empy string? if( asset == "" ) { return null; } // Already loaded? else if( _loadedBitmaps[ asset ] ) { var bmp:BitmapData = _loadedBitmaps[ asset ]; bitmap = super.createBitmap( bmp ); this.loadComplete(); return bmp; } else { queueBitmap( asset ); } return null; } // ___________________________________________________________________ QUEUE BITMAP private function queueBitmap( file:String ):void { // New filename? if( ! _subscribedMaterials[ file ] ) { // Queue file _waitingBitmaps.push( file ); // Init subscription _subscribedMaterials[ file ] = new Array(); } // Subscribe material _subscribedMaterials[ file ].push( this ); // Launch loading if needed if( _loadingIdle ) loadNextBitmap(); } // ___________________________________________________________________ LOAD NEXT BITMAP private function loadNextBitmap():void { // Retrieve next filename in queue var file:String = _waitingBitmaps[0]; var request:URLRequest = new URLRequest( file ); var bitmapLoader:Loader = new Loader(); bitmapLoader.contentLoaderInfo.addEventListener( ProgressEvent.PROGRESS, loadBitmapProgressHandler ); bitmapLoader.contentLoaderInfo.addEventListener( Event.COMPLETE, loadBitmapCompleteHandler ); try { // Load bitmap bitmapLoader.load( request ); // Save original url _loaderUrls[ bitmapLoader ] = file; // Busy loading _loadingIdle = false; Papervision3D.log( "ShadedBitmapFileMaterial: Loading bitmap from " + file ); } catch( error:Error ) { // Remove from queue _waitingBitmaps.shift(); // Loading finished _loadingIdle = true; Papervision3D.log( "[ERROR] ShadedBitmapFileMaterial: Unable to load file " + error.message ); } } // ___________________________________________________________________ LOAD BITMAP PROGRESS HANDLER private function loadBitmapProgressHandler( e:ProgressEvent ):void { var progressEvent:FileLoadEvent = new FileLoadEvent( FileLoadEvent.LOAD_PROGRESS, url, e.bytesLoaded, e.bytesTotal); dispatchEvent( progressEvent ); } // ___________________________________________________________________ LOAD BITMAP COMPLETE HANDLER private function loadBitmapCompleteHandler( e:Event ):void { var loader:Loader = Loader( e.target.loader ); var loadedBitmap:Bitmap = Bitmap( loader.content ); // Retrieve original url var url:String = _loaderUrls[ loader ]; // Retrieve loaded bitmapdata var bmp:BitmapData = super.createBitmap( loadedBitmap.bitmapData ); // Update subscribed materials for each( var material:ShadedBitmapFileMaterial in _subscribedMaterials[ url ] ) { material.bitmap = bmp; material.maxU = this.maxU; material.maxV = this.maxV; material.resetMapping(); material.loadComplete(); } // Include in library _loadedBitmaps[ url ] = bmp; // Remove from queue _waitingBitmaps.shift(); // Queue finished? if( _waitingBitmaps.length > 0 ) { // Continue loading loadNextBitmap(); } else { // Loading finished _loadingIdle = true; if( Boolean( callback ) ) callback(); } } // ___________________________________________________________________ LOAD COMPLETE private function loadComplete():void { this.fillAlpha = 1; this.fillColor = 0; this.loaded = true; // Dispatch event var fileEvent:FileLoadEvent = new FileLoadEvent( FileLoadEvent.LOAD_COMPLETE, this.url ); this.dispatchEvent( fileEvent ); } /** * drawFace3D */ override public function drawFace3D(instance:DisplayObject3D, face3D:Face3D, graphics:Graphics, v0:Vertex2D, v1:Vertex2D, v2:Vertex2D):int { var map:Matrix = (uvMatrices[face3D] || transformUV(face3D, instance)), x0:Number = v0.x, y0:Number = v0.y, x1:Number = v1.x, y1:Number = v1.y, x2:Number = v2.x, y2:Number = v2.y; // BITMAP: if( bitmap ){ // ---------------------------------------------------------------- // BITMAP: // ---------------------------------------------------------------- _triMatrix.a = x1 - x0; _triMatrix.b = y1 - y0; _triMatrix.c = x2 - x0; _triMatrix.d = y2 - y0; _triMatrix.tx = x0; _triMatrix.ty = y0; _localMatrix.a = map.a; _localMatrix.b = map.b; _localMatrix.c = map.c; _localMatrix.d = map.d; _localMatrix.tx = map.tx; _localMatrix.ty = map.ty; _localMatrix.concat(_triMatrix); graphics.beginBitmapFill( bitmap, _localMatrix, tiled, smooth); graphics.moveTo( x0, y0 ); graphics.lineTo( x1, y1 ); graphics.lineTo( x2, y2 ); graphics.lineTo( x0, y0 ); graphics.endFill(); // ---------------------------------------------------------------- // SHADING: // ---------------------------------------------------------------- var s:Number; var lt:Number3D = new Number3D(); light.copyTo(lt); lt.normalize(); s = Number3D.dot(face3D.face3DInstance.faceNormal, lt); if (doubleSided) { s = Math.abs(s); } graphics.beginFill( 0, 1-s ); graphics.moveTo( v0.x, v0.y ); graphics.lineTo( v1.x, v1.y ); graphics.lineTo( v2.x, v2.y ); graphics.lineTo( v0.x, v0.y ); graphics.endFill(); } else { graphics.beginFill( fillColor, fillAlpha ); graphics.moveTo( v0.x, v0.y ); graphics.lineTo( v1.x, v1.y ); graphics.lineTo( v2.x, v2.y ); graphics.lineTo( v0.x, v0.y ); graphics.endFill(); } return 1; } // ___________________________________________________________________ PRIVATE // Filenames in the queue static private var _waitingBitmaps :Array = new Array(); // URLs per loader static private var _loaderUrls :Dictionary = new Dictionary(); // Loaded bitmap library static private var _loadedBitmaps :Object = new Object(); // Materials subscribed to the loading queue static private var _subscribedMaterials :Object = new Object(); // Loading status static private var _loadingIdle :Boolean = true; } }