MODULE: mode7 0.3.0

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

Previous topic - Next topic

RootBound

Experiment is making progress! Thanks again to @Khris for all your help and to @eri0o for the module. This is great fun.

They/them. Here are some of my games:

Crimson Wizard

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

newwaveburritos

Ha ha dang that looks great.  My Quarantine-clone is kind of a pile by comparison.

Khris

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.

newwaveburritos

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

RootBound

#45
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

Code: ags
// 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();
  }
}
They/them. Here are some of my games:

RootBound

#46
And the effect actually gets much worse if the scale of the objects is increased (no other changes).

VIDEO HERE

EDIT: and if you scale the object down to 0.01 it gets much closer to moving correctly.
They/them. Here are some of my games:

Crimson Wizard

#47
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
[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.

Crimson Wizard

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"?

eri0o

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.

Crimson Wizard

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

eri0o

#51
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) 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)

Crimson Wizard

#52
I 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);

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.

RootBound

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.
They/them. Here are some of my games:

Crimson Wizard

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)

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.

RootBound

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

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

@eri0o wow, this looks incredible! Is the ground here made up of object sprites?
They/them. Here are some of my games:

Crimson Wizard

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

Snarky

#57
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.)

RootBound

@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.
They/them. Here are some of my games:

Snarky

#59
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, the most relevant lines appear to be 499-504 (really only 503-504, but including the others for context):

Code: ags
    // 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
    // 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.

SMF spam blocked by CleanTalk