Fixes and Text
This commit is contained in:
parent
e6ca5fce3b
commit
abf67dd663
10 changed files with 171 additions and 37 deletions
18
build.gradle
18
build.gradle
|
|
@ -24,9 +24,13 @@ dependencies {
|
||||||
implementation "net.fabricmc:fabric-language-kotlin:${project.fabric_kotlin_version}"
|
implementation "net.fabricmc:fabric-language-kotlin:${project.fabric_kotlin_version}"
|
||||||
implementation "com.mojang:brigadier:1.2.9"
|
implementation "com.mojang:brigadier:1.2.9"
|
||||||
|
|
||||||
|
// NanoVG is not part of Minecraft's LWJGL modules, so bundle it (and its
|
||||||
|
// natives) into the mod jar via include so it is present at runtime.
|
||||||
implementation "org.lwjgl:lwjgl-nanovg:${project.nanovg_version}"
|
implementation "org.lwjgl:lwjgl-nanovg:${project.nanovg_version}"
|
||||||
|
include "org.lwjgl:lwjgl-nanovg:${project.nanovg_version}"
|
||||||
['linux', 'linux-arm64', 'windows', 'macos', 'macos-arm64'].each { platform ->
|
['linux', 'linux-arm64', 'windows', 'macos', 'macos-arm64'].each { platform ->
|
||||||
runtimeOnly "org.lwjgl:lwjgl-nanovg:${project.nanovg_version}:natives-${platform}"
|
runtimeOnly "org.lwjgl:lwjgl-nanovg:${project.nanovg_version}:natives-${platform}"
|
||||||
|
include "org.lwjgl:lwjgl-nanovg:${project.nanovg_version}:natives-${platform}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -63,3 +67,17 @@ publishing {
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copies the built lattice and example mod jars into a Minecraft mods folder.
|
||||||
|
// Override the target with -PmodsDir=/path/to/mods (defaults to the Prism instance).
|
||||||
|
def defaultModsDir = "${System.getProperty('user.home')}/.local/share/PrismLauncher/instances/Lattice testing/minecraft/mods"
|
||||||
|
|
||||||
|
tasks.register('deployMods', Copy) {
|
||||||
|
group = 'lattice'
|
||||||
|
description = 'Copies the lattice and example mod jars into a Minecraft mods folder.'
|
||||||
|
dependsOn tasks.named('jar'), project(':example').tasks.named('jar')
|
||||||
|
|
||||||
|
from tasks.named('jar').flatMap { it.archiveFile }
|
||||||
|
from project(':example').tasks.named('jar').flatMap { it.archiveFile }
|
||||||
|
into providers.gradleProperty('modsDir').orElse(defaultModsDir)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
org.gradle.jvmargs=-Xmx4G
|
org.gradle.jvmargs=-Xmx10G
|
||||||
org.gradle.parallel=true
|
org.gradle.parallel=true
|
||||||
org.gradle.configuration-cache=false
|
org.gradle.configuration-cache=false
|
||||||
loom.ignoreDependencyLoomVersionValidation=true
|
loom.ignoreDependencyLoomVersionValidation=true
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
package xyz.meowing.lattice
|
package xyz.meowing.lattice
|
||||||
|
|
||||||
import net.minecraft.resources.Identifier
|
|
||||||
import xyz.meowing.lattice.client.Client.minecraft
|
|
||||||
import xyz.meowing.lattice.event.EventBus
|
import xyz.meowing.lattice.event.EventBus
|
||||||
import xyz.meowing.lattice.render.Font
|
import xyz.meowing.lattice.render.Font
|
||||||
import xyz.meowing.lattice.render.NVGRenderer
|
import xyz.meowing.lattice.render.NVGRenderer
|
||||||
|
|
@ -14,10 +12,7 @@ object Lattice {
|
||||||
val eventBus = EventBus()
|
val eventBus = EventBus()
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val defaultFont: Font by lazy {
|
val defaultFont: Font by lazy { Font("Default", "/assets/$MOD_ID/font.ttf") }
|
||||||
val id = Identifier.fromNamespaceAndPath(MOD_ID, "font.ttf")
|
|
||||||
Font("Default", minecraft.resourceManager.getResourceStack(id).first().open())
|
|
||||||
}
|
|
||||||
|
|
||||||
private var _renderer: Renderer? = null
|
private var _renderer: Renderer? = null
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder.literal
|
||||||
* The tree must always end in an [Executable][xyz.meowing.lattice.command.nodes.Executable],
|
* The tree must always end in an [Executable][xyz.meowing.lattice.command.nodes.Executable],
|
||||||
* which serves as an exit point for a command.
|
* which serves as an exit point for a command.
|
||||||
*/
|
*/
|
||||||
open class Commodore(private val root: LiteralNode) {
|
open class Commodore(@PublishedApi internal val root: LiteralNode) {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
vararg name: String,
|
vararg name: String,
|
||||||
|
|
@ -35,10 +35,18 @@ open class Commodore(private val root: LiteralNode) {
|
||||||
/**
|
/**
|
||||||
* DSL access to the root node for object-style definitions.
|
* DSL access to the root node for object-style definitions.
|
||||||
*/
|
*/
|
||||||
fun runs(function: Function<Unit>) = root.runs(function)
|
|
||||||
|
|
||||||
fun runs(block: () -> Unit) = root.runs(block)
|
fun runs(block: () -> Unit) = root.runs(block)
|
||||||
|
|
||||||
|
inline fun <reified P1> runs(noinline block: (P1) -> Unit) = root.runs(block)
|
||||||
|
|
||||||
|
inline fun <reified P1, reified P2> runs(noinline block: (P1, P2) -> Unit) = root.runs(block)
|
||||||
|
|
||||||
|
inline fun <reified P1, reified P2, reified P3> runs(noinline block: (P1, P2, P3) -> Unit) = root.runs(block)
|
||||||
|
|
||||||
|
inline fun <reified P1, reified P2, reified P3, reified P4> runs(noinline block: (P1, P2, P3, P4) -> Unit) = root.runs(block)
|
||||||
|
|
||||||
|
inline fun <reified P1, reified P2, reified P3, reified P4, reified P5> runs(noinline block: (P1, P2, P3, P4, P5) -> Unit) = root.runs(block)
|
||||||
|
|
||||||
fun literal(string: String, block: LiteralNode.() -> Unit = {}): LiteralNode {
|
fun literal(string: String, block: LiteralNode.() -> Unit = {}): LiteralNode {
|
||||||
return root.literal(string, block)
|
return root.literal(string, block)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package xyz.meowing.lattice.command.functions
|
||||||
|
|
||||||
import java.lang.invoke.MethodHandle
|
import java.lang.invoke.MethodHandle
|
||||||
import java.lang.invoke.MethodHandles
|
import java.lang.invoke.MethodHandles
|
||||||
|
import java.lang.reflect.ParameterizedType
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* # FunctionInvoker
|
* # FunctionInvoker
|
||||||
|
|
@ -59,7 +60,21 @@ sealed interface FunctionInvoker<T> {
|
||||||
mHandle = MethodHandles.lookup().unreflect(invokeMethod).bindTo(lambda)
|
mHandle = MethodHandles.lookup().unreflect(invokeMethod).bindTo(lambda)
|
||||||
|
|
||||||
if (parameterTypes.isEmpty()) {
|
if (parameterTypes.isEmpty()) {
|
||||||
parameters = invokeMethod.parameterTypes.mapIndexed { index, type ->
|
val rawTypes = invokeMethod.parameterTypes
|
||||||
|
// Kotlin K2/IR may compile lambdas so the only non-bridge invoke method
|
||||||
|
// has erased Object types. Recover concrete types from the FunctionN
|
||||||
|
// generic interface that the lambda class still declares.
|
||||||
|
val actualTypes = if (rawTypes.isNotEmpty() && rawTypes.all { it == Any::class.java }) {
|
||||||
|
recoverFromFunctionInterface(lambdaClass, rawTypes.size)
|
||||||
|
?: throw IllegalStateException(
|
||||||
|
"Cannot recover parameter types for lambda ${lambdaClass.name}. " +
|
||||||
|
"Interfaces: ${lambdaClass.genericInterfaces.toList()}. " +
|
||||||
|
"Use a typed runs { p: Type -> } overload instead of Function<Unit>."
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
rawTypes
|
||||||
|
}
|
||||||
|
parameters = actualTypes.mapIndexed { index, type ->
|
||||||
Parameter("param$index", type, false)
|
Parameter("param$index", type, false)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -76,6 +91,27 @@ sealed interface FunctionInvoker<T> {
|
||||||
override fun invoke(arguments: List<Any?>): T {
|
override fun invoke(arguments: List<Any?>): T {
|
||||||
return mHandle.invokeWithArguments(arguments) as T
|
return mHandle.invokeWithArguments(arguments) as T
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
fun recoverFromFunctionInterface(lambdaClass: Class<*>, paramCount: Int): Array<Class<*>>? {
|
||||||
|
for (iface in lambdaClass.genericInterfaces) {
|
||||||
|
val pt = iface as? ParameterizedType ?: continue
|
||||||
|
val raw = pt.rawType as? Class<*> ?: continue
|
||||||
|
if (!raw.name.startsWith("kotlin.jvm.functions.Function")) continue
|
||||||
|
val args = pt.actualTypeArguments
|
||||||
|
// FunctionN<P1,...,Pn,R> — last arg is return type
|
||||||
|
if (args.size != paramCount + 1) continue
|
||||||
|
return Array(paramCount) { i ->
|
||||||
|
when (val arg = args[i]) {
|
||||||
|
is Class<*> -> arg
|
||||||
|
is ParameterizedType -> arg.rawType as? Class<*> ?: Any::class.java
|
||||||
|
else -> Any::class.java
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
||||||
|
|
@ -72,8 +72,31 @@ class Executable : Node() {
|
||||||
* - Use a [ParameterModifier] to apply a custom parser to a certain parameter.
|
* - Use a [ParameterModifier] to apply a custom parser to a certain parameter.
|
||||||
* You need to ensure you're parsing for the correct class.
|
* You need to ensure you're parsing for the correct class.
|
||||||
*/
|
*/
|
||||||
fun runs(function: Function<Unit>) {
|
fun runs(block: () -> Unit) = define(block)
|
||||||
funInvoker = FunctionInvoker.from(function)
|
|
||||||
|
inline fun <reified P1> runs(noinline block: (P1) -> Unit) =
|
||||||
|
define(block, P1::class.java)
|
||||||
|
|
||||||
|
inline fun <reified P1, reified P2> runs(noinline block: (P1, P2) -> Unit) =
|
||||||
|
define(block, P1::class.java, P2::class.java)
|
||||||
|
|
||||||
|
inline fun <reified P1, reified P2, reified P3> runs(noinline block: (P1, P2, P3) -> Unit) =
|
||||||
|
define(block, P1::class.java, P2::class.java, P3::class.java)
|
||||||
|
|
||||||
|
inline fun <reified P1, reified P2, reified P3, reified P4> runs(noinline block: (P1, P2, P3, P4) -> Unit) =
|
||||||
|
define(block, P1::class.java, P2::class.java, P3::class.java, P4::class.java)
|
||||||
|
|
||||||
|
inline fun <reified P1, reified P2, reified P3, reified P4, reified P5> runs(noinline block: (P1, P2, P3, P4, P5) -> Unit) =
|
||||||
|
define(block, P1::class.java, P2::class.java, P3::class.java, P4::class.java, P5::class.java)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Low-level entry point that builds the invoker from a function and its
|
||||||
|
* explicit parameter types. Prefer the typed [runs] overloads, which capture
|
||||||
|
* the types via reified generics instead of relying on runtime reflection.
|
||||||
|
*/
|
||||||
|
@PublishedApi
|
||||||
|
internal fun define(function: Function<Unit>, vararg explicitTypes: Class<*>) {
|
||||||
|
funInvoker = FunctionInvoker.from(function, *explicitTypes)
|
||||||
|
|
||||||
for (parameter in funInvoker.parameters) {
|
for (parameter in funInvoker.parameters) {
|
||||||
val modifier = parameterModifiers[parameter.name]
|
val modifier = parameterModifiers[parameter.name]
|
||||||
|
|
|
||||||
|
|
@ -94,15 +94,29 @@ open class LiteralNode(val name: String) : Node() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an [Executable] and apply function directly.
|
* Creates an [Executable] that runs [block] with typed command arguments.
|
||||||
*
|
*
|
||||||
* This won't allow you to apply [modifiers][Executable.ParameterModifier]
|
* Each parameter type is captured via reified generics, so it works with
|
||||||
* to the function's parameters.
|
* Kotlin's invokedynamic lambdas where the types are erased at runtime.
|
||||||
*
|
|
||||||
* @see runs
|
|
||||||
*/
|
*/
|
||||||
fun runs(function: Function<Unit>) = executable {
|
inline fun <reified P1> runs(noinline block: (P1) -> Unit) = executable {
|
||||||
runs(function)
|
define(block, P1::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified P1, reified P2> runs(noinline block: (P1, P2) -> Unit) = executable {
|
||||||
|
define(block, P1::class.java, P2::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified P1, reified P2, reified P3> runs(noinline block: (P1, P2, P3) -> Unit) = executable {
|
||||||
|
define(block, P1::class.java, P2::class.java, P3::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified P1, reified P2, reified P3, reified P4> runs(noinline block: (P1, P2, P3, P4) -> Unit) = executable {
|
||||||
|
define(block, P1::class.java, P2::class.java, P3::class.java, P4::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified P1, reified P2, reified P3, reified P4, reified P5> runs(noinline block: (P1, P2, P3, P4, P5) -> Unit) = executable {
|
||||||
|
define(block, P1::class.java, P2::class.java, P3::class.java, P4::class.java, P5::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,12 @@ object GLState {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
var previousProgram = -1
|
var previousProgram = -1
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
var previousSampler = -1
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
var previousVao = -1
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
var drawing = false
|
var drawing = false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package xyz.meowing.lattice.render
|
||||||
|
|
||||||
import com.mojang.blaze3d.opengl.GlStateManager
|
import com.mojang.blaze3d.opengl.GlStateManager
|
||||||
import com.mojang.blaze3d.systems.RenderSystem
|
import com.mojang.blaze3d.systems.RenderSystem
|
||||||
|
import org.apache.logging.log4j.LogManager
|
||||||
import org.lwjgl.nanovg.NVGColor
|
import org.lwjgl.nanovg.NVGColor
|
||||||
import org.lwjgl.nanovg.NVGPaint
|
import org.lwjgl.nanovg.NVGPaint
|
||||||
import org.lwjgl.nanovg.NanoSVG
|
import org.lwjgl.nanovg.NanoSVG
|
||||||
|
|
@ -11,6 +12,7 @@ import org.lwjgl.opengl.GL11
|
||||||
import org.lwjgl.opengl.GL13
|
import org.lwjgl.opengl.GL13
|
||||||
import org.lwjgl.opengl.GL20
|
import org.lwjgl.opengl.GL20
|
||||||
import org.lwjgl.opengl.GL30
|
import org.lwjgl.opengl.GL30
|
||||||
|
import org.lwjgl.opengl.GL33C
|
||||||
import org.lwjgl.stb.STBImage
|
import org.lwjgl.stb.STBImage
|
||||||
import org.lwjgl.system.MemoryUtil
|
import org.lwjgl.system.MemoryUtil
|
||||||
import xyz.meowing.lattice.client.Client.minecraft
|
import xyz.meowing.lattice.client.Client.minecraft
|
||||||
|
|
@ -18,7 +20,6 @@ import xyz.meowing.lattice.render.Color.Companion.alpha
|
||||||
import xyz.meowing.lattice.render.Color.Companion.blue
|
import xyz.meowing.lattice.render.Color.Companion.blue
|
||||||
import xyz.meowing.lattice.render.Color.Companion.green
|
import xyz.meowing.lattice.render.Color.Companion.green
|
||||||
import xyz.meowing.lattice.render.Color.Companion.red
|
import xyz.meowing.lattice.render.Color.Companion.red
|
||||||
import java.lang.reflect.Field
|
|
||||||
import java.lang.reflect.Method
|
import java.lang.reflect.Method
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
|
@ -48,23 +49,30 @@ object NVGRenderer : Renderer {
|
||||||
// Resolved reflectively once: GlTexture.getFbo(GlStateManager$DirectStateAccess, depth) is not public API.
|
// Resolved reflectively once: GlTexture.getFbo(GlStateManager$DirectStateAccess, depth) is not public API.
|
||||||
private var fboLookup: Pair<Any, Method>? = null
|
private var fboLookup: Pair<Any, Method>? = null
|
||||||
private var fboLookupFailed = false
|
private var fboLookupFailed = false
|
||||||
|
private var loggedFbo = false
|
||||||
|
|
||||||
private fun mainFramebuffer(colorTex: Any): Int {
|
private fun fieldValue(obj: Any, name: String): Any? {
|
||||||
|
var clazz: Class<*>? = obj.javaClass
|
||||||
|
while (clazz != null) {
|
||||||
|
try {
|
||||||
|
return clazz.getDeclaredField(name).apply { isAccessible = true }.get(obj)
|
||||||
|
} catch (_: NoSuchFieldException) {
|
||||||
|
clazz = clazz.superclass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun mainFramebuffer(colorTex: Any, depthTex: Any?): Int {
|
||||||
if (fboLookupFailed) return 0
|
if (fboLookupFailed) return 0
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val lookup = fboLookup ?: run {
|
val lookup = fboLookup ?: run {
|
||||||
val device = RenderSystem.getDevice() ?: return 0
|
val device = RenderSystem.getDevice() ?: return 0
|
||||||
var dsaField: Field? = null
|
// GpuDevice wraps the GL backend (GlDevice), which owns directStateAccess.
|
||||||
var clazz: Class<*>? = device.javaClass
|
val backend = fieldValue(device, "backend") ?: device
|
||||||
while (clazz != null && dsaField == null) {
|
val dsa = fieldValue(backend, "directStateAccess")
|
||||||
try {
|
?: throw IllegalStateException("no directStateAccess")
|
||||||
dsaField = clazz.getDeclaredField("directStateAccess")
|
|
||||||
} catch (_: NoSuchFieldException) {}
|
|
||||||
clazz = clazz.superclass
|
|
||||||
}
|
|
||||||
dsaField?.isAccessible = true
|
|
||||||
val dsa = dsaField?.get(device) ?: throw IllegalStateException("no directStateAccess")
|
|
||||||
|
|
||||||
var getFbo: Method? = null
|
var getFbo: Method? = null
|
||||||
var texClazz: Class<*>? = colorTex.javaClass
|
var texClazz: Class<*>? = colorTex.javaClass
|
||||||
|
|
@ -78,9 +86,12 @@ object NVGRenderer : Renderer {
|
||||||
Pair(dsa, getFbo).also { fboLookup = it }
|
Pair(dsa, getFbo).also { fboLookup = it }
|
||||||
}
|
}
|
||||||
|
|
||||||
return (lookup.second.invoke(colorTex, lookup.first, null) as? Int) ?: 0
|
// Pass the depth texture so getFbo returns the FBO with the depth+stencil
|
||||||
} catch (_: Exception) {
|
// attachment; NanoVG's stencil fills render nothing on a color-only FBO.
|
||||||
|
return (lookup.second.invoke(colorTex, lookup.first, depthTex) as? Int) ?: 0
|
||||||
|
} catch (e: Throwable) {
|
||||||
fboLookupFailed = true
|
fboLookupFailed = true
|
||||||
|
LogManager.getLogger("Lattice").warn("FBO lookup failed: device=${RenderSystem.getDevice()?.javaClass?.name} color=${colorTex.javaClass.name} depth=${depthTex?.javaClass?.name}", e)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -95,10 +106,23 @@ object NVGRenderer : Renderer {
|
||||||
val colorTex = renderTarget.getColorTexture() ?: return
|
val colorTex = renderTarget.getColorTexture() ?: return
|
||||||
if (vg == -1L) return
|
if (vg == -1L) return
|
||||||
|
|
||||||
GlStateManager._glBindFramebuffer(GL30.GL_FRAMEBUFFER, mainFramebuffer(colorTex))
|
val fbo = mainFramebuffer(colorTex, renderTarget.getDepthTexture())
|
||||||
|
if (!loggedFbo) {
|
||||||
|
loggedFbo = true
|
||||||
|
LogManager.getLogger("Lattice").info("NVG beginFrame: fbo=$fbo failed=$fboLookupFailed size=${width}x$height target=${renderTarget.width}x${renderTarget.height}")
|
||||||
|
}
|
||||||
|
GlStateManager._glBindFramebuffer(GL30.GL_FRAMEBUFFER, fbo)
|
||||||
GlStateManager._viewport(0, 0, renderTarget.width, renderTarget.height)
|
GlStateManager._viewport(0, 0, renderTarget.width, renderTarget.height)
|
||||||
GlStateManager._activeTexture(GL30.GL_TEXTURE0)
|
GlStateManager._activeTexture(GL30.GL_TEXTURE0)
|
||||||
|
|
||||||
|
// NanoVG binds its own VAO without restoring the previous one.
|
||||||
|
GLState.previousVao = GL11.glGetInteger(GL30.GL_VERTEX_ARRAY_BINDING)
|
||||||
|
|
||||||
|
// Unbind MC's sampler from unit 0 so NanoVG's font atlas texture samples
|
||||||
|
// with its own parameters; otherwise glyphs render blank while shapes are fine.
|
||||||
|
GLState.previousSampler = GL11.glGetInteger(GL33C.GL_SAMPLER_BINDING)
|
||||||
|
GL33C.glBindSampler(0, 0)
|
||||||
|
|
||||||
NanoVG.nvgBeginFrame(vg, width, height, 1f)
|
NanoVG.nvgBeginFrame(vg, width, height, 1f)
|
||||||
NanoVG.nvgTextAlign(vg, NanoVG.NVG_ALIGN_LEFT or NanoVG.NVG_ALIGN_TOP)
|
NanoVG.nvgTextAlign(vg, NanoVG.NVG_ALIGN_LEFT or NanoVG.NVG_ALIGN_TOP)
|
||||||
GLState.drawing = true
|
GLState.drawing = true
|
||||||
|
|
@ -114,13 +138,23 @@ object NVGRenderer : Renderer {
|
||||||
GlStateManager._enableBlend()
|
GlStateManager._enableBlend()
|
||||||
GlStateManager._blendFuncSeparate(770, 771, 1, 0)
|
GlStateManager._blendFuncSeparate(770, 771, 1, 0)
|
||||||
|
|
||||||
|
// NanoVG's stencil strokes can leave the stencil test on and the color mask off.
|
||||||
|
GL11.glDisable(GL11.GL_STENCIL_TEST)
|
||||||
|
GL11.glColorMask(true, true, true, true)
|
||||||
|
|
||||||
if (GLState.previousProgram != -1) GlStateManager._glUseProgram(GLState.previousProgram)
|
if (GLState.previousProgram != -1) GlStateManager._glUseProgram(GLState.previousProgram)
|
||||||
|
|
||||||
|
// Restore the sampler on unit 0 that we unbound for the font atlas.
|
||||||
|
GlStateManager._activeTexture(GL30.GL_TEXTURE0)
|
||||||
|
if (GLState.previousSampler != -1) GL33C.glBindSampler(0, GLState.previousSampler)
|
||||||
|
|
||||||
if (GLState.previousActiveTexture != -1) {
|
if (GLState.previousActiveTexture != -1) {
|
||||||
GlStateManager._activeTexture(GLState.previousActiveTexture)
|
GlStateManager._activeTexture(GLState.previousActiveTexture)
|
||||||
if (GLState.previousBoundTexture != -1) GlStateManager._bindTexture(GLState.previousBoundTexture)
|
if (GLState.previousBoundTexture != -1) GlStateManager._bindTexture(GLState.previousBoundTexture)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (GLState.previousVao != -1) GL30.glBindVertexArray(GLState.previousVao)
|
||||||
|
|
||||||
GlStateManager._glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0)
|
GlStateManager._glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0)
|
||||||
GLState.drawing = false
|
GLState.drawing = false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -95,10 +95,10 @@ abstract class UIScreen(screenName: String = "Lattice-Screen") : Screen(Componen
|
||||||
super.onClose()
|
super.onClose()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Opens this screen on the next client tick, safe to call from any thread. */
|
/** Opens this screen on the render thread, safe to call from any thread. */
|
||||||
fun display() {
|
fun display() {
|
||||||
TimeScheduler.schedule(50) {
|
TimeScheduler.schedule(50) {
|
||||||
minecraft.setScreen(this@UIScreen)
|
minecraft.execute { minecraft.setScreen(this@UIScreen) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue