How exactly is the Viewport Z and Sprite Z factored into drawing?

Started by ForeverZer0, August 13, 2014, 09:40:43 am

Previous topic - Next topic

ForeverZer0

I assumed this was relatively simple, but as it turned out, its not. Now I ask that before you reply, I fully understand what is says in the manual, and I am not some noob who needs the basics explained to me, I am referring to rendering the textures on the device. I am also fully aware that the more recent object gets drawn first if they are equal.

I am running into a few minor issues with Z coordinates for this XNA/IronRuby engine I am making. The only place I really see this problem is with the default battle system. Here is what it appears as:

Spoiler: ShowHide


As you can see, everything appears fine except for the enemy being placed above the @actor_command_window.

I have tried a couple different ways of factoring sprite.z and viewport.z, by simply adding them together as one way. I was incorrect in this assumption, this is the way that I have been calculating, but have run into problems here, as the screenshot demonstrates. The enemy sprites have a Z of 304 on their own viewport that has a Z of 0. The @actor_command_window has a Z of 100 (inherited from Window_Base) without a viewport, so 0 would be added to it to get it's final depth. That makes a final tally of the enemy sprites having a Z of 304, and the @actor_command_window having a Z of 100. This is the reason the enemy sprites are on top, so simply adding them is not the correct answer.


I assumed next that drawing was ordered by the viewports, and the sprite's z only pertained to that particular viewport. For example, if there are two sprites, sprite A with z of 10, and sprite B with z of 0, but sprite B's viewport.z was higher than sprite A's viewport.z, sprite B would still appear on top. I was incorrect in this assumption as well, which can be demonstrated with this little snippet in RMXP.
Spoiler: ShowHide
viewport = Viewport.new(0, 0, 640, 480)
viewport.z = 50
sprite_A = Sprite.new(viewport)
sprite_A.z = 0
sprite_A.bitmap = RPG::Cache.character('001-Fighter01', 0)

sprite_B = Sprite.new
sprite_B.bitmap = RPG::Cache.character('002-Fighter02', 0)
sprite_B.z = 100

If that logic was correct, the second sprite should be under the first, since it does not have a viewport, and the first sprite's viewport has a Z of 50.

So I began testing how they relate within RMXP, and I am just getting more confused.
Spoiler: ShowHide
viewport = Viewport.new(0, 0, 640, 480)
viewport.z = 50
sprite_A = Sprite.new(viewport)
sprite_A.z = 100
sprite_A.bitmap = RPG::Cache.character('001-Fighter01', 0)

sprite_B = Sprite.new
sprite_B.bitmap = RPG::Cache.character('002-Fighter02', 0)
sprite_B.z = 100

No matter which order the sprites are created in, this causes the second sprite to always be on top, even though it does not have a viewport,  and the first one does with a Z of 50, and both sprite's have the same Z value.

Now, lets change the first sprites viewport to 100, and change nothing else.
Spoiler: ShowHide
viewport = Viewport.new(0, 0, 640, 480)
viewport.z = 100
sprite_A = Sprite.new(viewport)
sprite_A.z = 100
sprite_A.bitmap = RPG::Cache.character('001-Fighter01', 0)

sprite_B = Sprite.new
sprite_B.bitmap = RPG::Cache.character('002-Fighter02', 0)
sprite_B.z = 100

The second sprite appears on top, but only because it was created more recently, as you can test by changing the creation order. This leads me to believe that the viewport's Z doesn't do a damn thing, since it appears it wasn't even factored in to the drawing, and only the order the sprites were created in.

So let's test this theory, I will increase the viewport's Z very high, and lower the sprite's Z.
Spoiler: ShowHide
viewport = Viewport.new(0, 0, 640, 480)
viewport.z = 1000
sprite_A = Sprite.new(viewport)
sprite_A.z = 50
sprite_A.bitmap = RPG::Cache.character('001-Fighter01', 0)

sprite_B = Sprite.new
sprite_B.bitmap = RPG::Cache.character('002-Fighter02', 0)
sprite_B.z = 100


Oh wait, nope, that's not it, that makes the first sprite, with a lower Z, higher than the second sprite, simply because it's viewport was higher. But that didn't work earlier when we tried it with this snippet:
Spoiler: ShowHide
viewport = Viewport.new(0, 0, 640, 480)
viewport.z = 100
sprite_A = Sprite.new(viewport)
sprite_A.z = 100
sprite_A.bitmap = RPG::Cache.character('001-Fighter01', 0)

sprite_B = Sprite.new
sprite_B.bitmap = RPG::Cache.character('002-Fighter02', 0)
sprite_B.z = 100


So what the fuck? Does it simply take the higher of the two between the a sprite's viewport's Z, and it's own Z, then use that?  Or is there some magical, undocumented Z value added when a sprite does not have a viewport?

I don't know, maybe I am just getting burned out and need to take a break so I can step back and see this more clearly, but this has been my issue for the all of yesterday and today, and I have not gotten anywhere on it.
I am done scripting for RMXP. I will likely not offer support for even my own scripts anymore, but feel free to ask on the forum, there are plenty of other talented scripters that can help you.

Soulshaker3

As far as I know in c# when a variable is declared but not assigned it will have the default value in an integer his value is 0 so the viewport Z can't be magicly assigned. Other than that i don't know if this interests you but the way i do in my project in xna is that i only use the layerdephth and spritesort mode when i'm in the map any other screen(scene) it just gets drawn by call order with Deferred spritesort it can't possible be doable in your project because most of the scripts use the Z values and even the default ones so yeah i don't really know what's going on there
Hellow?

Blizzard

Imagine it like this: A viewport is like a plane or "meta-sprite" onto which stuff is rendered. Then the content of the viewport itself is rendered. That's all there is. So everything within a viewport is completely independent from everything on the outside. Which is why you probably won't be able to use the depth buffer properly to get the same effect. At least not without some sort of render target where you render all sprites within a viewport. This is also the reason why in RMXP you can specify the viewport for a sprite only upon creation and can't change it afterwards.
Check out Daygames and our games:

King of Booze 2      King of Booze: Never Ever
Drinking Game for Android      Never have I ever for Android
Drinking Game for iOS      Never have I ever for iOS


Quote from: winkioI do not speak to bricks, either as individuals or in wall form.

Quote from: Barney StinsonWhen I get sad, I stop being sad and be awesome instead. True story.

G_G

Luckily in XNA, you can render to a texture, I'm not sure how you're already doing it Zer0, but if you create a texture for every viewport then order those by their respective z values.

Soulshaker3

@blizz so what you're saying that a viewport is kind of like a render target in XNA everything that's in that viewport gets drawn to there and then the viewport itself gets drawn at it's Z value?
Hellow?

ForeverZer0

Quote from: soulshaker3 on August 13, 2014, 10:32:27 am
@blizz so what you're saying that a viewport is kind of like a render target in XNA everything that's in that viewport gets drawn to there and then the viewport itself gets drawn at it's Z value?


I am thinking so, but I going to have make some heavy changes as to how things are rendered. As of right now, I have managed to do everything within one batch. I created an abstract Renderable class that is the parent of Sprite, Plane, Window, and Tilemap. In the constructor, it simple adds the object to a global list of renderable objects, and removes them from this list when they are disposed. Each of these need to override a "Draw" method, so each time the screen is refreshed, it simply has to begin the spritebatch, with sorting set front to back, then iterate through each object in the list and invoke it's "Draw" method.

Until now, it has worked quite well, but if I am experiencing a problem as the screenshot above suggests, then there is an error in my implementation and I need to change it. My only end goal more than anything is to create a drop-in replacement of RMXP, where no scripts or anything needs edited. You can simply place the .exe in the game directory and start playing.

So, will something like this work:


  • Sort objects by their viewport

  • Render each object onto a RenderTarget that represents this viewport (not the screen), ordering sprite's by their Z, obviously.

  • Render each of these "viewports" onto the screen in their respective order, determined by the Z value.



Does that sound like I am understanding here?
If so, it will actually improve some ways, such as the Plane class, which is a bit hackish at the moment. If it got its own batch, I could simply use a LinearWrap when drawing it.
I am done scripting for RMXP. I will likely not offer support for even my own scripts anymore, but feel free to ask on the forum, there are plenty of other talented scripters that can help you.

Blizzard

I did almost exactly the same thing in ARC. The viewport's "draw" method actually just renders its own internal sprite batch onto a texture and then that texture is rendered. I also had to make a Renderable superclass and redesign the viewport class to have its own internal sprite batch.
Check out Daygames and our games:

King of Booze 2      King of Booze: Never Ever
Drinking Game for Android      Never have I ever for Android
Drinking Game for iOS      Never have I ever for iOS


Quote from: winkioI do not speak to bricks, either as individuals or in wall form.

Quote from: Barney StinsonWhen I get sad, I stop being sad and be awesome instead. True story.

ForeverZer0

Quote from: Blizzard on August 13, 2014, 11:11:45 am
I did almost exactly the same thing in ARC. The viewport's "draw" method actually just renders its own internal sprite batch onto a texture and then that texture is rendered.


Awesome, I already begin making the changes. Thank you much for the insight.
I am done scripting for RMXP. I will likely not offer support for even my own scripts anymore, but feel free to ask on the forum, there are plenty of other talented scripters that can help you.

Blizzard

Just remember that you might have to make POT textures as some PC hardware doesn't support NPOT textures. Best you check the the HW caps for that.
Check out Daygames and our games:

King of Booze 2      King of Booze: Never Ever
Drinking Game for Android      Never have I ever for Android
Drinking Game for iOS      Never have I ever for iOS


Quote from: winkioI do not speak to bricks, either as individuals or in wall form.

Quote from: Barney StinsonWhen I get sad, I stop being sad and be awesome instead. True story.

ForeverZer0

True, I doubt that would typically be two much of issue in RMXP, but Murphy's law applies, so I will account for it.
I am done scripting for RMXP. I will likely not offer support for even my own scripts anymore, but feel free to ask on the forum, there are plenty of other talented scripters that can help you.

Blizzard

Well, RMXP uses Bitmap instances which are basically data in RAM so they can be any size. I remember some people reporting odd white textures in ARC and that's the only thing I could think of that could cause this.
Check out Daygames and our games:

King of Booze 2      King of Booze: Never Ever
Drinking Game for Android      Never have I ever for Android
Drinking Game for iOS      Never have I ever for iOS


Quote from: winkioI do not speak to bricks, either as individuals or in wall form.

Quote from: Barney StinsonWhen I get sad, I stop being sad and be awesome instead. True story.

ForeverZer0

Just thought of something. How did you handle a Tilemap, with multiple layers, or do I need a separate RenderTarget for each priority level?
I am done scripting for RMXP. I will likely not offer support for even my own scripts anymore, but feel free to ask on the forum, there are plenty of other talented scripters that can help you.

Soulshaker3

I dont' really think a different rendertarget would need to be used for each drawable object you have. Just make a global render target for the whole scene where each object in the scene has their own Z(the layer depth) and then render them by the Z to the rendertarget. Here's what i do i open the batch in the main class and in that batch i draw the current scene updating/drawing after that i close it and open another one where i draw the render target. Instead of having a viewport for each object just make a global one. With that the Z value should not be a problem and if the window's Z is bigger it will stay on top if you do like that. That's the way i'm doing things that may or may nto work for you
Hellow?

Blizzard

@F0: I use light-weight sprites (32x32 px sized tiles, 20x15 for a resolution of 640x480, 3 layers of them) which are simply put in the same sprite batch since tilemap sprites do have their own Z values. There's no other good way to do this since the Z value depends on the Y value and even sprites in the same row can have different Z values depending on the tile's priority.
Check out Daygames and our games:

King of Booze 2      King of Booze: Never Ever
Drinking Game for Android      Never have I ever for Android
Drinking Game for iOS      Never have I ever for iOS


Quote from: winkioI do not speak to bricks, either as individuals or in wall form.

Quote from: Barney StinsonWhen I get sad, I stop being sad and be awesome instead. True story.

KK20

Which is exactly what Custom Resolution v0.96/7 does.
Except RM's sprites are terrible and lag everything.

Other Projects
RPG Maker XP Ace  Upgrade RMXP to RMVXA performance!
XPA Tilemap  Tilemap rewrite with many features, including custom resolution!

Nintendo Switch Friend Code: 8310-1917-5318
Discord: KK20 Tyler#8901

Join the CP Discord Server!

ForeverZer0

I might try a slightly different method, see how it works out.
Create a RenderTarget for each priority, and have a two-dimensional array of textures that represents what needs drawn on it. basically an equivalent of the @map_data, but with textures instead of integers.

I was also thinking of a way not to redraw if there is not any changes in the origin or updates to the autotiles. Just something I am brainstorming about...


EDIT:
Actually, don't know how I would ever make that work. Although priority does play a role in a tile's Z value, it's not the end result, so simply having an array for each priority isn't going to work.

EDIT 2:
Looks like using a sprite for each tile is the only way to make this work and maintain complete compatibility with RMXP. Creating a new render target for every possible Z value, which is absurd, is the only other way to properly reproduce how RMXP draws everything.

This sucks, though. I had the tilemap working perfectly, without the slightest problem, and then this stupid battle scene showed me the way I was doing was not compatible, so now I need to change how everything is drawn... :(
I am done scripting for RMXP. I will likely not offer support for even my own scripts anymore, but feel free to ask on the forum, there are plenty of other talented scripters that can help you.

Blizzard

Check out Daygames and our games:

King of Booze 2      King of Booze: Never Ever
Drinking Game for Android      Never have I ever for Android
Drinking Game for iOS      Never have I ever for iOS


Quote from: winkioI do not speak to bricks, either as individuals or in wall form.

Quote from: Barney StinsonWhen I get sad, I stop being sad and be awesome instead. True story.

KK20

Quote from: Blizzard on August 13, 2014, 02:21:13 pm
Both Y and priority affect the z value. It won't work.

fixed?
Quote from: ForeverZer0 on August 13, 2014, 02:03:08 pm
Creating a new render target for every possible Z value, which is absurd, is the only other way to properly reproduce how RMXP draws everything.

By this, do you mean having multiple sprites of SCREEN_WIDTH x 160 size (32 x 5-levels-of-priority) and one ground layer sprite SCREEN_WIDTH x SCREEN_HEIGHT?

Other Projects
RPG Maker XP Ace  Upgrade RMXP to RMVXA performance!
XPA Tilemap  Tilemap rewrite with many features, including custom resolution!

Nintendo Switch Friend Code: 8310-1917-5318
Discord: KK20 Tyler#8901

Join the CP Discord Server!

ForeverZer0

I had the idea out there, but it's not going to work.
Going to likely go with the sprite for each tile idea.
I am done scripting for RMXP. I will likely not offer support for even my own scripts anymore, but feel free to ask on the forum, there are plenty of other talented scripters that can help you.

winkio

Quote from: ForeverZer0 on August 13, 2014, 09:40:43 am
I assumed this was relatively simple, but as it turned out, its not. Now I ask that before you reply, I fully understand what is says in the manual, and I am not some noob who needs the basics explained to me, I am referring to rendering the textures on the device. I am also fully aware that the more recent object gets drawn first if they are equal.

I am running into a few minor issues with Z coordinates for this XNA/IronRuby engine I am making. The only place I really see this problem is with the default battle system. Here is what it appears as:

Spoiler: ShowHide


As you can see, everything appears fine except for the enemy being placed above the @actor_command_window.

I have tried a couple different ways of factoring sprite.z and viewport.z, by simply adding them together as one way. I was incorrect in this assumption, this is the way that I have been calculating, but have run into problems here, as the screenshot demonstrates. The enemy sprites have a Z of 304 on their own viewport that has a Z of 0. The @actor_command_window has a Z of 100 (inherited from Window_Base) without a viewport, so 0 would be added to it to get it's final depth. That makes a final tally of the enemy sprites having a Z of 304, and the @actor_command_window having a Z of 100. This is the reason the enemy sprites are on top, so simply adding them is not the correct answer.


I assumed next that drawing was ordered by the viewports, and the sprite's z only pertained to that particular viewport. For example, if there are two sprites, sprite A with z of 10, and sprite B with z of 0, but sprite B's viewport.z was higher than sprite A's viewport.z, sprite B would still appear on top. I was incorrect in this assumption as well, which can be demonstrated with this little snippet in RMXP.
Spoiler: ShowHide
viewport = Viewport.new(0, 0, 640, 480)
viewport.z = 50
sprite_A = Sprite.new(viewport)
sprite_A.z = 0
sprite_A.bitmap = RPG::Cache.character('001-Fighter01', 0)

sprite_B = Sprite.new
sprite_B.bitmap = RPG::Cache.character('002-Fighter02', 0)
sprite_B.z = 100

If that logic was correct, the second sprite should be under the first, since it does not have a viewport, and the first sprite's viewport has a Z of 50.

So I began testing how they relate within RMXP, and I am just getting more confused.
Spoiler: ShowHide
viewport = Viewport.new(0, 0, 640, 480)
viewport.z = 50
sprite_A = Sprite.new(viewport)
sprite_A.z = 100
sprite_A.bitmap = RPG::Cache.character('001-Fighter01', 0)

sprite_B = Sprite.new
sprite_B.bitmap = RPG::Cache.character('002-Fighter02', 0)
sprite_B.z = 100

No matter which order the sprites are created in, this causes the second sprite to always be on top, even though it does not have a viewport,  and the first one does with a Z of 50, and both sprite's have the same Z value.

Now, lets change the first sprites viewport to 100, and change nothing else.
Spoiler: ShowHide
viewport = Viewport.new(0, 0, 640, 480)
viewport.z = 100
sprite_A = Sprite.new(viewport)
sprite_A.z = 100
sprite_A.bitmap = RPG::Cache.character('001-Fighter01', 0)

sprite_B = Sprite.new
sprite_B.bitmap = RPG::Cache.character('002-Fighter02', 0)
sprite_B.z = 100

The second sprite appears on top, but only because it was created more recently, as you can test by changing the creation order. This leads me to believe that the viewport's Z doesn't do a damn thing, since it appears it wasn't even factored in to the drawing, and only the order the sprites were created in.

So let's test this theory, I will increase the viewport's Z very high, and lower the sprite's Z.
Spoiler: ShowHide
viewport = Viewport.new(0, 0, 640, 480)
viewport.z = 1000
sprite_A = Sprite.new(viewport)
sprite_A.z = 50
sprite_A.bitmap = RPG::Cache.character('001-Fighter01', 0)

sprite_B = Sprite.new
sprite_B.bitmap = RPG::Cache.character('002-Fighter02', 0)
sprite_B.z = 100


Oh wait, nope, that's not it, that makes the first sprite, with a lower Z, higher than the second sprite, simply because it's viewport was higher. But that didn't work earlier when we tried it with this snippet:
Spoiler: ShowHide
viewport = Viewport.new(0, 0, 640, 480)
viewport.z = 100
sprite_A = Sprite.new(viewport)
sprite_A.z = 100
sprite_A.bitmap = RPG::Cache.character('001-Fighter01', 0)

sprite_B = Sprite.new
sprite_B.bitmap = RPG::Cache.character('002-Fighter02', 0)
sprite_B.z = 100


So what the fuck? Does it simply take the higher of the two between the a sprite's viewport's Z, and it's own Z, then use that?  Or is there some magical, undocumented Z value added when a sprite does not have a viewport?

I don't know, maybe I am just getting burned out and need to take a break so I can step back and see this more clearly, but this has been my issue for the all of yesterday and today, and I have not gotten anywhere on it.



I don't know if it has been answered already (I read through the thread and I don't think it has), but this is how I remember it working:

The draw calls from RMXP work as a tree.  A sprite without a viewport is a leaf node, and gets drawn based on it's z value.  A viewport acts as a subtree, with all of it's child sprites being drawn together, regardless of other viewports or sprites without viewports.  I'm pretty sure that's all the explanation you need, but if it's unclear let me know.