News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu
avatar_redbox

Sprites each with their own mask

Started by redbox, 16:22, 02 June 14

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

redbox

Previously I have masked sprites by making PEN 0 transparent and using a pre-calculated 256 byte look-up table to work out the required mask when drawing the sprite.

However, now I have some sprites where the PEN 0 is also part of the sprite.  For example, in this sprite (20x16) the background is PEN 0 (which should be transparent) but so is the girl's hair (which should not be transparent):



So I guess here is what the mask would look like (I've used pink to highlight the non-transparent area):



I realise I'll now need to store a mask for each sprite rather than use one look-up table, but how do I generate the masks...?


arnoldemu

For mode 1, I would use 5 colours in this case.
The 5th colour being unique and defining where the transparent areas are.

If you're storing it as 1 byte mask and then 1 byte pixels, you will need to do 2 passes for each 4 pixels.
First to generate the mask identify those transparent pixels then the non-transparent pixels and output pen 3 for transparent and 0 for non-transparent. This effectively gives you 1 bits for the transparent and 0 for the non-transparent allowing you to AND the byte directly to the screen.

Then take another pass, where there are those transparent pixels, output pen 0. Then this can be ORed on.

I would suggest a tool on the pc to do that to give you the extra colours you need.

Otherwise you have to manually make the mask in an art package and you could easily make a mistake with that (taking out pixels you don't want to etc).

My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

SyX

With a little of python magic  in your gfx converter ;)

You can use an extra colour that you pass as parameter or a  colour not in the cpc palette or the alpha channel if you are using transparencies.

I use a pink tone too, but at the inverse you do it, like this:
[attachimg=1]

redbox

Thanks for the explanation.

I was thinking of the "5th colour", but couldn't get my head around how to implement it... but I understand now if I create a mask and use PEN 3 for transparent and PEN 0 for non-transparent, I essentially get an ON/OFF switch for each pixel in the byte according to the mask.  Then I can use AND and OR as per my previous routine.

Will give it a whirl, but don't like the fact I'll have to INC another register which will slow down my sprite routine  ;)

SyX

#4
Quote from: redbox on 22:18, 02 June 14
Will give it a whirl, but don't like the fact I'll have to INC another register which will slow down my sprite routine  ;)
But you should not need, simply mix sprites and the mask. Instead of having two pointers or something like this:
[attachimg=1]
Mix everything like this:
[attachimg=2]

redbox

Quote from: SyX on 22:44, 02 June 14
But you should not need, simply mix sprites and the mask. Instead of having two pointers or something like this:

Yes thanks, was thinking of doing this to save some space - am using 8-bit INCs so will make the boundary alignment better!

redbox

Quote from: arnoldemu on 17:35, 02 June 14
If you're storing it as 1 byte mask and then 1 byte pixels, you will need to do 2 passes for each 4 pixels.
First to generate the mask identify those transparent pixels then the non-transparent pixels and output pen 3 for transparent and 0 for non-transparent. This effectively gives you 1 bits for the transparent and 0 for the non-transparent allowing you to AND the byte directly to the screen.
Then take another pass, where there are those transparent pixels, output pen 0. Then this can be ORed on.

Okay, so I created the sprite and a mask using PEN 3.  Here is the sprite and mask (PEN 0 = dark green, PEN 1 = black, PEN 2 = white, PEN 3 = light green):



I then grabbed this sprite and stored the mask inline (like SyX suggested) so I have one byte sprite followed by one byte mask throughout.

I then used this code to draw a byte to the screen (HL = screen addresss, DE = sprite data):


ld a,(de) : ld c,a : inc de : ld a,(de) : and (hl) : or c : ld (hl),a : inc de : inc l


However, when I draw the sprite and go over another colour, it draws the mask and looks like a 'negative'.  For example:









Any ideas how I've got this wrong...?

arnoldemu

The mask should be the inverse. i.e. the outside around the character. You have it as the inside.

also switch your use of de/hl to make it more simple:


ld a,(de) ;; get screen byte
and (hl) ;; and with mask
inc hl
or (hl) ;; or with pixels
inc hl
ld (de),a ;; write to screen
inc de
My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

redbox

#8
Quote from: arnoldemu on 13:16, 03 June 14
The mask should be the inverse. i.e. the outside around the character. You have it as the inside.

Yay, this worked, thanks!!!

Quote from: arnoldemu on 13:16, 03 June 14
also switch your use of de/hl to make it more simple:

Yes, I thought about this because I'm not using BC for the mask look-up table anymore.

I have to flip the mask and sprite bytes over though for this to work.

Thanks for all the help, much appreciated  :) :) :)


MacDeath

#9
may be out of topic but. as the topic is "masked sprites"...

I remarked that a game like antiriad actually uses some sort of "octet" mask for sprites.

being mode0 the masks works with blocks of 2 pixels (=1 octet).
due to the graphic design of the game and the blocky mode0, no one seems to bother/notice.

I guess this may actually be a brilliant way to simplify the mask and the weight of the mask.
check : the mask to the pixel  :

Mode2 : double the weight for each pixels (as for masked speccy graphics).
Mode1 : add 50% of RAM weight : mask 1bpp + graphics 2bpp
Mode0 : add 1/4 "weight" per pixels : 1bpp mask + 4bpp graphics.

to get the mask in "octet pixels" may be even lighter then... each block of 2 pixels (= 1 octet) only needs 1 bit to be masked.
Also I guess it is faster to work with full octets, as you don't need to cut an octet for each transparency pixels.


On the other hand, Antiriad also uses 2bpp graphics and manages the 16 colours palette into 4x4 colours palettes (Black colour being redundant in each sub palette) as the game was ported graphically from C64, it uses the same sort of "logic" for the display.

needless to say, 1byte mask may not look good in mode1.  ;D

But to use "mode0" mask may be a way to gain some ressources, be it RAM datas or even (not sure) CPU as you don't need to cut the byte into 4x2bits (4 pixels) but into 2x4 bits (2 blocks of 2 pixels)

redbox

Quote from: MacDeath on 16:37, 03 June 14
I remarked that a game like antiriad actually uses some sort of "octet" mask for sprites.

It's an interesting idea, but I think it might come from the graphics having been ported.

If I were using MODE 0, then I'd just use a 256 byte pre-calculated lookup table and always keep PEN 0 transparent (if you needed PEN 0's colour again within the sprite you could just define it amongst the other 15).  Then you don't need to store any masks with the sprites at all, which saves a lot of RAM.

But in MODE 1 it is worth the overhead of storing a mask with each sprite because then essentially you get 5 colours instead of 4 - being 4 actual colours AND a transparent 'colour'.

Imagine only being able to use 3 colours with the above sprite of the girl - you'd lose the colour of her hair etc and it would look a lot worse.

arnoldemu

#11
So..

MacDeath's posting:

You can achieve this as so and without any mask table or mask data needing to be stored:


;; worse case = 12
;; best case = 10
ld a,(de)        ;; [2] read sprite pixels
inc de            ;; [2] update sprite pixel pointer
or a            ;; [1] zero (i.e. fully transparent?)
jp z,no_mask    ;; [3] yes, skip
ld (hl),a        ;; [2] no, write byte to screen
no_mask:
inc hl            ;; [2] update screen pos


so worst case is 12 NOPs for each byte (one or more opaque pixels), best case is 10 NOPs for each byte (fully transparent).

Compare this to the normal method (using lookup table and opaque or transparent pixels):


;; 14 NOPs always
ld a,(de)
ld c,a
ld a,(bc)
and (hl)
or c
ld (hl),a
inc hl
inc de


EDIT: The MacDeath way is quicker.
My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

MacDeath

#12
QuoteIt's an interesting idea, but I think it might come from the graphics having been ported.
I looked at the C64 version...

C64 : Hard sprites in "wide pixels".
there is exactly 3 colours + transparency...

no big blocky "2xmode0 pixels" mask, normal "mode0 pixel" mask.


CPC : the sprites actually use 4 colours,.
You can see black used when the sprites pass over coloured backgrounds, not seen a lot because the back-background is plain black.

So this means the game would generate the overlayed sprites "octets by octets" (byte by byte).

But not sure it may not be a by-product of the conversion from 2bpp datas into 4bpp graphics on screen.


Still I believe (despite I can't code for sh***t) that there may be a way to produce sprites with "1byte masked" effect in a way it would be fast.
I mean, octets/bytes are fully addressables, this can help I guess..

Quoteso worst case is 12 NOPs for each byte (one or more opaque pixels), best case is 10 NOPs for each byte (fully transparent).

Compare this to the normal method (using lookup table and opaque or transparent pixels):

;; 14 NOPs always
ld a,(de)
ld c,a
ld a,(bc)
and (hl)
or c
ld (hl),a
inc hl
inc de
So ?
Sorry I fail hard at coding...lol...

is this "better" to mask at bytes or not ?
if so, may be good for mode0 games with big sprites ?

arnoldemu

The "1byte masked" is the fastest way out of the "standard" way and the way that redbox has been asking about.

My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

MacDeath

#14
victory !!!
:D

Now let's get this into a freaking huge massive Mode0 action game !!! :laugh:


QuoteBut in MODE 1 it is worth the overhead of storing a mask with each sprite because then essentially you get 5 colours instead of 4 - being 4 actual colours AND a transparent 'colour'.

Imagine only being able to use 3 colours with the above sprite of the girl - you'd lose the colour of her hair etc and it would look a lot worse.
yeah, the "3 inks+transparency" in mode1 was the classic way to speccyport like if it were profitable...  :laugh:

exemple : strider.

sometimes they would not even use the whole possibility (= 3 colours).

check "Superwonderboy II in monstruous 1bpp land" per exemple. >:(


on the other hand some speccy encounters of the worst kind would even keep the 1bpp graphics + 1bpp mask as if no gate array happened...
Black Tiger per example, Pacmania, and so on...

BTW those limitatins were due to having to get some Speccy48 games on a CPC464...
slow loading + limited RAM = fail.

now that everybody has access to X-MEM's "576K"... no need to not use fully the 4 colours in mode1 I guess.

arnoldemu

In fact:


;; worse case = 11
;; best case = 10
ld a,(de)        ;; [2] read sprite pixels
inc de            ;; [2] update sprite pixel pointer
or a            ;; [1] zero (i.e. fully transparent?)
jr z,no_mask    ;; [3/2] yes, skip
ld (hl),a        ;; [2] no, write byte to screen
no_mask:
inc hl            ;; [2] update screen pos


if we change jp for jr. Worst case is 3 cycles for the JR. Best case is 2 cycles for JR.
of course we can use inc e or inc l if the screen has been setup correctly to furthur make it faster.
My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

Carnivius

Quote from: SyX on 22:44, 02 June 14
But you should not need, simply mix sprites and the mask. Instead of having two pointers or something like this:
[attachimg=1]
Mix everything like this:
[attachimg=2]

Hello.   I'm curious about what's happening here with the split sprites.   What is this for?   And can you explain it in a way that a stupid person (such as myself) would understand? :)
Favorite CPC games: Count Duckula 3, Oh Mummy Returns, RoboCop Resurrection, Tankbusters Afterlife

SyX

#17
Quote from: Carnivac on 19:28, 03 June 14
Hello.   I'm curious about what's happening here with the split sprites.   What is this for?   And can you explain it in a way that a stupid person (such as myself) would understand? :)
I'll try, Carnivac.

We have our pretty background:
[attachimg=1]

And then we want to print our lovely sprite:
[attachimg=5]

If we are not using masks (the black and white silhoutte in the previous pictures or the pink around this one), the result would be:
[attachimg=2]

Where that pink border around our sprite will be black and cover our background. Those are the games where you can see a box around the sprites.

Because we want to have pixels transparents around our sprite, for it can be really mixed with the background graphics, we need use those masks. The mask is only a map that say if a pixel is solid (our sprite) or transparent (the pixel will show the background).

For you can see more clear, i'm going to make the process of printing a sprite with mask in two steps.

First we would print the mask over the background, the shadow/silhoutte where the sprite will go:
[attachimg=3]

And then we copy the real sprite image over the mask:
[attachimg=4]

And basically, that is all.

With respect to the picture with the sprites and masks mixed, it's more a programming thing. We were speaking of storing the mask and sprites together for faster printing of those. Instead of doing the process in two steps, we can do in one (read one byte of the background, read one byte of the mask, mix mask and background, read one byte of the sprite and mixed it with the previous mask + background).

redbox

Quote from: MacDeath on 17:54, 03 June 14
victory !!!

That's a nice find MacDeath, especially as Arnoldemu has proven it to be so quick  :)

However, don't think I'll use it for my MODE 1 project as I'm sure you'll appreciate the pixel by pixel transparency instead - and you can't shout at me for using a crappy 3bpp-or-whatever-lame-ass-speccy-derived-crap  ;)

redbox

Quote from: SyX on 20:27, 03 June 14
We have our pretty background:

That's a very pretty background.  Are you working on a MODE 1 project too?

Btw, I ended up storing the mask and sprite bytes forwards and then backwards for each line.  So your redraw code becomes:


    ; line 1

    ld a,(de) : and (hl) : inc hl : or (hl) : inc hl : ld (de),a : inc e
    ld a,(de) : and (hl) : inc hl : or (hl) : inc hl : ld (de),a : inc e
    ld a,(de) : and (hl) : inc hl : or (hl) : inc hl : ld (de),a : inc e
    ld a,(de) : and (hl) : inc hl : or (hl) : inc hl : ld (de),a : inc e
    ld a,(de) : and (hl) : inc hl : or (hl) : inc hl : ld (de),a

    set     3,d

    ; line 2
    ld a,(de) : and (hl) : inc hl : or (hl) : inc hl : ld (de),a : dec e
    ld a,(de) : and (hl) : inc hl : or (hl) : inc hl : ld (de),a : dec e
    ld a,(de) : and (hl) : inc hl : or (hl) : inc hl : ld (de),a : dec e
    ld a,(de) : and (hl) : inc hl : or (hl) : inc hl : ld (de),a : dec e
    ld a,(de) : and (hl) : inc hl : or (hl) : inc hl : ld (de),a

    ld a,d : add a,8 : ld d,a : and &38 : jr nz,Line3
    ld a,e : add a,&40 : ld e,a : ld a,d : adc a,&c0 : ld d,a

    ; line 3
   ... repeat ...



SyX

Quote from: redbox on 20:44, 03 June 14
That's a very pretty background.  Are you working on a MODE 1 project too?
Yes, although there is a mode 0 idea in the workbench, too.

Only waiting to the brazillian postal service stops of moving the TotO expansions (minibooster and the rest) around the country, for being able of making tests. And of course, finish the backlog of utilities. Last one, i have patched Bonniedos for the Pulkomandy CF expansion (and i hope i can finish the FAT32 filesystem this year).

After that, i can return to make games... AT LAST!!! :D

Quote from: redbox on 20:44, 03 June 14
Btw, I ended up storing the mask and sprite bytes forwards and then backwards for each line.
Perfect, that was the next step for optimizing your print routine :)

redbox

#21
Quote from: SyX on 21:23, 03 June 14
After that, i can return to make games... AT LAST!!! :D

Can't wait...!  :)




AMSDOS

Last year I made this Text Based BASIC example that uses the Transparent command to print an object over the Background, though I found out it only works for Text Based things  :D I thought about writing one (using conventional TAG/TAGOFF characters), though I was wondering if something solid was produced, could I get away with only have to worry about the 4 sides of the object, or is it easier to remove the graphic (between frames) and draw the whole background in along with the character?
* Using the old Amstrad Languages :D   * with the Firmware :P
* I also like to problem solve code in BASIC :)   * And type-in Type-Ins! :D

Home Computing Weekly Programs
Popular Computing Weekly Programs
Your Computer Programs
Updated Other Program Links on Profile Page (Update April 16/15 phew!)
Programs for Turbo Pascal 3

redbox

Quote from: AMSDOS on 10:50, 04 June 14
Last year I made this Text Based BASIC example that uses the Transparent command to print an object over the Background, though I found out it only works for Text Based things  :D I thought about writing one (using conventional TAG/TAGOFF characters), though I was wondering if something solid was produced, could I get away with only have to worry about the 4 sides of the object, or is it easier to remove the graphic (between frames) and draw the whole background in along with the character?

You only need to worry about transparency when plotting the part of the 'sprite' that needs it really.  The rest of it can be solid colour(s).

But when you need to restore the background, there's two approaches really...

1) Store background to buffer and draw it back the next frame (over the sprite)

2) Use a tilemap, and mark the affected tiles as 'dirty' if the sprite draws over them, then draw them back the next frame

I've chose the second option because it's better  ;)


EgoTrip

That girl sprite looks eerily familiar.

Powered by SMFPacks Menu Editor Mod