Themabewertung:
  • 0 Bewertung(en) - 0 im Durchschnitt
  • 1
  • 2
  • 3
  • 4
  • 5
OpenSim (O)RM Map Generator
#1
OpenSim (O)RM Map Generator
ORM Materialien spiegeln die eigenschaft eines Materials wieder.
Beispiel: Jeans Stoff kann für mehrere Jeans, Schuhe oder Mobillar genutzt werden egal welche Farbe diese Objekte haben es zählt nur die Grundstruktur.

Funktion: Programm starten - Ordner auswählen der Texturen - von zum Beispiel freepbr enthält - dann generieren - und sie werden alle gespeichert.

Python Beispiel:
PHP-Code:
import os
import tkinter 
as tk
from tkinter import filedialog
messageboxttk
from PIL import Image
import glob
import threading

class ORMGeneratorGUI:
    
def __init__(selfroot):
        
self.root root
        self
.root.title("OpenSim (O)RM Map Generator")
        
self.root.geometry("600x400")
        
        
# Variablen
        
self.input_dir tk.StringVar()
        
self.output_dir tk.StringVar()
        
self.progress tk.DoubleVar()
        
self.status tk.StringVar(value="Bereit")
        
        
self.setup_ui()
    
    
def setup_ui(self):
        
# Hauptframe
        
main_frame ttk.Frame(self.rootpadding="10")
        
main_frame.grid(row=0column=0sticky="nsew")
        
        
# Root-Fenster Grid-Konfiguration
        
self.root.columnconfigure(0weight=1)
        
self.root.rowconfigure(0weight=1)
        
        
# Titel
        
title_label ttk.Label(main_frametext="OpenSim (O)RM Map Generator"
                               
font=("Arial"16"bold"))
        
title_label.grid(row=0column=0columnspan=3pady=(020))
        
        
# Eingabe-Verzeichnis Auswahl
        
ttk.Label(main_frametext="Eingabe-Verzeichnis:").grid(row=1column=0sticky=tk.Wpady=5)
        
ttk.Entry(main_frametextvariable=self.input_dirwidth=50).grid(row=1column=1padx=5pady=5)
        
ttk.Button(main_frametext="Durchsuchen"command=self.browse_input_dir).grid(row=1column=2pady=5)
        
        
# Ausgabe-Verzeichnis Auswahl
        
ttk.Label(main_frametext="Ausgabe-Verzeichnis:").grid(row=2column=0sticky=tk.Wpady=5)
        
ttk.Entry(main_frametextvariable=self.output_dirwidth=50).grid(row=2column=1padx=5pady=5)
        
ttk.Button(main_frametext="Durchsuchen"command=self.browse_output_dir).grid(row=2column=2pady=5)
        
        
# Optionen
        
options_frame ttk.LabelFrame(main_frametext="Optionen"padding="5")
        
options_frame.grid(row=3column=0columnspan=3sticky="ew"pady=10)
        
        
self.use_height_for_ao tk.BooleanVar(value=True)
        
ttk.Checkbutton(options_frametext="Height Map für AO verwenden (falls AO fehlt)"
                       
variable=self.use_height_for_ao).grid(row=0column=0sticky=tk.W)
        
        
self.overwrite_existing tk.BooleanVar(value=False)
        
ttk.Checkbutton(options_frametext="Existierende ORM-Maps überschreiben"
                       
variable=self.overwrite_existing).grid(row=1column=0sticky=tk.W)
        
        
# Fortschrittsbalken
        
ttk.Label(main_frametext="Fortschritt:").grid(row=4column=0sticky=tk.Wpady=(100))
        
progress_bar ttk.Progressbar(main_framevariable=self.progresslength=500)
        
progress_bar.grid(row=5column=0columnspan=3sticky="ew"pady=5)
        
        
# Status-Anzeige
        
status_label ttk.Label(main_frametextvariable=self.status)
        
status_label.grid(row=6column=0columnspan=3pady=5)
        
        
# Buttons
        
button_frame ttk.Frame(main_frame)
        
button_frame.grid(row=7column=0columnspan=3pady=20)
        
        
ttk.Button(button_frametext="ORM-Maps generieren"
                  
command=self.start_generation).pack(side=tk.LEFTpadx=5)
        
ttk.Button(button_frametext="Beenden"
                  
command=self.root.quit).pack(side=tk.LEFTpadx=5)
        
        
# Log-Ausgabe
        
log_frame ttk.LabelFrame(main_frametext="Log"padding="5")
        
log_frame.grid(row=8column=0columnspan=3sticky="nsew"pady=10)
        
        
self.log_text tk.Text(log_frameheight=8width=70)
        
scrollbar ttk.Scrollbar(log_frameorient=tk.VERTICALcommand=self.log_text.yview)
        
self.log_text.configure(yscrollcommand=scrollbar.set)
        
        
self.log_text.grid(row=0column=0sticky="nsew")
        
scrollbar.grid(row=0column=1sticky="ns")
        
        
# Grid-Konfiguration
        
main_frame.columnconfigure(1weight=1)
        
log_frame.columnconfigure(0weight=1)
        
log_frame.rowconfigure(0weight=1)
        
main_frame.rowconfigure(8weight=1)
    
    
def browse_input_dir(self):
        
directory filedialog.askdirectory(title="Eingabe-Verzeichnis auswählen")
        if 
directory:
            
self.input_dir.set(directory)
            
# Automatisch Output-Verzeichnis vorschlagen
            
if not self.output_dir.get():
                
self.output_dir.set(os.path.join(directory"ORM_Maps"))
    
    
def browse_output_dir(self):
        
directory filedialog.askdirectory(title="Ausgabe-Verzeichnis auswählen")
        if 
directory:
            
self.output_dir.set(directory)
    
    
def log(selfmessage):
        
self.log_text.insert(tk.ENDmessage "\n")
        
self.log_text.see(tk.END)
        
self.root.update_idletasks()
    
    
def start_generation(self):
        if 
not self.input_dir.get() or not self.output_dir.get():
            
messagebox.showerror("Fehler""Bitte Eingabe- und Ausgabe-Verzeichnis auswählen!")
            return
        
        
# In separatem Thread ausführen, um GUI nicht zu blockieren
        
thread threading.Thread(target=self.generate_orm_maps)
        
thread.daemon True
        thread
.start()
    
    
def generate_orm_maps(self):
        try:
            
input_dir self.input_dir.get()
            
output_dir self.output_dir.get()
            
            
self.status.set("Suche Texturen...")
            
self.progress.set(0)
            
self.log_text.delete(1.0tk.END)
            
            
# Alle Albedo-Texturen finden (case-insensitive und mit Unterordnern)
            
albedo_files = []
            for 
pattern in ["*_albedo.png""*_Albedo.png""*_ALBEDO.png"]:
                
albedo_files.extend(glob.glob(os.path.join(input_dirpattern)))
                
albedo_files.extend(glob.glob(os.path.join(input_dir"**"pattern), recursive=True))
            
            
# Duplikate entfernen
            
albedo_files = list(set(albedo_files))
            
            if 
not albedo_files:
                
self.log("Keine _albedo.png Dateien gefunden!")
                
self.log(f"Durchsuchtes Verzeichnis: {input_dir}")
                
                
# Zeige verfügbare PNG-Dateien zur Diagnose
                
all_pngs glob.glob(os.path.join(input_dir"*.png"))
                if 
all_pngs:
                    
self.log(f"\nGefundene PNG-Dateien ({len(all_pngs)}):")
                    for 
png in all_pngs[:5]:  # Zeige erste 5
                        
self.log(f"  - {os.path.basename(png)}")
                    if 
len(all_pngs) > 5:
                        
self.log(f"  ... und {len(all_pngs) - 5} weitere")
                
                
self.status.set("Fehler: Keine Texturen gefunden")
                return
            
            
self.log(f"Gefunden: {len(albedo_files)} Textur-Sets")
            
self.log("=" 50)
            
            
processed 0
            errors 
0
            
            
for ialbedo_file in enumerate(albedo_files):
                
base_name os.path.basename(albedo_file).replace("_albedo.png""")
                
                
# Fortschritt aktualisieren
                
progress_percent = (len(albedo_files)) * 100
                self
.progress.set(progress_percent)
                
self.status.set(f"Verarbeite: {base_name}")
                
                
# ORM-Map generieren
                
success self.create_single_orm_map(input_diroutput_dirbase_name)
                
                if 
success:
                    
processed += 1
                
else:
                    
errors += 1
            
            
# Abschluss
            
self.progress.set(100)
            
self.status.set("Fertig!")
            
self.log("=" 50)
            
self.log(f"Erfolgreich verarbeitet: {processed}")
            
self.log(f"Fehler: {errors}")
            
self.log(f"ORM-Maps gespeichert in: {output_dir}")
            
            
messagebox.showinfo("Fertig"f"Verarbeitung abgeschlossen!\nErfolgreich: {processed}\nFehler: {errors}")
            
        
except Exception as e:
            
self.log(f"FEHLER: {str(e)}")
            
self.status.set("Fehler aufgetreten")
            
messagebox.showerror("Fehler"f"Ein Fehler ist aufgetreten:\n{str(e)}")
    
    
def create_single_orm_map(selfinput_diroutput_dirbase_name):
        try:
            
# Datei-Pfade
            
ao_file os.path.join(input_dirf"{base_name}_ao.png")
            
roughness_file os.path.join(input_dirf"{base_name}_roughness.png")
            
metallic_file os.path.join(input_dirf"{base_name}_metallic.png")
            
height_file os.path.join(input_dirf"{base_name}_height.png")
            
            
# Output-Pfad
            
output_file os.path.join(output_dirf"{base_name}_ORM.png")
            
            
# Überspringen falls bereits existiert
            
if os.path.exists(output_file) and not self.overwrite_existing.get():
                
self.log(f"Übersprungen (existiert): {base_name}")
                return 
True
            
            
# Falls AO fehlt und Height Map verwendet werden soll
            
if self.use_height_for_ao.get() and not os.path.exists(ao_file) and os.path.exists(height_file):
                
self.log(f"  Verwende Height Map für AO: {base_name}")
                
ao_file height_file
            
            
# Überprüfen ob alle benötigten Dateien existieren
            
missing_files = []
            for 
file_path in [ao_fileroughness_filemetallic_file]:
                if 
not os.path.exists(file_path):
                    
missing_files.append(os.path.basename(file_path))
            
            if 
missing_files:
                
self.log(f"FEHLER {base_name}: Fehlende Dateien - {', '.join(missing_files)}")
                return 
False
            
            
# Bilder laden und verarbeiten
            
ao_img Image.open(ao_file).convert("L")
            
roughness_img Image.open(roughness_file).convert("L")
            
metallic_img Image.open(metallic_file).convert("L")
            
            
# Auf gemeinsame Größe bringen
            
sizes = [img.size for img in [ao_imgroughness_imgmetallic_img]]
            
target_size max(sizeskey=lambda xx[0] * x[1])
            
            if 
ao_img.size != target_size:
                
ao_img ao_img.resize(target_sizeImage.Resampling.LANCZOS)
            if 
roughness_img.size != target_size:
                
roughness_img roughness_img.resize(target_sizeImage.Resampling.LANCZOS)
            if 
metallic_img.size != target_size:
                
metallic_img metallic_img.resize(target_sizeImage.Resampling.LANCZOS)
            
            
# (O)RM Map kombinieren
            
orm_map Image.merge("RGB", (ao_imgroughness_imgmetallic_img))
            
            
# Speichern
            
os.makedirs(output_direxist_ok=True)
            
orm_map.save(output_file"PNG")
            
            
self.log(f"ERFOLG: {base_name} ({target_size[0]}x{target_size[1]})")
            return 
True
            
        except Exception 
as e:
            
self.log(f"FEHLER {base_name}: {str(e)}")
            return 
False

def main
():
    
root tk.Tk()
    
ORMGeneratorGUI(root)
    
root.mainloop()

if 
__name__ == "__main__":
    
main() 

Gemischt angezeigt kommt natürlich etwas heraus was merkwürdig aussieht und erst in RGB zerlegt sinn ergibt.
   
Ein Metaversum sind viele kleine Räume, die nahtlos aneinander passen,
sowie direkt sichtbar und begehbar sind, als wäre es aus einem Guss.



[-] The following 1 user says Thank You to Manfred Aabye for this post:
  • Bogus Curry
Zitieren
#2
OpenSim (O)RM Map Generator Version 2

ORM Map für PBR Material Informationen:
ORM ist eine gepackte Textur, die drei Graustufenbilder in den RGB-Kanälen kombiniert:

- Roter Kanal ® – Ambient Occlusion (O):
Baked Schatten in Vertiefungen → mehr Tiefe und Realismus
Weiß = keine Okklusion, Schwarz = volle Okklusion

- Grüner Kanal (G) – Roughness ®:
Steuert die Oberflächenrauheit
Weiß = sehr rau, Schwarz = sehr glatt/spiegelnd

- Blauer Kanal (B) – Metalness (M):
Bestimmt metallische Eigenschaften
Weiß = Metall, Schwarz = Nicht-Metall

Es wird jetzt deutlich mehr gefunden.
Eine Programmbuilder Batch für Windows Linux wurde hinzugefügt.
   
Sourcecode und Windows Programm vorhanden:
Github Download
Ein Metaversum sind viele kleine Räume, die nahtlos aneinander passen,
sowie direkt sichtbar und begehbar sind, als wäre es aus einem Guss.



[-] The following 1 user says Thank You to Manfred Aabye for this post:
  • Dorena Verne
Zitieren
#3
Wenn man nichts sieht, ist das auch doof Wink

   

Sourcecode und Windows Programme:
Github Download
Ein Metaversum sind viele kleine Räume, die nahtlos aneinander passen,
sowie direkt sichtbar und begehbar sind, als wäre es aus einem Guss.



Zitieren
#4
ORM-Maps-Viewer - Benutzerhandbuch

   

Was ist ORM?

ORM steht für Occlusion, Roughness, Metallic - ein Standard-Texturformat in der 3D-Grafik und Game-Engine-Entwicklung.

Die drei Kanäle erklärt
  • R (Red) = Occlusion (AO - Ambient Occlusion)
    • Beschreibt, wie viel indirektes Licht eine Oberfläche erhält
    • Dunkle Bereiche = weniger Licht (z.B. Ecken, Ritzen)
    • Helle Bereiche = mehr Licht (offene Flächen)
  • G (Green) = Roughness (Rauheit)
    • Bestimmt, wie glatt oder rau eine Oberfläche ist
    • Hell = rau (diffuse Reflexionen, matt)
    • Dunkel = glatt (scharfe Reflexionen, glänzend)
  • B (Blue) = Metallic (Metallisch)
    • Definiert, ob eine Oberfläche metallisch ist
    • Weiß = metallisch (Metalle wie Eisen, Gold)
    • Schwarz = nicht-metallisch (Holz, Stoff, Kunststoff)

Warum ORM?
Game-Engines wie Unreal Engine, Unity und Godot verwenden ORM-Maps, um mehrere Material-Eigenschaften in einer einzigen Textur zu speichern:
  • Speicherplatzersparnis - 3 Maps in 1 Datei
  • Bessere Performance - Weniger Texture-Lookups
  • Standard-Format - Kompatibel mit allen modernen Engines



Was kann der ORM-Maps-Viewer?
Der ORM-Maps-Viewer ist ein Tool zur visuellen Überprüfung und Qualitätskontrolle von ORM-Texturen:

Hauptfunktionen
  1. ORM-Maps laden und anzeigen
    • Unterstützt PNG, JPG, JPEG, JP2 Formate
    • Zeigt alle drei Kanäle getrennt an
  2. Einzelkanal-Ansicht
    • Rot-Kanal = Ambient Occlusion
    • Grün-Kanal = Roughness
    • Blau-Kanal = Metallic
    • Vollbild = Kombinierte ORM-Map
  3. Qualitätsprüfung
    • Überprüfen Sie, ob die Maps korrekt gepackt wurden
    • Identifizieren Sie Fehler oder Artefakte
    • Vergleichen Sie verschiedene ORM-Versionen
  4. Export-Möglichkeiten
    • Extrahieren Sie einzelne Kanäle als Graustufenbilder
    • Speichern Sie modifizierte ORM-Maps



Anleitung: ORM-Maps-Viewer benutzen
1. Programm starten
Windows:
Doppelklick auf: ORM-Maps-Viewer.exe

Python (Entwicklung):
Code:
python orm-maps-viewer.py

2. ORM-Textur laden
  1. Klicken Sie auf "ORM-Map laden"
  2. Navigieren Sie zu Ihrer ORM-Textur
  3. Wählen Sie die Datei (z.B. Material_ORM.png)
  4. Klicken Sie auf "Öffnen"

3. Kanäle anzeigen
Nach dem Laden sehen Sie 4 Vorschau-Bereiche:

Code:
┌─────────────┬─────────────┐
│ AO (Rot)    │ Roughness   │
│             │ (Grün)      │
├─────────────┼─────────────┤
│ Metallic    │ Vollbild    │
│ (Blau)      │ (ORM)       │
└─────────────┴─────────────┘

Einzelne Kanäle verstehen
AO-Kanal (Oben Links)
  • Zeigt Schattierungen durch Umgebungsverdeckung
  • Dunkle Bereiche = Schatten in Ecken/Ritzen
  • Helle Bereiche = offene Flächen

Roughness-Kanal (Oben Rechts)
  • Zeigt Oberflächenrauheit
  • Hell = raue, matte Oberfläche
  • Dunkel = glatte, glänzende Oberfläche

Metallic-Kanal (Unten Links)
  • Zeigt metallische Bereiche
  • Weiß = Metall
  • Schwarz = Nicht-Metall

Vollbild (Unten Rechts)
  • Die kombinierte ORM-Map wie sie in der Engine verwendet wird
  • Alle Kanäle zusammen in RGB

4. Qualitätskontrolle durchführen
Prüfen Sie jeden Kanal auf:
  • Korrekte Zuordnung - Sind die richtigen Maps in den richtigen Kanälen?
  • Keine Artefakte - Keine unerwünschten Flecken oder Rauschen?
  • Richtige Helligkeitswerte - Sind die Bereiche korrekt hell/dunkel?
  • Konsistenz - Passen die Kanäle zusammen?

5. Einzelne Kanäle exportieren (Optional)
Falls Sie einen Kanal als separate Graustufentextur benötigen:
  1. Klicken Sie auf "Kanal exportieren"
  2. Wählen Sie den gewünschten Kanal (AO, Roughness oder Metallic)
  3. Geben Sie einen Dateinamen ein
  4. Klicken Sie auf "Speichern"



Typische Anwendungsfälle
1. Nach ORM-Generierung prüfen
Nach der Erstellung einer ORM-Map mit dem ORM-Map-Generator:
Code:
orm-maps-generator.py → Material_ORM.png → orm-maps-viewer.py
  1. Laden Sie die generierte ORM-Map
  2. Prüfen Sie alle drei Kanäle visuell
  3. Stellen Sie sicher, dass keine Fehler aufgetreten sind

2. Heruntergeladene Assets überprüfen
Bei gekauften oder heruntergeladenen 3D-Assets:
  1. Öffnen Sie die mitgelieferte ORM-Map
  2. Überprüfen Sie, ob die Kanäle korrekt sind
  3. Vergleichen Sie mit den Albedo/Base-Color-Texturen

3. Eigene ORM-Maps erstellen
Wenn Sie manuell in Photoshop/GIMP ORM-Maps erstellen:
  1. Erstellen Sie RGB-Bild mit 3 Kanälen
  2. Laden Sie es im Viewer
  3. Prüfen Sie, ob die Zuordnung stimmt:
    • Rot = AO
    • Grün = Roughness
    • Blau = Metallic

4. Fehlersuche bei Rendering-Problemen
Wenn Materialien in Ihrer Engine falsch aussehen:
  1. Exportieren Sie die ORM-Map aus der Engine
  2. Öffnen Sie sie im Viewer
  3. Identifizieren Sie den problematischen Kanal
  4. Korrigieren Sie die Original-Textur



Tastenkombinationen
  • Tastenkombination Funktion
  • Strg + O ORM-Map laden
  • Strg + S Kanal exportieren
  • Strg + R Ansicht aktualisieren
  • Strg + W Fenster schließen
  • F11 Vollbildmodus
  • ESC Vollbild beenden




Unterstützte Dateiformate
Import (Laden)
  • PNG (empfohlen)
  • JPG/JPEG
  • JP2 (JPEG 2000)
  • TGA
  • BMP

Export (Speichern)
  • PNG (verlustfrei, empfohlen)
  • JPG (mit Qualitätseinstellung)
  • TGA



Technische Details
Farbkanal-Zuordnung
Code:
ORM-Map (RGB)
├── R (Red)   → AO (Ambient Occlusion)
├── G (Green) → Roughness
└── B (Blue)  → Metallic

Wertebereich
  • 0-255 (8-bit pro Kanal)
  • 0 = Schwarz (Minimum)
  • 255 = Weiß (Maximum)

Empfohlene Auflösungen
  • 512x512 - Eco
  • 1024x1024 - Standard
  • 2048x2048 - High Quality



System-Anforderungen
Minimum
  • Windows 10 oder höher
  • 2 GB RAM
  • Bildschirmauflösung: 1280x720

Empfohlen
  • Windows 11
  • 4 GB RAM oder mehr
  • Bildschirmauflösung: 1920x1080 oder höher

Download


Viel Erfolg mit Ihren PBR-Materialien! ✨
Ein Metaversum sind viele kleine Räume, die nahtlos aneinander passen,
sowie direkt sichtbar und begehbar sind, als wäre es aus einem Guss.



[-] The following 1 user says Thank You to Manfred Aabye for this post:
  • Bogus Curry
Zitieren
#5
OpenSim-ORM-Map-Tools V0.0.4
Aus dem Generator, dem Viewer und noch einer erweiterten Vorschau sowie einem GLTF Exporter wurden jetzt die OpenSim-ORM-Map-Tools.

   

   

   

Download
Ein Metaversum sind viele kleine Räume, die nahtlos aneinander passen,
sowie direkt sichtbar und begehbar sind, als wäre es aus einem Guss.



Zitieren
#6
ORM Maps Tools NG V1.0

ist ein professionelles Tool zur Verwaltung und Erstellung von PBR-Texturen (Physically Based Rendering) für OpenSimulator, Second Life und andere 3D-Anwendungen.

   

   

Download

Sourcecode

Anleitung

Fröhliche Weihnachten euch allen und viel Spaß mit dem Programm.
Ein Metaversum sind viele kleine Räume, die nahtlos aneinander passen,
sowie direkt sichtbar und begehbar sind, als wäre es aus einem Guss.



[-] The following 1 user says Thank You to Manfred Aabye for this post:
  • Dorena Verne
Zitieren


Möglicherweise verwandte Themen…
Thema Verfasser Antworten Ansichten Letzter Beitrag
  OpenSim Viewer Webinterface Modernisierung Manfred Aabye 3 575 19.10.2025, 11:25
Letzter Beitrag: Manfred Aabye
  OpenSim Currency Server 2025 Manfred Aabye 7 2.431 29.07.2025, 16:00
Letzter Beitrag: Manfred Aabye
  Roth2 Ruth2 für OpenSim Manfred Aabye 0 790 16.04.2025, 18:05
Letzter Beitrag: Manfred Aabye
  Server-Tutorial: Linux und OpenSim Mareta Dagostino 52 103.898 30.12.2024, 22:27
Letzter Beitrag: Pius Noel
Question OpenSim 0.9.3.0 ist jetzt stabil Jupiter Rowland 3 1.730 14.11.2024, 21:16
Letzter Beitrag: Jupiter Rowland

Gehe zu:


Benutzer, die gerade dieses Thema anschauen: 1 Gast/Gäste