Added dynamic text, fixed issues related to low FPS

- Hacked dynamic (quickly changing) text support into Typeface
  - No formatting for now
  - Requires a proper rewrite later
- Added DynamicLabel to display dynamic text
- Movement friction and player controls no longer depend on framerate
- Added FPS and TPS display
This commit is contained in:
OLEGSHA 2020-12-17 00:25:39 +03:00
parent bfdb22f357
commit 7cc4fcbffc
12 changed files with 203 additions and 33 deletions

View File

@ -85,10 +85,6 @@ public class OpenGLObjectTracker {
this.GLDeleter = GLDeleter;
}
public int getHandle() {
return referentGLhandle;
}
public void delete() {
GLDeleter.accept(referentGLhandle);
}

View File

@ -1,5 +1,7 @@
package ru.windcorp.progressia.client.graphics.font;
import java.util.function.Supplier;
import glm.vec._2.i.Vec2i;
import ru.windcorp.progressia.client.graphics.Colors;
import ru.windcorp.progressia.client.graphics.model.Renderable;
@ -49,6 +51,12 @@ public class Font {
return typeface.assemble(chars, style, align, maxWidth, color);
}
public Renderable assembleDynamic(
Supplier<CharSequence> supplier
) {
return typeface.assembleDynamic(supplier, color);
}
public int getWidth(CharSequence chars, int maxWidth) {
return typeface.getWidth(chars, style, align, maxWidth);
}

View File

@ -4,15 +4,20 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Supplier;
import glm.vec._3.Vec3;
import gnu.trove.map.TCharObjectMap;
import gnu.trove.map.hash.TCharObjectHashMap;
import ru.windcorp.progressia.client.graphics.backend.Usage;
import ru.windcorp.progressia.client.graphics.model.Face;
import ru.windcorp.progressia.client.graphics.model.Faces;
import ru.windcorp.progressia.client.graphics.model.Shape;
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
import ru.windcorp.progressia.client.graphics.model.ShapeRenderProgram;
import ru.windcorp.progressia.client.graphics.model.Renderable;
import ru.windcorp.progressia.client.graphics.texture.Texture;
import ru.windcorp.progressia.common.util.Vectors;
public abstract class SpriteTypeface extends Typeface {
@ -20,6 +25,8 @@ public abstract class SpriteTypeface extends Typeface {
private final int thickness;
private final Vec3 shadowOffset;
private final TCharObjectMap<Shape> charShapes = new TCharObjectHashMap<>();
public SpriteTypeface(String name, int height, int thinkness) {
super(name);
this.height = height;
@ -37,6 +44,11 @@ public abstract class SpriteTypeface extends Typeface {
return height;
}
@Override
public int getLineHeight() {
return getHeight();
}
public int getThickness() {
return thickness;
}
@ -276,6 +288,11 @@ public abstract class SpriteTypeface extends Typeface {
}
}
@Override
public Renderable assembleDynamic(Supplier<CharSequence> supplier, int color) {
return new DynamicText(supplier, createVectorFromRGBInt(color));
}
@Override
protected long getSize(
CharSequence chars, int style,
@ -304,6 +321,21 @@ public abstract class SpriteTypeface extends Typeface {
return pack(resultWidth, height);
}
private Shape createCharShape(char c, Vec3 color) {
return new Shape(
Usage.STATIC, getProgram(),
Faces.createRectangle(
getProgram(),
getTexture(c),
color,
Vectors.ZERO_3,
new Vec3(getWidth(c), 0, 0),
new Vec3(0, height, 0),
false
)
);
}
// TODO remove
private static Vec3 createVectorFromRGBInt(int rgb) {
int r = (rgb & 0xFF0000) >> 16;
@ -313,4 +345,42 @@ public abstract class SpriteTypeface extends Typeface {
return new Vec3(r / 256f, g / 256f, b / 256f);
}
private class DynamicText implements Renderable {
private final Supplier<CharSequence> supplier;
private final Vec3 color;
public DynamicText(Supplier<CharSequence> supplier, Vec3 color) {
this.supplier = supplier;
this.color = color;
}
@Override
public void render(ShapeRenderHelper renderer) {
CharSequence text = supplier.get();
int x = 0;
for (int i = 0; i < text.length(); ++i) {
char c = text.charAt(i);
renderer.pushTransform().translate(x, -getInterlineBuffer(), 0);
Shape charShape = getShape(c);
charShape.render(renderer);
renderer.popTransform();
x += getWidth(c);
}
}
private Shape getShape(char c) {
Shape shape = charShapes.get(c);
if (shape == null) {
shape = createCharShape(c, this.color);
charShapes.put(c, shape);
}
return shape;
}
}
}

View File

@ -1,5 +1,7 @@
package ru.windcorp.progressia.client.graphics.font;
import java.util.function.Supplier;
import glm.vec._2.i.Vec2i;
import ru.windcorp.progressia.client.graphics.model.Renderable;
import ru.windcorp.progressia.common.util.CoordinatePacker;
@ -52,6 +54,9 @@ public abstract class Typeface extends Named {
int color
);
// TODO implement styling
public abstract Renderable assembleDynamic(Supplier<CharSequence> supplier, int color);
public int getWidth(
CharSequence chars, int style,
float align, int maxWidth
@ -66,6 +71,8 @@ public abstract class Typeface extends Named {
return getHeight(getSize(chars, style, align, maxWidth));
}
public abstract int getLineHeight();
public Vec2i getSize(
CharSequence chars, int style,
float align, int maxWidth,

View File

@ -0,0 +1,36 @@
package ru.windcorp.progressia.client.graphics.gui;
import glm.mat._4.Mat4;
import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
import ru.windcorp.progressia.client.graphics.font.Font;
import java.util.function.Supplier;
public class DynamicLabel extends Component {
private Font font;
private Supplier<CharSequence> contents;
public DynamicLabel(String name, Font font, Supplier<CharSequence> contents, int width) {
super(name);
this.font = font;
this.contents = contents;
setPreferredSize(width, font.getHeight("", Integer.MAX_VALUE) * 2);
}
public Font getFont() {
return font;
}
public Supplier<CharSequence> getContentSupplier() {
return contents;
}
@Override
protected void assembleSelf(RenderTarget target) {
target.pushTransform(new Mat4().identity().translate(getX(), getY(), -1000).scale(2));
target.addCustomRenderer(font.assembleDynamic(getContentSupplier()));
target.popTransform();
}
}

View File

@ -51,7 +51,7 @@ public class Label extends Component {
@Override
protected void assembleSelf(RenderTarget target) {
target.pushTransform(
new Mat4().identity().translate(getX(), getY(), -1000)
new Mat4().identity().translate(getX(), getY(), -1000) // TODO wtf is this magic <---
.scale(2)
);

View File

@ -177,9 +177,10 @@ public class LayerWorld extends Layer {
return new StaticModel(b);
}
private static final float FRICTION_COEFF = Units.get("1e-5f kg/s");
private void tmp_applyFriction(EntityData entity, float tickLength) {
final float frictionCoeff = Units.get(1e-5f, "kg/s");
entity.getVelocity().mul((float) Math.exp(-frictionCoeff / entity.getCollisionMass() * tickLength));
entity.getVelocity().mul((float) Math.exp(-FRICTION_COEFF / entity.getCollisionMass() * tickLength));
}
private static final float MC_g = Units.get("32 m/s^2");

View File

@ -55,7 +55,7 @@ public class WorldData {
}
public void tmp_generate() {
final int size = 3;
final int size = 2;
Vec3i cursor = new Vec3i(0, 0, 0);
for (cursor.x = -(size / 2); cursor.x <= (size / 2); ++cursor.x) {

View File

@ -126,6 +126,10 @@ public class Server {
return this.serverThread.getTicker().getTickLength();
}
public double getTPS() {
return this.serverThread.getTicker().getTPS();
}
/**
* Returns the {@link WorldAccessor} object for this server. Use the provided accessor to
* request common {@link Evaluation}s and {@link Change}s.

View File

@ -49,6 +49,10 @@ public class TickerCoordinator {
private final AtomicInteger workingTickers = new AtomicInteger();
private boolean isTickStartSet = false;
private long tickStart = -1;
private double tickLength = 1.0 / 20; // Do something about it
private final Logger logger = LogManager.getLogger("Ticker Coordinator");
public TickerCoordinator(Server server, int tickers) {
@ -97,8 +101,23 @@ public class TickerCoordinator {
}
public double getTickLength() {
// TODO implement
return Units.SECONDS / 20;
return tickLength;
}
public double getTPS() {
return 1 / tickLength;
}
private void onTickStart() {
long now = System.currentTimeMillis();
if (isTickStartSet) {
tickLength = (now - tickStart) * Units.MILLISECONDS;
} else {
isTickStartSet = true;
}
tickStart = System.currentTimeMillis();
}
/*
@ -107,6 +126,7 @@ public class TickerCoordinator {
public void runOneTick() {
try {
onTickStart();
int passes = 0;

View File

@ -19,14 +19,18 @@ package ru.windcorp.progressia.test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Locale;
import ru.windcorp.progressia.client.ClientState;
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
import ru.windcorp.progressia.client.graphics.font.Font;
import ru.windcorp.progressia.client.graphics.gui.DynamicLabel;
import ru.windcorp.progressia.client.graphics.gui.GUILayer;
import ru.windcorp.progressia.client.graphics.gui.Label;
import ru.windcorp.progressia.client.graphics.gui.Panel;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutVertical;
import ru.windcorp.progressia.server.ServerState;
public class LayerTestGUI extends GUILayer {
@ -57,7 +61,23 @@ public class LayerTestGUI extends GUILayer {
() -> String.format("Gravity: %9s (G)", TestPlayerControls.getInstance().useMinecraftGravity() ? "Minecraft" : "Realistic")
));
panel.getChildren().forEach(c -> labels.add((Label) c));
panel.addChild(new DynamicLabel(
"FPSDisplay", new Font().withColor(0x37A3E6).deriveShadow(),
() -> String.format(Locale.US, "FPS: %5.1f", GraphicsInterface.getFPS()),
128
));
panel.addChild(new DynamicLabel(
"TPSDisplay", new Font().withColor(0x37A3E6).deriveShadow(),
() -> ServerState.getInstance() == null ? "TPS: n/a" : String.format(Locale.US, "TPS: %5.1f", ServerState.getInstance().getTPS()),
128
));
panel.getChildren().forEach(c -> {
if (c instanceof Label) {
labels.add((Label) c);
}
});
TestPlayerControls.getInstance().setUpdateCallback(() -> labels.forEach(Label::update));
getRoot().addChild(panel);

View File

@ -35,13 +35,13 @@ public class TestPlayerControls {
private static final float FLYING_SPEED = 6.0f * Units.METERS_PER_SECOND;
// (0; 1], 1 is instant change, 0 is no control authority
private static final float FLYING_CONTROL_AUTHORITY = 0.05f;
private static final float FLYING_CONTROL_AUTHORITY = Units.get("2 1/s");
// Horizontal and vertical max control speed when walking
private static final float WALKING_SPEED = 4.0f * Units.METERS_PER_SECOND;
// (0; 1], 1 is instant change, 0 is no control authority
private static final float WALKING_CONTROL_AUTHORITY = 0.1f;
private static final float WALKING_CONTROL_AUTHORITY = Units.get("15 1/s");
// Vertical velocity instantly add to player when they jump
private static final float JUMP_VELOCITY = 5f * Units.METERS_PER_SECOND;
@ -66,28 +66,36 @@ public class TestPlayerControls {
EntityData player = getEntity();
Mat3 angMat = new Mat3().identity().rotateZ(player.getYaw());
Vec3 movement = new Vec3(movementForward, -movementRight, 0);
if (movementForward != 0 && movementRight != 0) {
movement.normalize();
}
angMat.mul_(movement); // bug in jglm, .mul() and mul_() are swapped
final float speed, authority;
if (isFlying) {
movement.z = movementUp;
movement.mul(FLYING_SPEED);
movement.sub(player.getVelocity());
movement.mul(FLYING_CONTROL_AUTHORITY);
speed = FLYING_SPEED;
authority = FLYING_CONTROL_AUTHORITY;
} else {
movement.mul(WALKING_SPEED);
movement.sub(player.getVelocity());
movement.mul(WALKING_CONTROL_AUTHORITY);
movement.z = 0;
speed = WALKING_SPEED;
authority = WALKING_CONTROL_AUTHORITY;
}
player.getVelocity().add(movement);
Mat3 angMat = new Mat3().identity().rotateZ(player.getYaw());
Vec3 desiredVelocity = new Vec3(movementForward, -movementRight, 0);
if (movementForward != 0 && movementRight != 0) desiredVelocity.normalize();
angMat.mul_(desiredVelocity); // bug in jglm, .mul() and mul_() are swapped
desiredVelocity.z = movementUp;
desiredVelocity.mul(speed);
Vec3 change = new Vec3()
.set(desiredVelocity)
.sub(player.getVelocity())
.mul((float) Math.exp(-authority * GraphicsInterface.getFrameLength()))
.negate()
.add(desiredVelocity);
if (!isFlying) {
change.z = player.getVelocity().z;
}
player.getVelocity().set(change);
}
public void handleInput(Input input) {
@ -233,11 +241,11 @@ public class TestPlayerControls {
);
}
private EntityData getEntity() {
public EntityData getEntity() {
return getPlayer().getEntity();
}
private LocalPlayer getPlayer() {
public LocalPlayer getPlayer() {
return ClientState.getInstance().getLocalPlayer();
}