News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu
avatar_ervin

drawing lines with cpcTelera / SDCC (V1.0 RELEASE)

Started by ervin, 12:57, 19 July 16

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

ervin

Hi folks.

Wow, has that much time passed since my last CPC project?!?!
I've been away from the scene a bit since I finished RUNCPC last year, but I guess that old itch never goes away...

One thing I've always wanted to do is mess around with drawing lines, and since cpcTelera doesn't yet have any line drawing functions, why not give it a go?
I managed to get a mode 0 Bresenham routine working quite easily, but got stuck for some time on modes 1 and 2.

Fortunately I found this wonderful article:
CPCRULEZ > CODING > SDCC TUT'S PAR STEPHBB75 > Dessiner en mode 1

With a bit of help from google translate, I was able to quickly add mode 1 & 2 to my little test program.
8)

The attached dsk contains a binary compiled from pure SDCC (in cpcTelera), and therefore is not as fast as it will ultimately become.
I think the line drawing speeds are useful as-is for non-game applications, but for real-time situations (like games) it is too slow.

I've got quite a lot left to do.
- optimised vertical / horizontal / perfect-diagonal routines
- line clipping
- conversion of critical routines to asm

Line clipping is a tricky one.
I've researched 4 different techniques, each with their own pros and cons, but I'm not sure I want to go with any of them.
In a game, lines will likely be relatively short, so it might be more efficient to simply rush through Bresenham's without the pixel plotting, until the x,y are on-screen.

We shall see...

For anyone interested, here is the first version of the main.c source.
It's not pretty, as it's just experimenting at the moment.
I'm particularly keen on improving mode 1 and 2 speeds, but I don't really know how to yet (though I've seen hints from others - TFM? - in the past that it may be possible to use bit-level operations to really speed up mode 2).

[EDIT] There are some more tests included now. If anyone is interested in the latest source code at any time, please feel free to ask.


#include <cpctelera.h>

#define SCR_VMEM (u8*)0xC000

#define MODE0_LIMIT 159
#define MODE1_LIMIT 319
#define MODE2_LIMIT 639

void runTest();
void populate_yAddress();

void drawLine(i16,i16,i16,i16);
void drawLineMode0(i16,i16,i16,i16);
void drawLineMode1(i16,i16,i16,i16);
void drawLineMode2(i16,i16,i16,i16);

void putPixelMode0(u8,u8);
void putPixelMode1(u16,u8);
void putPixelMode2(u16,u8);

u8 const g_palette[16]={
    0x00, // black
    0x1a, // brightWhite
    0x0d, // white
    0x12, // brightGreen
    0x06, // brightRed
    0x02, // brightBlue
    0x18, // brightYellow
    0x00, // black
    0x00, // black
    0x00, // black
    0x00, // black
    0x00, // black
    0x00, // black
    0x00, // black
    0x00, // black
    0x00, // black
};

u8 lineColour;

u8 lineMask0_1A,lineMask0_1B;
u8 lineMask0_2A,lineMask0_2B;
u8 lineMask0_4A,lineMask0_4B;
u8 lineMask0_8A,lineMask0_8B;

u8 lineMask1_1A,lineMask1_1B,lineMask1_1C,lineMask1_1D;
u8 lineMask1_2A,lineMask1_2B,lineMask1_2C,lineMask1_2D;

u8 lineMask2_1A,lineMask2_1B,lineMask2_1C,lineMask2_1D,lineMask2_1E,lineMask2_1F,lineMask2_1G,lineMask2_1H;

u8 mode;
u8 testRun;

u8* yAddress[200];

void main(void){
    cpct_disableFirmware();

    cpct_fw2hw(g_palette,16);
    cpct_setPalette(g_palette,16);
    cpct_setBorder(g_palette[2]);

    while(1){
        runTest();

        while(1){
            cpct_scanKeyboard();

            if (cpct_isKeyPressed(Key_Esc)){
                break;
            }
        }
    }
}

void runTest(){
    i16 x,y;

    u8* pscr;

    cpct_setVideoMode(1);
    cpct_clearScreen_f64(0x00);

    pscr=cpct_getScreenPtr(SCR_VMEM,0,0);
    cpct_drawStringM1("Please select the test to run.",pscr+80+2,1,0);
    cpct_drawStringM1("(A) mode 0",pscr+240+2,1,0);
    cpct_drawStringM1("(B) mode 1",pscr+320+2,1,0);
    cpct_drawStringM1("(C) mode 2",pscr+400+2,1,0);
    cpct_drawStringM1("After the test, press ESC",pscr+560+2,1,0);
    cpct_drawStringM1("to return to this screen.",pscr+640+2,1,0);
    cpct_drawStringM1("(X) other mode 0",pscr+800+2,1,0);

    while(1){
        cpct_scanKeyboard();

        if (cpct_isKeyPressed(Key_A)){
            mode=0;
            testRun=0;
            break;
        }
        else if (cpct_isKeyPressed(Key_B)){
            mode=1;
            testRun=1;
            break;
        }
        else if (cpct_isKeyPressed(Key_C)){
            mode=2;
            testRun=2;
            break;
        }
        else if (cpct_isKeyPressed(Key_X)){
            mode=0;
            testRun=3;
            break;
        }
    }

    cpct_setVideoMode(mode);
    cpct_clearScreen_f64(0x00);

    populate_yAddress();

    // eye #1

    y=10;

    if (testRun==0){
        lineColour=3;

        for (x=8;x<=152;x+=4){
            drawLine(x,10,152,y);
            y+=5;
        }
    }
    else if (testRun==1){
        lineColour=1;

        for (x=16;x<=304;x+=8){
            drawLine(x,10,304,y);
            y+=5;
        }
    }
    else if (testRun==2){
        lineColour=1;

        for (x=32;x<=608;x+=16){
            drawLine(x,10,608,y);
            y+=5;
        }
    }

    // eye #2

    if (testRun==0){
        lineColour=4;
        x=152;

        for (y=10;y<=190;y+=5){
            drawLine(152,y,x,190);
            x-=4;
        }
    }
    else if (testRun==1){
        lineColour=3;
        x=304;

        for (y=10;y<=190;y+=5){
            drawLine(304,y,x,190);
            x-=8;
        }
    }
    else if (testRun==2){
        lineColour=1;
        x=608;

        for (y=10;y<=190;y+=5){
            drawLine(608,y,x,190);
            x-=16;
        }
    }

    // eye #3

    y=190;

    if (testRun==0){
        lineColour=5;

        for (x=152;x>=8;x-=4){
            drawLine(x,190,8,y);
            y-=5;
        }
    }
    else if (testRun==1){
        lineColour=1;

        for (x=304;x>=16;x-=8){
            drawLine(x,190,16,y);
            y-=5;
        }
    }
    else if (testRun==2){
        lineColour=1;

        for (x=608;x>=32;x-=16){
            drawLine(x,190,32,y);
            y-=5;
        }
    }

    // eye #4

    if (testRun==0){
        lineColour=6;
        x=8;

        for (y=190;y>=10;y-=5){
            drawLine(8,y,x,10);
            x+=4;
        }
    }
    else if (testRun==1){
        lineColour=3;
        x=16;

        for (y=190;y>=10;y-=5){
            drawLine(16,y,x,10);
            x+=8;
        }
    }
    else if (testRun==2){
        lineColour=1;
        x=32;

        for (y=190;y>=10;y-=5){
            drawLine(32,y,x,10);
            x+=16;
        }
    }

    if (testRun==3){
        lineColour=3;

        for (y=10;y<=190;y+=4){
            drawLine(10,y,150,y);
        }

        cpct_clearScreen_f64(0x00);
        lineColour=4;

        for (x=10;x<=150;x+=4){
            drawLine(x,10,x,190);
        }

        cpct_clearScreen_f64(0x00);
        lineColour=5;

        for (y=10;y<=70;y+=3){
            drawLine(20,y,140,y+120);
        }

        cpct_clearScreen_f64(0x00);
        lineColour=6;

        for (y=190;y>=130;y-=3){
            drawLine(20,y,140,y-120);
        }
    }
}

void populate_yAddress(){
    u8 i;

    for (i=0;i<=199;i++){
        yAddress[i]=cpct_getScreenPtr(SCR_VMEM,0,i);
    }
}

void drawLine(i16 x1,i16 y1,i16 x2,i16 y2){
    if (mode==0)
        drawLineMode0(x1,y1,x2,y2);
    else if (mode==1)
        drawLineMode1(x1,y1,x2,y2);
    else if (mode==2)
        drawLineMode2(x1,y1,x2,y2);
}

void drawLineMode0(i16 x1,i16 y1,i16 x2,i16 y2){
    i16 xn,yn;
    i16 dx,dy;

    // if line is left-x-space, discard it

    if ((x1<0) && (x2<0))
        return;

    // if line is right-x-space, discard it

    if ((x1>MODE0_LIMIT) && (x2>MODE0_LIMIT))
        return;

    // if line is above-y-space, discard it

    if ((y1<0) && (y2<0))
        return;

    // if line is below-y-space, discard it

    if ((y1>199) && (y2>199))
        return;

    // determine colour masks for mode 0

    if (lineColour&1){
        lineMask0_1A=128;
        lineMask0_1B=64;
    }
    else{
        lineMask0_1A=0;
        lineMask0_1B=0;
    }

    if (lineColour&2){
        lineMask0_2A=8;
        lineMask0_2B=4;
    }
    else{
        lineMask0_2A=0;
        lineMask0_2B=0;
    }

    if (lineColour&4){
        lineMask0_4A=32;
        lineMask0_4B=16;
    }
    else{
        lineMask0_4A=0;
        lineMask0_4B=0;
    }

    if (lineColour&{
        lineMask0_8A=2;
        lineMask0_8B=1;
    }
    else{
        lineMask0_8A=0;
        lineMask0_8B=0;
    }

    // single pixel

    if ((x1==x2) && (y1==y2)){
        putPixelMode0(x1,y1);
        return;
    }

    // swap endpoints if necessary

    if (x1>x2){
        x1^=x2;
        x2^=x1;
        x1^=x2;

        y1^=y2;
        y2^=y1;
        y1^=y2;
    }

    dx=x2-x1;
    dy=y2-y1;

    // line going down?

    if (dy>=0){
        // gentle line?
        // octant 7/3

        if (dy<=dx){
            yn=dx>>1;

            // plot valid pixels

            for (x1=x1;x1<=x2;x1++){
                putPixelMode0(x1,y1);
                yn+=dy;

                if (yn>=dx){
                    yn-=dx;
                    y1++;
                }
            }
        }

        // steep line?
        // octant 6/2

        else{
            xn=dy>>1;

            for (y1=y1;y1<=y2;y1++){
                putPixelMode0(x1,y1);
                xn+=dx;

                if (xn>=dy){
                    xn-=dy;
                    x1++;
                }
            }
        }
    }

    // line going up?

    else{
        // gentle line?
        // octant 0/4

        if (-dy<=dx){
            yn=dx>>1;

            for (x1=x1;x1<=x2;x1++){
                putPixelMode0(x1,y1);
                yn+=-dy;

                if (yn>=dx){
                    yn-=dx;
                    y1--;
                }
            }
        }

        // steep line?
        // octant 1/5

        else{
            xn=-dy>>1;

            for (y1=y1;y1>=y2;y1--){
                putPixelMode0(x1,y1);
                xn+=dx;

                if (xn>=-dy){
                    xn-=-dy;
                    x1++;
                }
            }
        }
    }
}

void drawLineMode1(i16 x1,i16 y1,i16 x2,i16 y2){
    i16 xn,yn;
    i16 dx,dy;

    // if line is left-x-space, discard it

    if ((x1<0) && (x2<0))
        return;

    // if line is right-x-space, discard it

    if ((x1>MODE1_LIMIT) && (x2>MODE1_LIMIT))
        return;

    // if line is above-y-space, discard it

    if ((y1<0) && (y2<0))
        return;

    // if line is below-y-space, discard it

    if ((y1>199) && (y2>199))
        return;

    // determine colour masks for mode 1

    if (lineColour&1){
        lineMask1_1A=128;
        lineMask1_1B=64;
        lineMask1_1C=32;
        lineMask1_1D=16;
    }
    else{
        lineMask1_1A=0;
        lineMask1_1B=0;
        lineMask1_1C=0;
        lineMask1_1D=0;
    }

    if (lineColour&2){
        lineMask1_2A=8;
        lineMask1_2B=4;
        lineMask1_2C=2;
        lineMask1_2D=1;
    }
    else{
        lineMask1_2A=0;
        lineMask1_2B=0;
        lineMask1_2C=0;
        lineMask1_2D=0;
    }

    // single pixel

    if ((x1==x2) && (y1==y2)){
        putPixelMode1(x1,y1);
        return;
    }

    // swap endpoints if necessary

    if (x1>x2){
        x1^=x2;
        x2^=x1;
        x1^=x2;

        y1^=y2;
        y2^=y1;
        y1^=y2;
    }

    dx=x2-x1;
    dy=y2-y1;

    // line going down?

    if (dy>=0){
        // gentle line?
        // octant 7/3

        if (dy<=dx){
            yn=dx>>1;

            // plot valid pixels

            for (x1=x1;x1<=x2;x1++){
                putPixelMode1(x1,y1);
                yn+=dy;

                if (yn>=dx){
                    yn-=dx;
                    y1++;
                }
            }
        }

        // steep line?
        // octant 6/2

        else{
            xn=dy>>1;

            for (y1=y1;y1<=y2;y1++){
                putPixelMode1(x1,y1);
                xn+=dx;

                if (xn>=dy){
                    xn-=dy;
                    x1++;
                }
            }
        }
    }

    // line going up?

    else{
        // gentle line?
        // octant 0/4

        if (-dy<=dx){
            yn=dx>>1;

            for (x1=x1;x1<=x2;x1++){
                putPixelMode1(x1,y1);
                yn+=-dy;

                if (yn>=dx){
                    yn-=dx;
                    y1--;
                }
            }
        }

        // steep line?
        // octant 1/5

        else{
            xn=-dy>>1;

            for (y1=y1;y1>=y2;y1--){
                putPixelMode1(x1,y1);
                xn+=dx;

                if (xn>=-dy){
                    xn-=-dy;
                    x1++;
                }
            }
        }
    }
}

void drawLineMode2(i16 x1,i16 y1,i16 x2,i16 y2){
    i16 xn,yn;
    i16 dx,dy;

    // if line is left-x-space, discard it

    if ((x1<0) && (x2<0))
        return;

    // if line is right-x-space, discard it

    if ((x1>MODE2_LIMIT) && (x2>MODE2_LIMIT))
        return;

    // if line is above-y-space, discard it

    if ((y1<0) && (y2<0))
        return;

    // if line is below-y-space, discard it

    if ((y1>199) && (y2>199))
        return;

    // determine colour masks for mode 2

    if (lineColour&1){
        lineMask2_1A=128;
        lineMask2_1B=64;
        lineMask2_1C=32;
        lineMask2_1D=16;
        lineMask2_1E=8;
        lineMask2_1F=4;
        lineMask2_1G=2;
        lineMask2_1H=1;
    }
    else{
        lineMask2_1A=0;
        lineMask2_1B=0;
        lineMask2_1C=0;
        lineMask2_1D=0;
        lineMask2_1E=0;
        lineMask2_1F=0;
        lineMask2_1G=0;
        lineMask2_1H=0;
    }

    // single pixel

    if ((x1==x2) && (y1==y2)){
        putPixelMode2(x1,y1);
        return;
    }

    // swap endpoints if necessary

    if (x1>x2){
        x1^=x2;
        x2^=x1;
        x1^=x2;

        y1^=y2;
        y2^=y1;
        y1^=y2;
    }

    dx=x2-x1;
    dy=y2-y1;

    // line going down?

    if (dy>=0){
        // gentle line?
        // octant 7/3

        if (dy<=dx){
            yn=dx>>1;

            // plot valid pixels

            for (x1=x1;x1<=x2;x1++){
                putPixelMode2(x1,y1);
                yn+=dy;

                if (yn>=dx){
                    yn-=dx;
                    y1++;
                }
            }
        }

        // steep line?
        // octant 6/2

        else{
            xn=dy>>1;

            for (y1=y1;y1<=y2;y1++){
                putPixelMode2(x1,y1);
                xn+=dx;

                if (xn>=dy){
                    xn-=dy;
                    x1++;
                }
            }
        }
    }

    // line going up?

    else{
        // gentle line?
        // octant 0/4

        if (-dy<=dx){
            yn=dx>>1;

            for (x1=x1;x1<=x2;x1++){
                putPixelMode2(x1,y1);
                yn+=-dy;

                if (yn>=dx){
                    yn-=dx;
                    y1--;
                }
            }
        }

        // steep line?
        // octant 1/5

        else{
            xn=-dy>>1;

            for (y1=y1;y1>=y2;y1--){
                putPixelMode2(x1,y1);
                xn+=dx;

                if (xn>=-dy){
                    xn-=-dy;
                    x1++;
                }
            }
        }
    }
}

void putPixelMode0(u8 nX,u8 nY){
    u8* vram=yAddress[nY]+(nX>>1);
    u8 remainder=nX%2;

    if(remainder==0)
        *vram=((((*vram&85)|lineMask0_1A)|lineMask0_2A)|lineMask0_4A)|lineMask0_8A;
    else
        *vram=((((*vram&170)|lineMask0_1B)|lineMask0_2B)|lineMask0_4B)|lineMask0_8B;
}

void putPixelMode1(u16 nX,u8 nY){
    u8* vram=yAddress[nY]+(nX>>2);
    u8 remainder=nX-((nX>>2)<<2);

    if(remainder==0)
        *vram=((*vram&=119)|lineMask1_1A)|lineMask1_2A;
    else if(remainder==1)
        *vram=((*vram&=187)|lineMask1_1B)|lineMask1_2B;
    else if(remainder==2)
        *vram=((*vram&=221)|lineMask1_1C)|lineMask1_2C;
    else
        *vram=((*vram&=238)|lineMask1_1D)|lineMask1_2D;
}

void putPixelMode2(u16 nX,u8 nY){
    u8* vram=yAddress[nY]+(nX>>3);
    u8 remainder=nX-((nX>>3)<<3);

    if(remainder==0)
        *vram=(*vram&=127)|lineMask2_1A;
    else if(remainder==1)
        *vram=(*vram&=191)|lineMask2_1B;
    else if(remainder==2)
        *vram=(*vram&=223)|lineMask2_1C;
    else if(remainder==3)
        *vram=(*vram&=239)|lineMask2_1D;
    else if(remainder==4)
        *vram=(*vram&=247)|lineMask2_1E;
    else if(remainder==5)
        *vram=(*vram&=251)|lineMask2_1F;
    else if(remainder==6)
        *vram=(*vram&=253)|lineMask2_1G;
    else
        *vram=(*vram&=254)|lineMask2_1H;
}

arnoldemu

Elite on BBC (not sure about CPC) used different line drawing methods depending on angle and length. You may find the same works good for you too.

In my unreleased rom game I drew lines and clipped them but the frame rate wasn't great (but then I was also doing some 3d calculations and the game stalled because of conversion between different coordinate systems and different fixed point representations).

I hope you do better :)



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

ervin

Interesting... I didn't know that about BBC Elite.

I've actually been wondering about your vector game - I've been looking forward to it for a long time.
Do you have any plans to revisit it?

arnoldemu

Quote from: ervin on 13:07, 19 July 16
Interesting... I didn't know that about BBC Elite.
ELITE by David Braben (Frontier Developments) Game - coffee and tea

Quote from: ervin on 13:07, 19 July 16
I've actually been wondering about your vector game - I've been looking forward to it for a long time.
Do you have any plans to revisit it?
Yes but later in the year.
My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

ervin


ervin

Alrighty, optimised routines for vertical, horizontal and 1:1 diagonal lines are done.
Now it's time to open the doors to the scary world of line clipping!
:o

ervin

#6
Quick question for everyone...
Do you think that (0,0) should be at the top-left of the screen, or at the bottom-left (in terms of pixels)?

BASIC uses the bottom-left, so I'm inclined to go with that.
It's the opposite to what screen address calculations give, but it's trivial to adjust the y coordinates to work the same way as they do in BASIC.

[EDIT] Actually, I've decided to go with top-left. That seems to be generally accepted these days as the default origin.
8)

roudoudou

Quote from: ervin on 12:57, 19 July 16- conversion of critical routines to asm



Before converting critical routines to asm, you have to optimise the algorithm.


The heavy part is the call to PutPixel.


But before remove the PutPixel, you must do the full clipping to be sure every pixels you will write will be on screen


If you ensure to always draw from top to bottom, then you have only two cases:
- step down and sometimes go to the left
- step down and sometimes go the the right


So the PutPixel routine becomes:
- init: compute the starting adress on screen and the starting mask (with the current position in the byte, 0/1 in mode 0, 0/1/2/3 in mode 1, 0/1/2/3/4/5/6/7 in mode 2)
- routine1: put pixel to current adress / jump to next line OR shift color mask (it's the same thing to do in all video mode, shifting) and possibly DECrease current adress when the position in the byte exceed 1,3 or 7*

- routine2: put pixel to current adress / jump to next line OR shift color mask (it's the same thing to do in all video mode, shifting) and possibly INCrease current adress when the position in the byte exceed 1,3 or 7*


I suggest to use a counter decrementing for both routines -> faster, you do not have to compare either test zero after dec.


--- this will be a huge work! ---


Once you've optimized the critical part of the algorithm, you may get some additionnal speed improvement with a native assembly code.


Finally, you will also trade all the clumsy code like the swapping coordinates (i have not red all the source but this one is absolutely not clever)
x1^=x2; x2^=x1; x1^=x2 (...)  -> use a temporary variable tmp=x1;x1=x2;x2=tmp; cause it's faster, and in assembly (maybe with the compile code too) will not require a tmp variable but a temporary register and will NOT compute the XOR for nothing.
My pronouns are RASM and ACE

ervin

@roudoudou
That's brilliant information and advice.
Thanks so much for all of that.
I'll study your suggestions and try to incorporate them.

AMSDOS

Quote from: ervin on 06:10, 20 July 16
Quick question for everyone...
Do you think that (0,0) should be at the top-left of the screen, or at the bottom-left (in terms of pixels)?

BASIC uses the bottom-left, so I'm inclined to go with that.
It's the opposite to what screen address calculations give, but it's trivial to adjust the y coordinates to work the same way as they do in BASIC.

[EDIT] Actually, I've decided to go with top-left. That seems to be generally accepted these days as the default origin.
8)


I don't know because I noticed you weren't using the Firmware, the Graphics used in BASIC come from the Firmware, so when something else is implemented (e.g. @Executioner Fast plot), all the graphical co-ordinates change.
* 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

ervin

#10
I've made some progress with the suggestions above from roudoudou.
(I particularly like the idea of shifting/rotating the colour masks, even though it was very tricky to implement).

I've implemented several of the ideas into one case for mode 0 lines, and although there hasn't (yet) been much of a speed improvement, there are now far fewer variables in that code block. This means that it will be much easier to convert the C code into efficient asm code. Really looking forward to that part.

But I've got a lot of other cases left to put these ideas into, for all 3 screen modes...

ronaldo

Nice work, @ervin . Line drawing is one feature that has already been requested for inclusion in CPCtelera, but it is tagged low priority (scheduled for CPCtelera 2.0). However, if you continue developing it and want to contribute it to the present development branch, that would be great :D.

And, of course, if you used it to make a vector game inspired in Elite, that would awesome :D.

AMSDOS

I'm not sure what Elite uses for it's vector graphics system, though apparantely Tankbusters is somehow using mode 3 to produce it's fast animation sequences. It's limited to 4 colours though.
* 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

ervin

Quote from: ronaldo on 09:09, 22 July 16
Nice work, @ervin . Line drawing is one feature that has already been requested for inclusion in CPCtelera, but it is tagged low priority (scheduled for CPCtelera 2.0). However, if you continue developing it and want to contribute it to the present development branch, that would be great :D .

And, of course, if you used it to make a vector game inspired in Elite, that would awesome :D .

It would be an absolute honour to have some of my code in cpctelera!
But I don't think my code is good enough...
We'll see how it goes.

ervin

Quote from: AMSDOS on 00:29, 23 July 16
I'm not sure what Elite uses for it's vector graphics system, though apparantely Tankbusters is somehow using mode 3 to produce it's fast animation sequences. It's limited to 4 colours though.

And let's not forget Dark Star and its sequel (Forbidden Planet?)!
They were phenomenal.

Axelay

Quote from: AMSDOS on 00:29, 23 July 16
I'm not sure what Elite uses for it's vector graphics system, though apparantely Tankbusters is somehow using mode 3 to produce it's fast animation sequences. It's limited to 4 colours though.


If I remember it correctly, it was an ACU article explaining what had been done in TankBusters and described it as mode 3, but it wasnt the hardware mode 3, it was mode 0 using a variation on the split bitplanes/dual playfields to have two 2bpp/4 colour mode 0 displays on a single 16kb screen, with the screen flipping achieved by changing the appropriate colours rather than the screen base address.  It drew the new display in hidden inks, set them visible and hid the inks used in the previous frame, then drew up the next frame in the newly hidden inks and so on.

ervin

Quote from: Axelay on 16:05, 23 July 16

If I remember it correctly, it was an ACU article explaining what had been done in TankBusters and described it as mode 3, but it wasnt the hardware mode 3, it was mode 0 using a variation on the split bitplanes/dual playfields to have two 2bpp/4 colour mode 0 displays on a single 16kb screen, with the screen flipping achieved by changing the appropriate colours rather than the screen base address.  It drew the new display in hidden inks, set them visible and hid the inks used in the previous frame, then drew up the next frame in the newly hidden inks and so on.

Ah yes, I remember that article.
I had no idea what any of it meant back then, and to be honest I don't understand it much now either!
8)

ervin

#17
Hi everyone.

Alrighty, work on clipping has begun.
Once all the clipping stuff is finished, I'll continue improving the code with the suggestions made by @roudoudou.

I've added a new download to the first post in this thread.

There are 6 tests available from the main menu.
Selecting a test will clear the screen and draw a box, which represents the test clipping region.
Moving the box around with the arrow keys will provide a "window" into the drawing underneath.

At the moment, I've got efficient clipping for lines which start/end completely to one side of the clipping region (i.e. left/right/above/below).
If a line fits that criteria, it is discarded instantly.

But the rest of the clipping is currently of the naive variety, where a pixel's (x,y) are checked, to see if the pixel is inside the clipping region.
This is HORRIBLY, UNBEARABLY SLOW, so I recommend running your emulator of choice at full speed.
(Big thanks to @Executioner for adding the SHIFT+F3/SHIFT+F4 options to WinAPE!)

As I put in better clipping, these checks will be removed, which should make things a heck of a lot faster.
8)

AMSDOS

Quote from: ervin on 12:21, 24 July 16
Ah yes, I remember that article.
I had no idea what any of it meant back then, and to be honest I don't understand it much now either!
8)


@Axelay could probably clarify better than me, I've read their comments as Tankbusters is just using Mode 0, which makes more sense as Mode 3 seems to be too slow for flipping one set of colours for the other set, based on the Demonstrations I've seen of it, though since it's assembly anything from "Back in the Day" could probably be seriously optimized now. For Tankbusters I think what's happening is it's Mode 0 to give it 16 colours, for the series of Rotating Shapes for example, I'm imagining everything being draw up or deleted as black and then the relevant pen is set as Visible.


I wrote something like this when I was first started writing my own Assembly which I still have, but the code I have doesn't look correct. I would of made a Screen Pattern, which altered when the inks switched, overall it was a clever way of animating as a series of colour cycles.
* 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

ervin

#19
Wow, this line-clipping stuff is tough!
So, so many cases I have to take into account, otherwise confusing, hard-to-solve crashes happen.

Making steady progress though...
Horizontal, vertical and 1:1 diagonals are now being drawn very quickly, and clipping nicely as well.
8)

All other lines also have endpoint clipping done.
Just starting-point clipping to go for these other lines.
And that's the toughest part of the clipping process!

ervin

Found a bad bug in the clipping of 1:1 diagonal lines.
Had me going nuts trying to find it!
But now that it's solved, I can move onto other ideas.

To fully test all the lines and clipping cases, I need to improve my testing techniques, which are too slow and take too long to set up.
I think what I should perhaps be doing is putting some lines on the screen (for example, perfect horizontals) and then letting the user move them around anywhere they like, including off the screen.
That way every line type will be testing against each clipping boundary.

I have to say, this is a very interesting and enjoyable project.
It's still running very slowly, being in pure C (apart from one asm function I wrote to rotate a byte), but I'm writing things in such a manner that conversion to asm should be relatively easy, and should result in huge speed gains.

arnoldemu

Quote from: ervin on 13:00, 29 July 16
I have to say, this is a very interesting and enjoyable project.
I am enjoying reading your posts :)
My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

ervin

Quote from: arnoldemu on 13:52, 29 July 16
I am enjoying reading your posts :)

Thanks!
Hopefully I don't get too wordy and start getting on peoples' nerves!
8)

Tai

Keep up the good work!  :)

ervin

Quote from: Tai on 15:13, 29 July 16
Keep up the good work!  :)

I will!
8)

I've now got a little testing routine that makes it much quicker to view the effects of having lines of different lengths being moved around the screen.
This should make all sorts of lines much quicker to test and complete clipping for.

I'm really looking forward to the optimisation phase.
I keep having to stop myself from being tempted to work on that prematurely!

Powered by SMFPacks Menu Editor Mod