Lamers Heaven Ausgabe 1: Voxel Space ST Autor: Mike of STAX Willkommen zur ersten Ausgabe von Lamers Heaven ! EINLEITUNG: Da diese Serie erst neu ab dieser Undercover Edition eröffnet wurde, möchte ich Euch erst kurz was über Sinn und Zweck der ganzen Vorstellung erzählen. Ich bin Mike of STAX und wurde von Moondog/TNB dazu überredet, ihn beim Aufbau einer ST Coding Corner zu unterstützen. Vielleicht hat der ein oder andere schon mal was von der Gruppe STAX gehört, in der letzten Zeit aber bestimmt nicht viel, da es doch sehr ruhig um uns geworden ist. Neben Matt war/bin ich für Coding/Design und in alten Tagen auch mal für die ein oder andere Grafik zuständig gewesen - doch das ist ehrlich gesagt schon eine Weile her. So war ich den Tränen der Rührung nahe, als ich die alten Quellen hervorholte und analysierte (wie war das noch anno 94 ...???). Soviel muß gleich vorweg gesagt werden: sämtliche Codes haben schon ein ehrfurchtsvolles Alter (Copyright 1994 und frühes 1995), auch wurde nichts mehr daran ver- ändert oder gar optimiert. Fast alle Effekte sind mit der Intention entstanden, möglichst viele verschiedene FX möglichst bald zu haben (Grundlagenforschung !). Moondog/TNB zufolge gibt es noch ein paar hartgesottene ST Coder (auch die Polen sind ja schwer im kommen ...), so daß ich mich für die Idee einer Coding Ecke begeistern ließ. Ich selbst habe meine lehrreichen Lektionen noch aus der ehrwürdigen "Hexer" - Serie in der 68000er/ST-Magazin um die Jahre 88/89. In jenen Tagen drehte sich noch alles um das Öffnen der Ränder, Raster Interrupts und Plane FX. Wer hierzu mehr wissen und das Gefühl der ST- Hochzeiten spüren will, dem seien diese Ausgaben empfohlen (bei Interesse gebe ich gerne die genauen Heftnummer an - im Kopf habe ich die nicht mehr ...). Genauer betrachtet hat sich auf dem ST seit diesen Zeiten doch viel getan. Dominierten am Anfang noch Raster/Randeffekte, wurde im Anschluß daran die Copy-Leistung des ST's hinsichtlich Scrolling (Screen, Laufschriften) und Blöcken aller Art (Shapes, Sprites) ausgelotet. Heutzutage dreht sich fast alles um Pixel Effekte - also Veränderung von einzelnen Pixeln, natürlich möglichst viele (freilich gab es auch viele Sternenfeld Routinen, aber die aktuellen FX fordern viel mehr Punktveränderungen). In diese Gruppe gehören sicherlich die Zoomer, Rotierer, Texturemapper, Gouraudshader oder sogar Bumpmapper ... (die Liste kann beliebig fortgesetzt werden - siehe PC - aber für uns ST-Jünger ist bei der Rechenzeit irgendwo auch mal Schluß, in der Fantasie nicht ...). Lamers Heaven als Coding Ecke soll eine feste Instanz des beliebten Undercover Mags werden, ob ich immer dabei bin, ist allerdings fraglich. Auf jeden Fall schon mal viel Spaß mit dieser und den folgenden Ausgaben. THEMA Ich möchte hier hauptsächlich Effekte der Pixelgruppe vorstellen, aktuelles Thema in dieser, der ersten Ausgabe, ist: VOXELSPACE Einige Anmerkungen zur Vorgehensweise. Ich werde keine kompletten Quellen mitliefern, sondern neben allegemeinen Erklärungen und detailierten Hinweisen nur Schlüsselteile des Codings vorstellen. Wer sich etwas damit beschäftigt, kommt damit sicherlich zurecht. Ausführbare PRG Files schon eher (leider hat die Zeit diesmal nicht mehr gereicht, sowas auf die Beine zu stellen - folgt noch) - schlüsselfertige Quellen, wie gesagt nicht. Manche der vorgestellten Methoden oder Tricks sind nicht auf meinem Mist gewachsen. So ist diesmal ein Trick beim Ablegen der Raydaten (folgt noch ...) und deren Berechnungs-routine nicht 100% original von mir. Manchmal bringt einen ein Gespräch mit anderern ST Leidensgenossen auf neue Ideen ... Motivation "Hallo Mike, hast Du schon Commanche gesehen, kuck doch mal her !". Tja, und da habe ich dann mal auf den PC Monitor geschaut - und gestaunt. Das ist ja echt übel gewesen. Ich habe dann spontan auf dem ST rumprobiert, aber da kam nix raus. Erst später wollte es dann klappen - und diesmal haben die PCler auch etwas blöd geschaut. Soviel vorweg: natürlich kann man das Vorbild nicht total erreichen, aber ein bißchen was geht immer ... Wer noch keine Voxelspace Routine gesehen hat, hier kurz ein Überblick: Voxelspace ist eine Technik mit der man 2D Aufnahmen mit einfachen Mitteln als 3D Modelle bzw. Volumenkörper anzeigen kann (Voxel = Volume Pixel), indem man diesen Aufnahmen mit den Voxeln räumliche Tiefe verleiht.Gern benutzt um über Landschaften zu gleiten, deren Ursprung oftmals Satellitenaufnahmen sind; wird auch in der Medizin - IT vielfach eingesetzt. In unserem Fall soll es um die Darstellung von Landschaften gehen, über die man mit einem Fluggerät gleiten kann. Ausgehend vom PC Commanche ergeben sich folgende Anforderungen: 1) Möglichst große Landkarte 2) Nuancenreich sowohl bei der Auflösung der Voxel als auch bei der naturgetreuen Farb-gebung 3) Hohe Geschwindigkeit der Animation 4) Freie Bewegbarkeit des Gleiters in alle Richtungen mit Drehung 5) Veränderbarkeit der Flughöhe des Gleiters Grundlagen Üble Geschichte, oder ? Ok, gehen wir mal durch, was wir alles brauchen und warum. 1) Landkarte Ausganspunkt ist eine 2D Landkarte, die wir dreidimensional darstellen wollen. Die einzelnen Pixel haben eine bestimmte Farbe. Den Pixeln wird nun noch eine Höhe zugewiesen. Damit steht schonmal die grundlegende Datenstruktur. Wenn wir so über die Landschaft düsen, sehen wir immer einen Ausschnitt aus unserer Karte. Um es erstmal einfach zu machen, stellen wir uns diesen Ausschnitt als Rechteck dar, der ausgelesen und auf dem Screen dargestellt wird. Unser Landkartenausschnitt könnte so aussehen (hier mal nur 4x4 Pixel, von oben betrachtet): M N O P I J K L E F G H A B C D /\ || Betrachter Stellt man sich nun jeden Pixel als einen kleinen Turm vor mit einer bestimmten Höhe, so sieht man vorne die erste Zeile mit den Türmen A,B,C,D. Sind diese etwas niedriger, sieht man auch die Türme dahinter, vielleicht sogar die MNOP Turmreihe. Die Türme werden zusätzlich in ihrer Höhe entsprechend ihrer Entfernung vom Betrachter perspektivisch angepaßt. Ein Turm weiter hinten ist zwar kleiner in seiner eigenen Höhe, aber beginnt am Boden auch etwas weiter oben. Am besten selbst mal in die Ferne sehen, um diese Perspektive in der Praxis zu sehen. Ok, unsere Pixel werden also zu Türmchen mit einer bestimmten Farbe und Höhe (diese beiden Informationen bilden zusammen unsere erste Voxel Datenstruktur). Wie man die Türmchen zeichnet, werden wir gleich kennenlernen. Man baut den Screen in Spalten auf. Jede X Spalte wird einzeln aufgebaut, bis der Screen- inhalt steht. Ausgehend von unserem Beispiel betreffen die Pixel A, E, I, M (Pixel + Höhe, ab jetzt Voxel = Turm genannt) unsere erste Scanspalte. Wir beginnen in jeder Screenspalte unten mit dem Zeichnen, die Landkarte gehen wir von vorne nach hinten durch. In unserem Fall also Turm A. Dieser habe die Höhe 2 , also werden zwei Pixel mit der Farbe A von unten nach oben gezeichnet (Schritt 1). Spalte 1 (Spalte ganz links zeigt die Zeile (Höhe) an): Schritt 1 Schritt 2 Schritt 3 Schritt 4 6 5 4 M 3 E E E 2 A A A A 1 A A A A Turm E habe die Höhe 3, da wir uns weiter vom Betrachter wegbewegen, wird die Höhe auf 2 angepaßt, das Grundoffset sei 1, 2+1 = 3 (immer von unten gerechnet !). Jetzt möchten wir aber den Turm A nicht übermalen, da der ja eigentlich vor uns steht und somit ja als erstes sichtbar sein müßte. Man merkt sich nach jeder Zeichenoperation die Höhe des Vorgängers und vergleicht sie mit der neu zu zeichnenden Höhe. Ist die neue Höhe kleiner als die alte, muß ja nichts gezeichnet werden, da der neue Turm hinter dem Vordermann und somit un-sichtbar für den Betrachter ist. Liegt sie über dem Vorgängerturm, zeichnet man nur die Höhendifferenz darüber (Schritt 2). Voxel I hat die Höhe 2, nach Perspektivenanpassung nur noch 1, Grundoffset sei 2, macht 3. Da der Vorgängerturm schon die Zeichenhöhe von 3 hatte ist der neue Turm nicht sichtbar (Schritt 3). Turm M sei wieder sichtbar (Schritt 4). Somit ist eine Bildschirmspalte gefüllt. Das gleiche Ver- fahren wird nun auf alle Spalten angewendet (Spaltenberechnung mit Voxeln B, F, J, N, usw.) bis der Landschaftscreen steht (siehe auch Grafik TURM.IFF). Noch ein Satz zur Voxeldatenstruktur. Da pro Voxel Höhe + Farbe anfallen (angenommen, wie auf dem PC 256 Farben = 1 Byte + 256 Höhen = 1 Byte -> 2 Byte pro Voxel) kommt da für eine ordentliche Landschaft schon was zusammen: 512 X 512 in der Ausdehnung macht 512x512x2 = 524 KB und das ist noch keine große Landschaft ! Bei Commanche waren die Maps größer (ca. 1024x1024 = 2MB), für den ST ist das indiskutabel, noch dazu weil noch anderes Zeugs mit in den Speicher muß (siehe IMPLEMENTIERUNG). 2) Nuancenreich sowohl bei der Auflösung der Voxel als auch bei der naturgetreuen Farb-gebung Die Breite unserer Voxeltürme ist natürlich entscheidend für die Auflösung insgesamt. Eine angestrebte Screenzeichenfläche von 256x160 (wer will, kann auch noch mehr machen ...) bedeutet bei einer 1 Pixel Turmbreite für den ST Overkill. Man muß es hier etwas grober an-gehen. Hier gibt es die Varianten von 4 (mein Vorschlag) oder 8 Pixeln. Das ist machbar. Bei den Farben ist bei 16 Schluß. Leider. Diese sollten daher gut aufeinander abgestimmt sein. Ich habe mal probiert mit Rasterungen Zwischenfarben zu erzeugen. Das Ergebnis war scheußlich ... 3) Hohe Geschwindigkeit der Animation Mit einer 4er Auflösung und einem kleinerem Fenster (wie unter 2) beschrieben) sind Frame- raten um die 17 Bilder drin. Das geht in Ordnung. 4) Freie Bewegbarkeit des Gleiters in alle Richtungen mit Drehung Das kommt gut, macht aber die Landkartenauswertung etwas schwieriger. Wer darauf ver- zichten kann, wertet die Landschaft wie oben beschrieben mit einem rechteckigen Fenster aus, welches er in X und Y Richtungen verschieben kann (aber eben nicht drehen ...). Wer es sich und dem ST so richtig geben möchte, macht es mit Drehung. Man kann hier nicht einfach über- bzw. nebeneinander liegende Voxel auslesen. Auslesen in Abhängigkeit von der Richtung kann man wie folgt: Man stelle sich einen Kreis vor mit Mittelpunkt B. B ist der Betrachter. man schickt nun Strahlen von diesem Mittelpunkt in die Welt hinaus; diese stellen die Sehstrahlen des Betrachters dar. Eine volle Umdrehung hat 360 Grad, also nehmen wir mal 360 Strahlen (Abstand 1 Grad). Wenn der Betrachter jetzt genau nach Norden schaut (Grad 0) und wir annehmen, daß sein Sichtkegel noch ein wenig nach links und rechts geht, werten wir z.B. die Strahlen 330 - 30 Grad aus (angenommen 60 Grad Sichtkegel). Wenn wir uns etwas nach rechts drehen (30 Grad), dann werten wir die Strahlen 0 - 60 aus (man stelle sich den Kreis mit den Strahlen vor; siehe auch Grafik STRAHL.IFF !). Viel mehr ist es gar nicht. Die Strahlen werden vorher berechnet (genauer: x + y Offsets - siehe IMPLEMENTIERUNG !) und auf die Landkarte angewendet (in Abhängigkeit von der Betrachterposition). Als weiters Goodie kann man auch noch das Kippen des Horizontes einbauen. Hierfür werden die Startscreenoffsets für die einzelnen Spalten = Türmchen nach oben/unten modifiziert. 5) Veränderbarkeit der Flughöhe des Gleiters Wenn sich die Flughöhe verändert, verändert sich auch die Perspektive auf die Landschaft. In Abhängigkeit davon muß die Perspektivenanpassung bei der Höhenberechnung während des Spaltenzeichnens durchgeführt werden. Dies beeinflußt die Perspektivenanpassung der Voxelgrundhöhe (wie in der Landkarte abgespeichert) und das Höhengrundoffset. Das läuft das auf mehrere Perspektiventabellen hinaus - das erkläre ich nicht weiter. Mit einem einfachen Strahlensatz kommt man da weiter - probierts mal. Neben der Drehbarkeit ist diese Funktionalität sicherlich auch optional. Implementierung 0) Landschaftsdaten Unsere Landschaft soll die Größe 256x256 haben. Für jeden Voxel speichern wir ein Byte für die Höhe und ein Byte für die Farbe ab. Man könnte es auch so angehen, daß man aus der Höhe die Farbe ableiten kann. Ich habe mich für die erste Methode entschieden. Eine Zeile der Landschaftsmatrix wird folgendermaßen im Speicher abgelegt: 256 Höhenbytes -> erste Landschaftszeile 256 Farbbytes -> erste Landschaftszeile 256 Höhenbytes -> zweite Landschaftszeile 256 Farbbytes -> zweite Landschaftszeile ... Wichtig: die Höhenwerte sind mit 4 multipliziert - damit man bei der Spaltenzeichenroutine direkt in den Code einspringen kann. Somit bleiben nur noch 64 Höhen (=256/4) übrig. Eine Landschaftsvoxelzeile hat also 512 Bytes. Insgesamt braucht die Landschaft: 512 Byte * 256 Zeilen = 128kb Wie man sich eine solche Landschaft generiert, ist eine andere Frage. Ich habe mir mit einem Fraktalprogramm 2D Ansichten berechnen lassen und diese in mein Format konvertiert (nach dem Schema: Höhe->Farbe). Eine Auswertung einer Sinuskurve ist natürlich auch möglich (und spart für das Programmfile Speicherplatz -> weniger Daten, wenn zur Laufzeit generiert). 1) Strahlenvorberechnung Wie bereits angesprochen, berechnen wir die Strahlen vorher. Eine Umdrehung soll hier aber aus 512 Stufen = Strahlen bestehen - hieraus resultiert eine etwas feinere Drehung. Bitte beachtet ierzu die Grafik STRAHL.IFF. Ein Sichtstrahl ist später zuständig für das Auslesen der Landschaftspunkte einer Spaltenbe- rechnung. Ein solcher Strahl besteht hier aus 64 Punktoffsets. Dies stellt die Sichtweite dar, mit der wir in die Landschaft Richtung Horizont kucken. Diese Offsets werden dann bei der Spaltenberechnung auf die aktulle Position aufaddiert - also um von einem Strahlpunkt zum nächsten zu kommen, addiert man das x-y-Offset. Dadurch wird das alles relativ zu der aktuellen Position in der Landkarte (absolute Punkte würden bei einer Bewegung nix bringen). Ein Punktoffset besteht aus X+Y Offset, die wir in einem Longword ablegen, und zwar so: Highword| Lowword y offset x offset Man nimmt als Strahlausgangspunkt meist nicht den Mittelpuntk des Kreises, sondern bewegt sich etwas weiter weg. Diesem Umstand trägt die Angabe "radius1" Rechnung, indem hier der Abstand vom Mittelpunkt des Sichkreises (siehe Grafik, Grundlagenbesprechung) ange-geben wird. ******* Source: Vorberechnung der Strahlen mit x,y Positionen ****** map_groesse equ 256 ; unsere Landschaft ist 256x256 groß strahlen equ 512 ; volle Umdrehung = 512 Strahlen = 360 Grad radius1 equ 40 ; Kreis innen radius2 equ 170 ; Kreis außen punkte equ 64 ; wieviel Punkte nach vorne sehen - Richtung Horizont bit_shift equ 9 ; 2**9 = 512 -> eine Zeile in der Landkarte ; siehe Landschaftsdaten berechne_strahlen: lea strahlen_speicher(PC),A0 ; hier werden die Strahlen abgelegt lea sin_tabelle(PC),A1 ; Sinustabelle mit 1024 Eintraegen lea cos_tabelle(PC),A4 ; Kosinustabelle moveq #0,D0 ; Strahlenzähler initialisieren strahlen_loop: move.l D0,-(SP) ; pro Strahl durchlaufen ... move.w D0,D4 mulu #1024,D4 ; 1024 Winkel in der Cos/Sin Tabelle ... divu #strahlen,D4 ; ... auf benötigte Strahlenwinkel umrechnen add.w D4,D4 ; fuer Tabellenzugriff ( pro Winkel ein Word) and.w #$07FE,D4 ; in der Tablle bleiben (sin+cos periodisch) move.w 0(A1,D4.w),D0 ; Sinuswert (multipliziert mit 2**15) move.w 0(A4,D4.w),D1 ; Kosinuswert (dito) move.w D0,D2 move.w D1,D3 muls #radius1,D0 ; X Startpunkt auf dem inneren Kreis (x1) muls #radius1,D1 ; Y Startpunkt auf dem inneren Kreis (y1) muls #radius2,D2 ; X Startpunkt auf dem äußeren Kreis (x2) muls #radius2,D3 ; Y Startpunkt auf dem äußeren Kreis (y2) add.l D0,D0 ; 2**15 * 2 = 2**16 add.l D1,D1 ; " add.l D2,D2 ; " add.l D3,D3 ; " swap D0 ; Multiplikation mit 2**16 rückgängigmachen swap D1 swap D2 swap D3 sub.w D0,D2 ; x2 - x1 = deltaX sub.w D1,D3 ; y2 - y1 = deltaY ext.l D2 ; für weitere Long Berechnungen erweitern ext.l D3 ; " suba.w A2,A2 ; A2 löschen - ist letzter x Wert suba.w A3,A3 ; A3 löschen - ist letzter y Wert moveq #0,D4 ; ist neuer x Wert moveq #0,D5 ; ist neuer y Wert ;-------------------------------------------------- ; und jetzt die Positionsoffsets pro Strahl berechnen move.w #punkte-1,D6 punkt_loop: movea.l D4,A5 ; Aktuellen x Strahlwert sichern movea.l D5,A6 ; gleiches für y divs #punkte,D4 ; zwischen Radius1 und radius2 auf dem Strahl bewegen ... divs #punkte,D5 ; Umrechnung, um die gewünschte Punktanzahl auf die add.w D0,D4 ; Distanz x1->x2, y1->y2 zu verteilen add.w D1,D5 ext.w D4 ext.w D5 movem.w D4-D5,-(SP) ; aktuelle x,y Position sichern sub.w A2,D4 ; von alter Position abziehen, ergibt Delta x ... sub.w A3,D5 ; Delta y für die Strahloffsets ! movem.w (SP)+,A2-A3 ; aktuelle Werte werden alte Position ! and.l #map_groesse-1,D4 ; innerhalb der Landschaft bleiben x and.l #map_groesse-1,D5 ; innerhalb der Landschaft bleiben y moveq #bit_shift,D7 ; spezielles x+y Format erzeugen: lsl.l D7,D5 ; y mit Zeilenbreite multiplizieren ; 256 für Höhen + 256 Pixel für Farben ; -> 512 = 2**9 or.l D4,D5 ; x ins Lowword move.l D5,(A0)+ ; x+y Wert für den berechneten Punkt ablegen ;--------------------------------------- move.l A5,D4 ; x+y Startwerte für Strahl wiederherstellen move.l A6,D5 add.l D2,D4 ; Weiter in x Richtung auf dem Strahl bewegen add.l D3,D5 ; weiter in y Richtung auf dem Strahl bewegen dbra D6,punkt_loop ; Strahl abarbeiten move.l (SP)+,D0 addq.w #1,D0 ; naechster Strahl cmp.w #strahlen,D0 ; Schon alle Strahlen ? bne strahlen_loop rts 2) Vorberechnung der Junk-Konvertierungstabelle und Junk Format Wie setzt man einzelne Punkt schnell auf dem ST (in 4 Planes natürlich) ??? Hierbei hilft der movep.l Befehl - aber der wird bestimmt in einer anderen Ausgabe be-sprochen. Für Voxel brauchen wir ihn gar nicht ... Allerdings kann die Auflösung einige Probleme machen. Wählt man 8 Pixel Breite für einen Voxel, dann ist es doch ein Job für movep. Wenn wir eine Voxelbreite von 4 Pixeln wollen, wird alles etwas komplizierter, aber sieht eben auch besser aus ! Aber wie setzte ich schnell 4 Pixel ? Man schreibt in der Spaltenzeichenschleife überhaupt keine Pixel, sondern benutzt einen Zwischenpuffer mit einem dafür günstigen Format - nennen wir ihn mal Junk Puffer. Hier schreiben wir dann unsere Voxeldaten rein. Im zweiten Schritt wird dann dieser Puffer aus- gewertet und in den ST Bildschirmspeicher geschrieben. Das muß natürlich flott gehen. In diesem Fall ist es schneller als Voxel gleich direkt zu zeichnen. So sieht das Format des Junk Puffers aus: Wir haben 16 Farben, man kann also mit 4 Bit alle Farben codieren. Also soll ein Voxel durch diese Information seine Farbe codieren (daß die Breite später 4 ist, interessiert uns hier noch nicht ! Eine Farbe pro Einheit = Voxel ). In ein Byte unseres Puffers passen somit 2 Voxelfarb- informationen. So sei es. #----------- Byte 1 --------------# #----------- Byte 2 --------------# ... Voxel1 Farbe Voxel2 Farbe Voxel3 Farbe Voxel 4 Farbe Beim Schreiben in unseren Puffer müssen wir nur aufpassen, daß wir einmal Bits 4,5,6,7 und einmal Bits 0,1,2,3 beschreiben (Highnibble, Lownibble). Dies werden später zwei ver-schiedene Spaltenzeichenroutinen übernehmen. Die nächste Frage ist natürlich, wie die Daten des Junk Puffers in den Bildschirmspeicher kommen. Ganz einfach - eine große Tabelle hilft mal wieder ! Die Junk Puffer Konvertierungsroutine (siehe Punkt 4) geht so vor: Es wird immer ein Wort aus dem Junk Puffer ausgelesen. Dieses Wort codiert ja 4 Voxel (pro Byte 2). Bei einer angestrebten Voxelbreite von 4 macht entsprechen dieser Information 4 Voxel * 4 Pixel = 16 Pixel. Hörte ich gerade 16 Pixel ??? Das ist doch schon was für unseren ST Bildschirmspeicher. Und so geht es weiter ... Über die Tabelle werden die 4 Farb-informationen in die 4 Planewörter des STs umgesetzt und in den Bildschirmspeicher geschrieben. Mehr ist es nicht. Zur Tabelle selbst: sie muß alle Kombinationen abdecken, also 16 Farben (Voxel 1) * 16 Farben(Voxel 2) * 16 Farben (Voxel 3) * 15 Farben (Voxel 4, ich benutze Farbe 16 für Rasterfarben, kann ich mir hier also sparen ...) * 4 Planewörter ST = 491520 Bytes ! Ist nicht gerade wenig .... ich weiß. ********** Source: Vorberechnungen der Junk Tabelle *********** precalculate_junk_tab: lea mammut_table,A0 lea d0_tab(PC),A1 lea d1_tab(PC),A2 lea d2_tab(PC),A3 lea d3_tab(PC),A4 moveq #0,D0 moveq #0,D1 moveq #0,D2 moveq #0,D3 loop: ; Spalte 0 move.w D0,D4 lsl.w #3,D4 movem.l 0(A1,D4.w),D5-D6 ; Spalte 1 move.w D1,D4 lsl.w #3,D4 or.l 0(A2,D4.w),D5 or.l 4(A2,D4.w),D6 ; Spalte 2 move.w D2,D4 lsl.w #3,D4 or.l 0(A3,D4.w),D5 or.l 4(A3,D4.w),D6 ; Spalte 3 move.w D3,D4 lsl.w #3,D4 or.l 0(A4,D4.w),D5 or.l 4(A4,D4.w),D6 ; In Tabelle schreiben move.l D5,(A0)+ move.l D6,(A0)+ ; Nächste Kombination addq.w #1,D0 cmp.w #16,D0 ; 16 * ... blt.s loop moveq #0,D0 addq.w #1,D1 cmp.w #16,D1 ; ... * 16 * ... blt.s loop moveq #0,D1 addq.w #1,D2 cmp.w #16,D2 ; ... * 16 * ... blt.s loop moveq #0,D2 addq.w #1,D3 cmp.w #15,D3 ; ... * 15 blt.s loop rts d0_tab: DC.W $000F,$000F,$000F,$000F DC.W $000F,0,0,0 DC.W 0,$000F,0,0 DC.W $000F,$000F,0,0 DC.W 0,0,$000F,0 DC.W $000F,0,$000F,0 DC.W 0,$000F,$000F,0 DC.W $000F,$000F,$000F,0 DC.W 0,0,0,$000F DC.W $000F,0,0,$000F DC.W 0,$000F,0,$000F DC.W $000F,$000F,0,$000F DC.W 0,0,$000F,$000F DC.W $000F,0,$000F,$000F DC.W 0,$000F,$000F,$000F DS.W 4 d1_tab: DC.W $00F0,$00F0,$00F0,$00F0 DC.W $00F0,0,0,0 DC.W 0,$00F0,0,0 DC.W $00F0,$00F0,0,0 DC.W 0,0,$00F0,0 DC.W $00F0,0,$00F0,0 DC.W 0,$00F0,$00F0,0 DC.W $00F0,$00F0,$00F0,0 DC.W 0,0,0,$00F0 DC.W $00F0,0,0,$00F0 DC.W 0,$00F0,0,$00F0 DC.W $00F0,$00F0,0,$00F0 DC.W 0,0,$00F0,$00F0 DC.W $00F0,0,$00F0,$00F0 DC.W 0,$00F0,$00F0,$00F0 DS.W 4 d2_tab: DC.W $0F00,$0F00,$0F00,$0F00 DC.W $0F00,0,0,0 DC.W 0,$0F00,0,0 DC.W $0F00,$0F00,0,0 DC.W 0,0,$0F00,0 DC.W $0F00,0,$0F00,0 DC.W 0,$0F00,$0F00,0 DC.W $0F00,$0F00,$0F00,0 DC.W 0,0,0,$0F00 DC.W $0F00,0,0,$0F00 DC.W 0,$0F00,0,$0F00 DC.W $0F00,$0F00,0,$0F00 DC.W 0,0,$0F00,$0F00 DC.W $0F00,0,$0F00,$0F00 DC.W 0,$0F00,$0F00,$0F00 DS.W 4 d3_tab: DC.W $F000,$F000,$F000,$F000 DC.W $F000,0,0,0 DC.W 0,$F000,0,0 DC.W $F000,$F000,0,0 DC.W 0,0,$F000,0 DC.W $F000,0,$F000,0 DC.W 0,$F000,$F000,0 DC.W $F000,$F000,$F000,0 DC.W 0,0,0,$F000 DC.W $F000,0,0,$F000 DC.W 0,$F000,0,$F000 DC.W $F000,$F000,0,$F000 DC.W 0,0,$F000,$F000 DC.W $F000,0,$F000,$F000 DC.W 0,$F000,$F000,$F000 DS.W 4 3) Spaltenzeichnerroutine Die Spaltenzeichenroutine geht nach dem bereits geschilderten Algorithmus vor (siehe Grundlagen). Anzumerken ist noch, daß die Farbtabelle die 4 Bit Farbinformationen im oberen Nibble eines Bytes speichert. In der zweiten Routine müssen die Farben dann noch für das untere Nibble geshiftet werden. ********** Source: Zeichnen der Spalten *********** mask equ %00000000000000011111111011111111 spalten equ 64 ; wir zeichnen 64 Spalten (256 Screenpixel / 4 Pixel pro Voxel) scan_nibble_high: ;**** Registerbelegung innerhalb der Schleifen **** ; d0= x+y Position ; d1= Neue Hoehe ; d2= Alte Hoehe ; d3= Spaltenpunkte Zähler ; d4= pers adr + work byte ; d5= perts adder ; d6= Offset einer Zeile innerhalb des Junk Puffers ; d7= And Maske für Bleiben innerhalb der Landschaft * a0= Delta-xy-offset Tabelle eines Strahls * a1= Zugriff auf die Höhendaten der Landschaft * a2= Zugriff auf die Farbdaten der Landschaft * a3= Für Direkteinsprung in die Turmzeichenbefehlsfolge * a4= Perspektiven Tabelle * a5= Aktuelle Screen Adresse * a6= Alte Screen Adresse lea byte_code_1(PC),A3 * Adresse für Code (Direkteinsprung) moveq #0,D1 * Neue Höhe init moveq #0,D2 * Alte Höhe init move.w #punkte-1,D3 * Anzahl der Voxel, die Richtung Horizont * ausgewertet werden sollen moveq #0,D4 * Perspektiventabelle Start ganz vorne beim Betrachter move.w #perspektiven*punkte,D5 * Offset für Schritt Richtung Horizont moveq #spalten/2,D6 * Zeilenoffset einer Junk Puffer Zeile move.l #mask,D7 * Landschaftsabhängig do_row_byte_1: add.w D5,D4 * In der Perspektiventabelle einen Schritt * Richtung Horizont machen ... add.l (A0)+,D0 * actual x-y-position and.l D7,D0 * mask raender move.b 0(A1,D0.l),D4 * Höhe auslesen move.b 0(A4,D4.l),D1 * Höhe perspektivisch anpassen ... * Ergebnis: Höhe * 4 -> für Direkteinsprung sub.w D1,D2 * Alte Höhe - neue Höhe < 0 ? * damit ist der neue Punkt nicht sichtbar ! * also nichts zeichnen bmi.s scan_me_byte_1 add.w D1,D2 * Operation rückgängig machen -> alte Höhe bleibt * der Maßstab ! dbra D3,do_row_byte_1 * Nächster Punkt rts scan_me_byte_1: move.b 0(A2,D0.l),D4 * Farbinformation holen (Byte) jmp 0(A3,D2.w) * Direkteinsprung (abh. von neu zu zeichnendem * Turmdelta -> die ) ********* Farbwerte eintragen *********** REPT max_y move.b D4,(A5) * Farbe in Junk Puffer schreiben suba.w D6,A5 * vorherige Zeile im Junk Puffer adressieren ; beide Befehle zusammen 2 Words = 4 Byte ENDR byte_code_1: move.w D1,D2 * neue Höhe wird zur alten Höhe dbra D3,do_row_byte_1 * nächstes Voxel rts Die selbe Prozedur für jede 2.Spalte - hier werden die niedrigen Nibbles des Junk Puffers gefüllt. scan_nibble_low: lea byte_code_2(PC),A3 moveq #0,D1 moveq #0,D2 move.w #punkte-1,D3 moveq #0,D4 move.w #perspektiven*punkte,D5 moveq #spalten/2,D6 * Zeilenoffset einer Junk Puffer Zeile move.l #mask,D7 * Landschaftsabhängig do_row_byte_2: add.w D5,D4 add.l (A0)+,D0 and.l D7,D0 move.b 0(A1,D0.l),D4 move.b 0(A4,D4.l),D1 sub.w D1,D2 bmi.s scan_me_byte_2 add.w D1,D2 dbra D3,do_row_byte_2 rts scan_me_byte_2: move.b 0(A2,D0.l),D4 lsr.b #4,D4 ; Farbinformation ins untere Nibble jmp 0(A3,D2.w) REPT max_y or.b D4,(A5) ; unteres Nibble füllen suba.w D6,A5 ENDR byte_code_2: move.w D1,D2 dbra D3,do_row_byte_2 rts 4) Junk Puffer Konvertierungsroutine Umsetzen der Voxel 4Bit Farbinformationen in den ST Bildschirmspeicher - siehe Abschnitt Junk Puffer Vorberechnung. ********** Source: Umsetzen des Junk Puffers in den ST Bildschirmspeicher *********** scanner: movea.l screen_adr(PC),A0 ; Bildschirmspeicheradresse lea mammut_table,A1 ; Konvertierungstabelle Adresse lea junk_puffer(PC),A2 ; Junk Puffer Adresse move.w #highs-1,D0 ; wieviele Zeilen im Junk Puffer copy_lines: movea.w D0,A3 moveq #0,D0 moveq #0,D2 moveq #0,D4 moveq #0,D6 move.w (A2)+,D0 ; Wort mit 4 Voxel Farben holen move.w (A2)+,D2 move.w (A2)+,D4 move.w (A2)+,D6 lsl.l #3,D0 ; 4 Plane Wörter = 8 Byte lsl.l #3,D2 lsl.l #3,D4 lsl.l #3,D6 movem.l 0(A1,D0.l),D0-D1 ; Konvertierte Wörter holen movem.l 0(A1,D2.l),D2-D3 movem.l 0(A1,D4.l),D4-D5 movem.l 0(A1,D6.l),D6-D7 movem.l D0-D7,(A0) * 64 Pixel schreiben movem.l D0-D7,160(A0) * Zeile verdoppeln (y double pix) off SET 16*4/2 REPT 3 moveq #0,D0 moveq #0,D2 moveq #0,D4 moveq #0,D6 move.w (A2)+,D0 move.w (A2)+,D2 move.w (A2)+,D4 move.w (A2)+,D6 lsl.l #3,D0 lsl.l #3,D2 lsl.l #3,D4 lsl.l #3,D6 movem.l 0(A1,D0.l),D0-D1 movem.l 0(A1,D2.l),D2-D3 movem.l 0(A1,D4.l),D4-D5 movem.l 0(A1,D6.l),D6-D7 movem.l D0-D7,off(A0) * 64 pixel movem.l D0-D7,off+160(A0) off SET off+16*4/2 ENDR lea 320(A0),A0 move.w A3,D0 dbra D0,copy_lines rts Zusammenfassung So, das wars erstmal für heute. Ich hoffe, daß Ihr mit den Beschreibungen klarkommt. Auf Diskette findet Ihr noch die beiden Grafiken TURM.IFF und STRAHL.IFF sowie alle Quellenausschnitte. Bitte gebt uns auch etwas Feedback - was war gut, was schlecht, könnte man was anders machen, oder habt Ihr spezielle Themenwünsche ..... schreibt ans Undercover HQ ! Ich wünsche allen ATARI Freaks ein paar schöne Sommermonate und noch viel Spaß mit den anderen Artikeln ..... Mike of STAX in 1997.