Trickfilm mit dem C 64
In die vierte Dimension, die bewegte dreidimensionale Grafik, dringen Sie mit diesem Programm vor.
Sie können so mit einfachsten Mitteln Trickfilme mit verblüffenden Eigenschaften erstellen.



Die Idee zu diesem Programm kam uns, als wir zum zweiten Mal die Sendung »Das Bild, das aus dem Rechner kam« sahen.
Die erste noch in Comal geschriebene Version berechnete jeweils ein Bild und zeichnete es sofort. Das Ergebnis: nur alle 7 Sekunden ein Bild! Auch als erst alle Linien berechnet und dann gezeichnet wurden, konnte von einem flüssigen Bewegungsablauf keine Rede sein. Bei der jetztigen Version wird der Teil des Zeichnens von einer Maschinencode-Routine übernommen, die eine Geschwindigkeit von 3 bis 10 Bilder/s erlaubt. Die Unterschiede gegenüber anderen Trickfilmgeneratoren:
- Es muß nur ein Bild eingegeben werden, alle anderen Bilder berechnet der Computer aus diesem Bild.
- Auch komplizierte Bewegungsabläufe können mühelos erzeugt werden (zum Beispiel, um einen Körper um die Z- und X-Achse zu drehen, dabei heranzuholen und nach links zu bewegen, bis er den Bildschirm verläßt, braucht man nur eine DATA-Zeile!).
- Es können sehr viele (bis zu 255) und große Bilder gespeichert werden, da nur die Linienkoordinaten gespeichert werden.
- Es werden keine Basic-Erweiterungen benötigt.
In die vierte Dimension, die bewegte dreidimensionale Grafik, dringen Sie mit unserem Listing des Monats vor.
Der »3D-Movie Maker« muß in zwei Teilen eingegeben werden: zuerst das Maschinenprogramm, dann der Basic-Teil. Wenn das Maschinenprogramm eingegeben und gestartet wurde, so speichert es sich, falls kein Prüfsummenfehler auftrat, selbst als »TRICK.OBJ« ab. Man braucht sich nun nicht mehr darum zu kümmern. Bei dem Basic-Teil, der nun eingegeben werden kann, dürfen alle REM-Zeilen ersatzlos wegfallen. Beim Start des Basic-Teils wird automatisch der Maschinenteil, der sich auf Diskette befinden sollte, nachgeladen.
Bedienung - Eingabe des Körpers
Vor der Benutzung des Programmes, müssen die Punkte Verbindungs- und Bewegungsvorschrift des Körpers in den DATA-Zeilen ab 8000 festgelegt werden. Dies ist notwendig, damit bei Änderungen oder einem erneuten Start nicht alles wieder neu eingegeben werden muß. Die DATA-Zeilen, die das Listing momentan enthält, erzeugen den auf dem Bildschirm herumfliegenden Schriftzug »64’er«. Die Punkte werden mit X, Y und Z-Koordinaten eingegeben. Als Endmarke dient hier dreimal die 1000. Bei der Verbindungsvorschrift wird jeweils der Anfangs- und Endpunkt angegeben zum Beispiel: Von Punkt 1 nach Punkt2 = DATA 1,2. Hier dient zweimal die 1000 als Endmarkierung.
Beispiel: Man will ein Kreuz erzeugen.
Punkte: | DATA0,10,0,10,0,0,-10,0 |
DATA —10, 0,0, 1000,1000,1000 |
Verbindungsvorschrift: | DATA 1,3,2,4,1000,1000 |
Bei der Bewegungsvorschrift ist es allerdings etwas komplizierter. Hier müssen zweimal drei Verschiebungsarten, drei Drehungsarten und die Dauer des Vorgangs angegeben werden. Zuerst kommt die erste Verschiebung in X-, Y- und Z-Richtung, dann die Drehung um die drei Achsen, nun die zweite Verschiebung und zum Schluß die Dauer des Ganzen. Diese Reihenfolge hat folgenden Sinn:
Wird zuerstverschoben und dann gedreht, dreht sich die Figur um den Bildschirmmittelpunkt. Bei umgekehrter Reihenfolge dreht sich die Figur an beliebiger Stelle um ihren eigenen Mittelpunkt. Hier ein Beispiel einer DATA-Zeile:
DATA 0,0,0,5,0,0, 0,5,0, 10
Die Figur bewegt sich 10 Bilder lang um jeweils 5 Stellen nach oben und dreht sich dabei um jeweils fünf Grad um die X-Achse. Einen Zoomeffekt erreicht man durch Verschieben in der Z-Achse, so kann man zum Beispiel mit DATA 0,0,0,0,0,0,0,0,1,50 eine Figur langsam vergrößern. Hier bildet einmal die 1000 die Schlußmarkierung. Um die vielen verschiedenen Möglichkeiten der Bewegung zu entdecken, lohnt es sich, die verschiedenen Kombinationen auszuprobieren (und dabei mit einfachen Bewegungen anzufangen).
Bedienung — Ablauf des Programms
Der »3D-Movie-Maker« stellt vier Menüpunkte zur Wahl:
- 1. Erzeugen einer Grafik
Hierbei berechnet der Computer die Anzahl der Bilder und fragt diese noch einmal ab. Sollen alle Bilder gezeichnet werden, braucht nur RETURN eingegeben werden. Nun wird der Speicherbedarf berechnet; reicht der vorhandene Speicher von 23 KByte aus, so beginnt der Rechenvorgang. Dabei wird die Anzahl der fertigen Bilder angezeigt. Ist der Rechenvorgang beendet (bei 150 Bildern mit je 10 Punkten zirka eine halbe Stunde) kehrt das Programm ins Menü zurück. Durch das Drücken der Leertaste kann man sich während der Berechnungen den Film ansehen und dann durch nochmaliges Betätigen fortfahren.
- 2./3. Laden/ Speichern
Da das Erstellen eines Films recht lange dauert, kann man fertige Filme abspeichern und laden. Das Programm hängt an den Filenamen automatisch ein »GRA« an, so daß die Dateien in der Directory sofort erkennbar sind. Tritt ein Disk-Error beim Laden oder Speichern auf, landet man wieder in der »INPUT FILENAME«-Zeile. Hier noch ein Hinweis zur LOAD-Routine: Da das Einlesen von 23 KByte mit einer GET-Schleife nicht einwandfrei funktionierte und 15 Minuten dauerte, verwendet die jetzige Version die LOAD-Routine des Betriebssystems.
- 4. Abspielen
Beim Aufruf dieser Funktion wird man zuerst nach der Anzahl der Durchläufe (maximal 255) und nach der Anzahl der Bilder je Lauf gefragt. Beim letzteren braucht man nur RETURN eingeben, um alle Bilder ablaufen zu lassen.
Der Algorithmus
Das eigentliche Kernstück des Programms ist ein Algorithmus, der die Punkte um einen Winkel dreht, die X-, Y- und Z-Koordinaten in Bildschirmkoordinaten umrechnet und dann die außerhalb liegenden Teile der Linien wegstreicht. Beim Drehen (hier um die Z-Achse) werden erst X- und Y-Koordinaten in Radius und Winkel (im Bogenmaß) umgerechnet; bezogen auf den Koordinatenursprung (siehe Bild 1). Nun wird der Winkel, um den gedreht werden soll, addiert und die neuen Koordinaten werden aus dem neuen Winkel und dem Radius errechnet (siehe Bild 2).


Beim Umrechnen in Bildschirmkoordinaten wird einfach die Z-Koordinate auf die X und Y-Koordinate so aufgerechnet, daß Punkte, die weiter vorne liegen, vom Mittelpunkt wegrücken. Außerdem wird der Koordinatenursprung in die Bildschirmmitte verlegt. Durch dieses Verfahren wird die Figur mit einem Fluchtpunkt in der Mitte des Bildschirms dargestellt Das nun folgende Wegstreichen der außerhalb liegenden Linien und Linienstücke geschieht mit Hilfe einer Gradengleichung. Falls nur Teile der Geraden außerhalb des Bildschirms liegen, wird der äußerste Wert für X beziehungsweise Y angenommen und die fehlende Koordinate errechnet (siehe Bild 3).
Beschreibung der Maschinenroutine
Die Zeichenroutine des »3D-Movie-Makers« übernimmt die Verwaltung der Grafik und ist, zum Erreichen eines Trickfilmeffekts, ganz auf Geschwindigkeit ausgelegt Aus diesem Grund erhielt sie die folgenden Merkmale:
- Sie ist in Assembler geschrieben.
- Sie berechnet keine Punkte, sondern zeichnet nur.
- Die Koordinaten werden nicht geprüft, das heißt: das Basic-Programm darf keine »unmöglichen« Koordinaten übermitteln.
- Der Bildschirm wird nur so weit gelöscht, wie es nötig ist
- Die Zeropage-Addressierung wird in breitem Umfang benutzt.
- Der IRQ wird abgestellt.
Das wichtigste Mittel zur Erzeugung eines flüssigen Bilderablaufs und die Grundidee der Routine ist jedoch das »verdeckte Zeichnen«. Dafür werden zwei Bitmaps benötigt (bei unserem Programm ab $A000 und $E000). Während nun eine der beiden zu sehen ist, wird auf der anderen gezeichnet. Nun wird das neue Bild sichtbar gemacht und das alte, welches nicht mehr zu sehen ist, wird gelöscht. Dieser Vorgang wiederholt sich, bis alle Bilder abgearbeitet sind. Hier noch eine Anmerkung: die Lineroutine haben wir mit einigen Änderungen dem Artikel »Ein schneller Drawline-Algorithmus« aus dem 64’er, 4/84 entnommen. Die Funktion läßt sich am besten dort nachvollziehen.
(Armin und Dirk Biernaczyk/rg)Felder:
X(I), Y(I), Z(I) | Originalkoordinaten der Figur aus den DATA-Zeilen |
X1(I), Y1(I), Z(I) | Koordinaten beim Rechnen und für das POKEN |
P1(I), P2(I) | Verbindungsvorschriften aus den DATA-Zeilen |
Normale Variablen:
PO | Speicherpointer für fertige Daten |
Q-1 | Zähler für fertige Bilder |
A2 | Anzahl der Linien pro Bild |
A1 | Anzahl der Punkte pro Bild |
AN | Anzahl der Bilder |
P1, P2, P3, P4, PB, PA | Variablen zur Berechnung des zu löschenden Bildschirmbereichs |
WX, WY, WZ, XA, YA, ZA, XB, YB, ZB | Variablen für die Drehbewegungen und Verschieben (werden bei jedem Bild auf W1, ... aufaddiert) |
W1, W2, W3, X1, Y1, Z1, X2, Y2, Z2 | Momentane Lage des Körpers |
100 rem ----------------------------- 105 rem -- 3d-movie-maker -- 110 rem -- ein programm von: -- 115 rem -- dirk & armin biernaczyk -- 120 rem -- an der papenburg 41 -- 125 rem -- 4630 bochum 6 -- 130 rem -- tel.: ////// -- 135 rem ----------------------------- 140 rem (c) 1985 by armin & dirk 145 rem biernaczyk 150 : 160 rem ------------------ 170 rem --- hauptmenue --- 180 rem ------------------ 190 : 191 ifa=0thena=1:load"trick.obj",8,1 192 : 195 poke56,50:clr:rem speicher herab. 200 printchr$(147) 210 printspc(10)"** 3d-movie-maker **" 220 print:print:print:print 230 printspc(9)"1 - grafik erstellen" 235 print 240 printspc(9)"2 - grafik abspielen" 245 print 250 printspc(9)"3 - grafik laden" 255 print 260 printspc(9)"4 - grafik abspeichern" 262 print 264 printspc(9)"5 - ende" 265 : 270 getw$:ifw$<"1"orw$>"5"then270 280 w=val(w$) 285 ifw=5thenend 290 onwgosub1040,5040,6040,7040 300 goto200 310 : 315 : 320 : 1000 rem ----------------- 1010 rem --- erstellen --- 1020 rem ----------------- 1030 : 1040 gosub4540 :rem variablen 1050 gosub4040 :rem einlesen 1060 printchr$(147)"fertige bilder: " 1061 ifan*(a2*6+2)<23000then1080 1062 print"zu wenig speicherplatz" 1063 poke198,0:wait198,1:return 1070 : 1080 forq=1toan :rem anzahl der bilder 1085 gosub3040 :rem bewegen 1090 gosub2040 :rem berechnen 1095 getta$:ifta$=" "thenifq>1thensys49152,q-1,a2,1 1100 next 1110 q=q-1 1120 return 1130 : 1135 : 2000 rem ----------------- 2010 rem --- berechnen --- 2020 rem ----------------- 2030 : 2040 rem --- verschieben1 --- 2050 : 2060 fori=1toa1 2070 x1(i)=x(i)+x1:y1(i)=y(i)+y1 2080 z1(i)=z(i)+z1 2090 next 2100 : 2110 rem --- drehen --- 2120 : 2130 ifw1=0then2250 2140 fori=1toa1 2150 xd=x1(i):yd=y1(i) 2160 ifxd=0thenxd=1e-20 2165 ifyd=0thenyd=1e-20 2170 r=sqr(xd*xd+yd*yd) 2180 w=atn(yd/xd) 2190 ifxd>0andyd<0thenw=w+~*2:goto2210 2200 ifxd<0thenw=w+~ 2210 w=w+w1 2220 y1(i)=sin(w)*r:x1(i)=cos(w)*r 2230 next 2240 : 2250 ifw2=0then2370 2260 fori=1toa1 2270 zd=z1(i):yd=y1(i) 2280 ifzd=0thenzd=1e-20 2285 ifyd=0thenyd=1e-20 2290 r=sqr(zd*zd+yd*yd) 2300 w=atn(yd/zd) 2310 ifzd>0andyd<0thenw=w+~*2:goto2330 2320 ifzd<0thenw=w+~ 2330 w=w+w2 2340 y1(i)=sin(w)*r:z1(i)=cos(w)*r 2350 next 2360 : 2370 ifw3=0then2510 2380 fori=1toa1 2390 zd=z1(i):xd=x1(i) 2400 ifzd=0thenzd=1e-20 2405 ifxd=0thenxd=1e-20 2410 r=sqr(zd*zd+xd*xd) 2420 w=atn(xd/zd) 2430 ifzd>0andxd<0thenw=w+~*2:goto2450 2440 ifzd<0thenw=w+~ 2450 w=w+w3 2460 x1(i)=sin(w)*r:z1(i)=cos(w)*r 2470 next 2480 : 2490 rem --- umrechnen --- 2500 : 2510 fori=1toa1 2530 x1(i)=(x1(i)+x2)*1.01^(z1(i)+z2) 2540 y1(i)=(y1(i)+y2)*1.01^(z1(i)+z2) 2560 next 2565 rem 1.01 kann leicht geaendert 2567 rem werden 2580 : 2590 pa=0:pb=199 2600 po=po-2 2601 rem ----------------------------- 2602 rem --- ubergetretene linien --- 2603 rem --- berechnen und poken --- 2604 rem ----------------------------- 2610 fori=1toa2 2620 x0%=x1(p1(i)):y1%=y1(p1(i)) 2630 x2%=x1(p2(i)):y2%=y1(p2(i)) 2640 x1%=0:x3%=0:me=0 2641 ifx0%> 159andx2%> 159then2760 2642 ifx0%<-159andx2%<-159then2760 2643 ify1%<- 99andy2%<- 99then2760 2644 ify1%> 99andy2%> 99then2760 2650 ify1%=y2%then2711 2655 ifx2%=x0%then2690 2660 m=(y2%-y1%)/(x2%-x0%) 2670 b=-m*x0%+y1% 2680 goto2720 2690 ify1%>99ory1%<-99theny1%=99*sgn(y1%) 2700 ify2%>99ory2%<-99theny2%=99*sgn(y2%) 2710 goto 2760 2711 ifx0%>159orx0%<-159thenx0%=159*sgn(x0%) 2712 ifx2%>159orx2%<-159thenx2%=159*sgn(x2%) 2713 goto 2760 2720 ifx0%>159orx0%<-159thenx0%=159*sgn(x0%):y1%=m*x0%+b 2730 ifx2%>159orx2%<-159thenx2%=159*sgn(x2%):y2%=m*x2%+b 2740 ify1%>99ory1%<-99theny1%=99*sgn(y1%):x0%=(y1%-b)/m 2750 ify2%>99ory2%<-99theny2%=99*sgn(y2%):x2%=(y2%-b)/m 2760 ify1%>99ory1%<-99thenx1%=255:x0%=0:y1%=0:y2%=0:x2%=0:goto2810 2770 ifx0%>159orx0%<-159thenx1%=255:x0%=0:y1%=0:y2%=0:x2%=0:goto2810 2780 : 2790 x0%=x0%+160:x2%=x2%+160 2791 y1%=y1%+100:y2%=y2%+100 2793 ify1%>pathenpa=y1% 2794 ify2%>pathenpa=y2% 2795 ify1%<pbthenpb=y1% 2796 ify2%<pbthenpb=y2% 2799 ifx0%>255thenx0%=x0%-256:x1%=1 2800 ifx2%>255thenx2%=x2%-256:x3%=1 2805 : 2810 pokepo,x0%:pokepo-1,x1% 2820 pokepo-2,y1%:pokepo-3,x2% 2830 pokepo-4,x3%:pokepo-5,y2% 2840 po=po-6 2850 next 2860 : 2862 ifpa<pbthenpa=199:pb=0 2863 pa=40*((paor7)+1)/256+1 2864 pb=40*(pband248)/256 2866 poke po+6*a2+2,int(p1)-int(p3) 2868 poke po+6*a2+1,p3 2870 p1=p2:p2=pa:p3=p4:p4=pb 2872 printchr$(19)spc(16)q 2880 return 2890 : 2900 : 3000 rem --------------- 3010 rem --- bewegen --- 3020 rem --------------- 3030 : 3040 ife=0then3100 3050 w1=w1+wz:w2=w2+wx:w3=w3+wy 3060 x1=x1+xa:y1=y1+ya:z1=z1+za 3070 x2=x2+xb:y2=y2+yb:z2=z2+zb 3080 e=e-1:return 3090 : 3100 read xa,ya,za,wx,wy,wz,xb,yb,zb,e 3110 wx=wx*~/180:wy=wy*~/180 3120 wz=wz*~/180:ya=-ya:yb=-yb 3130 goto3050 3140 : 3150 : 4000 rem ---------------- 4010 rem --- einlesen --- 4020 rem ---------------- 4030 : 4040 i=0 4050 i=i+1 4060 read x(i),y(i),z(i) 4070 y(i)=-y(i) 4080 ifx(i)<1000then4050 4090 a1=i-1 4100 i=0 4110 i=i+1 4120 read p1(i),p2(i) 4130 ifp1(i)<1000then4110 4140 a2=i-1 4150 : 4155 an=0 4157 reada:ifa=1000then4180 4160 fori=1to9:reada:next 4170 an=an+a:goto4157 4180 restore 4190 reada,a,a 4200 ifa<1000then4190 4210 reada,a 4220 ifa<1000then4210 4230 : 4240 printchr$(147)"bilderzahl "an 4250 printchr$(19)spc(11);:input i 4260 ifi>255ori>anthen4240 4270 an=i 4280 return 4290 : 4300 : 4500 rem ----------------- 4510 rem --- variablen --- 4520 rem ----------------- 4530 : 4533 rem nach bedarf dimensionieren 4536 : 4540 dim x(50),y(50),z(50) 4560 dim x1(50),y1(50),z1(50) 4570 dim p1(50),p2(50) 4580 po=35839:p1=32:p2=32:p3=0:p4=0 4590 return 4600 : 4605 : 5000 rem ----------------- 5010 rem --- abspielen --- 5020 rem ----------------- 5030 : 5040 ifq>0anda2>0then5090 5050 printchr$(147):print:print:print 5060 printspc(5)"es gibt keine grafik" 5070 poke198,0:wait198,1:poke198,0 5080 return 5090 input"{clr}wievile durchlauefe";du 5091 ifdu>255ordu<1then5090 5092 print"{home}{down}{down}wieviele bilder "q 5093 print"{up}"spc(19);:inputi 5094 ifi>qori<1then5092 5100 : 5105 sys49152,i,a2,du:return 5110 : 6000 rem ------------- 6010 rem --- laden --- 6020 rem ------------- 6030 : 6040 printchr$(147):print:print:print:print 6050 input" filename: ";na$ 6055 ifna$="m"thenreturn 6060 open2,8,2,na$+".gra,s,r" 6070 open1,8,15:input#1,fe$ 6080 iffe$="00"then6090 6082 close1:close2:goto6040 6090 get#2,q$,a2$ 6100 q=asc(q$):a2=asc(a2$) 6110 ad=35839-q*(a2*6+2) 6140 close2:close1 6150 ah=int(ad/256):al=ad-ah*256 6160 a$=na$+".gra,s" 6170 fori=51000to51000+len(a$)-1 6180 pokei,asc(mid$(a$,i-50999,1)) 6190 next 6200 poke183,len(a$) 6210 poke187,56:poke188,199 6220 poke185,0:poke186,8:poke147,0 6570 poke195,al:poke196,ah 6580 sys62648 6590 return 6600 : 6610 : 6620 : 7000 rem ------------------- 7010 rem --- abspeichern --- 7020 rem ------------------- 7030 : 7040 ifq>0anda2>0then7090 7050 printchr$(147):print:print:print 7060 printspc(5)"es gibt keine grafik" 7070 poke198,0:wait198,1:poke198,0 7080 return 7090 printchr$(147):print:print:print 7100 input" filename: ";na$ 7105 ifna$="m"thenreturn 7110 open2,8,2,na$+".gra,s,w" 7120 open1,8,15:input#1,fe$ 7130 iffe$="00"then7140 7135 close1:close2:goto7090 7140 print#2,chr$(q);chr$(a2); 7150 fori=35839-q*(a2*6+2)to35839 7160 print#2,chr$(peek(i));:next 7170 close2:close1:return 7175 : 7180 : 7185 : 8000 rem ------------------------- 8010 rem --- datas fuer punkte --- 8020 rem ------------------------- 8030 : 8040 data -20, 10,0 8050 data -30, 10,0 8060 data -30,-10,0 8070 data -20,-10,0 8080 data -20, 0,0 8090 data -30, 0,0 8100 data -15, 10,0 8110 data -15, 0,0 8120 data - 5, 10,0 8130 data - 5, 0,0 8140 data - 5,-10,0 8150 data 10, 10,0 8160 data 5, 5,0 8170 data 15,-10,0 8180 data 5,-10,0 8190 data 5, 0,0 8200 data 15, 0,0 8210 data 15, -5,0 8220 data 5, -5,0 8230 data 20,-10,0 8240 data 20, 0,0 8250 data 20, -5,0 8260 data 30, 0,0 8900 data 1000,1000,1000 8910 : 9000 rem ---------------------------- 9010 rem --- verbindungsvoschrift --- 9020 rem ---------------------------- 9030 : 9040 data 1, 2, 2, 3, 3, 4, 4, 5 9050 data 5, 6, 7, 8, 8,10, 9,11 9060 data 12,13, 14,15, 15,16, 16,17 9070 data 17,18, 18,19, 20,21, 22,23 9450 data 1000,1000 9460 : 9500 rem -------------------------- 9510 rem --- bewegungsvoschrift --- 9520 rem -------------------------- 9530 : 9531 rem xa,ya,za,wx,wy,wz,xb,yb,zb,e 9550 data 0,0, 1 ,0,0,0 ,0,0, 0 ,30 9560 data 0,0, 0 ,0,5,0 ,0,0, 0 ,108 9570 data 0,0,-30 ,0,0,0 ,0,0,-30 ,1 9580 data 0,0, 0 ,5,0,5 ,0,0,.81 ,36 9620 data 1000