From 26818e6d99f5d72c002f78428f43e77fe9d96f6d Mon Sep 17 00:00:00 2001 From: shr Date: Sun, 17 Sep 2023 20:45:10 +0900 Subject: [PATCH] made it a library --- source/funkin/SolidColorSprite.hx | 64 ------------- .../graphics/framebuffer/FrameBuffer.hx | 18 +++- .../framebuffer/FrameBufferManager.hx | 44 ++++----- .../funkin/graphics/framebuffer/SpriteCopy.hx | 51 +++++----- source/funkin/play/PlayState.hx | 95 ++----------------- source/funkin/play/stage/Stage.hx | 49 +++++++++- 6 files changed, 117 insertions(+), 204 deletions(-) delete mode 100644 source/funkin/SolidColorSprite.hx diff --git a/source/funkin/SolidColorSprite.hx b/source/funkin/SolidColorSprite.hx deleted file mode 100644 index b385ea1ae..000000000 --- a/source/funkin/SolidColorSprite.hx +++ /dev/null @@ -1,64 +0,0 @@ -package funkin; - -import flixel.FlxSprite; - -/** - * Provides a clone of a sprite that is filled with a single color while keeping its alpha. - */ -class SolidColorSprite extends FlxSprite -{ - /** - * The FlxSprite that this sprite referes to. - */ - public var reference:FlxSprite; - - /** - * The red color strength, from 0.0 to 1.0. - */ - public var red:Float; - - /** - * The green color strength, from 0.0 to 1.0. - */ - public var green:Float; - - /** - * The blue color strength, from 0.0 to 1.0. - */ - public var blue:Float; - - function new(reference:FlxSprite, red:Float = 1.0, green:Float = 1.0, blue:Float = 1.0) - { - super(); - this.reference = reference; - this.red = red; - this.green = green; - this.blue = blue; - } - - override function draw():Void - { - super.draw(); - - final rMult = reference.colorTransform.redMultiplier; - final gMult = reference.colorTransform.greenMultiplier; - final bMult = reference.colorTransform.blueMultiplier; - final aMult = reference.colorTransform.alphaMultiplier; - final rOff = Std.int(reference.colorTransform.redOffset); - final gOff = Std.int(reference.colorTransform.greenOffset); - final bOff = Std.int(reference.colorTransform.blueOffset); - final aOff = Std.int(reference.colorTransform.alphaOffset); - final tmpCameras = reference._cameras; - final tmpShader = reference.shader; - - reference._cameras = _cameras; - - reference.shader = shader; - reference.setColorTransform(0, 0, 0, 1, Std.int(red * 255 + 0.5), Std.int(green * 255 + 0.5), Std.int(blue * 255 + 0.5), 0); - reference.draw(); - - reference._cameras = tmpCameras; - reference.shader = tmpShader; - reference.setColorTransform(rMult, gMult, bMult, aMult, rOff, gOff, bOff, aOff); - } -} diff --git a/source/funkin/graphics/framebuffer/FrameBuffer.hx b/source/funkin/graphics/framebuffer/FrameBuffer.hx index 480bbf111..73567d0ba 100644 --- a/source/funkin/graphics/framebuffer/FrameBuffer.hx +++ b/source/funkin/graphics/framebuffer/FrameBuffer.hx @@ -22,21 +22,25 @@ class FrameBuffer public function new() { camera = new FlxCamera(); + camera.antialiasing = false; camera.bgColor = FlxColor.TRANSPARENT; camera.flashSprite.cacheAsBitmap = true; + @:privateAccess camera.flashSprite.stage = Lib.current.stage; } /** * Creates a frame buffer with the given size. * @param width the width * @param height the height + * @param bgColor the background color */ - public function create(width:Int, height:Int):Void + public function create(width:Int, height:Int, bgColor:FlxColor):Void { dispose(); final c3d = Lib.current.stage.context3D; texture = c3d.createTexture(width, height, BGRA, true); bitmap = BitmapData.fromTexture(texture); + camera.bgColor = bgColor; } /** @@ -45,7 +49,12 @@ class FrameBuffer */ public function follow(target:FlxCamera):Void { - camera.scroll.copyFrom(target.scroll); + camera.x = target.x; + camera.y = target.y; + camera.width = target.width; + camera.height = target.height; + camera.scroll.x = target.scroll.x; + camera.scroll.y = target.scroll.y; camera.setScale(target.scaleX, target.scaleY); } @@ -58,17 +67,22 @@ class FrameBuffer camera.clearDrawStack(); camera.canvas.graphics.clear(); camera.fill(camera.bgColor.to24Bit(), camera.useBgAlphaBlending, camera.bgColor.alphaFloat); + #if FLX_DEBUG + camera.debugLayer.graphics.clear(); + #end } /** * Renders all sprite copies. */ + @:access(flixel.FlxCamera) public function render():Void { for (spriteCopy in spriteCopies) { spriteCopy.render(camera); } + camera.render(); } /** diff --git a/source/funkin/graphics/framebuffer/FrameBufferManager.hx b/source/funkin/graphics/framebuffer/FrameBufferManager.hx index 2326b6c43..f842ef638 100644 --- a/source/funkin/graphics/framebuffer/FrameBufferManager.hx +++ b/source/funkin/graphics/framebuffer/FrameBufferManager.hx @@ -1,5 +1,6 @@ package funkin.graphics.framebuffer; +import flixel.util.FlxColor; import openfl.display.BitmapData; import flixel.FlxSprite; import flixel.FlxCamera; @@ -21,33 +22,33 @@ class FrameBufferManager /** * Creates a new frame buffer with a name. * @param name the name + * @param bgColor the background color + * @return the bitmap data of the frame buffer. the bitmap data instance + * will not be changed through frame buffer updates. */ - public function createFrameBuffer(name:String):Void + public function createFrameBuffer(name:String, bgColor:FlxColor):BitmapData { if (frameBufferMap.exists(name)) { FlxG.log.warn('frame buffer "$name" already exists'); + frameBufferMap[name].dispose(); + frameBufferMap.remove(name); } - else - { - final fb = new FrameBuffer(); - fb.create(camera.width, camera.height); - frameBufferMap[name] = fb; - } + final fb = new FrameBuffer(); + fb.create(camera.width, camera.height, bgColor); + frameBufferMap[name] = fb; + return fb.bitmap; } /** * Adds a copy of the sprite to the frame buffer. * @param name the name of the frame buffer * @param sprite the sprite - * @param color if this is not `-1`, the sprite will have the color while keeping its shape + * @param color if this is not `null`, the sprite will be filled with the color. + * if this is `null`, the sprite will keep its original color. */ - public function addSpriteTo(name:String, sprite:FlxSprite, color:Int = -1):Void + public function addSpriteCopyTo(name:String, sprite:FlxSprite, color:Null = null):Void { - if (!frameBufferMap.exists(name)) - { - createFrameBuffer(name); - } frameBufferMap[name].addSpriteCopy(new SpriteCopy(sprite, color)); } @@ -58,27 +59,20 @@ class FrameBufferManager { for (_ => fb in frameBufferMap) { + fb.follow(camera); fb.lock(); } } /** - * Renders all the copies of the sprites. Make sure this is called between - * `lock` and `unlock`. + * Unlocks the frame buffers. This updates the bitmap data of each frame buffer. */ - public function render():Void + public function unlock():Void { for (_ => fb in frameBufferMap) { fb.render(); } - } - - /** - * After calling this you can use bitmap data of all frame buffers. - */ - public function unlock():Void - { for (_ => fb in frameBufferMap) { fb.unlock(); @@ -88,7 +82,7 @@ class FrameBufferManager /** * Returns the bitmap data of the frame buffer * @param name the name of the frame buffer - * @return the ready-to-use bitmap data + * @return the bitmap data */ public function getFrameBuffer(name:String):BitmapData { @@ -96,7 +90,7 @@ class FrameBufferManager } /** - * Disposes all frame buffers. + * Disposes all frame buffers. The instance can be reused. */ public function dispose():Void { diff --git a/source/funkin/graphics/framebuffer/SpriteCopy.hx b/source/funkin/graphics/framebuffer/SpriteCopy.hx index aaa6bb730..b1fc82497 100644 --- a/source/funkin/graphics/framebuffer/SpriteCopy.hx +++ b/source/funkin/graphics/framebuffer/SpriteCopy.hx @@ -1,14 +1,15 @@ package funkin.graphics.framebuffer; +import flixel.util.FlxColor; import flixel.FlxCamera; import flixel.FlxSprite; class SpriteCopy { final sprite:FlxSprite; - var color:Int; + var color:Null; - public function new(sprite:FlxSprite, color:Int = -1) + public function new(sprite:FlxSprite, color:Null) { this.sprite = sprite; this.color = color; @@ -21,30 +22,34 @@ class SpriteCopy @:access(flixel.FlxSprite) public function render(camera:FlxCamera):Void { - final rMult = sprite.colorTransform.redMultiplier; - final gMult = sprite.colorTransform.greenMultiplier; - final bMult = sprite.colorTransform.blueMultiplier; - final aMult = sprite.colorTransform.alphaMultiplier; - final rOff = Std.int(sprite.colorTransform.redOffset); - final gOff = Std.int(sprite.colorTransform.greenOffset); - final bOff = Std.int(sprite.colorTransform.blueOffset); - final aOff = Std.int(sprite.colorTransform.alphaOffset); - final tmpCameras = sprite._cameras; - - sprite._cameras = [camera]; - - if (color != -1) + if (color == null) { - final red = color >> 16 & 0xFF; - final green = color >> 8 & 0xFF; - final blue = color & 0xFF; - sprite.setColorTransform(0, 0, 0, 1, red, green, blue, 0); + final tmpCameras = sprite._cameras; + sprite._cameras = [camera]; + sprite.draw(); + sprite._cameras = tmpCameras; } - sprite.draw(); - - sprite._cameras = tmpCameras; - if (color != -1) + else { + final rMult = sprite.colorTransform.redMultiplier; + final gMult = sprite.colorTransform.greenMultiplier; + final bMult = sprite.colorTransform.blueMultiplier; + final aMult = sprite.colorTransform.alphaMultiplier; + final rOff = Std.int(sprite.colorTransform.redOffset); + final gOff = Std.int(sprite.colorTransform.greenOffset); + final bOff = Std.int(sprite.colorTransform.blueOffset); + final aOff = Std.int(sprite.colorTransform.alphaOffset); + final tmpCameras = sprite._cameras; + final tmpShader = sprite.shader; + + sprite._cameras = [camera]; + sprite.shader = null; + + sprite.setColorTransform(0, 0, 0, 1, color.red, color.green, color.blue, 0); + sprite.draw(); + + sprite._cameras = tmpCameras; + sprite.shader = tmpShader; sprite.setColorTransform(rMult, gMult, bMult, aMult, rOff, gOff, bOff, aOff); } } diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 0dab2c52e..75a311a45 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -15,7 +15,10 @@ import flixel.FlxObject; import flixel.FlxSprite; import flixel.FlxState; import flixel.FlxSubState; -import flixel.input.keyboard.FlxKey; +import flixel.addons.display.FlxPieDial; +import flixel.addons.transition.FlxTransitionableState; +import flixel.addons.transition.FlxTransitionableSubState; +import flixel.addons.transition.Transition; import flixel.math.FlxMath; import funkin.play.components.ComboMilestone; import flixel.math.FlxPoint; @@ -25,8 +28,6 @@ import flixel.math.FlxRect; import flixel.text.FlxText; import flixel.tweens.FlxEase; import flixel.tweens.FlxTween; -import flixel.system.frontEnds.CameraFrontEnd; -import flixel.util.FlxColor; import flixel.ui.FlxBar; import flixel.util.FlxColor; import funkin.api.newgrounds.NGio; @@ -37,21 +38,24 @@ import openfl.geom.Rectangle; import funkin.audio.VoicesGroup; import funkin.save.Save; import funkin.Highscore.Tallies; +import funkin.NoteSplash; +import funkin.audio.VoicesGroup; +import funkin.data.notestyle.NoteStyleRegistry; import funkin.input.PreciseInputManager; import funkin.modding.events.ScriptEvent; import funkin.ui.mainmenu.MainMenuState; import funkin.modding.events.ScriptEventDispatcher; import funkin.play.character.BaseCharacter; import funkin.play.character.CharacterData.CharacterDataParser; -import funkin.play.cutscene.dialogue.Conversation; -import funkin.play.cutscene.dialogue.ConversationDataParser; import funkin.play.cutscene.VanillaCutscenes; import funkin.play.cutscene.VideoCutscene; import funkin.data.event.SongEventData.SongEventParser; import funkin.play.notes.NoteSprite; import funkin.play.notes.NoteDirection; +import funkin.play.notes.NoteSprite; import funkin.play.notes.Strumline; import funkin.play.notes.SustainTrail; +import funkin.play.notes.notestyle.NoteStyle; import funkin.play.scoring.Scoring; import funkin.play.song.Song; import funkin.data.song.SongRegistry; @@ -66,7 +70,6 @@ import funkin.ui.options.PreferencesMenu; import funkin.ui.debug.stage.StageOffsetSubState; import funkin.ui.story.StoryMenuState; import funkin.util.SerializerUtil; -import funkin.util.SortUtil; import lime.ui.Haptic; #if discord_rpc import Discord.DiscordClient; @@ -590,9 +593,6 @@ class PlayState extends MusicBeatSubState } initStrumlines(); - // Initialize sprites for the buffer texture. - initMaskSprites(); - // Initialize the judgements and combo meter. comboPopUps = new PopUpStuff(); comboPopUps.cameras = [camHUD]; @@ -980,28 +980,6 @@ class PlayState extends MusicBeatSubState processNotes(elapsed); } - @:access(flixel.FlxCamera) - @:access(flixel.system.frontEnds.CameraFrontEnd) - override function draw():Void - { - // Clears the draw stacks buffer cameras. - bufferCameraFrontEnd.lock(); - // Collects draw stacks to render stuff, for ALL cameras including - // the main ones and buffer ones. But at this point each camera's - // canvas.graphics is still empty. - super.draw(); - // Actually render (using canvas.graphics) stuff ONLY for the buffer cameras. - // For the main cameras, it will be done by FlxGame LATER. - bufferCameraFrontEnd.render(); - // Possibly applies some FXs to the buffer cameras. - bufferCameraFrontEnd.unlock(); - // Update the buffer texture using `flashSprite`. - // This is IMMEDIATELY done while the main cameras are not rendered yet, - // so any shaders in the main part that refer the texture can see the updated texture! - maskTexture.fillRect(new Rectangle(0, 0, FlxG.width, FlxG.height), 0); - maskTexture.draw(camMask.flashSprite, new openfl.geom.Matrix(1, 0, 0, 1, camMask.flashSprite.x, camMask.flashSprite.y)); - } - public override function dispatchEvent(event:ScriptEvent):Void { // ORDER: Module, Stage, Character, Song, Conversation, Note @@ -1275,11 +1253,6 @@ class PlayState extends MusicBeatSubState performCleanup(); super.destroy(); - - // It's manually obtained, don't forget to release it! - maskTextureBase.dispose(); - FlxG.signals.postUpdate.remove(syncBufferCameras); - bufferCameraFrontEnd.remove(camMask); } /** @@ -1314,8 +1287,6 @@ class PlayState extends MusicBeatSubState camCutscene = new FlxCamera(); camCutscene.bgColor.alpha = 0; // Show the game scene behind the camera. - initBufferCameras(); - FlxG.cameras.reset(camGame); FlxG.cameras.add(camHUD, false); FlxG.cameras.add(camCutscene, false); @@ -1329,24 +1300,6 @@ class PlayState extends MusicBeatSubState add(cameraFollowPoint); } - function initBufferCameras():Void - { - // Init cameras and stuff for buffers. - camMask = new FlxCamera(); - // note: removing this line will cause NullReferenceError inside OpenGLRenderer lol - camMask.flashSprite.cacheAsBitmap = true; - // Prevent potential memory leak. - if (maskTextureBase != null) maskTextureBase.dispose(); - // We need to directly create texture using Context3D, otherwise cannot render Sprite - // using OpenGLRenderer, which disables any shader applied to it. - maskTextureBase = Lib.current.stage.context3D.createTexture(FlxG.width, FlxG.height, Context3DTextureFormat.BGRA, true); - maskTexture = BitmapData.fromTexture(maskTextureBase); - // This must be done BEFORE `FlxG.cameras.reset`. - bufferCameraFrontEnd.reset(camMask); - // Sync buffer cameras after every update. - FlxG.signals.postUpdate.add(syncBufferCameras); - } - /** * Initializes the health bar on the HUD. */ @@ -1397,24 +1350,6 @@ class PlayState extends MusicBeatSubState add(menuBG); } - /** - * Syncs cameras for buffers; basically just copies how the main camera is doing - */ - function syncBufferCameras():Void - { - final tr = @:privateAccess FlxG.log._standardTraceFunction; - // tr("zoom: " + camGame.zoom); - for (cam in bufferCameraFrontEnd.list) - { - cam.x = camGame.x; - cam.y = camGame.y; - cam.scroll.x = camGame.scroll.x; - cam.scroll.y = camGame.scroll.y; - cam.zoom = camGame.zoom; - @:privateAccess cam.updateFlashSpritePosition(); - } - } - /** * Loads stage data from cache, assembles the props, * and adds it to the state. @@ -1597,18 +1532,6 @@ class PlayState extends MusicBeatSubState this.refresh(); } - function initMaskSprites():Void - { - // Add mask sprites to the mask camera. - for (sprite in currentStage.maskSprites) - { - this.add(sprite); - sprite.cameras = [camMask]; - } - // Set buffer textures to the current stage. - currentStage.maskTexture = maskTexture; - } - /** * Initializes the Discord Rich Presence. */ diff --git a/source/funkin/play/stage/Stage.hx b/source/funkin/play/stage/Stage.hx index d86d9404c..2f9efa42f 100644 --- a/source/funkin/play/stage/Stage.hx +++ b/source/funkin/play/stage/Stage.hx @@ -1,5 +1,8 @@ package funkin.play.stage; +import funkin.graphics.framebuffer.FrameBufferManager; +import flixel.util.FlxColor; +import funkin.graphics.framebuffer.SpriteCopy; import flixel.FlxCamera; import flixel.FlxSprite; import flixel.group.FlxSpriteGroup; @@ -20,6 +23,26 @@ import funkin.util.assets.FlxAnimationUtil; typedef StagePropGroup = FlxTypedSpriteGroup; +typedef FrameBufferSprite = +{ + /** + * The name of the target frame buffer. + */ + var name:String; + + /** + * The sprite to be rendered. + */ + var sprite:FlxSprite; + + /** + * The RGB color of the sprite. The alpha component will be ignored. + * If this is `null`, the sprite keeps its original color. + */ + @:optional + var color:Null; +} + /** * A Stage is a group of objects rendered in the PlayState. * @@ -34,10 +57,9 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass public var camZoom:Float = 1.0; - /** - * The list of sprites that should be rendered for mask texture. - */ - public var maskSprites:Array = []; + var frameBufferMan:FrameBufferManager; + + public final frameBufferSprites:Array = []; /** * The texture that has the mask information. Used for shader effects. @@ -76,6 +98,10 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass */ public function onCreate(event:ScriptEvent):Void { + if (frameBufferMan != null) frameBufferMan.dispose(); + frameBufferMan = new FrameBufferManager(FlxG.camera); + onFrameBufferCreate(); + buildStage(); this.refresh(); @@ -691,6 +717,8 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass { debugIconGroup = null; } + + frameBufferMan.dispose(); } /** @@ -743,6 +771,19 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass return Sprite; } + override function draw():Void + { + frameBufferMan.lock(); + super.draw(); + frameBufferMan.unlock(); + } + + /** + * Called when the frame buffer manager is ready. + * Create frame buffers inside this method. + */ + public function onFrameBufferCreate():Void {} + public function onScriptEvent(event:ScriptEvent) {} public function onPause(event:PauseScriptEvent) {}