PLUGIN: Snow/Rain 2.02

Started by Scorpiorus, Mon 03/02/2003 18:43:02

Previous topic - Next topic

Scorpiorus

Ok, I see... Thanks for clarifying.

I will definitely look into your blender function implementations to grasp some ideas or even get ready-to-use code.
But I suspect, however, that we may still encounter certain specific issues and need to write some special code to handle them.

And this is because we "don't know" the background scene image!

What the engine must be able to do, to correctly process plugin's blitting/drawing commands for a single flattening stage:

- blit opaque sprites (with fully-transparent areas, aka pink color) onto stage bitmap
- draw translucent (i.e. semi-transparent) sprites onto stage bitmap
- draw translucent sprites over other opaque sprites onto stage bitmap (i.e. overlapping)
- draw translucent sprites over other translucent sprites onto the same stage bitmap
- blit opaque sprites over already drawn mix of opaque and translucent sprites onto the same stage bitmap
- etc...

~ each drawing operation of translucent sprite can have its very own translucency/opaqueness level (i.e. IAGSEngine::BlitSpriteTranslucent() 'trans' param)
~ all of this must be done without knowing background scene image colors


Relying on the ideas from all of my ramble before the currently planned implementation is as follows:

All the source sprites to draw onto the stage bitmap are considered without their alpha channel (if any). That's how, I believe, the original DX5 plugin draw functions behave. (This may be somehow reconsidered and we can even try adding at least rough alpha blend support? Though this seems out of the scope of providing Allegro/DX5-drawing-compatibility...)

Since we have a single stage bitmap that later will be rendered by GPU over the current background scene image in one step, we "pass" all the different values of 'trans' param into GPU by simply writing them into stage bitmap's alpha channel.

When flattening each new overlapping translucent sprite into the stage bitmap we blend its RGB components with the stage bitmap's ones according to 'trans' param, but also adjust stage bitmap's alpha area accordingly to specify how that new mix should blend with the unknown background scene image.

If we blit opaque sprite onto stage bitmap, that already have some alpha translucency (and colors) there, we must not only blit the sprite over it but also set stage bitmap's alpha values to 255 in that area (because blitted opaque sprite should not somehow get translucency of previously drawn and now overpainted sprites "under" it).


A common scenario of preparing a stage bitmap for further rendering by GPU is this:

- clear stage bitmap (ARGB: A8R8G8B8) to color 0xFFFF00FF (i.e. Fully-transparent Pink which has an additional special meaning of "stage bitmap here is empty"; and Alpha=255 as an initial trick to try support blitting fully-opaque sprites off-hand!)
- set a special flag to indicate that not a single translucent sprite has been drawn yet
- pass control to plugin
- plugin calls draw commands and blits fully-opaque sprites (they still may have fully-transparent pinks, that's ok)
- plugin draws first translucent sprite somewhere
- stage bitmap's alpha values in that area are adjusted accordingly
- a special flag now indicates that some translucent sprite has just been drawn, meaning that we can't simply blit opaque sprite around anymore, without filling stage bitmap Alpha areas, where we are going to blit the opaque sprite, with 255)
- when we draw some translucent sprite over the stage bitmap, the blender function that does this, specifically checks stage bitmap's color there...
- if it's Pink (i.e. stage bitmap is empty here), translucent sprite pixel color just overwrites the pink without blending with it, and Alpha value is simply set to 'trans' value of translucent sprite;
- if it's not Pink (something is already there), translucent sprite pixel color blends with that color according to 'trans' param and the new mix gets written back into stage bitmap pixel...
- ...but also, the current Alpha value is retrieved from the stage bitmap, gets remixed with 'trans' param, and new alpha gets written back into stage bitmap to define the new blending proportion between the mixed thing in the stage bitmap pixel and unknown background scene beneath it.
- eventually, plugin finishes with drawing its sprites and passes control back to the engine to render the stage bitmap!


I'll try to come up with some, at least partially, working code as time permits. Since I already did some tests before...
Unfortunelty, I'm not yet ready doing it via git, but since it's new stuff mostly, there shouldn't be a problem to just copy and paste it into experiment branch...

Also, I may have missed something... so any questions, suggestions, corrections or any other feedback is welcome!

Crimson Wizard

#141
I must apologize, because being very busy with other things I cannot dedicate myself to this task. So I could easily miss something from your explanations.

I was wondering if not knowing underlying pixels at this stage is such a big deal. There must be a way to draw translucent images on this stage bitmap, which should have alpha channel already. If that is achieved, then rest seems to be GPU's problem.

I just wanted to point you to the function called DrawSpriteBlend. It is used for GUI and DrawingSurface. This function gets custom alpha as a "translucency" parameter to whole operation, thus seems to be very similar to what plugin's Blit function does.
It also chooses blender from the list of AGS blenders suitable for this particular situation (both bitmaps has alpha, or only one of them).
Hopefully that may give implementation ideas.

Scorpiorus

#142
Ah, that's great! I didn't realize there is a blender ready to work with translucent destination surfaces that also supports external "trans" param...
Thanks for pointing me to it. Otherwise I would have spent my weekend reimplementing the wheel!

I didn't look into your blender's implementation details yet, but at first glance this, indeed, seems what we need. At least the dest alpha recalculation formula looks familiar.

As to why I keep emphasizing on unknown background image, because I think normally when we're composing a scene starting from the background image, we just blend and forget, so to say, i.e. calculating new color mix and flattening it into background. But with this approach (destination with alpha channel) we have to not only mix RGB values but also constantly retreive and recalculate the dest bitmap alpha values again and again on drawing each new trans sprite above. Which I believe may be unnecessary slower composing a screen scene with software rendering methods, especially, since we got image color components as some fixed 8-bit integers (and not floating points).

EDIT:

Ok, just did a quick test with the AGS engine, replacing BlitSpriteTranslucent's internals with the DrawSpriteBlend() function and the snow/rain plugin seems to work fine! Still needs proper testing though.

EDIT:

Also, I'm going to check the IAGSEngine::BlitSpriteRotated() func to see if it works in our compatibility mode and how hard it would be to fix it if it doesn't...

Scorpiorus

#143
So, I was looking into how plugin compatibility drawing mode works with different plugin drawing functions and that's what I've got:

void DrawText(int x, int y, int font, int color, char *text);
void DrawTextWrapped(int x, int y, int width, int font, int color, const char *text);
void BlitSpriteRotated(int x, int y, BITMAP *spr, int angle);

These three functions seem to work with the Direct3D 9 Graphics Driver as well as with the Allegro/DX5 Graphics Driver.




void BlitBitmap(int x, int y, BITMAP *bmp, int masked);

Also works, but the "masked" flag has no effect in Direct3D 9 mode (transparent 0x00FF00FF pixels are always skipped).
With masked=0 sprites are blitted onto _stageVirtualScreen correctly, i.e. with PINKs included, but at a later moment stageVirtualScreen's texture gets PINKs replaced with ALPHA=0 anyway...
Not a big deal, but the behavior is different comparing to the Allegro/DX5 Graphics Driver.
And I'm not sure if there is an easy way to fix this without messing with hardware-accelerated driver implementations...




void BlitSpriteTranslucent(int x, int y, BITMAP *spr, int trans);

Seems to work with alpha-blending support enabled (i.e. VideoMemoryGraphicsDriver::DoNullSpriteCallback should pass hasAlpha=true when preparing DDB), and with the following implementation of BlitSpriteTranslucent (EDIT: doesn't work correctly with the Allegro/DX5 Graphics Driver in 32-bit color mode!):
Code: cpp

#include "gfx/gfx_util.h" // GfxUtil::DrawSpriteBlend()

void IAGSEngine::BlitSpriteTranslucent(int32 x, int32 y, BITMAP *bmp, int32 trans)
{
    GfxUtil::DrawSpriteBlend(gfxDriver->GetMemoryBackBuffer(), Point(x,y), &Bitmap(bmp,true), kBlendMode_Alpha, true, false, trans);
}


With Allegro/DX5 graphics driver, however, it doesn't work as before when drawing 8-bit color sprites onto 16-bit color background. It draws opaque sprites regardless of "trans" (aka alpha) value.
DrawSpriteBlend() calls DrawSpriteWithTransparency() and 8-bit color sprite is promoted to 16-bit color there, to match background color format.
But after conversion is done, "ds->Blit(&hctemp, x, y, kBitmap_Transparency);" is invoked thus ignoring "alpha" param passed into DrawSpriteWithTransparency:

https://github.com/adventuregamestudio/ags/blob/release-3.4.0/Engine/gfx/gfx_util.cpp#L143

To fix it work as it used to be, the "ds->Blit(&hctemp, x, y, kBitmap_Transparency);" line could probably be replaced with something like:
Code: cpp

        if (alpha < 0xFF && surface_depth > 8) 
        {
            set_trans_blender(0, 0, 0, alpha);
            ds->TransBlendBlt(&hctemp, x, y);
        }
        else
        {
            ds->Blit(&hctemp, x, y, kBitmap_Transparency);
        }




And I'm sorry for posting code snippets here instead of committing/pull-requesting into experimental git branches. Still going to set up git at some point...

Crimson Wizard

I believe I could add the base changes to 3.4.1 release, and someone (you or me, or other person) will be able to provide patches to it as time goes.

Scorpiorus

Would you mind including the changes I proposed in my previous post?
That should cover most cases where compatibility drawing mode would work correctly, as far as I can say.

And kind of nitpicking, the thing I noticed with VideoMemoryGraphicsDriver's destructor -- which virtually overrides GraphicsDriverBase/IGraphicsDriver destructor -- it's not marked as "virtual" whereas in other places AGS code follows a common practice to mark all virtually overriding functions with "virtual" (i.e. with "overrides" meaning).

https://github.com/ivan-mogilko/ags-refactoring/blob/improv--d3dpluginblit/Engine/gfx/gfxdriverbase.h#L123
https://github.com/ivan-mogilko/ags-refactoring/blob/improv--d3dpluginblit/Engine/gfx/graphicsdriver.h#L124

Crimson Wizard

Quote from: Scorpiorus on Fri 26/05/2017 23:42:51
Would you mind including the changes I proposed in my previous post?
That should cover most cases where compatibility drawing mode would work correctly, as far as I can say.

Yes, certainly.

Crimson Wizard

The discussed changes are in the new 3.4.1 beta: http://www.adventuregamestudio.co.uk/forums/index.php?topic=54681.msg636562780#msg636562780
Please check if I added your suggestions right.

Scorpiorus

#148
Yep, everything seems in place, thanks.

But I've given it a proper test now and just realized that translucent blitting doesn't work correctly in Allegro/Software mode at 32-bit color.
This is my omission where I only checked 16-bit color mode (because the Snow/Rain plugin demo game, which I was using, is 16-bit color).

The problem is that unlike the stage bitmap, the virtual screen (background) bitmap's alpha component must be 0xFF (not 0x00).
I don't know if it's worth the hassle to try forcing it to 0xFF, so that GfxUtil::DrawSpriteBlend (using _rgb2argb_blender) would be suitable for software graphics mode too.


And the alternative solution is this:

I really tried to avoid checking for graphic modes within IAGSEngine::BlitSpriteTranslucent and liked how it seemed to work with DrawSpriteBlend only, but looks like it may need a conditional there...

Code: cpp

void IAGSEngine::BlitSpriteTranslucent(int32 x, int32 y, BITMAP *bmp, int32 trans) {
    Bitmap wrap(bmp, true);
    if (USING_SOFTWARE_RENDERER)
    // TODO: only works if string pooling is enabled; find a better way to check for Graphics Driver fast enough?
    //if (gfxDriver->GetDriverID()=="DX5") // pointer comparison
    //if (gfxDriver->GetDriverID()=="Software") // pointer comparison
        GfxUtil::DrawSpriteWithTransparency(gfxDriver->GetMemoryBackBuffer(), &wrap, x, y, trans);
    else
        GfxUtil::DrawSpriteBlend(gfxDriver->GetMemoryBackBuffer(), Point(x,y), &wrap, kBlendMode_Alpha, true, false, trans);
}


I don't know if we a have a fast and reliable way of checking for current graphics driver.
What I have noticed though, is that "DX5" changed to "Software" in AGS 3.4.1 Beta 5. Which looks neat but may theoretically break some plugins that already rely on the "DX5" ID name.


EDIT:

By the way, how feasible would it be to include the Snow/Rain plugin sources into the AGS Engine, provided I'm doing it myself?

I checked JJS's recreation and it now works with recent changes too. But it seems there are problems with certain screen resolutions where snow/rain covers only half of the screen (checked with the snow/rain demo game). And the overall behavior is a little bit different from the original version.

Crimson Wizard

#149
Quote from: Scorpiorus on Fri 09/06/2017 00:05:49
And the alternative solution is this:

I really tried to avoid checking for graphic modes within IAGSEngine::BlitSpriteTranslucent and liked how it seemed to work with DrawSpriteBlend only, but looks like it may need a conditional there...

I don't know if we a have a fast and reliable way of checking for current graphics driver.

You can use gfxDriver->UsesMemoryBackBuffer. This effectively means that the buffer you draw upon already has room drawn on it. If even that will be too slow, then cache its value in a boolean variable in plugin system (something like "DrawingSurfaceHasAlpha"). Graphics renderer is only created once during single engine run, so that could be done at startup.

BTW, this reminds me, I thought plugin drawing is very similar to the one of DrawingSurface. Perhaps they may be merged somehow.


Quote from: Scorpiorus on Fri 09/06/2017 00:05:49
By the way, how feasible would it be to include the Snow/Rain plugin sources into the AGS Engine, provided I'm doing it myself?
Do you mean include plugin sources into repository or in the engine itself as a feature?

There are several plugins there already, so I guess it won't be an issue if you swap JJS variant with yours.
Personally, I did not like it very much that we keep those plugins in same repository though. They were there mostly because we were statically linking them with engine for mobile ports. Perhaps there could be way to move them out and include as submodules... but that's not relevant to current situation.

Scorpiorus

#150
Quote from: Crimson Wizard on Fri 09/06/2017 17:30:28You can use gfxDriver->UsesMemoryBackBuffer.

Ah, ok. So, hopefully, the final version of IAGSEngine::BlitSpriteTranslucent should look something like this:

Code: cpp

void IAGSEngine::BlitSpriteTranslucent(int32 x, int32 y, BITMAP *bmp, int32 trans) {
    Bitmap wrap(bmp, true);
    if (gfxDriver->UsesMemoryBackBuffer())
        GfxUtil::DrawSpriteWithTransparency(gfxDriver->GetMemoryBackBuffer(), &wrap, x, y, trans);
    else
        GfxUtil::DrawSpriteBlend(gfxDriver->GetMemoryBackBuffer(), Point(x,y), &wrap, kBlendMode_Alpha, true, false, trans);
}



QuoteBTW, this reminds me, I thought plugin drawing is very similar to the one of DrawingSurface. Perhaps they may be merged somehow.
Very well may be. I haven't yet looked into it, though.
Many of the plugin API drawing functions need some refactoring anyway (such as BlitSpriteRotated calling Allegro's rotate_sprite directly, etc...). That should be addressed at some point I believe.

QuoteDo you mean include plugin sources into repository or in the engine itself as a feature?
I mean repository, yeah.
There is some code related to the plugins in the engine, though (for static linking or providing stubs, etc...). I'm not yet sure if it needs to be altered for including/replacing the snow/rain plugin.
And I thoroughly support the idea of moving plugin sources to their own place eventually.



So, what's about the "Software" graphics driver ID name replacing "DX5"? Should we change it to "Software" once and for all?
I'm checking for "DX5" in the current ALPHA version of the snow/rain plugin, but I can workaround it no problem and check for both depending on the version of AGS.
Still I don't known if there are other plugins relying on "DX5" ID...

EDIT: Also, "DX5" is mentioned multiple times on the AGS Engine Plugins page:
http://www.adventuregamestudio.co.uk/site/ags/plugins/engine/

Crimson Wizard

Quote from: Scorpiorus on Mon 12/06/2017 20:02:39
So, what's about the "Software" graphics driver ID name replacing "DX5"? Should we change it to "Software" once and for all?
I'm checking for "DX5" in the current ALPHA version of the snow/rain plugin, but I can workaround it no problem and check for both depending on the version of AGS.
Still I don't known if there are other plugins replying on "DX5" ID...

TBH when I made decision to change it to "software" I did not think about plugins. But, to my knowledge only few plugins are actively used over years, and most popular are either open source or rewritten to make them portable.
Even if such incompatibility will be uncovered, it may be fixed with a config switch, similar to the fake "OS type" switch we made earlier for games & plugins that relied on OS id for some reason.

rocifier

Hi :) what's the status of this plugin with 3.4.1?

Crimson Wizard

#153
Quote from: rocifier on Sun 20/08/2017 02:00:04
Hi :) what's the status of this plugin with 3.4.1?

The first link in the first post sais "supports AGS 2.71, AGS 2.72, AGS 3.4.1 - Beta 6 and above". I think that's the version I was testing, and it should be working with all 3 graphic renderers in 3.4.1.
There is a newer version of plugin which supports Direct3D in a hackish way, as Scorpiorus mentioned above, but I think it is not needed now that 3.4.1 engine is fixed to work with original plugin.

m0ds

Seems to be working well with 3.4.1 ......... in directX mode! Wonderful. Well done Scorpiorus, thank you! And CW for engine update to help it.

Crimson Wizard

I would like to clarify something: does this plugin check for display mode colour depth or game's native colour depth (in game structure parameters)?

I am asking, because I am currently considering forcing Software renderer to work in 32-bit mode in case of 16-bit game too, as one of options to solve the issue described here. And also because 16-bit mode does not work well on modern systems / with modern gfx drivers (for example, with 16-bit games it does not refresh window on my computer anymore).

Scorpiorus

Quote from: MJL on Sat 25/11/2017 14:16:57Seems to be working well with 3.4.1 ......... in directX mode! Wonderful. Well done Scorpiorus, thank you! And CW for engine update to help it.
Good to know m0ds, thanks for testing it out!

Quote from: Crimson Wizard on Mon 27/11/2017 15:28:32I would like to clarify something: does this plugin check for display mode colour depth or game's native colour depth (in game structure parameters)?
The plugin reads the colour depth value, but it doesn't do anything with it, so it should be fine.

Anyway, feel free to do any adjustments to the engine necessary. In the end, I can always update the plugin to conform with them.
I'm still planning to release the plugin sources at some point :p



One thing that bugs me is that since AGS 3.0.0 the original plugin keeps moving particles when some deep-blocking function, such as Display(), is invoked.
The problem is that the plugin uses the rendering event to both MOVE and DRAW particles.
In pre-3.0 versions of AGS, the engine wasn't redrawing the screen when inside deep inner blocking loop (and hence it didn't call plugin render events).
The plugin uses the IsGamePaused() function to check if it needs to MOVE particles. But now that only works with game-pausing GUIs, etc.
One solution would be to make the AGS engine's IsGamePaused() return 1 when inside Display() and other similar functions. But I can see it's quite a hassle to implement.
From the plugin point of view, I'm not sure if we have a suitable plugin event, from where I could call the MOVE code only (I haven't yet checked it out though).

SMF spam blocked by CleanTalk