diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/DynamicBufferSource.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/DynamicBufferSource.kt new file mode 100644 index 000000000..a2b2b427e --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/DynamicBufferSource.kt @@ -0,0 +1,324 @@ +package ru.dbotthepony.mc.otm.client.render + +import com.google.common.collect.ImmutableList +import com.mojang.blaze3d.vertex.BufferBuilder +import com.mojang.blaze3d.vertex.VertexConsumer +import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap +import it.unimi.dsi.fastutil.objects.ReferenceArraySet +import net.minecraft.client.renderer.MultiBufferSource +import net.minecraft.client.renderer.RenderType +import net.minecraft.client.renderer.Sheets + +private fun equals(existing: ImmutableList?, types: Array): Boolean { + if (types.isEmpty()) { + return existing == null + } + + if (existing == null) { + return false + } + + if (existing.size != types.size) { + return false + } + + for (i in types.indices) { + if (types[i] !== existing[i]) { + return false + } + } + + return true +} + +private fun equals(existing: ImmutableList?, types: ImmutableList): Boolean { + if (existing === types) { + return true + } + + if (types.isEmpty()) { + return existing == null + } + + if (existing == null) { + return false + } + + if (existing.size != types.size) { + return false + } + + for (i in types.indices) { + if (types[i] !== existing[i]) { + return false + } + } + + return true +} + +class DynamicBufferSource : MultiBufferSource { + private class State( + val type: RenderType, + var after: ImmutableList? = null, + val immutableAfter: Boolean = false + ) { + var priority = -1 + var dirty: Boolean = false + val builder by lazy { + BufferBuilder(type.bufferSize()) + } + } + + private val bufferList = ArrayList().also { + var next = State(Sheets.solidBlockSheet(), null, true) + it.add(next) + next = State(Sheets.cutoutBlockSheet(), ImmutableList.of(next.type), true) + it.add(next) + next = State(Sheets.bannerSheet(), ImmutableList.of(next.type), true) + it.add(next) + next = State(Sheets.translucentCullBlockSheet(), ImmutableList.of(next.type), true) + it.add(next) + next = State(Sheets.shieldSheet(), ImmutableList.of(next.type), true) + it.add(next) + next = State(Sheets.bedSheet(), ImmutableList.of(next.type), true) + it.add(next) + next = State(Sheets.shulkerBoxSheet(), ImmutableList.of(next.type), true) + it.add(next) + next = State(Sheets.signSheet(), ImmutableList.of(next.type), true) + it.add(next) + next = State(Sheets.chestSheet(), ImmutableList.of(next.type), true) + it.add(next) + next = State(RenderType.translucentNoCrumbling(), ImmutableList.of(next.type), true) + it.add(next) + next = State(RenderType.armorGlint(), ImmutableList.of(next.type), true) + it.add(next) + next = State(RenderType.armorEntityGlint(), ImmutableList.of(next.type), true) + it.add(next) + next = State(RenderType.glint(), ImmutableList.of(next.type), true) + it.add(next) + next = State(RenderType.glintDirect(), ImmutableList.of(next.type), true) + it.add(next) + next = State(RenderType.glintTranslucent(), ImmutableList.of(next.type), true) + it.add(next) + next = State(RenderType.entityGlint(), ImmutableList.of(next.type), true) + it.add(next) + next = State(RenderType.entityGlintDirect(), ImmutableList.of(next.type), true) + it.add(next) + next = State(RenderType.waterMask(), ImmutableList.of(next.type), true) + it.add(next) + } + + private fun determineHeight(input: State, seen: MutableSet) { + if (!seen.add(input)) { + throw IllegalStateException("Recursive dependencies detected when going through ${input.type}: ${seen.joinToString(", ") { it.type.toString() + "[${it.after?.joinToString(", ")}]" }}") + } + + if (input.after == null) { + input.priority = 0 + } else { + for (type in input.after!!) { + for (input2 in bufferList) { + if (input2.type == type) { + if (input2.priority == -1) { + determineHeight(input2, seen) + } + + input.priority = input.priority.coerceAtLeast(input2.priority + 1) + break + } + } + } + + if (input.priority == -1) { + input.priority = 0 + } + } + } + + private fun sortBufferList() { + for (state in bufferList) { + state.priority = -1 + } + + for (state in bufferList) { + val seen = ReferenceArraySet() + determineHeight(state, seen) + } + + bufferList.sortWith { a, b -> + // most referenced buffers go first + b.priority.compareTo(a.priority) + } + } + + private val buffers = Reference2ObjectOpenHashMap().also { + for (v in bufferList) it[v.type] = v + } + + override fun getBuffer(renderType: RenderType): VertexConsumer { + var getState = buffers[renderType] + + if (getState == null) { + getState = State(renderType) + buffers[renderType] = getState + bufferList.add(getState) + getState.dirty = true + return getState.builder.also { + it.begin(renderType.mode(), renderType.format()) + } + } + + if (!getState.dirty) { + getState.dirty = true + getState.builder.begin(renderType.mode(), renderType.format()) + } + + return getState.builder + } + + fun getBuffer(renderType: RenderType, after: RenderType): VertexConsumer { + var getState = buffers[renderType] + + if (getState == null) { + getState = State(renderType, ImmutableList.of(after)) + buffers[renderType] = getState + bufferList.add(getState) + getState.dirty = true + sortBufferList() + return getState.builder.also { + it.begin(renderType.mode(), renderType.format()) + } + } + + if (!getState.immutableAfter && getState.after?.getOrNull(0) !== after) { + getState.after = ImmutableList.of(after) + sortBufferList() + } + + if (!getState.dirty) { + getState.dirty = true + getState.builder.begin(renderType.mode(), renderType.format()) + } + + return getState.builder + } + + fun getBuffer(renderType: RenderType, vararg after: RenderType): VertexConsumer { + var getState = buffers[renderType] + + if (getState == null) { + getState = State(renderType, ImmutableList.copyOf(after)) + buffers[renderType] = getState + bufferList.add(getState) + getState.dirty = true + sortBufferList() + return getState.builder.also { + it.begin(renderType.mode(), renderType.format()) + } + } + + if (!getState.immutableAfter && !equals(getState.after, after)) { + getState.after = ImmutableList.copyOf(after) + sortBufferList() + } + + if (!getState.dirty) { + getState.dirty = true + getState.builder.begin(renderType.mode(), renderType.format()) + } + + return getState.builder + } + + fun getBuffer(renderType: RenderType, after: ImmutableList): VertexConsumer { + var getState = buffers[renderType] + + if (getState == null) { + getState = State(renderType, after) + buffers[renderType] = getState + bufferList.add(getState) + getState.dirty = true + sortBufferList() + return getState.builder.also { + it.begin(renderType.mode(), renderType.format()) + } + } + + if (!getState.immutableAfter && !equals(getState.after, after)) { + getState.after = after + sortBufferList() + } + + if (!getState.dirty) { + getState.dirty = true + getState.builder.begin(renderType.mode(), renderType.format()) + } + + return getState.builder + } + + fun registerType(renderType: RenderType, after: RenderType) { + var getState = buffers[renderType] + + if (getState == null) { + getState = State(renderType, ImmutableList.of(after)) + buffers[renderType] = getState + bufferList.add(getState) + sortBufferList() + } else if (!getState.immutableAfter && getState.after?.getOrNull(0) !== after) { + getState.after = ImmutableList.of(after) + sortBufferList() + } + } + + fun registerType(renderType: RenderType, vararg after: RenderType) { + var getState = buffers[renderType] + + if (getState == null) { + getState = State(renderType, ImmutableList.copyOf(after)) + buffers[renderType] = getState + bufferList.add(getState) + sortBufferList() + } else if (!getState.immutableAfter && !equals(getState.after, after)) { + getState.after = ImmutableList.copyOf(after) + sortBufferList() + } + } + + fun registerType(renderType: RenderType, after: ImmutableList) { + var getState = buffers[renderType] + + if (getState == null) { + getState = State(renderType, after) + buffers[renderType] = getState + bufferList.add(getState) + sortBufferList() + } else if (!getState.immutableAfter && !equals(getState.after, after)) { + getState.after = after + sortBufferList() + } + } + + fun endBatch() { + for (state in bufferList) { + if (state.dirty) { + state.dirty = false + state.type.end(state.builder, 0, 0, 0) + } + } + } + + fun endBatch(type: RenderType) { + val state = buffers[type] ?: return + + if (state.dirty) { + state.dirty = false + type.end(state.builder, 0, 0, 0) + } + } + + companion object { + val GUI = DynamicBufferSource() + } +}