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
1 Like
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