diff --git a/source/flx3d/Flx3DCamera.hx b/source/flx3d/Flx3DCamera.hx index d4f76229e..48089857b 100644 --- a/source/flx3d/Flx3DCamera.hx +++ b/source/flx3d/Flx3DCamera.hx @@ -25,19 +25,25 @@ import flx3d.Flx3DUtil; import haxe.io.Path; import openfl.Assets; #end - import flixel.FlxCamera; -class Flx3DCamera extends FlxCamera { +class Flx3DCamera extends FlxCamera +{ #if THREE_D_SUPPORT private static var __3DIDS:Int = 0; public var view:View3D; var meshes:Array = []; - public function new(X:Int = 0, Y:Int = 0, Width:Int = 0, Height:Int = 0, DefaultZoom:Float = 1) { + + public function new(X:Int = 0, Y:Int = 0, Width:Int = 0, Height:Int = 0, DefaultZoom:Float = 1) + { if (!Flx3DUtil.is3DAvailable()) - throw "[Flx3DCamera] 3D is not available on this platform. Stages in use: " + Flx3DUtil.getTotal3D() + ", Max stages allowed: " + FlxG.stage.stage3Ds.length + "."; + throw "[Flx3DCamera] 3D is not available on this platform. Stages in use: " + + Flx3DUtil.getTotal3D() + + ", Max stages allowed: " + + FlxG.stage.stage3Ds.length + + "."; super(X, Y, Width, Height, DefaultZoom); __cur3DStageID = __3DIDS++; @@ -49,7 +55,8 @@ class Flx3DCamera extends FlxCamera { FlxG.stage.addChild(view); } - public override function render() { + public override function render() + { super.render(); view.x = FlxG.game.x + FlxG.game.scaleX * (flashSprite.x + flashSprite.scaleX * (_scrollRect.x + _scrollRect.scaleX * (_scrollRect.scrollRect.x))); @@ -65,7 +72,8 @@ class Flx3DCamera extends FlxCamera { view.render(); } - public function addModel(assetPath:String, callback:Asset3DEvent->Void, ?texturePath:String, smoothTexture:Bool = true) { + public function addModel(assetPath:String, callback:Asset3DEvent->Void, ?texturePath:String, smoothTexture:Bool = true) + { var model = Assets.getBytes(assetPath); if (model == null) throw 'Model at ${assetPath} was not found.'; @@ -79,36 +87,43 @@ class Flx3DCamera extends FlxCamera { if (texturePath != null) material = new TextureMaterial(Cast.bitmapTexture(Assets.getBitmapData(texturePath, true, false)), smoothTexture); - return loadData(model, context, switch(Path.extension(assetPath).toLowerCase()) { + return loadData(model, context, switch (Path.extension(assetPath).toLowerCase()) + { case "dae": new DAEParser(); case "md2": new MD2Parser(); case "md5": new MD5MeshParser(); case "awd": new AWDParser(); - default: new OBJParser(); - }, (event:Asset3DEvent) -> { - if (event.asset != null && event.asset.assetType == Asset3DType.MESH) { - var mesh:Mesh = cast event.asset; - if (material != null) - mesh.material = material; - meshes.push(mesh); - } - callback(event); - }); + default: new OBJParser(); + }, (event:Asset3DEvent) -> + { + if (event.asset != null && event.asset.assetType == Asset3DType.MESH) + { + var mesh:Mesh = cast event.asset; + if (material != null) + mesh.material = material; + meshes.push(mesh); + } + callback(event); + }); } private var __cur3DStageID:Int; private var _loaders:Map = []; - private function loadData(data:Dynamic, context:AssetLoaderContext, parser:ParserBase, onAssetCallback:Asset3DEvent->Void):AssetLoaderToken { + private function loadData(data:Dynamic, context:AssetLoaderContext, parser:ParserBase, onAssetCallback:Asset3DEvent->Void):AssetLoaderToken + { var token:AssetLoaderToken; var lib:Asset3DLibraryBundle = Asset3DLibraryBundle.getInstance('Flx3DView-${__cur3DStageID}'); token = lib.loadData(data, context, null, parser); - token.addEventListener(Asset3DEvent.ASSET_COMPLETE, (event:Asset3DEvent) -> { + token.addEventListener(Asset3DEvent.ASSET_COMPLETE, (event:Asset3DEvent) -> + { // ! Taken from Loader3D https://github.com/openfl/away3d/blob/master/away3d/loaders/Loader3D.hx#L207-L232 - if (event.type == Asset3DEvent.ASSET_COMPLETE) { - var obj:ObjectContainer3D = switch (event.asset.assetType) { + if (event.type == Asset3DEvent.ASSET_COMPLETE) + { + var obj:ObjectContainer3D = switch (event.asset.assetType) + { case Asset3DType.LIGHT: expect(event.asset, LightBase); case Asset3DType.CONTAINER: expect(event.asset, ObjectContainer3D); case Asset3DType.MESH: expect(event.asset, Mesh); @@ -126,25 +141,29 @@ class Flx3DCamera extends FlxCamera { onAssetCallback(event); }); - token.addEventListener(LoaderEvent.RESOURCE_COMPLETE, (_) -> { + token.addEventListener(LoaderEvent.RESOURCE_COMPLETE, (_) -> + { trace("Loader Finished..."); }); - _loaders.set(lib,token); + _loaders.set(lib, token); return token; } - override function destroy() { + override function destroy() + { if (meshes != null) - for(mesh in meshes) + for (mesh in meshes) mesh.dispose(); var bundle = Asset3DLibraryBundle.getInstance('Flx3DView-${__cur3DStageID}'); bundle.stopAllLoadingSessions(); @:privateAccess { - if (bundle._loadingSessions != null) { - for(load in bundle._loadingSessions) { + if (bundle._loadingSessions != null) + { + for (load in bundle._loadingSessions) + { load.dispose(); } } @@ -152,10 +171,12 @@ class Flx3DCamera extends FlxCamera { } FlxG.stage.removeChild(view); - try { + try + { view.dispose(); - } catch(e) { - + } + catch (e) + { } super.destroy(); @@ -164,4 +185,4 @@ class Flx3DCamera extends FlxCamera { public function addChild(c) view.scene.addChild(c); #end -} \ No newline at end of file +} diff --git a/source/flx3d/Flx3DView.hx b/source/flx3d/Flx3DView.hx index 59413e77d..9004d27af 100644 --- a/source/flx3d/Flx3DView.hx +++ b/source/flx3d/Flx3DView.hx @@ -25,20 +25,27 @@ import away3d.utils.Utils.expect; #end // FlxView3D with helpers for easier updating -class Flx3DView extends FlxView3D { +class Flx3DView extends FlxView3D +{ #if THREE_D_SUPPORT private static var __3DIDS:Int = 0; var meshes:Array = []; - public function new(x:Float = 0, y:Float = 0, width:Int = -1, height:Int = -1) { + + public function new(x:Float = 0, y:Float = 0, width:Int = -1, height:Int = -1) + { if (!Flx3DUtil.is3DAvailable()) - throw "[Flx3DView] 3D is not available on this platform. Stages in use: " + Flx3DUtil.getUsed3D() + ", Max stages allowed: " + Flx3DUtil.getTotal3D() + "."; + throw "[Flx3DView] 3D is not available on this platform. Stages in use: " + + Flx3DUtil.getUsed3D() + + ", Max stages allowed: " + + Flx3DUtil.getTotal3D() + + "."; super(x, y, width, height); __cur3DStageID = __3DIDS++; } - public function addModel(assetPath:String, callback:Asset3DEvent->Void, ?texturePath:String, smoothTexture:Bool = true) { - + public function addModel(assetPath:String, callback:Asset3DEvent->Void, ?texturePath:String, smoothTexture:Bool = true) + { var model = Assets.getBytes(assetPath); if (model == null) throw 'Model at ${assetPath} was not found.'; @@ -52,37 +59,44 @@ class Flx3DView extends FlxView3D { if (texturePath != null) material = new TextureMaterial(Cast.bitmapTexture(Assets.getBitmapData(texturePath, true, false)), smoothTexture); - return loadData(model, context, switch(Path.extension(assetPath).toLowerCase()) { + return loadData(model, context, switch (Path.extension(assetPath).toLowerCase()) + { case "dae": new DAEParser(); case "md2": new MD2Parser(); case "md5": new MD5MeshParser(); case "awd": new AWDParser(); - default: new OBJParser(); - }, (event:Asset3DEvent) -> { - if (event.asset != null && event.asset.assetType == Asset3DType.MESH) { - var mesh:Mesh = cast event.asset; - if (material != null) - mesh.material = material; - meshes.push(mesh); - } - callback(event); - }); + default: new OBJParser(); + }, (event:Asset3DEvent) -> + { + if (event.asset != null && event.asset.assetType == Asset3DType.MESH) + { + var mesh:Mesh = cast event.asset; + if (material != null) + mesh.material = material; + meshes.push(mesh); + } + callback(event); + }); } private var __cur3DStageID:Int; private var _loaders:Map = []; - private function loadData(data:Dynamic, context:AssetLoaderContext, parser:ParserBase, onAssetCallback:Asset3DEvent->Void):AssetLoaderToken { + private function loadData(data:Dynamic, context:AssetLoaderContext, parser:ParserBase, onAssetCallback:Asset3DEvent->Void):AssetLoaderToken + { var token:AssetLoaderToken; var lib:Asset3DLibraryBundle; lib = Asset3DLibraryBundle.getInstance('Flx3DView-${__cur3DStageID}'); token = lib.loadData(data, context, null, parser); - token.addEventListener(Asset3DEvent.ASSET_COMPLETE, (event:Asset3DEvent) -> { + token.addEventListener(Asset3DEvent.ASSET_COMPLETE, (event:Asset3DEvent) -> + { // ! Taken from Loader3D https://github.com/openfl/away3d/blob/master/away3d/loaders/Loader3D.hx#L207-L232 - if (event.type == Asset3DEvent.ASSET_COMPLETE) { - var obj:ObjectContainer3D = switch (event.asset.assetType) { + if (event.type == Asset3DEvent.ASSET_COMPLETE) + { + var obj:ObjectContainer3D = switch (event.asset.assetType) + { case Asset3DType.LIGHT: expect(event.asset, LightBase); case Asset3DType.CONTAINER: expect(event.asset, ObjectContainer3D); case Asset3DType.MESH: expect(event.asset, Mesh); @@ -100,26 +114,29 @@ class Flx3DView extends FlxView3D { onAssetCallback(event); }); - token.addEventListener(LoaderEvent.RESOURCE_COMPLETE, (_) -> { + token.addEventListener(LoaderEvent.RESOURCE_COMPLETE, (_) -> + { trace("Loader Finished..."); - }); - _loaders.set(lib,token); + _loaders.set(lib, token); return token; } - override function destroy() { + override function destroy() + { if (meshes != null) - for(mesh in meshes) + for (mesh in meshes) mesh.dispose(); var bundle = Asset3DLibraryBundle.getInstance('Flx3DView-${__cur3DStageID}'); bundle.stopAllLoadingSessions(); @:privateAccess { - if (bundle._loadingSessions != null) { - for(load in bundle._loadingSessions) { + if (bundle._loadingSessions != null) + { + for (load in bundle._loadingSessions) + { load.dispose(); } } @@ -132,4 +149,4 @@ class Flx3DView extends FlxView3D { public function addChild(c) view.scene.addChild(c); #end -} \ No newline at end of file +} diff --git a/source/flx3d/FlxView3D.hx b/source/flx3d/FlxView3D.hx index 11137eed3..b07b78e21 100644 --- a/source/flx3d/FlxView3D.hx +++ b/source/flx3d/FlxView3D.hx @@ -1,6 +1,7 @@ package flx3d; #if THREE_D_SUPPORT +import flx3d._internal.TextureView3D; import away3d.containers.View3D; import away3d.library.assets.IAsset; import flixel.FlxG; @@ -18,7 +19,35 @@ import flixel.FlxSprite; class FlxView3D extends FlxSprite { #if THREE_D_SUPPORT - @:noCompletion private var bmp:BitmapData; + @:noCompletion private var bmp:BitmapData = null; + private var _textureView:TextureView3D; + private var legacyRender:Bool = false; + + // With new rendering, it seems to only work if 2 or more views are being rendered. + // This workaround creates a second instance if there is only one view. + // "There is nothing more permanent than a temporary solution" + // -idk who said this i just wanted to quote it + private static var workaroundInstance:FlxView3D; + private static var createdWorkaround:Bool = false; + + private inline function createWorkaround() + { + if (!createdWorkaround && !legacyRender) + { + createdWorkaround = true; + workaroundInstance = new FlxView3D(0, 0, 1, 1); + FlxG.state.add(workaroundInstance); + } + } + + private inline function destroyWorkaround() + { + if (workaroundInstance == this) + { + workaroundInstance = null; + createdWorkaround = false; + } + } /** * The Away3D View @@ -40,19 +69,39 @@ class FlxView3D extends FlxSprite */ public function new(x:Float = 0, y:Float = 0, width:Int = -1, height:Int = -1) { + legacyRender = false; + super(x, y); + if (legacyRender) + { + view = new View3D(); + } + else + { + _textureView = new TextureView3D(); + _textureView.onUpdateBitmap = function(bitmap) + { + bmp = bitmap; + loadGraphic(bmp); + } + + view = _textureView; + } - view = new View3D(); view.visible = false; - view.width = width == -1 ? FlxG.width : width; - view.height = height == -1 ? FlxG.height : height; + this.width = width == -1 ? FlxG.width : width; + this.height = height == -1 ? FlxG.height : height; + if (legacyRender) + { + bmp = new BitmapData(Std.int(view.width), Std.int(view.height), true, 0x0); + loadGraphic(bmp); + } view.backgroundAlpha = 0; FlxG.stage.addChildAt(view, 0); - bmp = new BitmapData(Std.int(view.width), Std.int(view.height), true, 0x0); - loadGraphic(bmp); + createWorkaround(); } /** @@ -84,26 +133,34 @@ class FlxView3D extends FlxSprite view.dispose(); view = null; } + + destroyWorkaround(); } @:noCompletion override function draw() { - super.draw(); - if (dirty3D) { - view.visible = false; - FlxG.stage.addChildAt(view, 0); - - var old = FlxG.game.filters; - FlxG.game.filters = null; - - view.renderer.queueSnapshot(bmp); - view.render(); - - FlxG.game.filters = old; - FlxG.stage.removeChild(view); + if (legacyRender) + { + view.visible = false; + FlxG.stage.addChildAt(view, 0); + + var old = FlxG.game.filters; + FlxG.game.filters = null; + + view.renderer.queueSnapshot(bmp); + view.render(); + + FlxG.game.filters = old; + FlxG.stage.removeChild(view); + } + else + { + view.render(); + } } + super.draw(); } @:noCompletion override function set_width(newWidth:Float):Float diff --git a/source/flx3d/_internal/TextureView3D.hx b/source/flx3d/_internal/TextureView3D.hx new file mode 100644 index 000000000..efce9b9a4 --- /dev/null +++ b/source/flx3d/_internal/TextureView3D.hx @@ -0,0 +1,177 @@ +package flx3d._internal; + +import flixel.FlxG; +import haxe.Exception; +import away3d.containers.View3D; +import away3d.containers.Scene3D; +import away3d.cameras.Camera3D; +import away3d.core.render.RendererBase; +import away3d.core.managers.Stage3DManager; +import away3d.core.managers.Stage3DProxy; +import openfl.display3D.textures.TextureBase; +import openfl.display3D.textures.RectangleTexture; +import openfl.display3D.Context3D; +import openfl.display.BitmapData; +import openfl.events.Event; +import openfl.geom.Point; + +class TextureView3D extends View3D +{ + public var bitmap:BitmapData; + + private var _framebuffer:TextureBase = null; + + public var onUpdateBitmap:(BitmapData) -> Void; + + /** + * Prevents the engine from disposing Flixel's Stage3D/Context3D instance + */ + public override function dispose() @:privateAccess { + _stage3DProxy = null; + super.dispose(); + } + + private override function set_height(value:Float):Float + { + if (_height == value) + return value; + super.set_height(value); + _createFramebuffer(); + return value; + } + + private override function set_width(value:Float):Float + { + if (_width == value) + return value; + super.set_width(value); + _createFramebuffer(); + return value; + } + + public function new(scene:Scene3D = null, camera:Camera3D = null, renderer:RendererBase = null, forceSoftware:Bool = false, profile:String = "baseline", + contextIndex:Int = -1) + { + super(scene, camera, renderer, forceSoftware, profile, contextIndex); + + _stage3DProxy = Stage3DManager.getInstance(FlxG.stage).getStage3DProxy(0); + _createFramebuffer(); + } + + private function _createFramebuffer() + { + if (width == 0 || height == 0) + return; + if (_framebuffer != null) + _framebuffer.dispose(); + _framebuffer = FlxG.stage.context3D.createRectangleTexture(Std.int(_width), Std.int(_height), BGRA, true); + bitmap = BitmapDataCrashFix.fromTextureCrashFix(_framebuffer); + onUpdateBitmap(bitmap); + } + + /** + * Renders the view. + */ + public override function render():Void + { + Stage3DProxy.drawTriangleCount = 0; + + // if context3D has Disposed by the OS,don't render at this frame + if (stage3DProxy.context3D == null || !stage3DProxy.recoverFromDisposal()) + { + _backBufferInvalid = true; + return; + } + + // reset or update render settings + if (_backBufferInvalid) + updateBackBuffer(); + + if (_shareContext && _layeredView) + stage3DProxy.clearDepthBuffer(); + + if (!_parentIsStage) + { + var globalPos:Point = parent.localToGlobal(_localTLPos); + if (_globalPos.x != globalPos.x || _globalPos.y != globalPos.y) + { + _globalPos = globalPos; + _globalPosDirty = true; + } + } + + if (_globalPosDirty) + updateGlobalPos(); + + updateTime(); + + updateViewSizeData(); + + _entityCollector.clear(); + + // collect stuff to render + _scene.traversePartitions(_entityCollector); + + // update picking + _mouse3DManager.updateCollider(this); + _touch3DManager.updateCollider(); + + if (_requireDepthRender) + renderSceneDepthToTexture(_entityCollector); + + // todo: perform depth prepass after light update and before final render + if (_depthPrepass) + renderDepthPrepass(_entityCollector); + + @:privateAccess _renderer.clearOnRender = !_depthPrepass; + + if (_filter3DRenderer != null && _stage3DProxy.context3D != null) + { + _renderer.render(_entityCollector, _filter3DRenderer.getMainInputTexture(_stage3DProxy), _rttBufferManager.renderToTextureRect); + _filter3DRenderer.render(_stage3DProxy, camera, _depthRender); + } + else + { + @:privateAccess _renderer.shareContext = _shareContext; + if (_shareContext) + _renderer.render(_entityCollector, _framebuffer, _scissorRect); + else + _renderer.render(_entityCollector, _framebuffer); + } + + if (!_shareContext) + { + stage3DProxy.present(); + + // fire collected mouse events + _mouse3DManager.fireMouseEvents(); + _touch3DManager.fireTouchEvents(); + } + + // clean up data for this render + _entityCollector.cleanUp(); + + // register that a view has been rendered + stage3DProxy.bufferClear = false; + } +} + +class BitmapDataCrashFix extends BitmapData +{ + public static function fromTextureCrashFix(texture:TextureBase):BitmapDataCrashFix @:privateAccess { + if (texture == null) + return null; + + var bitmapData = new BitmapDataCrashFix(texture.__width, texture.__height, true, 0); + bitmapData.readable = false; + bitmapData.__texture = texture; + bitmapData.__textureContext = texture.__textureContext; + bitmapData.image = null; + return bitmapData; + } + + @:dox(hide) public override function getTexture(context:Context3D):TextureBase + { + return __texture; + } +}