How To

How to rotate a sprite from a point

3

AstonBrown 2022-11-06 14:47

Please help with examples thank you.


McPepic 2022-11-06 14:54

There is no way to rotate sprites in LowRes. The program you saw from SP4CEBAR comes from a special program that he made to write pixel data to a ROM using some algorithm.


was8bit 2022-11-06 15:30

You can FLIP a sprite... Xflip, or Yflip...

If you create a horizontal and a vertical and a diagonal, you can use FLIP to create all 8 directions...

... however, honestly, unless you are using a ton of graphics and need to free up space, its actually just easier to create all the various graphics and index them... and match them up to your indexed directions...

Ex.

DIM GLOBAL XDIR(8), YDIR(8), IMAGE(8)
GLOBAL PDIR

FOR I=1 TO 8
READ XDIR(I),YDIR(I),IMAGE(I)
NEXT I
DATA 1,0,1, 1,1,2, 0,1,3, -1,1,4, -1,0,5, -1,-1,6, 0,-1,7, 1,-1,8

Assuming your graphics for each direction are stored in 1 to 8

Then ... if player sprite is #0, then

PX=PX+XDIR(PDIR)
PY=PY+YDIR(PDIR)
SPRITE 0,PX,PY,IMAGE(PDIR)

will always have your player pointed in the direction its moving ;)


was8bit 2022-11-06 15:33

Its possible to use clever math rather than storing data, but if you choose to make changes later, clever math can quickly fail or cause issues that are not easy to find and fix... and index is super easy to change and also to find and fix if you made an error somewhere...


was8bit 2022-11-06 15:35

The indexed approach is also useful for adding animations, or graphic changes due to status...


AstonBrown 2022-11-07 02:03

oh
well thanks anyway everyone


was8bit 2022-11-07 05:43 (Edited)

Well, be glad that "rotate" is in the graphics editor... originally it didn't have such a thing ;)

... but lowresNX doesn't have any built in sprite manipulation... other than flip...


nathanielbabiak 2022-11-08 04:30 (Edited)

AstonBrown, I have a bunch of code that does this. But it's insanely complicated. See attached for an old example circa 2020. (Don't run it or review the source code, read this explanation first.)

(The key thing you need to understand for "how to rotate a sprite" is not the bitmapping math, nor the rotational formulas. I'll address those separately.)

The key thing you need to understand is: if you rotate a grid of pixels by any angle other than 90°, the new pixel locations don't line up exactly in the grid. Not only do you need to be sure you pick the "best" pixel, as a first step you need to be sure you pick any pixel. Other uploads on this site don't consider this, and you can see where 'fuzzy' (blank) pixels develop occasionally.

So, to address the grid issue, you need to use super sampling. In practice, you'll take the original grid of pixels you have, up-scale it so every pixel is 2x2, 3x3, or 4x4 blocks of pixels (4, 9, or 16 pixels per block of "original pixels"). Perform the up-scale with nearest neighbor interpolation so it looks super blocky.

Then, you use the rotation formulas. An internet search will yield those, but you just need to run the rotation on the whole grid of (super) pixels (not original pixels).

Lastly, you down-sample back to the original pixel dimensions. To do so, you take the locations of the original pixel grid, but use the rotated pixels, and tally up the number of pixels of each color inside your large grid. Then you need to pick a formula of some sort to determine what happens. For example, 90% of the pixels inside the large grid are one color, and 10% are not.

Now at this point, run the attachment (don't review the source code). You'll see the algorithm described above is exactly what the attachment does, using 8x8 grid size. (It takes a large super-sampled triangle on the left, and rotates it and down-samples it into a small triangle on the right.) It doesn't start from a bitmap though, it starts from a vector triangle.

All that said, surely you could code this description yourself... if only LowRes NX was simple enough to let you use DIM BITMAP(PX_WIDTH, PX_HEIGHT) right? (And hey, if that sounds like a stretch for you, that's no problem, just something else we can work on.)

AA.nx | Open in app
2022-11-08 04:33

nathanielbabiak 2022-11-08 04:38 (Edited)

So, suppose you could implement the algorithm above, if only you could work on a 2-dimensional array of pixels. Let's call the array BMP(), bmp for bitmap, and let's say you need two of them. One for a 32x32 "original", and one for a 128x128 super-sample (meaning we'd use 4x4 pixel blocks for supersampling). So we'd have:

DIM BMP_ORIGINAL(31,31), BMP_SUPER_SAMPLE(127,127)

...and each element (the value at BMP(x,y)) would be a color from 1 to 3, or would be value 0 meaning transparent.

If you can take the algorithm in the previous post and code it up using the definitions in this one, the next post will be the code that develops those bitmaps for you.


nathanielbabiak 2022-11-08 04:39 (Edited)

SUB BMP_LOAD( BMP1(), SRC_ADDR, CHAR_NUM, CHAR_WIDTH, CHAR_HEIGHT )
  DIM MASK( 7 )
  RESTORE BMP_L0AD
  BMP_L0AD:
  READ MASK(0), MASK(1), MASK(2), MASK(3), MASK(4), MASK(5), MASK(6), MASK(7)
  DATA     $80,     $40,     $20,     $10,      8 ,      4 ,      2 ,      1
  ADR = $10 * CHAR_NUM + SRC_ADDR
  FOR CY1 = 0 TO 7.99 * CHAR_HEIGHT STEP 8
    FOR CX1 = 0 TO 7.99 * CHAR_WIDTH STEP 8
      FOR PY1 = CY1 TO CY1 + 7
        HI1 = PEEK( ADR + 8 )
        LO1 = PEEK( ADR )
        FOR PX1 = 0 TO 7
          BMP1( CX1 + PX1, PY1 ) = 2 * SGN(HI1 AND MASK(PX1)) + SGN(LO1 AND MASK(PX1))
        NEXT PX1
        INC ADR
      NEXT PY1
      ADD ADR, 8
    NEXT CX1
    ADD ADR, -$10 * CHAR_WIDTH + $100
  NEXT CY1
END SUB
SUB BMP_SAVE( BMP1(), DST_ADDR, CHAR_NUM, CHAR_WIDTH, CHAR_HEIGHT )
  DIM MASK( 7 )
  RESTORE BMP_S4VE
  BMP_S4VE:
  READ MASK(0), MASK(1), MASK(2), MASK(3), MASK(4), MASK(5), MASK(6), MASK(7)
  DATA     $80,     $40,     $20,     $10,      8 ,      4 ,      2 ,      1
  ADR = $10 * CHAR_NUM + DST_ADDR
  FOR CY1 = 0 TO 7.99 * CHAR_HEIGHT STEP 8
    FOR CX1 = 0 TO 7.99 * CHAR_WIDTH STEP 8
      FOR PY1 = CY1 TO CY1 + 7
        LO1 = $00
        HI1 = $00
        FOR PX1 = 0 TO 7
          IF BMP1( CX1 + PX1, PY1 ) AND 1 THEN ADD LO1, MASK( PX1 )
          IF BMP1( CX1 + PX1, PY1 ) AND 2 THEN ADD HI1, MASK( PX1 )
        NEXT PX1
        POKE ADR    , LO1
        POKE ADR + 8, HI1
        INC ADR
      NEXT PY1
      ADD ADR, 8
    NEXT CX1
    ADD ADR, -$10 * CHAR_WIDTH + $100
  NEXT CY1
END SUB


nathanielbabiak 2022-11-08 04:41 (Edited)

Just make sure to check your bitmaps in the LowRes NX GFx Editor before you try to rotate them, as the character number from the Gfx editor is CHAR_NUM in the arguments.

Also, CHAR_WIDTH and CHAR_HEIGHT are in units of characters (8x8 pixels), they're not in units of pixels, so your bitmaps will need to be padded to a width and height that's a multiple of 8.

Anyway, that's it. Enjoy! Let me know if you have any questions.


AstonBrown 2022-11-08 09:28

oh ok
i think i get it
thanks nathanielbabiak


nathanielbabiak 2022-11-08 23:57 (Edited)

If the colors of the palette are not a gradient, then it doesn't make sense to use super sampling at all. In that case, there's a faster way to do this that I forgot to mention. It still considers the "any pixel" concept.

A simplistic (but incorrect) pixel rotation algorithm will take the non-rotated pixel bitmap and, for every pixel, look at the non-rotated coordinates, then rotate those coordinates into new coordinates. The problem with this approach is rounding - pixels are integers, and the new rotated bitmap will have gaps.

Instead, a correct pixel rotation algorithm would focus on the coordinates of the "final" (rotated) bitmap, before you've placed any pixels yet. For every pixel coordinate, "un-rotate" (determine the pixel coordinate of the original (non-rotated) bitmap). Then just sample the original pixel color at the original (non-rotated) coordinates, and plug the color value into the "final" (rotated) bitmap.

Because of pixel/integer rounding, you may ultimately sample some pixels more than once. But, because you "step through" all the "final" (rotated) coordinates, you won't skip any pixels. So, you won't have any gaps!


SP4CEBAR 2022-11-09 15:15 (Edited)

You asked it before but you probably didn't get the notification of my answer

Anyways, for anyone who wants to experiment with the rotation matrix, here's a simplified example:

SUB ROTATE(X,Y,A,PX,PY)
  XI=X-PX
  YI=Y-PY
  X= COS(A)*XI + SIN(A)*YI + PX
  Y= -SIN(A)*XI + COS(A)*YI + PY
END SUB

It rotates the point (X,Y) around the pivot point (PX,PY) by an angle (A) in radians.
To rotate an image, simply send the coordinates of all the pixels of the image to the subprogram


SP4CEBAR 2022-11-09 15:40 (Edited)

@nathanielbabiak your pixel library is insane, it runs so smoothly!


nathanielbabiak 2022-11-09 22:51

Hey thanks! Feel free to use it any way you wish! Just upload your creations so we can all check them out!


Log in to reply.