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>
127 lines
5.3 KiB
C#
127 lines
5.3 KiB
C#
using Chorizite.Core.Render.Enums;
|
|
using Silk.NET.OpenGL;
|
|
using System;
|
|
|
|
namespace AcDream.App.Rendering.Wb {
|
|
public class GlobalMeshBuffer : IDisposable {
|
|
private readonly GL _gl;
|
|
public uint VAO { get; private set; }
|
|
public uint VBO { get; private set; }
|
|
public uint IBO { get; private set; }
|
|
|
|
private int _vboCapacity = 1024 * 1024; // 1M vertices (~32MB)
|
|
private int _iboCapacity = 3 * 1024 * 1024; // 3M indices (~6MB)
|
|
private int _vboOffset = 0;
|
|
private int _iboOffset = 0;
|
|
|
|
public GlobalMeshBuffer(GL gl) {
|
|
_gl = gl;
|
|
InitBuffers();
|
|
}
|
|
|
|
private unsafe void InitBuffers() {
|
|
_gl.GenVertexArrays(1, out uint vao);
|
|
VAO = vao;
|
|
_gl.BindVertexArray(VAO);
|
|
|
|
_gl.GenBuffers(1, out uint vbo);
|
|
VBO = vbo;
|
|
_gl.BindBuffer(GLEnum.ArrayBuffer, VBO);
|
|
_gl.BufferData(GLEnum.ArrayBuffer, (nuint)(_vboCapacity * VertexPositionNormalTexture.Size), null, GLEnum.StaticDraw);
|
|
|
|
int stride = VertexPositionNormalTexture.Size;
|
|
_gl.EnableVertexAttribArray(0);
|
|
_gl.VertexAttribPointer(0, 3, GLEnum.Float, false, (uint)stride, (void*)0);
|
|
_gl.EnableVertexAttribArray(1);
|
|
_gl.VertexAttribPointer(1, 3, GLEnum.Float, false, (uint)stride, (void*)(3 * sizeof(float)));
|
|
_gl.EnableVertexAttribArray(2);
|
|
_gl.VertexAttribPointer(2, 2, GLEnum.Float, false, (uint)stride, (void*)(6 * sizeof(float)));
|
|
|
|
_gl.GenBuffers(1, out uint ibo);
|
|
IBO = ibo;
|
|
_gl.BindBuffer(GLEnum.ElementArrayBuffer, IBO);
|
|
_gl.BufferData(GLEnum.ElementArrayBuffer, (nuint)(_iboCapacity * sizeof(ushort)), null, GLEnum.StaticDraw);
|
|
|
|
_gl.BindVertexArray(0);
|
|
}
|
|
|
|
public unsafe (int baseVertex, int firstIndex) Append(VertexPositionNormalTexture[] vertices, ushort[] indices) {
|
|
if (vertices.Length == 0 || indices.Length == 0) return (0, 0);
|
|
|
|
// Check capacity
|
|
if (_vboOffset + vertices.Length > _vboCapacity) {
|
|
ResizeVBO(Math.Max(_vboCapacity * 2, _vboCapacity + vertices.Length));
|
|
}
|
|
if (_iboOffset + indices.Length > _iboCapacity) {
|
|
ResizeIBO(Math.Max(_iboCapacity * 2, _iboCapacity + indices.Length));
|
|
}
|
|
|
|
int baseVertex = _vboOffset;
|
|
int firstIndex = _iboOffset;
|
|
|
|
_gl.BindBuffer(GLEnum.ArrayBuffer, VBO);
|
|
fixed (VertexPositionNormalTexture* ptr = vertices) {
|
|
_gl.BufferSubData(GLEnum.ArrayBuffer, (nint)(baseVertex * VertexPositionNormalTexture.Size), (nuint)(vertices.Length * VertexPositionNormalTexture.Size), ptr);
|
|
}
|
|
|
|
_gl.BindBuffer(GLEnum.ElementArrayBuffer, IBO);
|
|
fixed (ushort* ptr = indices) {
|
|
_gl.BufferSubData(GLEnum.ElementArrayBuffer, (nint)(firstIndex * sizeof(ushort)), (nuint)(indices.Length * sizeof(ushort)), ptr);
|
|
}
|
|
|
|
_vboOffset += vertices.Length;
|
|
_iboOffset += indices.Length;
|
|
|
|
return (baseVertex, firstIndex);
|
|
}
|
|
|
|
private unsafe void ResizeVBO(int newCapacity) {
|
|
_gl.GenBuffers(1, out uint newVbo);
|
|
_gl.BindBuffer(GLEnum.ArrayBuffer, newVbo);
|
|
_gl.BufferData(GLEnum.ArrayBuffer, (nuint)(newCapacity * VertexPositionNormalTexture.Size), null, GLEnum.StaticDraw);
|
|
|
|
_gl.BindBuffer(GLEnum.CopyReadBuffer, VBO);
|
|
_gl.BindBuffer(GLEnum.CopyWriteBuffer, newVbo);
|
|
_gl.CopyBufferSubData(GLEnum.CopyReadBuffer, GLEnum.CopyWriteBuffer, 0, 0, (nuint)(_vboOffset * VertexPositionNormalTexture.Size));
|
|
|
|
_gl.DeleteBuffer(VBO);
|
|
VBO = newVbo;
|
|
_vboCapacity = newCapacity;
|
|
|
|
// Re-bind to VAO
|
|
_gl.BindVertexArray(VAO);
|
|
_gl.BindBuffer(GLEnum.ArrayBuffer, VBO);
|
|
int stride = VertexPositionNormalTexture.Size;
|
|
_gl.VertexAttribPointer(0, 3, GLEnum.Float, false, (uint)stride, (void*)0);
|
|
_gl.VertexAttribPointer(1, 3, GLEnum.Float, false, (uint)stride, (void*)(3 * sizeof(float)));
|
|
_gl.VertexAttribPointer(2, 2, GLEnum.Float, false, (uint)stride, (void*)(6 * sizeof(float)));
|
|
_gl.BindVertexArray(0);
|
|
}
|
|
|
|
private unsafe void ResizeIBO(int newCapacity) {
|
|
_gl.GenBuffers(1, out uint newIbo);
|
|
_gl.BindBuffer(GLEnum.ElementArrayBuffer, newIbo);
|
|
_gl.BufferData(GLEnum.ElementArrayBuffer, (nuint)(newCapacity * sizeof(ushort)), null, GLEnum.StaticDraw);
|
|
|
|
_gl.BindBuffer(GLEnum.CopyReadBuffer, IBO);
|
|
_gl.BindBuffer(GLEnum.CopyWriteBuffer, newIbo);
|
|
_gl.CopyBufferSubData(GLEnum.CopyReadBuffer, GLEnum.CopyWriteBuffer, 0, 0, (nuint)(_iboOffset * sizeof(ushort)));
|
|
|
|
_gl.DeleteBuffer(IBO);
|
|
IBO = newIbo;
|
|
_iboCapacity = newCapacity;
|
|
|
|
// Re-bind to VAO
|
|
_gl.BindVertexArray(VAO);
|
|
_gl.BindBuffer(GLEnum.ElementArrayBuffer, IBO);
|
|
_gl.BindVertexArray(0);
|
|
}
|
|
|
|
public void Dispose() {
|
|
if (VAO != 0) _gl.DeleteVertexArray(VAO);
|
|
if (VBO != 0) _gl.DeleteBuffer(VBO);
|
|
if (IBO != 0) _gl.DeleteBuffer(IBO);
|
|
VAO = VBO = IBO = 0;
|
|
}
|
|
}
|
|
}
|