RT4 JOGL => LWJGL OpenGL Bindings + Mobile 2.0

Various development-related discussions
Post Reply
User avatar
downthecrop
Staff
Posts: 33
Joined: Thu Aug 11, 2022 10:37 am
Location: twitch.tv/downthecrop
Has thanked: 13 times
Been thanked: 12 times
Contact:

RT4 JOGL => LWJGL OpenGL Bindings + Mobile 2.0

Post by downthecrop »

Porting JOGL to LWJGL

Hello friends! I am making a quick writeup to share my process of porting most of the features of the RT4 client from JOGL to LWJGL.

Apparently "It just takes like 20 minutes tops." according to an expert on Rune-Server :) . I started Jul 12, and had it launching under Android Jul 16.

To start with, they are very similar. Both provide bindings for OpenGL and because of that much of the syntax could simply be change from

Code: Select all

GL2 gl = GlRenderer.gl; 
gl.glClear()
to the simple

Code: Select all

GL11.glClear();
Or just import to call glClear() directly.

Anyhow the main differences were in how the window was created and the canvas was handled. JOGL created a hybrid surface with NativeAWT. This surface was a good solution at the time as it directly replaced the pre-existing AWT canvas that was used in previous clients and would simply accept the same mouse and key events due to it being an AWT canvas.

In LWJGL I used GLFW to create a new window instead.

Note: in the event that someone wants to use my work here as a baseline for native hd on macos yes it would work! Keyboard bindings are already in place. Just zip the natives and it's read to go (excluding the features I explain below)

By creating the GLFW window and glcontext on the same thread that JOGL was on I was able to basically "hijack" the context and have it draw on the GLFW window instead. This was a great relief because it meant that the porting job would be simpler than I thought!

From here I worked on changing all JOGL context calls to LWJGL GL11 or GL20 calls. Many constants like GL_CLEAR_COLOR or similar are still using JOGL for that. It makes no real difference as they work on both. I guess by using them we can't completely remove the jar files from the build but who cares.

Thankfully because of the context being handled by GLFW and being able to receive commands from both bindings I could go file by file ripping out the calls and converting them on Windows. Most of the syntax change was just removing trailing zeros but some ByteBuffers and big/little endian changes needed to be flipped beforehand. The testing was as easy as modifying the file. Logging in. Check if what I changed still worked and then continuing to the next file.

EG - JOGL

GlAlphaSprite.java

Code: Select all

@OriginalMember(owner = "client!el", name = "a", descriptor = "([I)V")
	@Override
	protected final void method1430(@OriginalArg(0) int[] arg0) {
		this.powerOfTwoWidth = IntUtils.clp2(this.width);
		this.powerOfTwoHeight = IntUtils.clp2(this.height);
		@Pc(20) byte[] local20 = new byte[this.powerOfTwoWidth * this.powerOfTwoHeight * 4];
		@Pc(22) int local22 = 0;
		@Pc(24) int local24 = 0;
		@Pc(32) int local32 = (this.powerOfTwoWidth - this.width) * 4;
		for (@Pc(34) int local34 = 0; local34 < this.height; local34++) {
			for (@Pc(40) int local40 = 0; local40 < this.width; local40++) {
				@Pc(49) int local49 = arg0[local24++];
				if (local49 == 0) {
					local22 += 4;
				} else {
					local20[local22++] = (byte) (local49 >> 16);
					local20[local22++] = (byte) (local49 >> 8);
					local20[local22++] = (byte) local49;
					local20[local22++] = (byte) (local49 >> 24);
				}
			}
			local22 += local32;
		}
		@Pc(94) ByteBuffer local94 = ByteBuffer.wrap(local20);
		@Pc(96) GL2 local96 = GlRenderer.gl;
		if (this.textureId == -1) {
			@Pc(103) int[] local103 = new int[1];
			local96.glGenTextures(1, local103, 0);
			this.textureId = local103[0];
		}
		GlRenderer.setTextureId(this.textureId);
		local96.glTexImage2D(GL2.GL_TEXTURE_2D, 0, GL2.GL_RGBA, this.powerOfTwoWidth, this.powerOfTwoHeight, 0, GL2.GL_RGBA, GL2.GL_UNSIGNED_BYTE, local94);
		GlCleaner.onCard2d += local94.limit() - this.anInt1869;
		this.anInt1869 = local94.limit();
	}
LWJGL

Code: Select all

@Override
	protected final void method1430(int[] arg0) {
		this.powerOfTwoWidth = IntUtils.clp2(this.width);
		this.powerOfTwoHeight = IntUtils.clp2(this.height);
		byte[] local20 = new byte[this.powerOfTwoWidth * this.powerOfTwoHeight * 4];
		int local22 = 0;
		int local24 = 0;
		int local32 = (this.powerOfTwoWidth - this.width) * 4;
		for (int local34 = 0; local34 < this.height; local34++) {
			for (int local40 = 0; local40 < this.width; local40++) {
				int local49 = arg0[local24++];
				if (local49 == 0) {
					local22 += 4;
				} else {
					local20[local22++] = (byte) (local49 >> 16);
					local20[local22++] = (byte) (local49 >> 8);
					local20[local22++] = (byte) local49;
					local20[local22++] = (byte) (local49 >> 24);
				}
			}
			local22 += local32;
		}
		/*
		Magic code to convert not working JOGL buffers into working LWJGL buffers.
		 */
		ByteBuffer tempBuffer = ByteBuffer.wrap(local20);
		ByteBuffer local94 = BufferUtils.createByteBuffer(tempBuffer.capacity());
		local94.put(tempBuffer);
		local94.flip();
		if (this.textureId == -1) {
			this.textureId = glGenTextures();
		}
		GlRenderer.setTextureId(this.textureId);
		glTexImage2D(GL2.GL_TEXTURE_2D, 0, GL2.GL_RGBA, this.powerOfTwoWidth, this.powerOfTwoHeight, 0, GL2.GL_RGBA, GL2.GL_UNSIGNED_BYTE, local94);
		GlCleaner.onCard2d += local94.limit() - this.anInt1869;
		this.anInt1869 = local94.limit();
	}
Also glDrawElements needed to be wrapped into a helper function.

Code: Select all

public static void glDrawElementsWrapper(int mode, int count, int type, java.nio.Buffer buffer) {  
    long pointer = MemoryUtil.memAddress(buffer);  
    glDrawElements(mode, count, type, pointer);  
}

Because the GLFW window is focused instead of the AWT game window mouse and keyboard events aren't registered when clicking on the window the same way they would with JOGL. To solve this in the simplest way I could think of I decided to create an event bridge that wraps GLFW inputs and sends them as reformed AWT Keyboard and Mouse events to Keyboard.java and Mouse.java this has the added benefit of allowing plugins that hook mouse and keyboard events to work without any issues. On Android I already had some special handling (the MobileClientBindings plugin) which after getting plugins loading was no issue. Special characters ect all work and even better now thanks to some extra backend work by the Pojav team all soft keyboard work by typing into an invisible text buffer and dispatching the characters instead of directly relying on dispatchKeyEvents for soft keyboard which have no predefined spec for key events.

Anyway! Here are some known issues with the current render system!

- Gigahack of clearing the texture cache every frame to avoid corruption
- High detail Water shader doesn't work
- arbMultisampleSupported & arbMultisampleSupported
- No startup/setup loading bar (AWT)
- just read the cool verbose log instead and pretend it's a red rectangle.
- World Map (glDrawPixels is borked)
- Switching between render methods (Software/OpenGL) at runtime
- Fixed Mode HD Is off-center

Things intentionally turned off because I don't want to bother fixing them right now.

- Shadows caused a texture corruption on mobile but they do work. https://gitlab.com/downthecrop/rt4-clie ... 386a0107c3
- The "loading please wait" message during gameState 28 doesn't work on mobile, probably due to the same glDrawPixels issue. We just skip the DoubleBuffer swap when this happens instead (no message bar but similar result)
- Audio doesn't work. I got really close! My buffers lag behind and area sounds/sfx weren't working. Something for another time. If you want to pick this up (maybe it just needs a new set of eyes on it it's really super close to working) you can checkout my branch here: https://gitlab.com/downthecrop/rt4-clie ... penalaudio (same audio on mobile and desktop so if you fix it on desktop it will be fixed on mobile)

I'd say 90% complete. Absolutely has everything you need to enjoy the game but some features like shadows and not clearing the texture cache ect just need someone with better gpu memory debugging to lend a hand.

It launches on mobile and plays great! 60fps (framecapped in the client) in a lot of scenarios on my Pixel 6 using HolyGL4ES renderer. I am doing a 50% resolution scale though. I'll add support for ui scaling maybe someday! Would probably just work if we packed the JRE17 the pojav boys provide.

The actual android side of things was basically just working the new PojavLauncher into a smaller version of itself. The original 2009Scape-mobile launcher didn't go far enough in removing unused components so in the 2.0 version we only have exactly what we need. Avoid confusion for anyone wanting to work on it. A lot less this referencing minecraft haha. ( could still use some work at this point in time )

Again amazing work by the Pojav team, special thanks to artDev for answering a few questions I had about launching files on the GL surface.

HolyGL4ES renderer works amazing (another pojav team effort) and I'm very grateful that the most complicated parts of this entire process had already been taken care of by more capable hands.

Most of the render pipeline stuff really does still go over my head and it doesn't help, most of it still hasn't been deobed.

Anyway! Check out 2009Scape Mobile 2.0. It's really great!

Watch my meme trailer: https://youtu.be/9yxBUYIvlvo (Made with REAL Windows Movie Maker + REAL Unregistered HyperCam2 for the authentic feel!)
Image
User avatar
grunks
Noob
Posts: 3
Joined: Thu Aug 10, 2023 9:37 pm

Re: RT4 JOGL => LWJGL OpenGL Bindings + Mobile 2.0

Post by grunks »

Nice work on getting this to a usable state, it looks great! I did notice that in HD mode the interface buttons are very close to the edge of the screen, is it possible to remove some of the 'real estate' that the app can use to move them away from the edge? I also noticed that long press to open menus doesn't seem to work. You may already be aware of these, but just thought I would list in checking it out!
User avatar
downthecrop
Staff
Posts: 33
Joined: Thu Aug 11, 2022 10:37 am
Location: twitch.tv/downthecrop
Has thanked: 13 times
Been thanked: 12 times
Contact:

Re: RT4 JOGL => LWJGL OpenGL Bindings + Mobile 2.0

Post by downthecrop »

You can actually do completely custom layouts yourself inside of the app. Top middle of HD screen click tje gear then "custom controls" you can drag the buttons wherever you like or even add more!
Image
User avatar
grunks
Noob
Posts: 3
Joined: Thu Aug 10, 2023 9:37 pm

Re: RT4 JOGL => LWJGL OpenGL Bindings + Mobile 2.0

Post by grunks »

Sorry, I should have clarified. It's the runescape interface buttons that are too close to the edge
User avatar
downthecrop
Staff
Posts: 33
Joined: Thu Aug 11, 2022 10:37 am
Location: twitch.tv/downthecrop
Has thanked: 13 times
Been thanked: 12 times
Contact:

Re: RT4 JOGL => LWJGL OpenGL Bindings + Mobile 2.0

Post by downthecrop »

I do plan on adding vertical and horizontal inset options in the future. Maybe next month. It's a one programming session thing. I'll put that on the todo.
Image
User avatar
grunks
Noob
Posts: 3
Joined: Thu Aug 10, 2023 9:37 pm

Re: RT4 JOGL => LWJGL OpenGL Bindings + Mobile 2.0

Post by grunks »

Nice, that would be awesome! Thanks for taking a look
Post Reply