Added nameplate rendering.
This commit is contained in:
parent
3a70a4566b
commit
ec9153053c
client/src/main
java/nl/andrewl/aos2_client
resources/shader/gui
core/src/main/java/nl/andrewl/aos_core
|
@ -213,6 +213,7 @@ public class Client implements Runnable {
|
|||
player.updateModelTransform();
|
||||
soundManager.playWalkingSounds(player, now);
|
||||
}
|
||||
gameRenderer.getGuiRenderer().updateNamePlates(players.values());
|
||||
}
|
||||
|
||||
public void interpolateProjectiles(float dt) {
|
||||
|
|
|
@ -171,6 +171,10 @@ public class GameRenderer {
|
|||
return camera;
|
||||
}
|
||||
|
||||
public GuiRenderer getGuiRenderer() {
|
||||
return guiRenderer;
|
||||
}
|
||||
|
||||
public void draw() {
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||
chunkRenderer.draw(camera, client.getWorld().getChunkMeshesToDraw());
|
||||
|
@ -266,6 +270,7 @@ public class GameRenderer {
|
|||
|
||||
// GUI rendering
|
||||
guiRenderer.start();
|
||||
guiRenderer.drawNameplates(myPlayer, camera.getViewTransformData(), perspectiveTransform.get(new float[16]));
|
||||
guiRenderer.drawNvg(screenWidth, screenHeight, myPlayer);
|
||||
guiRenderer.end();
|
||||
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
package nl.andrewl.aos2_client.render.gui;
|
||||
|
||||
import nl.andrewl.aos2_client.Camera;
|
||||
import nl.andrewl.aos2_client.model.ClientPlayer;
|
||||
import nl.andrewl.aos2_client.model.OtherPlayer;
|
||||
import nl.andrewl.aos2_client.render.ShaderProgram;
|
||||
import nl.andrewl.aos_core.FileUtils;
|
||||
import nl.andrewl.aos_core.model.Player;
|
||||
import nl.andrewl.aos_core.model.item.BlockItem;
|
||||
import nl.andrewl.aos_core.model.item.BlockItemStack;
|
||||
import nl.andrewl.aos_core.model.item.Gun;
|
||||
|
@ -12,11 +15,12 @@ import org.lwjgl.BufferUtils;
|
|||
import org.lwjgl.nanovg.NVGColor;
|
||||
import org.lwjgl.nanovg.NVGPaint;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
import static org.lwjgl.nanovg.NanoVG.*;
|
||||
import static org.lwjgl.nanovg.NanoVGGL3.*;
|
||||
|
@ -49,7 +53,13 @@ public class GuiRenderer {
|
|||
private final Matrix4f transformMatrix;
|
||||
private final float[] transformMatrixData;
|
||||
|
||||
private final Map<String, GUITexture> textures = new HashMap<>();
|
||||
private final ShaderProgram namePlateShaderProgram;
|
||||
private final int namePlateTransformUniform;
|
||||
private final int namePlateViewTransformUniform;
|
||||
private final int namePlatePerspectiveTransformUniform;
|
||||
private final int namePlateTextureSamplerUniform;
|
||||
private final Font namePlateFont;
|
||||
private final Map<OtherPlayer, GUITexture> playerNamePlates = new HashMap<>();
|
||||
|
||||
public GuiRenderer() throws IOException {
|
||||
vgId = nvgCreate(NVG_ANTIALIAS);
|
||||
|
@ -85,20 +95,26 @@ public class GuiRenderer {
|
|||
transformUniformLocation = shaderProgram.getUniform("transform");
|
||||
textureSamplerUniform = shaderProgram.getUniform("guiTexture");
|
||||
shaderProgram.bindAttribute(0, "position");
|
||||
this.transformMatrix = new Matrix4f();
|
||||
this.transformMatrixData = new float[16];
|
||||
}
|
||||
|
||||
public void loadTexture(String name, String resource) {
|
||||
try {
|
||||
textures.put(name, new GUITexture(resource));
|
||||
} catch (IOException e) {
|
||||
// Shader program for rendering name plates.
|
||||
namePlateShaderProgram = new ShaderProgram.Builder()
|
||||
.withShader("shader/gui/nameplate_vertex.glsl", GL_VERTEX_SHADER)
|
||||
.withShader("shader/gui/nameplate_fragement.glsl", GL_FRAGMENT_SHADER)
|
||||
.build();
|
||||
namePlateTransformUniform = namePlateShaderProgram.getUniform("transform");
|
||||
namePlateViewTransformUniform = namePlateShaderProgram.getUniform("viewTransform");
|
||||
namePlatePerspectiveTransformUniform = namePlateShaderProgram.getUniform("perspectiveTransform");
|
||||
namePlateTextureSamplerUniform = namePlateShaderProgram.getUniform("guiTexture");
|
||||
namePlateShaderProgram.bindAttribute(0, "vertexPosition");
|
||||
|
||||
try (var in = FileUtils.getClasspathResource("text/JetBrainsMono-Regular.ttf")) {
|
||||
namePlateFont = Font.createFont(Font.TRUETYPE_FONT, in).deriveFont(84f);
|
||||
} catch (FontFormatException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void addTexture(String name, GUITexture texture) {
|
||||
textures.put(name, texture);
|
||||
this.transformMatrix = new Matrix4f();
|
||||
this.transformMatrixData = new float[16];
|
||||
}
|
||||
|
||||
public void start() {
|
||||
|
@ -122,14 +138,86 @@ public class GuiRenderer {
|
|||
glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexCount);
|
||||
}
|
||||
|
||||
private void addNamePlate(OtherPlayer player) {
|
||||
GUITexture texture = new GUITexture(generateNameplateImage(player.getUsername()));
|
||||
playerNamePlates.put(player, texture);
|
||||
}
|
||||
|
||||
private void removeNamePlate(OtherPlayer player) {
|
||||
GUITexture texture = playerNamePlates.remove(player);
|
||||
texture.free();
|
||||
}
|
||||
|
||||
private void retainNamePlates(Collection<OtherPlayer> players) {
|
||||
Set<OtherPlayer> removalSet = new HashSet<>(playerNamePlates.keySet());
|
||||
removalSet.removeAll(players);
|
||||
for (OtherPlayer playerToRemove : removalSet) {
|
||||
removeNamePlate(playerToRemove);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateNamePlates(Collection<OtherPlayer> players) {
|
||||
for (OtherPlayer player : players) {
|
||||
if (!playerNamePlates.containsKey(player)) {
|
||||
addNamePlate(player);
|
||||
}
|
||||
}
|
||||
retainNamePlates(players);
|
||||
}
|
||||
|
||||
private BufferedImage generateNameplateImage(String username) {
|
||||
BufferedImage testImg = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D testGraphics = testImg.createGraphics();
|
||||
testGraphics.setFont(namePlateFont);
|
||||
int textWidth = testGraphics.getFontMetrics(namePlateFont).stringWidth(username);
|
||||
int textHeight = testGraphics.getFontMetrics(namePlateFont).getHeight();
|
||||
|
||||
int w = textWidth + 20;
|
||||
int h = textHeight + 10;
|
||||
BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D g = img.createGraphics();
|
||||
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
||||
g.setBackground(new Color(0, 0, 0, 0.5f));
|
||||
g.clearRect(0, 0, w, h);
|
||||
g.setColor(Color.WHITE);
|
||||
g.setFont(namePlateFont);
|
||||
g.drawString(username, 10, h - 15);
|
||||
return img;
|
||||
}
|
||||
|
||||
public void drawNameplates(ClientPlayer myPlayer, float[] viewTransformData, float[] perspectiveTransformData) {
|
||||
shaderProgram.stopUsing();
|
||||
namePlateShaderProgram.use();
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glActiveTexture(0);
|
||||
glUniform1i(namePlateTextureSamplerUniform, 0);
|
||||
glUniformMatrix4fv(namePlateViewTransformUniform, false, viewTransformData);
|
||||
glUniformMatrix4fv(namePlatePerspectiveTransformUniform, false, perspectiveTransformData);
|
||||
for (var entry : playerNamePlates.entrySet()) {
|
||||
OtherPlayer player = entry.getKey();
|
||||
// Skip rendering far-away nameplates.
|
||||
if (player.getPosition().distance(myPlayer.getPosition()) > 50) continue;
|
||||
GUITexture texture = entry.getValue();
|
||||
float aspectRatio = (float) texture.getHeight() / (float) texture.getWidth();
|
||||
transformMatrix.identity()
|
||||
.translate(player.getPosition().x(), player.getPosition().y() + Player.HEIGHT + 1f, player.getPosition().z())
|
||||
.rotate(myPlayer.getOrientation().x, Camera.UP)
|
||||
.scale(1f, aspectRatio, 0f)
|
||||
.get(transformMatrixData);
|
||||
glUniformMatrix4fv(namePlateTransformUniform, false, transformMatrixData);
|
||||
glBindTexture(GL_TEXTURE_2D, texture.getTextureId());
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexCount);
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
namePlateShaderProgram.stopUsing();
|
||||
shaderProgram.use();
|
||||
}
|
||||
|
||||
public void drawNvg(float width, float height, ClientPlayer player) {
|
||||
nvgBeginFrame(vgId, width, height, width / height);
|
||||
nvgSave(vgId);
|
||||
nvgFontSize(vgId, 60f);
|
||||
nvgFontFaceId(vgId, jetbrainsMonoFont);
|
||||
nvgTextAlign(vgId, NVG_ALIGN_LEFT | NVG_ALIGN_TOP);
|
||||
nvgFillColor(vgId, GuiUtils.rgba(1, 0, 0, 1, colorA));
|
||||
nvgText(vgId, 5, 5, "Hello world!");
|
||||
|
||||
drawCrosshair(width, height);
|
||||
drawHealthBar(width, height, player);
|
||||
|
@ -150,7 +238,9 @@ public class GuiRenderer {
|
|||
public void free() {
|
||||
memFree(jetbrainsMonoFontData);
|
||||
nvgDelete(vgId);
|
||||
for (var tex : textures.values()) tex.free();
|
||||
for (var texture : playerNamePlates.values()) {
|
||||
texture.free();
|
||||
}
|
||||
glDeleteBuffers(vboId);
|
||||
glDeleteVertexArrays(vaoId);
|
||||
shaderProgram.free();
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
#version 460 core
|
||||
|
||||
in vec2 texturePosition;
|
||||
out vec4 fragmentColor;
|
||||
|
||||
uniform sampler2D guiTexture;
|
||||
|
||||
void main() {
|
||||
fragmentColor = texture(guiTexture, texturePosition);
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
#version 460 core
|
||||
|
||||
in vec2 vertexPosition;
|
||||
|
||||
uniform mat4 transform;
|
||||
uniform mat4 viewTransform;
|
||||
uniform mat4 perspectiveTransform;
|
||||
|
||||
out vec2 texturePosition;
|
||||
|
||||
void main() {
|
||||
gl_Position = perspectiveTransform * viewTransform * transform * vec4(vertexPosition, 0.0, 1.0);
|
||||
texturePosition = vec2(
|
||||
(vertexPosition.x + 1.0) / 2.0,
|
||||
1 - (vertexPosition.y + 1.0) / 2.0
|
||||
);
|
||||
}
|
|
@ -47,4 +47,10 @@ public final class FileUtils {
|
|||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
public static InputStream getClasspathResource(String resource) throws IOException {
|
||||
InputStream in = FileUtils.class.getClassLoader().getResourceAsStream(resource);
|
||||
if (in == null) throw new IOException("Resource not found: " + resource);
|
||||
return in;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue