Adventure Game Studio

AGS Support => Modules, Plugins & Tools => Topic started by: eri0o on Tue 29/03/2022 02:11:31

Title: MODULE: mode7 0.3.0
Post by: eri0o on Tue 29/03/2022 02:11:31
mode7 version 0.3.0

Get Latest Release mode7.scm (https://github.com/ericoporto/mode7/releases/download/0.3.0/mode7.scm) | GitHub Repo (https://github.com/ericoporto/mode7) |  Download project .zip  (https://github.com/ericoporto/mode7/archive/0.3.0.zip)

AGS Script Module for Mode7 like graphics. See demo in here! (https://ericoporto.github.io/mode7/) (Use Firefox, Safari or Chrome 100+, WASD to move)

(https://user-images.githubusercontent.com/2244442/160260167-0f6ab7a0-fd55-472a-839f-5332f3970476.gif)

This module allows you to project a sprite on the screen with the visual aspect people are familiar from the mode7 graphics of SNES games. Use the Mode7 struct for that!

If you want to do more, and also want to have other elements in it, similar to Mario Kart, you can leverage Mode7World and Mode7Object functionalities.

This code is based on original code that was written by Khris and presented in this ags forum thread (https://www.adventuregamestudio.co.uk/forums/index.php?topic=45834.msg636452606#msg636452606). I asked Khris for the original code, which was very Kart oriented, I refactored to what I thought was more generic and made this module in the hopes people could pick around the code and make games out of it!

A note, the original code was a bit more performant, I pulled out some specific optimizations to make the code more flexible.

Script API
Spoiler


Mode7

Mode7.SetCamera
Code (ags) Select
void Mode7.SetCamera(float x, float y, float z, float xa, float ya, float focal_length)
Sets the camera position, angle and focal length.

Mode7.SetViewscreen
Code (ags) Select
void Mode7.SetViewscreen(int width, int height, optional int x,  optional int y)
Sets the screen area it will draw in.

Mode7.SetGroundSprite
Code (ags) Select
void Mode7.SetGroundSprite(int ground_graphic)
Sets the ground sprite, this is the mode7 rendered sprite.

Mode7.SetHorizonSprite
Code (ags) Select
void Mode7.SetHorizonSprite(int horizon_graphic, eHorizonType = eHorizonDynamic)
Sets a sprite that will roll around in the horizon, you can also make it static.

Mode7.SetBgColor
Code (ags) Select
void Mode7.SetBgColor(int bg_color)
Sets the color of the background where the ground sprite doesn't reach.

Mode7.SetSkyColor
Code (ags) Select
void Mode7.SetSkyColor(int sky_color)
Sets the color of the sky.

Mode7.TargetCamera
Code (ags) Select
void Mode7.TargetCamera(float target_x, float target_y, float target_z, float teta_angle, eCameraTargetType camType = eCameraTarget_FollowBehind, bool is_lazy = true)
Target the camera to something.

Mode7.Draw
Code (ags) Select
void Mode7.Draw()
Draws the ground sprite and horizon rendered in the Screen sprite.

Mode7.ResetGround
Code (ags) Select
void Mode7.ResetGround()
Clears the screen sprite.

Mode7.CameraAngleX
Code (ags) Select
float Mode7.CameraAngleX
The camera angle that is normal to the ground plane (e.g.: up and down).

Mode7.CameraAngleY
Code (ags) Select
float Mode7.CameraAngleY
The camera angle that is on the ground plane (e.g.: left and right).

Mode7.Screen
Code (ags) Select
DynamicSprite* Mode7.Screen
The Dynamic Sprite that represents the screen where the Mode7 ground is draw to.




Mode7World
This is an extension of Mode7 and gives you tools to present billboard sprites, positioned in the world coordinates, using the concept of Mode7Objects. You don't have to use it to do the drawing, but it should help you if you want to!

Mode7World.AddObject
Code (ags) Select
Mode7Object* Mode7World.AddObject(int x, int z, float factor, int graphic)
Adds an object, and sets it's x and z position. The y (vertical position) is always zero. You also must pass a scale factor and it's graphics.

Mode7World.AddExternalObject
Code (ags) Select
void Mode7World.AddExternalObject(int x, int z, float factor, int graphic)
Adds an external object that is not managed by the Mode7World. It will still be updated and draw, but removing it from the world will probably not garbage collect it.

Mode7World.RemoveObject
Code (ags) Select
void Mode7World.RemoveObject(int object_i = -1)
Remove a specific object from the world by it's index. If you don't pass a value, it will remove the last valid object added.

Mode7World.RemoveAllsObjects
Code (ags) Select
void Mode7World.RemoveAllsObjects()
Removes all objects from the Mode7 World.

Mode7World.GetAngleObjectAndCamera
Code (ags) Select
int Mode7World.GetAngleObjectAndCamera(Mode7Object* m7obj)
Returns the angle in degrees between the camera and whatever angle is set to a specific object, pointed by their index. Useful when you want to change the graphic of an object based on their relative angle.

Mode7World.UpdateObjects
Code (ags) Select
void Mode7World.UpdateObjects(optional bool do_sort)
Update the screen transform of all objects world positions to their screen positions. You must call it before drawing any objects! You can optionally skip any sorting if you plan rendering using overlays later, since they have zorder property which will be used later by the graphics driver.

Mode7World.DrawObjects
Code (ags) Select
void Mode7World.DrawObjects()
Draws only the objects in the screen sprite. You can use when you need to draw additional things between the ground and the objects. Or when you don't need the ground at all.

Mode7World.DrawObjectsOverlay
Code (ags) Select
void Mode7World.DrawObjectsOverlay()
Draws objects as overlays, without rasterizing at the screen sprite.

Mode7World.DrawWorld
Code (ags) Select
void Mode7World.DrawWorld()
Draws the ground sprite and the objects over it, in the screen sprite.

Mode7World.DrawWorld2D
Code (ags) Select
DynamicSprite* Mode7World.DrawWorld2D()
Gets a dynamic sprite with the world draw in top down view, useful for debugging.

Mode7World.Objects
Code (ags) Select
writeprotected Mode7Object* Mode7World.Objects[i]
Let's you access a specific object in the mode7 world by it's index. Make sure to access a valid position.

Mode7World.ObjectCount
Code (ags) Select
writeprotected int Mode7World.ObjectCount
Gets how many objects are currently in the mode7 world.

Mode7World.ObjectScreenVisibleCount
Code (ags) Select
writeprotected int Mode7World.ObjectScreenVisibleCount
Gets how many objects are actually visible in the screen.

You can iterate through all the screen objects as follows:

Code (ags) Select
for(int i=0; i < m7w.ObjectScreenVisibleCount; i++)
{
  // will make sure to access in order from far to closer
  int index = m7w.ObjectScreenVisibleID[m7w.ObjectScreenVisibleOrder[i]];
  Obj* m7object = m7w.Objects[index];
 
  // do as you you must with you m7object ...
}





Mode7Object
A Mode7Object, you should create objects by using Mode7World.AddObject. After world coordinates are set, you can use Mode7World.UpdateObjects to transform it's coordinates and get updated values in it's Screen prefixed properties.

Mode7Object.SetPosition
Code (ags) Select
void Mode7Object.SetPosition(float x, float y, float z)
A helper function to setting the Object world position in a single line.

Mode7Object.Draw
Code (ags) Select
void Mode7Object.Draw(DrawingSurface* ds)
Draw the object in a DrawingSurface as it would look in a screen.

Mode7Object.X
Code (ags) Select
float Mode7Object.X
Object World X Position on the plane.

Mode7Object.Y
Code (ags) Select
float Mode7Object.Y
Object World Y Position, perpendicular to plane.

Mode7Object.Z
Code (ags) Select
float Mode7Object.Z
Object World Z Position, orthogonal to X position.

Mode7Object.Factor
Code (ags) Select
float Mode7Object.Factor
Object Scaling factor to it's graphics.

Mode7Object.Angle
Code (ags) Select
float Mode7Object.Angle
Object angle, parallel to plane, not used for rendering.

Mode7Object.Graphic
Code (ags) Select
int Mode7Object.Graphic
Object sprite slot, it's width and height is used to calculate the screen coordinates.

Mode7Object.Visible
Code (ags) Select
bool Mode7Object.Visible
Object visibility.

Mode7Object.ScreenX
Code (ags) Select
int Mode7Object.ScreenX
On-Screen Object X position when drawing, if visible. It's regular top, left coordinates, similar to GUI, assumes a Graphic is set.

Mode7Object.ScreenY
Code (ags) Select
int Mode7Object.ScreenY
On-Screen Object Y position when drawing, if visible. It's regular top, left coordinates, similar to GUI, assumes a Graphic is set.

Mode7Object.ScreenWidth
Code (ags) Select
int Mode7Object.ScreenWidth
On-Screen Object Width when drawing, if visible. It's adjusted by the sprite used in Graphic, projection and scaling factor.

Mode7Object.ScreenHeight
Code (ags) Select
int Mode7Object.ScreenHeight
On-Screen Object Height when drawing, if visible. It's adjusted by the sprite used in Graphic, projection and scaling factor.

Mode7Object.ScreenVisible
Code (ags) Select
bool Mode7Object.ScreenVisible
True if object should be drawn on screen. Gets set to false if object is culled when projecting.

Mode7Object.ScreenZOrder
Code (ags) Select
int Mode7Object.ScreenZOrder
ZOrder of the object when drawing on screen, smaller numbers are below, bigger numbers are on top.
[close]

This is just a quick initial release, I plan to update this soon with a better demo and polish the API and other stuff!

Title: Re: MODULE: mode7 0.1.0
Post by: Dualnames on Tue 29/03/2022 06:19:19
3D Overrated
2D Outdated
Long have we waited
MODE 7 ACTIVATED!!


This is awesome, ofc, that would have helped before i botch-coded my own version!
Title: Re: MODULE: mode7 0.1.0
Post by: heltenjon on Tue 29/03/2022 08:09:08
Awesome! I just realized that speed sailing a viking ship around some tropical islands is the perfect start to my day!  ;-D
Title: Re: MODULE: mode7 0.1.0
Post by: eri0o on Tue 29/03/2022 15:17:50
@Dualnames ❤️❤️❤️

@heltenjon I have a thing for the sea 8-)
Title: Re: MODULE: mode7 0.1.0
Post by: lorenzo on Tue 29/03/2022 20:17:06
Ooh, this looks great! Makes you want to create a racing game :-D
I need to try it out!
Title: Re: MODULE: mode7 0.1.0
Post by: heltenjon on Tue 29/03/2022 21:01:53
Quote from: eri0o on Tue 29/03/2022 15:17:50
@heltenjon I have a thing for the sea 8-)
I know. You rented a boat! (https://www.adventuregamestudio.co.uk/site/games/game/2530-i-rented-a-boat/)  :-D
Title: Re: MODULE: mode7 0.1.0
Post by: AndreasBlack on Tue 29/03/2022 21:24:53
Wacky Wheels and Mario Kart were favourite racers as a kid  8-)
So cool! Great job as always!
Title: Re: MODULE: mode7 0.1.0
Post by: FanOfHumor on Thu 31/03/2022 05:47:41
This is great!This is just what i've been looking for.
Title: Re: MODULE: mode7 0.1.0
Post by: LimpingFish on Sat 02/04/2022 21:53:44
I love it!  ;-D
Title: Re: MODULE: mode7 0.1.0
Post by: Danvzare on Sun 03/04/2022 12:27:32
This is AWESOME!
I've even got an idea of what I could use it for...  :-D
Title: Re: MODULE: mode7 0.1.0
Post by: shaun9991 on Sun 03/04/2022 15:25:13
Incredible! Wizardry! :)
Title: Re: MODULE: mode7 0.2.0
Post by: eri0o on Sun 03/04/2022 15:27:46
Hey, thanks everyone for the reception! Again, this would not be possible without Khris original code that made pretty much all the hard math!

I am making a new game to try to stress test this, so I will added some more features to this in a near future. For now, made a 0.2.0 release with some minor additions and fixes.
Title: Re: MODULE: mode7 0.2.0
Post by: Crimson Wizard on Sun 03/04/2022 15:45:24
On a side note, theoretically speaking AGS is capable of rendering textures in full 3D, that is, for example, it could render a sprite rotated at a given angle along non-z axis, to achieve similar effect as the water in this demo game (or race track in AGS Kart demo game).

The biggest issue is the lack of "3d pipeline" as some called it, or in human words - there's no script commands and internal functions that would let configure this at the moment.
Title: Re: MODULE: mode7 0.2.0
Post by: eri0o on Sun 03/04/2022 16:17:47
(https://i.imgur.com/DoOv5F9.gif)video here (https://imgur.com/nKav3G9)

Quote from: Crimson Wizard on Sun 03/04/2022 15:45:24
On a side note, theoretically speaking AGS is capable of rendering textures in full 3D, that is, for example, it could render a sprite rotated at a given angle along non-z axis, to achieve similar effect as the water in this demo game (or race track in AGS Kart demo game).

Having a hardware accelerated way would make it possible to handle a ton more things in the drawing space!

Once ags itself has the accelerated drawing capabilities, I hope the future module additions though are still useful for the world part - I plan to at some point make a Mode7BigWorld or similar for the object streaming pictured above. (for now though, I don't know the proper API to this so I just started to randomly experiment with the idea yesterday, code here: https://github.com/ericoporto/roading/blob/main/roading/zone_manager.ash)

Spoiler
More stuff on the topic idea... Here's a lod like approach, where far away things get baked in the ground to cheap the drawing process

(https://user-images.githubusercontent.com/2244442/161439322-66c5f427-637e-40de-9c93-f0f0a7614e20.gif)video here (https://i.imgur.com/xGpPrAk.mp4)

(maximum limits so far: https://ericoporto.github.io/agsjs/trees/ )
[close]
Title: Re: MODULE: mode7 0.3.0
Post by: eri0o on Sun 17/04/2022 22:20:15
I updated the mode7 plugin module to version 0.3.0.

This introduces DrawObjectOverlays, which will allow rendering using Overlays instead of drawing a sprite. This can be significantly faster, and will allow a lot more objects to be on screen simultaneously.

The web demo has been upgraded to use this method.

I am working on a next version that will introduce big worlds with zoning, shown in the previous screencaps. I do plan a refactor at some point to simplify the API, but I am not sure yet how or when that will happen...
Title: MODULE: mode7 0.3.0
Post by: Hobbes on Mon 18/04/2022 07:34:23
This is just... WOW.

I'm toying with a short-ish MAGS game at some stage, Sci-Fi focussed. To have a ship-flying section with this (starfields streaking by with planets as large pixelated objects) could work, perhaps! Keen to give this a go right now, but can't see it fitting into my current project!

Keep up the great work!
Title: Re: MODULE: mode7 0.3.0
Post by: Snarky on Mon 18/04/2022 09:53:39
Quote from: eri0o on Sun 17/04/2022 22:20:15
I updated the mode7 plugin to version 0.3.0.

Plugin?

Quote from: eri0o on Sun 17/04/2022 22:20:15
rendering using Overlays instead of drawing a sprite. This can be significantly faster, and will allow a lot more objects to be on screen simultaneously.

Why is that? How are overlays quicker than how you were rendering it before?
Title: Re: MODULE: mode7 0.3.0
Post by: eri0o on Mon 18/04/2022 11:24:59
Oh shoot, do not write things when you are sleepy people... I meant module.

So overlays are faster for two reasons

Title: Re: MODULE: mode7 0.3.0
Post by: Snarky on Mon 18/04/2022 11:39:06
So by "sprite drawing" you mean RawDrawing all the sprites to the background each frame?
Title: Re: MODULE: mode7 0.3.0
Post by: eri0o on Mon 18/04/2022 11:45:24
Yeah, Dynamic Sprite DrawImage function.

But seriously, the ordering impacted more in frame rate when going for a low resolution. At low resolution, traversing the array for bubblesort took a ton of time. I attempted a quicksort too, but it did not improve at all (if you search the advanced technical forums I have done a quicksort in AGS script there at some point).

Ordering was very expensive to do on each frame, thus for dynamic sprite based software render in the module there's probably things to investigate in ordering algorithms.

If you feel like playing with the code at a later time, just fork it on GitHub and mess with it, it's possible you will find something interesting through experimenting, then please tell me back. :)
Title: Re: MODULE: mode7 0.3.0
Post by: Crimson Wizard on Mon 18/04/2022 12:02:53
Overlays happened to be the most useful kind of object in todays AGS when you need a large number of moving and transformed (scaled etc) sprites on screen.
* overlays are designed unlimited (limited only by the program memory)
* they are rendered as a texture and thus accelerated by the graphics card (if you run with Direct3D / OpenGL render).
* it's a tiny structure without any superfluous properties that may screw logic up if not taken care of.

Before 3.6.0 room objects and characters could be more performant, as overlays did not support scaling for some reason, but since 3.6.0 beta Overlays may be scaled too
(https://www.adventuregamestudio.co.uk/forums/index.php?topic=59842.msg636645302#msg636645302)

PS. There's currently a serious issue remaining with overlays that they don't share a sprite reference, but make a bitmap copy; i believe this should be taken care of in the future.
Title: Re: MODULE: mode7 0.3.0
Post by: Snarky on Mon 18/04/2022 13:14:47
Quote from: eri0o on Mon 18/04/2022 11:45:24
But seriously, the ordering impacted more in frame rate when going for a low resolution. At low resolution, traversing the array for bubblesort took a ton of time. I attempted a quicksort too, but it did not improve at all (if you search the advanced technical forums I have done a quicksort in AGS script there at some point).

Ordering was very expensive to do on each frame, thus for dynamic sprite based software render in the module there's probably things to investigate in ordering algorithms.

Ah, so when working with overlays you just set the Z-order to be the distance, and the driver takes care of the sorting. I was thinking of the Z-order as the sorting order, and figured you would still need to re-sort them whenever you moved.

I think you could probably optimize something similar for the draw sorting, where you build the sort order as you calculate the object distances (and possibly leverage the idea that most things will remain in the same order as in the last frame), but clearly that adds a lot of complexity.
Title: Re: MODULE: mode7 0.3.0
Post by: abstauber on Mon 18/04/2022 19:58:02
This module + demo games really looks super cool. I'd never thought to see mode7 that fluent with just plain AGS script.
But there's more to it than just drawing on an overlay instead of a room's surface, right?


And is there a particular thread discussing the new possibilities with overlays? All I remember is that when I did my weather module back in 2008, overlays were rather limited.
Title: Re: MODULE: mode7 0.3.0
Post by: Crimson Wizard on Mon 18/04/2022 20:20:55
Quote from: abstauber on Mon 18/04/2022 19:58:02
And is there a particular thread discussing the new possibilities with overlays?

There's not that much to mention, this is a relevant part of the change list (you may find it in the 3.6.0 release thread):
Quote
- Added Overlay.Width and Height properties, that let you freely scale existing Overlay.
- Added readonly Overlay.GraphicWidth and GraphicHeight properties that let read original overlay's graphic size (for textual overlays too).
- Added Overlay.Transparency property.
- Added Overlay.ZOrder property that lets you sort Overlays on screen among themselves and GUI.

EDIT: oh right, and also quite important
Quote- Removed Overlay limit.

So basically overlays at the moment are the only graphical object in game that a) is not limited b) may be created and deleted in script.
Title: Re: MODULE: mode7 0.3.0
Post by: eri0o on Mon 18/04/2022 20:23:46
Hum, I need to write the manual entry, but the gist is:

Overlays are unlimited (it used to be possible to have at maximum 20 overlays)
You can move them on screen, set width and height, and transparency. You can also set their Z order.

Other than this you can now access directly the portrait and text overlays used by the engine sierra speech (and other speech modes I think).

(https://user-images.githubusercontent.com/2244442/162595346-1dba4a8f-fca8-4c93-8c3d-0d83ac8dd076.gif)
Here's an example game to play around:
keysAndCups.zip (https://github.com/adventuregamestudio/ags/files/8457948/keysAndCups.zip)

Edit: CW answered too, but yeah. That does already makes it useful to do things. Like, I can probably do a cutdown version of my imgi module to provide a scripted hardware accelerated GUI if I remove the scrolling component from that module.

Here's a demo I placed hidden above: https://ericoporto.github.io/agsjs/trees/

Has to be run in a desktop computer with a mouse, WASD walks, C switches in and out of the Ship, mouse movement looks around or set direction. As you click, the game will enter fullscreen, and grab the mouse and set the mouse to infinity mode - this is still a bit hackish in the Web port, so as you exit the fullscreen the mouse will be released and not work in it anymore...
Title: Re: MODULE: mode7 0.3.0
Post by: abstauber on Mon 18/04/2022 20:44:13
Ah okay, thanks for the clarification.
I might have some more question, but I'll start a separate thread.

Back to topic ;)
Title: Re: MODULE: mode7 0.3.0
Post by: RootBound on Sun 23/07/2023 16:28:31
Hey mods, I hope it's OK to ask questions about a module that hasn't been commented on in over a year.

I'm trying to experiment with the module and having difficulty.

Right now I'm just trying to set a ground sprite and get it to be visible when I run the game. Nothing else yet.

I started with the blank game template, one empty room, and a player character with no sprite, set to start in the empty room. Then I imported a single ground sprite (slot 1).

I wrote this code, based on my best understanding of the script API detailed in the original post above:

// room script file

Mode7 ThePlace;

function room_Load()
{
  ThePlace.SetViewscreen(640, 180, 0, 0); //should cover half of screen. I'm assuming 0,0 places top left corner of drawing in top left corner of game viewport screen. Tried it at 320, 180 (center of screen) as well, with no difference in effect.
  ThePlace.SetGroundSprite(1); //some green ground
  ThePlace.SetCamera(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); //This will turn out to be a problem, but changing it all to 1.0 doesn't fix everything.
  ThePlace.Draw();
}

First I get an error saying sprite 19 doesn't exist. I wonder where this is called from and find it in line 111 of the Mode7 script. Not sure what that's about. I import 18 more sprites, all unique, then run again.

This time I get an error saying line 222 is dividing by zero. I look there and see it seems related to the camera. Change all camera values to one, like so:

Mode7.SetCamera(1.0, 1.0, 1.0, 1.0, 1.0, 1.0);
This time the game runs but I still just get a black screen with nothing drawn. Did I miss a command?

Title: Re: MODULE: mode7 0.3.0
Post by: Khris on Thu 31/08/2023 10:12:59
@RootBound

The signature of the SetCamera method is
  void Mode7.SetCamera(float x, float y, float z, float xa, float ya, float focal_length)
x,y,z is the position, so y should be greater than 0 or the camera is inside the plane.

xa and ya are the camera angles, i.e. xa determines the direction the camera is facing, ya is the up/down angle afaik.
xa can be anything from 0 to 2 * Pi, but values outside that range should wrap around. ya is supposed to be something like -0.1 for instance, i.e. the camera is slightly looking down.

The last value is the focal length, essentially a zoom factor. Try values like 35.0.
Title: Re: MODULE: mode7 0.3.0
Post by: RootBound on Thu 31/08/2023 18:15:16
Hi @Khris thanks so much for the response. I tinkered with the values (also tried changing the ground sprite in case that was causing the problem) and unfortunately I am still just getting a black screen.

Here's the entirety of the room code:

// room script file

Mode7 ThePlace;

function room_Load()
{
  ThePlace.SetViewscreen(100, 100, 100, 100);
  ThePlace.SetGroundSprite(17);
  ThePlace.SetCamera(10.0, 10.0, 10.0, 1.0, -0.1, 35.0);
  ThePlace.Draw();
 
}

Any other errors you see? I appreciate it.
Title: Re: MODULE: mode7 0.3.0
Post by: Khris on Sun 03/09/2023 14:25:06
The .Draw() call goes into room_RepExec; other than that I don't see an error.
The SetViewScreen command expects x, y, width and height, so setting all to 100 should work.
Title: Re: MODULE: mode7 0.3.0
Post by: RootBound on Sun 03/09/2023 14:57:50
Thanks @Khris. I put the Draw() in room_RepExec, and I imported a very simple background image to the room just to make sure the room is actually loading. It does fade in, but no mode7 objects are drawn. @eri0o do you have any thoughts?
Title: Re: MODULE: mode7 0.3.0
Post by: Khris on Sun 03/09/2023 15:16:28
Maybe the camera position and angle causes the mode7 ground to be off-screen?

Try
ThePlace.SetCamera(0.0, 10.0, 0.0, 0, -0.1, 100.0);
Title: Re: MODULE: mode7 0.3.0
Post by: newwaveburritos on Sun 03/09/2023 21:53:07
I think there are some debug keys you can use to move the camera around.  I, K, O, L all do something with the camera at least in the demo.  Arrow keys, too.
Title: Re: MODULE: mode7 0.3.0
Post by: RootBound on Wed 06/09/2023 13:13:00
@Khris No change unfortunately.
@newwaveburritos Didn't get a response from the debug keys either. I saw you're making your own game with mode7. What did you use as your starting base? Did you use a template and then add the module?
Title: Re: MODULE: mode7 0.3.0
Post by: Khris on Wed 06/09/2023 16:20:45
Those keys only work if they are handled accordingly, which isn't the case with just the module.

Not sure how to debug this tbh; can you maybe start with the demo game instead?
Title: Re: MODULE: mode7 0.3.0
Post by: newwaveburritos on Wed 06/09/2023 18:25:47
I definitely worked backwards from the demo rather than forward from the module.
Title: Re: MODULE: mode7 0.3.0
Post by: RootBound on Wed 06/09/2023 20:58:33
Thanks, both of you. The issue I was having with working backwards from the demo is that there is so much there that I don't know what is needed.

All I want is to create a mode7 ground object(?) that I can move across in one direction at several different (controllable) speeds, and to be able to track the mode7 camera coordinates so that I can teleport the camera back to the start when the camera reaches a certain point, and then change the ground texture. I don't need the boat or the sky or the 360 degree movement or even keyboard control.

This feels like it should be doable with the module but I don't know where to start with the whole demo in place.
Title: Re: MODULE: mode7 0.3.0
Post by: Khris on Thu 07/09/2023 13:02:19
I found the missing piece: the module only draws to its DynamicSprite, and you have to draw that to the room:

function room_RepExec()
{
  ThePlace.Draw();
  DrawingSurface* ds = Room.GetDrawingSurfaceForBackground();
  ds.DrawImage(0, 0, ThePlace.Screen.Graphic);
  ds.Release();
}
Title: Re: MODULE: mode7 0.3.0
Post by: RootBound on Thu 07/09/2023 16:35:13
Quote from: Khris on Thu 07/09/2023 13:02:19I found the missing piece: the module only draws to its DynamicSprite, and you have to draw that to the room:

IT WORKS!!!  ;-D  ;-D  ;-D  Now I can actually start experimenting!

Amazing. Thanks a million.
Title: Re: MODULE: mode7 0.3.0
Post by: RootBound on Thu 07/09/2023 17:28:21
EDIT: turns out this has nothing to do with the background, actually. This is the "sprite 19" issue from my original post. Sprite 19 was the same as the background image. Easy workaround is to make sprite 19 transparent. Doesn't solve the underlying issue, but it works.

Something extremely strange is happening. It appears that a copy of the room background image (in this case two color blocks) is being drawn on top of the mode7 ground texture. Could this an issue with GetDrawingSurfaceForBackground, or do you think the mode7 module is not drawing the texture correctly?
(https://i.imgur.com/KusUj67.png)
Title: Re: MODULE: mode7 0.3.0
Post by: RootBound on Fri 08/09/2023 01:07:40
Experiment is making progress! Thanks again to @Khris for all your help and to @eri0o for the module. This is great fun.

Title: Re: MODULE: mode7 0.3.0
Post by: Crimson Wizard on Fri 08/09/2023 01:19:23
Quote from: Khris on Thu 07/09/2023 13:02:19I found the missing piece: the module only draws to its DynamicSprite, and you have to draw that to the room:

BTW, you may assign the module's graphic to a room object or overlay to skip a second bitmap redraw and make things slightly faster. You may also do that once the dynamic sprite is created, probably at room load (?).

Code (ags) Select
function room_Load()
{
    // other set up

    oObject1.Graphic = ThePlace.Screen.Graphic;
}

When the dynamic sprite is updated, the object's look will also get updated automatically.
Title: Re: MODULE: mode7 0.3.0
Post by: newwaveburritos on Fri 08/09/2023 06:16:28
Ha ha dang that looks great.  My Quarantine-clone is kind of a pile by comparison.
Title: Re: MODULE: mode7 0.3.0
Post by: Khris on Fri 08/09/2023 10:40:23
I also encountered the Sprite 19 issue; the module is based on my old AGSKart code, and I simply hardcoded a bunch of stuff, including sprite slot 19 which ended up in Mode7::_DrawGroundSprites.
The workaround is to assign a random sprite to slot 19.

The two color block is probably due to the hard coded animated question mark; whatever you choose as sprite 19 ends up drawn to the ground on top of the main texture.
Title: Re: MODULE: mode7 0.3.0
Post by: newwaveburritos on Fri 08/09/2023 19:13:54
Oh yeah I did notice that question mark block as I was driving around doing the testing lol.  I was just like "eh I'll figure that out later..."
Title: Re: MODULE: mode7 0.3.0
Post by: RootBound on Sun 25/08/2024 17:08:03
Hey @Khris and @eri0o I am finally taking my game project further, and am having an issue with the billboard sprites - they appear to be passing much slower than the ground sprite is passing. Any idea what could be causing this?

VIDEO HERE (https://i.imgur.com/3hA3gkY.mp4)

// room script file

//Mode7 components
Mode7World TheWorld;
float cameraZ = 1700.0;
DrawingSurface* ds;
Overlay* GroundTexture;

function room_Load()
{
  //set up Mode7
  TheWorld.SetViewscreen(640, 360, 0, -10);
  TheWorld.SetGroundSprite(22);
  WhichTerrain = "Forest";
  TheWorld.SetCamera(0.0, 30.0, cameraZ, 0.0, 0.0, 360.0);
  TheWorld.AddObject(100, 300, 0.2, 7);
  TheWorld.AddObject(200, 300, 0.2, 7);
  TheWorld.AddObject(-100, 300, 0.2, 7);
  TheWorld.AddObject(-200, 300, 0.2, 7);
  TheWorld.AddObject(100, 500, 0.2, 7);
  TheWorld.AddObject(-100, 500, 0.2, 7);
  TheWorld.AddObject(-200, 500, 0.2, 7);
  SetGameSpeed(80);
 
}

function repeatedly_execute_always()
{
  //draw Mode7 ground every frame
 
  TheWorld.Draw();
  TheWorld.UpdateObjects(false);
  TheWorld.DrawObjectsOverlay();
 
  //Overlay version
  GroundTexture = Overlay.CreateRoomGraphical(0, 0, TheWorld.Screen.Graphic, true);
  GroundTexture.ZOrder = 0;
 
  //drawing surface version
  //ds = Room.GetDrawingSurfaceForBackground();
  //ds.DrawImage(0, 0, TheWorld.Screen.Graphic);
  //ds.Release();
 
  //place initial Mode7 camera position
  TheWorld.SetCamera(0.0, 30.0, cameraZ, 0.0, 0.0, 360.0);
 
  //move camera and change track animation loop/speed depending on speed variable
    if(Speed == 4){
      cameraZ -= 3.0;
      TrackAnimDelay = 0;
      TrackLoop = 1;
    }
    if(Speed == 3){
      cameraZ -= 1.7;
      TrackAnimDelay = 0;
      TrackLoop = 0;
    }
    if(Speed == 2){
      cameraZ -= 0.85;
      TrackAnimDelay = 1;
      TrackLoop = 0;
    }
    if(Speed == 1){
      cameraZ -= 0.45;
      TrackAnimDelay = 2;
      TrackLoop = 0;
    }
    if(Speed == 0){
      cameraZ -= 0.0;
      TrackLoop = 0;
    }
     
  //reset camera position to maintain continuous terrain loop
  if(cameraZ <= 300.0){
    cameraZ = 1700.0;
  }
 
  //debug GUI text
  LblSpeed.Text = String.Format("Speed:%d", Speed);
  LblTrackLoop.Text = String.Format("Track:%d", TrackLoop);
  LblTrackAnimDelay.Text = String.Format("AnimDel:%d", TrackAnimDelay);
  LblCameraZ.Text = String.Format("CameraZ:%d", (FloatToInt(cameraZ)));
 
  //Change Terrain if button was just clicked
  if (JustClickedTerrainButton){
    JustClickedTerrainButton = false;
    change_Terrain();
  }
}
Title: Re: MODULE: mode7 0.3.0
Post by: RootBound on Sun 25/08/2024 17:25:06
And the effect actually gets much worse if the scale of the objects is increased (no other changes).

VIDEO HERE (https://i.imgur.com/8DEoMqs.mp4)

EDIT: and if you scale the object down to 0.01 it gets much closer to moving correctly.
Title: Re: MODULE: mode7 0.3.0
Post by: Crimson Wizard on Tue 27/08/2024 18:41:00
Something is off in this module. I never used it myself, but I've got couple of reports from RootBound, one appeared to be an engine's bug with software renderer, and another seems to be an issue with this module.

The reason why it was not noticed maybe is because it becomes a real problem only with Software renderer.

What I found is that when some object gets past the camera, it is not marked as "not visible", and keeps growing in scale.

I did not have time to investigate this well, but quickly looking at the code in UpdateObjects function, where it calculates the distance to camera and object scale, makes me confused, because Visible flag is nowhere set, which means that it never changes to "false".

As a result, something like following happens:
Code (ags) Select
[Script][Debug]: Object 0, zd = 203.03, f = 1.77, w = 156, h = 227
[Script][Debug]: Object 1, zd = 203.03, f = 1.77, w = 156, h = 227
[Script][Debug]: Object 2, zd = 203.03, f = 1.77, w = 156, h = 227
[Script][Debug]: Object 3, zd = 203.03, f = 1.77, w = 156, h = 227
[Script][Debug]: Object 4, zd = 3.03, f = 118.64, w = 10452, h = 15186
[Script][Debug]: Object 5, zd = 3.03, f = 118.64, w = 10452, h = 15186
[Script][Debug]: Object 6, zd = 3.03, f = 118.64, w = 10452, h = 15186
[Script][Debug]: Object 0, zd = 202.18, f = 1.78, w = 157, h = 228
[Script][Debug]: Object 1, zd = 202.18, f = 1.78, w = 157, h = 228
[Script][Debug]: Object 2, zd = 202.18, f = 1.78, w = 157, h = 228
[Script][Debug]: Object 3, zd = 202.18, f = 1.78, w = 157, h = 228
[Script][Debug]: Object 4, zd = 2.18, f = 164.81, w = 14520, h = 21096
[Script][Debug]: Object 5, zd = 2.18, f = 164.81, w = 14520, h = 21096
[Script][Debug]: Object 6, zd = 2.18, f = 164.81, w = 14520, h = 21096

Notice the gigantic object scales: 14520 x 21096, and they keep growing!

This is not a big issue in Direct3D / OpenGL, because they scale textures only when rendering them.

But Software renderer is creating transformed (scaled) overlay bitmaps in memory, which quickly occupy all available memory space and crash the program.


EDIT:
On engine's side this situation may be improved by not preparing new overlay's graphic while it's offscreen. This is easy to do for GUI and screen overlays, but bit more complicated for room objects and room overlays, as these will have to be tested against all existing cameras.
Title: Re: MODULE: mode7 0.3.0
Post by: Crimson Wizard on Wed 28/08/2024 07:18:14
Seeing how in Overlays mode overlays are not getting removed from memory even when they are set as "not visible on screen", I think this module works with assumption that player may return to the same place and see these objects again.

This may not be always the case. For instance, in RootBound's game, at least from the looks of it, the camera only moves forward, so any object past the camera will never bee seen again.

Perhaps it will be a good idea to add a setting that sais: "automatically remove objects that went behind the camera"?
Title: Re: MODULE: mode7 0.3.0
Post by: eri0o on Wed 28/08/2024 10:48:54
In a new version I have removed the ground part because it never looks great (AGS just doesn't have a proper affine tramsform) and it relies on drawing surface which is quite slow.

The mode7 world is to be treated as a render area and your game logic should also deal with yanking and putting objects on it as necessary.

I will probably kill this module and put up a new one focusing only on the sprite scaler aspects. I have a sibling module that I am in process of glueing it together with this one - you can see it in the source code of don't give up the cat - it uses buckets to hold the objects that are out of the interesting area, meant for bigger worlds. https://github.com/ericoporto/dont-give-up-the-cat/blob/main/dont-give-up-the-cat/ZoneManager.ash

I am though working on a new game using this and have picked a lot of bugs but my updated code is ags4 only. I haven't released the code yet because I want to actually make a game first.
Title: Re: MODULE: mode7 0.3.0
Post by: Crimson Wizard on Wed 28/08/2024 11:40:09
Quote from: eri0o on Wed 28/08/2024 10:48:54In a new version I have removed the ground part because it never looks great (AGS just doesn't have a proper affine tramsform) and it relies on drawing surface which is quite slow.

Hmm, that's a shame, I think it looks pretty okay for the low res in the examples that I've seen, about how old games of this style looked like in 90-ies.

About speeding this up, in it might be possible to add sprite rotation around x/y axes too, then this will be done very fast by Direct3D/OpenGL.
But my concern here is about texture z-sort, whether it will get in a way or not (because if texture is rotated "away" from the viewer, theoretically it "collides" with any textures behind it).

Quote from: eri0o on Wed 28/08/2024 10:48:54The mode7 world is to be treated as a render area and your game logic should also deal with yanking and putting objects on it as necessary.

That may be true, but I think the module still has to cull the objects that go behind the "camera", marking them as not visible.
Title: Re: MODULE: mode7 0.3.0
Post by: eri0o on Wed 28/08/2024 11:57:12
It does remove the object by just marking it as not visible (here is an example in don't give up the cat https://github.com/ericoporto/dont-give-up-the-cat/blob/fc2810d9d580f6e1b51742489fa388fbd96cdbd0/dont-give-up-the-cat/Mode7Lite.asc#L247) I just haven't upgraded this version of the module. The problem is that the software drawing route of the module is kinda what you need to do to properly mix with the software drawing of the floor. But the software drawing path along with the ground and you can manage like maybe 200-300 things on screen decently. Obviously if you yank both and just rely on sprite scaling you can go much further: https://github.com/adventuregamestudio/ags/pull/2478#issuecomment-2253633845

I would like to yank the sprite scaling to a separate module and have the mode7 one be just about the affine scaling that can be used on a ground. I just haven't had the time for this.

I also don't remember if it's the case, but I had issues with the fake affine in code due to ints and rotation, it felt like it wasn't enough for the coordinates and you could easily be in a position where you are in between the possibilities so you want to turn and the ground starts blinking between both states, I would advise trying to design the game aesthetics in a way that is pixel art and works ok with this because otherwise there just is not enough precision for proper 3D ground rotation. If we could actually work per pixel (example here (https://github.com/adventuregamestudio/ags/issues/1997#issuecomment-1537151745)) then the rotation can be much smoother, and you get formidable precision. But obviously you lose performance a lot.

If we could figure an API at least the affine tramsform could maybe be added in the engine? (at least the skew from AGS waves could be added (https://github.com/adventuregamestudio/ags/compare/master...ericoporto:ags:add-agswaves))
Title: Re: MODULE: mode7 0.3.0
Post by: Crimson Wizard on Wed 28/08/2024 12:13:52
I tried a bit different fix based on the current downloadable module:

Code (ags) Select
this.Objects[obin].ScreenVisible = this.Objects[obin].Visible
      && (obj_d_x + w > this._screen_x) && (obj_d_y + h > this._screen_y)
      && (obj_d_x < this._screen_width) && (obj_d_y < this._screen_height);

That marks them not visible when they are off the virtual screen.

This is enough for objects that go into sides as camera moves forward, but still not enough to cull the objects which you approach directly, which would keep it in the camera's center. (I do not have a strong proof, because using a specific game scene for a test, but may imagine this case).

For a proper solution this should also include a z test, but I don't know the code well enough to figure this out quickly.


BTW, in regards to speed, the game I'm testing displays a camera moving along the railroad in a "straight line", with only few objects around,
and it runs pretty well in Software renderer, making ~50 fps at average on my machine.
I suppose that it may be feasible to aim at stable 30 fps mode7 scenes with this module.
Title: Re: MODULE: mode7 0.3.0
Post by: RootBound on Wed 28/08/2024 12:15:12
Quote from: Crimson Wizard on Wed 28/08/2024 07:18:14This may not be always the case. For instance, in RootBound's game, at least from the looks of it, the camera only moves forward, so any object past the camera will never bee seen again.

That's actually an illusion. The camera does in fact teleport back to the starting location repeatedly. I just did pretty well at making this seamless.

@eri0o my game actually doesn't involve any rotation, so I haven't run into these issues. I like the "zones" idea though. I might be able to script a function to remove objects once they leave the screen.

One last question though. Is it possible to make the objects appear when they are further away? Rught now they appear when the camera feels quite close to them. I didn't see an option to adjust this in the script API. If there isn't one, I can live with it, though. Thanks for your responses here.
Title: Re: MODULE: mode7 0.3.0
Post by: Crimson Wizard on Wed 28/08/2024 12:32:50
Quote from: eri0o on Wed 28/08/2024 11:57:12If we could figure an API at least the affine tramsform could maybe be added in the engine? (at least the skew from AGS waves could be added (https://github.com/adventuregamestudio/ags/compare/master...ericoporto:ags:add-agswaves))

Alan have preplanned "skewx, skewy" properties a while ago; these may be found in AGS 4 save formats as placeholders.
I don't know what is the correct way to do this with texture renderers, that may be either changing texture vertices (moving corner coords to the sides), or using a shader, i suppose it's a matter of finding out.
Title: Re: MODULE: mode7 0.3.0
Post by: RootBound on Wed 28/08/2024 12:35:12
Quote from: Crimson Wizard on Wed 28/08/2024 12:13:52I tried a bit different fix based on the current downloadable module:

Code: ags

this.Objects[obin].ScreenVisible = this.Objects[obin].Visible
      && (obj_d_x + w > this._screen_x) && (obj_d_y + h > this._screen_y)
      && (obj_d_x < this._screen_width) && (obj_d_y < this._screen_height);


@Crimson Wizard Will this allow the objects to come back when they reappear on screen? If not, I can try scripting my own function within the room script.

Quote from: Crimson Wizard on Wed 28/08/2024 12:13:52BTW, in regards to speed, the game I'm testing displays a camera moving along the railroad in a "straight line", with only few objects around,
and it runs pretty well in Software renderer, making ~50 fps at average on my machine.

I've been getting the same speeds but am planning to add a lot more objects, as the ones there now are basically just test objects. But so far the speed has been very good, and I've been happy with it.

Quote from: eri0o on Wed 28/08/2024 11:57:12Obviously if you yank both and just rely on sprite scaling you can go much further: https://github.com/adventuregamestudio/ags/pull/2478#issuecomment-2253633845 (https://github.com/adventuregamestudio/ags/pull/2478#issuecomment-2253633845)

@eri0o wow, this looks incredible! Is the ground here made up of object sprites?
Title: Re: MODULE: mode7 0.3.0
Post by: Crimson Wizard on Wed 28/08/2024 12:41:22
Quote from: RootBound on Wed 28/08/2024 12:35:12Will this allow the objects to come back when they reappear on screen?

Of course, since objects appear back inside the camera bounds, they immediately become marked as visible.
I was testing this in your game, and software renderer no longer crashes with out of memory error.
Title: Re: An AGS Train Simulator
Post by: Snarky on Wed 09/10/2024 21:03:10
Quote from: RootBound on Mon 07/10/2024 22:25:29

Looks awesome.

I am a little bit bothered by the jittering of the sprites. I guess it comes from the scaling and positioning being rounded to the nearest int, and positioned based on the baseline at the bottom, which can case the top edge to "jump back" a pixel.

E.g. if the y coordinate of the bottom is actually at 123.46 pixels and the height is 53.27 pixels, so that the top is at 70.19, that rounds to bottom=123 and height=53, giving top=70. But if it scales up a bit (comes closer) so that the height is 53.48 with the bottom at 123.56 and top at 70.08, that rounds to bottom at 124 and height 53, giving top=71. IOW, the top of the sprite moves down even though actually it should move up (or, given rounding, stay in the same position).

I think the problem would be fixed by calculating and rounding the projected coordinates of the corners of the sprite and scaling accordingly, rather than calculating and rounding the scaling directly. (The jitter would then affect the sprite size rather than the position of the edges, which I think will be less noticeable.)
Title: Re: Re: An AGS Train Simulator
Post by: RootBound on Wed 09/10/2024 22:35:42
@Snarky less jittering would be amazing. Right now the sprite scaling is all handled by the mode7 module, which is pretty beyond me in terms of programming. I can try reading through it, but I doubt I would know what to change or how to change it unfortunately.
Title: Re: MODULE: mode7 0.3.0
Post by: Snarky on Thu 10/10/2024 06:05:48
Quote from: RootBound on Wed 09/10/2024 22:35:42@Snarky less jittering would be amazing. Right now the sprite scaling is all handled by the mode7 module, which is pretty beyond me in terms of programming. I can try reading through it, but I doubt I would know what to change or how to change it unfortunately.

Looking at the code here (https://github.com/ericoporto/mode7/blob/main/mode7_demo/Mode7.asc#L499), the most relevant lines appear to be 499-504 (really only 503-504, but including the others for context):

Code (ags) Select
    // turn into 2D coords
    x2d = this._screen_x + this._screen_width/2  + FloatToInt(_ox[in] * f, eRoundNearest);
    y2d = this._screen_y + this._screen_height/2 + FloatToInt(_oy[in] * f, eRoundNearest);
    // size
    w = FloatToInt(IntToFloat(Game.SpriteWidth[slot])  * f * factor, eRoundNearest);
    h = FloatToInt(IntToFloat(Game.SpriteHeight[slot]) * f * factor, eRoundNearest);

There are some other parts of the calculation I don't fully follow (e.g. why it subtracts 90% of the sprite height from the final coordinate in line 507), but as a first attempt I would try:

Code (ags) Select
    // turn into 2D coords
    x2d = FloatToInt(_ox[in] * f, eRoundNearest);
    y2d = FloatToInt(_oy[in] * f, eRoundNearest);
    // size
    w = FloatToInt((IntToFloat(Game.SpriteWidth[slot]) * factor + _ox[in]) * f, eRoundNearest) - x2d;
    h = FloatToInt((IntToFloat(Game.SpriteHeight[slot]) * factor + _oy[in]) * f, eRoundNearest) - y2d;
    x2d += this._screen_x + this._screen_width/2;
    y2d += this._screen_y + this._screen_height/2;

This works in my head, but I haven't tested this to see if it actually gives the correct results. (Edit: Possibly the signs should be switched in the calculations, so that it subtracts _ox[in] and _oy[in] before rounding and adds x2d and y2d afterwards.) And it's possible that it's necessary to change a few other lines as well.
Title: Re: MODULE: mode7 0.3.0
Post by: RootBound on Thu 10/10/2024 13:54:19
@Snarky So this line throws a "cannot convert int to float" error when compiling.
Code (ags) Select
    w = FloatToInt(IntToFloat((Game.SpriteWidth[slot]) * factor + _ox[in]) * f, eRoundNearest) - x2d;

I tried removing the FloatToInt like so
Code (ags) Select
w = IntToFloat(Game.SpriteWidth[slot]) * factor + _ox[in] * f, eRoundNearest - x2d;
which of course gave a "cannot convert float to int" error. Not sure if it's being affected by a line somewhere else. There's also a lot of int/float conversions around generally, so I'm not sure which spot in the chain is throwing the error.  x2d is an int at this point, so I don't THINK that's the problem. It might be the _ox[in] piece, but I tried putting an IntToFloat around that too, and it didn't fix anything. Probably I'm just making a fool of myself.
Title: Re: MODULE: mode7 0.3.0
Post by: Snarky on Thu 10/10/2024 15:00:40
Quote from: RootBound on Thu 10/10/2024 13:54:19@Snarky So this line throws a "cannot convert int to float" error when compiling.
Code (ags) Select
    w = FloatToInt(IntToFloat((Game.SpriteWidth[slot]) * factor + _ox[in]) * f, eRoundNearest) - x2d;

My error with the parentheses. Hopefully this works better:

Code (ags) Select
    w = FloatToInt((IntToFloat(Game.SpriteWidth[slot]) * factor + _ox[in]) * f, eRoundNearest) - x2d;
Title: Re: MODULE: mode7 0.3.0
Post by: RootBound on Thu 10/10/2024 16:25:10
@Snarky It compiles!

At first the jittering actually looked a little worse (though seemingly smoother?) but then I switched the + and - signs in the equation, as you suggested earlier. There seems to be some jitter still present, but I do think less? The image of each sprite also seems to visibly change more frequently.


Thanks for your code!

EDIT: Hmm, now that I test it more, the vertical jitter seems to have been replaced by a bit of a side-to-side shivering, especially when objects are far away and camera movement is slower. I suppose I'll do a little more comparing of both methods and decide which I like better.
Title: Re: MODULE: mode7 0.3.0
Post by: Snarky on Thu 10/10/2024 19:05:03
Great! You could try using just the change to the height calculation and not the width change. (The code appears to place sprites based on the horizontal midpoint, which is another calculation that could induce jittering.)
Title: Re: MODULE: mode7 0.3.0
Post by: RootBound on Thu 10/10/2024 21:29:42
Quote from: Snarky on Thu 10/10/2024 19:05:03You could try using just the change to the height calculation and not the width change.

Huh! That seems to actually result in the best of both worlds. I would never have thought of that. Much appreciated!