feat(O-T4): extract ObjectMeshManager + mesh pipeline closure into AcDream.App.Rendering.Wb

Phase O Task 4: extract the WB mesh pipeline (ObjectMeshManager + 7 support files)
from references/WorldBuilder into src/AcDream.App/Rendering/Wb/ and bridge dat I/O
through our DatCollection via a thin DatCollectionAdapter.

O-D7 adapter path taken: ObjectMeshManager has 26 _dats.X call sites (threshold 20),
so a DatCollectionAdapter : IDatReaderWriter is introduced rather than refactoring
ObjectMeshManager's internal dat access directly.

Files added (verbatim copies, namespace-only changes):
- ObjectMeshManager.cs — mesh pipeline hub; IDatReaderWriter field satisfied by adapter
- GlobalMeshBuffer.cs — single global VAO/VBO/IBO manager
- EdgeLineBuilder.cs — wireframe edge geometry from CellStruct polygons
- ModernRenderData.cs — ModernBatchData + LandblockMdiCommand structs
- TextureAtlasManager.cs — texture array grouping by (Width, Height, Format)
- ParticleBatcher.cs — GPU particle batching; T4 interim uses BaseObjectRenderManager
  static fields from Chorizite.OpenGLSDLBackend.Lib (stays until T7)
- ParticleEmitterRenderer.cs — per-emitter particle lifecycle + rendering
- ActiveParticleEmitter.cs — wrapper holding renderer + part index + local offset
- DatCollectionAdapter.cs — NEW: bridges DatCollection → IDatReaderWriter; implements
  ResolveId() via DatDatabase.TypeFromId + Tree.TryGetFile in HighRes→Portal→Language→Cell
  order matching DefaultDatReaderWriter; DatDatabaseWrapper wraps DatDatabase as IDatDatabase

WbMeshAdapter.cs changes (T4 Step 6):
- _graphicsDevice switched from Chorizite.OpenGLSDLBackend.OpenGLGraphicsDevice to
  extracted AcDream.App.Rendering.Wb.OpenGLGraphicsDevice
- ParticleBatcher = new ParticleBatcher(_graphicsDevice) restored (T3 had null! placeholder)
- ObjectMeshManager now constructed with new DatCollectionAdapter(dats) instead of _wbDats
- _wbDats field + its construction + disposal + [indoor-upload] NULL_RESULT diagnostic block
  left intact — T7 cleanup removes these once WorldBuilder project ref is dropped

EmbeddedResourceReader.cs: replaced assembly manifest lookup (wrong prefix for our assembly)
with disk-based lookup mapping "Shaders.Particle.vert" → Rendering/Shaders/wb_particle.vert;
consistent with all other acdream shaders.

wb_particle.vert / wb_particle.frag: WB particle shaders copied verbatim with wb_ prefix
to distinguish from acdream's own particle.vert.

OpenGLGraphicsDevice.cs: ParticleBatcher property type updated to extracted ParticleBatcher;
setter changed from private to internal so WbMeshAdapter (same assembly) can assign post-ctor.

Build: green (0 errors, 0 warnings in AcDream.App).
Tests: 1147+8 baseline maintained (8 pre-existing failures unchanged).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-21 16:37:55 +02:00
parent 4cc38805b5
commit d16d8cd4e5
14 changed files with 3535 additions and 19 deletions

View file

@ -1,22 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace AcDream.App.Rendering.Wb {
/// <summary>
/// Resolves WB-style shader resource names (e.g. "Shaders.Particle.vert") to
/// the corresponding file under the acdream binary's Rendering/Shaders directory.
///
/// WB embeds shaders as assembly resources under the Chorizite.OpenGLSDLBackend
/// namespace. acdream ships shaders as plain files copied to the output directory.
/// This class adapts between the two conventions so ParticleBatcher and
/// ParticleEmitterRenderer can call GetEmbeddedResource without modification.
///
/// Mapping rule: "Shaders.Foo.vert" → Rendering/Shaders/wb_foo.vert
/// (lower-case, wb_ prefix to distinguish WB-origin shaders from acdream's own)
/// </summary>
public static class EmbeddedResourceReader {
internal static string GetEmbeddedResource(string filename) {
var assembly = Assembly.GetExecutingAssembly();
var resourceName = "Chorizite.OpenGLSDLBackend." + filename;
// Convert "Shaders.Particle.vert" → "wb_particle.vert"
// Strip leading "Shaders." then lowercase and prefix with wb_
string leafName;
const string shadersPrefix = "Shaders.";
if (filename.StartsWith(shadersPrefix, StringComparison.Ordinal))
{
var rest = filename.Substring(shadersPrefix.Length); // e.g. "Particle.vert"
leafName = "wb_" + rest.ToLowerInvariant(); // e.g. "wb_particle.vert"
}
else
{
leafName = "wb_" + filename.ToLowerInvariant();
}
using var stream = assembly.GetManifestResourceStream(resourceName)
?? throw new InvalidOperationException($"Could not find embedded resource '{resourceName}'");
using var reader = new StreamReader(stream);
var shadersDir = Path.Combine(AppContext.BaseDirectory, "Rendering", "Shaders");
var fullPath = Path.Combine(shadersDir, leafName);
return reader.ReadToEnd();
if (!File.Exists(fullPath))
throw new InvalidOperationException(
$"WB shader not found: '{fullPath}' (mapped from resource '{filename}'). " +
$"Ensure {leafName} is in src/AcDream.App/Rendering/Shaders/ with CopyToOutputDirectory.");
return File.ReadAllText(fullPath);
}
}
}