SDF font rendering with proper kerning

This commit is contained in:
Natty 2022-04-14 20:31:47 +02:00
parent 6772912f66
commit 12ad72636b
No known key found for this signature in database
GPG Key ID: 40AB22FA416C7019
24 changed files with 706 additions and 281 deletions

View File

@ -24,7 +24,6 @@ version numbers.*
### Safe submodules
* **PlutoCore** - Stable
* **PlutoFramebuffer** - Stable
* **PlutoGUI** - Stable, awaiting a rewrite
* **PlutoMesher** - Stable
* **PlutoShader** - Stable
* **PlutoTexture** - Stable
@ -34,6 +33,7 @@ version numbers.*
* **PlutoLib** - Mostly stable
### Unstable submodules
* **PlutoGUI** - Recently rewritten, the API is highly unstable
* **PlutoRuntime** - Somewhat tentative, the module API has been rewritten and might contain bugs
* **PlutoAudio** - Somewhat usable, unfinished
@ -42,17 +42,25 @@ version numbers.*
See `NEXT_RELEASE_DRAFT.md` for details.
### To be fixed
[ *Features or bugs that should be fixed **ASAP*** ]
* Implement gradient variation support for Libra fills
* Finish gradient rendering in FragmentFontShader.glsl
* Improve code quality in PlutoGUI
### Very high priority
[ *Implemented in the current release.* ]
* Streamline PlutoLib, remove bad APIs and improve code quality
* Improve image loading capabilities, possibly rewrite PlutoLib#TPL
* Improve image loading capabilities, possibly rewrite PlutoLib#TPL
* The stage system and automated asset loading
* Rewrite PlutoGUI
### High priority
[ *Implemented in the next release.* ]
* Finish PlutoAudio
* Depends on the stage system
* Expand upon the Color API
* Color mixing and blending
* Color transformation
* High-performance serialization
### Normal priority
[ *Planned for an upcoming release.* ]
@ -68,7 +76,3 @@ See `NEXT_RELEASE_DRAFT.md` for details.
* This feature requires a full rewrite and possibly a complete overhaul
* Mods should have limited execution levels, for example restricted file access
or disabled native library loading (this is probably not possible)
* Expand upon the Color API
* Color mixing and blending
* Color transformation
* High-performance serialization

View File

@ -12,6 +12,7 @@
* `[PlutoRuntime]` Fixed opening .zip filesystems
* `[PlutoShader]` Added `UniformArrayFloat`, `UniformArrayRGBA`,
`UniformArrayVec2`, `UniformArrayVec3`, `UniformArrayVec4`
* `[PlutoRuntime]` `SmartSeverity.MODULE_CHECK` now correctly uses the standard output instead of `stderr`.
## 22.0.0.0-alpha.7
* `[PlutoRuntime]` Fixed several resource filesystem bugs

View File

@ -0,0 +1,115 @@
package org.plutoengine.graphics;
import org.joml.Matrix3f;
import org.joml.primitives.Rectanglef;
import org.plutoengine.graphics.gui.PlutoGUICommandParser;
import org.plutoengine.graphics.gui.stbttf.STBTTBasicTextShaper;
import org.plutoengine.graphics.gui.stbttf.STBTTFont;
import org.plutoengine.libra.command.LiCommandBuffer;
import org.plutoengine.libra.command.impl.LiCommandSetTransform;
import org.plutoengine.libra.text.LiTextInfo;
import org.plutoengine.libra.text.font.LiFontFamily;
import org.plutoengine.libra.text.shaping.TextShaper;
import org.plutoengine.libra.text.shaping.TextStyleOptions;
import java.util.EnumSet;
public class ImmediateFontRenderer
{
public static void drawString(float x, float y, String text, LiFontFamily<STBTTFont> font, TextStyleOptions style)
{
var shaper = new STBTTBasicTextShaper();
var info = shaper.shape(EnumSet.of(TextShaper.EnumFeature.KERNING), font, text, style);
draw(x, y, info, style);
}
public static void drawStringNoKern(float x, float y, String text, LiFontFamily<STBTTFont> font, TextStyleOptions style)
{
var shaper = new STBTTBasicTextShaper();
var info = shaper.shape(EnumSet.noneOf(TextShaper.EnumFeature.class), font, text, style);
draw(x, y, info, style);
}
public static void draw(float x, float y, LiTextInfo info, TextStyleOptions style)
{
var transformBuf = LiCommandBuffer.uncleared();
var fitBox = style.getFitBox();
var initialScale = style.getSize() / STBTTFont.PIXEL_HEIGHT;
var scaleX = initialScale;
var scaleY = initialScale;
var bounds = info.getBoundingBox().scale(scaleX, scaleY, new Rectanglef());
if (fitBox != null)
{
// Rescale in sync in both are set to scale
if (style.getOverflowX() == TextStyleOptions.OverflowXStrategy.SCALE_TO_FIT &&
style.getOverflowY() == TextStyleOptions.OverflowYStrategy.SCALE_TO_FIT)
{
var smaller = Math.min(fitBox.lengthX() / bounds.lengthX(), fitBox.lengthY() / bounds.lengthY());
var rescale = Math.min(1.0f, smaller);
scaleX *= rescale;
bounds.scale(rescale, rescale);
}
else
{
if (style.getOverflowX() == TextStyleOptions.OverflowXStrategy.SCALE_TO_FIT)
{
var rescale = Math.min(1.0f, fitBox.lengthX() / bounds.lengthX());
scaleX *= rescale;
bounds.scale(rescale, 1.0f);
}
if (style.getOverflowY() == TextStyleOptions.OverflowYStrategy.SCALE_TO_FIT)
{
var rescale = Math.min(1.0f, fitBox.lengthY() / bounds.lengthY());
scaleY *= rescale;
bounds.scale(1.0f, rescale);
}
}
x += switch (style.getHorizontalAlign()) {
case START -> fitBox.minX - bounds.minX;
case CENTER -> (fitBox.maxX + fitBox.minX) / 2.0f - bounds.lengthX() / 2.0f;
case END -> fitBox.maxX - bounds.lengthX();
};
y += switch (style.getVerticalAlign()) {
case START -> fitBox.minY - bounds.minY;
case CENTER -> (fitBox.maxY + fitBox.minY) / 2.0f - bounds.lengthY() / 2.0f;
case END -> fitBox.maxY - bounds.lengthY();
};
}
else
{
x += switch (style.getHorizontalAlign()) {
case START -> -bounds.minX;
case CENTER -> -bounds.minX + -bounds.lengthX() / 2.0f;
case END -> -bounds.lengthX();
};
y += switch (style.getVerticalAlign()) {
case START -> -bounds.lengthY();
case CENTER -> -bounds.lengthY() / 2.0f;
case END -> 0;
};
}
transformBuf.push(new LiCommandSetTransform(new Matrix3f().scale(scaleX, scaleY, 1.0f).m20(x).m21(y)));
var buf = info.getDrawCommandBuffer();
var commandParser = new PlutoGUICommandParser();
commandParser.add(LiCommandBuffer.cleared());
commandParser.add(transformBuf);
commandParser.add(buf);
try (var drawCalls = commandParser.parse())
{
drawCalls.render();
}
}
}

View File

@ -1,6 +1,5 @@
package org.plutoengine.graphics;
import org.joml.Matrix3f;
import org.plutoengine.Pluto;
import org.plutoengine.graphics.gui.FontShader;
import org.plutoengine.graphics.texture.MagFilter;
@ -34,10 +33,6 @@ public class PlutoGUIMod implements IModEntryPoint
instance = mod;
fontShader = new RenderShaderBuilder(mod.getResource("shaders.VertexFontShader#glsl"), mod.getResource("shaders.FragmentFontShader#glsl")).build(FontShader.class, false);
fontShader.start();
fontShader.recolor.load(1, 1, 1, 1);
fontShader.transformationMatrix.load(new Matrix3f());
uiElementsAtlas = new RectangleTexture();
uiElementsAtlas.load(mod.getResource("gui.elements#png"), MagFilter.NEAREST, MinFilter.NEAREST, WrapMode.CLAMP_TO_EDGE, WrapMode.CLAMP_TO_EDGE);

View File

@ -1,33 +0,0 @@
package org.plutoengine.graphics;
import org.joml.Matrix3f;
import org.plutoengine.graphics.gui.PlutoGUICommandParser;
import org.plutoengine.graphics.gui.STBTTBasicTextShaper;
import org.plutoengine.graphics.gui.STBTTFont;
import org.plutoengine.libra.command.LiCommandBuffer;
import org.plutoengine.libra.command.impl.LiCommandSetTransform;
import org.plutoengine.libra.text.shaping.TextShaper;
import java.util.EnumSet;
public class TestFontRenderer
{
public static void drawString(STBTTFont font, String text)
{
var shaper = new STBTTBasicTextShaper();
var info = shaper.shape(EnumSet.of(TextShaper.EnumFeature.KERNING), font, text);
var transformBuf = new LiCommandBuffer();
transformBuf.push(new LiCommandSetTransform(new Matrix3f().scale(.75f).m20(400f).m21(400f)));
var buf = info.getDrawCommandBuffer();
var commandParser = new PlutoGUICommandParser();
commandParser.add(transformBuf);
commandParser.add(buf);
var drawCalls = commandParser.parse();
drawCalls.render();
drawCalls.close();
}
}

View File

@ -2,14 +2,18 @@ package org.plutoengine.graphics.gui;
import org.joml.Matrix3fc;
import org.plutoengine.graphics.gl.vao.attrib.ReservedAttributes;
import org.plutoengine.libra.paint.LiColorPaint;
import org.plutoengine.libra.paint.LiGradientPaint;
import org.plutoengine.libra.paint.LiPaint;
import org.plutoengine.shader.ShaderBase;
import org.plutoengine.shader.ShaderProgram;
import org.plutoengine.shader.VertexArrayAttribute;
import org.plutoengine.shader.uniform.*;
import org.plutoengine.shader.uniform.auto.AutoViewportProjection;
import org.plutoengine.util.color.IRGBA;
@ShaderProgram
public final class FontShader extends ShaderBase implements IGUIShader
public final class FontShader extends ShaderBase implements ISDFTextShader
{
@AutoViewportProjection
@Uniform(name = "projection")
@ -19,10 +23,25 @@ public final class FontShader extends ShaderBase implements IGUIShader
public UniformMat3 transformationMatrix;
@Uniform
public UniformRGBA recolor;
public UniformInt paintType;
@Uniform
public UniformBoolean italic;
public UniformRGBA paintColor;
@Uniform
public UniformInt paintGradientStopCount;
@Uniform
public UniformArrayRGBA paintGradientColors;
@Uniform
public UniformArrayFloat paintGradientPositions;
@Uniform
public UniformArrayVec2 paintGradientEnds;
@Uniform
public UniformFloat pxScale;
@VertexArrayAttribute(ReservedAttributes.POSITION)
public int position;
@ -33,9 +52,54 @@ public final class FontShader extends ShaderBase implements IGUIShader
@VertexArrayAttribute(2)
public int page;
@VertexArrayAttribute(3)
public int paintUVCoords;
@Override
public void setTransform(Matrix3fc transform)
{
this.transformationMatrix.load(transform);
}
@Override
public void setPaint(LiPaint paint)
{
switch (paint.getType())
{
case SOLID_COLOR -> {
var col = ((LiColorPaint) paint).getColor();
this.paintType.load(0);
this.paintColor.load(col.getFloatComponentsRGBA());
}
case GRADIENT -> {
var gradPaint = (LiGradientPaint) paint;
this.paintType.load(1);
this.paintGradientEnds.load(gradPaint.getStart(), gradPaint.getEnd());
var stops = gradPaint.getStops();
this.paintGradientStopCount.load(stops.length);
var colors = new IRGBA[stops.length];
var positions = new float[stops.length];
int i = 0;
for (var stop : stops)
{
var col = stop.color();
colors[i] = col.getFloatComponentsRGBA();
positions[i] = stop.position();
i++;
}
this.paintGradientColors.load(colors);
this.paintGradientPositions.load(positions);
}
}
}
@Override
public void setPixelScale(float scale)
{
this.pxScale.load(scale);
}
}

View File

@ -1,8 +1,11 @@
package org.plutoengine.graphics.gui;
import org.joml.Matrix3fc;
import org.plutoengine.libra.paint.LiPaint;
public interface IGUIShader
{
void setTransform(Matrix3fc transform);
void setPaint(LiPaint paint);
}

View File

@ -0,0 +1,6 @@
package org.plutoengine.graphics.gui;
public interface ISDFTextShader extends IGUIShader
{
void setPixelScale(float scale);
}

View File

@ -4,12 +4,15 @@ import org.plutoengine.graphics.gl.DrawMode;
import org.plutoengine.graphics.gl.vao.VertexArray;
import org.plutoengine.graphics.gl.vao.VertexArrayBuilder;
import org.plutoengine.graphics.gui.command.PlutoCommandDrawMesh;
import org.plutoengine.graphics.gui.command.PlutoCommandDrawMeshDirectBuffer;
import org.plutoengine.graphics.gui.command.PlutoCommandSwitchShader;
import org.plutoengine.graphics.gui.command.PlutoCommandSwitchTexture;
import org.plutoengine.libra.command.AbstractGUICommandParser;
import org.plutoengine.libra.command.IGUIRenderer;
import org.plutoengine.libra.command.LiCommandBuffer;
import org.plutoengine.libra.command.impl.LiCommandSetPaint;
import org.plutoengine.libra.command.impl.LiCommandSetTransform;
import org.plutoengine.libra.command.impl.LiCommandSpecial;
import org.plutoengine.shader.ShaderBase;
import java.util.ArrayDeque;
@ -52,6 +55,9 @@ public class PlutoGUICommandParser extends AbstractGUICommandParser
attribsToEnable.removeAll(alreadyEnabledAttribs);
alreadyEnabledAttribs.addAll(attribsToEnable);
if (drawCmd instanceof PlutoCommandDrawMeshDirectBuffer dBuf)
dBuf.close();
drawCalls.add(() -> {
vao.bind();
attribsToEnable.forEach(VertexArray::enableAttribute);
@ -72,6 +78,17 @@ public class PlutoGUICommandParser extends AbstractGUICommandParser
drawCalls.add(() -> shaderCapture.setTransform(transformCmd.getTransform()));
}
case SET_PAINT -> {
if (!(cmd instanceof LiCommandSetPaint paintCmd))
throw new IllegalStateException();
var shaderCapture = currentShader;
assert shaderCapture != null;
drawCalls.add(() -> shaderCapture.setPaint(paintCmd.getPaint()));
}
case SWITCH_SHADER -> {
if (!(cmd instanceof PlutoCommandSwitchShader swSh))
throw new IllegalStateException();
@ -93,6 +110,14 @@ public class PlutoGUICommandParser extends AbstractGUICommandParser
drawCalls.add(textureCapture::bind);
}
case SPECIAL -> {
if (!(cmd instanceof LiCommandSpecial cSp))
throw new IllegalStateException();
var af = cSp.getAction();
drawCalls.add(() -> af.accept(mergedBuffer));
}
}
}

View File

@ -1,12 +0,0 @@
package org.plutoengine.graphics.gui;
import org.plutoengine.libra.command.LiCommandBuffer;
import org.plutoengine.libra.text.LiTextInfo;
public class PlutoTextInfo extends LiTextInfo
{
protected PlutoTextInfo(LiCommandBuffer commandBuffer)
{
super(commandBuffer);
}
}

View File

@ -1,20 +1,21 @@
package org.plutoengine.graphics.gui.command;
import org.plutoengine.graphics.gl.vao.attrib.AttributeInfo;
import org.plutoengine.graphics.gl.vbo.EnumArrayBufferType;
import org.plutoengine.libra.command.impl.LiCommand;
import org.plutoengine.libra.command.impl.LiCommandDrawMesh;
import java.nio.Buffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.*;
import java.util.Collections;
import java.util.Map;
import java.util.TreeMap;
public class PlutoCommandDrawMesh extends LiCommandDrawMesh
public abstract sealed class PlutoCommandDrawMesh extends LiCommandDrawMesh permits PlutoCommandDrawMeshDirectBuffer, PlutoCommandDrawMeshHeap
{
private final Map<Integer, AttributeInfo> attributeInfo;
private final Map<Integer, Buffer> data;
private IntBuffer indices;
protected final Map<Integer, AttributeInfo> attributeInfo;
protected final Map<Integer, Buffer> data;
protected IntBuffer indices;
public PlutoCommandDrawMesh()
{
@ -48,19 +49,7 @@ public class PlutoCommandDrawMesh extends LiCommandDrawMesh
this.addIndices(IntBuffer.wrap(data));
}
public void addIndices(IntBuffer data)
{
if (data == null)
return;
if (this.indices == null)
this.indices = IntBuffer.allocate(data.remaining());
if (this.indices.remaining() < data.remaining())
this.indices = IntBuffer.allocate(Math.max(this.indices.capacity() << 1, this.indices.capacity() + data.remaining())).put(this.indices.flip());
this.indices.put(data);
}
public abstract void addIndices(IntBuffer data);
public void addAttribute(int attrib, float[] data, int dimensions)
{
@ -70,30 +59,7 @@ public class PlutoCommandDrawMesh extends LiCommandDrawMesh
this.addAttribute(attrib, FloatBuffer.wrap(data), dimensions);
}
public void addAttribute(int attrib, FloatBuffer data, int dimensions)
{
if (data == null)
return;
var meta = this.attributeInfo.computeIfAbsent(attrib, k -> new AttributeInfo(EnumArrayBufferType.FLOAT, attrib, dimensions));
if (meta.dimensions() != dimensions)
throw new IllegalArgumentException("Attribute dimensions mismatch!");
this.data.compute(attrib, (k, v) -> {
if (v == null)
return FloatBuffer.allocate(data.remaining()).put(data);
if (!(v instanceof FloatBuffer fab))
throw new IllegalArgumentException();
if (data.remaining() <= fab.remaining())
return fab.put(data);
var newBuf = FloatBuffer.allocate(Math.max(v.capacity() << 1, v.capacity() + data.remaining()));
return newBuf.put(fab.flip()).put(data);
});
}
public abstract void addAttribute(int attrib, FloatBuffer data, int dimensions);
public void addAttribute(int attrib, int[] data, int dimensions)
{
@ -103,30 +69,7 @@ public class PlutoCommandDrawMesh extends LiCommandDrawMesh
this.addAttribute(attrib, IntBuffer.wrap(data), dimensions);
}
public void addAttribute(int attrib, IntBuffer data, int dimensions)
{
if (data == null)
return;
var meta = this.attributeInfo.computeIfAbsent(attrib, k -> new AttributeInfo(EnumArrayBufferType.INT, attrib, dimensions));
if (meta.dimensions() != dimensions)
throw new IllegalArgumentException("Attribute dimensions mismatch!");
this.data.compute(attrib, (k, v) -> {
if (v == null)
return IntBuffer.allocate(data.remaining()).put(data);
if (!(v instanceof IntBuffer fab))
throw new IllegalArgumentException();
if (data.remaining() <= fab.remaining())
return fab.put(data);
var newBuf = IntBuffer.allocate(Math.max(v.capacity() << 1, v.capacity() + data.remaining()));
return newBuf.put(fab.flip()).put(data);
});
}
public abstract void addAttribute(int attrib, IntBuffer data, int dimensions);
@Override
public boolean supportsMerge(LiCommand other)
@ -154,7 +97,7 @@ public class PlutoCommandDrawMesh extends LiCommandDrawMesh
}
});
this.addIndices(pcdm.indices);
this.addIndices(pcdm.getIndices());
return this;
}

View File

@ -0,0 +1,142 @@
package org.plutoengine.graphics.gui.command;
import org.lwjgl.system.MemoryUtil;
import org.plutoengine.graphics.gl.vao.attrib.AttributeInfo;
import org.plutoengine.graphics.gl.vbo.EnumArrayBufferType;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
public final class PlutoCommandDrawMeshDirectBuffer extends PlutoCommandDrawMesh implements AutoCloseable
{
public void addIndices(IntBuffer data)
{
if (data == null)
return;
if (this.indices == null)
{
if (data.isDirect())
{
this.indices = data;
var size = this.indices.remaining();
this.indices.limit(size);
this.indices.position(size);
return;
}
else
{
this.indices = MemoryUtil.memAllocInt(data.remaining());
}
}
if (this.indices.remaining() < data.remaining())
{
var newSize = Math.max(this.indices.capacity() << 1, this.indices.capacity() + data.remaining());
this.indices = MemoryUtil.memRealloc(this.indices, newSize);
}
this.indices.put(data);
if (data.isDirect())
MemoryUtil.memFree(data);
}
public void addAttribute(int attrib, FloatBuffer data, int dimensions)
{
if (data == null)
return;
var meta = this.attributeInfo.computeIfAbsent(attrib, k -> new AttributeInfo(EnumArrayBufferType.FLOAT, attrib, dimensions));
if (meta.dimensions() != dimensions)
throw new IllegalArgumentException("Attribute dimensions mismatch!");
this.data.compute(attrib, (k, v) -> {
if (v == null)
{
if (data.isDirect())
{
var size = data.remaining();
data.limit(size);
data.position(size);
return data;
}
else
{
return MemoryUtil.memAllocFloat(data.remaining()).put(data);
}
}
if (!(v instanceof FloatBuffer fab))
throw new IllegalArgumentException();
if (data.remaining() > fab.remaining())
{
var newSize = Math.max(fab.capacity() << 1, fab.capacity() + data.remaining());
fab = MemoryUtil.memRealloc(fab, newSize);
}
fab.put(data);
if (data.isDirect())
MemoryUtil.memFree(data);
return fab;
});
}
public void addAttribute(int attrib, IntBuffer data, int dimensions)
{
if (data == null)
return;
var meta = this.attributeInfo.computeIfAbsent(attrib, k -> new AttributeInfo(EnumArrayBufferType.INT, attrib, dimensions));
if (meta.dimensions() != dimensions)
throw new IllegalArgumentException("Attribute dimensions mismatch!");
this.data.compute(attrib, (k, v) -> {
if (v == null)
{
if (data.isDirect())
{
var size = data.remaining();
data.limit(size);
data.position(size);
return data;
}
else
{
return MemoryUtil.memAllocInt(data.remaining()).put(data);
}
}
if (!(v instanceof IntBuffer iab))
throw new IllegalArgumentException();
if (data.remaining() > iab.remaining())
{
var newSize = Math.max(iab.capacity() << 1, iab.capacity() + data.remaining());
iab = MemoryUtil.memRealloc(iab, newSize);
}
iab.put(data);
if (data.isDirect())
MemoryUtil.memFree(data);
return iab;
});
}
@Override
public void close()
{
MemoryUtil.memFree(this.indices);
this.data.values()
.forEach(MemoryUtil::memFree);
}
}

View File

@ -0,0 +1,97 @@
package org.plutoengine.graphics.gui.command;
import org.plutoengine.graphics.gl.vao.attrib.AttributeInfo;
import org.plutoengine.graphics.gl.vbo.EnumArrayBufferType;
import org.plutoengine.libra.command.impl.LiCommand;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
public final class PlutoCommandDrawMeshHeap extends PlutoCommandDrawMesh
{
public void addIndices(IntBuffer data)
{
if (data == null)
return;
if (this.indices == null)
this.indices = IntBuffer.allocate(data.remaining());
if (this.indices.remaining() < data.remaining())
this.indices = IntBuffer.allocate(Math.max(this.indices.capacity() << 1, this.indices.capacity() + data.remaining())).put(this.indices.flip());
this.indices.put(data);
}
public void addAttribute(int attrib, FloatBuffer data, int dimensions)
{
if (data == null)
return;
var meta = this.attributeInfo.computeIfAbsent(attrib, k -> new AttributeInfo(EnumArrayBufferType.FLOAT, attrib, dimensions));
if (meta.dimensions() != dimensions)
throw new IllegalArgumentException("Attribute dimensions mismatch!");
this.data.compute(attrib, (k, v) -> {
if (v == null)
return FloatBuffer.allocate(data.remaining()).put(data);
if (!(v instanceof FloatBuffer fab))
throw new IllegalArgumentException();
if (data.remaining() <= fab.remaining())
return fab.put(data);
var newBuf = FloatBuffer.allocate(Math.max(v.capacity() << 1, v.capacity() + data.remaining()));
return newBuf.put(fab.flip()).put(data);
});
}
public void addAttribute(int attrib, IntBuffer data, int dimensions)
{
if (data == null)
return;
var meta = this.attributeInfo.computeIfAbsent(attrib, k -> new AttributeInfo(EnumArrayBufferType.INT, attrib, dimensions));
if (meta.dimensions() != dimensions)
throw new IllegalArgumentException("Attribute dimensions mismatch!");
this.data.compute(attrib, (k, v) -> {
if (v == null)
return IntBuffer.allocate(data.remaining()).put(data);
if (!(v instanceof IntBuffer iab))
throw new IllegalArgumentException();
if (data.remaining() <= iab.remaining())
return iab.put(data);
var newBuf = IntBuffer.allocate(Math.max(v.capacity() << 1, v.capacity() + data.remaining()));
return newBuf.put(iab.flip()).put(data);
});
}
@Override
public PlutoCommandDrawMeshHeap merge(LiCommand other)
{
if (!(other instanceof PlutoCommandDrawMesh pcdm))
throw new UnsupportedOperationException();
pcdm.data.forEach((k, v) -> {
var attrInfo = this.attributeInfo.get(k);
switch (attrInfo.type())
{
case FLOAT -> this.addAttribute(k, (FloatBuffer) v, attrInfo.dimensions());
case INT -> this.addAttribute(k, (IntBuffer) v, attrInfo.dimensions());
case UNSIGNED_INT -> throw new UnsupportedOperationException();
}
});
this.addIndices(pcdm.indices);
return this;
}
}

View File

@ -1,26 +1,42 @@
package org.plutoengine.graphics.gui;
package org.plutoengine.graphics.gui.stbttf;
import org.joml.primitives.Rectanglef;
import org.lwjgl.system.MemoryUtil;
import org.plutoengine.graphics.PlutoGUIMod;
import org.plutoengine.graphics.gui.command.PlutoCommandDrawMesh;
import org.plutoengine.graphics.gui.command.PlutoCommandDrawMeshDirectBuffer;
import org.plutoengine.graphics.gui.command.PlutoCommandSwitchShader;
import org.plutoengine.graphics.gui.command.PlutoCommandSwitchTexture;
import org.plutoengine.libra.command.LiCommandBuffer;
import org.plutoengine.libra.command.impl.LiCommandSetPaint;
import org.plutoengine.libra.command.impl.LiCommandSpecial;
import org.plutoengine.libra.text.LiTextInfo;
import org.plutoengine.libra.text.font.GlyphInfo;
import org.plutoengine.libra.text.font.LiFontFamily;
import org.plutoengine.libra.text.shaping.IShapingStrategy;
import org.plutoengine.libra.text.shaping.TextShaper;
import org.plutoengine.libra.text.shaping.TextStyleOptions;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.Objects;
public class STBTTBasicTextShaper implements IShapingStrategy<STBTTFont.STBTTGlyphMetrics, STBTTFont.STBTTGlyphAtlas, STBTTFont>
{
private LiCommandBuffer commandBuffer;
public STBTTBasicTextShaper setCommandBuffer(LiCommandBuffer commandBuffer)
{
this.commandBuffer = commandBuffer;
return this;
}
@Override
public LiTextInfo shape(EnumSet<TextShaper.EnumFeature> features, STBTTFont font, String text)
public LiTextInfo shape(EnumSet<TextShaper.EnumFeature> features, LiFontFamily<STBTTFont> fontFamily, String text, TextStyleOptions style)
{
var commandBuf = new LiCommandBuffer();
var commandBuf = Objects.requireNonNullElseGet(this.commandBuffer, LiCommandBuffer::uncleared);
var font = style.pickFont(fontFamily);
var atlas = font.getGlyphAtlas();
var atlasTexture = atlas.getGlyphAtlasTexture();
@ -31,9 +47,11 @@ public class STBTTBasicTextShaper implements IShapingStrategy<STBTTFont.STBTTGly
var shaderSwitch = new PlutoCommandSwitchShader(shader);
commandBuf.push(shaderSwitch);
commandBuf.push(new LiCommandSetPaint(style.getPaint()));
var cpCount = (int) text.codePoints().count();
var mesh = new PlutoCommandDrawMesh();
var mesh = new PlutoCommandDrawMeshDirectBuffer();
final var quadVerts = 4;
final var twoTriVerts = 6;
@ -44,6 +62,8 @@ public class STBTTBasicTextShaper implements IShapingStrategy<STBTTFont.STBTTGly
var uvDim = 2;
var uvBuf = MemoryUtil.memAllocFloat(uvDim * quadVerts * cpCount);
var paintUVBuf = MemoryUtil.memAllocFloat(uvDim * quadVerts * cpCount);
var pageDim = 1;
var pageBuf = MemoryUtil.memAllocInt(pageDim * quadVerts * cpCount);
@ -60,14 +80,17 @@ public class STBTTBasicTextShaper implements IShapingStrategy<STBTTFont.STBTTGly
float[] uvs = new float[uvDim * quadVerts];
int[] pages = new int[pageDim * quadVerts];
float scale = 1 / (float) STBTTFont.PIXEL_HEIGHT;
float scale = font.getScale();
commandBuf.push(new LiCommandSpecial(cb -> PlutoGUIMod.fontShader.setPixelScale(style.getSize())));
float x = 0;
float y = 0;
GlyphInfo<STBTTFont.STBTTGlyphAtlas, STBTTFont.STBTTGlyphMetrics> info;
GlyphInfo<?, ?> info;
STBTTFont.STBTTGlyphMetrics metrics = null;
int cp;
float minX = Float.POSITIVE_INFINITY, maxX = Float.NEGATIVE_INFINITY, minY = Float.POSITIVE_INFINITY, maxY = Float.NEGATIVE_INFINITY;
while (cpIt.hasNext())
{
@ -82,8 +105,8 @@ public class STBTTBasicTextShaper implements IShapingStrategy<STBTTFont.STBTTGly
}
}
if (metrics != null)
x += metrics.getKerning(cp);
if (metrics != null && features.contains(TextShaper.EnumFeature.KERNING))
x += metrics.getKerning(cp) * scale;
metrics = font.getGlyphMetrics(cp);
info = atlas.getGlyph(cp);
@ -91,17 +114,25 @@ public class STBTTBasicTextShaper implements IShapingStrategy<STBTTFont.STBTTGly
if (metrics == null)
continue;
x += metrics.getLeftSideBearing() * scale;
if (info != null)
{
float gx = x;
float gy = y + font.getAscent() * scale - metrics.getCY0() * scale;
float gx = x + metrics.getXOrigin();
float gy = y + metrics.getYOrigin() + font.getAscent() * scale;
vertices[6] = vertices[0] = gx - STBTTFont.SDF_PADDING;
vertices[3] = vertices[1] = gy + STBTTFont.SDF_PADDING;
vertices[4] = vertices[2] = gx + metrics.getCX1() * scale - metrics.getCX0() * scale + STBTTFont.SDF_PADDING;
vertices[7] = vertices[5] = gy - metrics.getCY1() * scale + metrics.getCY0() * scale - STBTTFont.SDF_PADDING;
float xLow = gx - STBTTFont.SDF_PADDING;
float xHigh = gx + metrics.getCX1() * scale - metrics.getCX0() * scale + STBTTFont.SDF_PADDING;
float yLow = gy - STBTTFont.SDF_PADDING;
float yHigh = gy + metrics.getCY1() * scale - metrics.getCY0() * scale + STBTTFont.SDF_PADDING;
minX = Math.min(minX, xLow);
minY = Math.min(minY, yLow);
maxX = Math.max(maxX, xHigh);
maxY = Math.max(maxY, yHigh);
vertices[6] = vertices[0] = xLow;
vertices[3] = vertices[1] = yHigh;
vertices[4] = vertices[2] = xHigh;
vertices[7] = vertices[5] = yLow;
var uvRect = info.getRect();
uvs[6] = uvs[0] = uvRect.minX;
@ -128,14 +159,23 @@ public class STBTTBasicTextShaper implements IShapingStrategy<STBTTFont.STBTTGly
x += metrics.getAdvanceX() * scale;
}
mesh.addAttribute(shader.position, vertexBuf.flip(), vertDim);
vertexBuf.flip();
while (vertexBuf.hasRemaining())
{
paintUVBuf.put((vertexBuf.get() - minX) / (maxX - minX));
paintUVBuf.put((vertexBuf.get() - minY) / (maxY - minY));
}
mesh.addAttribute(shader.position, vertexBuf.rewind(), vertDim);
mesh.addAttribute(shader.uvCoords, uvBuf.flip(), uvDim);
mesh.addAttribute(shader.page, pageBuf.flip(), pageDim);
mesh.addAttribute(shader.paintUVCoords, paintUVBuf.flip(), uvDim);
mesh.addIndices(indexBuf.flip());
commandBuf.push(mesh);
return new PlutoTextInfo(commandBuf);
return new LiTextInfo(commandBuf, new Rectanglef(minX, minY, maxX, maxY));
}
}

View File

@ -1,9 +1,10 @@
package org.plutoengine.graphics.gui;
package org.plutoengine.graphics.gui.stbttf;
import org.joml.primitives.Rectanglef;
import org.lwjgl.stb.*;
import org.lwjgl.system.MemoryStack;
import org.plutoengine.buffer.BufferHelper;
import org.plutoengine.graphics.gui.SDFTextureArray;
import org.plutoengine.graphics.texture.MagFilter;
import org.plutoengine.graphics.texture.MinFilter;
import org.plutoengine.graphics.texture.WrapMode;
@ -29,7 +30,7 @@ import java.util.stream.IntStream;
public class STBTTFont extends LiFont<STBTTFont.STBTTGlyphAtlas, STBTTFont.STBTTGlyphMetrics> implements AutoCloseable
{
public static final int PIXEL_HEIGHT = 64;
public static final int SDF_PADDING = 4;
public static final int SDF_PADDING = 8;
public static final int SHEET_SIZE = 1024;
private int descent;
@ -37,6 +38,10 @@ public class STBTTFont extends LiFont<STBTTFont.STBTTGlyphAtlas, STBTTFont.STBTT
private int lineGap;
private int lineAdvance;
private float scale;
private Map<Integer, Integer> glyphIndexLookup;
private Map<KerningPair, Integer> kerningTable;
private record KerningPair(int left, int right)
@ -49,6 +54,11 @@ public class STBTTFont extends LiFont<STBTTFont.STBTTGlyphAtlas, STBTTFont.STBTT
super(name);
}
public float getScale()
{
return this.scale;
}
public int getAscent()
{
return this.ascent;
@ -71,7 +81,8 @@ public class STBTTFont extends LiFont<STBTTFont.STBTTGlyphAtlas, STBTTFont.STBTT
public int getKerningOffset(int left, int right)
{
return this.kerningTable.getOrDefault(new KerningPair(left, right), 0);
var sk = new KerningPair(glyphIndexLookup.getOrDefault(left, -1), glyphIndexLookup.getOrDefault(right, -1));
return this.kerningTable.getOrDefault(sk, 0);
}
@Override
@ -181,8 +192,6 @@ public class STBTTFont extends LiFont<STBTTFont.STBTTGlyphAtlas, STBTTFont.STBTT
return null;
}
float scale = STBTruetype.stbtt_ScaleForPixelHeight(fontInfo, PIXEL_HEIGHT);
var nameBuf = STBTruetype.stbtt_GetFontNameString(fontInfo,
STBTruetype.STBTT_PLATFORM_ID_MICROSOFT,
STBTruetype.STBTT_MS_EID_UNICODE_BMP,
@ -201,6 +210,7 @@ public class STBTTFont extends LiFont<STBTTFont.STBTTGlyphAtlas, STBTTFont.STBTT
Logger.logf(SmartSeverity.ADDED, "Loading font: %s%n", name);
var font = new STBTTFont(name);
font.scale = STBTruetype.stbtt_ScaleForPixelHeight(fontInfo, PIXEL_HEIGHT);
var ascentBuf = stack.callocInt(1);
var descentBuf = stack.callocInt(1);
@ -209,9 +219,6 @@ public class STBTTFont extends LiFont<STBTTFont.STBTTGlyphAtlas, STBTTFont.STBTT
var codepoints = IntStream.rangeClosed(0x0000, 0x04ff);
var onedgeValue = 220;
var pixelDistScale = onedgeValue / (float) SDF_PADDING;
var sdfWidthBuf = stack.mallocInt(1);
var sdfHeightBuf = stack.mallocInt(1);
var xOffsBuf = stack.mallocInt(1);
@ -232,6 +239,7 @@ public class STBTTFont extends LiFont<STBTTFont.STBTTGlyphAtlas, STBTTFont.STBTT
var colorModel = new ComponentColorModel(colorSpace, new int[] { 8 }, false,false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
font.atlas = font.new STBTTGlyphAtlas();
font.glyphIndexLookup = new HashMap<>();
var advanceWidth = stack.mallocInt(1);
var leftSideBearing = stack.mallocInt(1);
@ -241,10 +249,13 @@ public class STBTTFont extends LiFont<STBTTFont.STBTTGlyphAtlas, STBTTFont.STBTT
var cx1 = stack.mallocInt(1);
var cy1 = stack.mallocInt(1);
var onedgeValue = 128;
var pixelDistScale = onedgeValue / (float) SDF_PADDING;
for (var cp : codepoints.toArray())
{
var buf = STBTruetype.stbtt_GetCodepointSDF(fontInfo,
scale,
font.scale,
cp,
SDF_PADDING,
(byte) onedgeValue,
@ -324,6 +335,8 @@ public class STBTTFont extends LiFont<STBTTFont.STBTTGlyphAtlas, STBTTFont.STBTT
glyphMetrics.cy1 = cy1.get(0);
font.addGlyph(cp, glyphMetrics, glyphInfo);
font.glyphIndexLookup.put(cp, STBTruetype.stbtt_FindGlyphIndex(fontInfo, cp));
}
if (!sheets.contains(atlas))
@ -332,10 +345,16 @@ public class STBTTFont extends LiFont<STBTTFont.STBTTGlyphAtlas, STBTTFont.STBTT
nodes.free();
var kerningTableLength = STBTruetype.stbtt_GetKerningTableLength(fontInfo);
var kerningTable = STBTTKerningentry.malloc(kerningTableLength, stack);
STBTruetype.stbtt_GetKerningTable(fontInfo, kerningTable);
font.kerningTable = new HashMap<>();
kerningTable.forEach(e -> font.kerningTable.put(new KerningPair(e.glyph1(), e.glyph2()), e.advance()));
try (var kerningTable = STBTTKerningentry.malloc(kerningTableLength))
{
STBTruetype.stbtt_GetKerningTable(fontInfo, kerningTable);
font.kerningTable = new HashMap<>();
kerningTable.forEach(e -> {
// System.out.printf("%s -> %s = %d%n", Character.toString(e.glyph1()), Character.toString(e.glyph2()), e.advance());
font.kerningTable.put(new KerningPair(e.glyph1(), e.glyph2()), e.advance());
});
}
font.ascent = ascentBuf.get();
font.descent = descentBuf.get();

View File

@ -9,9 +9,6 @@ import org.plutoengine.graphics.gl.vbo.IndexArrayBuffer;
import java.util.*;
import org.plutoengine.logger.Logger;
import org.plutoengine.logger.SmartSeverity;
public class VertexArray implements AutoCloseable
{
protected final List<Integer> usedAttributes;
@ -136,8 +133,6 @@ public class VertexArray implements AutoCloseable
this.indices = null;
}
Logger.logf(SmartSeverity.REMOVED, "Vertex array ID %d deleted...\n", this.glID);
GL33.glDeleteVertexArrays(this.glID);
this.glID = 0;

View File

@ -31,7 +31,7 @@ public enum SmartSeverity implements ISeverity
MODULE_MINUS("[-] [M] ", false),
MODULE_WARNING("[!] [M] ", true),
MODULE_ERROR("[X] [M] ", true),
MODULE_CHECK("[✓] [M] ", true),
MODULE_CHECK("[✓] [M] ", false),
EVENT("[i] [E] ", false),
EVENT_PLUS("[+] [E] ", false),

View File

@ -10,6 +10,10 @@
"fonts": {
"path": "fonts.zip",
"type": "zip"
},
"plutofonts": {
"path": "plutofonts",
"type": "open"
}
}
}

View File

@ -2,25 +2,67 @@
in vec2 uvCoordinates;
flat in int atlasPage;
in vec2 paintUVCoordinates;
uniform sampler2DArray textureSampler;
uniform vec4 recolor;
uniform int paintType;
uniform vec4 paintColor;
uniform int paintGradientStopCount;
uniform vec4[16] paintGradientColors;
uniform float[16] paintGradientPositions;
uniform vec2[2] paintGradientEnds;
uniform float pxScale;
out vec4 out_Color;
vec4 solidColor(void)
{
return paintColor;
}
vec4 gradientColor(void)
{
float par = smoothstep(paintGradientEnds[0].x, paintGradientEnds[1].x, paintUVCoordinates.x) * (paintGradientStopCount - 1);
float nPar = clamp(par, 0, float(paintGradientStopCount - 1)) + paintGradientPositions[0] * 0.000001;
vec4 lCol = paintGradientColors[int(floor(nPar))];
vec4 rCol = paintGradientColors[int(ceil(nPar))];
float gamma = 0.45;
vec4 ilCol = vec4(pow(lCol.r, 1 / gamma), pow(lCol.g, 1 / gamma), pow(lCol.b, 1 / gamma), lCol.a);
vec4 irCol = vec4(pow(rCol.r, 1 / gamma), pow(rCol.g, 1 / gamma), pow(rCol.b, 1 / gamma), rCol.a);
vec4 fCol = mix(ilCol, irCol, fract(nPar));
return vec4(pow(fCol.r, gamma), pow(fCol.g, gamma), pow(fCol.b, gamma), fCol.a);
}
void main(void)
{
vec3 textCoords = vec3(uvCoordinates, atlasPage);
vec3 texCoords = vec3(uvCoordinates, atlasPage);
float threshold = 1 - 240.0 / 255.0;
float threshold = 180.0 / 255.0 - 5.0 / pow(pxScale, 1.6); // Also help small text be readable
float signedDist = 1 - texture(textureSampler, textCoords).r;
float signedDist = texture(textureSampler, texCoords).r - threshold;
float fw = fwidth(signedDist);
vec4 col;
vec4 col = recolor;
switch (paintType)
{
case 0:
col = solidColor();
break;
col.a *= smoothstep(threshold + 0.2, threshold, signedDist);
case 1:
col = gradientColor();
break;
}
col.a *= smoothstep(0, 2.4 / pxScale, signedDist);
out_Color = col;
}

View File

@ -3,20 +3,20 @@
in vec2 position;
in vec2 uvCoords;
in int page;
in vec2 paintUVCoords;
out vec2 uvCoordinates;
out vec2 paintUVCoordinates;
flat out int atlasPage;
uniform mat4 projection;
uniform mat3 transformation;
uniform int italic;
void main(void)
{
vec2 pos = vec2(position.x - position.y * italic / 4.0, position.y);
atlasPage = page;
uvCoordinates = uvCoords;
vec3 transformed = vec3((transformation * vec3(pos, 1.0)).xy, 0.0);
paintUVCoordinates = paintUVCoords;
vec3 transformed = vec3((transformation * vec3(position, 1.0)).xy, 0.0);
gl_Position = projection * vec4(transformed, 1.0);
}

View File

@ -1,7 +1,13 @@
package org.plutoengine.demo;
import org.plutoengine.graphics.PlutoGUIMod;
import org.plutoengine.graphics.gui.STBTTFont;
import org.plutoengine.graphics.gui.stbttf.STBTTFont;
import org.plutoengine.graphics.texture.MagFilter;
import org.plutoengine.graphics.texture.MinFilter;
import org.plutoengine.graphics.texture.WrapMode;
import org.plutoengine.graphics.texture.texture2d.RectangleTexture;
import org.plutoengine.libra.text.font.LiFontFamily;
import org.plutoengine.libra.text.shaping.TextStyleOptions;
import org.plutoengine.mod.IModEntryPoint;
import org.plutoengine.mod.Mod;
import org.plutoengine.mod.ModEntry;
@ -13,17 +19,25 @@ import org.plutoengine.mod.ModEntry;
)
public class BasicApplicationDemoMod implements IModEntryPoint
{
public static STBTTFont font;
public static LiFontFamily<STBTTFont> font;
public static RectangleTexture plutoLogo;
@Override
public void onLoad(Mod mod)
{
font = STBTTFont.load(mod.getResource("fonts$robotoregular#ttf"));
font = new LiFontFamily<>();
font.add(TextStyleOptions.STYLE_REGULAR, STBTTFont.load(mod.getResource("plutofonts$plutostardust#ttf")));
plutoLogo = new RectangleTexture();
plutoLogo.load(mod.getResource("icons$icon128#png"), MagFilter.NEAREST, MinFilter.NEAREST, WrapMode.CLAMP_TO_EDGE, WrapMode.CLAMP_TO_EDGE);
}
@Override
public void onUnload()
{
plutoLogo.close();
font.close();
}
}

View File

@ -1,39 +1,40 @@
package org.plutoengine.demo;
import org.joml.primitives.Rectanglef;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL33;
import org.plutoengine.Pluto;
import org.plutoengine.PlutoApplication;
import org.plutoengine.PlutoLocal;
import org.plutoengine.display.Display;
import org.plutoengine.display.Framerate;
import org.plutoengine.graphics.ImmediateFontRenderer;
import org.plutoengine.graphics.PlutoGUIMod;
import org.plutoengine.graphics.Renderer2D;
import org.plutoengine.graphics.TestFontRenderer;
import org.plutoengine.graphics.gl.vao.QuadPresets;
import org.plutoengine.graphics.RectangleRenderer2D;
import org.plutoengine.graphics.gui.FontShader;
import org.plutoengine.graphics.texture.MagFilter;
import org.plutoengine.graphics.texture.MinFilter;
import org.plutoengine.graphics.texture.WrapMode;
import org.plutoengine.graphics.texture.texture2d.Texture2D;
import org.plutoengine.input.InputBus;
import org.plutoengine.libra.paint.LiGradientPaint;
import org.plutoengine.libra.paint.LiPaint;
import org.plutoengine.libra.text.shaping.TextStyleOptions;
import org.plutoengine.math.ProjectionMatrix;
import org.plutoengine.mod.ModLoader;
import org.plutoengine.shader.RenderShaderBuilder;
import org.plutoengine.shader.uniform.auto.AutomaticUniforms;
import org.plutoengine.tpl.ImageLoader;
import org.plutoengine.util.color.Color;
import org.plutoengine.util.color.EnumColorFormat;
import org.plutoengine.util.color.HSB;
import java.awt.image.BufferedImage;
import java.nio.file.Path;
import java.util.List;
import java.util.stream.Collectors;
public class Main extends PlutoApplication
{
public static Main INSTANCE;
public static void main(String[] args) throws Exception
public static void main(String[] args)
{
var config = new PlutoApplication.StartupConfig();
config.vsync(1);
config.windowName("JSRClone " + VersionInfo.GAME_VERSION);
config.windowName("PlutoEngine Demo " + VersionInfo.GAME_VERSION);
INSTANCE = new Main();
INSTANCE.run(args, config);
@ -42,13 +43,55 @@ public class Main extends PlutoApplication
@Override
public void loop()
{
GL33.glEnable(GL11.GL_BLEND);
GL33.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GL33.glEnable(GL33.GL_BLEND);
GL33.glBlendFunc(GL33.GL_SRC_ALPHA, GL33.GL_ONE_MINUS_SRC_ALPHA);
var projection = ProjectionMatrix.createOrtho2D(this.display.getWidth(), this.display.getHeight());
AutomaticUniforms.VIEWPORT_PROJECTION.fire(projection);
TestFontRenderer.drawString(BasicApplicationDemoMod.font, "Testing ");
var style = new TextStyleOptions(20.0f);
style.setPaint(LiPaint.solidColor(Color.WHITE));
var mods = PlutoLocal.components().getComponent(ModLoader.class).getAllMods();
var modStr = mods.stream().map(mod -> {
var modManifest = mod.getManifest();
return String.format("%s %s", modManifest.displayName(), mod.getVersion());
}).collect(Collectors.joining("\n"));
ImmediateFontRenderer.drawString(5, 5, Framerate.getInterpolatedFPS() + " FPS", BasicApplicationDemoMod.font, style);
ImmediateFontRenderer.drawString(5, 30, modStr, BasicApplicationDemoMod.font, style);
var watermarkStyle = new TextStyleOptions(20.0f);
watermarkStyle.setHorizontalAlign(TextStyleOptions.TextAlign.CENTER);
watermarkStyle.setPaint(LiPaint.solidColor(Color.from(0x4f000000, EnumColorFormat.CF_INT_ARGB)));
ImmediateFontRenderer.drawString(this.display.getWidth() / 2.0f, 5, "PlutoEngine demo build licensed the MIT license...", BasicApplicationDemoMod.font, watermarkStyle);
RectangleRenderer2D.draw()
.at(this.display.getWidth() / 2.0f - 150, this.display.getHeight() / 2.0f - 246, 300, 300)
.recolor(0.5f, 0.5f, 0.5f, 1.0f)
.texture(BasicApplicationDemoMod.plutoLogo).flush();
RectangleRenderer2D.draw()
.at(this.display.getWidth() / 2.0f - 150, this.display.getHeight() / 2.0f - 250, 300, 300)
.texture(BasicApplicationDemoMod.plutoLogo).flush();
var welcomeStyle = new TextStyleOptions(60.0f)
.setHorizontalAlign(TextStyleOptions.TextAlign.CENTER)
.setVerticalAlign(TextStyleOptions.TextAlign.CENTER)
.setOverflowX(TextStyleOptions.OverflowXStrategy.SCALE_TO_FIT)
.setFitBox(new Rectanglef(50.0f, 50.0f, this.display.getWidth() - 50.0f, this.display.getHeight() - 50.0f))
.setPaint(LiPaint.solidColor(Color.VERY_DARK_GRAY));
ImmediateFontRenderer.drawString(0, 102, "Welcome to PlutoEngine v. %s!".formatted(Pluto.VERSION), BasicApplicationDemoMod.font, welcomeStyle);
float gPos = (System.currentTimeMillis() % 7200) / 20.0f;
ImmediateFontRenderer.drawString(500, 50, "Hue: %f".formatted(gPos), BasicApplicationDemoMod.font, style);
var stops = new LiGradientPaint.Stop[16];
for (int i = 0; i < stops.length; i++)
stops[i] = new LiGradientPaint.Stop(i / (float) stops.length, Color.from(new HSB(gPos + i * 10, 1.0f, 1.0f).toRGBA()));
welcomeStyle.setPaint(LiPaint.horizontaLinearGradient(stops));
ImmediateFontRenderer.drawString(0, 100, "Welcome to PlutoEngine v. %s!".formatted(Pluto.VERSION), BasicApplicationDemoMod.font, welcomeStyle);
if (InputBus.Keyboard.pressed(GLFW.GLFW_KEY_R))
{
@ -64,90 +107,8 @@ public class Main extends PlutoApplication
shader.stop();
shader.close();
PlutoGUIMod.fontShader = newShader;
newShader.start();
newShader.recolor.load(1, 1, 1, 1);
}
}
/*
var va = Renderer2D.standardQuad;
va.bind();
va.enableAllAttributes();
var font = BasicApplicationDemoMod.font;
var tex = font.getAtlas();
tex.bind();
var shader = PlutoGUIMod.fontShader;
shader.start();
shader.recolor.load(1, 1, 1, 1);
shader.italic.load(false);
shader.page.load(0);
shader.uvBase.load(0.0f, 0.0f);
shader.uvDelta.load(1.0f, 1.0f);
for (int i = 0; i < tex.getDepth(); i++)
{
var padding = 8;
var x = i % 3;
var y = i / 3;
var size = this.size;
shader.transformationMatrix.load(TransformationMatrix.create(20 + x * (size + padding), 20 + y * (size + padding), 0,
0, 0, 0,
size, size, 1));
shader.page.load(i);
va.draw(DrawMode.TRIANGLES);
}
this.size += InputBus.Mouse.getScrollY() * 20;
if (InputBus.Keyboard.pressed(GLFW.GLFW_KEY_R))
{
var newShader = new RenderShaderBuilder(
PlutoGUIMod.instance.getResource("shaders.VertexFontShader#glsl"),
PlutoGUIMod.instance.getResource("shaders.FragmentFontShader#glsl")
).build(FontShader.class, false);
if (newShader != null)
{
shader.stop();
shader.close();
PlutoGUIMod.fontShader = newShader;
}
}
*/
/*
var buildStr = String.format("Build %s", VersionInfo.GAME_BUILD);
var strWidth = FontHelper.calcStringWidth(buildStr, "default", 0.75f);
FontRenderer.drawString(this.display.getWidth() - strWidth + 1, 3, buildStr, 0, 0, 0, 1, 0.75f, true);
FontRenderer.drawString(this.display.getWidth() - strWidth, 2, buildStr, 0.7f, 0.7f, 0.7f, 1, 0.75f, false);
var fpsStr = String.format("%d FPS", Framerate.getInterpolatedFPS());
FontRenderer.drawString(3, 3, fpsStr, 0, 0, 0, 1, 0.75f, true);
FontRenderer.drawString(2, 2, fpsStr, 0.13f, 0.75f, 0.62f, 1, 0.75f, false);
var mods = PlutoLocal.components().getComponent(ModLoader.class).getAllMods();
int modNr = 0;
for (var mod : mods)
{
var modManifest = mod.getManifest();
var modStr = String.format("%s &c[0xff999999]&i1%s", modManifest.displayName(), mod.getVersion());
FontRenderer.drawString(8, 50 + modNr * 18, modStr, 0, 0, 0, 0, 0.7f, "default", true);
FontRenderer.drawString(7, 49 + modNr * 18, modStr, 1, 1, 1, 1, 0.7f, "default", false);
modNr++;
}
*/
}
public static Display getDisplay()