Has anyone seen or done Mode 7 with MonoGame?

Mode 7 - Wikipedia

I recently(ish) went 10 rounds with trying to get a Mode 7 implementation using a SpriteBatch in Monogame. On the face of it, it sounded entirely simple.

I decided that my knowledge wasn’t quite there and figured it was a problem most easily solved by using textured geometry and making a game that was 3D pretending to be 2D.

But, thought that I’d throw this out to the community here - has anyone seen or done anything like this? I was looking at using it to create textured floors/ceilings in a Ray Casting engine… but I’ve also got a bit of a desire to create a Mario Kart / F-Zero clone of old at some point.

Thanks!

1 Like

Remember seeing it once but that was a long time ago…

I converted the first bit of code from the linked page below to C#…

// Using System.Numerics.FixedPoint static class
    using static System.Numerics.FixedPoint;

// Constants and fields declaration for 3D graphics camera position and distances
    private Vector3 _camPos; // camera position
    private const int M7D = 3000; // fixed point multiplication factor for horizontal distance
    private const int LuDiv = 65536; // fixed point representation for 1.0

    // Method implementation for Mode7 affine 3D projection on background 2
    void M7HblA()
    {
        // Variable declaration for intermediate calculations
        int lam, xs, ys;

        // Calculate pseudo-distance between camera and screen
        lam = (int)(_camPos.Y * LuDiv / GraphicsDevice.Viewport.Height * 256 >> 8);

        // Calculate coefficients for affine transformation
        xs = 120 * lam; // horizontal scaling factor
        ys = M7D * lam; // vertical scaling factor

        // Look up local sine and cosine values
        var cosf = (fixed) (_gCosf * 256);
        var sinf = (fixed) (_gSinf * 256);

        // Compute scale and rotation components of affine transformation matrix
        REG_BG2PA = (cosf * lam >> 8).IntValue(); // horizontal scale
        REG_BG2PC = (sinf * lam >> 8).IntValue(); // vertical scale and rotation

        // Apply translation and scaling to initial camera position
        REG_BG2X = (int)(_camPos.X - (xs * cosf - ys * sinf) / 256); // horizontal translation
        REG_BG2Y = (int)(_camPos.Z - (xs * sinf + ys * cosf) / 256); // vertical translation
    }

But this should help you get going:

Tonc: Mode 7 Part 1 (coranac.com)

EDIT
Updated code with comments

1 Like

Thanks @MrValentine!

Bookmarking this resource - hopefully get a chance to have another shot at it this weekend :slight_smile:

1 Like

Let us know how it goes! :sake:

This is not a simple problem in modern times.

The SNES and all non-HDMI consoles wrote each pixel color to VRAM or directly timed to the output (ATARI 2600, C64, Apple II…). This was called racing the scan-line.

Memory latency has increased with higher clock rates. The channel widths have increased, leading to higher throughput. Reading and writing any values smaller than 64 bits incurs a performance penalty.


Mode 7 requires a non-linear UV texture mapping, since the texture sampler linearly interpolates between vertices, a custom solution is required.

So there are two modern solutions:

Best Solution: Write a custom pixel shader to address the non-linearity by transforming the UV inputs of the texture sampler call.

Or:
Pre-Compute the transformed textures.(not a good idea due to storage and memory size requirements)


And one classic solution(sits between the above solutions):
Use Texture.GetData() and Texture.SetData() to use the data array to manually set each pixel.

Pin the data and use pointer arithmetic in an unsafe context to ensure the calculations complete in a timely manner.
See https://github.com/MonoGame/MonoGame/blob/b21463b419e55b4c898030fc22bee77dabb11210/MonoGame.Framework/Graphics/DefaultColorProcessors.cs#L28 as an example of pinning and modifying texture data in an unsafe context.
This method would provide the closest technique to the original implementation.


My last suggestion would be to implement full 3D and reverse engineer a series of de-projection matrices for each object space’s Z axis. The implementation is well beyond the scope of this response.

1 Like

A little update…

I’ve finally sat down with this puzzle again with a little more knowledge and am close to a solution now using some Matrix Transformations and Render Targets.

This link has been pretty useful too: Game Components Affine and Non-Affine Transforms in Windows Phone 7 (c-sharpcorner.com)

Hopefully I’ll close in on getting it just how I’d like it when I get another spare hour or so.

2 Likes

And after just a little more effort I’ve reached enough of a solution that I think I’ve got the starting place for projects along these lines:

MattDrivenDev/MonoGameMode7: A simple (don’t ask about the matrix math!) SNES-style Mode7 camera for a sprite-based MonoGame game. (github.com)

4 Likes

Really cool project! :smiley:

1 Like