Produktion

Phreaks

Herunterladen 2010_condense_phreaks.zip

Mitwirkende

  • Code — NoRecess
  • Grafik — Ced
  • Musik — Tom et Jerry

Mitwirkende wie auf Pouet aufgeführt.

Diese Demo beruht auf einem einzigen Effekt: einem fortgeschrittenen Animationsabspieler. Er bietet Delta-Packing mit pixelgenauer Präzision samt Antialiasing. Nötig waren dafür eine besondere Fassung des Packers Exomizer sowie eine eigene Werkzeugkette auf PC-Seite, um die Daten zu erzeugen. Die Demo wurde mit dem Compiler PhrozenC gebaut und vollständig in meiner eigenen IDE erstellt.

Der Algorithmus erklärt

Phreaks ist eine Demo, die auf einem einzigen Effekt beruht: einem Abspieler für Animationssequenzen. Viele Leute haben mich gebeten, den gesamten Entstehungsprozess dahinter zu erklären. Dieser Artikel zeigt die einzelnen Entwicklungsschritte (und die Logik dahinter). Hoffentlich macht er deutlich, dass hinter einer Demo oft ein gewaltiges Stück Arbeit steckt — und nein, es ist nicht so einfach, sie zu machen, auch wenn es angeblich nur ein simpler Animationsabspieler ist …

Zur Zeit der Veröffentlichung geschrieben und so belassen, wie es geschrieben wurde.

Der Ursprung

Alles ging von einer früheren Produktion von mir aus, Phat 2. Auch diese Demo hatte einen Animationsabspieler wie den in Phreaks; sein Prinzip war schnell und leicht umzusetzen. Leider war er optisch unschön, weil er auf Blöcken beruhte — die Auflösung war also ziemlich grob.

Technisch wurde jedes Einzelbild der Animation in einem eigenen Puffer abgelegt. Die Texturdaten waren tatsächlich eine Kombination der Opcodes PUSH BC, PUSH DE, PUSH HL, PUSH AF und EXX. Jedes Einzelbild wurde einzeln mit dem Packer BitBuster V1.2 von Team Bomba gepackt. Der Abspieler auf dem Amstrad CPC entpackte dann ein Einzelbild der Animation und führte die Opcodes aus, um es auf den Bildschirm zu bringen.

Verbesserungen gegenüber Phat 2

Als Phat 2 erschien, haben mich viele Leute gefragt, wie ich das gemacht habe. Schnell wurde mir klar, dass mein Ansatz — so gut er war — die Grenzen nicht ausreizte. Es war Luft nach oben. Ich habe eine persönliche Regel für Demos: denselben Effekt nicht zweimal machen oder wiederverwenden. Ich wusste also, dass als Nächstes die Speicherverwaltung besser werden musste (um viel mehr Einzelbilder unterzubringen), aber auch die Optik, durch eine höhere Auflösung (damals dachte ich optisch noch an eine blockbasierte Lösung, nur mit kleineren Blöcken). Und dann fing ich an, das Ding zu prototypisieren …

Die Daten

Zuerst musste ich Daten finden. Ursprünglich dachte ich daran, sie von Grund auf selbst zu machen — Einzelbild für Einzelbild hätte ich mit dem ausgezeichneten Grafikwerkzeug Paint.net Verformungen eines Logos erzeugen können. Ich habe erste Versuche gemacht, aber es überzeugte nicht, und ich wusste, dass die Leute auf tanzende Menschen warteten. Also habe ich schließlich im Netz nach brauchbaren Daten für eine Demo gesucht. Dann hat Apple (und seine Werbung) meinen Tag gerettet; dank ihnen habe ich dieses Video auf YouTube gefunden:

Es ist zwar nicht das beste Video, das ich je gesehen habe, aber ich dachte, die verschiedenen Bewegungsphasen darin könnten ein interessantes Ergebnis liefern. Ich wusste, dass ich dabei bleiben und mich auf die bestmögliche Wiedergabe auf dem Amstrad CPC konzentrieren musste.

Wandlung in Rohdaten

Ich musste einen YouTube-Downloader benutzen. Der, den ich verwendet habe, heißt SaveVideoDownload, aber ich empfehle ihn nicht und gebe auch keine Links an, denn es scheint sich um jene Sorte Browser-Erweiterung zu handeln, die weit mehr tut, als sie vorgibt — ich verdächtige sie, Spyware zu installieren und dergleichen. Trotzdem hat sie ihren Zweck erfüllt und mir das gesamte Video als .mpg geliefert.

Als ich das Video hatte, musste ich einen Bitmap-Extraktor benutzen: Für jedes Einzelbild des .mpg-Videos wollte ich eine Liste von .bmp-Dateien. Dafür habe ich den VideoLAN Player (VLC) verwendet. Ich erinnere mich nicht mehr an die genauen Schritte in der Software, aber ich weiß, dass ich trickreiche Einstellungen (und Codecs?) brauchte, damit es wie gewünscht lief — wichtig ist hier nur zu wissen, dass sich damit Bitmaps extrahieren lassen.

Ich habe die Einzelbilder aufgeräumt — zwischen Tausenden von Bitmaps zu sortieren ist definitiv eine langwierige Sache!

Anschließend habe ich mit dem Werkzeug IrfanViewer jedes Einzelbild auf 2 Farben reduziert und auf 768 × 205 skaliert (meine bevorzugte „Breitbild“-Auflösung für den CPC — hier mit Videomodus 2).

Zuletzt habe ich jedes Einzelbild in Paint.net geladen und einen Weichzeichner darübergelegt. Das war ein wichtiger Schritt, denn jedes Einzelbild war skaliert worden und stammte aus einer MPEG-Kompression, was als traurige Folge viele Artefakte um die Figur herum ergab. Der Weichzeichner hat die Figur abgerundet. Beim Weichzeichnen kamen allerdings neue Farben ins Bild, also musste ich die Farben noch einmal auf 2 reduzieren — all diese Schritte habe ich von Hand gemacht (das Werkzeug bietet dafür keine Stapelverarbeitung), es war also wirklich eine recht mühsame Angelegenheit (sie hat mich viel Zeit gekostet und viel Konzentration verlangt).

Nach Tagen der Arbeit (ja …) hatte ich meine Daten bereit, um von meinen eigenen Werkzeugen gewandelt zu werden …

Von Bitmaps zu Animationsdaten (im Geiste von Phat 2)

Mein erster Ansatz war der einfachste: einen ganzen Bildschirm an Daten mit Exomizer packen und zur Laufzeit entpacken. Ich habe den Videomodus 2 des Amstrad CPC benutzt, der die bestmögliche Auflösung bietet. Auf dem Amstrad CPC ausgeführt sah das zwar beeindruckend aus, war aber sehr langsam und fraß so viel RAM — im Grunde belegte es 3 vollständige Bänke des erweiterten RAM (17 KB × 3). So war es unbrauchbar, und ich dachte zuerst, ein Animationsabspieler mit einer so hohen Auflösung sei etwas, worauf ich mich einfach nicht einlassen könne. Aber ich blieb dran …

Von Bitmaps zu Animationsdaten (rasterzeilenbasiert)

Ich wusste, dass ich am Ende pixelgenaue Präzision wollte. Also kam ich schnell darauf, waagerechte Rasterzeilen für die Bilddaten zu benutzen. Für jede Zeile des Bildes habe ich eine Liste von X1- und X2-Ganzzahlen abgelegt (die Kanten einer Linie), die waagerechte Linien beschreiben. Mit Exomizer komprimiert bekam ich deutlich bessere Ergebnisse als mit dem vorigen Algorithmus — beim Speicherbedarf war es am Ende doppelt so gut, in der Ausführung aber immer noch wirklich langsam.

Overflow — der mich während des gesamten Entstehungsprozesses der Demo großartig unterstützt hat — riet mir zu diesem Zeitpunkt, mit Pixelinvertierung zu arbeiten. Mit diesem Kniff hatte ich nur noch X1-Positionen, wobei X2 zu einem Umschalten zwischen Pixel an und aus wurde. Speichertechnisch war das besser, aber immer noch zu groß — etwa 30 KB für eine einzige Animation.

Ich habe viele kleine Anpassungen an meinem Datenformat vorgenommen, um Speicher zu sparen, aber reif für die breite Öffentlichkeit war es definitiv nicht.

Von Bitmaps zu Animationsdaten (mit Delta-Packing, im Geiste von Phat 2)

Dann war da jene Sache, auf die mich nach dem Erscheinen von Phat 2 alle hingewiesen hatten: Delta-Packing. Dabei wird das erste Einzelbild der Animation vollständig gezeichnet und beim nächsten nur noch der Zustand der Pixel geändert, die sich geändert haben. Um das zu erreichen, habe ich einige Zeit damit verbracht, meine Werkzeuge zum Erzeugen der entsprechenden Daten umzuschreiben. Zu dieser Zeit entschied ich mich, für den Effekt wieder auf Blöcke à la Phat 2 umzuschwenken. Das Ergebnis nach 2 Wochen Arbeit war schlicht großartig: Es war schnell, und dieselbe Animationssequenz, die ich von Anfang an benutzt hatte, lag unter 17 KB. In diesem Entwicklungsstand wurde mir klar, dass ich etwa das Doppelte des Inhalts der Demo Phat 2 im Speicher des CPC unterbringen konnte!

Aber mein Ziel war, etwas optisch Besseres als Phat 2 zu machen, also musste ich nun über eine bessere Auflösung nachdenken …

Von Bitmaps zu Animationsdaten (mit Delta-Packing, im Geiste von Phat 2, mit Verbesserungen)

Dann habe ich angefangen, den vorhandenen Algorithmus zu verbessern. Ich entschied mich für eine besondere Kodierung all jener Blöcke, die optisch ganz gefüllt oder ganz leer blieben — sagen wir, eine Gruppe von Blöcken wie 8×8 würde tatsächlich als 1×1-Block unter einer besonderen, eindeutigen Kennung abgelegt. Für jedes Einzelbild habe ich außerdem eine Liste von Sprites geführt, die rund um die ganze „blockige“ Figur auf dem Bildschirm gezeichnet würden. Ich habe viele Kniffe eingeführt, um mit Exomizer besser komprimierbare Daten zu erzeugen, und die Ergebnisse waren recht überzeugend. Die Animationssequenz, die anfangs 3 Bänke RAM belegte, brauchte für dieselbe Optik nun 14 KB — für mich war das beeindruckend.

Zu diesem Zeitpunkt hatte ich das Vorgehen für eine Demo auf Basis dieses Algorithmus klar vor Augen. Sie beruhte weiterhin auf dem Videomodus 2 des Amstrad CPC.

Umordnen der Animationsdaten (mit einer eigenen Fassung von Exomizer von Hicks/Vanity)

Zu dieser Zeit fiel mir auf, dass Exomizer bessere Ergebnisse liefert, wenn es einen großen, zusammenhängenden Puffer bekommt statt vieler kleiner. Ich wollte die vorhandene Exomizer-Umsetzung so ändern, dass sie einen Wechsel des Quellpuffers im laufenden Betrieb unterstützt. Denken Sie an den Fall, dass ich eine Animationssequenz habe, die komprimiert 30 KB Speicher belegt: Es wäre schön, diesen Puffer in den getrennten erweiterten RAM-Bänken (je 17 KB) unterzubringen.

Ich habe mit Hicks/Vanity über meinen sehr speziellen Bedarf rund um Exomizer gesprochen, und er hat sich freundlicherweise bereit erklärt, eine solche Routine zu schreiben. Nachdem ich seine Arbeit erhalten hatte und nach ein paar Fehlerkorrekturen war die Routine vollständig in die Demo eingebaut. Wie geplant schrumpften die Daten noch weiter, sodass die anfangs 14 KB große Animation nun 11 KB belegte!

Von Bitmaps zu Animationsdaten (endlich der echte Algorithmus von Phreaks!)

Viele Tage vergingen — ich brauchte einfach etwas Erholung. Aber ich dachte weiter über einen besseren Weg nach, die Datenmenge zu verkleinern. 11 KB für meinen Testfall waren in Ordnung, aber eine Demo wäre mit so großen Daten kaum machbar gewesen. Und dann kam mir eines Tages die Idee. Im Grunde beruht der Effekt weiterhin auf Blöcken. Für jedes Einzelbild habe ich eine Liste von Sprites benutzt, um pixelgenaue Blöcke zu beschreiben. Ich sagte mir: Warum nicht alle Bilder der gesamten Animationssequenz analysieren und daraus eine Liste von Sprites erzeugen, die sich alle Bilder teilen? Also habe ich das getan.

Ich habe meine Werkzeuge dafür überarbeitet. Es hat mich einige Mühe gekostet, die optimale Sprite-Auflösung zu finden: Waren die Sprites zu groß, endete es in einer endlosen Liste verschiedener Sprites; waren sie zu klein, endete es mit zu vielen Sprite-Indizes in den Daten. Die optimale Größe, die ich fand, war 2×5. Mit dieser Sprite-Auflösung fühlte ich mich im Videomodus 2 des Amstrad CPC nicht wohl, dafür wurde plötzlich der Videomodus 0 reizvoll, in dem ein Byte VRAM 2 Pixel darstellt.

Am Ende brauchte meine Test-Animationssequenz 108 eindeutige Kacheln. Damit konnte ich die Kachelindizes in einem einzigen Byte ablegen, was ebenfalls eine ziemlich gute Nachricht war. Wieder einmal musste ich mein Datenformat anpassen, um mit Exomizer bessere Ergebnisse zu erzielen. Das Ergebnis: Die Animation belegte nur noch 6 KB Speicher …

Die großen Fehlschläge in Sachen Kompression

Zusätzlich zu all dieser Arbeit habe ich versucht, waagerechtes und senkrechtes Spiegeln der Kacheln einzuführen. Beim Verweis auf eine Kachel hatte ich irgendwo 2 Bits für diese Information. Das ergab kleinere Rohdaten, führte aber leider zu einer schlechteren Kompression durch Exomizer, als wenn ich gar nichts getan hätte.

Außerdem stellte sich heraus, dass der ganze Code für die Sonderbehandlung ganz gefüllter und ganz leerer Blöcke nach der Kompression weniger effizient war, als einfach ein Sprite dafür zu benutzen. Speichertechnisch war das eine gute Nachricht, aber ich hatte so viel Zeit mit diesen Feinheiten verbracht, dass es mich enttäuschte, so viel Arbeit für … nichts gemacht zu haben.

Und schließlich fand ich unter den vielen Kniffen, mit denen ich mich herumschlug, heraus, dass das Ablegen von Listen mit Pixel-AN/AUS nicht effizient ist — mit dem Denken in Pixelumkehrungen kam ich weiter: Ist ein Pixel AN, wird es AUS (und umgekehrt).

Weiter beim Rendering: Unterstützung für Anti-Aliasing

Ich dachte zunächst, die Anti-Aliasing-Unterstützung sei für viele offensichtlich, aber anscheinend haben nur wenige den Dreh wirklich verstanden :) Es braucht also ein paar Erklärungen.

Es ist kein Nacheffekt, der angewandt wird, wenn das ganze Bild fertig gezeichnet ist. Das wäre in der Ausführung recht langsam und würde vermutlich so viel Speicher fressen, dass ich gar nicht daran denken möchte :)

Also das schmutzige Geheimnis: Wie in diesem Artikel beschrieben, beruht der Effekt auf Kacheln. Es war keine zufällige Entscheidung, für die Kacheln eine Breite von 2 Pixeln zu wählen. Folglich gibt es für eine waagerechte Kachelzeile 4 mögliche Zustände: PIXEL AUS + PIXEL AUS, PIXEL AN + PIXEL AUS, PIXEL AUS + PIXEL AN, PIXEL AN + PIXEL AN. In der Demo Phreaks wird das jeweils zu 0 + 0, 1 + 0, 0 + 1, 2 + 2 (wobei 0, 1 und 2 Palettenindizes sind). 1 + 0 und 0 + 1 werden stets am Anfang und am Ende einer Zeile benutzt. Denken Sie darüber nach: Optisch täuscht das einen Anti-Aliasing-Effekt vor!

Weiter beim Rendering: Unterstützung für den Reverse-Modus

Der Reverse-Modus bedeutet, die Animation mit allen Pixeln waagerecht gespiegelt darzustellen. Dieser Kniff bringt eine gewisse Abwechslung in den optischen Effekt. In der Demo Phreaks wird das erreicht, indem alle Kacheln waagerecht gespiegelt werden (im Grunde wird also aus PIXEL AN + PIXEL AUS ein PIXEL AUS + PIXEL AN, und aus PIXEL AUS + PIXEL AN ein PIXEL AN + PIXEL AUS — was dank des Videomodus 0 des Amstrad CPC ziemlich schnell geht!). Zusätzlich muss beim Zeichnen einer waagerechten Kachelzeile auch die Position gespiegelt werden („X = Bildschirmbreite - X“).

Fazit

Am Ende hat es mich 4 Monate Arbeit gekostet, die Animationssequenz so zum Laufen zu bringen, wie ich es wollte. Es war ziemlich aufregend, zu denken, das endgültige Datenformat sei gefunden, und dann ein paar Tage später mit neuen, besseren Ideen zurückzukommen.

Wie Sie sehen, war das Erzeugen der Daten ein Riesenstück Arbeit. Genau das ist es, was den Leuten beim Anschauen einer Demo oft nicht bewusst ist. Tatsächlich bin ich auf die ganze Arbeit, die ich darum herum gemacht habe, ziemlich stolz — auch auf die Fehlschläge, auch angesichts all der Schritte, die zum endgültigen Ergebnis nötig waren. Ich habe die Grenzen dessen, was ich mit dem Amstrad CPC zu können glaubte, wirklich verschoben. Am liebsten denke ich daran, dass sich solche Daten mit einem originalen Amstrad CPC nicht erzeugen ließen; es steckt zu viel Rechnerei darin, zu viele Daten — in meinem Kopf ist das wirklich 2011. Und genau deshalb ist es so interessant, beim Amstrad CPC zu bleiben …

Ich hoffe, Sie halten die Demo Phreaks nun für einen echten Fortschritt in der (kleinen) Welt der CPC-Demos! :)

YouTube:

Die Demo Phreaks auf YouTube

Auf YouTube ansehen

Bilderfolge

Acht Standbilder, aufgenommen vom Diskettenabbild der Release.

Der Eröffnungstext: here we are, once again a new demo for you guys

Eine Tänzerin in Blau auf grünen Balken

Der Text: State of the Art, adapted on Amstrad CPC

Der Text: 195 frames, per-pixel based antialias, delta packing

Eine Tänzerin in Weiß auf hellblauen Balken

Der Gruß-Text, an Arkos und Vanity

Eine Tänzerin in Blau auf orangen Balken

Eine Tänzerin in Blau auf gelben Balken

Kommentare von Pouet

Die folgenden Kommentare wurden auf Pouet.net zu Phreaks hinterlassen. Sie sind so zitiert, wie sie geschrieben wurden. Von jener Seite übernommen am 17. August 2026.

Nice BASIC demo xD (No, really, guys, the demo is cool!)

Devilmarkus, 2010-12-16 · rulez

Youtube link : http://www.youtube.com/watch?v=N7o4sDbSQmg

norecess, 2010-12-16

Cool! Anims are slow. I like the antialiasing.

Optimus, 2010-12-16 · rulez

meh

kusma, 2010-12-16

Nice demo! But S.O.T.A. fans like me will always prefer dancing girls!

ham, 2010-12-16

Nice job!

Soundy, 2010-12-16 · rulez

technically cool i guess, designwise meh, music is annoying. Piggie

shock__, 2010-12-16

Just to explain a little bit.. The major thing to retain in this prod is not the FX itself ; it’s relatively trivial and everyone with some Z80 knowledge would be able to accomplish the same. No, the real thing is the number of UNIQUE frames, which is 195. Think about it : Amstrad CPC has 128Kb, remove 40Kb for video-memory (with the special resolution I use), remove 8Kb for the music (and its player), remove another 6Kb for the sprites displayed on top of the FX, then remove once again another 5Kb for the background, another 5Kb for the font, another 11Kb for the code itself… Let’s calculate… we have 53Kb free. So the 195 frames are packed in 53Kb. 20 frames is super easy to manage ; 50 frames : you need a good data structure then.. 195 frames is kind of big numbers here. Hope you realize that :)

norecess, 2010-12-17

Nice

Buckethead, 2010-12-17 · rulez

195 frames or not, this is pretty boring. basically, what kusma said.

StingRay, 2010-12-17

Alle 26 Kommentare anzeigenWeniger Kommentare anzeigen

Actually, the prog displays a single effect during its whole demonstration but i really like the anti-aliasing of the frames. Design is nice; same goes for the tune. Congrats to all involved; you managed to release (another) nice product for CPC just before the fall of 2010! Well done guys!

voxy, 2010-12-17 · rulez

Well done, you did it in the right time… What Stingray said ” it’s a pretty boring.” Graphics and music don’t suit this demo.

Beb, 2010-12-17

The visuals are boring, maybe you should have taken some wacky swirls or zoomer instead. The music is very nice though.

Exin, 2010-12-18 · rulez

well, why not?!

ɧ4ɾɗվ., 2010-12-18 · rulez

for the player

Shazz^TRSi, 2010-12-18 · rulez

The design is really… horrible. Ok for the perf, 195 frames in 53kb, but I must admit that “animations” are not my cup of tea (cf. Phat 2)…

Hicks, 2010-12-18

The technical achievement is definitely worth a thumb up! The animations seem a bit disconnected from the music though.

Rouquemoute, 2010-12-19 · rulez

Phreaks would have been an awesome demo if Condense hired a designer…

toms, 2010-12-20

watchable

comankh, 2010-12-20

+ generally positive feelings + nice packing - slow depacking & drawing for 4 mhz

Skate, 2011-01-08 · rulez

video please?

guardian ٩๏̯͡๏۶, 2011-01-08

@Guardian: Youtube.

Gʀɪʍʍy, 2011-01-09

Nice demo.

Octoate, 2011-03-21 · rulez

Models of the animation are not my kind of stuff but 195 frames of this size is not usual on CPC, I guess.

Eldrik, 2012-04-15 · rulez

Thumb up for the technical achievement. However, it would be far better (beauty and speed) too see with a cleaner design. I’m looking forward to a new demo

krusty, 2012-04-15 · rulez

good work norecess!

guardian ٩๏̯͡๏۶, 2013-03-04 · rulez