GG-Nodes

Was braucht man in Blender, um ein 3D-Häuschen zu modellieren?

Anfangen kann man mit einer Wand mit Löchern drin, dann eine Bodenplatte oder Geschossplatte mit Öffnungen. Fenster und Türen sind anschließend dran. Eine Treppe verbindet die Ebenen. Den oberen Abschluss bildet eine Sparrendachkonstruktion. Drumherum gibt’s Grasland.

Richtig Architektur kann man damit nicht betreiben, aber man hat vielleicht einen Einstiegspunkt.

Das habe ich nun mit parametrisierten Bauteilen realisiert:

Fenster oder Tür? Beides!

Habe in Blender erst jetzt die Geometry Nodes entdeckt. Das bietet die Möglichkeit, fantastische Objekte zu bauen. Seit meinen AutoCad-Zeiten vor 25 Jahren schwebte mir vor, ein Fenster-Element (also Fenster wie Fenster und nicht wie Windows) zu bauen, bei dem die Parameter Außenmaße, Rahmenbreite und -dicke und noch ein paar andere Werte einzugeben sind, und schwupps ist das Element gezeichnet. In Blender geht das nun ziemlich einfach. Ob es sich um ein Fenster oder eine Tür handelt, wird durch das Vorhandensein des unteren Rahmenelements im Blendrahmen entschieden.

Versprechen und Umsetzung

oder: Ein Butler für Mäuschen

Weihnachten 2021 hatte ich für mein Mäuschen „nur“ einen Gutschein

Ich konnte dabei auf eine 35 Jahre alte Butler-Planung von mir zurückgreifen, die aus der „guten, alten“ Analogzeit stammt.

Mithilfe „hochmoderner Digitaltechnik“ 😛 habe ich den damaligen Entwurf überarbeitet und Eddi Arent (RIP) als Hauptdarsteller verpflichtet:

Und nun isser fertig, live und in schwarz/weiss:

Willkommen, Eddi !

Ringgleis-Rallye

Letztes Jahr hatte ich eine Fahrrad-Rallye zum Braunschweiger Ringgleis veranstaltet. Da ich der Meinung bin, dass sich solch eine Veranstaltung geradezu anbietet, per Software unterstützt zu werden, schrieb ich die R-Rallye:

Die App ist eine Browser-Anwendung, voll funktionsfähig und für mobile Nutzung optimiert. Ein großer Teil der Aufgaben ist jedoch leider inzwischen veraltet, da sich die lokalen Gegebenheiten (z.B. Graffitis) geändert haben. Wenn ich mal wieder Zeit habe, werde ich die Aufgaben aktualisieren. Dabei unterstützt mich der Task-Editor. (Bei aktualisierten Aufgaben und Antworten wird der Task-Editor von hier natürlich nicht mehr erreichbar sein)

Exkurs über Webhosting

Hier eine kleine Übersicht über die Funktionsweise und die Bestandteile des ‚Webhostings‘. Also: wie veröffentliche ich eine eigene Website? Und zwar ohne mich bei Webbaukasten-Anbietern registrieren zu müssen und damit in den meisten Fällen im Netz eines Anbieters gefangen zu sein. Guckstu:

Wenn du eine Website erstellen willst, brauchst du einen Webhoster. Bei diesem bestellst du ein (kostenpflichtiges) Hostingpaket mit Wunschdomain („irgendeinentollenseitennamen.de“) und bekommst damit als Eigentümer Zugang zur Domain-Verwaltung und zum Webspace. Mithilfe eines FTP-Programms (z.B. FileZilla) und den vom Webhoster mitgeteilten Zugangsdaten machst du den Datei-Upload in deinen Webspace. Wenn du WordPress verwenden willst, lädst du dir die zip-Datei von wordpress.org herunter und packst den Inhalt des Unterordners ‚wordpress‘ auf den Webspace. Mittels der Domain-Verwaltung sagst du dem System, welcher Ordner des Webspace der Domain zugeordnet werden soll. Für WordPress (und andere CMS) benötigst du eine Datenbank, die über die Domain-Verwaltung eingerichtet werden kann. Nun kann die Designverwaltung aufgerufen werden (bei WordPress initial aufzurufen mit der domain und im Folgenden mit domain/wp-login) und der Spaß beginnt.

Die vorangegangenen Schritte sind im Allgemeinen nur einmalig auszuführen. Alles Folgende passiert innerhalb der Designverwaltung (z.B. innerhalb von WordPress).

Zu WordPress:

Worpress ist historisch gesehen eine Software , um Web-Blogs zu erstellen. Aus diesem Grunde unterscheidet die Admin-Oberfläche zwischen ‚Beiträgen‘ und ‚Seiten‘. Standardmäßig gibt es immer eine Blogseite, auf der die Beiträge gesammelt werden, und alle anderen Seiten, die üblicherweise auch im Menü zu finden sind. Ausgangspunkt für das Layout sind die ‚Themes‘. Einige sind bereits in der Installationsversion mitgeliefert, andere lassen sich (oftmals kostenpflichtig) nachinstallieren. Die Themes haben spezifische Einstellungsmöglichkeiten, die über den ‚Design -> Customizer‘ aufzurufen sind. Du solltest dir auf jeden Fall Zeit nehmen, um ein wenig rumzuexperimentieren. Das englischsprachige Manual https://codex.wordpress.org hilft auf jeden Fall.

Hier noch ein Hinweis zu den Sternchen * in der Grafik: alle Zugangsdaten sollten in deinem Passwortcontainer hinterlegt werden, z.B. KeePass.

Musik am Rechner

Nach einigen Forschungsaufwendungen ist mein DAW-Setup nun vorerst beendet.
Besonderheit dabei ist die ausschließliche Nutzung von Open-Source-Software und damit ein überschaubarer Kostenrahmen. Wenn ich mich ranhalte, lerne ich vielleicht demnächst auch ein wenig Klavierspielen und verbessere meine Gitarrenskills weiter. Im Moment jedoch ist es viel aufregender, die funktionierende Gesamtkonfiguration auszuprobieren durch Ausloten der runtergeladenen Soundfonts, rauszufinden, wie Drums gemacht werden und mit welchen Effekten die E-Gitarre versehen werden kann.

Das Setup:

Hardware

  • Rechner ist bestückt mit einem Quadcore-Prozessor mit 3,6 GHz und 8 GB RAM.
  • Betriebssystem Ubuntu 18.04.01 (Bionic) mit folgender Zusatzeinstellung: Eintrag in /etc/security/limits.conf:
@audio   -  rtprio     95
@audio   -  memlock    unlimited
  • Außerdem muss der aktuelle user (Benutzername) der Gruppe audio zugewiesen werden:
sudo usermod -a -G audio Benutzername
  • meine Instrumente, die einen „elektrischen Anschluß“ haben:
  • Als zusätzliche und fast wichtigste Hardwarekomponente kam als letztes ein Audio-Interface von Steinberg (UR22mkII) hinzu. Denn nur mit so einer externen Soundkarte gibt’s keine Probleme mit Brummen, Rauschen, Frequenzgang, Pegel, Impedanz und Latenzen. Das von mir gewählte Modell funktioniert out of the box. Ubuntu 18.04 findet sowohl die Audio-Ein- und Ausgänge als auch den MIDI-Port ohne zusätzliche Software. Der UR22 ist wie gemacht für meinen kleinen Instrumentenpool.

Software

  • qjackctl: dies ist das Frontend für den JACK-Server. JACK ist das System der Audio- und Midi-Signalverarbeitung unter Linux. Viele (wenn nicht gar die meisten) Audio- und Midi-Programme unter Linux unterstützen das JACK-Konzept. JACK ist der Dirigent der Audio-Programme und regelt die Signalwege und „timing-tasks“ und sollte deswegen immer als erstes gestartet werden. Meine JACK-Einstellungen: Abtastrate: 44100, Frames/Periode: 256 ergibt eine Latenz von 11,6 ms, ohne dass JACK in xruns läuft.
  • pulseaudio-module-jack: weil es auch Standardprogramme gibt (wie z.B. Browser), die den pulseaudio-Soundserver unterstützen, gibt es diese „Bridge“, die es z.B. ermöglicht, Audio-Signale aus Video-Portalen mit Effekten zu versehen und in eine Audiospur zu leiten.
  • Ardour: das Schweizer Taschenmesser für die Musikproduktion unter Linux. Audio- und Midi-Spuren in unbegrenzter Anzahl, Einfügen von Effektgeräten und Klangerzeuger in Form von (LV2-)Plugins.
  • Guitarix: der konfigurierbare Guitar-Amp für jeden Geschmack mit allen Effekten, die man so braucht. Plus Stimmgerät. Und für den schnellen Rhythmus sogar eine kleine „Hobby“-Drum-Engine.
  • Drumgizmo mit DRSKit: echte hochqualitative Drum-Samples (4,3 GB) zum Einbinden in Ardour. Wer noch mehr Drums will, verwendet Hydrogen.
  • calf-Plugins: Sammlung von spannenden LV2-Instrumenten, -Effekten, -Filtern und -Tools. Einmal installiert stehen sie sofort zum Einfügen in Audio- oder Midi-Spuren zur Verfügung.
  • X42-plugins: Sammlung von interessanten Plugins. Insbesondere reizte mich die darin enthaltene MIDI Filter Collection, und darin der MIDI Keysplit, der es ermöglicht, den Tastaturbereich des Midi-Keyboards in zwei oder mehrere Bereiche zu teilen: linke Hand Bass-Linie oder Drums, rechte Hand Harfe oder ähnliches.
  • Soundfonts: für das mit Ardour mitgelieferte Instrument a-fluid Synth gibt es Unmengen an Soundfonts zum Download (Dateien mit der Endung .sf2). Da hab ich auch eins der besten Pianos gefunden.

Wenn dann irgendwann tatsächlich vorzeigbare Ergebnisse vorliegen, werden sie hier vorgezeigt :).

musikalische Dinge

Im Zuge dessen, dass ich diesen Platz sowieso gerade als „Gerd-Museum“ nutze, dürfen meine historischen Gitarren-Aufnahmen nicht fehlen:

Damals (2006) nutzte ich noch das vielbenutzte „Fenster“-Betriebssystem und als Digital Audio Workstation verwendete ich MuLab in der Free-Version.

Spaceship Kemenate-Hagenbrücke

kemefotohinten

Es war schon immer ein Problem: Was passiert, wenn ein Raumschiff zu einem unbekannten Ziel gebeamt wird? Ist an dem neuen Ort überhaupt Platz für das gebeamte Objekt? Bei dem nun folgenden Objekt hat man das Gefühl „mh, ja, das ist ja gerade noch mal gut gegangen“.

Nee, im Ernst, das ist schon ein heftig cooles Kleinod, was die om-architekten da gezaubert haben, und mußte deswegen auch unters blender-threejs-Messer.

kemeblender

It’s just a feelin‘ (Geraldine)

Und schon wieder Blender. Yes, it rocks !
Route 66, Woodstock, Jimi Hendrix und ich im zarten Alter von 13 zeichnete meine Geraldine: die Materialisierung von Freiheit und Abenteuer.

geraldine2wp
rend_vorderrad

… und 40 Jahre später wurde die flache 2D-Zeichnung endlich zur dritten Dimension erhoben:

Low-Poly-Mesh fürs Web

Das Objekt meiner Betrachtung ist das Haus meiner Mutter.
Die Idee ist, ein minimiertes Gebäudemodell in blender zu bauen, eine perspektivkorrigierte Fototextur aufzubringen und zu hoffen, dass die Unzulänglichkeiten nicht so dominierend sind. Mein Ziel ist es, die Kubatur und „Atmosphäre“ eines Gebäudes rüberzubringen.

Original und „Fälschung“:

Das Blender-Export-Addon von MrDoob und ein wenig threejs-Script bescherte mir das. Das ist mit sehr wenig Aufwand (aus der Hüfte geschossene Fotos und ein Low-Poly-Model) schnell gemacht und erfüllt zumindest meine Erwartung. Dieses Model besteht aus nur 42 (!) Vertices.

Kleine Blender-Projekte

Virtueller Kartonmodellbau

Es gibt ja eine ziemlich große Gemeinde von Kartonmodellbauern. Dementsprechend komplexe und aufwendige Kartonmodelle gibt es: z.B. die gesamte Insel Mont-Saint-Michel oder die Sagrada Família. Ich beschränke mich als blutiger Anfänger auf einen einfacheren Kartonbogen, den es beim AGK gibt: es handelt sich um den Kartonbogen eines französischen Verlages eines deutschen Arbeiterhauses aus dem Jahre 1902.

wp1

Die meisten Leute, die sich mit diesem Metier beschäftigen, stellen sich die Frage, wie man aus einem 3D-Modell einen Modellbaubogen erstellt. (In der Tat gibt es dazu sogar ein Blender-Plugin).
Hier jedoch gehts andersherum: man nehme einen Modellbaubogen und benutze blender als virtuellen Cutter und Klebstoff. Ein Filmchen demonstriert, was ich meine.

Mein Gesellenstück

Schön, wie sich vergangene Projekte eignen, mit neuen Mitteln verarbeitet zu werden. Mein Gesellenstück als Tischler entstand 1987:

wp1

Hier der Film dazu.

Verflixte Parallaxe

Da stand ich nun im Garten meiner Mutter, stellte mich genau in die Mitte, und schoss 42 Bilder mit meiner Digicam, indem ich mich um meine eigene Achse drehte. Irgendwo in meinem Hinterkopf hatte ich zwar das Parallaxenproblem, aber ich dachte: die Software gleicht das schon aus. Denkste ! Zwei Bilder, die sich überlappen, aber von leicht unterschiedlichen Standorten aufgenommen, können partout nicht miteinander kombiniert werden, ohne dass es merkwürdige Phänomene gibt (s. Panorama). Ich denke, es wird Zeit, ein ordentliches Stativ mit Panoramakopf zu besorgen.

IMG_2871 - IMG_2913_blended_fused-k

(Panorama erstellt mit Hugin, Panoramaplayer mit three.js von MrDoob)

Minimalistisches CMS

An den großen Contentmanagement-Systemen hat mich immer der enorme Umfang genervt. Da werden Unmengen an MBs auf den Webserver geladen und diejenigen, die im Alltag die Inhaltspflege übernehmen sollen, müssen aufwendig angelernt werden. Mal ehrlich: es ist fast einfacher, HTML, CSS und JavaScript von der Pike auf zu erlernen als die Redaktionsoberfäche von Typo3  (oder WordPress 😉 ) zu bedienen. Das hat mich dazu animiert, das onefile_cms zu schreiben. Eine einzige kleine PHP-Script-Datei und ein HTML-Template mit nur 5 Variablen sind die Zutaten. Natürlich kann damit kein Enterprise-Portal entwickelt werden, aber für eine mehrseitige kleine Homepage mit 1-stufigem Menü reicht’s.

<?php

// onefile_cms by G. Drinkmann Ver 0.12 (29.05.2013)
// Contentmanagement-System mit Wiki-Dialekt zur Contentbearbeitung
// Dieser Quelltext kann völlig frei verwendet werden

error_reporting(E_ALL);

// ############################ HTML-Template ##########################################

// das Template sollte folgendes enthalten:

// Variablen im Template:
// <!-- {sitetitle} -->
//    wird ersetzt durch Homepage-Titel
// <!-- {menutitle} -->
//    der Menüname der angezeigten Seite
// <!-- {ul-menu} -->
//    Menü als automatisch erzeugte ul-Liste (CSS s.u.)
// <!-- {content-width} -->
//    wird ersetzt durch Content-Breite
// <!-- {content} -->
//    der eigentliche Seiteninhalt aus der Redaktionsseite

// CSS:
// #menu   id des ul-Elements
// #menu li   z.B.{display:inline; list-style:none;}
// #menu a.menunormal   class-Attribut des a-Elements im Menü
// #menu a.menucurrent   class-Attribut des aktiven a-Elements im Menü
// img.Lfloat   class-Attribut von linksbündig formatierten Bildern mit Text-Umlauf
// img.Rfloat   class-Attribut von rechtsbündig formatierten Bildern mit Text-Umlauf

// Konvention:
// Namen der Bilder des Templates sollten mit "ONEFILECMS_" beginnen

// ------- HTML-Template-Datei ---------------------
$ggtplfile = "tpl1.html";

// ############################ Script-Variablen #######################################

// ------- Homepage-Titel --------------------------------------------------------------
$ggsitetitel = "Kiosk JOJO";

// ------- md5-Passwort (zu erstellen bei z.B. http://www.functions-online.com/md5.html)
$ggpass = "e6b3cd51a7afb493493d0c4a0bb0c8b2"; // jo33jo (bitte comment löschen)

// ------- Edit-URL-Parameter (m=) zum Aufruf der Redaktionsseite ----------------------
$ggeditpara = "edit"; // ohne Leer- oder Sonderzeichen !

// ------- Template ist XHTML ----------------------------------------------------------
$ggisxhtml = false;

// ------- Content-Breite --------------------------------------------------------------
$ggcontwidth = "750px";

// ############################ ab hier Chefsache !! ###################################
$ggcontfile = "content1file.txt";
$ggxsl = ($ggisxhtml) ? " /" : "";
function ggerror($ggmsg) {
    global $ggcontent, $ggsitetitel, $ggxsl;
    $ggcontent[0]['mtitle'] = "FEHLER";
    $ggcontent[0]['read'] = "<i>".$ggmsg."</i><br".$ggxsl."><br".$ggxsl.">Die Seite \"".$ggsitetitel."\" ist momentan nicht verf&uuml;gbar.<br".$ggxsl.">Versuchen Sie es bitte sp&auml;ter nocheinmal.<br".$ggxsl.">Danke !";
}
if (isset($_GET['m'])) { // Check des GET-Parameter
    if ($_GET['m'] === $ggeditpara) $ggpar = "e";
    else $ggpar = intval($_GET['m']);
} else $ggpar = 0;
if ($ggpar === "e") { // Seite editieren
    if (md5($_POST['pass']) === $ggpass && isset($_POST['content'])) {
        file_put_contents("./".$ggcontfile, urldecode($_POST['content']));
        if (isset($_FILES['pic'])) {
            if (preg_match("/\.(jpg|gif|png)$/i", $_FILES['pic']['name']) || ($ggtplfile === $_FILES['pic']['name'])) {
                move_uploaded_file($_FILES['pic']['tmp_name'], "./".$_FILES['pic']['name']);
            }
        }
        if (isset($_POST['delimg'])) {
            for ($i=0; $i<count($_POST['delimg']); $i++) {
                @unlink("./".urldecode($_POST['delimg'][$i]));
            }
        }
    }
    if (file_exists("./".$ggcontfile)) {
        $ggfstr = file_get_contents("./".$ggcontfile);
    } else $ggfstr = utf8_encode("; Bitte mit Inhalt füllen");
    $ggpiclis = ""; // vorhandene Bilder listen
    if ($gghandle = opendir('.')) {
        while (false !== ($ggfile = readdir($gghandle))) {
            if (preg_match("/\.(jpg|gif|png)$/i", $ggfile) && (!preg_match("/^ONEFILECMS_/", $ggfile))) {
                $ggpiclis .= "{{".$ggfile."}}<input type='checkbox' name='delimg[]' value='".$ggfile."' title='Bild l&ouml;schen'><img onmouseover='document.getElementById(\"preview\").src = this.src; document.getElementById(\"preview\").className=\"gvisible\"' onmouseout='document.getElementById(\"preview\").className=\"ghidden\"' class='pici' src='".$ggfile."'> &nbsp;&nbsp;";
            }
        }
        closedir($gghandle);
    }
    if (!$ggpiclis) $ggpiclis = "<i>Keine</i>";
    $ggaction = $_SERVER['PHP_SELF']."?m=".$ggeditpara;
    $ggeditsite = <<<EOTE
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>{$ggsitetitel} editieren</title>
<style type="text/css">
* {font-family:arial}
img.pici {border:0;padding:0;margin:0;height:18px}
img.gvisible {visibility:visible}
img.ghidden {visibility:hidden}
</style>
</head>
<body style="background-color:#ebebeb">
<h2>Website <a href="{$_SERVER['PHP_SELF']}" style="color:red" target="_blank"><i>{$ggsitetitel}</i></a> editieren (Inhalts-Breite <span style="color:red">{$ggcontwidth}</span>):</h2>
<p style="font-size:10px">Zeile mit ; am Anfang = Kommentar<br>
M--- am Anfang der Zeile = neuer Men&uuml;titel<br>
HTML-Tags werden ausgef&uuml;hrt<br>
== am Anfang der Zeile = gro&szlig;e &Uuml;berschrift<br>
=== am Anfang der Zeile = kleinere &Uuml;berschrift<br>
| am Anfang der Zeile = Tabelle (weitere Zellen mit | trennen)<br>
* am Anfang der Zeile = Liste<br>
{{*.jpg oder gif oder png}} = Bild in Originalgr&ouml;&szlig;e<br>
{{*.jpg oder gif oder png|L oder R}} = Bild links bzw. rechts mit Text-Umlauf<br>
[[Men&uuml;index|Linktext oder -bild]] = interner Link (Index beginnt bei 0)<br>
[[Linkadresse|Linktext oder -bild]] = externer Link<br>
**W&ouml;rter** = Fett<br>
//W&ouml;rter// = Kursiv<br>
---- = horizontale Linie<br>
((x)) am Anfang = Text-Umlauf (Float) beenden</p>
<form name="f1" action="{$ggaction}" method="post" enctype="multipart/form-data">
<p><b>Inhalt</b><br>
<textarea name="content" style="width: 600px; height: 300px; font-size: 12pt">{$ggfstr}</textarea></p>
<p id="piclist"><b>Hochgeladene Bilder:</b><br>
{$ggpiclis}</p>
<p><b>Datei hochladen:</b> jpg, gif, png oder Template <i>{$ggtplfile}</i><br>
<input type="file" name="pic" size="61"></p>
<p>Passwort <input type="password" onkeydown="document.getElementsByName('OK')[0].disabled=false" name="pass" style="width: 200px;">
<input style="font-weight:bold" type="submit" name="OK" value="Seite speichern" disabled="disabled"></p>
</form>
<img class="ghidden" id="preview" src="nixda" style="position:absolute; left:3px; top:3px; border:solid #808080 30px;">
</body>
</html>
EOTE;
    echo $ggeditsite;
} else { // normaler Seitenaufruf
    if (file_exists("./".$ggtplfile)) {
        $ggsitestr = file_get_contents("./".$ggtplfile);
    } else die("TEMPLATE NICHT VORHANDEN !");
    $ggcontent = array(); // Initialisierung des Content-Array
    $ggcount=-1;
    if (file_exists("./".$ggcontfile)) { // Steuerdatei ins Content-Array einlesen
        $ggf = fopen("./".$ggcontfile, 'rb');
        while (!feof($ggf)) {
            $ggtmp = rtrim(fgets($ggf));
            if (substr(ltrim($ggtmp),0,4)==="M---") {
                $ggcount++;
                $ggcontent[$ggcount]['mtitle'] = ltrim(substr(ltrim($ggtmp),4));
                $ggcontent[$ggcount]['mtitleclass'] = "menunormal";
                $ggcontent[$ggcount]['read'] = "";
            } elseif (substr(ltrim($ggtmp),0,1)!==";") {
                $ggcontent[$ggcount]['read'] .= $ggtmp."<brbr>";
            }
        }
        fclose($ggf);
        if ($ggcount === -1) ggerror("Fehler: Kein Men&uuml; in Steuer-Datei eingetragen");
    } else ggerror("Fehler: Steuer-Datei nicht vorhanden");

    if ($ggpar>$ggcount) $ggpar = 0;
    $ggcontent[$ggpar]['mtitleclass'] = "menucurrent";
    $ggsitestr = str_replace("<!-- {sitetitle} -->", $ggsitetitel, $ggsitestr);
    $ggsitestr = str_replace("<!-- {menutitle} -->", $ggcontent[$ggpar]['mtitle'], $ggsitestr);
    $ggsitestr = str_replace("<!-- {content-width} -->", $ggcontwidth, $ggsitestr);
    // --- Menü zusammenbauen ---
    $ggmenustr = "<ul id=\"menu\">\n"; 
    for ($i=0; $i<count($ggcontent); $i++) {
        $ggmenustr .= "<li><a href=\"".$_SERVER['PHP_SELF']."?m=".$i."\" class=\"".$ggcontent[$i]['mtitleclass']."\" target=\"_self\">".$ggcontent[$i]['mtitle']."</a></li>\n";
    }
    $ggmenustr .= "</ul>\n";
    $ggsitestr = str_replace("<!-- {ul-menu} -->", $ggmenustr, $ggsitestr);
    // --- Content zusammenbauen ---
    $ggcont = $ggcontent[$ggpar]['read'];
    $ggcont = explode("<brbr>",$ggcont);
    $ggcontstr = "";
    $istab = 0; // Tabelle
    $isul = false; // Bullet-Liste
    $ggcleardiv = "<div style='clear:both;visibility:hidden;width:0;height:0;margin:0;padding:0;border:0;'></div>\n";
    for ($i=0; $i<count($ggcont); $i++) {
        if (substr(ltrim($ggcont[$i]),0,5)==="((x))") {
            $ggisclear = " style='clear:both'";
            $ggcont[$i] = substr(ltrim($ggcont[$i]),5);
        } else $ggisclear = "";
        if (substr(ltrim($ggcont[$i]),0,1)==="|") {
            $ggtra = explode("|",substr(ltrim($ggcont[$i]),1));
            if (!$istab) {
                $ggcontstr .= "<table".$ggisclear.">\n";
            } elseif ($istab !== count($ggtra)) {
                $ggcontstr .= "</table>\n<table".$ggisclear.">\n";
            }
            $istab = count($ggtra);
            $ggcontstr .= "<tr>";
            for ($j=0; $j<$istab; $j++) {
                $ggcontstr .= "<td>".trim($ggtra[$j])."</td>";
            }
            $ggcontstr .= "</tr>";
        } else {
            if ($istab) $ggcontstr .= "</table>\n";
            $istab = 0;
            if ((substr(ltrim($ggcont[$i]),0,1)==="*") && (substr(ltrim($ggcont[$i]),1,1)!=="*")) {
                if (!$isul) $ggcontstr .= "<ul".$ggisclear.">\n";
                $isul = true;
                $ggcontstr .= "<li>".trim(substr(ltrim($ggcont[$i]),1))."</li>";
            } else {
                if ($isul) $ggcontstr .= "</ul>\n";
                $isul = false;
                if (substr(ltrim($ggcont[$i]),0,3)==="===") {
                    $ggcontstr .= "<h2".$ggisclear.">".substr(ltrim($ggcont[$i]),3)."</h2>";
                } elseif (substr(ltrim($ggcont[$i]),0,2)==="==") {
                    $ggcontstr .= "<h1".$ggisclear.">".substr(ltrim($ggcont[$i]),2)."</h1>";
                } elseif (trim($ggcont[$i])==="----") {
                    $ggcontstr .= "<hr".$ggisclear.">";
                } elseif ($ggisclear) {
                    $ggcontstr .= $ggcleardiv.$ggcont[$i]."<br".$ggxsl.">";
                } else $ggcontstr .= $ggcont[$i]."<br".$ggxsl.">";
            }
        }
        $ggcontstr .= "\n";
    }
    $ggcontstr = preg_replace("/{{(\S.*\.(jpg|JPG|gif|GIF|png|PNG))\|L}}/", "<img src=\"$1\" style=\"float:left\" class=\"Lfloat\"".$ggx.">", $ggcontstr); // Bilder mit float links
    $ggcontstr = preg_replace("/{{(\S.*\.(jpg|JPG|gif|GIF|png|PNG))\|R}}/", "<img src=\"$1\" style=\"float:right\" class=\"Rfloat\"".$ggx.">", $ggcontstr); // Bilder mit float rechts
    $ggcontstr = preg_replace("/{{(\S.*\.(jpg|gif|png))}}/i", "<img src=\"$1\"".$ggx.">", $ggcontstr); // Bilder
    $ggcontstr = preg_replace("/\[\[(\d+)\|(\S.*\S|\S)]]/", "<a href=\"".$_SERVER['PHP_SELF']."?m=$1\" target=\"_self\">$2</a>", $ggcontstr); // interne Links
    $ggcontstr = preg_replace("/\[\[(\S+)\|(\S.*\S|\S)]]/", "<a href=\"$1\" target=\"_blank\" title=\"Link in neuem Fenster\">$2</a>", $ggcontstr); // externe Links
    $ggcontstr = preg_replace("/ /", "&nbsp;", $ggcontstr); // Leerzeichen
    $ggcontstr = preg_replace("/&nbsp;(\S)/", " $1", $ggcontstr);
    $ggcontstr = preg_replace("/\*\*(\S.*\S|\S)\*\*/", "<strong>$1</strong>", $ggcontstr); // Fett
    $ggcontstr = preg_replace("#//(\S.*\S|\S)//#", "<em>$1</em>", $ggcontstr); // Kursiv
    $ggsitestr = str_replace("<!-- {content} -->", $ggcontstr, $ggsitestr);
    $ggsitestr .= $ggcleardiv;
    echo $ggsitestr;
}
?>

Also, man nehme den Quelltext, erstelle daraus eine Datei names index.php, fülle die Variablen aus, erstelle eine Template-Datei (mit dem Namen wie bei den Variablen angegeben) und lade beide Dateien (inklusive der zum Template gehörigen Bilder) auf den Webserver. Die index.php-Datei beinhaltet beides: die Maschinerie der eigentlichen Website für den Betrachter und die der Redaktion. Die Redaktionsseite wird über den Link <DeineURL>/index.php?m=<Variable URL-Parameter> aufgerufen. Ok, es sind mehr als nur eine Datei im Spiel, doch das eigentliche Programm besteht in der Tat nur aus dieser einen PHP-Datei.

Das Template sollte eine ganz normale HTML-Seite sein. Die Namen der Templatebilder sollten mit „ONEFILECMS_“ beginnen. Folgende Variablen können in diesem Template plaziert sein:

  • <!– {sitetitle} –> der Homepagetitel
  • <!– {menutitle} –> der Menüname der angezeigten Seite
  • <!– {ul-menu} –> Seiten-Menü als automatisch erzeugte ul-Liste
  • <!– {content-width} –> Breite des Inhaltbereichs
  • <!– {content} –> der eigentliche Seiteninhalt

Hier ist eine kleine Testseite zur Anschauung: der Kiosk des (fiktiven) Jojo

kioskjojo

Das zugehörige Template ist die Datei tpl1.html. Im Quelltext dieser Seite sind die Variablen sichtbar.

Jojo’s Kiosk kann mit der Edit-Seite bearbeitet werden.

kioskjojoedit

Dabei wird zur leicht erlernbaren Bearbeitung ein Wiki-Dialekt angewendet. Hat Ähnlichkeit mit Creole. Zum Speichern wird JEDESMAL das Paßwort abgefragt. Das ist ein wenig lästig, aber sicher.