Added preliminary font rendering implementation.
This commit is contained in:
parent
cf67e4588e
commit
0b5bfca706
|
@ -164,6 +164,10 @@
|
||||||
<groupId>org.lwjgl</groupId>
|
<groupId>org.lwjgl</groupId>
|
||||||
<artifactId>lwjgl-glfw</artifactId>
|
<artifactId>lwjgl-glfw</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.lwjgl</groupId>
|
||||||
|
<artifactId>lwjgl-nanovg</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.lwjgl</groupId>
|
<groupId>org.lwjgl</groupId>
|
||||||
<artifactId>lwjgl-meshoptimizer</artifactId>
|
<artifactId>lwjgl-meshoptimizer</artifactId>
|
||||||
|
@ -195,6 +199,11 @@
|
||||||
<artifactId>lwjgl-glfw</artifactId>
|
<artifactId>lwjgl-glfw</artifactId>
|
||||||
<classifier>${lwjgl.natives}</classifier>
|
<classifier>${lwjgl.natives}</classifier>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.lwjgl</groupId>
|
||||||
|
<artifactId>lwjgl-nanovg</artifactId>
|
||||||
|
<classifier>${lwjgl.natives}</classifier>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.lwjgl</groupId>
|
<groupId>org.lwjgl</groupId>
|
||||||
<artifactId>lwjgl-meshoptimizer</artifactId>
|
<artifactId>lwjgl-meshoptimizer</artifactId>
|
||||||
|
|
|
@ -5,7 +5,7 @@ import nl.andrewl.aos2_client.Client;
|
||||||
import nl.andrewl.aos2_client.config.ClientConfig;
|
import nl.andrewl.aos2_client.config.ClientConfig;
|
||||||
import nl.andrewl.aos2_client.model.ClientPlayer;
|
import nl.andrewl.aos2_client.model.ClientPlayer;
|
||||||
import nl.andrewl.aos2_client.render.chunk.ChunkRenderer;
|
import nl.andrewl.aos2_client.render.chunk.ChunkRenderer;
|
||||||
import nl.andrewl.aos2_client.render.gui.GUIRenderer;
|
import nl.andrewl.aos2_client.render.gui.GuiRenderer;
|
||||||
import nl.andrewl.aos2_client.render.gui.GUITexture;
|
import nl.andrewl.aos2_client.render.gui.GUITexture;
|
||||||
import nl.andrewl.aos2_client.render.model.Model;
|
import nl.andrewl.aos2_client.render.model.Model;
|
||||||
import nl.andrewl.aos_core.model.Team;
|
import nl.andrewl.aos_core.model.Team;
|
||||||
|
@ -39,7 +39,7 @@ public class GameRenderer {
|
||||||
|
|
||||||
private final ClientConfig.DisplayConfig config;
|
private final ClientConfig.DisplayConfig config;
|
||||||
private ChunkRenderer chunkRenderer;
|
private ChunkRenderer chunkRenderer;
|
||||||
private GUIRenderer guiRenderer;
|
private GuiRenderer guiRenderer;
|
||||||
private ModelRenderer modelRenderer;
|
private ModelRenderer modelRenderer;
|
||||||
private final Camera camera;
|
private final Camera camera;
|
||||||
private final Client client;
|
private final Client client;
|
||||||
|
@ -129,7 +129,11 @@ public class GameRenderer {
|
||||||
this.chunkRenderer = new ChunkRenderer();
|
this.chunkRenderer = new ChunkRenderer();
|
||||||
log.debug("Initialized chunk renderer.");
|
log.debug("Initialized chunk renderer.");
|
||||||
|
|
||||||
this.guiRenderer = new GUIRenderer();
|
try {
|
||||||
|
this.guiRenderer = new GuiRenderer();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
crosshairTexture = new GUITexture("gui/crosshair.png");
|
crosshairTexture = new GUITexture("gui/crosshair.png");
|
||||||
clipTexture = new GUITexture("gui/clip.png");
|
clipTexture = new GUITexture("gui/clip.png");
|
||||||
bulletTexture = new GUITexture("gui/bullet.png");
|
bulletTexture = new GUITexture("gui/bullet.png");
|
||||||
|
@ -192,7 +196,7 @@ public class GameRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void draw() {
|
public void draw() {
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||||
chunkRenderer.draw(camera, client.getWorld().getChunkMeshesToDraw());
|
chunkRenderer.draw(camera, client.getWorld().getChunkMeshesToDraw());
|
||||||
|
|
||||||
ClientPlayer myPlayer = client.getMyPlayer();
|
ClientPlayer myPlayer = client.getMyPlayer();
|
||||||
|
@ -324,6 +328,7 @@ public class GameRenderer {
|
||||||
-0.90f,
|
-0.90f,
|
||||||
-0.90f
|
-0.90f
|
||||||
);
|
);
|
||||||
|
guiRenderer.drawNvg(screenWidth, screenHeight);
|
||||||
guiRenderer.end();
|
guiRenderer.end();
|
||||||
|
|
||||||
glfwSwapBuffers(windowHandle);
|
glfwSwapBuffers(windowHandle);
|
||||||
|
|
|
@ -1,19 +1,40 @@
|
||||||
package nl.andrewl.aos2_client.render.gui;
|
package nl.andrewl.aos2_client.render.gui;
|
||||||
|
|
||||||
import nl.andrewl.aos2_client.render.ShaderProgram;
|
import nl.andrewl.aos2_client.render.ShaderProgram;
|
||||||
|
import nl.andrewl.aos2_client.util.ResourceUtils;
|
||||||
|
import nl.andrewl.aos_core.FileUtils;
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
import org.lwjgl.BufferUtils;
|
import org.lwjgl.BufferUtils;
|
||||||
|
import org.lwjgl.nanovg.NVGColor;
|
||||||
|
import org.lwjgl.nanovg.NVGPaint;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.FloatBuffer;
|
import java.nio.FloatBuffer;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.lwjgl.nanovg.NanoVG.*;
|
||||||
|
import static org.lwjgl.nanovg.NanoVGGL3.*;
|
||||||
import static org.lwjgl.opengl.GL46.*;
|
import static org.lwjgl.opengl.GL46.*;
|
||||||
|
import static org.lwjgl.system.MemoryUtil.memFree;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages rendering of 2D GUI components like cross-hairs, inventory stuff, etc.
|
* Manages rendering of 2D GUI components like cross-hairs, inventory stuff, etc.
|
||||||
*/
|
*/
|
||||||
public class GUIRenderer {
|
public class GuiRenderer {
|
||||||
|
private final long vgId;
|
||||||
|
private final int jetbrainsMonoFont;
|
||||||
|
private final ByteBuffer jetbrainsMonoFontData;
|
||||||
|
|
||||||
|
private static final NVGColor colorA = NVGColor.create();
|
||||||
|
private static final NVGColor colorB = NVGColor.create();
|
||||||
|
private static final NVGColor colorC = NVGColor.create();
|
||||||
|
|
||||||
|
private static final NVGPaint paintA = NVGPaint.create();
|
||||||
|
private static final NVGPaint paintB = NVGPaint.create();
|
||||||
|
private static final NVGPaint paintC = NVGPaint.create();
|
||||||
|
|
||||||
private final int vaoId;
|
private final int vaoId;
|
||||||
private final int vboId;
|
private final int vboId;
|
||||||
private final int vertexCount;
|
private final int vertexCount;
|
||||||
|
@ -25,7 +46,16 @@ public class GUIRenderer {
|
||||||
|
|
||||||
private final Map<String, GUITexture> textures = new HashMap<>();
|
private final Map<String, GUITexture> textures = new HashMap<>();
|
||||||
|
|
||||||
public GUIRenderer() {
|
public GuiRenderer() throws IOException {
|
||||||
|
vgId = nvgCreate(NVG_ANTIALIAS);
|
||||||
|
jetbrainsMonoFontData = FileUtils.readClasspathResourceAsDirectByteBuffer("text/JetBrainsMono-Regular.ttf");
|
||||||
|
jetbrainsMonoFont = nvgCreateFontMem(
|
||||||
|
vgId,
|
||||||
|
"jetbrains-mono",
|
||||||
|
jetbrainsMonoFontData,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
vaoId = glGenVertexArrays();
|
vaoId = glGenVertexArrays();
|
||||||
vboId = glGenBuffers();
|
vboId = glGenBuffers();
|
||||||
FloatBuffer buffer = BufferUtils.createFloatBuffer(8);
|
FloatBuffer buffer = BufferUtils.createFloatBuffer(8);
|
||||||
|
@ -87,6 +117,17 @@ public class GUIRenderer {
|
||||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexCount);
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void drawNvg(float width, float height) {
|
||||||
|
nvgBeginFrame(vgId, width, height, width / height);
|
||||||
|
nvgSave(vgId);
|
||||||
|
nvgFontSize(vgId, 60f);
|
||||||
|
nvgFontFaceId(vgId, jetbrainsMonoFont);
|
||||||
|
nvgTextAlign(vgId, NVG_ALIGN_LEFT | NVG_ALIGN_TOP);
|
||||||
|
nvgText(vgId, 50, 50, "Hello world!");
|
||||||
|
nvgRestore(vgId);
|
||||||
|
nvgEndFrame(vgId);
|
||||||
|
}
|
||||||
|
|
||||||
public void end() {
|
public void end() {
|
||||||
glDisable(GL_BLEND);
|
glDisable(GL_BLEND);
|
||||||
glEnable(GL_DEPTH_TEST);
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
@ -96,9 +137,13 @@ public class GUIRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void free() {
|
public void free() {
|
||||||
|
memFree(jetbrainsMonoFontData);
|
||||||
|
nvgDelete(vgId);
|
||||||
for (var tex : textures.values()) tex.free();
|
for (var tex : textures.values()) tex.free();
|
||||||
glDeleteBuffers(vboId);
|
glDeleteBuffers(vboId);
|
||||||
glDeleteVertexArrays(vaoId);
|
glDeleteVertexArrays(vaoId);
|
||||||
shaderProgram.free();
|
shaderProgram.free();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package nl.andrewl.aos2_client.render.gui;
|
||||||
|
|
||||||
|
import org.lwjgl.nanovg.NVGColor;
|
||||||
|
|
||||||
|
public class GuiUtils {
|
||||||
|
public static NVGColor rgba(float r, float g, float b, float a, NVGColor color) {
|
||||||
|
color.r(r);
|
||||||
|
color.g(g);
|
||||||
|
color.b(b);
|
||||||
|
color.a(a);
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isBlack(NVGColor color) {
|
||||||
|
return color.r() == 0 && color.g() == 0 && color.b() == 0 && color.a() == 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
package nl.andrewl.aos2_client.util;
|
||||||
|
|
||||||
|
import org.lwjgl.BufferUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.channels.Channels;
|
||||||
|
import java.nio.channels.ReadableByteChannel;
|
||||||
|
import java.nio.channels.SeekableByteChannel;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
import static org.lwjgl.BufferUtils.createByteBuffer;
|
||||||
|
import static org.lwjgl.system.MemoryUtil.memSlice;
|
||||||
|
|
||||||
|
public class ResourceUtils {
|
||||||
|
public static ByteBuffer ioResourceToByteBuffer(String resource, int bufferSize) throws IOException {
|
||||||
|
ByteBuffer buffer;
|
||||||
|
|
||||||
|
Path path = Paths.get(resource);
|
||||||
|
if (Files.isReadable(path)) {
|
||||||
|
try (SeekableByteChannel fc = Files.newByteChannel(path)) {
|
||||||
|
buffer = createByteBuffer((int)fc.size() + 1);
|
||||||
|
while (fc.read(buffer) != -1) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try (
|
||||||
|
InputStream source = ResourceUtils.class.getClassLoader().getResourceAsStream(resource);
|
||||||
|
ReadableByteChannel rbc = Channels.newChannel(source)
|
||||||
|
) {
|
||||||
|
buffer = createByteBuffer(bufferSize);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
int bytes = rbc.read(buffer);
|
||||||
|
if (bytes == -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (buffer.remaining() == 0) {
|
||||||
|
buffer = resizeBuffer(buffer, buffer.capacity() * 3 / 2); // 50%
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.flip();
|
||||||
|
return memSlice(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ByteBuffer resizeBuffer(ByteBuffer buffer, int newCapacity) {
|
||||||
|
ByteBuffer newBuffer = BufferUtils.createByteBuffer(newCapacity);
|
||||||
|
buffer.flip();
|
||||||
|
newBuffer.put(buffer);
|
||||||
|
return newBuffer;
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
|
@ -2,14 +2,40 @@ package nl.andrewl.aos_core;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
|
||||||
public final class FileUtils {
|
public final class FileUtils {
|
||||||
private FileUtils() {}
|
private FileUtils() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads a classpath resource as a string.
|
||||||
|
* @param resource The resource to read.
|
||||||
|
* @return The string contents of the resource.
|
||||||
|
* @throws IOException If the resource can't be found or read.
|
||||||
|
*/
|
||||||
public static String readClasspathFile(String resource) throws IOException {
|
public static String readClasspathFile(String resource) throws IOException {
|
||||||
try (InputStream in = FileUtils.class.getClassLoader().getResourceAsStream(resource)) {
|
try (InputStream in = FileUtils.class.getClassLoader().getResourceAsStream(resource)) {
|
||||||
if (in == null) throw new IOException("Could not load classpath resource: " + resource);
|
if (in == null) throw new IOException("Could not load classpath resource: " + resource);
|
||||||
return new String(in.readAllBytes());
|
return new String(in.readAllBytes());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads a classpath resource into a directly-allocated byte buffer that
|
||||||
|
* must be deallocated manually.
|
||||||
|
* @param resource The resource to read.
|
||||||
|
* @return The byte buffer containing the resource.
|
||||||
|
* @throws IOException If the resource can't be found or read.
|
||||||
|
*/
|
||||||
|
public static ByteBuffer readClasspathResourceAsDirectByteBuffer(String resource) throws IOException {
|
||||||
|
try (InputStream in = FileUtils.class.getClassLoader().getResourceAsStream(resource)) {
|
||||||
|
if (in == null) throw new IOException("Could not load classpath resource: " + resource);
|
||||||
|
byte[] bytes = in.readAllBytes();
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocateDirect(bytes.length).order(ByteOrder.nativeOrder());
|
||||||
|
buffer.put(bytes);
|
||||||
|
buffer.flip();
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,10 +20,7 @@ import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.DatagramPacket;
|
import java.net.*;
|
||||||
import java.net.DatagramSocket;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -118,6 +115,8 @@ public class ClientCommunicationHandler {
|
||||||
.withShutdownHook(() -> server.getPlayerManager().deregister(this.player));
|
.withShutdownHook(() -> server.getPlayerManager().deregister(this.player));
|
||||||
new Thread(tcpReceiver).start();
|
new Thread(tcpReceiver).start();
|
||||||
}
|
}
|
||||||
|
} catch (SocketTimeoutException e) {
|
||||||
|
// Ignore this one, since this will happen if the client doesn't send data properly.
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.warn("An IOException occurred while attempting to establish a connection to a client.", e);
|
log.warn("An IOException occurred while attempting to establish a connection to a client.", e);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue