Skip to content

Commit 1192340

Browse files
committed
feat: add 4BPP/16colors sprites handling
1 parent a39a7a7 commit 1192340

File tree

3 files changed

+190
-6
lines changed

3 files changed

+190
-6
lines changed

docs/programming/sprites.md

+66
Original file line numberDiff line numberDiff line change
@@ -129,10 +129,76 @@ When optimizing its calls, you need to call it at least twice (once for each dou
129129
- enabling sprite,
130130
- changes in sprite chaining.
131131
132+
133+
132134
### Chaining sprites in a single channel
133135
134136
This is currently not supported.
135137
138+
139+
## Handling 16colors/4BPP sprites
140+
141+
16 colors sprites can be now handle in ACE :
142+
143+
Remember you need to have 1 line before your actual sprite in your bitmap and 1 line after your actual sprite in your bitmap.
144+
145+
```c
146+
s_pSprite0Data = bitmapCreate(16, 34, 4, BMF_CLEAR|BMF_INTERLEAVED); // 16x32 2BPP
147+
// Showing all colors as an example. Put your sprite bitmap instead
148+
blitRect(s_pSprite0Data,0, 0, 8, 4, 0);
149+
blitRect(s_pSprite0Data,0, 4, 8, 4, 1);
150+
blitRect(s_pSprite0Data,0, 8, 8, 4, 2);
151+
blitRect(s_pSprite0Data,0, 12, 8, 4, 3);
152+
blitRect(s_pSprite0Data,0, 16, 8, 4, 4);
153+
blitRect(s_pSprite0Data,0, 20, 8, 4, 5);
154+
blitRect(s_pSprite0Data,0, 24, 8, 4, 6);
155+
blitRect(s_pSprite0Data,0, 28, 8, 4, 7);
156+
blitRect(s_pSprite0Data,8, 0, 8, 4, 8);
157+
blitRect(s_pSprite0Data,8, 4, 8, 4, 9);
158+
blitRect(s_pSprite0Data,8, 8, 8, 4, 10);
159+
blitRect(s_pSprite0Data,8, 12, 8, 4, 11);
160+
blitRect(s_pSprite0Data,8, 16,8, 4, 12);
161+
blitRect(s_pSprite0Data,8, 20, 8, 4, 13);
162+
blitRect(s_pSprite0Data,8, 24, 8, 4, 14);
163+
blitRect(s_pSprite0Data,8, 28, 8, 4, 15);
164+
165+
s_pSprite0 = spriteAdd(0, s_pSprite0Data);
166+
```
167+
Because, a 16 colors sprites use 2 channels, it must be used on an even channel (0,2,4,6). The odd channel just after will be taken by the 16 colors sprite so don't use it. (so 1,3,5,7).
168+
169+
You need to use the SetPos functions to set the sprite position :
170+
```c
171+
spriteSetPos(s_pSprite0,100,100);
172+
173+
spriteSetPosY(s_pSprite0,s_pSprite0->wY-2);
174+
175+
spriteSetPosX(s_pSprite0,s_pSprite0->wX-2);
176+
```
177+
178+
`spriteRequestMetadataUpdate` is not needed when you use theses functions.
179+
180+
181+
182+
**Don't change the position by doing : `s_pSprite0->wX=newvalue`**
183+
184+
185+
For 16 colors sprites, `spriteSetBitmap` is not optimized, don't use it (yet) for animation.
186+
187+
## Q&A
188+
189+
### My sprite is showing behind the playfield
190+
191+
After a view load blcon2 is set to 0, you need to set the blcon2 to what you want.
192+
193+
````
194+
#include <ace/utils/custom.h>
195+
[...]
196+
viewLoad(s_pView);
197+
// To insert after the viewload
198+
// Update blcon2 to put sprite in front of http://amigadev.elowar.com/read/ADCD_2.1/Hardware_Manual_guide/node0159.html
199+
g_pCustom->bplcon2=0b00100000;
200+
```
201+
136202
## Managing sprites in a different way
137203

138204
It is very much possible that you will find ACE's sprite manager's abilities insufficient (e.g. you want to manage your sprite pointers or data using copperlist).

include/ace/managers/sprite.h

+28
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ typedef struct tSprite {
3434
UBYTE isEnabled;
3535
UBYTE isHeaderToBeUpdated;
3636
UBYTE isAttached; // Odd Sprites Only.
37+
UBYTE is4PP; // 16-color sprites only.
3738
} tSprite;
3839

3940
/**
@@ -104,6 +105,8 @@ void spriteRemove(tSprite *pSprite);
104105
* @param pBitmap Bitmap to be used for display/control data. The bitmap must be
105106
* in 2BPP interleaved format as well as start and end with an empty line,
106107
* which will not be displayed but used for storing control data.
108+
* 4BPP interleaved format is also supported for 16-color sprites it will take 2 channels, you must use an even channel as main channel.
109+
* In 4BPP mode, it's not optimized for animation.
107110
*/
108111
void spriteSetBitmap(tSprite *pSprite, tBitMap *pBitmap);
109112

@@ -149,6 +152,31 @@ void spriteSetAttached(tSprite *pSprite, UBYTE isAttached);
149152
*/
150153
void spriteRequestMetadataUpdate(tSprite *pSprite);
151154

155+
/**
156+
* @brief Set Position of the sprite. Mandatory for 4BPP sprites
157+
*
158+
* @param pSprite Sprite to be enabled.
159+
* @param wX X position, measured from the left of the view.
160+
* @param wY Y position, measured from the top of the view.
161+
*/
162+
void spriteSetPos(tSprite *pSprite,WORD wX, WORD wY);
163+
164+
/**
165+
* @brief Set Position of the sprite. Mandatory for 4BPP sprites
166+
*
167+
* @param pSprite Sprite to be enabled.
168+
* @param wX X position, measured from the left of the view.
169+
*/
170+
void spriteSetPosX(tSprite *pSprite,WORD wX);
171+
172+
/**
173+
* @brief Set Position of the sprite. Mandatory for 4BPP sprites
174+
*
175+
* @param pSprite Sprite to be enabled.
176+
* @param wY Y position, measured from the top of the view.
177+
*/
178+
void spriteSetPosY(tSprite *pSprite, WORD wY);
179+
152180
/**
153181
* @brief Updates the sprite's metadata if set as requiring update.
154182
* Be sure to call it at least whenever sprite's metadata needs updating.

src/ace/managers/sprite.c

+96-6
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,15 @@ static void spriteChannelRequestCopperUpdate(tSpriteChannel *pChannel) {
3030
pChannel->ubCopperRegenCount = 2; // for front/back buffers in raw mode
3131
}
3232

33+
tSprite* get4BPPAttachedSprite(tSprite *pSprite) {
34+
tSpriteChannel *pChannel = &s_pChannelsData[pSprite->ubChannelIndex+1];
35+
/*
36+
if(!pChannel->ubCopperRegenCount) {
37+
return ;
38+
}*/
39+
return pChannel->pFirstSprite;
40+
}
41+
3342
void spriteManagerCreate(const tView *pView, UWORD uwRawCopPos) {
3443
// TODO: add support for non-chained mode (setting sprxdat with copper)?
3544
s_pView = pView;
@@ -132,6 +141,10 @@ void spriteSetEnabled(tSprite *pSprite, UBYTE isEnabled) {
132141
pSprite->isEnabled = isEnabled;
133142
// TODO: only after modifying first sprite in chain, change next sprite ptr in the prior one
134143
s_pChannelsData[pSprite->ubChannelIndex].ubCopperRegenCount = 2; // for front/back buffers
144+
145+
if (pSprite->is4PP) {
146+
s_pChannelsData[pSprite->ubChannelIndex+1].ubCopperRegenCount = 2;
147+
}
135148
}
136149

137150
void spriteSetAttached(tSprite *pSprite, UBYTE isAttached) {
@@ -153,9 +166,9 @@ void spriteRequestMetadataUpdate(tSprite *pSprite) {
153166
}
154167

155168
void spriteSetBitmap(tSprite *pSprite, tBitMap *pBitmap) {
156-
if(!(pBitmap->Flags & BMF_INTERLEAVED) || pBitmap->Depth != 2) {
169+
if(!(pBitmap->Flags & BMF_INTERLEAVED) || (pBitmap->Depth != 2 && (pBitmap->Depth != 4 || (pSprite->ubChannelIndex & 1) == 1))) {
157170
logWrite(
158-
"ERR: Sprite channel %hhu bitmap %p isn't interleaved 2BPP\n",
171+
"ERR: Sprite channel %hhu bitmap %p isn't interleaved 2BPP(for any channel) or 4BPP(for an even channel)\n",
159172
pSprite->ubChannelIndex, pBitmap
160173
);
161174
return;
@@ -170,11 +183,50 @@ void spriteSetBitmap(tSprite *pSprite, tBitMap *pBitmap) {
170183
return;
171184
}
172185

173-
pSprite->pBitmap = pBitmap;
174-
spriteSetHeight(pSprite, pBitmap->Rows - 2);
186+
if (pBitmap->Depth == 2) {
187+
pSprite->pBitmap = pBitmap;
188+
spriteSetHeight(pSprite, pBitmap->Rows - 2);
175189

176-
tSpriteChannel *pChannel = &s_pChannelsData[pSprite->ubChannelIndex];
177-
spriteChannelRequestCopperUpdate(pChannel);
190+
tSpriteChannel *pChannel = &s_pChannelsData[pSprite->ubChannelIndex];
191+
spriteChannelRequestCopperUpdate(pChannel);
192+
}
193+
if (pBitmap->Depth == 4) {
194+
195+
pSprite->is4PP=1;
196+
197+
tBitMap *pointers_low;
198+
tBitMap *pointers_high;
199+
200+
pointers_high = bitmapCreate(
201+
ubByteWidth*8, pBitmap->Rows,
202+
2, BMF_CLEAR | BMF_INTERLEAVED);
203+
204+
pointers_low = bitmapCreate(
205+
ubByteWidth*8, pBitmap->Rows,
206+
2, BMF_CLEAR | BMF_INTERLEAVED);
207+
208+
// Convert the 4bpp bitmap to 2bpp.
209+
for (UWORD r = 0; r < pBitmap->Rows; r++)
210+
{
211+
UWORD offetSrc = r * pBitmap->BytesPerRow;
212+
UWORD offetDst = r * pointers_low->BytesPerRow;
213+
memcpy(pointers_low->Planes[0] + offetDst, pBitmap->Planes[0] + offetSrc, ubByteWidth);
214+
memcpy(pointers_low->Planes[1] + offetDst, pBitmap->Planes[1] + offetSrc, ubByteWidth);
215+
memcpy(pointers_high->Planes[0] + offetDst, pBitmap->Planes[2] + offetSrc, ubByteWidth);
216+
memcpy(pointers_high->Planes[1] + offetDst, pBitmap->Planes[3] + offetSrc, ubByteWidth);
217+
}
218+
219+
pSprite->pBitmap = pointers_low;
220+
spriteSetHeight(pSprite, pointers_low->Rows - 2);
221+
222+
tSpriteChannel *pChannel = &s_pChannelsData[pSprite->ubChannelIndex];
223+
spriteChannelRequestCopperUpdate(pChannel);
224+
225+
226+
tSprite *attached_sprite = spriteAdd(pSprite->ubChannelIndex+1, pointers_high);
227+
228+
spriteSetAttached(attached_sprite, 1);
229+
}
178230
}
179231

180232
void spriteProcessChannel(UBYTE ubChannelIndex) {
@@ -214,6 +266,40 @@ void spriteProcessChannel(UBYTE ubChannelIndex) {
214266
copSetMoveVal(&pList[0].sMove, ulSprAddr >> 16);
215267
copSetMoveVal(&pList[1].sMove, ulSprAddr & 0xFFFF);
216268
}
269+
270+
if (pSprite->is4PP) {
271+
spriteProcessChannel(ubChannelIndex+1);
272+
}
273+
}
274+
275+
276+
void spriteSetPos(tSprite *pSprite,WORD wX, WORD wY) {
277+
pSprite->wX = wX;
278+
pSprite->wY = wY;
279+
pSprite->isHeaderToBeUpdated = 1;
280+
if (pSprite->is4PP) {
281+
tSprite *pAttachedSprite = get4BPPAttachedSprite(pSprite);
282+
pAttachedSprite->wX = wX;
283+
pAttachedSprite->wY = wY;
284+
}
285+
}
286+
287+
void spriteSetPosX(tSprite *pSprite,WORD wX) {
288+
pSprite->wX = wX;
289+
pSprite->isHeaderToBeUpdated = 1;
290+
if (pSprite->is4PP) {
291+
tSprite *pAttachedSprite = get4BPPAttachedSprite(pSprite);
292+
pAttachedSprite->wX = wX;
293+
}
294+
}
295+
296+
void spriteSetPosY(tSprite *pSprite, WORD wY) {
297+
pSprite->wY = wY;
298+
pSprite->isHeaderToBeUpdated = 1;
299+
if (pSprite->is4PP) {
300+
tSprite *pAttachedSprite = get4BPPAttachedSprite(pSprite);
301+
pAttachedSprite->wY = wY;
302+
}
217303
}
218304

219305
void spriteProcess(tSprite *pSprite) {
@@ -246,6 +332,10 @@ void spriteProcess(tSprite *pSprite) {
246332
BTST(uwHStart, 0)
247333
);
248334

335+
if (pSprite->is4PP) {
336+
tSprite *pAttachedSprite = get4BPPAttachedSprite(pSprite);
337+
spriteProcess(pAttachedSprite);
338+
}
249339
}
250340

251341
void spriteSetHeight(tSprite *pSprite, UWORD uwHeight) {

0 commit comments

Comments
 (0)