Jump to content
IGNORED

Sprite Issues


OVERRiDE

Recommended Posts

Hi all,

 

Thanks again for giving us JagStudio to empower ourselves to start developing on the Jag.

 

I have been playing with JagStudio this week, and I am seeing a few issues with the Sprite handling that I believe are a mix of Bugs, Hardware Nuances, Feature Requests, and finally a Lack of Understanding.  But putting this out there hoping to get help in finding the solution and to correct my understanding.

 

Scaling

For what I am wanting to do, scaling will be very important.  The documentation indicates when a sprite is scaled, the scale factor of 32 will produce a 1:1 result, effectively producing an unscaled image.  Now I have tested displaying a 4x4 grid of 32x32 pixel sprites, 16bpp, unscaled, rendered on ton top of another 4x4 grid of 32x32 pixel sprites, 16bpp, this grid is scaled with the factor of 32.  The expected result is these two grids will be seamless, and of the exact same size.


However the actual result is the scaled grid is not aligned with the uncsaled grid; it seems to be offset by 1 pixel on the y axis and is an extra pixel wide on the x axis.  It looks like this:

 

288102759_Screenshot2022-03-02151738.thumb.png.132af23aa113fba86a32fc319b122e13.png

 

And what is the unit system being used on the scale factor?  It seems to really behave more like a "display size" attribute from what I have tested;  using a 32x32 16bpp sprite, scale factor set to 48 for x and y, results in a sprite which aligns with an actual 48x48 16bpp sprite unscaled.  And similarly, using the same base sprite but setting scale factor to 63 displays a sprite which is aligned with a 64x64 pixel sprite unscaled ( scale factor of 64 seems to reintroduce the off by 1 error I mentioned above, where 63 seems a perfect 64 pixel display ).   So is this really a scaling factor, as in a multiplier?  If so, how do we calculate this factor from a floating point value?

 

Culling

There seems to be a Bug with the edge_kill functionality, which seems to not be taking the scaling factor into account.

 

In this example, a scaled sprite is being culled before it reaches the near x axis of the screen.  The expected result is the sprite will be visible until the left most edge of the sprite is offscreen, however it is being culled way before that.

 

sprite_kill_a.thumb.png.ed6d8416f923e74406df075bdb749ce5.png

 

sprite_kill_b.thumb.png.7f26a63285f75be2e6ea6b421a863332.png

 

My biggest issue with edge_kill is that it does not reactivate the sprite once it is back onscreen.  I believe what I really want is edge_ignore, but in this sample level I am getting very strange results using edge_ignore; this glitch does not happen with any of the other culling modes:

edge_kill:

 

edge_kill.thumb.png.3af2d7f5a43d57f34bf601be94668b71.png

 

edge_ignore:

edge_ignore.thumb.png.6d098eac7e3facaff638e08473ff68c7.png

 

Does edge ignore actually "Do Nothing" in the Raptor API? I can manage sprite culling myself, but it seems like the API is overriding my active flags.

 

Scrolling

The scrolling seems to accelerate on the y axis, specifically near the top of the screen.  Here I am shifting every sprite by 2 pixels each frame on button press, however near the top of the screen sprites move further away from eachother.  I have tried using the rapSpriteShift function, but also wrote my own function to do this, and they actually both produce the same resutls:

Notice the uniform spacing between the blocks on the left column:

 

sprite_scroll_a.thumb.png.c0ba427465cec1193a97bf10d0f7ad50.png

 

Now notice the top most block on the left column is no longer uniformly spaced with the other blocks:

sprite_scroll_b.thumb.png.426f309ed1efbd007b7fc17ba4a39086.png

 

Transparency

Is it possible to control what color is used for transparency, or does it have to be black?  For the assets I am working with, white is used for transparency, and could save some pre-processing if we can control this through the API

 

Rotation

Does this API expose the rotation features of the Blitter?  If so, how do we set this up?

 

I think that is it for now, thanks in advance for any assistance.  I can upload the project files if anybody is willing to take a closer look.

 

Thanks!

Link to comment
Share on other sites

Hi

 

Lets break this down.

 

Scaliing:

Bugs in the hardware shift the image/crop the image, even at 1:1  (Usually the top line of a sprite is shifted, but the left can also be shifted)

Scaling is calculated as a xxx.yyyyy  where xxx is the integer scale value and yyyyy are the fractional, so 00100000 is 1:1 (complete with hardware bugs)

See the h/w manual for more details on the scaling bugs.

 

Culling:

RAPTOR does not take into account any scaling values when using culling. It will cull based on the original sprite size.

edge_ignore does just that, it ignores the edge check. Going out of bounds will, as per the hardware, produce odd results ile wrapping, or vertically repeating bitmaps.

edge_kill will not reactivate the object, it has been 'killed' (ie, set to inactive) - you will have to set its sprite_active flag manually if using this.

 

Scrolling: (Note, you are not actually describing scrolling here)

Scaled sprites will be culled at the top border accoring to their original height, so what you are seeing is normal.

 

Transparency:

Index 0 in bitmap mode is always the trans colour, regardless of the palette used.

In 16 bit modes true black $000 is transparent - there is no way in the h/w to change this.

 

Rotation:

All the blitter functions are available via the blitter calls as per the latest update.

There are currently no commands built in to handle rotation, however you should be able to generate a blitter command to do this using the calls.

 

Hope that helps!

CJ

 

  • Like 4
Link to comment
Share on other sites

@CyranoJ thank you again for your quick reply!  That does help, I've worked through some of those issues but now see another problem.

 

What I am seeing, is that if I have more that 8 sprites in a row (line) of size 32x32 at 16bpp, there is a major artifact produced on real hardware that gets increasingly worse as more sprites are displayed in that line.  Hard to explain in words so I have taken some screens of what this looks like.

 

Up To 8 Sprites in each row (horizontal line) on-screen: Looks good!

 

20220305_190806.thumb.jpg.14dcf7d87c03b1938516de51347bf423.jpg

 

Add more sprites each line and we see artifcating introduced on each row with more than 8 sprites

20220305_190820.thumb.jpg.45df81beee52c8bde12b0886070ee0b7.jpg

 

Add even more sprites each line and we see much worse artifacting

 

20220305_190837.thumb.jpg.5a854c83d4c13de78e73bc6eb9af37fa.jpg

 

20220305_190855.thumb.jpg.7b03ae71b8f0b57a13aa34a290a546d6.jpg

 

To the point where the image is unrecognizable

20220305_190655.thumb.jpg.748ecfa841a88674ae9bda8a789f420b.jpg

 

 

20220305_191812.thumb.jpg.ca86451740507266fa7e93f66256e7a8.jpg

 

However these issues are not present when running under emulation, everything is surprisingly fine under VJag
 

1568483616_Screenshot2022-03-05200148.thumb.png.05f7dc0414690f6a2b2ec95652a8db33.png

 

1603348886_Screenshot2022-03-05202934.thumb.png.8a2a17c16387de5a784d5ee3991e3bee.png

 

 

596197193_Screenshot2022-03-05202356.thumb.png.e9470c74ab873acc3e97030d7295dd62.png

 

Link to comment
Share on other sites

Your list is too long, thats the ObjectProcessor taking longer than a scanline to process the object list chain.

 

You really can't build a map out of tiles in a straight list without branches (or use the tilemap features of the API)

Link to comment
Share on other sites

I have had this screen tearing too.  It might sound obvious, and it is documented, but scaled sprites take twice as long to process compared to unscaled sprites.  If you don't need the scaling of the OP you could use pre-scaled sprites instead to get more out of the OP.

Link to comment
Share on other sites

Thank you all for your replies!

 

I knew the Jag was going to be the most limited system I have ever develop on, but I did not think 8 scaled sprites in a row was going to be pushing the hardware boundaries lol.

 

"You want Scaled Sprites!  Heck yeah the 64-bit Atari Jaguar can do that!!!! Oh wait, you want more than 8 on a row?.... Um, no, that is impossible!"

 

@CyranoJ I have made solid progress on my map converter already, I would really rather avoid having to convert everything to a TileMap (for more reason than 1; Precision, Animations, Specific Collision Handling, Requires Parallel Structure In-engine, etc. ). 

 

@42bs can you elaborate on your proposal?  The "background" is already a single sprite ( a large sprite lol ).  Its the foreground layers that are giving problems, so I am not sure I understand your feedback.

 

As @xzpecter mentioned, in this case it seems the bottleneck is in the scaling function; I can display as many 32x32, 24x24, or 16x16 Sprites as can fit on-screen ( and more off-screen, culled ) if we disable Scaling, and no artifacts are present on real hardware!

 

However, this is only feasible if arbitrary resolution sprites are supported, and I seem to be hitting issues for non-divisible-by-8 sprite resolutions.  For example, 16x16, 24x24,32x32 pixel sprites have no problems.  But if I try a 22x22 pixel sprite, it looks like garbage data.  Are arbitrary resolution sprites supported?

 

For reference, here is a snippet of the 22x22 sprite (NOT working) and the 24x24 sprite (working)

; SPRITE_22
    dc.l    1                                ; (REPEAT COUNTER)                 ; Create this many objects of this type (or 1 for a single object)
    dc.l    is_active                        ; sprite_active                    ; sprite active flag
    dc.w    22*0,0                            ; sprite_x                        ; 16.16 x value to position at
    dc.w    16,0                            ; sprite_y                        ; 16.16 y value to position at
    dc.w    0,0                                ; sprite_xadd                    ; 16.16 x addition for sprite movement
    dc.w    0,0                                ; sprite_yadd                    ; 16.16 y addition for sprite movement
    dc.l    22                                ; sprite_width                    ; width of sprite (in pixels)
    dc.l    22                                ; sprite_height                    ; height of sprite (in pixels)
    dc.l    is_normal                        ; sprite_flip                    ; flag for mirroring data left<>right
    dc.l    0                                ; sprite_coffx                    ; x offset from center for collision box center
    dc.l    0                                ; sprite_coffy                    ; y offset from center for collision box center    
    dc.l    22/2                            ; sprite_hbox                    ; width of collision box
    dc.l    22/2                                ; sprite_vbox                    ; height of collision box
    dc.l    SPRITE_022                        ; sprite_gfxbase                ; start of bitmap data
    dc.l    16                                ; (BIT DEPTH)                    ; bitmap depth (1/2/4/8/16/24)
    dc.l    is_RGB                            ; (CRY/RGB)                        ; bitmap GFX type
    dc.l    is_trans                        ; (TRANSPARENCY)                ; bitmap TRANS flag
    dc.l    22*22*2                            ; sprite_framesz                ; size per frame in bytes of sprite data
    dc.l    22*2                            ; sprite_bytewid                ; width in bytes of one line of sprite data
    dc.l    0                                ; sprite_animspd                ; frame delay between animation changes
    dc.l    0                                ; sprite_maxframe                ; number of frames in animation chain
    dc.l    ani_rept                        ; sprite_animloop                ; repeat or play once
    dc.l    edge_ignore                        ; sprite_wrap                    ; wrap on screen exit, or remove
    dc.l    spr_inf                            ; sprite_timer                    ; frames sprite is active for (or spr_inf)
    dc.l    spr_linear                        ; sprite_track                    ; use 16.16 xadd/yadd or point to 16.16 x/y table
    dc.l    0                                ; sprite_tracktop                ; pointer to loop point in track table (if used)
    dc.l    spr_unscale                        ; sprite_scaled                    ; flag for scaleable object
    dc.l    32                                ; sprite_scale_x                ; x scale factor (if scaled)
    dc.l    32                                ; sprite_scale_y                ; y scale factor (if scaled)
    dc.l    -1                                ; sprite_was_hit                ; initially flagged as not hit
    dc.l    1                                ; sprite_CLUT                    ; no_CLUT (8/16/24 bit) or CLUT (1/2/4 bit)
    dc.l    can_hit                            ; sprite_colchk                    ; if sprite can collide with another
    dc.l    cd_keep                            ; sprite_remhit                    ; flag to remove (or keep) on collision
    dc.l    single                            ; sprite_bboxlink                ; single for normal bounding box, else pointer to table
    dc.l    1                                ; sprite_hitpoint                ; Hitpoints before death
    dc.l    2                                ; sprite_damage                    ; Hitpoints deducted from target
    dc.l    22*2                            ; sprite_gwidth                    ; GFX width (of data)


; SPRITE_24
    dc.l    1                                ; (REPEAT COUNTER)                 ; Create this many objects of this type (or 1 for a single object)
    dc.l    is_active                        ; sprite_active                    ; sprite active flag
    dc.w    24*0,0                            ; sprite_x                        ; 16.16 x value to position at
    dc.w    140,0                            ; sprite_y                        ; 16.16 y value to position at
    dc.w    0,0                                ; sprite_xadd                    ; 16.16 x addition for sprite movement
    dc.w    0,0                                ; sprite_yadd                    ; 16.16 y addition for sprite movement
    dc.l    24                                ; sprite_width                    ; width of sprite (in pixels)
    dc.l    24                                ; sprite_height                    ; height of sprite (in pixels)
    dc.l    is_normal                        ; sprite_flip                    ; flag for mirroring data left<>right
    dc.l    0                                ; sprite_coffx                    ; x offset from center for collision box center
    dc.l    0                                ; sprite_coffy                    ; y offset from center for collision box center    
    dc.l    24/2                            ; sprite_hbox                    ; width of collision box
    dc.l    24/2                                ; sprite_vbox                    ; height of collision box
    dc.l    SPRITE_024                        ; sprite_gfxbase                ; start of bitmap data
    dc.l    16                                ; (BIT DEPTH)                    ; bitmap depth (1/2/4/8/16/24)
    dc.l    is_RGB                            ; (CRY/RGB)                        ; bitmap GFX type
    dc.l    is_trans                        ; (TRANSPARENCY)                ; bitmap TRANS flag
    dc.l    24*24*2                            ; sprite_framesz                ; size per frame in bytes of sprite data
    dc.l    24*2                            ; sprite_bytewid                ; width in bytes of one line of sprite data
    dc.l    0                                ; sprite_animspd                ; frame delay between animation changes
    dc.l    0                                ; sprite_maxframe                ; number of frames in animation chain
    dc.l    ani_rept                        ; sprite_animloop                ; repeat or play once
    dc.l    edge_ignore                        ; sprite_wrap                    ; wrap on screen exit, or remove
    dc.l    spr_inf                            ; sprite_timer                    ; frames sprite is active for (or spr_inf)
    dc.l    spr_linear                        ; sprite_track                    ; use 16.16 xadd/yadd or point to 16.16 x/y table
    dc.l    0                                ; sprite_tracktop                ; pointer to loop point in track table (if used)
    dc.l    spr_unscale                        ; sprite_scaled                    ; flag for scaleable object
    dc.l    32                                ; sprite_scale_x                ; x scale factor (if scaled)
    dc.l    32                                ; sprite_scale_y                ; y scale factor (if scaled)
    dc.l    -1                                ; sprite_was_hit                ; initially flagged as not hit
    dc.l    1                                ; sprite_CLUT                    ; no_CLUT (8/16/24 bit) or CLUT (1/2/4 bit)
    dc.l    can_hit                            ; sprite_colchk                    ; if sprite can collide with another
    dc.l    cd_keep                            ; sprite_remhit                    ; flag to remove (or keep) on collision
    dc.l    single                            ; sprite_bboxlink                ; single for normal bounding box, else pointer to table
    dc.l    1                                ; sprite_hitpoint                ; Hitpoints before death
    dc.l    2                                ; sprite_damage                    ; Hitpoints deducted from target
    dc.l    24*2                            ; sprite_gwidth                    ; GFX width (of data)

 

 

 

 

20220306_194105.jpg

20220306_194404.jpg

  • Like 1
Link to comment
Share on other sites

Quote

However, this is only feasible if arbitrary resolution sprites are supported, and I seem to be hitting issues for non-divisible-by-8 sprite resolutions.  For example, 16x16, 24x24,32x32 pixel sprites have no problems.  But if I try a 22x22 pixel sprite, it looks like garbage data.  Are arbitrary resolution sprites supported?

No, the hardware requires aligned widths for the data, you can however set them to transparent and put a 22x22 in a 24x22 box.  Any height is fine.

 

  • Like 4
Link to comment
Share on other sites

The OP is _not_ a sprite engine. It was not designed for this. But rather to display (independent) objects on the screen.

So don't blame it on the OP if a tile based system does no work!

What I meant is to draw all static parts into a single object.

So you end up with the background object, the static object and maybe score and live objects. And the rest may be the moving objects.

Link to comment
Share on other sites

20 minutes ago, 42bs said:

The OP is _not_ a sprite engine. It was not designed for this. But rather to display (independent) objects on the screen.

So don't blame it on the OP if a tile based system does no work!

Have you seen our demo of "Jumping at Shadows' ?

 

The object processor is just fine for this kind of work if used correctly.

 

RAPTOR API and therefore JS work this way intentionally and yes, the object processor is actually a turbo charged sprite engine. That is what it was designed for. 

 

People can of course use the blitter or the op (as the API does) neither is wrong if it works.

  • Like 1
Link to comment
Share on other sites

1 hour ago, CyranoJ said:

Have you seen our demo of "Jumping at Shadows' ?

Just did. Nice! How many objects at the same time?

 

1 hour ago, CyranoJ said:

That is what it was designed for. 

I heard/read it differently. But, hey, if you use the machine as it is designed for, you end up with Cybermorph ;-)

 

  • Haha 2
Link to comment
Share on other sites

159 objects at the moment...  you made me do math, you bad man :)

 

159 is low compared to some other games (likely Super Burnout destroys this), but then they don't have three layers of them covering the entire screen hehe.

 

Check out FaCTs for an insanely long object list!

  • Like 3
Link to comment
Share on other sites

On 3/6/2022 at 10:00 PM, 42bs said:

The OP is _not_ a sprite engine. It was not designed for this. But rather to display (independent) objects on the screen.

So don't blame it on the OP if a tile based system does no work!

What I meant is to draw all static parts into a single object.

So you end up with the background object, the static object and maybe score and live objects. And the rest may be the moving objects.

Umm... yeah... according to the docs the Object Processor is a sprite engine... and then some I guess lol.  In practice what it is not is a sprite scaling engine that is able to handle sprite scaling at a decent throughput.  I'd bet the Sega CD hardware is probably better than this ( I have 0 experience on that hardware, just a guess ). Literally a 8 scaled sprites in the same horizontal line will break the OP.

 

However as @xzpecter offered, disable scaling and the throughput is much, much better.  In my use cases we are talking more than 2x the throughput he referenced.

Screenshot 2022-03-09 191615.png

Link to comment
Share on other sites

@CyranoJ speaking of Jumping At Shadows, this is looking excellent, I will be a customer of yours once this is completed!

 

And thanks again for the detail, good to know the pixel alignment is only strict on the X-axis, we do have some freedom on the Y-axis ( this is important for non-square sprites ).

 

I am still seeing some issues, maybe user error but also maybe bugs with the JagStudio / Raptor API.  Mostly these are related to the handling of "active" sprites.

  • edge_kill - I believe this is a bug; using edge_kill will permanently remove a sprite from display.  Even if I manually set EVERY sprite as R_is_active before each vsync.  Expected result is we are able to see every "active" sprite on-screen.  But the actual result is that even "active" sprites which have been culled in previous frames via edge_kill are no longer visible.
  • vsync - If we submit an empty list ( all sprites are culled / not active ) we still see the display from the most recent frame which was not empty.  Expected result is we see nothing on screen.
  • There seems to be sprite retention of non-active sprites.  Its hard to explain but is similar to the note above; sometimes a sprite which is not-active is still visible on screen

I can provide whatever sources you need to reproduce these cases.  Thanks again for all of your efforts!

Link to comment
Share on other sites

  • edge_kill is working corectly in the API, this might be a JS issue or could be your code - can we see it?
  • vsync - this is also correct, as setlist will set the list and return, but any changes between the last writes to the objects and the previous raptor handler call won't have been processed. There is an update-offscrene-list call to fix this, so you can force a list update before making it visitble.
  • the first object in any list has to be active, if it isn't, odd things will happen.

 

Would need to see the code to help further.

 

Link to comment
Share on other sites

@CyranoJ thanks again for the reply.  Here is a demo which is using edge_kill, but is setting every sprite active every frame.  I would expect every on-screen sprite to be visible, however if a sprite ever gets killed it will never be visible again.  You can reproduce this by using the d-pad to move the world around and see once a sprite is culled, it remains culled even though I am setting it active.

 

Also including edge_ignore.rar which is a patch to apply to the main archive; in this build I am using edge_ignore and culling the sprites outside of Raptor ( overly aggressive to ensure its working as expected, can adjust the min and max values in spritemovements.c to set to full screen bounds ).  In this build we do not get the same error that we see using edge_kill.  So obviously I have a workaround, just reporting this as it does appear to be a bug in JagStudio.

 

As far as the other issues, most likely they are due to having the first object in the list become inactive as you mentioned.  What is the normal way to manage this, just adding a dummy, fully transparent sprite at the start of the list?

2D_Test_1.rar edge_ignore.rar

  • Like 1
Link to comment
Share on other sites

OK, spent a while looking at this, and I believe you have found an undocumented feature bug!

 - workaround - set the x/y co-ordinates again when re-activating the object.

 

Before auto-object culling was introduced to remove objects from the list the engine used to set culled objects offscreen.  It appears it is still doing the co-ordinate change even though the culling is working.

 

Thank you for this! Appreciate the feedback, I will try and correct in an upcoming release.

 

 

  • Like 2
Link to comment
Share on other sites

Okay think I finally have the majority of issues worked out with the rendering, manually handling things that JagStudio has Bugs with, and working with the OP as much as possible by applying scaling and rotation to the Sprites as a part of my level conversion process.  Things are working on real hardware, and is actually much smoother on real hardware compared to emulator.

 

I've attached another build, same demo as before but a step forward with the map conversion now handling scaling and rotation.

 

Next steps: start working on gameplay!

 

579793523_Screenshot2022-03-11192241.thumb.png.9bd29b7f9d9317fe5edc97a317c43079.png

 

312957779_Screenshot2022-03-11191653.thumb.png.771666da3ba1840617f491de9cff1938.png

Screenshot 2022-03-11 192241.png

2D_Test_1_cave1b.rar

  • Like 7
Link to comment
Share on other sites

  • 7 months later...
  • 2 weeks later...
On 10/28/2022 at 11:25 PM, Clint Thompson said:

@OVERRiDE how's the game coming along?

Abandoned lol.

 

If you can't tell, my idea was to write a Mario clone using Secret Maryo Chronicles game assets.  A sort of port of that game, but essentially a rewrite for the Jag.

 

But it turns out the OP really struggled with being used as a sprite engine, and hit massive slowdowns on even the first level of the game towards the later part where the screen is mostly full of sprites.  Ive sampled many levels of the game and the majority of them just have too many sprites on screen at once for the Jag to cope with.

 

For the record, I had my level converter pre-scaling and pre-rotating all of the sprites, and using GPU culling of off-screen sprites (via raptor) so there was no more optimization that could be done for a sprite engine that I can see.

 

Any other path forward would be a tremendous time suck that I cant afford.  My hats off to the masochistic devs here who invest their time trying to push this brutal platform.  I am a software engineer by trade, but it seems you also need to be a hardware engineer to really get anything meaningful out of the Jag.  For me, this was a neat exercise, but found little besides headache working on this system lol.  I've tapped out and have moved on.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...