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 APISpoiler
Mode7
Mode7.SetCamera
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
void Mode7.SetViewscreen(int width, int height, optional int x, optional int y)
Sets the screen area it will draw in.
Mode7.SetGroundSprite
void Mode7.SetGroundSprite(int ground_graphic)
Sets the ground sprite, this is the mode7 rendered sprite.
Mode7.SetHorizonSprite
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
void Mode7.SetBgColor(int bg_color)
Sets the color of the background where the ground sprite doesn't reach.
Mode7.SetSkyColor
void Mode7.SetSkyColor(int sky_color)
Sets the color of the sky.
Mode7.TargetCamera
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
void Mode7.Draw()
Draws the ground sprite and horizon rendered in the Screen sprite.
Mode7.ResetGround
void Mode7.ResetGround()
Clears the screen sprite.
Mode7.CameraAngleX
float Mode7.CameraAngleX
The camera angle that is normal to the ground plane (e.g.: up and down).
Mode7.CameraAngleY
float Mode7.CameraAngleY
The camera angle that is on the ground plane (e.g.: left and right).
Mode7.Screen
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
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
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
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
void Mode7World.RemoveAllsObjects()
Removes all objects from the Mode7 World.
Mode7World.GetAngleObjectAndCamera
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
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
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
void Mode7World.DrawObjectsOverlay()
Draws objects as overlays, without rasterizing at the screen sprite.
Mode7World.DrawWorld
void Mode7World.DrawWorld()
Draws the ground sprite and the objects over it, in the screen sprite.
Mode7World.DrawWorld2D
DynamicSprite* Mode7World.DrawWorld2D()
Gets a dynamic sprite with the world draw in top down view, useful for debugging.
Mode7World.Objects
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
writeprotected int Mode7World.ObjectCount
Gets how many objects are currently in the mode7 world.
Mode7World.ObjectScreenVisibleCount
writeprotected int Mode7World.ObjectScreenVisibleCount
Gets how many objects are actually visible in the screen.
You can iterate through all the screen objects as follows:
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
void Mode7Object.SetPosition(float x, float y, float z)
A helper function to setting the Object world position in a single line.
Mode7Object.Draw
void Mode7Object.Draw(DrawingSurface* ds)
Draw the object in a DrawingSurface as it would look in a screen.
Mode7Object.X
float Mode7Object.X
Object World X Position on the plane.
Mode7Object.Y
float Mode7Object.Y
Object World Y Position, perpendicular to plane.
Mode7Object.Z
float Mode7Object.Z
Object World Z Position, orthogonal to X position.
Mode7Object.Factor
float Mode7Object.Factor
Object Scaling factor to it's graphics.
Mode7Object.Angle
float Mode7Object.Angle
Object angle, parallel to plane, not used for rendering.
Mode7Object.Graphic
int Mode7Object.Graphic
Object sprite slot, it's width and height is used to calculate the screen coordinates.
Mode7Object.Visible
bool Mode7Object.Visible
Object visibility.
Mode7Object.ScreenX
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
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
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
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
bool Mode7Object.ScreenVisible
True if object should be drawn on screen. Gets set to false if object is culled when projecting.
Mode7Object.ScreenZOrder
int Mode7Object.ScreenZOrder
ZOrder of the object when drawing on screen, smaller numbers are below, bigger numbers are on top.
This is just a quick initial release, I plan to update this soon with a better demo and polish the API and other stuff!
- v0.1.0 - initial release.
- v0.2.0 - added ResetGround, CameraAngleX, CameraAngleY to Mode7, added Visible to Mode7Object, added AddExternalObject and DrawWorld2D to Mode7World, change GetAngleObjectAndCamera api.
- v0.3.0 - added support for using Overlays for rendering mode7 objects using Mode7World.DrawObjectsOverlay, and also to skip sorting for overlay based drawing.
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!
Awesome! I just realized that speed sailing a viking ship around some tropical islands is the perfect start to my day! ;-D
@Dualnames â¤ï¸â¤ï¸â¤ï¸
@heltenjon I have a thing for the sea 8-)
Ooh, this looks great! Makes you want to create a racing game :-D
I need to try it out!
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
Wacky Wheels and Mario Kart were favourite racers as a kid 8-)
So cool! Great job as always!
This is great!This is just what i've been looking for.
I love it! ;-D
This is AWESOME!
I've even got an idea of what I could use it for... :-D
Incredible! Wizardry! :)
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.
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.
(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/ )
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...
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!
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?
Oh shoot, do not write things when you are sleepy people... I meant module.
So overlays are faster for two reasons
- You push your bitmap graphic to a texture, and instead of bliting it's pixels from that moment forward you just move the texture on screen, no need to keep reading those pixels every frame
- when drawing things on with sprite drawing, you need to do so in the order, you blitz the sprites from the back and then the ones next until the ones near are drawn, and to do so you need to order the sprites before you can draw then. The ordering is VERY SLOW in AGS Script, it has a great impact on drawing the pixels! When using overlays you can set the ZOrder property and let's the AGS graphics driver do the ordering, which is much faster.
So by "sprite drawing" you mean RawDrawing all the sprites to the background each frame?
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. :)
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.
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.
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.
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.
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...
Ah okay, thanks for the clarification.
I might have some more question, but I'll start a separate thread.
Back to topic ;)
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?
@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.
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.
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.
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?
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);
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.
@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?
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?
I definitely worked backwards from the demo rather than forward from the module.
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.
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();
}
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.
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)
Experiment is making progress! Thanks again to
@Khris for all your help and to
@eri0o for the module. This is great fun.
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 (?).
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.
Ha ha dang that looks great. My Quarantine-clone is kind of a pile by comparison.
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.
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..."
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();
}
}
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.
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:
[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.
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"?
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.
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.
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))
I tried a bit different fix based on the current downloadable module:
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.
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.
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.
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?
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.
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.)
@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.
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):
// 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:
// 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.
@Snarky So this line throws a "cannot convert int to float" error when compiling.
w = FloatToInt(IntToFloat((Game.SpriteWidth[slot]) * factor + _ox[in]) * f, eRoundNearest) - x2d;
I tried removing the FloatToInt like so
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.
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.
w = FloatToInt(IntToFloat((Game.SpriteWidth[slot]) * factor + _ox[in]) * f, eRoundNearest) - x2d;
My error with the parentheses. Hopefully this works better:
w = FloatToInt((IntToFloat(Game.SpriteWidth[slot]) * factor + _ox[in]) * f, eRoundNearest) - x2d;
@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.
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.)
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!