Code:
using System;using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Globalization;
namespace ObjMergerTool
{
public class ModelPlacement
{
public string ModelFile { get; set; }
public float PositionX { get; set; }
public float PositionY { get; set; }
public float PositionZ { get; set; }
public float RotationX { get; set; }
public float RotationY { get; set; }
public float RotationZ { get; set; }
public float RotationW { get; set; }
public float ScaleFactor { get; set; }
public float FinalX { get; set; }
public float FinalY { get; set; }
public float FinalZ { get; set; }
public void CalculateFinalPosition()
{
const float MAX_SIZE = 51200f / 3f;
FinalX = MAX_SIZE - PositionX;
FinalY = PositionY;
FinalZ = MAX_SIZE - PositionZ;
}
}
public class ObjMerger
{
private List<string> vertices = new List<string>();
private List<string> faces = new List<string>();
private int vertexOffset = 0;
private int currentFileVertexCount = 0;
public void MergeAllFiles(string[] objFiles, string baseDirectory)
{
vertices.Clear();
faces.Clear();
vertexOffset = 0;
foreach (var fileInfo in objFiles.OrderBy(f => f))
{
currentFileVertexCount = 0;
ProcessFile(fileInfo);
vertexOffset += currentFileVertexCount;
var csvFile = Path.ChangeExtension(fileInfo, null) + "_ModelPlacementInformation.csv";
if (File.Exists(csvFile))
{
ProcessPlacementFile(csvFile, baseDirectory);
}
}
WriteToFile(Path.Combine(baseDirectory, "merged_complete_with_placements.obj"));
}
private void ProcessFile(string fileName)
{
foreach (var line in File.ReadLines(fileName))
{
if (string.IsNullOrWhiteSpace(line)) continue;
var parts = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (parts[0] == "v")
{
currentFileVertexCount++;
vertices.Add(line);
}
else if (parts[0] == "f")
{
faces.Add("f " + string.Join(" ", parts.Skip(1).Select(i => (int.Parse(i.Split('/')[0]) + vertexOffset).ToString())));
}
}
}
private void ProcessModelPlacement(string objFile, ModelPlacement placement)
{
foreach (var line in File.ReadLines(objFile))
{
if (string.IsNullOrWhiteSpace(line)) continue;
var parts = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (parts[0] == "v" && parts.Length >= 4)
{
currentFileVertexCount++;
float x = float.Parse(parts[1], CultureInfo.InvariantCulture) * placement.ScaleFactor + placement.FinalX;
float y = float.Parse(parts[2], CultureInfo.InvariantCulture) * placement.ScaleFactor + placement.FinalY;
float z = float.Parse(parts[3], CultureInfo.InvariantCulture) * placement.ScaleFactor + placement.FinalZ;
vertices.Add($"v {x.ToString(CultureInfo.InvariantCulture)} {y.ToString(CultureInfo.InvariantCulture)} {z.ToString(CultureInfo.InvariantCulture)}");
}
else if (parts[0] == "f")
{
faces.Add("f " + string.Join(" ", parts.Skip(1).Select(i => (int.Parse(i.Split('/')[0]) + vertexOffset).ToString())));
}
}
}
private void ProcessPlacementFile(string csvPath, string baseDirectory)
{
var lines = File.ReadAllLines(csvPath);
if (lines.Length <= 1) return;
for (int i = 1; i < lines.Length; i++)
{
if (string.IsNullOrWhiteSpace(lines[i])) continue;
var values = lines[i].Split(';');
if (values.Length < 9) continue;
var placement = new ModelPlacement();
try
{
placement.ModelFile = values[0];
placement.PositionX = float.Parse(values[1].Replace(",", "."), CultureInfo.InvariantCulture);
placement.PositionY = float.Parse(values[2].Replace(",", "."), CultureInfo.InvariantCulture);
placement.PositionZ = float.Parse(values[3].Replace(",", "."), CultureInfo.InvariantCulture);
placement.RotationX = !string.IsNullOrEmpty(values[4]) ? float.Parse(values[4].Replace(",", "."), CultureInfo.InvariantCulture) : 0f;
placement.RotationY = !string.IsNullOrEmpty(values[5]) ? float.Parse(values[5].Replace(",", "."), CultureInfo.InvariantCulture) : 0f;
placement.RotationZ = !string.IsNullOrEmpty(values[6]) ? float.Parse(values[6].Replace(",", "."), CultureInfo.InvariantCulture) : 0f;
placement.RotationW = !string.IsNullOrEmpty(values[7]) ? float.Parse(values[7].Replace(",", "."), CultureInfo.InvariantCulture) : 0f;
placement.ScaleFactor = !string.IsNullOrEmpty(values[8]) ? float.Parse(values[8].Replace(",", "."), CultureInfo.InvariantCulture) : 1f;
}
catch (Exception ex)
{
Console.WriteLine($"Fehler beim Parsen der Zeile: {lines[i]}");
Console.WriteLine($"Exception: {ex.Message}");
continue;
}
placement.CalculateFinalPosition();
string modelPath = placement.ModelFile;
if (modelPath.StartsWith("..\\"))
{
var baseDir = Directory.GetParent(baseDirectory).Parent.FullName;
modelPath = modelPath.Replace("..\\..\\", "").Replace(".m2", ".obj");
modelPath = Path.Combine(baseDir, modelPath);
if (File.Exists(modelPath))
{
Console.WriteLine($"Platziere Model: {modelPath}");
currentFileVertexCount = 0;
ProcessModelPlacement(modelPath, placement);
vertexOffset += currentFileVertexCount;
}
else
{
Console.WriteLine($"Model nicht gefunden: {modelPath}");
}
}
}
}
private void WriteToFile(string outputPath)
{
using (var writer = new StreamWriter(outputPath))
{
writer.WriteLine("# Merged OBJ File");
writer.WriteLine($"# Vertices: {vertices.Count}, Faces: {faces.Count}");
foreach (var vertex in vertices)
writer.WriteLine(vertex);
foreach (var face in faces)
writer.WriteLine(face);
}
}
}
class Program
{
static void Main(string[] args)
{
try
{
Console.Write("Basis-Verzeichnis eingeben: ");
string baseDirectory = Console.ReadLine().Trim();
var files = Directory.GetFiles(baseDirectory, "adt_*.obj", SearchOption.TopDirectoryOnly);
Console.WriteLine($"Gefunden: {files.Length} OBJ-Dateien");
new ObjMerger().MergeAllFiles(files, baseDirectory);
}
catch (Exception ex)
{
Console.WriteLine($"FEHLER: {ex.Message}");
Console.WriteLine($"StackTrace: {ex.StackTrace}");
}
Console.WriteLine("\nTaste drücken zum Beenden...");
Console.ReadKey();
}
}
}