Class IrisCompanionMesh

java.lang.Object
com.hbm_m.client.render.IrisCompanionMesh
All Implemented Interfaces:
IrisCompanionMeshResource

public final class IrisCompanionMesh extends Object implements IrisCompanionMeshResource
Lazy-built companion VBO+VAO that holds geometry in a vertex format compatible with the ExtendedShader returned by Iris/Oculus for ShaderKey.BLOCK_ENTITY.

The data is produced by a BufferBuilder starting from DefaultVertexFormat.NEW_ENTITY; when Iris is loaded its mixin extends the format on the fly to IrisVertexFormats.ENTITY so that fields like midTexCoord, tangent, entityId, blockEntityId and uv_mid_block are populated automatically.

The companion mesh is built once per part on first request from the render thread; if the build fails (e.g. Iris not loaded, build off-thread) the mesh is marked failed and the calling code is expected to fall back to vanilla paths.

  • Constructor Summary

    Constructors
    Constructor
    Description
    IrisCompanionMesh(List<net.minecraft.client.renderer.block.model.BakedQuad> quads)
     
  • Method Summary

    Modifier and Type
    Method
    Description
    void
    Switches this VAO's UV2 attribute to read from lightmapVboId (per-vertex mode) instead of returning the per-draw constant set via glVertexAttribI2i.
    long
    Allocates or reuses a slot for the given lightmap key.
    void
    bindLightmapForInstance(int slotIndex)
    Repoints the VAO's UV2 attribute at the given instance slot within lightmapVboId.
    void
    Binds this mesh's VAO if it is not already active.
    void
    Releases all GL resources (VAO/VBO/EBO + auxiliary lightmap buffers) and native CPU scratch.
    boolean
    Attempts to build the companion mesh.
    void
    ensureLightmapCapacity(int requiredInstances)
    Ensures the auxiliary lightmap VBO + CPU scratch can hold at least requiredInstances concurrent instance slots.
    void
    Convenience for callers that wrote into a set of slots and need the GPU-visible contents before drawing.
    int
     
    int
    Allocates or reuses a slot for the given lightmap key.
    int
     
    int
     
    int
    GL handle of the VBO this companion holds - used by debug logging.
    int
     
    boolean
     
    boolean
     
    void
    prepareForShader(int programId)
    Resolves the GLSL-linker-assigned locations of the Iris-extended attributes (iris_Entity, mc_midTexCoord, at_tangent) for the given program and binds our VBO data to those locations on this VAO.
    void
    Reverts the VAO back to the per-draw constant UV2 mode (disabled attribute array — callers supply the value via glVertexAttribI2i).
    boolean
     
    boolean
     
    void
    Uploads all dirty slots to the GPU in one contiguous range, for the fallback (non-persistent) path.
    void
    uploadLightmapRange(int firstSlot, int slotCount)
    Uploads the populated CPU-side lightmap data for instance slots [firstSlot, firstSlot + slotCount) to the GPU.
    void
    writeInstanceLightmap(int slotIndex, float[] corner16)
    Computes per-vertex UV2 (blockU, skyV) for the given instance slot by trilinearly combining the 8 corner lightmap samples with the baked perVertexCornerWeights, and writes the result as USHORT pairs into lightmapCpuScratch at offset slotIndex * vertexCount * 4.

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Constructor Details

    • IrisCompanionMesh

      public IrisCompanionMesh(List<net.minecraft.client.renderer.block.model.BakedQuad> quads)
  • Method Details

    • ensureBuilt

      public boolean ensureBuilt()
      Attempts to build the companion mesh. Must be called on the render thread. Returns true on success, false if a previous build failed or this build attempt failed.
      Specified by:
      ensureBuilt in interface IrisCompanionMeshResource
    • supportsPerVertexLightmap

      public boolean supportsPerVertexLightmap()
      Specified by:
      supportsPerVertexLightmap in interface IrisCompanionMeshResource
      Returns:
      true if per-vertex trilinear lighting is available for this mesh (weights precomputed + UV2 attribute location known).
    • supportsSlicedPerVertexLightmap

      public boolean supportsSlicedPerVertexLightmap()
      Returns:
      true if writeInstanceLightmap(int, float[]) can combine a float[32] 2×4×2 light probe set with perVertexSlicedWeights (Iris / extended path matches tall VBOs that use LightSampleCache#getOrSample16).
    • ensureLightmapCapacity

      public void ensureLightmapCapacity(int requiredInstances)
      Ensures the auxiliary lightmap VBO + CPU scratch can hold at least requiredInstances concurrent instance slots. Grows (never shrinks) to the next power-of-two. No-op if already large enough.

      Each slot holds vertexCount pairs of 16-bit lightmap values (blockU, skyV) → vertexCount * 4 bytes. For a 2000-vertex part with 64 slots that is 512 KB — comfortably small even summed across every distinct multiblock part in the game.

      Must be called on the render thread; allocates GL resources on first use.

      Specified by:
      ensureLightmapCapacity in interface IrisCompanionMeshResource
    • allocLightmapSlot

      public long allocLightmapSlot(long key)
      Allocates or reuses a slot for the given lightmap key.
      Returns:
      packed long: low 32 bits = slot, bit 32 = reused flag (1 means slot already contained key).
    • getOrAllocateLightmapSlot

      public int getOrAllocateLightmapSlot(long key)
      Allocates or reuses a slot for the given lightmap key. The key must be a stable hash of the instance's quantized 8-corner UV2 (typically derived after round+clamp to 0..240).

      Slot reuse is LRU-ish via simple eviction cursor; bounded by lightmapInstanceCapacity.

    • uploadDirtySlotsIfNeeded

      public void uploadDirtySlotsIfNeeded()
      Uploads all dirty slots to the GPU in one contiguous range, for the fallback (non-persistent) path. No-op when persistent mapping is active.
    • finishLightmapWrites

      public void finishLightmapWrites()
      Convenience for callers that wrote into a set of slots and need the GPU-visible contents before drawing. (Persistent mapped path: barrier; fallback: upload dirty slots.)
      Specified by:
      finishLightmapWrites in interface IrisCompanionMeshResource
    • writeInstanceLightmap

      public void writeInstanceLightmap(int slotIndex, float[] corner16)
      Computes per-vertex UV2 (blockU, skyV) for the given instance slot by trilinearly combining the 8 corner lightmap samples with the baked perVertexCornerWeights, and writes the result as USHORT pairs into lightmapCpuScratch at offset slotIndex * vertexCount * 4.

      Does NOT upload to the GPU; batch the uploads via uploadLightmapRange(int, int) after all instances for a draw pass have been written (one glBufferSubData per pass is far cheaper than one per instance).

      Specified by:
      writeInstanceLightmap in interface IrisCompanionMeshResource
      Parameters:
      slotIndex - 0-based slot, must be < lightmapInstanceCapacity
      corner16 - 16 floats laid out as [c0.blockU, c0.skyV, c1.blockU, c1.skyV, ..., c7.blockU, c7.skyV]; exactly the format LightSampleCache.getOrSample8(net.minecraft.world.level.block.entity.BlockEntity, long, float[], net.minecraft.core.BlockPos, org.joml.Matrix4f, int, float[]) produces, typically already on the 0..240 scale
    • uploadLightmapRange

      public void uploadLightmapRange(int firstSlot, int slotCount)
      Uploads the populated CPU-side lightmap data for instance slots [firstSlot, firstSlot + slotCount) to the GPU.

      Typically called once per batch flush after all writeInstanceLightmap(int, float[]) calls for that batch are done — this collapses what would otherwise be one glBufferSubData per instance into a single driver roundtrip.

    • activatePerVertexLightmap

      public void activatePerVertexLightmap()
      Switches this VAO's UV2 attribute to read from lightmapVboId (per-vertex mode) instead of returning the per-draw constant set via glVertexAttribI2i. Idempotent — repeated calls after a no-op return immediately.

      The VAO state is persistent across frames, so we only need to do the heavy glEnableVertexAttribArray + glVertexAttribIPointer dance on the first call (or after an ensureLightmapCapacity(int) growth invalidated the binding).

      The caller MUST have this VAO bound (via getVaoId()) before invoking this method. On return, the UV2 attribute points at slot 0; use bindLightmapForInstance(int) to switch slots per draw.

      Specified by:
      activatePerVertexLightmap in interface IrisCompanionMeshResource
    • bindLightmapForInstance

      public void bindLightmapForInstance(int slotIndex)
      Repoints the VAO's UV2 attribute at the given instance slot within lightmapVboId. Must be called between activatePerVertexLightmap() and the per-instance glDrawElements.

      Skips the GL call when the slot matches the previous one (rare — only happens if one part of the batch draws the same instance twice, e.g. for shadow + main in the same batch, which our pipeline currently splits into distinct passes).

      Specified by:
      bindLightmapForInstance in interface IrisCompanionMeshResource
    • restoreConstantLightmap

      public void restoreConstantLightmap()
      Reverts the VAO back to the per-draw constant UV2 mode (disabled attribute array — callers supply the value via glVertexAttribI2i). Paired with activatePerVertexLightmap(). No-op if we were never in per-vertex mode.

      Rarely needed in practice — any code path that does per-vertex lighting on one frame wants the same on every frame — but useful for debug toggles and shader-pack hot-reload scenarios where we might need to start clean.

      Specified by:
      restoreConstantLightmap in interface IrisCompanionMeshResource
    • prepareForShader

      public void prepareForShader(int programId)
      Resolves the GLSL-linker-assigned locations of the Iris-extended attributes (iris_Entity, mc_midTexCoord, at_tangent) for the given program and binds our VBO data to those locations on this VAO.

      Why this is needed. Iris's transformers (EntityPatcher.patchEntityId, CommonTransformer.patchMultiTexCoord3) inject these attributes into the GLSL without layout(location = N) qualifiers, and MixinShaderInstance only adds explicit glBindAttribLocation calls for the six core Mojang attributes (Position/Color/UV0/UV1/UV2/Normal → 0..5). The linker is therefore free to put iris_Entity at, say, location 6 while Mojang's VertexFormat.setupBufferState would have bound the IrisVertexFormats.ENTITY format's iris_Entity element (which sits at list-index 7 in the format) to GL location 7. Whenever those disagree, the shader reads garbage from a slot nothing ever bound - which is exactly the broken-geometry symptom that appears intermittently near torches and gets dramatically worse when redstone particles or Embeddium chunk re-bakes pollute the global "current attribute value" bank.

      The fix. We query GL20.glGetAttribLocation(int, java.nio.ByteBuffer) per attribute name to get the real linker-resolved location, then bind a VBO pointer to the correct byte offset within our VBO using the appropriate integer/float pipeline. Iris's MixinBufferBuilder.iris$beforeNext already wrote correct per-vertex values for these attributes into our VBO when we called BufferBuilder.putBulkData(java.nio.ByteBuffer), so the shader now reads real, stable data - completely immune to any global state pollution from other rendering paths.

      The caller MUST have this VAO bound (via getVaoId()) before invoking this method. The work is cached per program ID, so repeated calls with the same shader are nearly free.

      Specified by:
      prepareForShader in interface IrisCompanionMeshResource
    • isBuilt

      public boolean isBuilt()
    • isFailed

      public boolean isFailed()
    • getVaoId

      public int getVaoId()
      Specified by:
      getVaoId in interface IrisCompanionMeshResource
      Returns:
      GL handle of the companion VAO, or -1 when not built.

      Implemented by getVaoId().

    • getVboId

      public int getVboId()
      GL handle of the VBO this companion holds - used by debug logging.
    • getIndexCount

      public int getIndexCount()
      Specified by:
      getIndexCount in interface IrisCompanionMeshResource
      Returns:
      number of indices in the EBO (quadCount * 6), or 0 when not built.

      Implemented by getIndexCount().

    • getUv2Location

      public int getUv2Location()
      Specified by:
      getUv2Location in interface IrisCompanionMeshResource
      Returns:
      the GL attribute location for UV2 (lightmap) within this VAO, or -1 if the format does not contain such an element. Use with glVertexAttrib2f(getUv2Location(), blockU, skyV) to override the lightmap on a per-draw basis.
    • bindVaoIfNeeded

      public void bindVaoIfNeeded()
      Binds this mesh's VAO if it is not already active. Safe to call before glDrawElements when Embeddium/Iris may have rebound VAO 0 between setup and draw (instanced flush loop).
    • getVertexCount

      public int getVertexCount()
    • destroy

      public void destroy()
      Description copied from interface: IrisCompanionMeshResource
      Releases all GL resources (VAO/VBO/EBO + auxiliary lightmap buffers) and native CPU scratch. Safe to call off the render thread - deletion is deferred via RenderSystem.recordRenderCall.

      Implemented by destroy().

      Specified by:
      destroy in interface IrisCompanionMeshResource