Raytrace

The place for codemasters or beginners to talk about programming any language for the Spectrum.
Post Reply
Alone Coder
Manic Miner
Posts: 401
Joined: Fri Jan 03, 2020 10:00 am

Raytrace

Post by Alone Coder »

Maybe everyone is familiar with this BASIC raycasting example that runs for 8 hours. I can't find its listing any more, but its algorithm has survived in C:

Code: Select all

#include <vcl.h>
#include <math.h>
#pragma hdrstop

#include "nedodefs.h"

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"

#define FLOAT int

#define SQRTFIXK 256L
#define FIXK (SQRTFIXK*SQRTFIXK)

#define spheres 2
FLOAT cx[spheres] = {
        (FLOAT)(-30*FIXK/100), (FLOAT)(90*FIXK/100)
};
FLOAT cy[spheres] = {
        (FLOAT)(-80*FIXK/100), (FLOAT)(-110*FIXK/100)
};
FLOAT cz[spheres] = {
        (FLOAT)(300*FIXK/100), (FLOAT)(200*FIXK/100)
};
FLOAT r[spheres] = {(FLOAT)(60*FIXK/100), (FLOAT)(20*FIXK/100)};
FLOAT q[spheres];

    FLOAT projx[spheres];
    FLOAT projy[spheres];
    FLOAT projr[spheres];

TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------

FLOAT umul(FLOAT a,FLOAT b)
{
  RETURN ((a>>8) * (b>>8));
}

FLOAT mul(FLOAT a,FLOAT b)
{
  FLOAT res;
  FLOAT ia;
  FLOAT ib;
  ia = a;
  ib = b;
  IF (ia < 0 /*>= 0x80000000L*/) {ia = -ia;};
  IF (ib < 0 /*>= 0x80000000L*/) {ib = -ib;};
  res = umul(ia, ib);
  IF ((a^b) < 0 /*>= 0x80000000L*/) {res = -res;};
  RETURN res;
}

FLOAT udiv(FLOAT a,FLOAT b)
{
  FLOAT ib;
  ib = b>>8;///SQRTFIXK;
  if (ib == 0L) {
    ib = 1L;
  };
  RETURN ((a / ib)<<8/**SQRTFIXK*/);
}

FLOAT idiv(FLOAT a,FLOAT b)
{
  FLOAT res;
  FLOAT ia;
  FLOAT ib;
  ia = a;
  ib = b;
  IF (ia < 0 /*>= 0x80000000L*/) {ia = -ia;};
  IF (ib < 0 /*>= 0x80000000L*/) {ib = -ib;};
  res = udiv(ia, ib);
  IF ((a^b) < 0 /*>= 0x80000000L*/) {res = -res;};
  RETURN res;
}

FLOAT add(FLOAT a,FLOAT b)
{
  RETURN (a + b);
}

FLOAT mul2(FLOAT a)
{
  RETURN add(a,a);
}

FLOAT sub(FLOAT a,FLOAT b)
{
  RETURN (a - b);
}

FLOAT neg(FLOAT a)
{
  RETURN (-a);
}

UINT lsqrt(FLOAT arg)
{
  BYTE count=0x10;
  UINT res=0;
  UINT tmp=0;
  IF (arg!=0L) {
/*    IF (!(arg&0xFF000000L)) {
      arg = arg<<8;
      count-=4;
    };*/
    res = 1;
    WHILE ((tmp<1)&&(count!=0x00)){
      DEC count;
      IF (arg&0x80000000L) {tmp|=2;};
      IF (arg&0x40000000L) {tmp|=1;};
      arg = arg << 2L;
    };//поиск первой 1-ы
    DEC tmp;

    WHILE (count!=0x00) {
      tmp = tmp << 2;
      res = res + res; //res << 1;
      DEC count;
      IF (arg&0x80000000L) {tmp = tmp | 2;};
      IF (arg&0x40000000L) {tmp = tmp | 1;};
      arg = arg << 2L;
      IF (tmp >= ((res<<1)|1)) {
        tmp = tmp - ((res<<1)|1);
        res = res | 1;
      };
    };
  };
  RETURN res;
}

FLOAT root(FLOAT a)
{
  RETURN ((FLOAT)lsqrt(a)*SQRTFIXK);
}

FLOAT fract(FLOAT a)
{
  RETURN ( a & (FIXK/2) /*!= 0L*/ );
}

BOOL positive(FLOAT a)
{
  RETURN (a >= 0 /*< 0x80000000L*/);
}

BOOL less(FLOAT a, FLOAT b)
{
  RETURN ((unsigned int)a < (unsigned int)b);
}


void TForm1::prhex(AnsiString name, int a)
{
  AnsiString s = "";
  AnsiString sc = " ";
  char c;
  for (int i = 0; i < 8; i++) {
    c = (a & 0x0f) + '0';
    if (c>=('0'+10)) c = c+'a'-('0'+10);
    sc[1] = c;
    s = sc + s;
    a = a >> 4;
  };
  Memo1->Lines->Add(name+s);
}

PROC TForm1::tracepixel(int i,int j)
{
  BYTE n;
  BYTE k;
  FLOAT s;
  FLOAT px;
  FLOAT py;
  FLOAT pz;
  FLOAT sc;
  FLOAT aa;
  FLOAT bb;
  FLOAT pp;
  FLOAT nx;
  FLOAT ny;
  FLOAT nz;
  FLOAT nn;
  FLOAT u;
  FLOAT v;
  FLOAT l;

  FLOAT x;
  FLOAT y;
  FLOAT z;
  FLOAT dx;
  FLOAT dy;
  FLOAT dz;
  FLOAT dd;

#define GROUND 0xfe
#define SKY 0xff
#define EYEX (FLOAT)(30*FIXK/100)
#define EYEY (FLOAT)(-50*FIXK/100)
#define EYEZ (FLOAT)(0*FIXK/100)

  x = EYEX;
  y = EYEY;
  z = EYEZ;
#define KVECTOR 64L
//  dx = (FLOAT)((FLOAT)(j-128)*FIXK/KVECTOR);
//  dy = (FLOAT)((FLOAT)(88-i)*FIXK/KVECTOR);
//  dz = (FLOAT)((FLOAT)(300)*FIXK/KVECTOR);

#define MULVECTOR 0x0400L
  dx = mul(((FLOAT)(j)-(FLOAT)(128))*FIXK, MULVECTOR);
  dy = mul(((FLOAT)(88)-(FLOAT)(i))*FIXK, MULVECTOR);
  dz = mul((FLOAT)(256)*FIXK, MULVECTOR);
  //prhex("dx=",dx);
  //prhex("dy=",dy);
  //prhex("dz=",dz);

  dd = add(mul(dx,dx),add(mul(dy,dy),mul(dz,dz))); //dx*dx+dy*dy+dz*dz;
  //prhex("dx*dx=",mul(dx,dx));
  //prhex("dy*dy=",mul(dy,dy));
  //prhex("dz*dz=",mul(dz,dz));
  //prhex("dd=",dd);

L100:
  IF (positive(y) || !positive(dy)) {
    n = SKY;
    s = 0x7fffffffL;
  }ELSE {
    n = GROUND;
    s = neg(idiv(y, dy)); //-y/dy; //???
  };
  //prhex("s=",s);

  k = 0x00;
  REPEAT { //ищем сферу, на которую смотрим (если есть)
    px = sub(cx[k], x); //c[k][0]-x;
    //prhex("px=",px);
    py = sub(cy[k], y); //c[k][1]-y;
    //prhex("py=",py);
    pz = sub(cz[k], z); //c[k][2]-z;
    //prhex("pz=",pz);

#define FIXX 256
#define SCRX (((FLOAT)(j)-(FLOAT)(128))*FIXX)
#define SCRY (((FLOAT)(88)-(FLOAT)(i))*FIXX)
    IF (!(
        (SCRX >= sub(projx[k],projr[k]))
      &&(SCRX < add(projx[k],projr[k]))
      &&(SCRY >= sub(projy[k],projr[k]))
      &&(SCRY < add(projy[k],projr[k]))
//      &&((i^j)&1)
       )) {
      //Image1->Canvas->Pixels[j][175-i] = 0xff7fff;
      goto L200; //не та сфера
      //goto RETURNME;
    };

    pp = add(mul(px,px),add(mul(py,py),mul(pz,pz))); //px*px+py*py+pz*pz;
  //prhex("px*px=",mul(px,px));
  //prhex("py*py=",mul(py,py));
  //prhex("pz*pz=",mul(pz,pz));
    //prhex("pp=",pp);
    sc = add(mul(px,dx),add(mul(py,dy),mul(pz,dz))); //px*dx+py*dy+pz*dz;
    //prhex("sc=",sc);
    IF (!positive(sc)) {
      goto L200; //не та сфера
    };
    bb = idiv(mul(sc,sc),dd); //sc*sc/dd;
    //prhex("bb=",bb);
    aa = q[k]-pp+bb; //add(sub(q[k],pp),bb); //q[k]-pp+bb;
    //prhex("aa=",aa);
    IF (!positive(aa)) {
      goto L200; //не та сфера
    };
    sc = idiv(sub(root(bb),root(aa)),root(dd)); //(sqrt(bb)-sqrt(aa))/sqrt(dd);
    //prhex("scp=",sub(root(bb),root(aa)));
    //prhex("scq=",root(dd));
    //prhex("sc=",sc);
//    IF (less(sc,s) /*|| (n >= 0x80)*/) { //нашли сферу
      n = k;
      s = sc;
      goto LFOUND;
//    };
L200:
    INC k;
  }UNTIL (k == spheres);
  IF (n == SKY) {goto RETURNME;}; //небо
LFOUND:
//    Image1->Canvas->Pixels[j][175-i] = 0x007fff;
//    goto RETURNME;
  dx = mul(dx,s); //dx*s;
  dy = mul(dy,s); //dy*s;
  dz = mul(dz,s); //dz*s;
  //prhex("dx=",dx);
  //prhex("dy=",dy);
  //prhex("dz=",dz);
  dd = mul(dd,mul(s,s)); //dd*s*s;
  //prhex("dd=",dd);
  x = add(x,dx);  //x+dx;
  y = add(y,dy); //y+dy;
  z = add(z,dz); //z+dz;
  IF (n == GROUND) {goto L300;}; //земля
  nx = sub(x,cx[n]); //x-c[n][0];
  ny = sub(y,cy[n]); //y-c[n][1];
  nz = sub(z,cz[n]); //z-c[n][2];
  nn = add(mul(nx,nx),add(mul(ny,ny),mul(nz,nz))); //nx*nx+ny*ny+nz*nz;
  //prhex("nn=",nn);
  l = idiv(mul2(add(mul(dx,nx),add(mul(dy,ny),mul(dz,nz)))),nn); //2*(dx*nx+dy*ny+dz*nz)/nn;
  //prhex("l=",l);
  dx = sub(dx,mul(nx,l)); //dx-nx*l;
  dy = sub(dy,mul(ny,l)); //dy-ny*l;
  dz = sub(dz,mul(nz,l)); //dz-nz*l;
  //prhex("dx=",dx);
  //prhex("dy=",dy);
  //prhex("dz=",dz);
  goto L100; //отражение

L300: //земля
  k = 0x00;
  REPEAT {
    u = sub(cx[k],x); //c[k][0]-x;
    v = sub(cz[k],z); //c[k][2]-z;
    IF (add(mul(u,u),mul(v,v)) <= q[k]) {goto RETURNME;};  //(u*u+v*v)
    INC k;
  }UNTIL (k == spheres);
  IF (fract(x) != fract(z)) {Image1->Canvas->Pixels[j][175-i] = 0xffffff;};   //((x+100)-(int)(x+100))  //(z-(int)(z))
RETURNME:
}

void __fastcall TForm1::FormCreate(TObject *Sender)
{
  BYTE i;
  BYTE j;
  BYTE k;

  k = 0x00;
  REPEAT {
    q[k] = mul(r[k],r[k]); //r[k]*r[k];
    INC k;
  }UNTIL (k == spheres);

  k = 0x00;
  REPEAT {
    projx[k] = idiv(sub(cx[k],EYEX), sub(cz[k],EYEZ));
    projy[k] = idiv(sub(cy[k],EYEY), sub(cz[k],EYEZ));
    projr[k] = idiv(r[k]*17/16, sub(cz[k],EYEZ)); //больше, потому что "бок" сферы торчит за проекцию
  prhex("projx=",projx[k]);
  prhex("projy=",projy[k]);
  prhex("projr=",projr[k]);
    INC k;
  }UNTIL (k == spheres);

  i = 0x00;
  REPEAT {
    j = 0x00;
    REPEAT {
      Image1->Canvas->Pixels[j][175-i] = 0x000000;
      tracepixel(i,j);
      INC j;
    }UNTIL (j == (BYTE)255);
    INC i;
  }UNTIL (i == (BYTE)175);

  cx[0] = cx[0]+(FLOAT)(10*FIXK/100);
  cz[1] = cz[1]+(FLOAT)(30*FIXK/100);

  Memo1->Lines->Add(IntToStr(0x100000000000000 >> 16));
}
There also was this pre-calculated demo with the same scene by Daniel Nagy: http://www.pouet.net/prod.php?which=80709
In 2018, I tried to made the same scene in 1K and under 1 minute (Daniel Nagy had over 5 minutes with his short floats - I lost his web page with the code), but ended at 72 seconds (in 16 bit arithmetics): http://alonecoder.nedopc.com/zx/raytrace1k.zip
The engine must remain universal for future applications. So, no hard-wired masking for "unused" screen zones. My final speedup was realtime calculated 2D masks for every object on the scene. I also tried simple animation, but the final version doesn't fit in 1K with both animation and masking.

Code: Select all

	device pentagon1024 ;don't trust this, it's for 48K :)
GO=0x6001;0x8001

SQUAREMUL=1
SQUARESQR=1
showtime=1
fastest=1
original_coords=1

IMVEC=0x7d00;0xae00;0xbe00
IMER=(IMVEC/256+1)*257
        
SQRTFIXK=256
FIXK=(SQRTFIXK*SQRTFIXK)
FIXKDIV2=(FIXK/2)

spheres=0x02

cx0=-(30*FIXK/100>>8) ;-30
cy0=-(80*FIXK/100>>8) ;-80
cz0=(300*FIXK/100>>8) ;300
r0=(60*FIXK/100>>8) ;60

        if original_coords
cx1=(83*FIXK/100>>8) ;90 ;88
cy1=-(120*FIXK/100>>8) ;-110 ;-114
cz1=(200*FIXK/100>>8)
r1=(20*FIXK/100>>8)
        else
cx1=(87*FIXK/100>>8) ;90
cy1=-(100*FIXK/100>>8) ;-110 (100 оптимизируется MegaLZ)
cz1=(200*FIXK/100>>8) ;200 (200 оптимизируется MegaLZ)
r1=(20*FIXK/100>>8) ;20
        endif

q0=(r0*r0>>8)
q1=(r1*r1>>8)
        
GROUND=0x3e;0x7f ;код команды ld a,N
SKY=0xff
EYEX=(30*FIXK/100)
EYEY=-(50*FIXK/100)
EYEZ=(0*FIXK/100)
KVECTOR=64
;EYEDZ=(300*FIXK/KVECTOR)
EYEDZ=(256*FIXK/KVECTOR)
EYEDZDIVFIXK=(EYEDZ/SQRTFIXK)
SQEYEDZ=(EYEDZDIVFIXK*EYEDZDIVFIXK) 

	macro SQDELOOP
;+de*+de
        ld hl,0
        ld d,l;0
;полное умножение требует 16(15) проходов, а мы сделаем по частям в 8(7) проходов
;0de0 * 0de0 = de0*d + de*e = (d*d<<16) + (d*e<<9) + (e*e)
;(d*e<<9) -> hl0
        rlca ;"d"
        jr nc,$+4
        ld l,e ;"0e"
        add hl,hl
        dup 6
        rlca ;"d"
        jr nc,$+3
        add hl,de ;"0e"
        add hl,hl
        edup
;hl0 = (d*e<<9)
        
        if 1==0 ;don't use bc (невыгодно по сравнению с sqde:ld iy,de:...:sqde:add iy,de:...:sqde:add iy,de
        ex de,hl
        ld h,tsqr/256+1
        ld h,(hl) ;(e*e)
         ld l,a
         ld a,h
        ld h,tsqr/256
        ld h,(hl) ;(d*d<<16)
        ld l,a
        add hl,de
        ex de,hl
        else
        
         ld c,a
        ld d,tsqr/256+1
        ld a,(de) ;(e*e)
        add a,l
        ld e,a
        ld b,tsqr/256
        ld a,(bc) ;(d*d<<16)
        adc a,h
        ld d,a
        endif
;out: 0de0
	endm

	macro SQDE
        ld a,d
        rlca
        jr nc,$+2+3+3
         xor a
         sub e
         ld e,a
         sbc a,d
         sub e
         rlca ;"d"
        if SQUARESQR
        scf
        rra
        ld d,a
        res 0,e
        ex de,hl
        ld e,(hl)
        inc l
        ld d,(hl)
	ld a,l
	cp 8 ;костыль для младшего бита
        rl e;sla e
        rl d
        ;ld a,(hl)
        ;inc l
        ;ld h,(hl)
        ;ld l,a
        ;add hl,hl ;выигрыш 1 байт по сравнению с оптимизированным
        ;ex de,hl
        else
         
	SQDELOOP
	endif
	endm
	
	macro SQHL
        ld a,h
        rlca
        jr nc,$+2+3+3
         xor a
         sub l
         ld l,a
         sbc a,h
         sub l
         rlca ;"h"
        if SQUARESQR
        scf
        rra
        ld h,a
        res 0,l
        ;ex de,hl
        ld e,(hl)
        inc l
        ld d,(hl)
	;ld a,l
	;cp 8 ;костыль для младшего бита
        rl e;sla e
        rl d
        ;ld a,(hl)
        ;inc l
        ;ld h,(hl)
        ;ld l,a
        ;add hl,hl ;выигрыш 1 байт по сравнению с оптимизированным
        ;ex de,hl
        else
         
	ex de,hl
	SQDE
	endif
	endm
	
        macro DIVLOOP
;(keep bc)
;hla = "0de"
;a = hla/bc0
;do 7 bits
;shift left hla, try sub, keep carry (inverted bit of result)
	rla;add a,a
	adc hl,hl ;no carry ;rl l нельзя
	sbc hl,bc
	jr nc,$+3
	add hl,bc
	rla
        dup 7
	adc hl,hl ;no carry
	sbc hl,bc
	jr nc,$+3
	add hl,bc
	rla
        edup
        cpl
        ld d,a ;"res_l"
;hl = "de"
;a = hla/bc0
;do 7 bits
;shift left hla, try sub, keep carry (inverted bit of result)
        dup 7
	add hl,hl ;no carry
	sbc hl,bc
	jr nc,$+3
	add hl,bc
	rla
        edup

	add hl,hl ;no carry
	sbc hl,bc
	;jr nc,$+3
	;add hl,bc
        rla
        cpl
	ld e,a
;out: de
        endm

        macro DIVDEBC_POSITIVE
;(keep bc)
        ld h,0
        ld l,d
        ld a,e
;hla = "0de"
        DIVLOOP
;out: de
        endm

        macro DIVDEBC_AXSIGN_NONEGBC
;(keep bc)
        ld a,d
        rla
        jr nc,$+2+6;idivnonegde
        xor a
        sub e
        ld e,a
        sbc a,d
        sub e
        ld d,a
;idivnonegde
;out: de
;idiv_0de0_00bc
        DIVDEBC_POSITIVE
        ex af,af' ;M=разные знаки
        jp p,$+3+6;ret p
        xor a
        sub e
        ld e,a
        sbc a,d
        sub e
        ld d,a
        ;ret
        endm

        macro DIVDEBC_ASIGN
        ex af,af' ;M=разные знаки
        ld a,b
        rla
        jr nc,$+2+6;idivnonegbc
        xor a
        sub c
        ld c,a
        sbc a,b
        sub c
        ld b,a
;idivnonegbc
        DIVDEBC_AXSIGN_NONEGBC
        endm

        macro ROOTHL
        if 1==1
    xor a
    ld b,a

    ld e,l
    ld l,h
    ld h,a

    sla l;add hl,hl
    sla l;add hl,hl
    ;cp h
    ;jr nc,$+5
    ;dec h
    ;ld a,4

    add hl,hl
    add hl,hl
    ld c,a
    sub h
    jr nc,$+6
    cpl
    ld h,a
    inc c
    inc c

    ld a,c
    add hl,hl
    add hl,hl
    add a,a
    ld c,a
    sub h
    jr nc,$+6
    cpl
    ld h,a
    inc c
    inc c

    ld a,c
    add hl,hl
    add hl,hl
    add a,a
    ld c,a
    sub h
    jr nc,$+6
    cpl
    ld h,a
    inc c
    inc c

    ld a,c
    ld l,e

    add hl,hl
    add hl,hl
    add a,a
    ld c,a
    sub h
    jr nc,$+6
    cpl
    ld h,a
    inc c
    inc c

    ld a,c
    add hl,hl
    add hl,hl
    add a,a
    ld c,a
    sub h
    jr nc,$+6
    cpl
    ld h,a
    inc c
    inc c

    ld a,c
    add a,a
    ld c,a
    add hl,hl
    add hl,hl
    jr nc,$+6
    sub h
    jp $+6
    sub h
    jr nc,$+6
    inc c
    inc c
    cpl
    ld h,a

     ld e,1
     
    ld a,l
    ld l,h
    add a,a
    ld h,a
    adc hl,hl
    adc hl,hl
    sll c 
    rl b
    sbc hl,bc
    jr nc,$+3
    add hl,bc
    sbc a,a 
    ;add a,a 
    ;inc a 
     or e;1
    add a,c 
    ld c,a

;iteration 9
    add hl,hl 
    add hl,hl
    sll c 
    rl b
    sbc hl,bc
    jr nc,$+3
    add hl,bc
    sbc a,a 
    ;add a,a 
    ;inc a 
     or e;1
    add a,c 
    ld c,a

    add hl,hl 
    add hl,hl
    sll c 
    rl b
    sbc hl,bc
    jr nc,$+3
    add hl,bc
    sbc a,a 
    ;add a,a 
    ;inc a 
     or e;1
    add a,c 
    ld c,a

    add hl,hl 
    add hl,hl
    sll c 
    rl b
    sbc hl,bc
    jr nc,$+3
    add hl,bc
    sbc a,a ;0/-1
    ;add a,a ;0/-2
    ;inc a ;+1/-1
     or e;1
    add a,c 
    ;ld c,a

    ;add hl,hl 
    ;add hl,hl
    ;sll c 
    ;rl b
    ;sbc hl,bc
    ;jr nc,$+3
    ;add hl,bc
    ;sbc a,a 
    ;add a,a 
    ;inc a 
    ;add a,c 
    ;ld c,a
;12th iteration completed
; output in BC
    ;srl b 
    ;rr c
    ld h,b
    ld l,a;c
        
        else
        
;root0hl0
        or a ;NC
        ld bc,64 ;c=64 (const), eb будет результат
        ld a,l
        ld l,h
        ld h,b;0
        ld e,b;0
        
;0hla=00NN
        dup 4
        sbc hl,bc
        jr nc,$+3
        add hl,bc
        ccf
        rl b
        rla
        adc hl,hl
        rla
        adc hl,hl
        edup
        
;ahl0=0??0
        sbc hl,bc
        jr nc,$+3
        add hl,bc
        ccf
        rl b
        rl e
        add hl,hl
        rla
        add hl,hl
        rla
        dup 3
        sbc hl,bc
        sbc a,e
        jr nc,$+4
        add hl,bc
        adc a,e
        ccf
        rl b
        rl e
        add hl,hl
        rla
        add hl,hl
        rla
        edup
        
;ahl0=???0
        dup 3
        sbc hl,bc
        sbc a,e
        jr nc,$+4
        add hl,bc
        adc a,e
        ccf
        rl b
        rl e
        add hl,hl
        rla
        add hl,hl
        rla
        edup
        sbc hl,bc
        sbc a,e
        jr nc,$+4
        add hl,bc
        adc a,e
        ccf
        ;rl b
        ;rl e
;eb=результат q=[sqrt N]
        ld l,b
        ld h,e
        adc hl,hl
;out: hl ;NC
        endif
        endm

        macro MULDEBCLOOP
;0de * 0bc = (de * b)<<8 + (de * c) => ah
;a=b<<1
;keep de! (можно портить биты 0,15)
        if SQUAREMUL
;d.e * b.c = 0.5*(d.e+b.c)^2 - 0.5*d.e^2 - 0.5*b.c^2
        rra
        ld b,a
        res 0,c
        ;set 7,d
        ;res 0,e

;адрес в таблице: %1hhhhhhh.lllllll0
        if 1==1
        ld h,d
        ld l,e
        add hl,bc ;d.e+b.c
        ;ld ($+4),hl
        ;ld hl,(0)
        ;set 7,b
        ;ld ($+6),bc
        ;ld bc,(0)
        ;sbc hl,bc
        ;ld ($+6),de
        ;ld bc,(0)
        ;sbc hl,bc
         set 7,b
         ex de,hl
         ld a,(bc)
         inc c
         add a,(hl)
         inc l
         ld lx,a
         ld a,(bc)
         adc a,(hl)
         ld b,a ;0.5*d.e^2 + 0.5*b.c^2
         ex de,hl
        ld a,(hl) ;0.5*(d.e+b.c)^2
        sub lx
        inc l
         ld c,a
        ld a,(hl)
        sbc a,b
         ;ld h,c
         ;add hl,hl ;11t = 1 s
;out: ah = 0.5*(d.e+b.c)^2 - 0.5*d.e^2 - 0.5*b.c^2
        else
        
        ld h,d
        ld l,e
        add hl,bc ;d.e+b.c
        ex de,hl
        ld a,(de) ;0.5*(d.e+b.c)^2
        inc e
        sub (hl) ;0.5*d.e^2
        inc l
         ld lx,a
        ld a,(de)
        sbc a,(hl)
         ld hx,a
        ex de,hl
        ;ix = 0.5*(d.e+b.c)^2 - 0.5*d.e^2
        ld h,b
        ld l,c
        set 7,h
        ld a,lx
        sub (hl)
        inc l
         ld c,a
        ld a,hx
        sbc a,(hl)
         ;ld h,c
;out: ac = 0.5*(d.e+b.c)^2 - 0.5*d.e^2 - 0.5*b.c^2
        endif
         
        else
;hl=0
        add a,a
        add a,a
        add a,a
        jr nc,$+5;4
         ld h,d
         ld l,e
         add hl,hl
        add a,a
        jr nc,$+3
        add hl,de
        dup 3;4
        add hl,hl
        add a,a
        jr nc,$+3
        add hl,de
        edup
        ld b,h ;b = hsb(de * b)
        ld h,a;0 ;l = lsb(de * b)
;c*de => ahl
         ld a,c
         ld c,h;0
        dup 7
        add hl,hl
        rla
        jr nc,$+4
        add hl,de
        adc a,c;0
        edup
        add hl,hl
        rla
        jr nc,$+3;4
        add hl,de
        ;adc a,c;0
;ahl = (de * c) + lsb(de * b)<<8
        adc a,b ;b = hsb(de * b)
        endif
;out: ah
        endm

        macro MULDEBC_SIGNED
;+de0 * +bc0 -> .de.
        ld a,d
        xor b
        ex af,af' ;M=разные знаки '
        ld a,d
        rla
        jr nc,$+2+6;mul_noneghld0
        xor a
        sub e
        ld e,a
        sbc a,d
        sub e
        ld d,a
;mul_noneghld0
        ld a,b
        add a,a
        jr nc,$+2+6;mul_nonegbcx0
        xor a
        sub c
        ld c,a
        sbc a,b
        sub c
        ;ld b,a
        add a,a
;mul_nonegbcx0
        if SQUAREMUL
        set 7,d
        res 0,e
        else
        ld hl,0
        endif
;0de * 0bc = (de * b)<<8 + (de * c) => de
;a=b<<1
         ;ld a,b
         ;add a,a
        MULDEBCLOOP
        if SQUAREMUL
         ld e,c
        else
         ld e,h
        endif 
         ld d,a
;out: de
        ex af,af' ;M=разные знаки '
        jp p,$+3+6;ret p
        xor a
        sub e
        ld e,a
        sbc a,d
        sub e
        ld d,a
        endm

        macro MULDEBC
        if fastest
        MULDEBC_SIGNED
        else
        call muldebc
        endif
        endm

        macro MULDEBC_TOIY
        if 1==0
        MULDEBC
        ld hy,d
        ld ly,e
        else
;+de0 * +bc0 -> .iy.
        ld a,d
        xor b
        ex af,af' ;M=разные знаки '
        ld a,d
        rla
        jr nc,$+2+6;mul_noneghld0
        xor a
        sub e
        ld e,a
        sbc a,d
        sub e
        ld d,a
;mul_noneghld0
        ld a,b
        add a,a
        jr nc,$+2+6;mul_nonegbcx0
        xor a
        sub c
        ld c,a
        sbc a,b
        sub c
        ;ld b,a
        add a,a
;mul_nonegbcx0
        if SQUAREMUL
        set 7,d
        res 0,e
        else
        ld hl,0
        endif
;0de * 0bc = (de * b)<<8 + (de * c)
;b*de => hl
         ;ld a,b
         ;add a,a
        MULDEBCLOOP
        if SQUAREMUL
         ld b,a;hy,a
;out: iy
        ex af,af' ;M=разные знаки '
        jp p,$+3+6;8;ret p
        xor a
        sub c
        ld c,a
        sbc a,b;hy
        sub c
        ld b,a;hy,a
	ld hy,b
        ld ly,c
        else
         ld e,h
         ld hy,a
;out: iy
        ex af,af' ;M=разные знаки '
        jp p,$+3+8;ret p
        xor a
        sub e
        ld e,a
        sbc a,hy
        sub e
        ld hy,a
        ld ly,e
        endif
        endif
        endm

        macro MULDEBC_POSITIVE
        if SQUAREMUL
        set 7,d
        res 0,e
        else
        ld hl,0
        endif
;0de * 0bc = (de * b)<<8 + (de * c)
;b*de => hl
        ld a,b
        add a,a
        MULDEBCLOOP
        if SQUAREMUL
         ld e,c
        else
         ld e,h
        endif
         ld d,a
;out: de
        endm

        macro MULDEBC_TOHL_POSITIVE
        if SQUAREMUL
        set 7,d
        res 0,e
        else
        ld hl,0
        endif
;0de * 0bc = (de * b)<<8 + (de * c)
;b*de => hl
        ld a,b
        add a,a
        MULDEBCLOOP
        if SQUAREMUL
         ld l,c
        else
         ld l,h
        endif
         ld h,a
;out: hl
        endm

        macro MULDEBC_TOHL_DEPOSITIVE
        if SQUAREMUL
        set 7,d
        res 0,e
        endif
;+de0 * +bc0 -> .hl. (keeps de)
        xor a
        if !SQUAREMUL
        ld l,a
        ld h,a
        endif
        xor b
        ex af,af' ;M=разные знаки '
        ld a,b
        add a,a
        jr nc,$+2+6;mul_nonegbcx0
        xor a
        sub c
        ld c,a
        sbc a,b
        sub c
        ;ld b,a
        add a,a
;mul_nonegbcx0
;0de * 0bc = (de * b)<<8 + (de * c)
;b*de => hl
         ;ld a,b
         ;add a,a
        MULDEBCLOOP
        if SQUAREMUL
         ld l,c
        else
         ld l,h
        endif
         ld h,a
;out: HL
        ex af,af' ;M=разные знаки '
        jp p,$+3+6;ret p
        xor a
        sub l
        ld l,a
        sbc a,h
        sub l
        ld h,a
        endm


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;        
	org GO
begin
        ;xor a
        ;out (0xfe),a
        ;ld de,0x5801
        ;ld h,d
        ;ld l,a
        ;ld bc,767
        ;ld (hl),7
        ;ldir

        if SQUAREMUL
;d.e * b.c = 0.5*(d.e+b.c)^2 - 0.5*d.e^2 - 0.5*b.c^2
;адрес в таблице: %1hhhhhhh.lllllll0
;максимум = 127^2/2 = #1f80
	;ex de,hl ;hl=0 after dehrum
        ld de,#8000
        ;ld sp,hl
        xor a ;сумма мл
        ;ld d,a
        ;ld e,a
        ;ld b,a
        ;ld c,a
        ;ld de,0 ;сумма
        ld bc,0 ;счётчик
        ld lx,a ;счётчик мл
mksquare
        ex de,hl
        ld (hl),e
        inc l
        ld (hl),d
        inc lx
        inc lx
        inc lx
        inc lx
        jr nz,$+3
        inc bc
        add a,lx
        ex de,hl ;тут лучше
        adc hl,bc
        ;ex de,hl
        
        ;inc hl
        ;inc h
        ;dec h
	inc de
	inc d
	dec d
        jr nz,mksquare
        endif

        if !SQUARESQR
        ld bc,1
        ld de,tsqr
        ;ld hl,0 ;выгоднее на MegaLZ и Hrum (после dehrum уже hl=0, de=rnd, bc=#1010)
mksqr0
        ex de,hl
        ld (hl),e
        inc h
        ld (hl),d
        dec h
        ex de,hl
        add hl,bc
        inc bc
        inc bc
        inc e
        jr nz,mksqr0
;e=0
;hl=0
;bc=0x201
        endif

        if showtime
        ld de,IMVEC;0xbe00 ;выгоднее на MegaLZ
        ld a,d
        ld i,a
        inc a
        ;ld a,IMER/256
        ld (de),a
        inc e
        jr nz,$-2
        inc d
        ld (de),a
        ld e,d
        ld hl,on_int.
         ld b,1
        ldir
        im 2
        ei
        else
        ;di
raytrace_begin
        ;ld a,0xaf
        endif

        if 1==1
        ld l,2*(spheres-1)+1
mkbounding0
        push hl
         dec l
        ld h,cz/256
        ld c,(hl)
        inc l
        ld b,(hl)
;de=cz
        ;ld hl,-EYEZ ;0
        ;add hl,de
        ;ld b,h
        ;ld c,l
;bc=sub(cz[k],EYEZ) >= 0
        ;pop hl
        ;push hl
        inc h ;ld h,rad/256
        ld d,(hl)
        dec l
        ld e,(hl)
        ld a,d
        or a
        ex af,af'
        DIVDEBC_AXSIGN_NONEGBC ;(keep bc)
        pop hl
        ld h,projr/256
        push hl
        ld (hl),e
        
        ld h,cx/256
        ld d,(hl)
        dec l
        ld e,(hl)
        ld hl,-((EYEX>>8)&0xffff)&0xffff
        add hl,de
        ex de,hl
        ld a,d
        or a
        ex af,af'
        DIVDEBC_AXSIGN_NONEGBC ;(keep bc)
        pop hl
        inc h
        push hl
        ;ld h,projx/256
        ld (hl),e
        
        ld h,cy/256
        ld d,(hl)
        dec l
        ld e,(hl)
        ld hl,-((EYEY>>8)&0xffff)&0xffff
        add hl,de
        ex de,hl
        ld a,d
        or a
        ex af,af'
        DIVDEBC_AXSIGN_NONEGBC ;(keep bc)
        pop hl
        ;inc h
        ;ld h,projy/256
         dec l
        ld (hl),e

        dec l
        jp p,mkbounding0
        
        endif
        
;  i = 0x00;
;  REPEAT {
;    //eyedy = ((FLOAT)(88)-(FLOAT)(i))<<10L;
;    //eyedy = mul(((FLOAT)(88)-(FLOAT)(i))*FIXK, MULVECTOR);
;    //sqeyedy = sq(eyedy);
;    count_eyedy();
;    j = 0x00;
;    REPEAT {
;      tracepixel(i,j);
;      INC j;
;    }UNTIL (j == 0x00);
;    INC i;
;  }UNTIL (i == 0xb0);
        ld a,0xaf
raytrace_lines0        
;//eyedy = ((FLOAT)(88)-(FLOAT)(i))<<10L;
;//eyedy = mul(((FLOAT)(88)-(FLOAT)(i))*FIXK, MULVECTOR);
        ld (tracepixel.i),a
        sub 88
         ld (scryHSB),a
        ld l,a
        SBC A,a
        ld h,a
        add hl,hl
        add hl,hl
        LD [eyedyUINT],hl
        SQHL
        LD hl,+(SQEYEDZ>>8)&0xffff
        add hl,de
	LD [sqeyedyUINT],HL
        
        ld a,(tracepixel.i)
        ld c,0
        call 8880
raytrace_pixels0
        push af
         push hl
        call tracepixel
         pop hl
        rl (hl)
        pop af
        inc a
        push af
        and 7
        jr nz,nonextbyte
        inc l
nonextbyte
        pop af
        jr nz,raytrace_pixels0
tracepixel.i=$+1
        ld a,0
        dec a;sub 1
        jp nz,raytrace_lines0
        if showtime
        ld iy,23610
        ld hl,10072
        exx
        im 1
_TIMER=$+1
        ld bc,0
        sla c
        rl b
        ld sp,0x5fe8
        ;ret

on_int.
        push hl
;_TIMER=$+1+IMER-on_int.
	ld hl,(_TIMER);0
	inc hl
	ld (_TIMER),hl
        pop hl
        ei
        ret
        else
	if 1==1
        halt
	else
        ;ld hl,cz
        ;ld a,(hl)
        ;sub 5
        ;ld (hl),a
        ;ld hl,cz+2
        ;ld a,(hl)
        ;add a,5
        ;ld (hl),a
        
        ld hl,cy
        inc (hl)
        ;inc hl
        ;inc hl
        ;dec (hl)
        jp raytrace_begin
	endif
        endif

tracepixel
;//x = EYEX;
;//y = EYEY;
;//z = EYEZ;
;//dx = ((FLOAT)(j)-(FLOAT)(128))<<10L; //dx = mul(((FLOAT)(j)-(FLOAT)(128))*FIX
;//dy = eyedy;
;//dz = EYEDZ; //mul((FLOAT)(300)*FIXK, MULVECTOR);
;//dd = sq(dx) + sqeyedy + SQEYEDZ; //sq(dz); //dx*dx+dy*dy+dz*dz;
;a = j
eyedyUINT=$+1
        LD hl,0 ;+(EYEDZ>>8)&0xffff и т.п. невыгодно
        LD [tracepixel.dyUINT],HL
        LD HL,+(EYEDZ>>8)&0xffff
        LD [tracepixel.dzUINT],HL
        LD hl,+(EYEX>>8)&0xffff
        LD [tracepixel.xUINT],hl
        LD hl,+(EYEZ>>8)&0xffff ;ld l,h проигрывает 2 байта в MegaLZ, 1 байт в Hrum
        LD [tracepixel.zUINT],hl
        LD hl,+(EYEY>>8)&0xffff
        LD [tracepixel.yUINT],hl
        sub l;128
         ld (scrxHSB),a
        ld l,a
        SBC A,a
        ld h,a
        add hl,hl
        add hl,hl
        ld (tracepixel.dxUINT),hl
        SQHL ;out: de
sqeyedyUINT=$+1
        LD hl,+(SQEYEDZ>>8)&0xffff;0
        add hl,de
        LD [tracepixel.ddUINT],HL
tracepixel.L100
;//IF (positive(y) || negative(dy)) { n = SKY; s = 0x7fffffffL; }ELSE { n = GROUND; s = neg(idiv(y, dy)); };

        LD BC,[tracepixel.dyUINT] ;лучшая перестановка
       ld hl,[tracepixel.yUINT]
       ld a,h
       cpl
       or b
        LD [tracepixel.sUINT+1],a
        JP M,skyorground_count_s.q
;P
         ex de,hl ;de=y
       DIVDEBC_ASIGN ;de / bc
        LD [tracepixel.sUINT],de
        LD A,GROUND
skyorground_count_s.q
        LD [tracepixel.n],A
        
        ld l,2*(spheres-1)+1
checkbounding0
        ld h,projr/256
        ld e,(hl) ;projr
        inc h;ld h,projx/256
scrxHSB=$+1
        ld a,GROUND;0
        sub (hl) ;a = SCRX - projx
        dec l
        add a,e ;a = SCRX - (projx-projr)
        jp m,tracepixel.NOTFOUNDb
        srl a
        cp e
        jp nc,tracepixel.NOTFOUNDb
        ;inc h;ld h,projy/256
scryHSB=$+1
        ld a,GROUND;0
        sub (hl) ;a = SCRY - projy
        add a,e ;a = SCRY - (projy-projr)
        jp m,tracepixel.NOTFOUNDb
        srl a
        cp e
        ;jr nc,tracepixel.NOTFOUNDb
        jr c,checkspheres
         ;ld a,(scrxHSB)
         ;ld hl,scryHSB
         ;xor (hl)
         ;rra
         ;ret c
tracepixel.NOTFOUNDb
        dec l
        jp p,checkbounding0
        jp checkspheresq
        
checkspheres
         ;jp tracepixel.NOTFOUND
	ld a,2*(spheres-1)
tracepixel.checkspheres0
	LD [tracepixel.k],A
;//ищем сферу, на которую смотрим (если есть)
;//px = cx[k] - x;
;//py = cy[k] - y;
;//pz = cz[k] - z;
;//pp = sq(px) + sq(py) + sq(pz);
;//sc = mul(px,dx) + mul(py,dy) + mul(pz,dz); 
       ld (count_pxpypzppsc_cxaddr),a
       ld (count_pxpypzppsc_cyaddr),a
       ld (count_pxpypzppsc_czaddr),a
       
;центр проекции сферы: cx[k]/pz, cy[k]/pz
;радиус проекции сферы: r[k]/pz
;    IF (!(
;        (SCRX >= sub(projx[k],projr[k]))
;      &&(SCRX < add(projx[k],projr[k]))
;      &&(SCRY >= sub(projy[k],projr[k]))
;      &&(SCRY < add(projy[k],projr[k]))
;       )) {
;      goto L200; //не та сфера
;    };
        
count_pxpypzppsc_czaddr=$+1
        ld hl,(cz)
tracepixel.zUINT=$+1
        LD bc,0;x0101
        ;or a
        SBC HL,bc
        push hl ;pz=cz[k]-z
        SQHL ;out: de
        ld hy,d
        ld ly,e ;sq(pz)
count_pxpypzppsc_cyaddr=$+1
        ld hl,(cy)
tracepixel.yUINT=$+1
        LD bc,0;x0101
        ;or a
        SBC HL,bc
        push hl ;py=cy[k]-y
        SQHL ;out: de
        add iy,de ;sq(pz) + sq(py)
count_pxpypzppsc_cxaddr=$+1
        ld hl,(cx)
tracepixel.xUINT=$+1
        LD bc,0;x0101
        SBC HL,bc
        push hl ;px=cx[k]-x
        SQHL ;out: de
        add iy,de
        LD [tracepixel.ppUINT],iy ;pp = sq(px) + sq(py) + sq(pz)
        
        pop bc ;px=cx[k]-x
tracepixel.dxUINT=$+1
        LD de,0;x1111
        MULDEBC_TOIY
        pop bc ;py=cy[k]-y
tracepixel.dyUINT=$+1
        LD de,0;x1111
        MULDEBC
        add iy,de
        pop bc ;pz=cz[k]-z
tracepixel.dzUINT=$+1
        LD de,0;x1111
        MULDEBC
        add iy,de ;sc = mul(px,dx) + mul(py,dy) + mul(pz,dz)
;//IF (negative(sc)) { goto L200; }; //не та сфера
;//bb = idiv(sq(sc),dd); //sc*sc/dd;
;//aa = q[k]-pp+bb; //add(sub(q[k],pp),bb); //q[k]-pp+bb;
;//IF (negative(aa)) { goto L200; }; //не та сфера
;//s = idiv(root(bb)-root(aa), root(dd)); //(sqrt(bb)-sqrt(aa))/sqrt(dd);
;//n = k;
;//goto LFOUND;
;//L200:
       ld d,hy
       ld e,ly ;de = sc = mul(px,dx) + mul(py,dy) + mul(pz,dz) ;ниже невыгодно в MegaLZ (2 байта)
	LD a,d
        or a;rla
	JP M,tracepixel.NOTFOUND ;выгоднее в MegaLZ
	if SQUARESQR
        set 7,d
        res 0,e
        ex de,hl
        ld e,(hl)
        inc l
        ld d,(hl)
	;ld a,l
	;cp 8 ;костыль для младшего бита
        rl e;sla e
        rl d
	else
        SQDE
	endif
tracepixel.ddUINT=$+1
	LD bc,0
        DIVDEBC_POSITIVE ;de = bb
        ld hl,(q)
tracepixel.k=$-2
checksphereradius_qaddr=$-2
        add hl,de ;q[k]+bb ;CY=0, т.к. q[k] (>=0) + bb (>=0)
tracepixel.ppUINT=$+1
	LD bc,0;x0101
        ;or a
        sbc hl,bc ;q[k]+bb-pp
	JP M,tracepixel.NOTFOUND
         push de ;bb
        ROOTHL
         ex (sp),hl ;push sqrt(aa), pop bb
        ROOTHL;call root0hl0
         pop bc ;sqrt(aa)
        ;or a
	SBC HL,bc ;hl = sqrt(bb)-sqrt(aa)
         push hl
	LD hl,[tracepixel.ddUINT]
        ROOTHL
        ld b,h
        ld c,l ;bc = sqrt(dd)
         pop de ;de = sqrt(bb)-sqrt(aa)
        DIVDEBC_POSITIVE ;sqrt(bb)-sqrt(aa) >= 0
	LD [tracepixel.sUINT],de ;s = (sqrt(bb)-sqrt(aa))/sqrt(dd)
	LD A,(tracepixel.k);0x3e
	LD [tracepixel.n],A
        ;de = s
	jr tracepixel.FOUND
tracepixel.NOTFOUND ;не та сфера
        ld a,(tracepixel.k)
	sub 2
	JP nc,tracepixel.checkspheres0
checkspheresq

	LD A,[tracepixel.n]
	or a ;SUB 0x80
	ret M ;nc ;//небо
tracepixel.sUINT=$+1
        LD de,0
tracepixel.FOUND
;de = s
;//dx = mul(dx,s); //dx*s;
;//dy = mul(dy,s); //dy*s;
;//dz = mul(dz,s); //dz*s;
;//dd = mul(sq(s),dd); //dd*s*s;
;//x = x + dx;  //x+dx;
;//y = y + dy; //y+dy;
;//z = z + dz; //z+dz;
        if SQUAREMUL
        set 7,d
        res 0,e
        endif
	LD bc,(tracepixel.dxUINT)
	MULDEBC_TOHL_DEPOSITIVE
        if SQUAREMUL
        dec e
        endif
	LD [tracepixel.dxUINT],hl ;dx = dx*s
	LD bc,[tracepixel.xUINT]
        add hl,bc
	LD [tracepixel.xUINT],hl ;x = x+dx
	LD bc,[tracepixel.dyUINT]
	MULDEBC_TOHL_DEPOSITIVE
        if SQUAREMUL
        dec e
        endif
	LD [tracepixel.dyUINT],hl ;dy = dy*s
	LD bc,[tracepixel.yUINT]
        add hl,bc
	LD [tracepixel.yUINT],hl ;y = y+dy
	LD bc,[tracepixel.dzUINT]
	MULDEBC_TOHL_DEPOSITIVE
        if SQUARESQR&SQUAREMUL
        dec e ;выигрыш 1 байт по сравнению с dec l
	endif
	LD [tracepixel.dzUINT],hl ;dz = dz*s
	LD bc,[tracepixel.zUINT]
        add hl,bc
	LD [tracepixel.zUINT],hl ;z = z+dz

        if SQUARESQR&SQUAREMUL
        ex de,hl
        ld e,(hl)
        inc l
        ld d,(hl)
	ld a,l
	cp 8 ;костыль для младшего бита
        rl e;sla e
        rl d
        else
	LD HL,[tracepixel.sUINT]
        SQHL ;out: de
        endif
	LD bc,[tracepixel.ddUINT]
        MULDEBC_TOHL_POSITIVE
	LD [tracepixel.ddUINT],hl ;dd = dd*s*s

tracepixel.n=$+1
	LD A,0x3e
	cp GROUND
	JP z,tracepixel.ground ;//земля
;//nx = x - cx[n];
;//ny = y - cy[n];
;//nz = z - cz[n];
;//l = idiv(mul2( mul(dx,nx) + mul(dy,ny) + mul(dz,nz)), /**nn*/sq(nx) + sq(ny) 
;//dx = dx - mul(nx,l); //dx-nx*l;
;//dy = dy - mul(ny,l); //dy-ny*l;
;//dz = dz - mul(nz,l);  //dz-nz*l;
        ld (count_reflection_cxaddr),a
        ld (count_reflection_cyaddr),a
        ld l,a
	LD h,cz/256
        ld c,(hl)
        inc l
        ld b,(hl)
	LD HL,[tracepixel.zUINT]
	SBC HL,BC
	LD [tracepixel.nz],HL ;z-cz[n]
        SQHL ;out: de
	ld hy,d
        ld ly,e
count_reflection_cyaddr=$+2
	LD bc,(cy)
	LD HL,[tracepixel.yUINT]
	SBC HL,BC
	LD [tracepixel.ny],HL ;y-cy[n]
        SQHL ;out: de
        add iy,de
count_reflection_cxaddr=$+2
	LD bc,(cx)
	LD HL,[tracepixel.xUINT]
	SBC HL,BC
	LD [tracepixel.nx],HL ;x-cx[n]
        SQHL ;out: de
        add iy,de
         push iy ;делитель = sq(nx)+sq(ny)+sq(nz)

tracepixel.nx=$+1
        LD bc,0;x0101
	LD de,[tracepixel.dxUINT]
	MULDEBC_TOIY
tracepixel.ny=$+1
        LD bc,0;x0101
	LD de,[tracepixel.dyUINT]
	MULDEBC
        add iy,de
tracepixel.nz=$+1
        LD bc,0;x0101
	LD de,[tracepixel.dzUINT]
	MULDEBC
        add iy,de
        ld d,hy
        ld e,ly
        rl e
        rl d ;for sign ;de = делимое = 2*(dx*nx+dy*ny+dz*nz)
         pop bc ;bc = делитель = sq(nx)+sq(ny)+sq(nz)
        ;ld a,d
        ;xor b
        ex af,af'
	DIVDEBC_AXSIGN_NONEGBC ;de / bc
         push de ;tracepixel.l = 2*(dx*nx+dy*ny+dz*nz)/(sq(nx)+sq(ny)+sq(nz)) ;со знаком

	LD bc,[tracepixel.nx] ;со знаком
	MULDEBC
        ld hl,(tracepixel.dxUINT)
        ;or a
        sbc hl,de;bc
        ld (tracepixel.dxUINT),hl ;dx-nx*l
         pop de
         push de ;tracepixel.l
	LD bc,[tracepixel.ny] ;со знаком
	MULDEBC
        ld hl,(tracepixel.dyUINT)
        ;or a
        sbc hl,de;bc
        ld (tracepixel.dyUINT),hl ;dy-ny*l
         pop de ;tracepixel.l
	LD bc,[tracepixel.nz] ;со знаком
	MULDEBC
        ld hl,(tracepixel.dzUINT)
        ;or a
        sbc hl,de;bc
        ld (tracepixel.dzUINT),hl ;dz-nz*l
	JP tracepixel.L100 ;//отражение
tracepixel.ground
;//земля
;//k = 0x00;
;//REPEAT {
;//  IF (less((sq(/**u*/cx[k]-x) + sq(/**v*/cz[k]-z)), q[k])) {goto RETURNME;}; 
;//  INC k;
;//}UNTIL (k == spheres);
;//IF (fract(x) != fract(z)) {
;//  invpix(j,i)
;//};   //((x+100)-(int)(x+100))  //(z-(int)(z))
	ld a,2*(spheres-1)
drawground_findshadow0
	LD [tracepixel.k],A
        ld (drawground_findshadow_qaddr),a
        ld (drawground_findshadow_cxaddr),a
	LD l,A
	LD h,cz/256
	LD E,[HL]
	INC L
	LD D,[HL]
	LD hl,[tracepixel.zUINT]
        sbc hl,de
        SQHL ;out: de
        ld hy,d
        ld ly,e
drawground_findshadow_cxaddr=$+2
        ld de,(cx)
	LD HL,[tracepixel.xUINT]
        sbc hl,de
        SQHL ;out: de
        add iy,de
	 ld d,hy
         ld e,ly
drawground_findshadow_qaddr=$+1
        ld hl,(q)
        sbc hl,de
        ret nc
	LD a,[tracepixel.k]
	sub 2
	JP nc,drawground_findshadow0

        ld a,(tracepixel.xUINT) ;дробная часть
        ld hl,tracepixel.zUINT
        xor (hl)
        rla
        ret
        
        if !fastest
muldebc
;+de0 * +bc0 -> .de.
        ld a,d
        xor b
        ex af,af' ;M=разные знаки '
        ld a,d
        rla
        jr nc,mul_noneghld0
        xor a
        sub e
        ld e,a
        sbc a,d
        sub e
        ld d,a
mul_noneghld0
        ld a,b
        rla
        jr nc,mul_nonegbcx0
        xor a
        sub c
        ld c,a
        sbc a,b
        sub c
        ld b,a
mul_nonegbcx0
        if SQUAREMUL
        set 7,d
        res 0,e
        endif
        MULDEBC_POSITIVE
        ex af,af' ;M=разные знаки '
        ret p
        xor a
        sub e
        ld e,a
        sbc a,d
        sub e
        ld d,a
        ret
        endif
        
        if !SQUARESQR
        ;ds (-$)&255
tsqr=0x7b00;0x7a00 ;код команды ld a,d (не помогает)
        ;ds 512 ;incbin "tsqr"
        endif

        align 256
q ;такой порядок выгоднее в MegaLZ
	DW q1
	DW q0
        align 256
cx
	DW cx1
	DW cx0
        align 256
cy
	DW cy1
	DW cy0
        align 256
cz
	DW cz1
	DW cz0
        align 256 ;сразу после cz
rad
	DW r1*17/16 ; //больше, потому что бок сферы торчит за проекцию
	DW r0*17/16
end
        align 256
projr
        dw 0 ;используется только старший байт
        dw 0
        align 256 ;projx и projy сразу после rad
projx
        dw 0 ;используется только старший байт
        dw 0
;        align 256
;projy
;        dw 0 ;используется только младший байт
;        dw 0

;end

	display "End=",end
	display "Free after end=",/d,#c000-end
	display "Size ",/d,end-begin," bytes"
	
	savebin "code.c",begin,end-begin
	
	LABELSLIST "..\us\user.l"
User avatar
ZXDunny
Manic Miner
Posts: 498
Joined: Tue Nov 14, 2017 3:45 pm

Re: Raytrace

Post by ZXDunny »

We messed around with this in SpecBAS:

Image

And it turned out very well. Still remains in BASIC and thus very readable.
Alone Coder
Manic Miner
Posts: 401
Joined: Fri Jan 03, 2020 10:00 am

Re: Raytrace

Post by Alone Coder »

My result looks far worse so far...
https://youtu.be/xAsypCPMzgI?t=5136
Post Reply