Lunasa
  • 2D Vector Graphics in Minecraft
  • Rendering Libraries
    • NanoVG
      • LWJGL 2.x
      • LWJGL 3.x
Powered by GitBook
On this page
  • Step 1: Configuring our buildscript
  • Step 2: Setting up dependencies
  • Step 3: Patching NanoVG at runtime

Was this helpful?

  1. Rendering Libraries
  2. NanoVG

LWJGL 2.x

This page covers setting up NanoVG in LWJGL 2.x projects. These include legacy Minecraft versions, such as 1.8.9 and 1.12.2.

PreviousNanoVGNextLWJGL 3.x

Last updated 6 months ago

Was this helpful?

LWJGL 2 is an old version of LWJGL and doesn't have the NanoVG bindings. However, LWJGL 3 does have the bindings we need, and we'll go through a simple process to integrate LWJGL 3 bindings into LWJGL 2 projects.

Of course, any of the modern bindings can be used in the legacy versions, not just NanoVG!

Starting off, we need to have a base for our project where we can add NanoVG. I'm using Legacy Fabric, and this will only work with Legacy Fabric.

Legacy Fabric is arguably better than other mod loaders in the case of Minecraft clients. Forge and other platforms are considered deprecated and no longer used, and no support will be provided for those. TLDR: You'll get bullied for using Forge.

We will need to add the LWJGL 3 dependencies to our project, obviously, there are a few conflicts that we need to rule out. We can just relocate these.

Step 1: Configuring our buildscript

To start off, open up gradle.properties and append this snippet of code into it. This just defines the LWJGL 3 version you want to use, we're defining it here, so it's easier to change in the future. The version might be a little bit outdated by the time you're reading this, so always check on the and the to make sure you are always up to date.

gradle.properties
lwjgl_version = 3.3.4

Then, you need to append this at the start of the buildscript, right after the plugins block:

build.gradle.kts
val lwjglVersion = properties["lwjgl_version"] as String

val lwjglNatives = Pair(
	System.getProperty("os.name")!!,
	System.getProperty("os.arch")!!
).let { (name, arch) ->
	when {
		arrayOf("Linux", "SunOS", "Unit").any { name.startsWith(it) } ->
			if (arrayOf("arm", "aarch64").any { arch.startsWith(it) })
				"natives-linux${if (arch.contains("64") || arch.startsWith("armv8")) "-arm64" else "-arm32"}"
			else if (arch.startsWith("ppc"))
				"natives-linux-ppc64le"
			else if (arch.startsWith("riscv"))
				"natives-linux-riscv64"
			else
				"natives-linux"
		arrayOf("Mac OS X", "Darwin").any { name.startsWith(it) }     ->
			"natives-macos"
		arrayOf("Windows").any { name.startsWith(it) }                ->
			if (arch.contains("64"))
				"natives-windows${if (arch.startsWith("aarch64")) "-arm64" else ""}"
			else
				"natives-windows-x86"
		else                                                                            ->
			throw Error("Unrecognized or unsupported platform. Please set \"lwjglNatives\" manually")
	}
}
build.gradle
project.ext.lwjglVersion = project.findProperty('lwjgl_version')

switch (OperatingSystem.current()) {
	case OperatingSystem.LINUX:
		project.ext.lwjglNatives = "natives-linux"
		def osArch = System.getProperty("os.arch")
		if (osArch.startsWith("arm") || osArch.startsWith("aarch64")) {
			project.ext.lwjglNatives += osArch.contains("64") || osArch.startsWith("armv8") ? "-arm64" : "-arm32"
		} else if  (osArch.startsWith("ppc")) {
			project.ext.lwjglNatives += "-ppc64le"
		} else if  (osArch.startsWith("riscv")) {
			project.ext.lwjglNatives += "-riscv64"
		}
		break
	case OperatingSystem.MAC_OS:
		project.ext.lwjglNatives = "natives-macos"
		break
	case OperatingSystem.WINDOWS:
		def osArch = System.getProperty("os.arch")
		project.ext.lwjglNatives = osArch.contains("64")
			? "natives-windows${osArch.startsWith("aarch64") ? "-arm64" : ""}"
			: "natives-windows-x86"
		break
}

This code defines what platform the dependency will use. For example, if you're on a x64 Windows machine, this code will infer that and tell Gradle to use the x64 Windows version of LWJGL.

Step 2: Setting up dependencies

We can't forget the dependencies. These are what give you access to these libraries. We'll focus on setting up these dependencies, namely, Patched LWJGL and the natives, to work with LWJGL 2.x

First, we will start off with appending the Jitpack maven repository to our repositories block. This can be done by adding this entry into repositories

build.gradle.kts
maven("https://jitpack.io")
build.gradle
maven { url = 'https://jitpack.io' }

Once you've done that, we can finally add in the real deal: our LWJGL dependencies!

You can do that by appending these at the end of your dependendencies block:

build.gradle.kts
implementation(platform("org.lwjgl:lwjgl-bom:$lwjglVersion"))

implementation("io.waterwave.Legacy-LWJGL3:lwjgl:$lwjglVersion-5")
implementation("io.waterwave.Legacy-LWJGL3:lwjgl-stb:$lwjglVersion-5")
implementation("io.waterwave.Legacy-LWJGL3:lwjgl-nanovg:$lwjglVersion-5")

runtimeOnly("org.lwjgl:lwjgl::$lwjglNatives")
runtimeOnly("org.lwjgl:lwjgl-nanovg::$lwjglNatives")
runtimeOnly("org.lwjgl:lwjgl-stb::$lwjglNatives")
implementation platform("org.lwjgl:lwjgl-bom:${lwjglVersion}")

implementation "io.waterwave.Legacy-LWJGL3:lwjgl:${lwjglVersion}-5"
implementation "io.waterwave.Legacy-LWJGL3:lwjgl-stb:${lwjglVersion}-5"
implementation "io.waterwave.Legacy-LWJGL3:lwjgl-nanovg:${lwjglVersion}-5"

runtimeOnly "org.lwjgl:lwjgl::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-nanovg::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-stb::$lwjglNatives" 

Once you've configured dependencies, you can refresh the project and wait for Gradle to sync everything.

Step 3: Patching NanoVG at runtime

NanoVG needs a little bit of patching to work with LWJGL. We can patch NanoVG's function provider with our own, compatible with LWJGL 2, so it works well.

First, add this class into your mod:

package dev.lunasa.prov;

import org.lwjgl.opengl.GLContext;
import org.lwjgl.system.FunctionProvider;

import java.lang.reflect.Method;
import java.nio.ByteBuffer;

/**
 * This class is only required in legacy versions which use LWJGL 2.
 *
 * @author DJ The Redstoner
 */
public class Lwjgl2FunctionProvider implements FunctionProvider {

    private final Method m_getFunctionAddress;

    public Lwjgl2FunctionProvider() {
        try {
            m_getFunctionAddress = GLContext.class.getDeclaredMethod("getFunctionAddress", String.class);
            m_getFunctionAddress.setAccessible(true);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public long getFunctionAddress(CharSequence functionName) {
        try {
            return (long) m_getFunctionAddress.invoke(null, functionName.toString());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public long getFunctionAddress(ByteBuffer byteBuffer) {
        throw new UnsupportedOperationException();
    }
}

You can place the class anywhere, that's up to you.

We'll make NanoVG use this function provider using SpongePowered Mixins, Fabric has that built-in!

The Mixins method only works with Fabric. For other mod loaders, you have to use ASM to patch the classes.

Here are the sources of the mixins. It's quite easy to grasp:

import dev.lunasa.prov.Lwjgl2FunctionProvider;
import org.spongepowered.asm.mixin.Mixin;

import org.lwjgl.system.*;
import org.spongepowered.asm.mixin.injection.*;
import org.spongepowered.asm.mixin.injection.callback.*;

/**
 * This class is only required in legacy versions which use LWJGL 2.
 *
 * @author Lunasa
 */
@Mixin(targets = "org.lwjgl.nanovg.NanoVGGLConfig")
public abstract class MixinNanoVGGLConfig {
    @Inject(method = "getFunctionProvider", at = @At("HEAD"),
            cancellable = true, remap = false)
    private static void getFunctionProvider(String className, CallbackInfoReturnable<FunctionProvider> cir) {
        cir.setReturnValue(new Lwjgl2FunctionProvider());
    }
}

This just forces NanoVG to use our function provider instead of their default one.

Now, you will have NanoVG in your classpath and you will be able to use it inside LWJGL 2!

LWJGL website
Legacy LWJGL3 repository