Files |  Tutorials |  Articles |  Links |  Home |  Team |  Forum |  Wiki |  Impressum

Aktuelle Zeit: Do Jul 19, 2018 20:27

Foren-Übersicht » Sonstiges » Projekte
Unbeantwortete Themen | Aktive Themen



Ein neues Thema erstellen Auf das Thema antworten  [ 4 Beiträge ] 
Autor Nachricht
 Betreff des Beitrags: Zero-Prime
BeitragVerfasst: Do Jul 24, 2014 09:29 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2523
Wohnort: Berlin
Programmiersprache: C/C++, C#
Dieses Projekt ist der 2. Anlauf für ein Player driven PvP Sandbox MMO.
Zero-Prime hab ich vor vielen Jahre mit ein Freund auf Papier gebracht und in Laufe der Entwicklung bin ich dann auch auf DelphiGL gestoßen und seit dem seit ihr mich auch nicht mehr los geworden :twisted:
Das Projekt ist eingeschlafen, weil Erfahrung und Technologie fehlte aber das Game Design Dokument haben wir über die Jahre aktuell gehalten.

Nun, wo mein Radon Framework doch recht brauchbar ist und ich diverse Middlewares durch hab fühle ich mich in der Lage in die 2. Runde zu gehen.
Ich werde in Laufe der Zeit mehr über das Spiel und die Entwicklung schreiben.

Die Entwicklung hat am 18.07.2014 begonnen und der Stand ist folgender.
Das ganze Projekt liegt auf meinem Privaten SVN Server und Teile sind als Projekte auf GitHub zu finden.

Ich verwende aktuell folgende Bibliotheken und Frameworks(19.09.2014).
Radon Framework
Radon CMake Framework
Radon Fast Entity Component System
Sony ATF(Tools mit ui)
libRocket
AssImp
FMOD Studio
Bullet Physics
LuaJit
RakNet
PhysFS
LZMA SDK
cjson
lua filesystem

Tools:
texconv vorher NVidia Texture Tools
Substance Designer
Blender
Audacity
Radon Convert
ZP Model Converter
DOM Convert
ManicTime(um ein Gefühl für die Entwicklungszeit zu bekommen)

Eckdaten(19.08.2014)
-OpenGL 4.5 fähiger Client mit Radon Fast Entity Component System integration.
-Radon Convert für hot asset loading und automatisierte Asset Erzeugung
-Dom Converter für das konvertieren von json zu hash listen
-Model Converter zum konvertieren von Collada(dae), Blender(blend) und FBX
-Scene Editor
-Zero Driver Overhead Pipeline

In Arbeit(19.08.2014)
-Forward+ Rendering
-physical based rendering
-procedural planet rendering

_________________
"Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren"
Benjamin Franklin

Projekte: https://github.com/tak2004


Zuletzt geändert von TAK2004 am Di Aug 19, 2014 22:05, insgesamt 4-mal geändert.

Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Zero-Prime
BeitragVerfasst: Mi Aug 06, 2014 23:32 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2523
Wohnort: Berlin
Programmiersprache: C/C++, C#
Ich habe ein DOMConverter geschrieben, welcher json Datein lesen und in HashListen flatten kann.
Folgendes ist meine Render Pipeline Konfiguration und dient als Beispiel um das Konzept zu erklären.
Code:
  1. {
  2.     "global_resources":{
  3.         "render_targets":[
  4.             {"name":"depth_buffer", "format":"D24F", "scale_width":1.0, "scale_height":1.0, "clear": true},
  5.             {"name":"albedo", "format":"RGBA8B", "scale_width":1.0, "scale_height":1.0, "clear": false},
  6.             {"name":"normal", "format":"RGBA8B", "scale_width":1.0, "scale_height":1.0, "clear": false},
  7.             {"name":"roughness", "format":"RGBA8B", "scale_width":1.0, "scale_height":1.0, "clear": false},
  8.             {"name":"hdr_accumulation", "format":"RGBA16F", "scale_width":1.0, "scale_height":1.0, "clear": true}
  9.         ],
  10.         "render_passes":[
  11.             {"name":"z_prepass", "depth_buffer":"depth_buffer", "process":"FRONT_BACK"},
  12.             {"name":"gbuffer", "render_targets":["albedo", "normal", "roughness"], "depth_buffer":"depth_buffer", "process":"PARALLEL"},
  13.             {"name":"deferred_shading", "render_targets":["hdr_accumulation"], "depth_buffer":"depth_buffer", "process":"PARALLEL"},
  14.             {"name":"transparent", "render_targets":["hdr_accumulation"], "depth_buffer":"depth_buffer", "process":"BACK_FRONT"}
  15.         ]
  16.     }
  17. }

Die Datein besteht aus mehreren Leveln, von Objekten und Arrays, wie es für ein DOM Tree üblich ist.
Der DOM Konverter liest nun die Datei, und für jeden Endknoten wird ein Eintrag mit dem Weg zum Knoten und dem Wert angelegt.
Ein Eintrag in der Liste wäre folgender.
Code:
  1. Key = .global_resources.render_targets[].name_0
  2. Value = depth_buffer

Die Klammern[] makieren ein Array und _0 sagt das das erste Array Index 0 ist also eigentlich sage ich damit foglendes.
Code:
  1. .global_resources.render_targets[0].name

Also wie schreib ich das nicht einfach so hin ?
Ich vermeide Strings im Code und in den Daten und mache aus allen Keys und Strings Hashes und speicher diese.
Wenn ich nun den Eintrag zugreifen will, dann müsste ich Teile des Strings im Code hinterlegen, weil erst zur Laufzeit bekannt ist, welchen Eintrag ich denn im Array zugreifen will und man würde in etwa sowas verwenden.
Code:
  1. auto key = String::Format(".global_resources.render_targets[%d].name", index);

Wie gesagt möchte ich Strings im Code vermeiden und man kann das einfacher Lösen, wenn man die Dynamischen Informationen nachgelagert in den Hash einfliessen lässt.
Code:
  1. pass.Name = m_Config.ExpectString(RF_HASH(".global_resources.render_passes[].name"), i);

RF_HASH ist ein Template Konstruktion, welche der Compiler bei aktiver Optimierung zu einer Zahl(RF_TYPE::UInt64) runter vereinfacht und damit zur Runtime gar kein String existiert.
Im 2. Schritt werden alle übergebenen Indices zu einem String zusammen gebaut, durch "_" voneinander getrennt und am Anfang der Hash gesetzt.
Code:
  1. 13853049662206441152_2_0

Dieser String wird zur Runtime Konstruiert und ist im Stringpool ledeglich nur 1x als "%llu" und 1x als "_%llu"(llu == UInt64) zu finden, egal welche Kombinationen ich verwende.
Nun weiter, der String wird auch durch die gleiche Hash Funktion zur laufzeit gejagt und der neue Hash entspricht einen existierenden Hash Eintrag in der Hashliste, die der DOM Konverter gebaut hatte.
Code:
  1. DomConvert output
  2. String = 8986800961242094592_3
  3. String = 11476899374313023506_3
  4. Parameter count = .global_resources.render_passes[].render_targets = 12797011335562384212
  5. Array size = 12797011335562384212_3
  6. String = 13853049662206441152_3_0
  7. Parameter count = .global_resources.render_targets = 705631002783424115
  8. Array size = .global_resources.render_targets
  9. Parameter count = .global_resources.render_targets[].clear = 4601265248431463828
  10. Integer type = 4601265248431463828_0
  11. ...

Folgendes ist dann ein Code Auszug aus meiner Renderer Klasse.
Code:
  1.         m_Passes.Resize(m_Config.ExpectInteger(RF_HASH(".global_resources.render_passes")));
  2.         for (RF_Type::Size i = 0; i < m_Passes.Count(); ++i)
  3.         {                
  4.             auto& pass = m_Passes(i);
  5.             pass.Name = m_Config.ExpectString(RF_HASH(".global_resources.render_passes[].name"), i);
  6.             pass.DepthBuffer = m_Config.ExpectString(RF_HASH(".global_resources.render_passes[].depth_buffer"), i);
  7.             pass.Process = m_Config.ExpectString(RF_HASH(".global_resources.render_passes[].process"), i);
  8.             pass.BindTargets.Resize(m_Config.ExpectInteger(RF_HASH(".global_resources.render_passes[].render_targets"), i));
  9.             for (RF_Type::Size j = 0; j < pass.BindTargets.Count(); ++j)
  10.             {
  11.                 pass.BindTargets(j) = m_Config.ExpectString(RF_HASH(".global_resources.render_passes[].render_targets[]"), i, j);
  12.             }
  13.         }

Der Code ist leserlicher, weil man die Lesbaren Pfade sieht und der Code ist schneller, weil man zur Laufzeit mit UInt64 Werten rum hantiert und Hash Tables mögen die wesentlich mehr.

Das DomConvert Tool kann auch noch Resolve Points speicher, also Hash und String, damit man wieder an den Wert kommt und die Keys kann man auch mit Echtnamen exportieren lassen aber benutzen tu ich das nicht.
Aktuell verwende ich folgende Varianten für die jeweiligen Use-Cases.
  • globalconfig.json mit Value Resolve Entries
  • localization/*.json mit Value Resolve Entries
  • materials/*.json nur Hashes
  • renderer/*.json nur Hashes
  • textures/*.json nur Hashes
  • shaders/*.json nur Hashes

Die globalconfig.json enthält Engine spezifische Pfade, die natürlich als Hash nich so viel bringen.
Code:
  1. {
  2.     "RendererConfigPath":"renderer/config.json.table",
  3.     "SoundConfigPath":"sound/config.json.table",
  4.     "LocalizationPath":"localization/",
  5.     "DefaultLanguage":"en",
  6.     "resolve":[
  7.         {"from":"data", "to":"appdir:data/"},
  8.         {"from":"shader", "to":"data:shaders/"},
  9. ...


Die Lokalisierung guckt nach [Sprache]_[unwichtigername].json und entscheidet ob die benötigt wird.
Code:
  1. {
  2.     "groupname" : "errors",
  3.     "NoRenderConfig" : "Couldn't load renderer configuration!"
  4. }

Im Programm hab ich dann folgendes.
Code:
  1. RF_IO::LogError(Localization::Instance().GetText(RF_HASH("errors"), RF_HASH(".NoRenderConfig")).c_str());


Der Renderer lädt und Konfiguriert sich übrigens zur Laufzeit neu, wenn ich Änderungen an der Konfiguration mache.
Der RadonConvert Prozess läuft immer im Hintergrund und kümmert sich um das automatische Konvertieren und benachrichtigen von den Verbundenen Clients.
Folgendes ist mein buildconfig.lua Script, welches ragiert, wenn das geänderte Asset aus dem renderer Ordner kommt und mit eine Json Datei ist.
Der Client wird dann durch GenerateAsset Informiert, dass das outputFilename Asset neu geladen werden sollte.
Code:
  1. function splitfilename(strfilename)
  2.     -- Returns the Path, Filename, and Extension as 3 values
  3.     return string.match(strfilename, "(.-)([^\\/]-([^\\/%.]+))$")
  4. end
  5.    
  6. function start(inputFilename)
  7.     path, file, extension = splitfilename(inputFilename)
  8.     outputFilename = path..file..".table"
  9.     if (path == "renderer/" and extension == "json") then
  10.         Execute("DOMConverter.exe", "-i "..GetAssetDir()..inputFilename.." -o "..GetExportDir()..outputFilename)
  11.         GenerateAsset(outputFilename)
  12.     end
  13. end

_________________
"Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren"
Benjamin Franklin

Projekte: https://github.com/tak2004


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Zero-Prime
BeitragVerfasst: Di Aug 19, 2014 21:41 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2523
Wohnort: Berlin
Programmiersprache: C/C++, C#
Diesmal gibt es die ersten bunden Bildchen nicht wie bei Radon Framework, dort gibt es immer bunten Code und viiiieeeel Text :roll:

Content Pipeline
Ich hab kräftig an der Pipeline geschraubt, daher die folgende Zerlegung.
Converter
Wie im vorigen Post schon geschrieben benutze ich Lua Code um die einzelnen Arten von Assets zu verarbeiten.
Meine Shader werden durch ein buildshader.lua file verarbeitet.
Code:
  1. ...--util code
  2.  
  3. function start(inputFilename)
  4.     path, file, extension = splitfilename(inputFilename)
  5.     if (path == "shaders/") then
  6.         if (extension == "json") then
  7.             outputFilename = "shaders/"..Hash(inputFilename)
  8.             Execute("DOMConverter.exe", "-i "..GetAssetDir()..inputFilename.." -o "..GetExportDir()..outputFilename)
  9.         else
  10.             outputFilename = "shaders/"..Hash(inputFilename)
  11.             parameters = translate(GetAssetDir()..inputFilename.." "..GetExportDir()..outputFilename)
  12.             os.execute("copy "..parameters)
  13.         end
  14.        
  15.         file, extension = GetFileAndExtension(inputFilename)
  16.         outputFilename = file..'.json'        
  17.         GenerateAsset(outputFilename)
  18.     end
  19. end

Sollte im shaders Ordner eine json file verändert werden, dann wird diese durch den DOMConverter Tool gejagt und updated die Shader Konfiguration.
Code:
  1. {
  2.     "shaders":[
  3.         { "type":"vertex", "file":"shaders/static.vert" },
  4.         { "type":"fragment", "file":"shaders/static.frag" }
  5.     ]
  6. }

Alles andere wird einfach in den Shader Ordner kopiert mit einem Namen, der durch ein Hash kommt.
Die ganze Hash Geschichte hab ich im vorigen Post erklärt, wer es etwas Theoretischer mag kann da mal rein lesen.
Das GenerateAsset sagt mein Client bescheit und meine Engine lädt dann die Shader on the fly neu.
Dabei ist es wichtig zu sagen, dann dies nur passiert, wenn der Shader schon geladen ist und dabei nicht das alte Shader Programm zerstört wird, sondern die neu kompilierten Shader erneut linkt und die Shader wieder weg räumt.
Da die Programm ID die gleiche bleibt, muss ich keine Resourcen bescheit geben, dass die Updaten müssen.
Damit hab ich den ersten Teil der Hot Asset Loading Pipeline fertig :)

Mein 2. fertige Script ist buildtexture.lua und da hab ich sehr viel Liebe rein fließen lassen.
Ich post das Script nicht, weil es einfach viel zu lang ist und es nicht wirklich eindampfen kann.
Das Script guckt ob es eine File ist, dessen Dateiendung es verarbeiten kann("bmp", "dds", "tiff", "tga", "jp2", "jpg", "jpeg", "png", "svg", "psd", "json") und triggered darauf hin den Konvertierungsprozess.
Sollte für das Bild keine json file existieren, dann erzeugt das Script ein default Konfig.
Code:
  1. {
  2.     "file":"radonframework3.svg",
  3.     "width":512,
  4.     "height":256,
  5.     "filter":"Kaiser",
  6.     "mipmaps":true,
  7.     "colorspace":"sRGB",
  8.     "outputformat":"bc7_unorm_srgb"
  9. }

Die Bildgröße hole ich mir mit identify von ImageMagick und berechne dann die nächste pow2 größe und resize und crop mit convert von ImageMagick.
Dabei wird auch gleich das bild in sRGB Farbraum konvertiert und als png in ein Temp Ordner abgelegt.
Wenn man irgendwas mit Bildern machen will, dann führt eigentlich kein Weg an ImageMagick vorbei, da wirken selbst Photoshop und Gimp's Fähigkeiten in Sachen Bildverarbeitung wie primitive Stock und Stein Werkzeuge.
Mir war wichtig, dass ich bei der Skalierung und exportieren die maximale Qualität erhalte und ImageMagick bietet gleich eine Fantastilarde Tools um ein Bild in den passenden Farbraum zu bekommen, dann mit den richtigen filtern und Algorithmen es zu skalieren, korrigieren und dann in den finalen Farbraum zu konvertieren. Einiges ist auch schon OpenCL gestützt und das Tool unterstützt so ziemlich jedes Format, was ein Pixel sein könnte(Bild, Video, Volume und Vektor Formate).
Genug Lobpreisung nun hab ich ein png, welches in dem Fall der SVG File 4 16bit Kanäle nutzt und lossless kompression verwendet.
Diese File muss nun in DDS Format, denn so alles andere macht kein Sinn bei OpenGL, selbst rgb ist in DDS besser aufgehoben.
Erst hab ich hier für Nvidia Texture Tool verwendet aber ich war von ImageMagick so verwöhnt, dass ich mich zu sehr eingeschränkt fühlte und dann kann es nicht mal BC6 und BC7(halbwahr, gibt ne test implementierung die grottig lahm ist).
Ich hab dann nach einigem suchen Microsoft's Texture Tool gefunden und war begeistert.
Alle Operationen werden auf der GPU in einem DX11 Context gemacht, daher ist alles extrem schnell und selbst ein BC7 Bild braucht nur knapp 1.5 Sekunden statt 3-4Minuten wie beim NV Tool.
Es kennt jedes GPU Texturformat und weil DDS nun mal MS eigenes Format ist kann das Tool auch alles möglich rein quetschen, selbst volume arrays, cubemap arrays mit mipmap chains und natürlich ein stink normale Bild ^^
Ein weiterer Vorteil ist, dass texconv auch mipmap chains bis zu einer bestimmten tiefe beschränken kann.
Bei BCn oder DXT1-5 und BPTC sind die letzten 2 MipMaps für die Katz, weil kleiner als 4x4 net geht.
Das SVG wird dann letztlich zu einer DDS-BC7 Textur und in VS2013 Preview kann man dann folgendes betrachten(das checkboard visualisiert die transparenz).
Dateianhang:
vs2013_svg_preview.jpg
vs2013_svg_preview.jpg [ 105.94 KiB | 3715-mal betrachtet ]


Allerdings wird das noch nicht per Hot Asset Loading neu geladen, Texturen sind noch nicht mal in der Engine :\

Das 3. Script ist buildmodel.lua und funktioniert schon partiell.
Ich hab ein ModelConverter geschrieben, welcher AssImp verwendet, um mir ein festes Format raus zu schreiben, akzeptieren tut das Script "blend", "dae" und "fbx".
Folgendes Model hab ich mühselig gemodelt und gemapped, damit ich meine Pipeline testen kann, nicht nur Model sondern auch Material, Texture und natürlich das physical based rendering.
Dateianhang:
material_beacon.jpg [98.57 KiB]
Noch nie heruntergeladen

Das Model besteht aus 3 Materialien und hat mehere Layer um Overdraw und Transparenz zu testen und natürlich ein Affenkopf.

Scene Editor
Fanboy Faktor over 9000 !!!

Ich hab gestern auf dem Weg zur Arbeit mit mein Scene Editor angefangen und heute auf der Rückfahrt hab ich schon tatsächlich ein Stand, der mir korrekt ein Baum aufbauen lässt, Properties ändern und das ganze auch als XML wieder weg zu speichern.
Quasi produktiv nutztbar aber noch Luft für Verbesserung und das dank ATF(ist im Initialpost verlinkt).
Dateianhang:
editor_v1.jpg [125.62 KiB]
Noch nie heruntergeladen

Das einarbeiten, wie was Funktioniert und was eigentlich alles so gibt dauert schon ein Wochenende aber wenn man dann tatsächlich mit der Arbeit anfängt, dann kommt man sehr schnell zu den ersten Ergebnissen und viele mühselige Dinge sind einfach schon fertig verfügbar und werden nur ran gestöpselt.
Das Laden/Speicher, Undo/Redo, Datenbeschreibung, Datenaustausch und Editieren wird über eine XSL Beschreibung realisiert.
Man definiert einmal seine Objekte in XML und dann muss man noch die Files durch ein Codegenerator jagen und der genannte Teil ist getan.
Wenn man dann noch die Objekte in einer Palette haben will oder kein Standard Datentyp nimmt, dann muss man noch ein paar Zeilen zusammen kopieren aber auch das ist doch eher primitiv einfach.
Ein Kollege hat sein UI Editor damit geschrieben und er meinte, dass der größte Teil sehr schnell und einfach zu machen war aber er doch ein weilchen brauchte um sein Renderer zu integrieren. Allerdings hat er die schnelle und hilfreiche Antwort von den Entwicklern betont.
Ich grübel immer noch ob ich eine 3D Preview einbaue(nicht mein render system), weil ich spätestens beim platzieren von Objekten doch lieber eine direkte Kommunikation haben will und das Framework hat bereits Collada(war irgendwie klar, Sony's Framework nutzt Sony's Model Format ...) und ein OpenGL based Viewer Komponente drin.

Engine
Ich habe Triangle gemacht :D
Dateianhang:
Screenshot 2014-08-19 20.23.36.png
Screenshot 2014-08-19 20.23.36.png [ 49.05 KiB | 3715-mal betrachtet ]

Ich glaube das war das Aufwändigste Dreieck was ich je gerendert hab.
Erstmal musste ich Shader einbauen, oben schon beschrieben geht das nun, dann hab ich ein Bug in meiner Quaternion Klasse gehabt(böser Optimierung Versucht), dann ein Bug in meiner 4x4Matrix Klasse(noch ein böser Optimierungs Versuch grml) und dann hab ich noch ein Bug in meiner alten Kamera Klasse gehabt(kein Optimierungs Versuch, einfach nur fail).
Das spricht nicht gerade für eine gute Codeabdeckung, denn alle Funktionen laufen durch Unit Tests und sind nicht angeschlagen.
Gut eine Woche Später, diverse Refactorings, Bugfixes, einigen neuen Features und leichten Zweifel an meinen Coding Fähigkeiten hab ich dann doch mal was richtiges auf dem Schirm bekommen.

Ich hab natürlich auch noch einiges an Zeit in Radon Framework verbracht aber das ist ja nicht Teil des Threads.

_________________
"Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren"
Benjamin Franklin

Projekte: https://github.com/tak2004


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Zero-Prime
BeitragVerfasst: Fr Aug 22, 2014 22:10 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2523
Wohnort: Berlin
Programmiersprache: C/C++, C#
http://forum.outerra.com/FMX/presentation/
Das sollte ziemlich gut auf der GPU machbar sein, wenn man nicht noch pre OGL4 unterstützen muss wie Outerra.
Interessant ist das mappen von Farbwerten auf Mixer.
Ich hab das mal am Anfang von unserem aktuellem Projekt probiert aber nicht mit einem direktem mappen sondern über shader mit Farbrad und mein Ansatz war zum scheitern verurteilt, weil man nur in nachbarfarben blenden kann.
Ich musste auch immer erst RGB in YUV um rechnen, die haben die Daten schon in YUV und damit kann man schneller im Shader mappen.
Die Mixer sind in Prinzip das gleiche wie bei Witcher 3, wo man ground layer und forground layer listen hat, auf den texturen zu gewiesen sind und man dann die forground layer über slope, normal, höhe z.B. Schnee, Sand, Wasser korrekter platzieren und bei Gras z.B. dann Pflanzen und bei Steinigen Forground kleine Steine platziert werden.

Das nachbessern der Bergspitzen nach dem glatt lutschen der Heightmap ist auch interessant, den Schritt würde ich auch gerne auf Arbeit einbauen, weil bei uns die Berge halt abgerundete Kanten haben.

_________________
"Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren"
Benjamin Franklin

Projekte: https://github.com/tak2004


Nach oben
 Profil  
Mit Zitat antworten  
Beiträge der letzten Zeit anzeigen:  Sortiere nach  
Ein neues Thema erstellen Auf das Thema antworten  [ 4 Beiträge ] 
Foren-Übersicht » Sonstiges » Projekte


Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 1 Gast


Du darfst keine neuen Themen in diesem Forum erstellen.
Du darfst keine Antworten zu Themen in diesem Forum erstellen.
Du darfst deine Beiträge in diesem Forum nicht ändern.
Du darfst deine Beiträge in diesem Forum nicht löschen.
Du darfst keine Dateianhänge in diesem Forum erstellen.

Suche nach:
Gehe zu:  
  Powered by phpBB® Forum Software © phpBB Group
Deutsche Übersetzung durch phpBB.de
[ Time : 0.027s | 20 Queries | GZIP : On ]