Jump to content
IGNORED

Wen hop? The Search for Planet X


Andrew Davie

Recommended Posts

This is my bizarre compile-time "bit reverse" table, generated through C #define statements.

It produces a 256 byte table with each entry having the bits in the binary entry reversed left-right.

Weird and yet somehow satisfying...

 

// COMPILE-TIME REVERSE BITS IN BYTE
#define RVS(a) ( \
      ((((a) >> 0) & 1) << 7) \
    | ((((a) >> 1) & 1) << 6) \
    | ((((a) >> 2) & 1) << 5) \
    | ((((a) >> 3) & 1) << 4) \
    | ((((a) >> 4) & 1) << 3) \
    | ((((a) >> 5) & 1) << 2) \
    | ((((a) >> 6) & 1) << 1) \
    | ((((a) >> 7) & 1) << 0) \
    )

#define P0(a) RVS(a)
#define P1(a) P0(a), P0(a+1)
#define P2(a) P1(a), P1(a+2)
#define P3(a) P2(a), P2(a+4)
#define P4(a) P3(a), P3(a+8)
#define P5(a) P4(a), P4(a+16)
#define P6(a) P5(a), P5(a+32)
#define P7(a) P6(a), P6(a+64)
#define P8(a) P7(a), P7(a+128)

// Want to call RVS(n) for 0-255 values. The weird #defines above aloow a single-call
// It's effectively a recursive power-of-two call of the base RVS macro

const unsigned char BitRev[] = {
    P8(0),
};

 

  • Like 2
Link to comment
Share on other sites

So, this video is a bit weird - but what you're looking at here is randomly generated moon textures. These textures will very soon be wrapped around a real-time-generated spinning planet/moon onscreen.  It's the intermission between planets; I hope to have a planet zooming towards you, spinning, shaded, with texture.  To do this, I've had to reorganise memory significantly. Rather than a 40x25 board, I'm now using a dynamically generated memory map/usage based on the board size. The textures generated here are two hemispheres, so one half of the texture (i.e., 40PF pixels) will be visible at any one time. And on top of that I'm planning a shadow/night area, and on top of that ... spinning... and on top of that... animation. Sounds tricky, but give me a week or so.  And because I'm reusing systems (the texture is generated and animated using the standard board draw, for example), it's not so expensive memory-wise.  Memory reorganisation has allowed me to effectively triple-buffer the graphics and still have room for the data streams.  Why triple-buffer?  Well, one to hold the texture and have it drawn/animating as per normal (described above). Then a double-buffer for the planet draw, which will take multiple frames.  I'll be mapping pixel-by-pixel from the texture map onto an effective sphere on the screen. This will take a fair bit of processing - so you'll be looking at one frame while the other is being drawn, and then it flips.  I won't have sprites, so the memory requirement will be 20 x 8 (board representing the texture) + 10 x 8 x 6 = 480 (visible part of texture, post-rendering by normal draw system) + 10 x 8 x 6 = visible screen + 10 x 8 x 6 = screen being rendered = 160 + 480 + 480 + 480 bytes.  So each complete frame of planet draw will be firstly to render the hemisphere texture based on planet rotation (to a buffer the size of the screen).  This gives a texture 40 pixels wide which is then wrapped around a smaller on-screen sphere (=circle but with correct perspective wrap) (of variable size). Once the texture is rendered (and remember it's a normal screen/board, so it can have animation/movement -- you could play the game on this spinning planet, in theory)... so anyway, once texture is rendered, then we are texture-mapping the texture onto the sphere. This is done with some magic math stuff, effectively picking pixels out of the texture and placing them in the right place on the sphere double-buffer buffer that we're using. Once that is finished, we apply a shadow/mask to the buffer to give the shading on the planet. Once that is done, we flip the double-buffer, and display what we've done. And repeat.  Scroll/rotate planet texture.  render texture with animations.... map texture to sphere... flip buffers. As you can see, it's quite complex but I have this vision and I want to see how it looks. 

 

 

 

  • Like 7
Link to comment
Share on other sites

On 7/30/2021 at 5:57 PM, Andrew Davie said:

This is my bizarre compile-time "bit reverse" table, generated through C #define statements.

It produces a 256 byte table with each entry having the bits in the binary entry reversed left-right.

Weird and yet somehow satisfying...

 


// COMPILE-TIME REVERSE BITS IN BYTE
#define RVS(a) ( \
      ((((a) >> 0) & 1) << 7) \
    | ((((a) >> 1) & 1) << 6) \
    | ((((a) >> 2) & 1) << 5) \
    | ((((a) >> 3) & 1) << 4) \
    | ((((a) >> 4) & 1) << 3) \
    | ((((a) >> 5) & 1) << 2) \
    | ((((a) >> 6) & 1) << 1) \
    | ((((a) >> 7) & 1) << 0) \
    )

#define P0(a) RVS(a)
#define P1(a) P0(a), P0(a+1)
#define P2(a) P1(a), P1(a+2)
#define P3(a) P2(a), P2(a+4)
#define P4(a) P3(a), P3(a+8)
#define P5(a) P4(a), P4(a+16)
#define P6(a) P5(a), P5(a+32)
#define P7(a) P6(a), P6(a+64)
#define P8(a) P7(a), P7(a+128)

// Want to call RVS(n) for 0-255 values. The weird #defines above aloow a single-call
// It's effectively a recursive power-of-two call of the base RVS macro

const unsigned char BitRev[] = {
    P8(0),
};

 

A very elegant solution.  Nice.  ?

Link to comment
Share on other sites

@Andrew Davie

Wow, this game looks amazing!  You certainly have skilled talent making the 2600 do what you are with this game and a very creative to imagine such a game like this within the modest limit of the console.   I have never seen anything like this attempted on a Atari 2600.  How far along is the game and once it is finished are you going to be selling it in the AtariAge store (this game is one game that I am eager to add to my Atari 2600 games library).

Link to comment
Share on other sites

1 hour ago, Tidus79001 said:

@Andrew Davie

Wow, this game looks amazing!  You certainly have skilled talent making the 2600 do what you are with this game and a very creative to imagine such a game like this within the modest limit of the console.   I have never seen anything like this attempted on a Atari 2600.  How far along is the game and once it is finished are you going to be selling it in the AtariAge store (this game is one game that I am eager to add to my Atari 2600 games library).

TY. I have a history of not completing stuff, so to be honest I wouldn't hold my breath if I were you. On the other hand, I definitely could use a bit of income so there's an incentive to get this one done. Most of the systems are complete, so finishing would be more a matter of working on the playability, level design, and transition.  There's still a lot of hard work. I have no idea if/how/when I might sell it. AtariAge would be one option. Another would be to just do it all myself in a very small run.  No real plans at this stage. 

 

I might point out that "the modest limit of the console" is a bit misguiding, because the ARM processor on the cartridge provides a lot of processing power, memory, and ease-of-programming that makes these sorts of games possible.

Link to comment
Share on other sites

7 hours ago, Andrew Davie said:

TY. I have a history of not completing stuff, so to be honest I wouldn't hold my breath if I were you. On the other hand, I definitely could use a bit of income so there's an incentive to get this one done. Most of the systems are complete, so finishing would be more a matter of working on the playability, level design, and transition.  There's still a lot of hard work. I have no idea if/how/when I might sell it. AtariAge would be one option. Another would be to just do it all myself in a very small run.  No real plans at this stage. 

 

I might point out that "the modest limit of the console" is a bit misguiding, because the ARM processor on the cartridge provides a lot of processing power, memory, and ease-of-programming that makes these sorts of games possible.

Ccrowd funding might be a an option if you need a bit more cash to work on this (I would be one on your first backers for sure).  If not completed still even what you have would be a great WIP game if for the community to play if you decide to share the ROM at some point here on the forums.

Link to comment
Share on other sites

8 minutes ago, Tidus79001 said:

Ccrowd funding might be a an option if you need a bit more cash to work on this (I would be one on your first backers for sure).  If not completed still even what you have would be a great WIP game if for the community to play if you decide to share the ROM at some point here on the forums.

I think the days of crowd funding have been and gone. Also fraught with danger for everyone. These days I think I'd go with finishing a product first, and then thinking about selling it. But thanks for the suggestion and vote of confidence.

  • Like 3
Link to comment
Share on other sites

So, planet display. It's been a difficult bit of work, but I have the basic systems functioning now.

This is a bit of a hack; I'll smooth out the movement soonish.  But still, pretty cool... if a bit artefact-y.  Husk likes artefacts.

I have put in three demo "planets"...

PS: Switch video to 720p;  it's too dim otherwise.

 

 

Edited by Andrew Davie
  • Like 8
Link to comment
Share on other sites

Well, here's the first WenHop binary. All it does at the moment is display the planets, as above.

Hold SELECT to switch to the next planet. This version is slightly improved on the texture tracking than the video.

Also, moon is brighter, and speed of rotation faster.  My next goal is to get the surface to track more convincingly as it rotates.

 

 

WENHOP20210805.bin

  • Like 4
Link to comment
Share on other sites

Hi Andrew,

 

Looks great!  This may be a silly question, but it sounded like the game required extra hardware to handle all of the processing power.  Is that hardware support natively available in emulation already?  Just trying to get myself a bit smarter on how the technology works, and the best way to try out the binary.

 

Thank you!

Link to comment
Share on other sites

11 minutes ago, Propane13 said:

Hi Andrew,

 

Looks great!  This may be a silly question, but it sounded like the game required extra hardware to handle all of the processing power.  Is that hardware support natively available in emulation already?  Just trying to get myself a bit smarter on how the technology works, and the best way to try out the binary.

 

Thank you!

 

It uses the CDFJ bankswitch scheme that is supported already by emulators such as Stella and Gopher2600.  Gopher2600 is the more accurate in terms of timing, and perhaps in terms of hardware emulation too.  But they are both excellent and suitable for playing/testing CDFJ games. The "extra hardware" is the ARM chip that lives on all CDFJ games/cartridges whose job is to "manage" the bankswitching. But as a bonus, it's possible to also use the ARM when it would otherwise be idle to do a bit of legwork that would otherwise be impossible on the 6507. It's a bit complex to get everything working in harmony, but when you do there are some pretty cool things you can make happen.

 

 

 

 

  • Like 5
Link to comment
Share on other sites

I got off my lazy hackity hack butt and did the math properly. Now the texture is locked onto the surface of the sphere possibly as good as it's going to get. It's not perfect, but I think it's now close to correct. Given it's a 20-pixel-across object onscreen, it's not doing too bad.  I've spent many hours on this, and now it's time to move on...

 

 

 

  • Like 7
Link to comment
Share on other sites

Here's an updated binary.  SELECT (long-press) to go to next planet. Left/right to alter rotation. It has one or two bugs/glitches; rotation sometimes stops... there's a pulsing/shimmering in the colours on some planets. I'll sort those things out sometime.  

 

 

WENHOP20210809.bin

  • Like 3
Link to comment
Share on other sites

One of the parameters I have is the pixel ratio; as I adjust this, I do get other side-effects. Although I'd spent quite some time trying to get that front curvature just slightly more curved, I had a hard time achieving that.  Well, funny enough - I did a minor tweak to the pixel ratio (from 5.5 to 6) and as an unexpected (and welcome) side effect, I get more curvature on the front face. And it looks pretty good to me.

 

I've also added bands of colour - darker at the top, lighter at the equator, darker at the bottom. This works really nicely to emphasise the 3D effect, and now those planets where I have done that look pretty good. The doge star, of course, but also the plain old moon... with a bit of a tone, too. It's all come together nicely.

 

 

 

... and... that may be about it for a week or two. I have things to do.

 

 

WENHOP20210809b.bin

  • Like 2
Link to comment
Share on other sites

Here's the complete source code for texture mapping a surface bitmap onto the rotating sphere...

 

#include "main.h"
#include "drawplanet.h"
#include "defines_from_dasm_for_c.h"
#include "characterset.h"
#include "defines.h"

// duplicate from defines_cdfj.h
// Raw queue pointers
extern void* DDR;
#ifndef RAM
#define RAM ((unsigned char*)DDR)
#endif

int planetX = 20 << 16;
int planetY = (_ARENA_SCANLINES << 16) / 3 / 2;


const int masker[] = {
0b000000000000100100010000100000, // 3
0b000000000001001001001000100000, // 6
0b000000000001010100100100010000, // 9
0b000000000001010101010010010000, // 12
0b000000000001101010101010010000, // 15
0b000000000001101101101001001000, // 18
0b000000000001101101101001001000, // 21
0b000000000001110111010101001000, // 24
0b000000000001110111010101001000, // 27
0b000000000001111101101101001000, // 30
0b000000000001111101101101001000, // 33
0b000000000001111101101101001000, // 36
0b000000000001111111011010101000, // 39
0b000000000001111111011010101000, // 42
0b000000000001111111011010101000, // 45
0b000000000011110111111010101000, // 48
0b000000000011110111111010101000, // 51
0b000000000011110111111010101000, // 54
0b000000000011110111111010101000, // 57
0b000000000011110111111010101000, // 60
0b000000000011111111110110100100, // 63
0b000000000011111111110110100100, // 66
0b000000000011111111110110100100, // 69
0b000000000011111111110110100100, // 72
0b000000000011111111110110100100, // 75
0b000000000011111111110110100100, // 78
0b000000000011111111110110100100, // 81
0b000000000011111111110110100100, // 84
0b000000000011111111110110100100, // 87
0b000000000011111111110110100100, // 90
0b000000000011111111110110100100, // 93
0b000000000011111111110110100100, // 96
0b000000000011111111110110100100, // 99
0b000000000011111111110110100100, // 102
0b000000000011111111110110100100, // 105
0b000000000011110111111010101000, // 108
0b000000000011110111111010101000, // 111
0b000000000011110111111010101000, // 114
0b000000000011110111111010101000, // 117
0b000000000011110111111010101000, // 120
0b000000000001111111011010101000, // 123
0b000000000001111111011010101000, // 126
0b000000000001111111011010101000, // 129
0b000000000001111101101101001000, // 132
0b000000000001111101101101001000, // 135
0b000000000001111101101101001000, // 138
0b000000000001110111010101001000, // 141
0b000000000001110111010101001000, // 144
0b000000000001101101101001001000, // 147
0b000000000001101101101001001000, // 150
0b000000000001101010101010010000, // 153
0b000000000001010101010010010000, // 156
0b000000000001010100100100010000, // 159
0b000000000001001001001000100000, // 162
0b000000000000100100010000100000, // 165
};




const int lineOffset[] = {
47 , // 3
73 , // 6
85 , // 9
105 , // 12
114 , // 15
131 , // 18
140 , // 21
149 , // 24
166 , // 27
172 , // 30
178 , // 33
195 , // 36
201 , // 39
207 , // 42
213 , // 45
227 , // 48
233 , // 51
239 , // 54
245 , // 57
259 , // 60
265 , // 63
271 , // 66
274 , // 69
288 , // 72
294 , // 75
300 , // 78
306 , // 81
320 , // 84
323 , // 87
329 , // 90
335 , // 93
341 , // 96
355 , // 99
358 , // 102
364 , // 105
370 , // 108
384 , // 111
390 , // 114
396 , // 117
402 , // 120
416 , // 123
422 , // 126
428 , // 129
434 , // 132
451 , // 135
457 , // 138
463 , // 141
480 , // 144
489 , // 147
498 , // 150
515 , // 153
524 , // 156
544 , // 159
556 , // 162
582 , // 165
-1,
};


int pscrollSpeed = -500;

void drawPlanet() {

 
    if (SWCHA & 0x40)
        pscrollSpeed += 100;
    if (SWCHA & 0x80)
        pscrollSpeed -= 100;

    scrollX += pscrollSpeed;
    if (scrollX >= (30 << 16))
        scrollX -= (30 << 16);
    if (scrollX < 0)
        scrollX += 30 << 16; //+= (20 << 16);



    int shift = 4- ((scrollX >> 14 ) & 3);
    int frac = scrollX >> 16;
    unsigned char *xchar;
    const unsigned char *image[6];

    for (int half = 0; half < 10; half += 5) { 

        int base = half ? VIDBUF_PF0_RIGHT : VIDBUF_PF0_LEFT;

        unsigned char *pf0 = RAM + buf[base];
        unsigned char *pf1 = RAM + buf[base + 1];
        unsigned char *pf2 = RAM + buf[base + 2];


        for (int scanline = 0; lineOffset[scanline] >= 0 && scanline * 3 < SCANLINES; scanline++) {
            
            xchar = RAM + _BOARD + (half + frac) + boardWidth * (lineOffset[scanline] >> 5);
            int charow = lineOffset[scanline] & 31;

            for (int i = 0; i < 6; i++) {
                unsigned char piece = *xchar++;
                unsigned int type = CharToType[piece];
                if (Animate[type])
                    piece = (*Animate[type])[AnimIdx[type].index];
                image[i] = *charSet[piece] + charow;
            }


            int p2;

            int mask = masker[scanline];

            for (int icc = 0; icc < 3; icc++) {

                int p = ((*image[0]++ & 0b1111) << 20
                      | (*image[1]++ & 0b1111) << 16
                      | (*image[2]++ & 0b1111) << 12
                      | (*image[3]++ & 0b1111) << 8
                      | (*image[4]++ & 0b1111) << 4
                      | (*image[5]++ & 0b1111)) >> shift;


                p2 = 0;

                if (!half) {

                    for (int i = 0; i <20; i++)
                        if (mask & (1 << i))
                            p2 = (p2 << 1) | ((p >> (19-i)) & 1);
                }
                else {

                    for (int i = 0; i < 20; i++)
                        if (mask & (1 << i))
                            p2 = (p2 >> 1) | (((p >> i) & 1) << 19);
                }

                *pf0++ = BitRev[p2 >> 16];
                *pf1++ = p2 >> 8;
                *pf2++ = BitRev[p2 & 0xFF];
            }
        }
    }


}

 

  • Like 3
Link to comment
Share on other sites

As you can see from the above, the math is pre-calculated (by a Python tool) and table-driven. I tested to see if the calculations are correct by trying with different sized planets, and as the video shows, all is good. I could install (say) 10 pre-calculated sizes - but since the math is actually quite simple ... maybe I can port it to the game code itself and have realtime calcs instead of pre-calculated.  Of note in this video is how well you can read "MOON" on the small one, even though the resolution is super-low.  In fact, when I slow it down you see what's happening.  We're getting pixel blending and "super-resolution" when I rotate at speed. Nice.

 

 

For those interested, here's the python code that does the precalculations...

 

import math

print("Pixel selection...")

desiredPlanetRadiusPixels = 20
textureWidth = 20
pixelRatio = 6.25

full180 = desiredPlanetRadiusPixels * 2
for line in range(3, full180, 3):
    v = line - desiredPlanetRadiusPixels
    offsetText = math.sqrt(desiredPlanetRadiusPixels * desiredPlanetRadiusPixels - (v * v)) / pixelRatio
    offset = int(offsetText + 0.5)

    bits = 0
    for subPix in range(0, offset):

        realAngle = math.degrees(math.asin((subPix + 0.5)/offset))
        texturePix = realAngle * (textureWidth) / 90
        bits |= (1 << (19-int(texturePix + 0.5)))

    print(format(bits, '#032b') + ', //', line)

print("Line selection...")

planetHeight = 20 * 24
halfHeight = planetHeight / 2

for line in range(3, full180, 3):
    v = line - desiredPlanetRadiusPixels
    angle = math.degrees(math.acos(-v/desiredPlanetRadiusPixels))
    length = int(angle * planetHeight / 180)
    length = int(length / 3) * 3

    row = int(length/24)
    col = (length - row * 24) / 3
    col = int(col) * 3
    length = (row << 5) + col

    print(int(length), ', //', line)

 

  • Like 3
Link to comment
Share on other sites

I've possibly taken this as far as I can go now. Here is a short test of scaling. I wanted to see how objects would look at various resolutions. Surprisingly good and recognisable, actually! Rotation/movement is the key to keeping it looking good, I think.

 

 

 

 

  • Like 7
Link to comment
Share on other sites

7 hours ago, Andrew Davie said:

I've possibly taken this as far as I can go now. Here is a short test of scaling. I wanted to see how objects would look at various resolutions. Surprisingly good and recognisable, actually! Rotation/movement is the key to keeping it looking good, I think.

 

 

 

 

?‍♂️That’s pure magic!

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...