.286
w equ word ptr
b equ byte ptr
surfclen equ 200                ;maximale Lnge der Oberflchendef.
Punktelen equ 4*100             ;Lnge des Point-Arrays
anz_fl equ 30                   ;maximale Anzahl Flchen
anz_eck equ 10                  ;maximale Anzahl Ecken
data segment                    ;externe Variablen aus Pascal-Teil
  extrn vz:word                 ;Gesamt-Tiefe
  extrn rotx:Word               ;Rotations-Winkel
  extrn roty:Word
  extrn rotz:word
  extrn worldconst:dataptr      ;Array mit Punkten
  extrn surfcconst:dataptr      ;Array mit Oberflchendefinitionen
  extrn lightsrc:word           ;Flag fr Lichtquellenschattierung
  extrn fl_sort:word            ;Flag fr Flchensortierung
  extrn fl_ruecken:word         ;Flag fr Flchenrckenunterdrckung
  extrn Texture:Byte            ;Flag fr Texturen
  extrn Fuellen:Byte            ;Flag fr Fllen / Drahtmodell

crotx dw 0                      ;x-, y- und z-Winkel als Offset auf
croty dw 0                      ;den jeweiligen Sinus-Wert
crotz dw 0

rotx_x dw 0                     ;x,y,z nach x-rot
rotx_y dw 0
rotx_z dw 0
roty_x dw 0                     ;nach y-rot
roty_y dw 0
roty_z dw 0
rotz_x dw 0                     ;nach z-rot, endgltig
rotz_y dw 0
rotz_z dw 0

startpoly dw 0                  ;Beginn d. Def. der aktuellen Flche

Punkte      dw Punktelen dup (0);nimmt fertig berechnete Koordinaten auf
Punkteptr   dw 0                ;Zeiger im Punkte-Array
Punkte3d    dw Punktelen dup (0);nimmt fertige 3d-Koordinaten auf (Textur)
mittel      dw anz_fl*2 dup (0) ;Verzeichnis der mittleren z-Werte
mittelptr   dw 0                ;Zeiger im Mittel-Array
n           dw 0,0,0,0,0,0      ;Normalenvektor 32 Bit
n_betr      dw 0                ;Betrag des Normalenvektors

extrn sinus:dataptr

data ends

extrn drawpol:near              ;zeichnet Flche als Drahtmodell
extrn fillpol:near              ;fllt Flche
extrn wurzel:near               ;berechnet Wurzel von ax

getdelta macro                  ;berechnet die beiden Flchenvektoren
  mov ax,poly3d[0]              ;x: Ursprungsecke
  mov delta2[0],ax              ;in delta2 zwischenspeichern
  sub ax,poly3d[8]              ;Differenz zum ersten Punkt bilden
  mov delta1[0],ax              ;und delta1 fertig

  mov ax,poly3d[2]              ;y: Ursprungsecke
  mov delta2[2],ax              ;in delta2 zwischenspeichern
  sub ax,poly3d[10d]            ;Differenz zum ersten Punkt bilden
  mov delta1[2],ax              ;und delta1 fertig

  mov ax,poly3d[4]              ;z: Ursprungsecke
  mov delta2[4],ax              ;in delta2 zwischenspeichern
  sub ax,poly3d[12d]            ;Differenz zum ersten Punkt bilden
  mov delta1[4],ax              ;und delta1 fertig

  mov bp,polyn                  ;letzten Punkt anwhlen
  dec bp
  shl bp,3                      ;jeweils 8 Byte
  mov ax,poly3d[bp]             ;x holen
  sub delta2[0],ax              ;Differerenz bilden
  mov ax,poly3d[bp+2]           ;y holen
  sub delta2[2],ax              ;Differerenz bilden
  mov ax,poly3d[bp+4]           ;z holen
  sub delta2[4],ax              ;Differerenz bilden
endm

setkoord macro quelle,offst     ;setzt fertig berechnete Screenkoord
.386
  mov ax,quelle                 ;Koordinate projizieren
  cwd
  shld dx,ax,7
  shl ax,7
  idiv cx
   add ax,offst                 ;Bildschirmmitte ist 0/0/0
  mov bx,Punkteptr              ;im Punkte-Array vermerken
  mov Punkte[bx],ax
  add Punkteptr,2               ;Array-Zeiger weiter
endm

z2cx macro tabofs               ;holt z-koordinate nach cx
  mov cx,tabofs + 4
  add cx,vz                     ;z-translation drauf
  mov bx,mittelptr              ;im Mittel-Array vermerken
  add mittel[bx],cx
endm

xrot macro zkoord,qkoord        ;rotiert qkoord um x, speichert in zkoord
.386
  mov bp,crotx                  ;winkel holen
  mov bx,[qkoord]
  shl bx,3                      ;x8, um auf Punkte-Eintrge zu allignen
  mov Punkteptr,bx

  sub bx,[qkoord]               ;insg. x6, um auf Welt-Eintrge zu allignen
  sub bx,[qkoord]
  add bx,offset worldconst      ;auf Welt setzen
  mov ax,[bx]                   ;x holen
  mov zkoord,ax                 ;und unverndert setzen

  mov ax,[bx+2]                 ;y holen
  imul w ds:[bp+60d]            ;*cos rotx
  shrd ax,dx,14d
  mov cx,ax                     ;in cx sichern
  mov ax,[bx+4]                 ;z holen
  imul w ds:[bp]                ;*-sin rotx
  shrd ax,dx,14d
  sub cx,ax
  mov zkoord+2,cx               ;y wert fertig und setzen

  mov ax,[bx+2]                 ;y holen
  imul w ds:[bp]                ;*sin rotx
  shrd ax,dx,14d
  mov cx,ax                     ;sichern in cx
  mov ax,[bx+4]                 ;z holen
  imul w ds:[bp+60d]            ;*cos rotx
  shrd ax,dx,14d
  add cx,ax
  mov zkoord+4,cx
endm

yrot macro zkoord,qkoord        ;rotiert qkoord um y, speichert in zkoord
  mov bp,croty                  ;winkel holen
  mov ax,qkoord+2               ;y holen
  mov zkoord+2,ax               ;und unverndert setzen

  mov ax,qkoord                 ;x holen
  imul w ds:[bp+60d]            ;*cos roty
  shrd ax,dx,14d
  mov cx,ax                     ;in cx sichern
  mov ax,qkoord+4               ;z holen
  imul w ds:[bp]                ;*sin roty
  shrd ax,dx,14d
  add cx,ax
  mov zkoord,cx                 ;x wert fertig und setzen

  mov ax,qkoord                 ;x holen
  imul w ds:[bp]                ;*-sin roty
  shrd ax,dx,14d
  mov cx,ax                     ;sichern in cx
  mov ax,qkoord+4               ;z holen
  imul w ds:[bp+60d]            ;*cos roty
  shrd ax,dx,14d
  sub ax,cx
  mov zkoord+4,ax
endm

zrot macro zkoord,qkoord        ;rotiert qkoord um z, speichert in zkoord
  mov bx,Punkteptr              ;Eintragung in 3d-Punkte-Array vorbereiten

  mov bp,crotz                  ;winkel holen
  mov ax,qkoord+4               ;z holen
  mov zkoord+4,ax               ;und unverndert setzen
  mov Punkte3d[bx+4],ax         ;auerdem im 3D-Array merken

  mov ax,qkoord                 ;x holen
  imul w ds:[bp+60d]            ;*cos rotz
  shrd ax,dx,14d
  mov cx,ax                     ;in cx sichern
  mov ax,qkoord+2               ;y holen
  imul w ds:[bp]                ;*-sin rotz
  shrd ax,dx,14d
  sub cx,ax
  mov zkoord,cx                 ;x wert fertig und setzen
  mov Punkte3d[bx],cx

  mov ax,qkoord                 ;x holen
  imul w ds:[bp]                ;*sin rotz
  shrd ax,dx,14d
  mov cx,ax                     ;sichern in cx
  mov ax,qkoord+2               ;y holen
  imul w ds:[bp+60d]            ;*cos rotz
  shrd ax,dx,14d
  add cx,ax
  mov zkoord+2,cx
  mov Punkte3d[bx+2],cx

endm

get_normal macro                ;berechnet Normalenvektor einer Flche
  mov ax,delta1[2]              ;a2*b3
  imul delta2[4]
  shrd ax,dx,4
  mov n[0],ax
  mov ax,delta1[4]              ;a3*b2
  imul delta2[2]
  shrd ax,dx,4
  sub n[0],ax
  mov ax,delta1[4]              ;a3*b1
  imul delta2[0]
  shrd ax,dx,4
  mov n[2],ax
  mov ax,delta1[0]              ;a1*b3
  imul delta2[4]
  shrd ax,dx,4
  sub n[2],ax
  mov ax,delta1[0]              ;a1*b2
  imul delta2[2]
  shrd ax,dx,4
  mov n[4],ax
  mov ax,delta1[2]
  imul delta2[0]
  shrd ax,dx,4
  sub n[4],ax                   ;Kreuzprodukt (=Normalenvektor) fertig

  mov ax,n[0]                   ;x1 ^ 2
  imul ax
  mov bx,ax
  mov cx,dx
  mov ax,n[2]                   ;+x2 ^ 2
  imul ax
  add bx,ax
  adc cx,dx
  mov ax,n[4]                   ;+x3 ^ 2
  imul ax
  add ax,bx
  adc dx,cx                     ;Summe in dx:ax
  push si
  call wurzel                   ;wurzel in ax
  pop si
  mov n_betr,ax                 ;Betrag des Normalenvektors fertig
endm

light macro                     ;bestimmt Helligkeit einer Flche
  mov ax,n[0]
  imul l[0]                     ;Lichtvektor * Normalenvektor
  mov bx,ax                     ;in cx:bx Summe bilden
  mov cx,dx
  mov ax,n[2]
  imul l[2]
  add bx,ax
  adc cx,dx
  mov ax,n[4]
  imul l[4]
  add ax,bx                     ;Skalarprodukt fertig in dx:ax
  adc dx,cx
  idiv l_betr                   ;durch l_betr divid.

  mov bx,n_betr                 ;und durch n_betr
  cwd
  shld dx,ax,5                  ;Werte von -32 bis +32
  shl ax,5d
  mov bp,startpoly              ;Adressierung der Flchenfarbe vorbereiten
  idiv bx                       ;Division durch Nenner
  inc ax
  or ax,ax
  js zugewandt                  ;wenn cos  positiv -> vom Licht abgewandt
  xor ax,ax                     ;also keine Beleuchtung
zugewandt:
  sub b polycol,al              ;cos<0 -> auf Grundfarbe addieren
endm

code segment
assume cs:code,ds:data

public drawworld

public linecount
public polycol
public polyn
public poly2d
public poly3d
linecount dw 0
polycol dw 3                    ;aktuelle Flchenfarbe
polyn   dw 0                    ;Anzahl tatschlich vorhandener Ecken
poly2d dw anz_eck*4 dup (0)     ;Ecken des zu zeichnenden Polygons
poly3d dw anz_eck*4 dup (0)     ;3D-Ecken

public Txt_Nr
Txt_Nr dw 0                     ;Nummer der aktuellen Textur

public delta1,delta2
delta1      dw 0,0,0            ;Ebenenvektoren
delta2      dw 0,0,0

l            dw 11d,11d,11d     ;Lichtvektor
l_betr      dw 19d              ;Betrag des Lichtvektors


drawworld proc pascal           ;zeichnet dreidimensionale Welt
  push ds
  push es
  push bp
  lea si,surfcconst             ;Oberflchen werden durch si adressiert
  mov mittelptr,0               ;im Mittel-Array mit 0 anfangen
  mov ax,ds:[rotx]              ;Winkel holen,
  shl ax,1                      ;als Speicheroffset umrechnen
  add ax,offset sinus
  mov crotx,ax                  ;und in Hilfsvariablen ablegen
  mov ax,ds:[roty]              ;genauso fr y
  shl ax,1
  add ax,offset sinus
  mov croty,ax
  mov ax,ds:[rotz]              ;und z
  shl ax,1
  add ax,offset sinus
  mov crotz,ax
npoly:                          ;Polygon-Schleife
  mov startpoly,si              ;fr sptere Verwendung sichern
  add si,2                      ;Farbe berspringen
  mov cx,[si]                   ;Anzahl Ecken holen
  mov linecount,cx              ;Zhler laden
  inc cx                        ;wegen geschlossener Flche
  mov w polyn,cx                ;in Punkte-Array eintragen
  add si,2                      ;weiter auf eigentliche Koordinaten

nline:
  xrot rotx_x,si                ;koordinaten rotieren um x
  yrot roty_x,rotx_x            ;um y
  zrot rotz_x,roty_x            ;und um z
  z2cx rotz_x                   ;z start holen

  setkoord rotz_x,160           ;Koordinaten schreiben
  setkoord rotz_y,100

  add si,2                      ;nchste Eckpunkt
  dec linecount                 ;Linienzhler weiter
  je polyok                     ;alle gezeichnet -> Schluss
  jmp nline                     ;sonst nchste Linie

polyok:
  mov bx,mittelptr              ;Mittelwert errechnen:
  mov ax,mittel[bx]             ;Summe holen
  mov cx,polyn
  dec cx
  cwd
  div cx                        ;und durch Anzahl Ecken teilen
  mov mittel[bx],ax             ;zurckschreiben
  mov ax,startpoly              ;auch "Nummer" der Flche schreiben
  mov mittel[bx+2],ax
  add mittelptr,4               ;und weiter
  cmp w [si+2],0                ;alle Polygone fertig ?
  je fertig
  jmp npoly

fertig:
  cmp b fl_sort,0               ;Flchen sortieren ?
  je kein_quicksort
  call quicksort pascal,0,bx    ;Feld von 0 bis aktuelle Position sortieren

kein_quicksort:
  mov mittel[bx+4],0            ;Abschluss setzen
  mov ax,cs                     ;Zielsegment setzen
  mov es,ax
  xor bx,bx                     ;mit erster Flche beginnen
npoly_draw:
  lea di,poly2d                 ;Ziel:Poly-Array
  mov bp,mittel[bx+2]           ;Zeiger auf Farbe und Punkte der Flche holen
  mov ax,ds:[bp]                ;Farbe holen und setzen
  mov polycol,ax

  mov texture,0                 ;Annahme: keine Textur
  cmp ah,0ffh                   ;Textur ?
  jne keine_textur
  mov texture,1                 ;ja, dann setzen
  mov b txt_nr,al               ;Nummer merken

keine_textur:
  mov b lightsrc,0              ;Annahme: keine Schattierung
  cmp ah,0feh                   ;Schattierung ?
  jne keine_Lichtquelle
  mov b lightsrc,1              ;ja, dann setzen

keine_Lichtquelle:
  add bp,2                      ;auf Anzahl positionieren
  mov cx,ds:[bp]                ;Anzahl Ecken holen
  mov polyn,cx                  ;in Poly-Array schreiben
npoint:
  add bp,2
  mov si,ds:[bp]                ;Zeiger auf tatschl. Punkt holen
  shl si,3                      ;3 Word Eintrge !
  add si,offset Punkte          ;und x/y von Punkte-Array in Poly-Koord.

  mov ax,[si+Punkte3d-Punkte]   ;3d-x holen
  mov es:[di+poly3d-poly2d],ax  ;3d-x setzen
  mov ax,[si+Punkte3d-Punkte+2] ;3d-y holen
  mov es:[di+poly3d-poly2d+2],ax;3d-y setzen
  mov ax,[si+Punkte3d-Punkte+4] ;3d-z holen
  mov es:[di+poly3d-poly2d+4],ax;3d-z setzen

  movsw                         ;2D-Koordinaten setzen
  movsw

  add di,4                      ;nchsten Poly2d-Eintrag
  dec cx                        ;alle Ecken ?
  jne npoint

  mov bp,polyn                  ;erste Ecke auf letzte kopieren
  shl bp,3                      ;auf ersten Punkt positionieren
  neg bp
  mov ax,es:[di+bp]             ;und kopieren
  mov es:[di],ax
  mov ax,es:[di+bp+2]
  mov es:[di+2],ax

  add di,poly3d-poly2d          ;das gleiche fr 3d-Koordinaten
  mov ax,es:[di+bp]             ;und kopieren
  mov es:[di],ax
  mov ax,es:[di+bp+2]
  mov es:[di+2],ax
  mov ax,es:[di+bp+4]
  mov es:[di+4],ax

  cmp fuellen,1                 ;Flche fllen ?
  jne lines

  getdelta                      ;ja, dann Delta1 und 2 berechnen
  cmp b lightsrc,0              ;Lichtquelle ?
  jne schattiere
  jmp kein_licht

schattiere:                     ;ja,
  push bx
  get_normal                    ;dann Normalenvektor
  light                         ;und Helligkeit berechnen
  pop bx

kein_licht:
  inc polyn                     ;Anzahl Ecken erhhen
  call fillpol                  ;Flche zeichnen

next:
  add bx,4                      ;nchste Flche anpeilen
  cmp mittel[bx],0              ;letzte ?
  je _npoly_draw                ;nein, dann weiter
  jmp npoly_draw

lines:
  push bx
  call drawpol                  ;Polygon zeichnen
  pop bx
  jmp next

_npoly_draw:
  pop bp                        ;und fertig
  pop es
  pop ds
  ret
drawworld endp


public quicksort
quicksort proc pascal unten,oben:word
;sortiert Mitten-Array nach Quicksort-Algorithmus

local schluessel:word
local links:word
  push bx
  mov bx,unten                  ;Mitte finden
  add bx,oben
  shr bx,1
  and bx,not 3                  ;auf 4er Blcke posit
  mov dx,mittel[bx]             ;Schluessel holen
  mov schluessel,dx
  mov  ax,unten                 ;rechts und links mit Grundwerten init.
  mov si,ax
  mov links,ax
  mov ax,oben
  mov di,ax

  mov dx,schluessel
links_naeher:
  cmp mittel[si],dx             ;grer als Schluessel -> weitersuchen
  jbe links_dran
  add si,4                      ;auf nchsten posit
  jmp links_naeher              ;und den berprfen
links_dran:
  cmp mittel[di],dx             ;kleiner als Schluessel -> weitersuchen
  jae rechts_dran
  sub di,4                      ;auf nchsten posit
  jmp links_dran                ;und den berprfen
rechts_dran:
  cmp si,di                     ;links <= rechts ?
  jg end_schl                   ;nein -> Teilbereich fertig sortiert
  mov eax,dword ptr mittel[si]  ;Mittelwerte und Positionen tauschen
  xchg eax,dword ptr mittel[di]
  mov dword ptr mittel[si],eax

  add si,4                      ;Zeiger weiterbewegen
  sub di,4
end_schl:
  cmp si,di                     ;links > rechts, dann weitermachen
  jle links_naeher
  mov links,si                  ;links sichern, wg. Rekursion
  cmp unten,di                  ;unten < rechts -> linken Teilbereich sort.
  jge rechts_fertig
  call quicksort pascal,unten,di;rekursiv hlften weiter sortieren
rechts_fertig:
  mov si,links                  ;oben > links -> rechten Teilbereich sort.
  cmp oben,si
  jle links_fertig
  call quicksort pascal,si,oben ;rekursiv hlften weiter sortieren
links_fertig:
  pop bx
  ret
quicksort endp

code ends
end