Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Entity models get stretched vertices with shaders: `rawFloatBuffer` is rebuilt from a non-zero `byteBuffer` position

Open
#6 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
3/5
Estimated time
1-2 days
Newbie friendliness
82/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
java

Research direction

Start by reading MixinBufferBuilder.optiRefine$restoreWritableFloatBuffer and the growBuffer calls from endVertex, then inspect WorldVertexBufferUploader.draw to confirm how byteBuffer.position is left behind. Reproduce with OptiFine and OptiRefine using the reported shader setup or diagnostic mixin. Done means rawFloatBuffer consistently starts at offset 0 and compiled model quads no longer have displaced vertices.

Written by the indexing model from the issue text.

Description

Environment

Cleanroom 0.6.13-alpha (Java 25), OptiFine 1.12.2 HD_U_G5, OptiRefine built from 78ff8ce. Seen with Sildur's Vibrant v2.02 and Mellow v3.4.1.

What happens

With a shader pack on, some entity/tile-entity model parts sometimes get one or more vertices pulled far away when their display list is compiled. Examples: a chest lid with a large triangular "wedge", or a thin strip coming out of a villager's head. The result changes from run to run and then stays until the display lists are rebuilt.

Cause

  1. WorldVertexBufferUploader.draw sets byteBuffer.position(format.getOffset(i)) for every element and never sets it back to 0. BufferBuilder.reset() and begin() do not touch the position either. After a Tessellator draw in POSITION_TEX_COLOR (24 bytes), the Tessellator's byteBuffer stays at position 20.
  2. MixinBufferBuilder.optiRefine$restoreWritableFloatBuffer injects at the TAIL of every growBuffer call, and endVertex calls growBuffer on every vertex. It does this.rawFloatBuffer = this.byteBuffer.asFloatBuffer();. The view starts at the buffer's current position, so it is shifted by 20 bytes.
  3. Under shaders, models use SVertexFormat.defVertexFormatTextured (56 bytes). After the 4th vertex of a quad, SVertexBuilder.calcNormal reads and writes the quad through rawFloatBuffer, with indices counted from 0. With the 20-byte shift, the midV writes (floatBuffer.put(base + i * 14 + 9, midV)) land on bytes 56, 112 and 168. Those are the x coordinates of vertices 1, 2 and 3. The normal and tangent are also computed from shifted reads.
  4. In OptiFine itself the float view is only created inside the "buffer actually grows" branch, after rewind(), so it always starts at byte 0.

Evidence

We used a diagnostic mixin (a test-only mod) that checks every quad compiled into a model display list:

  • The quad positions are correct after the last endVertex and wrong at Tessellator.draw HEAD. Only the x of vertices 1-3 changes.
  • Every corrupted quad had rawFloatBuffer starting 20 bytes into byteBuffer, and the previous Tessellator draw was POSITION_TEX_COLOR.
  • The corruption also happens with only OptiFine + OptiRefine installed: 2 corrupted ModelPlayer quads per run, in the shadow pass. Other mods only change which model gets hit, depending on draw order.
  • Re-anchoring the view after OptiRefine's TAIL injection, with duplicate().clear(), keeps the native order. With that, 1320 of 1320 compiled quads start at offset 0 and none are corrupted.

Suggestion

Only rebuild the view when the buffer actually grew, or build it from position 0, for example:

ByteBuffer whole = this.byteBuffer.duplicate();
whole.clear();
this.rawFloatBuffer = whole.order(this.byteBuffer.order()).asFloatBuffer();
Dominant language
Java
Stars
27
Forks
5
PR merge metrics
No merged PRs in 30d

Getting set up

This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from Ecdcaeb/OptiRefine

All issues in Ecdcaeb/OptiRefine

Similar issues

More Java issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.