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; this.GLDeleter = GLDeleter;
} }
public int getHandle() {
return referentGLhandle;
}
public void delete() { public void delete() {
GLDeleter.accept(referentGLhandle); GLDeleter.accept(referentGLhandle);
} }

View File

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

View File

@ -4,22 +4,29 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier;
import glm.vec._3.Vec3; 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.backend.Usage;
import ru.windcorp.progressia.client.graphics.model.Face; import ru.windcorp.progressia.client.graphics.model.Face;
import ru.windcorp.progressia.client.graphics.model.Faces; import ru.windcorp.progressia.client.graphics.model.Faces;
import ru.windcorp.progressia.client.graphics.model.Shape; 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.ShapeRenderProgram;
import ru.windcorp.progressia.client.graphics.model.Renderable; import ru.windcorp.progressia.client.graphics.model.Renderable;
import ru.windcorp.progressia.client.graphics.texture.Texture; import ru.windcorp.progressia.client.graphics.texture.Texture;
import ru.windcorp.progressia.common.util.Vectors;
public abstract class SpriteTypeface extends Typeface { public abstract class SpriteTypeface extends Typeface {
private final int height; private final int height;
private final int thickness; private final int thickness;
private final Vec3 shadowOffset; private final Vec3 shadowOffset;
private final TCharObjectMap<Shape> charShapes = new TCharObjectHashMap<>();
public SpriteTypeface(String name, int height, int thinkness) { public SpriteTypeface(String name, int height, int thinkness) {
super(name); super(name);
this.height = height; this.height = height;
@ -37,6 +44,11 @@ public abstract class SpriteTypeface extends Typeface {
return height; return height;
} }
@Override
public int getLineHeight() {
return getHeight();
}
public int getThickness() { public int getThickness() {
return thickness; return thickness;
} }
@ -275,6 +287,11 @@ public abstract class SpriteTypeface extends Typeface {
faces.add(copy); faces.add(copy);
} }
} }
@Override
public Renderable assembleDynamic(Supplier<CharSequence> supplier, int color) {
return new DynamicText(supplier, createVectorFromRGBInt(color));
}
@Override @Override
protected long getSize( protected long getSize(
@ -303,6 +320,21 @@ public abstract class SpriteTypeface extends Typeface {
return pack(resultWidth, height); 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 // TODO remove
private static Vec3 createVectorFromRGBInt(int rgb) { private static Vec3 createVectorFromRGBInt(int rgb) {
@ -312,5 +344,43 @@ public abstract class SpriteTypeface extends Typeface {
return new Vec3(r / 256f, g / 256f, b / 256f); 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; package ru.windcorp.progressia.client.graphics.font;
import java.util.function.Supplier;
import glm.vec._2.i.Vec2i; import glm.vec._2.i.Vec2i;
import ru.windcorp.progressia.client.graphics.model.Renderable; import ru.windcorp.progressia.client.graphics.model.Renderable;
import ru.windcorp.progressia.common.util.CoordinatePacker; import ru.windcorp.progressia.common.util.CoordinatePacker;
@ -51,6 +53,9 @@ public abstract class Typeface extends Named {
float align, int maxWidth, float align, int maxWidth,
int color int color
); );
// TODO implement styling
public abstract Renderable assembleDynamic(Supplier<CharSequence> supplier, int color);
public int getWidth( public int getWidth(
CharSequence chars, int style, CharSequence chars, int style,
@ -66,6 +71,8 @@ public abstract class Typeface extends Named {
return getHeight(getSize(chars, style, align, maxWidth)); return getHeight(getSize(chars, style, align, maxWidth));
} }
public abstract int getLineHeight();
public Vec2i getSize( public Vec2i getSize(
CharSequence chars, int style, CharSequence chars, int style,
float align, int maxWidth, 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 @Override
protected void assembleSelf(RenderTarget target) { protected void assembleSelf(RenderTarget target) {
target.pushTransform( target.pushTransform(
new Mat4().identity().translate(getX(), getY(), -1000) new Mat4().identity().translate(getX(), getY(), -1000) // TODO wtf is this magic <---
.scale(2) .scale(2)
); );

View File

@ -176,10 +176,11 @@ public class LayerWorld extends Layer {
return new StaticModel(b); return new StaticModel(b);
} }
private static final float FRICTION_COEFF = Units.get("1e-5f kg/s");
private void tmp_applyFriction(EntityData entity, float tickLength) { private void tmp_applyFriction(EntityData entity, float tickLength) {
final float frictionCoeff = Units.get(1e-5f, "kg/s"); entity.getVelocity().mul((float) Math.exp(-FRICTION_COEFF / entity.getCollisionMass() * tickLength));
entity.getVelocity().mul((float) Math.exp(-frictionCoeff / entity.getCollisionMass() * tickLength));
} }
private static final float MC_g = Units.get("32 m/s^2"); 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() { public void tmp_generate() {
final int size = 3; final int size = 2;
Vec3i cursor = new Vec3i(0, 0, 0); Vec3i cursor = new Vec3i(0, 0, 0);
for (cursor.x = -(size / 2); cursor.x <= (size / 2); ++cursor.x) { 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(); 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 * Returns the {@link WorldAccessor} object for this server. Use the provided accessor to
* request common {@link Evaluation}s and {@link Change}s. * 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 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"); private final Logger logger = LogManager.getLogger("Ticker Coordinator");
public TickerCoordinator(Server server, int tickers) { public TickerCoordinator(Server server, int tickers) {
@ -97,8 +101,23 @@ public class TickerCoordinator {
} }
public double getTickLength() { public double getTickLength() {
// TODO implement return tickLength;
return Units.SECONDS / 20; }
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() { public void runOneTick() {
try { try {
onTickStart();
int passes = 0; int passes = 0;

View File

@ -19,14 +19,18 @@ package ru.windcorp.progressia.test;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Locale;
import ru.windcorp.progressia.client.ClientState; 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.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.GUILayer;
import ru.windcorp.progressia.client.graphics.gui.Label; import ru.windcorp.progressia.client.graphics.gui.Label;
import ru.windcorp.progressia.client.graphics.gui.Panel; 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.LayoutAlign;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutVertical; import ru.windcorp.progressia.client.graphics.gui.layout.LayoutVertical;
import ru.windcorp.progressia.server.ServerState;
public class LayerTestGUI extends GUILayer { public class LayerTestGUI extends GUILayer {
@ -57,7 +61,23 @@ public class LayerTestGUI extends GUILayer {
() -> String.format("Gravity: %9s (G)", TestPlayerControls.getInstance().useMinecraftGravity() ? "Minecraft" : "Realistic") () -> 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)); TestPlayerControls.getInstance().setUpdateCallback(() -> labels.forEach(Label::update));
getRoot().addChild(panel); getRoot().addChild(panel);

View File

@ -35,13 +35,13 @@ public class TestPlayerControls {
private static final float FLYING_SPEED = 6.0f * Units.METERS_PER_SECOND; private static final float FLYING_SPEED = 6.0f * Units.METERS_PER_SECOND;
// (0; 1], 1 is instant change, 0 is no control authority // (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 // Horizontal and vertical max control speed when walking
private static final float WALKING_SPEED = 4.0f * Units.METERS_PER_SECOND; private static final float WALKING_SPEED = 4.0f * Units.METERS_PER_SECOND;
// (0; 1], 1 is instant change, 0 is no control authority // (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 // Vertical velocity instantly add to player when they jump
private static final float JUMP_VELOCITY = 5f * Units.METERS_PER_SECOND; private static final float JUMP_VELOCITY = 5f * Units.METERS_PER_SECOND;
@ -66,28 +66,36 @@ public class TestPlayerControls {
EntityData player = getEntity(); EntityData player = getEntity();
Mat3 angMat = new Mat3().identity().rotateZ(player.getYaw()); final float speed, authority;
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
if (isFlying) { if (isFlying) {
movement.z = movementUp; speed = FLYING_SPEED;
movement.mul(FLYING_SPEED); authority = FLYING_CONTROL_AUTHORITY;
movement.sub(player.getVelocity());
movement.mul(FLYING_CONTROL_AUTHORITY);
} else { } else {
movement.mul(WALKING_SPEED); speed = WALKING_SPEED;
movement.sub(player.getVelocity()); authority = WALKING_CONTROL_AUTHORITY;
movement.mul(WALKING_CONTROL_AUTHORITY);
movement.z = 0;
} }
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) { public void handleInput(Input input) {
@ -233,11 +241,11 @@ public class TestPlayerControls {
); );
} }
private EntityData getEntity() { public EntityData getEntity() {
return getPlayer().getEntity(); return getPlayer().getEntity();
} }
private LocalPlayer getPlayer() { public LocalPlayer getPlayer() {
return ClientState.getInstance().getLocalPlayer(); return ClientState.getInstance().getLocalPlayer();
} }