MODULE: mode7 0.3.0

Started by eri0o, Tue 29/03/2022 02:11:31

Previous topic - Next topic

eri0o

mode7 version 0.3.0

Get Latest Release mode7.scm | GitHub Repo | Download project .zip

AGS Script Module for Mode7 like graphics. See demo in here! (Use Firefox, Safari or Chrome 100+, WASD to move)



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. 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
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
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
void Mode7.SetGroundSprite(int ground_graphic)

Sets the ground sprite, this is the mode7 rendered sprite.

Mode7.SetHorizonSprite
Code: ags
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
void Mode7.SetBgColor(int bg_color)

Sets the color of the background where the ground sprite doesn't reach.

Mode7.SetSkyColor
Code: ags
void Mode7.SetSkyColor(int sky_color)

Sets the color of the sky.

Mode7.TargetCamera
Code: ags
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
void Mode7.Draw()

Draws the ground sprite and horizon rendered in the Screen sprite.

Mode7.ResetGround
Code: ags
void Mode7.ResetGround()

Clears the screen sprite.

Mode7.CameraAngleX
Code: ags
float Mode7.CameraAngleX

The camera angle that is normal to the ground plane (e.g.: up and down).

Mode7.CameraAngleY
Code: ags
float Mode7.CameraAngleY

The camera angle that is on the ground plane (e.g.: left and right).

Mode7.Screen
Code: ags
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
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
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
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
void Mode7World.RemoveAllsObjects()

Removes all objects from the Mode7 World.

Mode7World.GetAngleObjectAndCamera
Code: ags
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
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
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
void Mode7World.DrawObjectsOverlay()

Draws objects as overlays, without rasterizing at the screen sprite.

Mode7World.DrawWorld
Code: ags
void Mode7World.DrawWorld()

Draws the ground sprite and the objects over it, in the screen sprite.

Mode7World.DrawWorld2D
Code: ags
DynamicSprite* Mode7World.DrawWorld2D()

Gets a dynamic sprite with the world draw in top down view, useful for debugging.

Mode7World.Objects
Code: ags
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
writeprotected int Mode7World.ObjectCount

Gets how many objects are currently in the mode7 world.

Mode7World.ObjectScreenVisibleCount
Code: ags
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
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
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
void Mode7Object.Draw(DrawingSurface* ds)

Draw the object in a DrawingSurface as it would look in a screen.

Mode7Object.X
Code: ags
float Mode7Object.X

Object World X Position on the plane.

Mode7Object.Y
Code: ags
float Mode7Object.Y

Object World Y Position, perpendicular to plane.

Mode7Object.Z
Code: ags
float Mode7Object.Z

Object World Z Position, orthogonal to X position.

Mode7Object.Factor
Code: ags
float Mode7Object.Factor

Object Scaling factor to it's graphics.

Mode7Object.Angle
Code: ags
float Mode7Object.Angle

Object angle, parallel to plane, not used for rendering.

Mode7Object.Graphic
Code: ags
int Mode7Object.Graphic

Object sprite slot, it's width and height is used to calculate the screen coordinates.

Mode7Object.Visible
Code: ags
bool Mode7Object.Visible

Object visibility.

Mode7Object.ScreenX
Code: ags
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
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
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
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
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
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!


  • 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.

Dualnames

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!
Worked on Strangeland, Primordia, Hob's Barrow, The Cat Lady, Mage's Initiation, Until I Have You, Downfall, Hunie Pop, and every game in the Wadjet Eye Games catalogue (porting)

heltenjon

Awesome! I just realized that speed sailing a viking ship around some tropical islands is the perfect start to my day!  ;-D

eri0o

@Dualnames ❤️❤️❤️

@heltenjon I have a thing for the sea 8-)

lorenzo

Ooh, this looks great! Makes you want to create a racing game :-D
I need to try it out!

heltenjon


AndreasBlack

Wacky Wheels and Mario Kart were favourite racers as a kid  8-)
So cool! Great job as always!

FanOfHumor

This is great!This is just what i've been looking for.

LimpingFish

Steam: LimpingFish
PSN: LFishRoller
XB: TheActualLimpingFish
Spotify: LimpingFish

Danvzare

This is AWESOME!
I've even got an idea of what I could use it for...  :-D

shaun9991

Support Cloak and Dagger Games on Patreon: https://www.patreon.com/user?u=460039

eri0o

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.

Crimson Wizard

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.

eri0o

#13
video here

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

video here

(maximum limits so far: https://ericoporto.github.io/agsjs/trees/ )
[close]

eri0o

#14
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...

Hobbes

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!

Snarky

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?

eri0o

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.

Snarky

So by "sprite drawing" you mean RawDrawing all the sprites to the background each frame?

eri0o

#19
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. :)

SMF spam blocked by CleanTalk