/* LEDSprites V4 class by Aaron Liddiment (c) 2015 Inspiration came from my old C64 days :) FastLED v3.1 library by Daniel Garcia and Mark Kriegsmann. Written & tested on a Teensy 3.1 using Arduino V1.0.5r2 & teensyduino V1.20 */ #include #include #include cSprite::cSprite(uint8_t Width, uint8_t Height, const uint8_t *Data, uint8_t NumFrames, SpriteNumBits_t BitsPixel, const struct CRGB *ColTable, const uint8_t *Mask) { if (Data) Setup(Width, Height, Data, NumFrames, BitsPixel, ColTable, Mask); } void cSprite::Setup(uint8_t Width, uint8_t Height, const uint8_t *Data, uint8_t NumFrames, SpriteNumBits_t BitsPixel, const struct CRGB *ColTable, const uint8_t *Mask) { m_Width = Width; m_Height = Height; m_Data = Data; m_NumFrames = NumFrames; m_BitsPixel = (uint8_t)BitsPixel; m_ColTable = ColTable; m_Mask = Mask; m_PrevSprite = m_NextSprite = NULL; m_X = m_Y = 0; m_Frame = m_FrameRate = m_XRate = m_YRate = m_CounterFrame = m_CounterX = m_CounterY = m_Options = m_Flags = 0; m_XChange = m_YChange = 0; m_NumCols = (1 << m_BitsPixel) - 1; m_MaskSize = ((m_Width + 7) / 8) * m_Height; m_FrameSize = m_MaskSize * m_BitsPixel; } void cSprite::Update() { if (m_CounterFrame > 0) { if ((--m_CounterFrame) == 0) { m_CounterFrame = m_FrameRate; m_Frame++; if (m_Frame >= m_NumFrames) m_Frame = 0; } } if (m_CounterX > 0) { if ((--m_CounterX) == 0) { m_CounterX = m_XRate; if (m_Options & SPRITE_X_KEEPIN) { if ( ((m_Flags & SPRITE_EDGE_X_MIN) && (m_XChange < 0)) || ((m_Flags & SPRITE_EDGE_X_MAX) && (m_XChange > 0)) ) m_XChange = 0 - m_XChange; } m_X += m_XChange; } } if (m_CounterY > 0) { if ((--m_CounterY) == 0) { m_CounterY = m_YRate; if (m_Options & SPRITE_Y_KEEPIN) { if ( ((m_Flags & SPRITE_EDGE_Y_MIN) && (m_YChange < 0)) || ((m_Flags & SPRITE_EDGE_Y_MAX) && (m_YChange > 0)) ) m_YChange = 0 - m_YChange; } m_Y += m_YChange; } } } boolean cSprite::Combine(int16_t dx, int16_t dy, cSprite *Src) { // Purely added to make Tetris example! Note that the colour tables have to be the same! uint32_t sfm2, sfm1, sfd2, sfd1; uint8_t *dstData, *dstMask; if (m_BitsPixel != Src->m_BitsPixel) return(false); else { uint8_t *SrcData = (uint8_t *)&(Src->m_Data[Src->m_Frame * Src->m_FrameSize]); uint8_t *DstData = (uint8_t *)&(m_Data[(m_Frame * m_FrameSize) + ((dx / 8) * m_BitsPixel) + (((m_Height - Src->m_Height) - dy) * ((m_Width + 7) / 8) * m_BitsPixel)]); uint8_t *SrcMask = (uint8_t *)&(Src->m_Mask[Src->m_Frame * Src->m_MaskSize]); uint8_t *DstMask = (uint8_t *)&(m_Mask[(m_Frame * m_MaskSize) + (dx / 8) + (((m_Height - Src->m_Height) - dy) * ((m_Width + 7) / 8))]); int8_t sm = dx % 8; int8_t sd = sm * m_BitsPixel; for (int k=Src->m_Height; k>0; --k) { dstData = DstData; dstMask = DstMask; for (int j=0; jm_Width; j+=8) { sfd1 = 0; for (int k=m_BitsPixel; k>0; --k) sfd1 = (sfd1 << 8) | *SrcData++; if (sfd1 != 0) { sfd2 = sfd1 << ((m_BitsPixel * 8) - sd); sfd1 >>= sd; for (int i=m_BitsPixel-1; i>=0; --i) { dstData[i] |= (uint8_t)sfd1; sfd1 >>= 8; if (sfd2 != 0) { dstData[m_BitsPixel + i] |= (uint8_t)sfd2; sfd2 >>= 8; } } } sfm1 = *SrcMask++; if (sfm1 != 0) { sfm2 = sfm1 << (8 - sm); sfm1 >>= sm; dstMask[0] |= (uint8_t)sfm1; if (sfm2 > 0) dstMask[1] |= (uint8_t)sfm2; } dstData = &dstData[m_BitsPixel]; dstMask++; } DstData = &DstData[((m_Width + 7) / 8) * m_BitsPixel]; DstMask = &DstMask[(m_Width + 7) / 8]; } return(true); } } void cSprite::Render(cLEDMatrixBase *Matrix) { int16_t y = m_Y + m_Height - 1; uint8_t *sprframedata = (uint8_t *)&(m_Data[m_Frame * m_FrameSize]); for (int i=m_Height; i>0; --i,--y) { int16_t x = m_X; for (int j=0; j0; --k) sfd = (sfd << 8) | *sprframedata++; for (int k=min(m_Width-j, 8),sm=m_BitsPixel*7; k>0; --k,sm-=m_BitsPixel,++x) { uint8_t col = (sfd >> sm) & m_NumCols; if (col > 0) (*Matrix)(x, y) = m_ColTable[col - 1]; } } } } void cSprite::EdgeDetect(cLEDMatrixBase *Matrix) { bool onXMatrix = false, onYMatrix = false, onEdgeXMin = false, onEdgeXMax = false, onEdgeYMin = false, onEdgeYMax = false; if ((m_X + m_Width) <= 0) onEdgeXMin = true; else if (m_X >= Matrix->Width()) onEdgeXMax = true; else { uint8_t *Mask = (uint8_t *)&m_Mask[m_Frame * m_MaskSize]; uint8_t XCnt = (m_Width + 7) / 8; int16_t xoff; if (m_X <= 0) xoff = max(0, 0 - m_X); else xoff = max(0, (Matrix->Width() - 1) - m_X); uint8_t XBit = xoff % 8; uint8_t XMid = xoff / 8; for (int16_t y=(m_Y+m_Height)-1; y>=m_Y; --y) { if ((y >= 0) && (y < Matrix->Height())) { for (uint8_t x=0; x> XBit))) || ((x > XMid) && (Mask[x])) ) { if (m_X <= 0) { onXMatrix = true; onYMatrix = true; } else onEdgeXMax = true; } } } Mask = &Mask[XCnt]; } } if ((m_Y + m_Height) <= 0) onEdgeYMin = true; else if (m_Y >= Matrix->Height()) onEdgeYMax = true; else { uint8_t *Mask = (uint8_t *)&m_Mask[m_Frame * m_MaskSize]; uint8_t XCnt = (m_Width + 7) / 8; for (int16_t y=(m_Y+m_Height)-1; y>=m_Y; --y) { for (uint8_t x=0; x= (Matrix->Height() - 1)) onEdgeYMax = true; if ( (y >= 0) && (y < Matrix->Height()) ) onYMatrix = true; } } Mask = &Mask[XCnt]; } } m_Flags &= (~ (SPRITE_EDGE_MASK | SPRITE_OFF_MASK)); if (onXMatrix) { if (onEdgeXMin) m_Flags |= SPRITE_EDGE_X_MIN; if (onEdgeXMax) m_Flags |= SPRITE_EDGE_X_MAX; } else if ((onEdgeXMin) || (onEdgeXMax)) m_Flags |= SPRITE_MATRIX_X_OFF; if (onYMatrix) { if (onEdgeYMin) m_Flags |= SPRITE_EDGE_Y_MIN; if (onEdgeYMax) m_Flags |= SPRITE_EDGE_Y_MAX; } else if ((onEdgeYMin) || (onEdgeYMax)) m_Flags |= SPRITE_MATRIX_Y_OFF; } void cSprite::SetPosition(int16_t X, int16_t Y) { m_X = X; m_Y = Y; } void cSprite::SetFrame(uint8_t Frame, uint8_t FrameRate) { m_Frame = max(m_NumFrames - 1, Frame); m_FrameRate = m_CounterFrame = FrameRate; } void cSprite::SetMotion(int8_t XChange, uint8_t XRate, int8_t YChange, uint8_t YRate) { m_XChange = XChange; m_XRate = m_CounterX = XRate; m_YChange = YChange; m_YRate = m_CounterY = YRate; } void cSprite::SetOptions(uint8_t Options) { m_Options = Options; } void cSprite::SetPositionFrameMotionOptions(int16_t X, int16_t Y, uint8_t Frame, uint8_t FrameRate, int8_t XChange, uint8_t XRate, int8_t YChange, uint8_t YRate, uint8_t Options) { SetPosition(X, Y); SetFrame(Frame, FrameRate); SetMotion(XChange, XRate, YChange, YRate); SetOptions(Options); } cLEDSprites::cLEDSprites(cLEDMatrixBase *Matrix) { m_Matrix = Matrix; m_SpriteHead = NULL; } void cLEDSprites::AddSprite(cSprite *Spr) { if (m_SpriteHead == NULL) { m_SpriteHead = Spr; Spr->m_PrevSprite = Spr->m_NextSprite = NULL; } else { if (IsSprite(Spr) == false) { cSprite *tmpSpr = m_SpriteHead; while (tmpSpr->m_NextSprite != NULL) tmpSpr = tmpSpr->m_NextSprite; tmpSpr->m_NextSprite = Spr; Spr->m_PrevSprite = tmpSpr; Spr->m_NextSprite = NULL; } } } boolean cLEDSprites::IsSprite(cSprite *Spr) { cSprite *tmpSpr = m_SpriteHead; while ((tmpSpr) && (tmpSpr != Spr)) tmpSpr = tmpSpr->m_NextSprite; if (tmpSpr) return(true); else return(false); } void cLEDSprites::RemoveSprite(cSprite *Spr) { if (IsSprite(Spr) == true) { if (Spr->m_PrevSprite != NULL) (Spr->m_PrevSprite)->m_NextSprite = Spr->m_NextSprite; if (Spr->m_NextSprite != NULL) (Spr->m_NextSprite)->m_PrevSprite = Spr->m_PrevSprite; if (m_SpriteHead == Spr) m_SpriteHead = Spr->m_NextSprite; Spr->m_PrevSprite = Spr->m_NextSprite = NULL; } } void cLEDSprites::RemoveAllSprites() { cSprite *tmpSpr; while ((tmpSpr = m_SpriteHead) != NULL) { m_SpriteHead = tmpSpr->m_NextSprite; tmpSpr->m_PrevSprite = tmpSpr->m_NextSprite = NULL; } } void cLEDSprites::ChangePriority(cSprite *Spr, SpritePriority_t Priority) { if (IsSprite(Spr) == true) { if (Priority == SPR_FRONT) { if (Spr->m_NextSprite != NULL) { cSprite *tmpSpr; if (Spr->m_PrevSprite != NULL) (Spr->m_PrevSprite)->m_NextSprite = Spr->m_NextSprite; else m_SpriteHead = Spr->m_NextSprite; (Spr->m_NextSprite)->m_PrevSprite = Spr->m_PrevSprite; tmpSpr = Spr->m_NextSprite; while (tmpSpr->m_NextSprite) tmpSpr = tmpSpr->m_NextSprite; tmpSpr->m_NextSprite = Spr; Spr->m_PrevSprite = tmpSpr; Spr->m_NextSprite = NULL; } } else if (Priority == SPR_FORWARD) { if (Spr->m_NextSprite != NULL) { if (Spr->m_PrevSprite != NULL) (Spr->m_PrevSprite)->m_NextSprite = Spr->m_NextSprite; else m_SpriteHead = Spr->m_NextSprite; (Spr->m_NextSprite)->m_PrevSprite = Spr->m_PrevSprite; Spr->m_PrevSprite = Spr->m_NextSprite; Spr->m_NextSprite = (Spr->m_NextSprite)->m_NextSprite; (Spr->m_PrevSprite)->m_NextSprite = Spr; if (Spr->m_NextSprite != NULL) (Spr->m_NextSprite)->m_PrevSprite = Spr; } } else if (Priority == SPR_BACKWARD) { if (Spr->m_PrevSprite != NULL) { if (Spr->m_NextSprite != NULL) (Spr->m_NextSprite)->m_PrevSprite = Spr->m_PrevSprite; (Spr->m_PrevSprite)->m_NextSprite = Spr->m_NextSprite; Spr->m_NextSprite = Spr->m_PrevSprite; Spr->m_PrevSprite = (Spr->m_PrevSprite)->m_PrevSprite; (Spr->m_NextSprite)->m_PrevSprite = Spr; if (Spr->m_PrevSprite != NULL) (Spr->m_PrevSprite)->m_NextSprite = Spr; else m_SpriteHead = Spr; } } else if (Priority == SPR_BACK) { if (Spr->m_PrevSprite != NULL) { if (Spr->m_NextSprite != NULL) (Spr->m_NextSprite)->m_PrevSprite = Spr->m_PrevSprite; (Spr->m_PrevSprite)->m_NextSprite = Spr->m_NextSprite; m_SpriteHead->m_PrevSprite = Spr; Spr->m_NextSprite = m_SpriteHead; m_SpriteHead = Spr; Spr->m_PrevSprite = NULL; } } } } void cLEDSprites::UpdateSprites() { cSprite *tmpSpr = m_SpriteHead; while (tmpSpr) { tmpSpr->Update(); if (tmpSpr->m_Options & SPRITE_DETECT_EDGE) tmpSpr->EdgeDetect(m_Matrix); tmpSpr = tmpSpr->m_NextSprite; } } void cLEDSprites::RenderSprites() { cSprite *tmpSpr = m_SpriteHead; while (tmpSpr) { tmpSpr->Render(m_Matrix); tmpSpr = tmpSpr->m_NextSprite; } } void cLEDSprites::DetectCollisions(cSprite *srcSpr) { cSprite *colSpr[2]; bool FirstLoop = true; if (srcSpr != NULL) colSpr[0] = srcSpr; else colSpr[0] = m_SpriteHead; while (colSpr[0] != NULL) { if (FirstLoop) colSpr[0]->m_Flags &= (~SPRITE_COLLISION); // Clear collision flag if (srcSpr != NULL) colSpr[1] = m_SpriteHead; else colSpr[1] = colSpr[0]->m_NextSprite; while (colSpr[1] != NULL) { while ((colSpr[0] == colSpr[1]) && (colSpr[1] != NULL)) colSpr[1] = colSpr[1]->m_NextSprite; if (colSpr[1] != NULL) { if (FirstLoop) colSpr[1]->m_Flags &= (~SPRITE_COLLISION); // Clear collision flag if ((colSpr[0]->m_Options & SPRITE_DETECT_COLLISION) || (colSpr[1]->m_Options & SPRITE_DETECT_COLLISION)) { bool chkColission = false; if ( (colSpr[0]->m_X < (colSpr[1]->m_X + colSpr[1]->m_Width)) && (colSpr[1]->m_X < (colSpr[0]->m_X + colSpr[0]->m_Width)) && (colSpr[0]->m_Y < (colSpr[1]->m_Y + colSpr[1]->m_Height)) && (colSpr[1]->m_Y < (colSpr[0]->m_Y + colSpr[0]->m_Height)) ) { uint8_t *colMask[2], partX, partY, colXBit; int16_t colXCnt, colYCnt; uint16_t xoff, yoff; if (colSpr[0]->m_X <= colSpr[1]->m_X) partX = 0; else partX = 1; if ((colSpr[partX]->m_Y + colSpr[partX]->m_Height) >= (colSpr[1 - partX]->m_Y + colSpr[1 - partX]->m_Height)) partY = partX; else partY = 1 - partX; xoff = colSpr[1 - partX]->m_X - colSpr[partX]->m_X; colMask[partX] = (uint8_t *)&colSpr[partX]->m_Mask[(colSpr[partX]->m_Frame * colSpr[partX]->m_MaskSize) + (xoff / 8)]; colMask[1 - partX] = (uint8_t *)&colSpr[1 - partX]->m_Mask[(colSpr[1 - partX]->m_Frame * colSpr[1 - partX]->m_MaskSize)]; colXBit = xoff % 8; colXCnt = min(colSpr[partX]->m_Width - xoff, colSpr[1 - partX]->m_Width); yoff = (colSpr[partY]->m_Y + colSpr[partY]->m_Height) - (colSpr[1 - partY]->m_Y + colSpr[1 - partY]->m_Height); colMask[partY] = (uint8_t *)&colMask[partY][yoff * ((colSpr[partY]->m_Width + 7) / 8)]; colYCnt = min(colSpr[partY]->m_Height - yoff, colSpr[1 - partY]->m_Height); for (; colYCnt>0; --colYCnt) { uint8_t x = 0; int16_t tmpXCnt = colXCnt; do { uint8_t colAData = colMask[partX][x] << colXBit; if ((8 - colXBit) < colXCnt) colAData |= colMask[partX][x + 1] >> (8 - colXBit); if ((colAData & colMask[1 - partX][x]) != 0x00) { chkColission = true; break; } ++x; colXCnt -= 8; } while (colXCnt > 0); if (chkColission) break; colXCnt = tmpXCnt; colMask[0] = (uint8_t *)&colMask[0][(colSpr[0]->m_Width + 7) / 8]; colMask[1] = (uint8_t *)&colMask[1][(colSpr[1]->m_Width + 7) / 8]; } } if (chkColission) { colSpr[0]->m_Flags |= SPRITE_COLLISION; colSpr[1]->m_Flags |= SPRITE_COLLISION; } } colSpr[1] = colSpr[1]->m_NextSprite; } } if (srcSpr != NULL) break; colSpr[0] = colSpr[0]->m_NextSprite; FirstLoop = false; } }