Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Messages - Scorpiorus

#21
I managed to track down that nasty flickering system cursor glitch in OpenGL letterbox modes, where it shows over black borders outside of the viewport area of our fullscreen window.

A short story is that the Allegro version (4.4.2-agspatch) we use with AGS Engine, forces Windows operating system (OS) to draw a system arrow cursor if the mouse cursor position is outside of the window's viewport area defined with the Allegro void set_mouse_range(int x1, int y1, int x2, int y2) function. And that arrow cursor showing is pretty hardcoded inside Allegro internals, I have to say. WinAPI's ::ShowCursor(FALSE) won't help, because it relies on OS' window message handling, and Allegro totally controls it with its own window procedure, simply doing it not the way ShowCursor(FALSE) wants...

At the moment, we pass our letterbox inner area coords into Allegro's set_mouse_range(). If OS' mouse cursor is inside that area, Allegro draws a customizable OS system cursor, which we have defined as show_os_cursor(MOUSE_CURSOR_NONE). But if it's outside, Allegro uses OS' arrow cursor, no matter what...

A quick solution would be to set the Allegro's mouse range to the whole screen/fullscreen-window even in letterbox modes. The player still won't be able to move their game-mouse-cursor over that black area, as it's restricted elsewhere in the engine. The only question remains -- if is it ok with the AGS engine to expand Allegro mouse range to the entire screen/window? I've tested it on Windows OS and it seemed to work fine. But I'm not sure about other ports of the engine... In theory, if it gets pushed back inside game viewport area by some other engine code (who let you out of the box?!), it shouldn't cause any trouble, should it?

And this is the suggested quick fix to consider:
(original source code: ags/Engine/device/mousew32.cpp)
Code: cpp

#include "gfx/graphicsdriver.h"
extern IGraphicsDriver *gfxDriver;

void Mouse::SetGraphicArea()
{
    //Rect dst_r = GameScaling.ScaleRange(play.viewport);
    //mgraphconfine(dst_r.Left, dst_r.Top, dst_r.Right, dst_r.Bottom);

    DisplayMode dm = gfxDriver->GetDisplayMode();
    mgraphconfine(0, 0, dm.Width-1, dm.Height-1); // <-- mgraphconfine() in its turn calls Allegro's set_mouse_range()
}



If the above fix or its variations won't be suitable, then the other methods would involve either directly patching Allegro source codes to replace the hardcoded arrow cursor with no_cursor, or trying to take remote control over its window message loop with void win_set_window(HWND wnd) and/or void win_set_msg_pre_proc(int (*proc)(HWND, UINT, WPARAM, LPARAM, int *retval)) functions, which is also tricky and would add more bulk into the AGS engine source code, because we then may have to copy Allegro window message handling function code over into the engine and mock up our own version of it, fixing the hardcoded cursor but also replicating everything else Allegro does with handling window messages.



To clarify the original issue, the Allegro version we use in AGS, basically does the following:

When we call allegro void set_mouse_range(int x1, int y1, int x2, int y2), it among other things stores the passed constraints in its internal global variables:
(source code: allegro-4.4.2-agspatch/src/win/wmouse.c)
Code: c

static int mouse_minx = 0;            /* mouse range */
static int mouse_miny = 0;
static int mouse_maxx = 319;
static int mouse_maxy = 199;


Next, Allegro tries to repeatedly update its mouse position variables (globals _mouse_x and _mouse_y) asking for a new cursor position values from Windows OS, and checking if the new position is within the bounds set by set_mouse_range, with the following macros/function.
Code: c
#define READ_CURSOR_POS(p)  // p - out param that's passed in to get Windows OS cursor position send back to the macros/function invoker
{                                                   
   GetCursorPos(&p);                                // use WinAPI to retrieve mouse cursor position into a temporary
                                                    
   p.x -= wnd_x;                                    
   p.y -= wnd_y;                                    
                                                    
   if ((p.x < mouse_minx) || (p.x > mouse_maxx) ||  // if mouse cursor position is outside of the area earlier defined with set_mouse_range()... 
       (p.y < mouse_miny) || (p.y > mouse_maxy)) {  
      if (_mouse_on) {                              // then...
         _mouse_on = FALSE;                         // mark the mouse as OUTSIDE; _mouse_on will be used within mouse_set_syscursor()
         wnd_schedule_proc(mouse_set_syscursor);    // queue up a background call into mouse_set_syscursor() via window message procedure
      }                                             
   }                                                
   else {                                           
      if (!_mouse_on) {                             
         _mouse_on = TRUE;                          
         wnd_schedule_proc(mouse_set_syscursor);    
      }                                             
      _mouse_x = p.x;                               
      _mouse_y = p.y;                               
   }                                                
}



Allegro's window message handling procedure to invoke the queued call into mouse_set_syscursor():
(source code: allegro-4.4.2-agspatch/src/win/wwnd.c)
Code: bf

// Window procedure for the Allegro window class.
static LRESULT CALLBACK directx_wnd_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam)
{
   ....
   if (message == msg_call_proc)
      return ((int (*)(void))wparam) (); // <---this is what really calls mouse_set_syscursor()
                                         // (NOTE: see the "Code:" tag to get the idea of how this works :PpPpPppp)
   if (message == msg_suicide) {
      DestroyWindow(wnd);
      return 0;
   }
   ....
   ....
   ....
   switch (message) {
      case WM_CREATE:
      case WM_DESTROY:
      case WM_SETCURSOR: // <---WinAPI's ShowCursor() works with this message (probably trying to disable it which is useless)
         if (!user_wnd_proc || _mouse_installed) {
            mouse_set_syscursor(); // <---another (direct) call into mouse_set_syscursor()
            return 1;  /* not TRUE */
         }
         break;
      case WM_ACTIVATE: ....
      case WM_TIMER: ....
      case WM_ENTERSIZEMOVE: ....
      case WM_EXITSIZEMOVE: ....
      case WM_MOVE: ....
      case WM_SIZE: ....
      ....
      case WM_INITMENUPOPUP: ....
      case WM_MENUSELECT: ....
      case WM_MENUCHAR : ....
      case WM_CLOSE: ....
   }
   ....
}



And finally, the cause of all the trouble -- mouse_set_syscursor():
(source code: allegro-4.4.2-agspatch/src/win/wmouse.c)
Code: c

// mouse_set_syscursor: [window thread]
// Selects whatever system cursor we should display.
int mouse_set_syscursor(void)
{
   HWND allegro_wnd = win_get_window();
   if ((mouse_dinput_device && _mouse_on) || (gfx_driver && !gfx_driver->windowed)) {
      SetCursor(_win_hcursor);  // <---this is what sets our NO_CURSOR thing when the cursor is inside the letterbox viewport area...
                                // ...because _win_hcursor == NULL, as we ordered with "show_os_cursor(MOUSE_CURSOR_NONE)"

      /* Make sure the cursor is removed by the system. */
      PostMessage(allegro_wnd, WM_MOUSEMOVE, 0, 0);
   }
   else
      SetCursor(LoadCursor(NULL, IDC_ARROW)); // <---the hardcoded ARROW CURSOR when outside! (SetCursor() is a WinAPI function)

   return 0;
}


The _mouse_on==FALSE condition gets us into the ARROW CURSOR branch. And !gfx_driver->windowed can't help us much, since technically we are in windowed mode where our window has no borders and is being stretched all over the screen.

The arrow cursor is only a fraction of a second outside of the inner viewport area before it gets pushed back by the AGS engine code.
Still that time is enough to get it noticed over a black portion of the screen...



Sorry for "a short story"... :p

p.s.
It keeps me amused reading Allegro manuals and source code. Lots of fun ^^
Spoiler

Code: c

int gfx_directx_show_mouse(struct BITMAP *bmp, int x, int y)
{
   if (hcursor) {
      _win_hcursor = hcursor;
   }
   if (_win_hcursor) {
      POINT p;

      SetCursor(_win_hcursor);
      /* Windows is too stupid to actually display the mouse pointer when we
       * change it and waits until it is moved, so we have to generate a fake
       * mouse move to actually show the cursor.
       */
      GetCursorPos(&p);
      SetCursorPos(p.x, p.y);
      return 0;
   }

   return -1;
}

[close]
#22
I've seen it on Game Jolt with the source code included:
http://gamejolt.com/games/the-art-of-dying/21047
#23
Also, we can have up to 15 different walkbehinds in a single AGS room.

So if you are planning on using a large scrolling room/screen for your top-down city scene, like in Hyper Light Drifter you mentioned, it may be practically difficult to cover all the walkbehind cases there, without extensive scripting and/or other workarounds such as using room objects and probably even characters to work as extra walkbehinds (and there are additional limits on how much entities we can have in one room or be visible on screen, depending on the version of AGS).

The scripting solution (aside from coding your own drawing engine inside AGS) would be to reuse the same walkbehinds by adjusting their baselines at run-time with the SetWalkBehindBase(int area, int baseline) script function. This should be done carefully though (and may not even always work), as you may affect other room objects and non-player characters located/visible on the screen/viewport.

Another thing is that for Hyper Light Drifter/Japanese-RPG-style view you probably want your characters' X and Y speeds match, where as for Gemini Rue/classic-adventure-game-style view you'd have character y-speed slower than x-speed to fit perspective, using the Character.SetWalkSpeed(int x_speed, int y_speed) AGS script function.


Personally, I think it's actually a real shame HLD doesn't have any adventure game elements complementing its slash and shoot mechanics, looking at the presented game screen it has so much potentially interact-able objects and characters... That's probably because it's also console/gamepad-control-oriented, though.
#24
Awesome!
And great work with implementing and polishing the OpenGL graphics renderer!
I sense it may well become a primary renderer, seeing as people are starting to have issues with using the 9th version of Direct3D API on the recent versions of MS Windows, somewhat similar to how it was with DirectDraw in the past.
#25
I've just found two shader source files in the repository that seem related to tinting.
Or is there any additional source code that's missing?

https://github.com/adventuregamestudio/ags/blob/master/Engine/resource/tintshader.fx
https://github.com/adventuregamestudio/ags/blob/master/Engine/resource/tintshaderLegacy.fx
#26
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/
#27
Quote from: Tabata on Tue 06/06/2017 09:28:56[imgzoom]http://fs5.directupload.net/images/user/170606/s9kboosx.gif[/imgzoom]

I really like how it reads A4 if converted to hex. Wonder if that's on purpose :wink:


Great entries, everyone!
#28
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.
#29
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
#30
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...
#31
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...
#32
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!
#33
Getting back to the flattened bitmaps idea... If it were possible to implement translucency more or less suitable, could we count on _stageVirtualScreen supporting 8-bit alpha channel (ARGB mode) with hardware accelerated drivers?

I noticed that the old snow/rain demo game (which is 16-bit color) runs in 32-bit color mode with the AGS Direct3D 9 driver.
#34
Yep, like I mentioned before it is a very specific thing with particle systems, i.e. drawing tons of little images but in fact using just several (driver dependent) bitmaps as a source.
I don't allow drawing 10k instances of a particle in the plugin (only 2k max, at the moment), but I just thought that if it will be HW-accelerated I could probably increase the limit.

Using std::vector is perfectly fine if we are going to add elements to its end, but inserting them, especially one by one, would trigger copying the whole thing on the "right" each time we do it.
Another "bad" thing with std::vector is that it may well decide to reallocate its internal stuff to some other memory address (say, when we add new elements and it runs out of reserved space at its back), and then we can end up with dangling pointers if we had them pointing to vector's elements. I'm not sure maybe this is not a problem for us if we just pass element's addresses to _renderSprite() and don't resize the std::vector within that function.

EDIT:

So, it seems like flattening-bitmaps-to-scene-overlay approach has its own advantages here, where the engine doesn't have to care how to store/handle bitmaps passed via some plugin API Blit function.
The irony here, is that, practically, the bitmap passed to plugin Blit functions most likely was acquired with IAGSEngine::GetSpriteGraphic() and hence already loaded in sprite cache and may even have a device dependent representation ready to render. But since it's passed as an Allegro BITMAP all ties are lost and the engine has to deal with it as with some totally new entity (though, of course, this actually may very well be the case if it's some custom bitmap prepared by plugin).
And therefore there are, again, certain complexities of whether the engine should somehow try caching bitmaps passed via plugin blit functions, to use the same device dependent bitmaps and doesn't have to create new/destroy old DDB every single time...
#35
Yeah, IIRC the flip params come from some global settings and I wanted to support them in the snow/rain plugin for AGS 3.4.0. But for now I just pass them as "no v/h flip".
#36
I'm not sure what is a common implementation of std::vector. It seems like it could be a plain array. Then insertion may take some time. And I'm going to try and draw 10k particles as a test for the plugin since it will be HW-accelerated. But, I don't think it will be too much of a performance hit with even std::vector anyway...

Quote from: Crimson Wizard on Tue 09/05/2017 01:50:35AFAIK _renderSprite is called after some variables are calculated, and these variables are local, thus calling it from external function may be problematic without larger code changes.
Well, I'm just calling it with NullSpriteEntry substituted with my bitmap. Seems to work. But then again maybe it only seems so...
#37
Quote from: Crimson Wizard on Tue 09/05/2017 01:24:22E: Or, do you mean same thing as I suggested as alternative?
Ah, yeah. Probably adding/inserting new entries to draw may be too late since we are in the rendering phase already but calling _renderSprite() just-in-time, like I do in the snow/rain plugin should work, I think.
#38
To be honest, I wanted to suggest implementing plugin API blit functions compatibility mode to use driver's rendering functions instead. Because emulating flattened sprites with different translucencies by hacking them into _stageVirtualScreen's alpha channel seems too involved.

A specialized Engine API to manage drawing in a new way is a good idea too, since you removed the hard limit on the max number of drawing list entries in 3.4.1, but it would still be neat to support the original drawing functions such as DrawText(), DrawTextWrapped(), BlitBitmap(), BlitSpriteTranslucent() and BlitSpriteRotated() to work in hardware accelerated mode.
Maybe it's worth re-stating them to support only drawing to screen and if plugin tries to redirect drawing with SetVirtualScreen() just abort with error.

And even further, I don't know if there are any plugins working with allegro bitmaps in some specific way, but personally I always treated BITMAP* just like a pointer to pass around.
Doh! It even happened to me where I didn't notice to have it declared as Windows BITMAP* or something, and it worked and compiled since I didn't care of its internals but was passing pointer values :)
#39
Ok, I did some research on this and it seems that preparing a 32-bit alpha-translucent overlay bitmap (_stageVirtualScreen) is not as simple as I thought at first.
The main issue is that, indeed, unlike with Allegro/DX5 blending, we don't have the background scene image and because of that cannot do blending in an incremental additive way (i.e. starting from the background).
And if multiple overlapping sprites are blended with the overlay bitmap (_stageVirtualScreen), it is not clear how (if at all practically possible) to do it correctly.
It is possible to accumulate their blending with each other, but I am not quite sure how difficult (including performance concerns) it would be to calculate _stageVirtualScreen's alpha values to represent the "final" alpha-translucency that would consider trans contribution of each overlapping sprite, because:

B - background scene image color, [0..255]
S1,S2,S3 - colors of 3 overlapping sprites/pixels, [0..255]
a1,a2,a3 - alpha values of 3 overlapping sprites/pixels, [0..255]
A1 is a shortcut for (255 - a1)
A2 is a shortcut for (255 - a2)
A3 is a shortcut for (255 - a3)
Res - result scene color, [0..255]
ALPHA - what we'd need to write into _stageVirtualScreen

in a classic way it would be:

Res = (((B*A1 + S1*a1)/255 * A2 + S2*a2)/255 * A3 + S3*a3)/255;

but since 'B' is unknown, we'd have transformed it into:

Res = B*(A1*A2*A3)/(255^3) + SomeKnownColor(S1,S2,S3,a1,a2,a3);

Next (A1*A2*A3)/(255^3) is (255-ALPHA)/255 (aka background scene opacity factor) that the GPU would calculate from ALPHA, to blend _stageVirtualScreen with the background scene;

So, ALPHA = 255 - (A1*A2*A3) / (255^2), is what should go into _stageVirtualScreen alpha component... it seems :p

And then in general:
ALPHA = 255 - (A1*A2*A3*...*An) / (255^[n-1]), where n - total number of overlapping sprites for the current pixel.

I'm not sure how to do it in a single pass... And if it's at all the correct way of doing this...


And here is a simple custom Allegro blend function that DOESN'T follow anything from above, so it won't work correctly with overlapping translucent sprites:
Code: CPP

unsigned long custom_blender_func_b32( unsigned long x, unsigned long y, unsigned long n )
{
    // From Allegro 4.4.2 manual:
    // x - blending modifier color;
    // y - base color to be modified;
    // n - (aka 'a') interpolation factor is in the range [0-255] and controls the solidity of the blending;

    // From Allegro 4.4.2 manual:
    // When a translucent drawing function is used (ex: draw_trans_sprite),
    // x is the color of the source,
    // y is the color of the bitmap being drawn onto,
    // n is the alpha level that was passed to the function that sets the blending mode
    // (the RGB triplet that was passed to that function is not taken into account).
    
    // TODO: revise implementation! Check Endianness!
    // using 'n' as a temporary to construct a new background pixel color
    n = n << 24;               // move Alpha to highest byte
    n = n | (x & 0x00FFFFFF);  // get sprite pixel color but don't use its Alpha component if any
    return n;                  // return new background pixel color
}

unsigned long custom_blender_func_STUB( unsigned long x, unsigned long y, unsigned long n )
{
    //return y;
    return 0xFFFFFFFF;
}


And adjusted IAGSEngine::BlitSpriteTranslucent():
Code: CPP

void IAGSEngine::BlitSpriteTranslucent(int32 x, int32 y, BITMAP *bmp, int32 trans)
{
    Bitmap *ds = gfxDriver->GetMemoryBackBuffer();
    
    // Hardware-accelerated graphics driver:
    if (HARDWARE_ACCELERATED_GRAPHICS_DRIVER && ALL_BITMAPS_ARE_32BIT && _stageVirtualScreen_and_its_HW_BITMAP_SUPPORT_ALPHA_CHANNEL)
    //if (bitmap_color_depth(ds->GetAllegroBitmap())==32)
    {
        set_blender_mode_ex(
            custom_blender_func_STUB,  // 15-bit (same color depth)
            custom_blender_func_STUB,  // 16-bit (same color depth)
            custom_blender_func_STUB,  // 24-bit (same color depth)
            custom_blender_func_b32,   // 32-bit (same color depth)
            custom_blender_func_STUB,  // 15-bit (different color depth)
            custom_blender_func_STUB,  // 16-bit (different color depth)
            custom_blender_func_STUB,  // 24-bit (different color depth)
            0, 0, 0, trans);
        
        draw_trans_sprite(ds->GetAllegroBitmap(), bmp, x, y); // TODO: use other blit func?
    }
    else // Allegro/DX5 graphics driver:
    {
        set_trans_blender(0, 0, 0, trans);
        // FIXME: call corresponding Graphics Blit
        draw_trans_sprite(ds->GetAllegroBitmap(), bmp, x, y);
    }
}


So, the above code assumes that VideoMemoryGraphicsDriver::DoNullSpriteCallback() calls CreateDDBFromBitmap() and UpdateDDBFromBitmap() with hasAlpha = true.

Unfortunately, even such simple blend implementation is much slower compared to the Allegro/DX5 AGS graphics driver.
Ah well... that's what we get for not following GPU's pipeline. But still, as a compatibility solution it's not that bad.

I'll see if I can come up with something else regarding the issue...
#40
Quote from: Crimson Wizard on Sat 06/05/2017 19:57:12Not sure if I understand what you are saying correctly. To elaborate, multiple BlitSpriteTranslucent calls done during same event callback will draw upon same bitmap. But that bitmap will be cleared before each next callback.
Yeah, what I mean is that since we don't have the access to the current "background" screen image at the moment of calling into BlitSpriteTranslucent() and hence can't blend sprites with the "background" immediately, we should instead prepare and pass translucency info for the GPU to blend the entire intermediate bitmap later (when the control returns to the engine from the plugin draw event).
And because it's a single flattened bitmap holding all the drawn sprites on it (with *different*, still virtual, trans levels), the only possible way(?) to mark translucency for each of the bitmap's area where a sprite is located seems to be adjusting the alpha channel data of the entire bitmap accordingly?

QuoteI was thinking that maybe plugin could detect bitmap format, and draw alpha sprites if its 32-bit (or detect hardware-accelerated renderer, if there is such method). On engine side it will just mark this surface as having alpha layer, which would blend over rest of the room correctly.
Yep, but then it would use a predefined static translucency from the sprite. Where BlitSpriteTranslucent() allows to specify a different trans factor for each of its call on the same sprite.

QuoteI have the collection of prebuilt libraries, if that may help:
https://www.dropbox.com/s/4p6nw6waqwat6co/ags-prebuilt-libs.zip?dl=0
Should save me quite some time! Thanks a bunch!
SMF spam blocked by CleanTalk