Initial font renderer rewrite and Libra integration
This commit is contained in:
parent
c675729996
commit
7b7daafc15
|
@ -0,0 +1,3 @@
|
|||
[submodule "libra"]
|
||||
path = libra
|
||||
url = git@github.com:493msi/libra.git
|
|
@ -1,3 +1,10 @@
|
|||
## 22.1.0.0-alpha.0
|
||||
* `[PlutoGUI]` **Complete rewrite of the GUI library**
|
||||
* `[Pluto*]` **Unified the cleanup methods of all OpenGL object classes to `close`**
|
||||
* `[PlutoLib]` New dependency: `joml-primitives`
|
||||
* `[PlutoDisplay]` Removed the `flipped` word from all buffer functions
|
||||
* `[PlutoRuntime]` Fixed opening .zip filesystems
|
||||
|
||||
## 22.0.0.0-alpha.7
|
||||
* `[PlutoRuntime]` Fixed several resource filesystem bugs
|
||||
|
||||
|
|
|
@ -12,17 +12,18 @@ object Versions {
|
|||
}
|
||||
|
||||
const val jomlVersion = "1.10.2"
|
||||
const val jomlPrimitivesVersion = "1.10.0"
|
||||
const val steamworks4jVersion = "1.8.0"
|
||||
const val steamworks4jServerVersion = "1.8.0"
|
||||
|
||||
const val versionYear = 22
|
||||
const val versionMajor = 0
|
||||
const val versionMajor = 1
|
||||
const val versionMinor = 0
|
||||
const val versionPatch = 0
|
||||
|
||||
const val isPrerelease = true
|
||||
const val prereleaseName = "alpha"
|
||||
const val prerealeaseUpdate = 7
|
||||
const val prerealeaseUpdate = 0
|
||||
|
||||
val versionFull =
|
||||
if (isPrerelease)
|
||||
|
|
|
@ -3,7 +3,7 @@ package org.plutoengine.audio.al;
|
|||
import org.joml.Vector3fc;
|
||||
import org.lwjgl.openal.AL10;
|
||||
|
||||
public abstract class AudioSource
|
||||
public abstract class AudioSource implements AutoCloseable
|
||||
{
|
||||
protected final int source;
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ public final class BufferHelper
|
|||
* @author 493msi
|
||||
* @since 0.1
|
||||
*/
|
||||
public static IntBuffer flippedIntBuffer(int[] data)
|
||||
public static IntBuffer intBuffer(int[] data)
|
||||
{
|
||||
IntBuffer buffer = BufferUtils.createIntBuffer(data.length);
|
||||
buffer.put(data);
|
||||
|
@ -46,7 +46,7 @@ public final class BufferHelper
|
|||
* @author 493msi
|
||||
* @since 0.1
|
||||
*/
|
||||
public static FloatBuffer flippedFloatBuffer(float[] data)
|
||||
public static FloatBuffer floatBuffer(float[] data)
|
||||
{
|
||||
FloatBuffer buffer = BufferUtils.createFloatBuffer(data.length);
|
||||
buffer.put(data);
|
||||
|
@ -64,7 +64,7 @@ public final class BufferHelper
|
|||
* @author 493msi
|
||||
* @since 0.1
|
||||
*/
|
||||
public static ByteBuffer flippedByteBuffer(byte[] data)
|
||||
public static ByteBuffer byteBuffer(byte[] data)
|
||||
{
|
||||
ByteBuffer buffer = BufferUtils.createByteBuffer(data.length);
|
||||
buffer.put(data);
|
||||
|
@ -136,8 +136,8 @@ public final class BufferHelper
|
|||
* @author 493msi
|
||||
* @since 0.3
|
||||
*/
|
||||
public static ByteBuffer readToFlippedByteBuffer(Path path) throws IOException
|
||||
public static ByteBuffer readToByteBuffer(Path path) throws IOException
|
||||
{
|
||||
return flippedByteBuffer(Files.readAllBytes(path));
|
||||
return byteBuffer(Files.readAllBytes(path));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,4 +7,12 @@ description = ""
|
|||
|
||||
dependencies {
|
||||
api(project(":plutoengine:plutospritesheet"))
|
||||
api(project(":libra"))
|
||||
|
||||
api("io.reactivex.rxjava3", "rxjava", "3.1.4")
|
||||
|
||||
implementation("org.lwjgl", "lwjgl-yoga")
|
||||
runtimeOnly("org.lwjgl", "lwjgl-yoga", classifier = org.plutoengine.Versions.lwjglNatives)
|
||||
|
||||
implementation("org.commonmark", "commonmark", "0.18.1")
|
||||
}
|
|
@ -1,13 +1,12 @@
|
|||
package org.plutoengine.graphics;
|
||||
|
||||
import org.joml.Matrix3f;
|
||||
import org.plutoengine.Pluto;
|
||||
import org.plutoengine.graphics.font.FontManager;
|
||||
import org.plutoengine.graphics.font.FontShader;
|
||||
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.RectangleTexture;
|
||||
import org.plutoengine.gui.font.FontRenderer;
|
||||
import org.plutoengine.mod.IModEntryPoint;
|
||||
import org.plutoengine.mod.Mod;
|
||||
import org.plutoengine.mod.ModEntry;
|
||||
|
@ -28,18 +27,17 @@ public class PlutoGUIMod implements IModEntryPoint
|
|||
|
||||
public static RectangleTexture uiElementsAtlas;
|
||||
|
||||
private static FontShader fontShader;
|
||||
public static FontShader fontShader;
|
||||
|
||||
public void onLoad(Mod mod)
|
||||
{
|
||||
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());
|
||||
|
||||
// Load the default font
|
||||
FontManager.loadFont(mod.getResource("font.default"));
|
||||
|
||||
FontRenderer.load(fontShader);
|
||||
|
||||
uiElementsAtlas = new RectangleTexture();
|
||||
uiElementsAtlas.load(mod.getResource("gui.elements#png"), MagFilter.NEAREST, MinFilter.NEAREST, WrapMode.CLAMP_TO_EDGE, WrapMode.CLAMP_TO_EDGE);
|
||||
|
@ -47,11 +45,8 @@ public class PlutoGUIMod implements IModEntryPoint
|
|||
|
||||
public void onUnload()
|
||||
{
|
||||
uiElementsAtlas.delete();
|
||||
uiElementsAtlas.close();
|
||||
|
||||
FontManager.unloadAll();
|
||||
|
||||
FontRenderer.unload();
|
||||
fontShader.dispose();
|
||||
fontShader.close();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
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();
|
||||
}
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
package org.plutoengine.graphics.font;
|
||||
|
||||
import org.plutoengine.graphics.texture.MagFilter;
|
||||
import org.plutoengine.graphics.texture.MinFilter;
|
||||
import org.plutoengine.graphics.texture.Texture;
|
||||
import org.plutoengine.graphics.texture.WrapMode;
|
||||
import org.plutoengine.graphics.texture.texture2d.RectangleTexture;
|
||||
import org.plutoengine.gui.font.CharacterInfo;
|
||||
import org.plutoengine.gui.font.Font;
|
||||
import org.plutoengine.logger.Logger;
|
||||
import org.plutoengine.logger.SmartSeverity;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class FontManager
|
||||
{
|
||||
private static final Map<String, Font> fonts = new HashMap<>();
|
||||
|
||||
public static void loadFont(Path address)
|
||||
{
|
||||
String fontname = null;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
var def = new HashMap<Character, CharacterInfo>();
|
||||
|
||||
int row = 0;
|
||||
|
||||
try
|
||||
{
|
||||
var lines = Files.readAllLines(address.resolve("definitions#txt"));
|
||||
|
||||
for (var line : lines)
|
||||
{
|
||||
if (line.startsWith("//"))
|
||||
continue;
|
||||
|
||||
if (row == 0)
|
||||
{
|
||||
String[] fontinfo = line.split(",");
|
||||
|
||||
fontname = fontinfo[0];
|
||||
|
||||
String[] dim = fontinfo[1].split("x");
|
||||
|
||||
width = Integer.parseInt(dim[0]);
|
||||
height = Integer.parseInt(dim[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
String[] offs = line.split(" ")[1].split(";");
|
||||
|
||||
def.put(line.charAt(0), new CharacterInfo(row - 1, Integer.parseInt(offs[0]), Integer.parseInt(offs[1])));
|
||||
}
|
||||
|
||||
row++;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.log(SmartSeverity.ERROR, "Could not load font: " + address.toString());
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
Font font = new Font(fontname, width, height, def);
|
||||
RectangleTexture texture = new RectangleTexture();
|
||||
texture.load(address.resolve("tex#png"), MagFilter.NEAREST, MinFilter.LINEAR, WrapMode.CLAMP_TO_EDGE, WrapMode.CLAMP_TO_EDGE);
|
||||
font.setTexture(texture);
|
||||
|
||||
fonts.put(fontname, font);
|
||||
}
|
||||
|
||||
public static void unloadAll()
|
||||
{
|
||||
fonts.values()
|
||||
.stream()
|
||||
.map(Font::getTexture)
|
||||
.forEach(Texture::delete);
|
||||
fonts.clear();
|
||||
}
|
||||
|
||||
public static Font getFontByName(String fontname)
|
||||
{
|
||||
var font = fonts.get(fontname);
|
||||
|
||||
if (font == null)
|
||||
{
|
||||
// Logger.log(SmartSeverity.WARNING, "Font with name " + fontname + " could not be found, using the default one instead (if there is one).");
|
||||
return fonts.get("default");
|
||||
}
|
||||
|
||||
return font;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package org.plutoengine.graphics.font;
|
||||
package org.plutoengine.graphics.gui;
|
||||
|
||||
import org.joml.Matrix3fc;
|
||||
import org.plutoengine.graphics.gl.vao.attrib.ReservedAttributes;
|
||||
import org.plutoengine.shader.ShaderBase;
|
||||
import org.plutoengine.shader.ShaderProgram;
|
||||
|
@ -8,20 +9,14 @@ import org.plutoengine.shader.uniform.*;
|
|||
import org.plutoengine.shader.uniform.auto.AutoViewportProjection;
|
||||
|
||||
@ShaderProgram
|
||||
public final class FontShader extends ShaderBase
|
||||
public final class FontShader extends ShaderBase implements IGUIShader
|
||||
{
|
||||
@AutoViewportProjection
|
||||
@Uniform(name = "projection")
|
||||
public UniformMat4 projectionMatrix;
|
||||
|
||||
@Uniform(name = "transformation")
|
||||
public UniformMat4 transformationMatrix;
|
||||
|
||||
@Uniform
|
||||
public UniformVec2 uvBase;
|
||||
|
||||
@Uniform
|
||||
public UniformVec2 uvDelta;
|
||||
public UniformMat3 transformationMatrix;
|
||||
|
||||
@Uniform
|
||||
public UniformRGBA recolor;
|
||||
|
@ -34,4 +29,13 @@ public final class FontShader extends ShaderBase
|
|||
|
||||
@VertexArrayAttribute(ReservedAttributes.UV)
|
||||
public int uvCoords;
|
||||
|
||||
@VertexArrayAttribute(2)
|
||||
public int page;
|
||||
|
||||
@Override
|
||||
public void setTransform(Matrix3fc transform)
|
||||
{
|
||||
this.transformationMatrix.load(transform);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package org.plutoengine.graphics.gui;
|
||||
|
||||
import org.joml.Matrix3fc;
|
||||
|
||||
public interface IGUIShader
|
||||
{
|
||||
void setTransform(Matrix3fc transform);
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package org.plutoengine.graphics.gui;
|
||||
|
||||
import org.plutoengine.component.PlutoLocalComponent;
|
||||
|
||||
public class PlutoGUI extends PlutoLocalComponent
|
||||
{
|
||||
@Override
|
||||
public boolean isUnique()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
package org.plutoengine.graphics.gui;
|
||||
|
||||
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.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.LiCommandSetTransform;
|
||||
import org.plutoengine.shader.ShaderBase;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
|
||||
public class PlutoGUICommandParser extends AbstractGUICommandParser
|
||||
{
|
||||
@Override
|
||||
protected IGUIRenderer parse(LiCommandBuffer mergedBuffer)
|
||||
{
|
||||
var drawCalls = new ArrayList<Runnable>();
|
||||
var closeCalls = new ArrayDeque<Runnable>();
|
||||
var alreadyEnabledAttribs = new HashSet<Integer>();
|
||||
var currentShader = (ShaderBase & IGUIShader) null;
|
||||
|
||||
for (var cmd : mergedBuffer)
|
||||
{
|
||||
var type = cmd.getType();
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case DRAW_MESH -> {
|
||||
if (!(cmd instanceof PlutoCommandDrawMesh drawCmd))
|
||||
throw new IllegalStateException();
|
||||
|
||||
var vab = new VertexArrayBuilder();
|
||||
|
||||
var data = drawCmd.getData();
|
||||
var attrInfo = drawCmd.getAttributeInfo();
|
||||
data.forEach((attr, val) -> vab.attrib(attr, attrInfo.get(attr).dimensions(), val.flip()));
|
||||
|
||||
var indices = drawCmd.getIndices();
|
||||
if (indices != null)
|
||||
vab.indices(indices.flip());
|
||||
|
||||
var vao = vab.build();
|
||||
var attribs = vao.getUsedAttributes();
|
||||
var attribsToEnable = new HashSet<>(attribs);
|
||||
attribsToEnable.removeAll(alreadyEnabledAttribs);
|
||||
alreadyEnabledAttribs.addAll(attribsToEnable);
|
||||
|
||||
drawCalls.add(() -> {
|
||||
vao.bind();
|
||||
attribsToEnable.forEach(VertexArray::enableAttribute);
|
||||
vao.draw(DrawMode.TRIANGLES);
|
||||
});
|
||||
|
||||
closeCalls.add(vao::close);
|
||||
}
|
||||
|
||||
case SET_TRANSFORM -> {
|
||||
if (!(cmd instanceof LiCommandSetTransform transformCmd))
|
||||
throw new IllegalStateException();
|
||||
|
||||
var shaderCapture = currentShader;
|
||||
|
||||
assert shaderCapture != null;
|
||||
|
||||
drawCalls.add(() -> shaderCapture.setTransform(transformCmd.getTransform()));
|
||||
}
|
||||
|
||||
case SWITCH_SHADER -> {
|
||||
if (!(cmd instanceof PlutoCommandSwitchShader swSh))
|
||||
throw new IllegalStateException();
|
||||
|
||||
var shaderCapture = currentShader = (ShaderBase & IGUIShader) swSh.getShader();
|
||||
|
||||
assert shaderCapture != null;
|
||||
|
||||
drawCalls.add(() -> shaderCapture.start());
|
||||
}
|
||||
|
||||
case SWITCH_TEXTURE -> {
|
||||
if (!(cmd instanceof PlutoCommandSwitchTexture swTx))
|
||||
throw new IllegalStateException();
|
||||
|
||||
var textureCapture = swTx.getTexture();
|
||||
|
||||
assert textureCapture != null;
|
||||
|
||||
drawCalls.add(textureCapture::bind);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new IGUIRenderer()
|
||||
{
|
||||
@Override
|
||||
public void render()
|
||||
{
|
||||
drawCalls.forEach(Runnable::run);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
var it = closeCalls.descendingIterator();
|
||||
while (it.hasNext())
|
||||
{
|
||||
var call = it.next();
|
||||
call.run();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package org.plutoengine.graphics.gui;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL33;
|
||||
import org.plutoengine.graphics.texture.MagFilter;
|
||||
import org.plutoengine.graphics.texture.MinFilter;
|
||||
import org.plutoengine.graphics.texture.Texture;
|
||||
import org.plutoengine.graphics.texture.WrapMode;
|
||||
import org.plutoengine.tpl.ImageLoader;
|
||||
import org.plutoengine.tpl.ImageY;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
|
||||
public class SDFTextureArray extends Texture
|
||||
{
|
||||
public SDFTextureArray()
|
||||
{
|
||||
super(GL33.GL_TEXTURE_2D_ARRAY, 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsMipMapping()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeData(long address)
|
||||
{
|
||||
GL33.glTexImage3D(GL33.GL_TEXTURE_2D_ARRAY, 0, GL33.GL_R8, this.width, this.height, this.depth, 0, GL33.GL_RED, GL11.GL_UNSIGNED_BYTE, address);
|
||||
}
|
||||
|
||||
public void loadImg(List<BufferedImage> imageData, int width, int height, int depth, MagFilter magFilter, MinFilter minFilter, WrapMode... wrap)
|
||||
{
|
||||
var data = imageData.stream()
|
||||
.map(ImageLoader::loadImageGrayscale)
|
||||
.map(ImageY::getData)
|
||||
.toList();
|
||||
|
||||
this.load(data, width, height, depth, magFilter, minFilter, wrap);
|
||||
}
|
||||
|
||||
public void load(List<ByteBuffer> imageData, int width, int height, int depth, MagFilter magFilter, MinFilter minFilter, WrapMode... wrap)
|
||||
{
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.depth = imageData.size();
|
||||
|
||||
this.bind();
|
||||
|
||||
this.setFilteringOptions(magFilter, minFilter);
|
||||
this.setWrapOptions(wrap);
|
||||
|
||||
this.writeData(0);
|
||||
|
||||
for (int i = 0; i < imageData.size(); i++)
|
||||
{
|
||||
var img = imageData.get(i);
|
||||
GL33.glTexSubImage3D(GL33.GL_TEXTURE_2D_ARRAY, 0,
|
||||
0, 0, i,
|
||||
this.width, this.height, 1,
|
||||
GL33.GL_RED, GL11.GL_UNSIGNED_BYTE, img);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(ByteBuffer imageData, int width, int height, MagFilter magFilter, MinFilter minFilter, WrapMode... wrap)
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
package org.plutoengine.graphics.gui;
|
||||
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
import org.plutoengine.graphics.PlutoGUIMod;
|
||||
import org.plutoengine.graphics.gui.command.PlutoCommandDrawMesh;
|
||||
import org.plutoengine.graphics.gui.command.PlutoCommandSwitchShader;
|
||||
import org.plutoengine.graphics.gui.command.PlutoCommandSwitchTexture;
|
||||
import org.plutoengine.libra.command.LiCommandBuffer;
|
||||
import org.plutoengine.libra.text.LiTextInfo;
|
||||
import org.plutoengine.libra.text.font.GlyphInfo;
|
||||
import org.plutoengine.libra.text.shaping.IShapingStrategy;
|
||||
import org.plutoengine.libra.text.shaping.TextShaper;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
|
||||
public class STBTTBasicTextShaper implements IShapingStrategy<STBTTFont.STBTTGlyphMetrics, STBTTFont.STBTTGlyphAtlas, STBTTFont>
|
||||
{
|
||||
|
||||
@Override
|
||||
public LiTextInfo shape(EnumSet<TextShaper.EnumFeature> features, STBTTFont font, String text)
|
||||
{
|
||||
var commandBuf = new LiCommandBuffer();
|
||||
|
||||
var atlas = font.getGlyphAtlas();
|
||||
var atlasTexture = atlas.getGlyphAtlasTexture();
|
||||
var texSwitch = new PlutoCommandSwitchTexture(atlasTexture);
|
||||
commandBuf.push(texSwitch);
|
||||
|
||||
var shader = PlutoGUIMod.fontShader;
|
||||
var shaderSwitch = new PlutoCommandSwitchShader(shader);
|
||||
commandBuf.push(shaderSwitch);
|
||||
|
||||
var cpCount = (int) text.codePoints().count();
|
||||
|
||||
var mesh = new PlutoCommandDrawMesh();
|
||||
|
||||
final var quadVerts = 4;
|
||||
final var twoTriVerts = 6;
|
||||
|
||||
var vertDim = 2;
|
||||
var vertexBuf = MemoryUtil.memAllocFloat(vertDim * quadVerts * cpCount);
|
||||
|
||||
var uvDim = 2;
|
||||
var uvBuf = MemoryUtil.memAllocFloat(uvDim * quadVerts * cpCount);
|
||||
|
||||
var pageDim = 1;
|
||||
var pageBuf = MemoryUtil.memAllocInt(pageDim * quadVerts * cpCount);
|
||||
|
||||
var indexBuf = MemoryUtil.memAllocInt(twoTriVerts * cpCount);
|
||||
|
||||
var cpIt = text.codePoints().iterator();
|
||||
|
||||
var indices = new int[] {
|
||||
0, 1, 2,
|
||||
0, 2, 3
|
||||
};
|
||||
|
||||
float[] vertices = new float[vertDim * quadVerts];
|
||||
float[] uvs = new float[uvDim * quadVerts];
|
||||
int[] pages = new int[pageDim * quadVerts];
|
||||
|
||||
float scale = 1 / (float) STBTTFont.PIXEL_HEIGHT;
|
||||
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
|
||||
GlyphInfo<STBTTFont.STBTTGlyphAtlas, STBTTFont.STBTTGlyphMetrics> info;
|
||||
STBTTFont.STBTTGlyphMetrics metrics = null;
|
||||
int cp;
|
||||
|
||||
while (cpIt.hasNext())
|
||||
{
|
||||
cp = cpIt.next();
|
||||
|
||||
switch (cp)
|
||||
{
|
||||
case '\n' -> {
|
||||
x = 0;
|
||||
y += font.getLineAdvance() * scale;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (metrics != null)
|
||||
x += metrics.getKerning(cp);
|
||||
|
||||
metrics = font.getGlyphMetrics(cp);
|
||||
info = atlas.getGlyph(cp);
|
||||
|
||||
if (metrics == null)
|
||||
continue;
|
||||
|
||||
x += metrics.getLeftSideBearing() * scale;
|
||||
|
||||
if (info != null)
|
||||
{
|
||||
float gx = x;
|
||||
float gy = y + font.getAscent() * scale - metrics.getCY0() * 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;
|
||||
|
||||
var uvRect = info.getRect();
|
||||
uvs[6] = uvs[0] = uvRect.minX;
|
||||
uvs[3] = uvs[1] = 1 - uvRect.maxY;
|
||||
uvs[4] = uvs[2] = uvRect.maxX;
|
||||
uvs[7] = uvs[5] = 1 - uvRect.minY;
|
||||
|
||||
Arrays.fill(pages, info.getPage());
|
||||
|
||||
vertexBuf.put(vertices);
|
||||
uvBuf.put(uvs);
|
||||
pageBuf.put(pages);
|
||||
|
||||
indexBuf.put(indices);
|
||||
|
||||
indices[0] += quadVerts;
|
||||
indices[1] += quadVerts;
|
||||
indices[2] += quadVerts;
|
||||
indices[3] += quadVerts;
|
||||
indices[4] += quadVerts;
|
||||
indices[5] += quadVerts;
|
||||
}
|
||||
|
||||
x += metrics.getAdvanceX() * scale;
|
||||
}
|
||||
|
||||
mesh.addAttribute(shader.position, vertexBuf.flip(), vertDim);
|
||||
mesh.addAttribute(shader.uvCoords, uvBuf.flip(), uvDim);
|
||||
mesh.addAttribute(shader.page, pageBuf.flip(), pageDim);
|
||||
|
||||
mesh.addIndices(indexBuf.flip());
|
||||
|
||||
commandBuf.push(mesh);
|
||||
|
||||
return new PlutoTextInfo(commandBuf);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,358 @@
|
|||
package org.plutoengine.graphics.gui;
|
||||
|
||||
import org.joml.primitives.Rectanglef;
|
||||
import org.lwjgl.stb.*;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
import org.plutoengine.buffer.BufferHelper;
|
||||
import org.plutoengine.graphics.texture.MagFilter;
|
||||
import org.plutoengine.graphics.texture.MinFilter;
|
||||
import org.plutoengine.graphics.texture.WrapMode;
|
||||
import org.plutoengine.libra.text.font.GlyphInfo;
|
||||
import org.plutoengine.libra.text.font.GlyphMetrics;
|
||||
import org.plutoengine.libra.text.font.LiFont;
|
||||
import org.plutoengine.logger.Logger;
|
||||
import org.plutoengine.logger.SmartSeverity;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ComponentColorModel;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.WritableRaster;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
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 SHEET_SIZE = 1024;
|
||||
|
||||
private int descent;
|
||||
private int ascent;
|
||||
private int lineGap;
|
||||
private int lineAdvance;
|
||||
|
||||
private Map<KerningPair, Integer> kerningTable;
|
||||
|
||||
private record KerningPair(int left, int right)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private STBTTFont(String name)
|
||||
{
|
||||
super(name);
|
||||
}
|
||||
|
||||
public int getAscent()
|
||||
{
|
||||
return this.ascent;
|
||||
}
|
||||
|
||||
public int getDescent()
|
||||
{
|
||||
return this.descent;
|
||||
}
|
||||
|
||||
public int getLineGap()
|
||||
{
|
||||
return this.lineGap;
|
||||
}
|
||||
|
||||
public int getLineAdvance()
|
||||
{
|
||||
return this.lineAdvance;
|
||||
}
|
||||
|
||||
public int getKerningOffset(int left, int right)
|
||||
{
|
||||
return this.kerningTable.getOrDefault(new KerningPair(left, right), 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
var tex = this.getGlyphAtlas().getGlyphAtlasTexture();
|
||||
tex.close();
|
||||
}
|
||||
|
||||
public class STBTTGlyphAtlas extends LiFont<STBTTGlyphAtlas, STBTTGlyphMetrics>.GlyphAtlas
|
||||
{
|
||||
private SDFTextureArray glyphAtlasTexture;
|
||||
|
||||
public void setGlyphAtlasTexture(SDFTextureArray glyphAtlasTexture)
|
||||
{
|
||||
this.glyphAtlasTexture = glyphAtlasTexture;
|
||||
}
|
||||
|
||||
public SDFTextureArray getGlyphAtlasTexture()
|
||||
{
|
||||
return this.glyphAtlasTexture;
|
||||
}
|
||||
}
|
||||
|
||||
public class STBTTGlyphMetrics extends GlyphMetrics
|
||||
{
|
||||
private final int codepoint;
|
||||
private int advanceX;
|
||||
private int leftSideBearing;
|
||||
|
||||
private int cx0;
|
||||
private int cy0;
|
||||
private int cx1;
|
||||
private int cy1;
|
||||
|
||||
private int xOrigin;
|
||||
private int yOrigin;
|
||||
|
||||
private STBTTGlyphMetrics(int codepoint)
|
||||
{
|
||||
this.codepoint = codepoint;
|
||||
}
|
||||
|
||||
public int getAdvanceX()
|
||||
{
|
||||
return this.advanceX;
|
||||
}
|
||||
|
||||
public int getLeftSideBearing()
|
||||
{
|
||||
return this.leftSideBearing;
|
||||
}
|
||||
|
||||
public int getCodepoint()
|
||||
{
|
||||
return this.codepoint;
|
||||
}
|
||||
|
||||
public int getKerning(int cp)
|
||||
{
|
||||
return STBTTFont.this.getKerningOffset(this.codepoint, cp);
|
||||
}
|
||||
|
||||
public int getCX0()
|
||||
{
|
||||
return this.cx0;
|
||||
}
|
||||
|
||||
public int getCY0()
|
||||
{
|
||||
return this.cy0;
|
||||
}
|
||||
|
||||
public int getCX1()
|
||||
{
|
||||
return this.cx1;
|
||||
}
|
||||
|
||||
public int getCY1()
|
||||
{
|
||||
return this.cy1;
|
||||
}
|
||||
|
||||
public int getXOrigin()
|
||||
{
|
||||
return this.xOrigin;
|
||||
}
|
||||
|
||||
public int getYOrigin()
|
||||
{
|
||||
return this.yOrigin;
|
||||
}
|
||||
}
|
||||
|
||||
public static STBTTFont load(Path path)
|
||||
{
|
||||
try (var stack = MemoryStack.stackPush())
|
||||
{
|
||||
var fontInfo = STBTTFontinfo.calloc(stack);
|
||||
|
||||
var data = BufferHelper.readToByteBuffer(path);
|
||||
|
||||
if (!STBTruetype.stbtt_InitFont(fontInfo, data))
|
||||
{
|
||||
Logger.logf(SmartSeverity.ERROR, "Failed to load font: %s%n", path);
|
||||
|
||||
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,
|
||||
STBTruetype.STBTT_MS_LANG_ENGLISH,
|
||||
1);
|
||||
|
||||
if (nameBuf == null)
|
||||
{
|
||||
Logger.logf(SmartSeverity.ERROR, "Failed to load font: %s%n", path);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
var name = StandardCharsets.UTF_16BE.decode(nameBuf).toString();
|
||||
|
||||
Logger.logf(SmartSeverity.ADDED, "Loading font: %s%n", name);
|
||||
|
||||
var font = new STBTTFont(name);
|
||||
|
||||
var ascentBuf = stack.callocInt(1);
|
||||
var descentBuf = stack.callocInt(1);
|
||||
var lineGapBuf = stack.callocInt(1);
|
||||
STBTruetype.stbtt_GetFontVMetrics(fontInfo, ascentBuf, descentBuf, lineGapBuf);
|
||||
|
||||
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);
|
||||
var yOffsBuf = stack.mallocInt(1);
|
||||
|
||||
var rectPacker = STBRPContext.malloc(stack);
|
||||
var nodes = STBRPNode.calloc(SHEET_SIZE * 4);
|
||||
|
||||
STBRectPack.stbrp_init_target(rectPacker, SHEET_SIZE, SHEET_SIZE, nodes);
|
||||
STBRectPack.stbrp_setup_allow_out_of_mem(rectPacker, true);
|
||||
var rect = STBRPRect.malloc(1, stack);
|
||||
|
||||
var sheets = new ArrayList<BufferedImage>();
|
||||
|
||||
var atlas = new BufferedImage(SHEET_SIZE, SHEET_SIZE, BufferedImage.TYPE_BYTE_GRAY);
|
||||
var graphics = atlas.getGraphics();
|
||||
var colorSpace = ColorSpace.getInstance(ColorSpace.CS_GRAY);
|
||||
var colorModel = new ComponentColorModel(colorSpace, new int[] { 8 }, false,false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
|
||||
|
||||
font.atlas = font.new STBTTGlyphAtlas();
|
||||
|
||||
var advanceWidth = stack.mallocInt(1);
|
||||
var leftSideBearing = stack.mallocInt(1);
|
||||
|
||||
var cx0 = stack.mallocInt(1);
|
||||
var cy0 = stack.mallocInt(1);
|
||||
var cx1 = stack.mallocInt(1);
|
||||
var cy1 = stack.mallocInt(1);
|
||||
|
||||
for (var cp : codepoints.toArray())
|
||||
{
|
||||
var buf = STBTruetype.stbtt_GetCodepointSDF(fontInfo,
|
||||
scale,
|
||||
cp,
|
||||
SDF_PADDING,
|
||||
(byte) onedgeValue,
|
||||
pixelDistScale,
|
||||
sdfWidthBuf,
|
||||
sdfHeightBuf,
|
||||
xOffsBuf,
|
||||
yOffsBuf);
|
||||
|
||||
var width = sdfWidthBuf.get(0);
|
||||
var height = sdfHeightBuf.get(0);
|
||||
|
||||
var glyphInfo = (GlyphInfo<STBTTGlyphAtlas, STBTTGlyphMetrics>) null;
|
||||
|
||||
if (buf != null)
|
||||
{
|
||||
|
||||
rect.w(width);
|
||||
rect.h(height);
|
||||
|
||||
if (STBRectPack.stbrp_pack_rects(rectPacker, rect) == 0)
|
||||
{
|
||||
sheets.add(atlas);
|
||||
|
||||
atlas = new BufferedImage(SHEET_SIZE, SHEET_SIZE, BufferedImage.TYPE_BYTE_GRAY);
|
||||
graphics = atlas.getGraphics();
|
||||
|
||||
STBRectPack.stbrp_init_target(rectPacker, SHEET_SIZE, SHEET_SIZE, nodes);
|
||||
STBRectPack.stbrp_setup_allow_out_of_mem(rectPacker, true);
|
||||
|
||||
STBRectPack.stbrp_pack_rects(rectPacker, rect);
|
||||
}
|
||||
|
||||
var dataBuf = new DataBuffer(DataBuffer.TYPE_BYTE, width * height) {
|
||||
@Override
|
||||
public int getElem(int bank, int i)
|
||||
{
|
||||
return buf.get(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setElem(int bank, int i, int val)
|
||||
{
|
||||
buf.put(i, (byte) val);
|
||||
}
|
||||
};
|
||||
|
||||
var sampleModel = colorModel.createCompatibleSampleModel(width, height);
|
||||
var raster = new WritableRaster(sampleModel, dataBuf, new Point()) {};
|
||||
var image = new BufferedImage(colorModel, raster, false, null);
|
||||
graphics.drawImage(image, rect.x(), rect.y(), null);
|
||||
|
||||
var glyphRect = new Rectanglef(
|
||||
rect.x() / (float) SHEET_SIZE,
|
||||
rect.y() / (float) SHEET_SIZE,
|
||||
(rect.x() + rect.w()) / (float) SHEET_SIZE,
|
||||
(rect.y() + rect.h()) / (float) SHEET_SIZE);
|
||||
|
||||
glyphInfo = new GlyphInfo<>(font.atlas, sheets.size(), glyphRect);
|
||||
|
||||
STBTruetype.stbtt_FreeSDF(buf);
|
||||
}
|
||||
|
||||
STBTruetype.stbtt_GetCodepointHMetrics(fontInfo, cp, advanceWidth, leftSideBearing);
|
||||
|
||||
var glyphMetrics = font.new STBTTGlyphMetrics(cp);
|
||||
glyphMetrics.advanceX = advanceWidth.get(0);
|
||||
glyphMetrics.leftSideBearing = leftSideBearing.get(0);
|
||||
|
||||
glyphMetrics.xOrigin = xOffsBuf.get(0);
|
||||
glyphMetrics.yOrigin = yOffsBuf.get(0);
|
||||
|
||||
STBTruetype.stbtt_GetCodepointBox(fontInfo, cp, cx0, cy0, cx1, cy1);
|
||||
glyphMetrics.cx0 = cx0.get(0);
|
||||
glyphMetrics.cy0 = cy0.get(0);
|
||||
glyphMetrics.cx1 = cx1.get(0);
|
||||
glyphMetrics.cy1 = cy1.get(0);
|
||||
|
||||
font.addGlyph(cp, glyphMetrics, glyphInfo);
|
||||
}
|
||||
|
||||
if (!sheets.contains(atlas))
|
||||
sheets.add(atlas);
|
||||
|
||||
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()));
|
||||
|
||||
font.ascent = ascentBuf.get();
|
||||
font.descent = descentBuf.get();
|
||||
font.lineGap = lineGapBuf.get();
|
||||
font.lineAdvance = font.ascent - font.descent + font.lineGap;
|
||||
var tex = new SDFTextureArray();
|
||||
tex.loadImg(sheets, SHEET_SIZE, SHEET_SIZE, sheets.size(), MagFilter.LINEAR, MinFilter.LINEAR, WrapMode.MIRROR_CLAMP_TO_EDGE, WrapMode.MIRROR_CLAMP_TO_EDGE);
|
||||
font.atlas.setGlyphAtlasTexture(tex);
|
||||
|
||||
return font;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.logf(SmartSeverity.ERROR, "Failed to load font: %s%n", path);
|
||||
Logger.log(e);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
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.*;
|
||||
|
||||
public class PlutoCommandDrawMesh extends LiCommandDrawMesh
|
||||
{
|
||||
private final Map<Integer, AttributeInfo> attributeInfo;
|
||||
private final Map<Integer, Buffer> data;
|
||||
private IntBuffer indices;
|
||||
|
||||
public PlutoCommandDrawMesh()
|
||||
{
|
||||
this.attributeInfo = new TreeMap<>();
|
||||
this.data = new TreeMap<>();
|
||||
}
|
||||
|
||||
public IntBuffer getIndices()
|
||||
{
|
||||
if (this.indices == null)
|
||||
return null;
|
||||
|
||||
return this.indices;
|
||||
}
|
||||
|
||||
public Map<Integer, AttributeInfo> getAttributeInfo()
|
||||
{
|
||||
return Collections.unmodifiableMap(this.attributeInfo);
|
||||
}
|
||||
|
||||
public Map<Integer, Buffer> getData()
|
||||
{
|
||||
return Collections.unmodifiableMap(this.data);
|
||||
}
|
||||
|
||||
public void addIndices(int[] data)
|
||||
{
|
||||
if (data == null)
|
||||
return;
|
||||
|
||||
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 void addAttribute(int attrib, float[] data, int dimensions)
|
||||
{
|
||||
if (data == null)
|
||||
return;
|
||||
|
||||
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 void addAttribute(int attrib, int[] data, int dimensions)
|
||||
{
|
||||
if (data == null)
|
||||
return;
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsMerge(LiCommand other)
|
||||
{
|
||||
if (!(other instanceof PlutoCommandDrawMesh pcdm))
|
||||
return false;
|
||||
|
||||
return this.attributeInfo.equals(pcdm.attributeInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlutoCommandDrawMesh 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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package org.plutoengine.graphics.gui.command;
|
||||
|
||||
import org.plutoengine.graphics.gui.IGUIShader;
|
||||
import org.plutoengine.libra.command.impl.LiCommandSwitchShader;
|
||||
|
||||
public class PlutoCommandSwitchShader extends LiCommandSwitchShader<IGUIShader>
|
||||
{
|
||||
public PlutoCommandSwitchShader(IGUIShader shader)
|
||||
{
|
||||
super(shader);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package org.plutoengine.graphics.gui.command;
|
||||
|
||||
import org.plutoengine.graphics.texture.Texture;
|
||||
import org.plutoengine.libra.command.impl.LiCommandSwitchTexture;
|
||||
|
||||
public class PlutoCommandSwitchTexture extends LiCommandSwitchTexture<Texture>
|
||||
{
|
||||
public PlutoCommandSwitchTexture(Texture texture)
|
||||
{
|
||||
super(texture);
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
package org.plutoengine.gui.font;
|
||||
|
||||
public record CharacterInfo(int number, int leftOffset, int rightOffset)
|
||||
{
|
||||
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
package org.plutoengine.gui.font;
|
||||
|
||||
import org.plutoengine.graphics.texture.texture2d.RectangleTexture;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class Font
|
||||
{
|
||||
private String name;
|
||||
private int width;
|
||||
private int height;
|
||||
private final HashMap<Character, CharacterInfo> definitions;
|
||||
private RectangleTexture texture;
|
||||
|
||||
public Font(String name, int width, int height, HashMap<Character, CharacterInfo> def)
|
||||
{
|
||||
this.name = name;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.definitions = def;
|
||||
}
|
||||
|
||||
public String getName()
|
||||
{
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public int getTextureWidth()
|
||||
{
|
||||
return this.width;
|
||||
}
|
||||
|
||||
public int getTextureHeight()
|
||||
{
|
||||
return this.height;
|
||||
}
|
||||
|
||||
public RectangleTexture getTexture()
|
||||
{
|
||||
return this.texture;
|
||||
}
|
||||
|
||||
public void setTexture(RectangleTexture texture)
|
||||
{
|
||||
this.texture = texture;
|
||||
this.width = texture.getWidth();
|
||||
this.height = texture.getHeight();
|
||||
}
|
||||
|
||||
public HashMap<Character, CharacterInfo> getDefinitions()
|
||||
{
|
||||
return this.definitions;
|
||||
}
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
package org.plutoengine.gui.font;
|
||||
|
||||
import org.plutoengine.graphics.font.FontManager;
|
||||
|
||||
public class FontHelper
|
||||
{
|
||||
public static int calcStringWidth(Object string, String fontname, float relativeSize)
|
||||
{
|
||||
Font font = FontManager.getFontByName(fontname);
|
||||
|
||||
if (font == null)
|
||||
{
|
||||
System.out.println("Font doesn't exist: " + fontname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
float absoluteCharWidth = 16;
|
||||
String text = string.toString();
|
||||
|
||||
int maxW = 0;
|
||||
int totalSpacing = 0;
|
||||
|
||||
for (int i = 0; i < text.length(); i++)
|
||||
{
|
||||
if (text.length() > i + 1)
|
||||
{
|
||||
if (text.charAt(i) == '\\' && text.charAt(i + 1) == '&')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// &c[0xff770077]
|
||||
if (text.length() > i + 13)
|
||||
{
|
||||
if (text.charAt(i) == '&' && text.charAt(i + 1) == 'c' && text.charAt(i + 2) == '[' && text.charAt(i + 13) == ']')
|
||||
{
|
||||
char c = '0';
|
||||
char cBef = '0';
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
c = text.charAt(i - 1);
|
||||
}
|
||||
|
||||
if (i > 1)
|
||||
{
|
||||
cBef = text.charAt(i - 2);
|
||||
}
|
||||
|
||||
if (c != '\\' || cBef == '\\' && c == '\\')
|
||||
{
|
||||
i += 13;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (text.length() > i + 2)
|
||||
{
|
||||
if (text.charAt(i) == '&' && text.charAt(i + 1) == 'i')
|
||||
{
|
||||
char c = '0';
|
||||
char cBef = '0';
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
c = text.charAt(i - 1);
|
||||
}
|
||||
|
||||
if (i > 1)
|
||||
{
|
||||
cBef = text.charAt(i - 2);
|
||||
}
|
||||
|
||||
if (c != '\\' || cBef == '\\' && c == '\\')
|
||||
{
|
||||
i += 2;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (text.charAt(i) == '\n')
|
||||
{
|
||||
totalSpacing = 0;
|
||||
}
|
||||
else if (text.charAt(i) == ' ')
|
||||
{
|
||||
totalSpacing += 12 * relativeSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
CharacterInfo charInf = font.getDefinitions().get(text.charAt(i));
|
||||
|
||||
if (charInf == null)
|
||||
{
|
||||
charInf = font.getDefinitions().get('?');
|
||||
}
|
||||
|
||||
totalSpacing -= charInf.leftOffset() * relativeSize - relativeSize;
|
||||
totalSpacing += absoluteCharWidth * relativeSize;
|
||||
totalSpacing -= charInf.rightOffset() * relativeSize - 1;
|
||||
}
|
||||
|
||||
maxW = Math.max(maxW, totalSpacing);
|
||||
}
|
||||
|
||||
return maxW;
|
||||
}
|
||||
|
||||
public static int calcStringHeight(Object string, float relSize)
|
||||
{
|
||||
|
||||
return (int) (30f * relSize * string.toString().split("\n").length);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,287 +0,0 @@
|
|||
package org.plutoengine.gui.font;
|
||||
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3f;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import org.plutoengine.graphics.font.FontManager;
|
||||
import org.plutoengine.graphics.font.FontShader;
|
||||
import org.plutoengine.graphics.gl.DrawMode;
|
||||
import org.plutoengine.graphics.gl.vao.QuadPresets;
|
||||
import org.plutoengine.graphics.gl.vao.VertexArray;
|
||||
import org.plutoengine.math.TransformationMatrix;
|
||||
|
||||
public class FontRenderer
|
||||
{
|
||||
private static final int LINE_HEIGHT = 30;
|
||||
private static final int SPACE_WIDTH = 12;
|
||||
|
||||
private static final int CHAR_WIDTH = 16;
|
||||
private static final int CHAR_HEIGHT = 24;
|
||||
|
||||
private static VertexArray charVAO;
|
||||
private static FontShader fontShader;
|
||||
|
||||
public static void load(FontShader defaultFontShaderIn)
|
||||
{
|
||||
charVAO = QuadPresets.basicNoNeg();
|
||||
fontShader = defaultFontShaderIn;
|
||||
}
|
||||
|
||||
public static void unload()
|
||||
{
|
||||
if (charVAO != null)
|
||||
{
|
||||
charVAO.delete();
|
||||
}
|
||||
}
|
||||
|
||||
public static void prepareInstance(Font font)
|
||||
{
|
||||
GL11.glEnable(GL11.GL_BLEND);
|
||||
fontShader.start();
|
||||
charVAO.bind();
|
||||
charVAO.enableAllAttributes();
|
||||
font.getTexture().bind();
|
||||
}
|
||||
|
||||
public static void drawString(float xPos, float yPos, Object string, Object color, float relativeSize, String fontname, boolean isShadow)
|
||||
{
|
||||
Font font = FontManager.getFontByName(fontname);
|
||||
|
||||
if (font == null)
|
||||
{
|
||||
System.err.println("Font doesn't exist: " + fontname);
|
||||
return;
|
||||
}
|
||||
|
||||
float charWidth = CHAR_WIDTH * relativeSize;
|
||||
float charHeight = CHAR_HEIGHT * relativeSize;
|
||||
|
||||
float lineHeight = LINE_HEIGHT * relativeSize;
|
||||
float spaceWidth = SPACE_WIDTH * relativeSize;
|
||||
|
||||
prepareInstance(font);
|
||||
|
||||
color(color);
|
||||
|
||||
String text = String.valueOf(string);
|
||||
|
||||
float drawX = xPos;
|
||||
float drawY = yPos;
|
||||
|
||||
for (int characterIndex = 0; characterIndex < text.length(); characterIndex++)
|
||||
{
|
||||
int column;
|
||||
int row;
|
||||
|
||||
var currentChar = text.charAt(characterIndex);
|
||||
|
||||
if (text.length() > characterIndex + 1)
|
||||
{
|
||||
var nextChar = text.charAt(characterIndex + 1);
|
||||
|
||||
if (currentChar == '\\' && nextChar == '&')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Inline coloring (tm) :) -> &c[0xff770077]
|
||||
if (text.length() > characterIndex + 13)
|
||||
{
|
||||
if (currentChar == '&' && nextChar == 'c' && text.charAt(characterIndex + 2) == '[' && text.charAt(characterIndex + 13) == ']')
|
||||
{
|
||||
char c = '0';
|
||||
char cBef = '0';
|
||||
|
||||
if (characterIndex > 0)
|
||||
{
|
||||
c = text.charAt(characterIndex - 1);
|
||||
}
|
||||
|
||||
if (characterIndex > 1)
|
||||
{
|
||||
cBef = text.charAt(characterIndex - 2);
|
||||
}
|
||||
|
||||
if (c != '\\' || cBef == '\\')
|
||||
{
|
||||
if (!isShadow)
|
||||
{
|
||||
color(text.substring(characterIndex + 3, characterIndex + 13));
|
||||
}
|
||||
|
||||
characterIndex += 13;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (text.length() > characterIndex + 2)
|
||||
{
|
||||
if (currentChar == '&' && nextChar == 'i' && (text.charAt(characterIndex + 2) == '1' || text.charAt(characterIndex + 2) == '0'))
|
||||
{
|
||||
char c = '0';
|
||||
char cBef = '0';
|
||||
|
||||
if (characterIndex > 0)
|
||||
{
|
||||
c = text.charAt(characterIndex - 1);
|
||||
}
|
||||
|
||||
if (characterIndex > 1)
|
||||
{
|
||||
cBef = text.charAt(characterIndex - 2);
|
||||
}
|
||||
|
||||
if (c != '\\' || cBef == '\\')
|
||||
{
|
||||
italic(text.charAt(characterIndex + 2) == '1');
|
||||
|
||||
characterIndex += 2;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float shift = 0;
|
||||
|
||||
switch (currentChar)
|
||||
{
|
||||
case '\n' -> {
|
||||
color(color);
|
||||
drawX = xPos;
|
||||
drawY += lineHeight;
|
||||
continue;
|
||||
}
|
||||
case ' ' -> {
|
||||
drawX += spaceWidth;
|
||||
continue;
|
||||
}
|
||||
case 'g', 'y', 'p', 'j' -> shift = 6 * relativeSize;
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
|
||||
var fontDefs = font.getDefinitions();
|
||||
var charInf = fontDefs.get(currentChar);
|
||||
|
||||
if (charInf == null)
|
||||
{
|
||||
charInf = fontDefs.get('?');
|
||||
}
|
||||
|
||||
var atlasIndex = charInf.number();
|
||||
|
||||
row = atlasIndex / CHAR_WIDTH;
|
||||
column = atlasIndex % CHAR_WIDTH;
|
||||
|
||||
// Position of the current character in the texture atlas in pixels
|
||||
float u = column * CHAR_WIDTH;
|
||||
float v = row * CHAR_HEIGHT;
|
||||
|
||||
// Offset from the left
|
||||
drawX -= charInf.leftOffset() * relativeSize;
|
||||
|
||||
float posY = shift + drawY;
|
||||
|
||||
fontShader.uvBase.load(u, font.getTextureHeight() - v - CHAR_HEIGHT);
|
||||
fontShader.uvDelta.load(CHAR_WIDTH, CHAR_HEIGHT);
|
||||
|
||||
Matrix4f transformation = TransformationMatrix.create(new Vector3f(drawX, posY, 0), new Vector3f(0, 0, 0), new Vector3f(charWidth, charHeight, 0));
|
||||
|
||||
fontShader.transformationMatrix.load(transformation);
|
||||
|
||||
charVAO.draw(DrawMode.TRIANGLES);
|
||||
|
||||
drawX += charWidth;
|
||||
drawX -= charInf.rightOffset() * relativeSize;
|
||||
drawX += relativeSize;
|
||||
}
|
||||
|
||||
italic(false);
|
||||
}
|
||||
|
||||
public static void color(Object color)
|
||||
{
|
||||
color(color, false);
|
||||
}
|
||||
|
||||
public static void color(Object color, boolean darker)
|
||||
{
|
||||
float dark = 0;
|
||||
|
||||
if (darker)
|
||||
{
|
||||
dark = 0.35f;
|
||||
}
|
||||
|
||||
if (color instanceof float[] c)
|
||||
{
|
||||
if (c.length == 4)
|
||||
{
|
||||
recolor(c[0] - dark, c[1] - dark, c[2] - dark, c[3]);
|
||||
}
|
||||
else if (c.length == 3)
|
||||
{
|
||||
recolor(c[0] - dark, c[1] - dark, c[2] - dark, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (color instanceof String col)
|
||||
{
|
||||
if (col.length() == 7)
|
||||
{
|
||||
recolor(Integer.valueOf(col.substring(1, 3), 16) / 256f - dark, Integer.valueOf(col.substring(3, 5), 16) / 256f - dark, Integer.valueOf(col.substring(5, 7), 16) / 256f - dark, 1);
|
||||
}
|
||||
|
||||
if (col.length() == 10)
|
||||
{
|
||||
recolor(Integer.valueOf(col.substring(4, 6), 16) / 256f - dark, Integer.valueOf(col.substring(6, 8), 16) / 256f - dark, Integer.valueOf(col.substring(8, 10), 16) / 256f - dark, Integer.valueOf(col.substring(2, 4), 16) / 256f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void recolor(float r, float g, float b, float a)
|
||||
{
|
||||
fontShader.recolor.load(r, g, b, a);
|
||||
}
|
||||
|
||||
private static void italic(boolean useItalic)
|
||||
{
|
||||
fontShader.italic.load(useItalic);
|
||||
}
|
||||
|
||||
public static void drawString(float x, float y, Object text, float r, float g, float b, float a, float size)
|
||||
{
|
||||
drawString(x, y, text, new float[] { r, g, b, a }, size, "default", false);
|
||||
}
|
||||
|
||||
public static void drawString(float x, float y, Object text)
|
||||
{
|
||||
drawString(x, y, text, new float[] { 0, 0, 0, 1 }, 1, "default", false);
|
||||
}
|
||||
|
||||
public static void drawString(float x, float y, Object text, float r, float g, float b, float a, float size, boolean isShadow)
|
||||
{
|
||||
drawString(x, y, text, new float[] { r, g, b, a }, size, "default", isShadow);
|
||||
}
|
||||
|
||||
public static void drawString(float x, float y, Object text, float r, float g, float b, float a, float size, String fontname, boolean isShadow)
|
||||
{
|
||||
drawString(x, y, text, new float[] { r, g, b, a }, size, fontname, isShadow);
|
||||
}
|
||||
|
||||
public static void drawString(float x, float y, Object text, float r, float g, float b, float a, float size, String fontname)
|
||||
{
|
||||
drawString(x, y, text, new float[] { r, g, b, a }, size, fontname, false);
|
||||
}
|
||||
|
||||
public static void drawString(float xPos, float yPos, Object string, Object color, float relativeSize, String fontname)
|
||||
{
|
||||
drawString(xPos, yPos, string, color, relativeSize, fontname, false);
|
||||
}
|
||||
}
|
|
@ -18,6 +18,8 @@ dependencies {
|
|||
api("com.google.guava", "guava", "28.0-jre")
|
||||
|
||||
api("org.joml", "joml", Versions.jomlVersion)
|
||||
api("org.joml", "joml-primitives", Versions.jomlPrimitivesVersion)
|
||||
|
||||
api("commons-io", "commons-io", "2.6")
|
||||
|
||||
api("org.apache.commons", "commons-lang3", "3.12.0")
|
||||
|
|
|
@ -2,7 +2,7 @@ package org.plutoengine.event.lambda;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* A simple functional interface based event factory for objects basically
|
||||
|
@ -25,7 +25,7 @@ public class LambdaEventFactory
|
|||
*/
|
||||
public static class LambdaEvent<T>
|
||||
{
|
||||
private final List<Consumer<T>> consumers;
|
||||
private final List<Predicate<T>> consumers;
|
||||
|
||||
private LambdaEvent()
|
||||
{
|
||||
|
@ -42,7 +42,7 @@ public class LambdaEventFactory
|
|||
*
|
||||
* @author 493msi
|
||||
*/
|
||||
public void addListener(Consumer<T> callback)
|
||||
public void addListener(Predicate<T> callback)
|
||||
{
|
||||
this.consumers.add(callback);
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ public class LambdaEventFactory
|
|||
*
|
||||
* @author 493msi
|
||||
*/
|
||||
public void removeListener(Consumer<T> callback)
|
||||
public void removeListener(Predicate<T> callback)
|
||||
{
|
||||
this.consumers.remove(callback);
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ public class LambdaEventFactory
|
|||
*/
|
||||
public void fire(T value)
|
||||
{
|
||||
this.consumers.forEach(c -> c.accept(value));
|
||||
this.consumers.removeIf(c -> !c.test(value));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package org.plutoengine.graphics.gl.vao;
|
||||
|
||||
import org.plutoengine.graphics.gl.vao.attrib.data.VecArray;
|
||||
|
||||
/**
|
||||
* @author 493msi
|
||||
*
|
||||
|
@ -10,48 +8,85 @@ public class QuadPresets
|
|||
{
|
||||
public static VertexArray basicQuad()
|
||||
{
|
||||
float[] uvs = { 0, 0, 1, 0, 1, 1, 0, 1 };
|
||||
float[] uvs = {
|
||||
0, 0,
|
||||
1, 0,
|
||||
1, 1,
|
||||
0, 1 };
|
||||
|
||||
float[] positions = { -1, 1, 1, 1, 1, -1, -1, -1 };
|
||||
float[] positions = {
|
||||
-1, 1,
|
||||
1, 1,
|
||||
1, -1,
|
||||
-1, -1 };
|
||||
|
||||
int[] indices = { 0, 1, 2, 0, 2, 3 };
|
||||
int[] indices = {
|
||||
0, 1, 2,
|
||||
0, 2, 3
|
||||
};
|
||||
|
||||
VertexArrayBuilder vab = new VertexArrayBuilder();
|
||||
vab.vertices(new VecArray<>(positions, 2));
|
||||
vab.uvs(new VecArray<>(uvs, 2));
|
||||
vab.indices(new VecArray<>(indices, 1));
|
||||
vab.vertices(positions, 2);
|
||||
vab.uvs(uvs, 2);
|
||||
vab.indices(indices);
|
||||
|
||||
return vab.export();
|
||||
return vab.build();
|
||||
}
|
||||
|
||||
public static VertexArray halvedSize()
|
||||
{
|
||||
float[] uvs = { 0, 0, 1, 0, 1, 1, 0, 1 };
|
||||
float[] uvs = {
|
||||
0, 0,
|
||||
1, 0,
|
||||
1, 1,
|
||||
0, 1
|
||||
};
|
||||
|
||||
float[] positions = { -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, -0.5f, -0.5f, -0.5f };
|
||||
float[] positions = {
|
||||
-0.5f, 0.5f,
|
||||
0.5f, 0.5f,
|
||||
0.5f, -0.5f,
|
||||
-0.5f, -0.5f
|
||||
};
|
||||
|
||||
int[] indices = { 0, 1, 2, 0, 2, 3 };
|
||||
int[] indices = {
|
||||
0, 1, 2,
|
||||
0, 2, 3
|
||||
};
|
||||
|
||||
VertexArrayBuilder vab = new VertexArrayBuilder();
|
||||
vab.vertices(new VecArray<>(positions, 2));
|
||||
vab.uvs(new VecArray<>(uvs, 2));
|
||||
vab.indices(new VecArray<>(indices, 1));
|
||||
return vab.export();
|
||||
vab.vertices(positions, 2);
|
||||
vab.uvs(uvs, 2);
|
||||
vab.indices(indices);
|
||||
return vab.build();
|
||||
}
|
||||
|
||||
public static VertexArray basicNoNeg()
|
||||
{
|
||||
float[] uvs = { 0, 0, 1, 0, 1, 1, 0, 1 };
|
||||
float[] uvs = {
|
||||
0, 0,
|
||||
1, 0,
|
||||
1, 1,
|
||||
0, 1
|
||||
};
|
||||
|
||||
float[] positions = { 0, 1, 1, 1, 1, 0, 0, 0 };
|
||||
float[] positions = {
|
||||
0, 1,
|
||||
1, 1,
|
||||
1, 0,
|
||||
0, 0
|
||||
};
|
||||
|
||||
int[] indices = { 0, 1, 2, 0, 2, 3 };
|
||||
int[] indices = {
|
||||
0, 1, 2,
|
||||
0, 2, 3
|
||||
};
|
||||
|
||||
VertexArrayBuilder vab = new VertexArrayBuilder();
|
||||
vab.vertices(new VecArray<>(positions, 2));
|
||||
vab.uvs(new VecArray<>(uvs, 2));
|
||||
vab.indices(new VecArray<>(indices, 1));
|
||||
vab.vertices(positions, 2);
|
||||
vab.uvs(uvs, 2);
|
||||
vab.indices(indices);
|
||||
|
||||
return vab.export();
|
||||
return vab.build();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,18 +3,16 @@ package org.plutoengine.graphics.gl.vao;
|
|||
import org.lwjgl.opengl.GL33;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
import org.plutoengine.graphics.gl.DrawMode;
|
||||
import org.plutoengine.graphics.gl.vao.attrib.AttributeInfo;
|
||||
import org.plutoengine.graphics.gl.vbo.ArrayBuffer;
|
||||
import org.plutoengine.graphics.gl.vbo.IndexArrayBuffer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
import java.util.*;
|
||||
|
||||
import org.plutoengine.logger.Logger;
|
||||
import org.plutoengine.logger.SmartSeverity;
|
||||
|
||||
public class VertexArray
|
||||
public class VertexArray implements AutoCloseable
|
||||
{
|
||||
protected final List<Integer> usedAttributes;
|
||||
protected final Vector<ArrayBuffer<?>> vertexAttributes;
|
||||
|
@ -34,22 +32,29 @@ public class VertexArray
|
|||
|
||||
this.glID = GL33.glGenVertexArrays();
|
||||
|
||||
Logger.logf(SmartSeverity.ADDED, "Vertex array ID %d created...\n", this.glID);
|
||||
Logger.logf(SmartSeverity.ADDED, "Vertex array ID %d created...%n", this.glID);
|
||||
}
|
||||
|
||||
public void createArrayAttribute(ArrayBuffer<?> buffer, int attribID)
|
||||
public void createArrayAttribute(AttributeInfo info, ArrayBuffer<?> buffer)
|
||||
{
|
||||
var attribID = info.position();
|
||||
var dimensions = info.dimensions();
|
||||
var type = buffer.getDataType();
|
||||
|
||||
this.bind();
|
||||
buffer.bind();
|
||||
GL33.glVertexAttribPointer(attribID, buffer.getVertexDimensions(), buffer.getType().getGLID(), false, 0, 0);
|
||||
GL33.glVertexAttribPointer(attribID, dimensions, type.getGLID(), false, 0, 0);
|
||||
|
||||
this.vertexAttributes.set(attribID, buffer);
|
||||
this.usedAttributes.add(attribID);
|
||||
|
||||
if (!this.hasIndices())
|
||||
{
|
||||
this.vertexCount = buffer.getVertexCount();
|
||||
this.vertexCount = buffer.getSize() / dimensions;
|
||||
}
|
||||
|
||||
public Set<Integer> getUsedAttributes()
|
||||
{
|
||||
return Set.copyOf(this.usedAttributes);
|
||||
}
|
||||
|
||||
public List<ArrayBuffer<?>> getVertexAttributes()
|
||||
|
@ -64,7 +69,7 @@ public class VertexArray
|
|||
|
||||
public void enableAllAttributes()
|
||||
{
|
||||
this.usedAttributes.forEach(GL33::glEnableVertexAttribArray);
|
||||
this.usedAttributes.forEach(VertexArray::enableAttribute);
|
||||
}
|
||||
|
||||
public void bindIndices(IndexArrayBuffer buffer)
|
||||
|
@ -72,7 +77,7 @@ public class VertexArray
|
|||
this.bind();
|
||||
buffer.bind();
|
||||
this.indices = buffer;
|
||||
this.vertexCount = buffer.getVertexCount();
|
||||
this.vertexCount = buffer.getSize();
|
||||
}
|
||||
|
||||
public void bind()
|
||||
|
@ -89,7 +94,7 @@ public class VertexArray
|
|||
{
|
||||
if (this.hasIndices())
|
||||
{
|
||||
GL33.glDrawElements(mode.getGLID(), this.vertexCount, this.indices.getType().getGLID(), MemoryUtil.NULL);
|
||||
GL33.glDrawElements(mode.getGLID(), this.vertexCount, this.indices.getDataType().getGLID(), MemoryUtil.NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -101,7 +106,7 @@ public class VertexArray
|
|||
{
|
||||
if (this.hasIndices())
|
||||
{
|
||||
GL33.glDrawElementsInstanced(mode.getGLID(), this.vertexCount, this.indices.getType().getGLID(), MemoryUtil.NULL, count);
|
||||
GL33.glDrawElementsInstanced(mode.getGLID(), this.vertexCount, this.indices.getDataType().getGLID(), MemoryUtil.NULL, count);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -119,15 +124,17 @@ public class VertexArray
|
|||
return this.indices != null;
|
||||
}
|
||||
|
||||
public void delete()
|
||||
public void close()
|
||||
{
|
||||
this.usedAttributes.stream().map(this.vertexAttributes::get).forEach(ArrayBuffer::delete);
|
||||
this.usedAttributes.stream()
|
||||
.map(this.vertexAttributes::get)
|
||||
.forEach(ArrayBuffer::close);
|
||||
this.vertexAttributes.clear();
|
||||
this.usedAttributes.clear();
|
||||
|
||||
if (this.indices != null)
|
||||
{
|
||||
this.indices.delete();
|
||||
this.indices.close();
|
||||
this.indices = null;
|
||||
}
|
||||
|
||||
|
@ -142,4 +149,14 @@ public class VertexArray
|
|||
{
|
||||
return this.glID;
|
||||
}
|
||||
|
||||
public static void enableAttribute(int attribute)
|
||||
{
|
||||
GL33.glEnableVertexAttribArray(attribute);
|
||||
}
|
||||
|
||||
public static void disableAttribute(int attribute)
|
||||
{
|
||||
GL33.glDisableVertexAttribArray(attribute);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
package org.plutoengine.graphics.gl.vao;
|
||||
|
||||
import org.plutoengine.graphics.gl.vao.attrib.AttributeInfo;
|
||||
import org.plutoengine.graphics.gl.vao.attrib.ReservedAttributes;
|
||||
import org.plutoengine.graphics.gl.vao.attrib.data.VecArray;
|
||||
import org.plutoengine.graphics.gl.vbo.FloatArrayBuffer;
|
||||
import org.plutoengine.graphics.gl.vbo.IndexArrayBuffer;
|
||||
import org.plutoengine.graphics.gl.vbo.*;
|
||||
|
||||
import java.nio.Buffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
public class VertexArrayBuilder
|
||||
{
|
||||
|
@ -14,28 +17,68 @@ public class VertexArrayBuilder
|
|||
this.va = new VertexArray();
|
||||
}
|
||||
|
||||
public VertexArrayBuilder vertices(VecArray<float[]> vertices)
|
||||
public VertexArrayBuilder vertices(FloatBuffer vertices, int dimensions)
|
||||
{
|
||||
this.va.createArrayAttribute(new FloatArrayBuffer(vertices), ReservedAttributes.POSITION);
|
||||
return this.attrib(ReservedAttributes.POSITION, dimensions, vertices);
|
||||
}
|
||||
|
||||
public VertexArrayBuilder uvs(FloatBuffer uvs, int dimensions)
|
||||
{
|
||||
return this.attrib(ReservedAttributes.UV, dimensions, uvs);
|
||||
}
|
||||
|
||||
public VertexArrayBuilder colors(FloatBuffer colors, int dimensions)
|
||||
{
|
||||
return this.attrib(ReservedAttributes.COLOR, dimensions, colors);
|
||||
}
|
||||
|
||||
public VertexArrayBuilder vertices(float[] vertices, int dimensions)
|
||||
{
|
||||
return this.vertices(FloatBuffer.wrap(vertices), dimensions);
|
||||
}
|
||||
|
||||
public VertexArrayBuilder uvs(float[] uvs, int dimensions)
|
||||
{
|
||||
return this.uvs(FloatBuffer.wrap(uvs), dimensions);
|
||||
}
|
||||
|
||||
public VertexArrayBuilder colors(float[] colors, int dimensions)
|
||||
{
|
||||
return this.colors(FloatBuffer.wrap(colors), dimensions);
|
||||
}
|
||||
|
||||
public VertexArrayBuilder attrib(int attribute, int dimensions, Buffer data)
|
||||
{
|
||||
if (data instanceof FloatBuffer fab)
|
||||
{
|
||||
var attrInfo = new AttributeInfo(EnumArrayBufferType.FLOAT, attribute, dimensions);
|
||||
var attr = FloatArrayBuffer.from(fab);
|
||||
this.va.createArrayAttribute(attrInfo, attr);
|
||||
}
|
||||
else if (data instanceof IntBuffer iab)
|
||||
{
|
||||
var attrInfo = new AttributeInfo(EnumArrayBufferType.INT, attribute, dimensions);
|
||||
var attr = IntArrayBuffer.from(iab);
|
||||
this.va.createArrayAttribute(attrInfo, attr);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public VertexArrayBuilder uvs(VecArray<float[]> uvs)
|
||||
public VertexArrayBuilder indices(int[] indices)
|
||||
{
|
||||
this.va.createArrayAttribute(new FloatArrayBuffer(uvs), ReservedAttributes.UV);
|
||||
return this.indices(IntBuffer.wrap(indices));
|
||||
}
|
||||
|
||||
public VertexArrayBuilder indices(IntBuffer indices)
|
||||
{
|
||||
var data = IndexArrayBuffer.from(indices);
|
||||
this.va.bindIndices(data);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public VertexArrayBuilder indices(VecArray<int[]> indices)
|
||||
{
|
||||
this.va.bindIndices(new IndexArrayBuffer(indices));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public VertexArray export()
|
||||
public VertexArray build()
|
||||
{
|
||||
return this.va;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package org.plutoengine.graphics.gl.vao.attrib;
|
||||
|
||||
import org.intellij.lang.annotations.MagicConstant;
|
||||
import org.plutoengine.graphics.gl.vbo.EnumArrayBufferType;
|
||||
|
||||
public record AttributeInfo(
|
||||
EnumArrayBufferType type,
|
||||
@MagicConstant(valuesFromClass = ReservedAttributes.class) int position,
|
||||
int dimensions
|
||||
)
|
||||
{
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
package org.plutoengine.graphics.gl.vao.attrib.data;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
|
||||
public class VecArray<T>
|
||||
{
|
||||
private final T data;
|
||||
private final int vecDimensions;
|
||||
private final int vertexCount;
|
||||
|
||||
public VecArray(T data, int vecDimensions)
|
||||
{
|
||||
var dataType = data.getClass();
|
||||
|
||||
if (!dataType.isArray())
|
||||
{
|
||||
throw new IllegalStateException("Input data must be of an array type!");
|
||||
}
|
||||
|
||||
this.data = data;
|
||||
this.vecDimensions = vecDimensions;
|
||||
var dataLength = Array.getLength(data);
|
||||
|
||||
if (dataLength % vecDimensions != 0)
|
||||
{
|
||||
throw new IllegalArgumentException("Data size must be divisible by the amount of vector dimensions!");
|
||||
}
|
||||
|
||||
this.vertexCount = dataLength / vecDimensions;
|
||||
}
|
||||
|
||||
public T getData()
|
||||
{
|
||||
return this.data;
|
||||
}
|
||||
|
||||
public int getVecDimensions()
|
||||
{
|
||||
return this.vecDimensions;
|
||||
}
|
||||
|
||||
public int getVertexCount()
|
||||
{
|
||||
return this.vertexCount;
|
||||
}
|
||||
}
|
|
@ -1,43 +1,45 @@
|
|||
package org.plutoengine.graphics.gl.vbo;
|
||||
|
||||
import org.plutoengine.graphics.gl.vao.attrib.data.VecArray;
|
||||
import org.lwjgl.opengl.GL33;
|
||||
|
||||
import static org.lwjgl.opengl.GL15.*;
|
||||
import java.nio.Buffer;
|
||||
|
||||
public abstract class ArrayBuffer<T extends VecArray<?>>
|
||||
public sealed abstract class ArrayBuffer<T extends Buffer> implements AutoCloseable permits FloatArrayBuffer, IndexArrayBuffer, IntArrayBuffer
|
||||
{
|
||||
protected int glID;
|
||||
|
||||
private final int vertexDimensions;
|
||||
private final int vertexCount;
|
||||
protected final int type;
|
||||
private final int size;
|
||||
|
||||
public ArrayBuffer(T data)
|
||||
protected ArrayBuffer(int type, int size)
|
||||
{
|
||||
this.glID = glGenBuffers();
|
||||
this.bind();
|
||||
this.bindData(data);
|
||||
|
||||
this.vertexDimensions = data.getVecDimensions();
|
||||
this.vertexCount = data.getVertexCount();
|
||||
this.glID = GL33.glGenBuffers();
|
||||
this.type = type;
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public abstract EnumArrayBufferType getType();
|
||||
public abstract EnumArrayBufferType getDataType();
|
||||
|
||||
protected abstract void bindData(T data);
|
||||
public abstract void writePartialData(T data, long offset);
|
||||
|
||||
public int getSize()
|
||||
{
|
||||
return this.size;
|
||||
}
|
||||
|
||||
public void bind()
|
||||
{
|
||||
glBindBuffer(GL_ARRAY_BUFFER, this.glID);
|
||||
GL33.glBindBuffer(this.type, this.glID);
|
||||
}
|
||||
|
||||
public void unbind()
|
||||
{
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
GL33.glBindBuffer(this.type, 0);
|
||||
}
|
||||
|
||||
public void delete()
|
||||
public void close()
|
||||
{
|
||||
glDeleteBuffers(this.glID);
|
||||
GL33.glDeleteBuffers(this.glID);
|
||||
|
||||
this.glID = 0;
|
||||
}
|
||||
|
@ -46,14 +48,4 @@ public abstract class ArrayBuffer<T extends VecArray<?>>
|
|||
{
|
||||
return this.glID;
|
||||
}
|
||||
|
||||
public int getVertexDimensions()
|
||||
{
|
||||
return this.vertexDimensions;
|
||||
}
|
||||
|
||||
public int getVertexCount()
|
||||
{
|
||||
return this.vertexCount;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,24 +1,60 @@
|
|||
package org.plutoengine.graphics.gl.vbo;
|
||||
|
||||
import org.lwjgl.opengl.GL33;
|
||||
import org.plutoengine.graphics.gl.vao.attrib.data.VecArray;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
public class FloatArrayBuffer extends ArrayBuffer<VecArray<float[]>>
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
public final class FloatArrayBuffer extends ArrayBuffer<FloatBuffer>
|
||||
{
|
||||
public FloatArrayBuffer(VecArray<float[]> data)
|
||||
private FloatArrayBuffer(int size)
|
||||
{
|
||||
super(data);
|
||||
super(GL33.GL_ARRAY_BUFFER, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void bindData(VecArray<float[]> vertexData)
|
||||
public void writePartialData(FloatBuffer data, long offset)
|
||||
{
|
||||
GL33.glBufferData(GL33.GL_ARRAY_BUFFER, vertexData.getData(), GL33.GL_STATIC_DRAW);
|
||||
GL33.glBufferSubData(this.type, offset, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumArrayBufferType getType()
|
||||
public EnumArrayBufferType getDataType()
|
||||
{
|
||||
return EnumArrayBufferType.FLOAT;
|
||||
}
|
||||
|
||||
public static FloatArrayBuffer from(FloatBuffer data)
|
||||
{
|
||||
if (!data.isDirect())
|
||||
{
|
||||
var fb = MemoryUtil.memAllocFloat(data.remaining());
|
||||
fb.put(data);
|
||||
fb.flip();
|
||||
var fab = from(fb);
|
||||
MemoryUtil.memFree(fb);
|
||||
return fab;
|
||||
}
|
||||
|
||||
var fab = new FloatArrayBuffer(data.remaining());
|
||||
fab.bind();
|
||||
GL33.glBufferData(fab.type, data, GL33.GL_STATIC_DRAW);
|
||||
return fab;
|
||||
}
|
||||
|
||||
public static FloatArrayBuffer from(float[] data)
|
||||
{
|
||||
var fab = new FloatArrayBuffer(data.length);
|
||||
fab.bind();
|
||||
GL33.glBufferData(fab.type, data, GL33.GL_STATIC_DRAW);
|
||||
return fab;
|
||||
}
|
||||
|
||||
public static FloatArrayBuffer empty(int size)
|
||||
{
|
||||
var fab = new FloatArrayBuffer(size);
|
||||
fab.bind();
|
||||
GL33.glBufferData(fab.type, size, GL33.GL_STATIC_DRAW);
|
||||
return fab;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,50 +1,60 @@
|
|||
package org.plutoengine.graphics.gl.vbo;
|
||||
|
||||
import static org.lwjgl.opengl.GL15.GL_ELEMENT_ARRAY_BUFFER;
|
||||
import static org.lwjgl.opengl.GL15.GL_STATIC_DRAW;
|
||||
import static org.lwjgl.opengl.GL15.glBindBuffer;
|
||||
import static org.lwjgl.opengl.GL15.glBufferData;
|
||||
import org.lwjgl.opengl.GL33;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import org.plutoengine.graphics.gl.vao.attrib.data.VecArray;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
public class IndexArrayBuffer extends ArrayBuffer<VecArray<int[]>>
|
||||
public final class IndexArrayBuffer extends ArrayBuffer<IntBuffer>
|
||||
{
|
||||
public IndexArrayBuffer(int[] data)
|
||||
private IndexArrayBuffer(int size)
|
||||
{
|
||||
super(new VecArray<>(data, 1));
|
||||
}
|
||||
|
||||
public IndexArrayBuffer(VecArray<int[]> data)
|
||||
{
|
||||
super(data);
|
||||
|
||||
if (data.getVecDimensions() != 1)
|
||||
{
|
||||
throw new IllegalArgumentException("Index buffers must have exactly one vertex dimension!");
|
||||
}
|
||||
super(GL33.GL_ELEMENT_ARRAY_BUFFER, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind()
|
||||
public void writePartialData(IntBuffer data, long offset)
|
||||
{
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this.glID);
|
||||
GL33.glBufferSubData(this.type, offset, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void bindData(VecArray<int[]> data)
|
||||
{
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, data.getData(), GL_STATIC_DRAW);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unbind()
|
||||
{
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumArrayBufferType getType()
|
||||
public EnumArrayBufferType getDataType()
|
||||
{
|
||||
return EnumArrayBufferType.UNSIGNED_INT;
|
||||
}
|
||||
|
||||
public static IndexArrayBuffer from(IntBuffer data)
|
||||
{
|
||||
if (!data.isDirect())
|
||||
{
|
||||
var ib = MemoryUtil.memAllocInt(data.remaining());
|
||||
ib.put(data);
|
||||
ib.flip();
|
||||
var iab = from(ib);
|
||||
MemoryUtil.memFree(ib);
|
||||
return iab;
|
||||
}
|
||||
|
||||
var iab = new IndexArrayBuffer(data.remaining());
|
||||
iab.bind();
|
||||
GL33.glBufferData(iab.type, data, GL33.GL_STATIC_DRAW);
|
||||
return iab;
|
||||
}
|
||||
|
||||
public static IndexArrayBuffer from(int[] data)
|
||||
{
|
||||
var iab = new IndexArrayBuffer(data.length);
|
||||
iab.bind();
|
||||
GL33.glBufferData(iab.type, data, GL33.GL_STATIC_DRAW);
|
||||
return iab;
|
||||
}
|
||||
|
||||
public static IndexArrayBuffer empty(int size)
|
||||
{
|
||||
var iab = new IndexArrayBuffer(size);
|
||||
iab.bind();
|
||||
GL33.glBufferData(iab.type, size, GL33.GL_DYNAMIC_DRAW);
|
||||
return iab;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
package org.plutoengine.graphics.gl.vbo;
|
||||
|
||||
import org.lwjgl.opengl.GL33;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
public non-sealed class IntArrayBuffer extends ArrayBuffer<IntBuffer>
|
||||
{
|
||||
private IntArrayBuffer(int size)
|
||||
{
|
||||
super(GL33.GL_ARRAY_BUFFER, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writePartialData(IntBuffer data, long offset)
|
||||
{
|
||||
GL33.glBufferSubData(this.type, offset, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumArrayBufferType getDataType()
|
||||
{
|
||||
return EnumArrayBufferType.INT;
|
||||
}
|
||||
|
||||
public static IntArrayBuffer from(IntBuffer data)
|
||||
{
|
||||
if (!data.isDirect())
|
||||
{
|
||||
var ib = MemoryUtil.memAllocInt(data.remaining());
|
||||
ib.put(data);
|
||||
ib.flip();
|
||||
var iab = from(ib);
|
||||
MemoryUtil.memFree(ib);
|
||||
return iab;
|
||||
}
|
||||
|
||||
var iab = new IntArrayBuffer(data.remaining());
|
||||
iab.bind();
|
||||
GL33.glBufferData(iab.type, data, GL33.GL_STATIC_DRAW);
|
||||
return iab;
|
||||
}
|
||||
|
||||
public static IntArrayBuffer from(int[] data)
|
||||
{
|
||||
var iab = new IntArrayBuffer(data.length);
|
||||
iab.bind();
|
||||
GL33.glBufferData(iab.type, data, GL33.GL_STATIC_DRAW);
|
||||
return iab;
|
||||
}
|
||||
|
||||
public static IntArrayBuffer empty(int size)
|
||||
{
|
||||
var iab = new IntArrayBuffer(size);
|
||||
iab.bind();
|
||||
GL33.glBufferData(iab.type, size, GL33.GL_DYNAMIC_DRAW);
|
||||
return iab;
|
||||
}
|
||||
}
|
|
@ -208,6 +208,8 @@ public final class ModLoader extends PlutoLocalComponent
|
|||
|
||||
this.loadedModStack.addLast(mod);
|
||||
}
|
||||
|
||||
this.loadingPhase = EnumModLoadingPhase.DONE;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
|
@ -35,7 +35,10 @@ public enum EnumBackingFileSystem
|
|||
return fs.getPath(path);
|
||||
}),
|
||||
FS_ZIP("zip",
|
||||
uri -> FileSystems.newFileSystem(uri, Map.of()),
|
||||
uri -> {
|
||||
var provider = FileSystems.getDefault().provider();
|
||||
return FileSystems.newFileSystem(provider.getPath(uri), Map.of(), null);
|
||||
},
|
||||
(uri, fileSystem) -> fileSystem.getPath("/"),
|
||||
(fs, address, ext) -> {
|
||||
var sep = fs.getSeparator();
|
||||
|
|
|
@ -4,7 +4,7 @@ import java.awt.image.BufferedImage;
|
|||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* A wrapper around a native color buffer for easier handling
|
||||
* A wrapper around a native ABGR buffer for easier handling
|
||||
* by various APIs, such as OpenGL and GLFW.
|
||||
*
|
||||
* TPNImage is <em>always</em> assumed to be ABGR due to image format
|
||||
|
|
|
@ -9,16 +9,18 @@ import java.nio.ByteOrder;
|
|||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.plutoengine.logger.Logger;
|
||||
import org.plutoengine.logger.SmartSeverity;
|
||||
|
||||
/**
|
||||
* Quick ABGR (8-bit per channel, 32 bits per pixel) image loader for OpenGL textures.
|
||||
* Quick ABGR (8-bit per channel, 32 bits per pixel) and grayscale image loader for OpenGL textures.
|
||||
* Color component swizzling may be needed.
|
||||
*
|
||||
* @author 493msi
|
||||
*
|
||||
* @see ImageABGR
|
||||
* @see ImageY
|
||||
*
|
||||
* @since pre-alpha
|
||||
*/
|
||||
|
@ -34,8 +36,6 @@ public class ImageLoader
|
|||
static
|
||||
{
|
||||
placeholder = new BufferedImage(PLACEHOLDER_SIZE, PLACEHOLDER_SIZE, BufferedImage.TYPE_INT_ARGB);
|
||||
var data = placeholder.getData();
|
||||
var dataBuffer = (DataBufferInt) data.getDataBuffer();
|
||||
|
||||
for (int i = 0; i < PLACEHOLDER_SIZE * PLACEHOLDER_SIZE; i++)
|
||||
{
|
||||
|
@ -43,7 +43,7 @@ public class ImageLoader
|
|||
int y = i / PLACEHOLDER_SIZE;
|
||||
boolean checker = x / PLACEHOLDER_CHECKEDBOARD_SQUARE_SIZE % 2 == y / PLACEHOLDER_CHECKEDBOARD_SQUARE_SIZE % 2;
|
||||
|
||||
dataBuffer.setElem(i, checker ? 0xFFFF0000 : 0xFF000000);
|
||||
placeholder.setRGB(x, y, checker ? 0xFFFF0000 : 0xFF000000);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,7 @@ public class ImageLoader
|
|||
/**
|
||||
* Writes a {@link BufferedImage} into a {@link ImageABGR} buffer.
|
||||
*
|
||||
* If the input {@link Path} is null, a placeholder will be generated.
|
||||
* If the input {@link BufferedImage} is null, a placeholder will be generated.
|
||||
*
|
||||
* @param image The source {@link BufferedImage}
|
||||
* @param flipY Whether the image should be flipped vertically (for OpenGL uses)
|
||||
|
@ -147,7 +147,7 @@ public class ImageLoader
|
|||
DataBuffer dataBuffer = data.getDataBuffer();
|
||||
DataBufferByte byteBuffer = (DataBufferByte) dataBuffer;
|
||||
byte[] byteData = byteBuffer.getData();
|
||||
ByteBuffer buffer = ByteBuffer.allocateDirect(width * height * 4).order(ByteOrder.nativeOrder());
|
||||
ByteBuffer buffer = BufferUtils.createByteBuffer(width * height * 4);
|
||||
buffer.put(byteData);
|
||||
buffer.flip();
|
||||
|
||||
|
@ -156,9 +156,9 @@ public class ImageLoader
|
|||
|
||||
|
||||
/**
|
||||
* Writes a {@link BufferedImage} into a {@link ImageABGR} buffer.
|
||||
* Writes a {@link BufferedImage} into an {@link ImageABGR} buffer.
|
||||
*
|
||||
* If the input {@link Path} is null, a placeholder will be generated.
|
||||
* If the input {@link BufferedImage} is null, a placeholder will be generated.
|
||||
*
|
||||
* @param image The source {@link BufferedImage}
|
||||
*
|
||||
|
@ -172,4 +172,75 @@ public class ImageLoader
|
|||
{
|
||||
return loadImageSpecial(image, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a {@link BufferedImage} into an {@link ImageY} buffer.
|
||||
*
|
||||
* If the input {@link Path} is null, a placeholder will be generated.
|
||||
*
|
||||
* @param image The source {@link BufferedImage}
|
||||
* @param flipY Whether the image should be flipped vertically (for OpenGL uses)
|
||||
*
|
||||
* @return The output {@link ImageY}, never null
|
||||
*
|
||||
* @see ImageY
|
||||
*
|
||||
* @since 22.1.0.0-alpha.1
|
||||
* */
|
||||
public static ImageY loadImageGrayscaleSpecial(@Nullable BufferedImage image, boolean flipY)
|
||||
{
|
||||
if (image == null)
|
||||
{
|
||||
Logger.log(SmartSeverity.WARNING, "[TPL] Null BufferedImage supplied, generating a placeholder.");
|
||||
|
||||
return loadImageGrayscaleSpecial(placeholder, flipY);
|
||||
}
|
||||
|
||||
int width = image.getWidth();
|
||||
int height = image.getHeight();
|
||||
|
||||
if (width > 16384 || height > 16384 || width < 1 || height < 1)
|
||||
{
|
||||
Logger.log(SmartSeverity.ERROR, "[TPL] BufferedImage size is invalid (< 1 or > 16384), generating a placeholder.");
|
||||
|
||||
return loadImageGrayscaleSpecial(placeholder, flipY);
|
||||
}
|
||||
|
||||
BufferedImage copy = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
|
||||
Graphics2D imgGraphics = copy.createGraphics();
|
||||
imgGraphics.drawImage(image,
|
||||
0, flipY ? copy.getHeight() : 0, copy.getWidth(), flipY ? 0 : copy.getHeight(),
|
||||
0, 0, image.getWidth(), image.getHeight(),
|
||||
null); // I wonder if this is pixel-perfect
|
||||
imgGraphics.dispose();
|
||||
|
||||
Raster data = copy.getRaster();
|
||||
DataBuffer dataBuffer = data.getDataBuffer();
|
||||
DataBufferByte byteBuffer = (DataBufferByte) dataBuffer;
|
||||
byte[] byteData = byteBuffer.getData();
|
||||
ByteBuffer buffer = BufferUtils.createByteBuffer(width * height);
|
||||
buffer.put(byteData);
|
||||
buffer.flip();
|
||||
|
||||
return new ImageY(buffer, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a {@link BufferedImage} into an {@link ImageY} buffer.
|
||||
*
|
||||
* If the input {@link BufferedImage} is null, a placeholder will be generated.
|
||||
*
|
||||
* @param image The source {@link BufferedImage}
|
||||
*
|
||||
* @return The output {@link ImageABGR}, never null
|
||||
*
|
||||
* @see ImageY
|
||||
*
|
||||
* @since 22.1.0.0-alpha.1
|
||||
* */
|
||||
public static ImageY loadImageGrayscale(@Nullable BufferedImage image)
|
||||
{
|
||||
return loadImageGrayscaleSpecial(image, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
package org.plutoengine.tpl;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* A wrapper around a native Y buffer for easier handling
|
||||
* by various APIs, such as OpenGL and GLFW.
|
||||
*
|
||||
* @author 493msi
|
||||
*
|
||||
* @since 22.1.0.0-alpha.0
|
||||
*/
|
||||
public class ImageY
|
||||
{
|
||||
private final ByteBuffer data;
|
||||
private final int width;
|
||||
private final int height;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ImageY} from the specified buffer, width and height.
|
||||
*
|
||||
* @param bfr The input {@link ByteBuffer}
|
||||
* @param width This image's width
|
||||
* @param height This image's height
|
||||
*
|
||||
* @since pre-alpha
|
||||
* */
|
||||
public ImageY(ByteBuffer bfr, int width, int height)
|
||||
{
|
||||
this.data = bfr;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the width of the color buffer.
|
||||
*
|
||||
* @return The width of this {@link ImageY}
|
||||
*
|
||||
* @since pre-alpha
|
||||
* */
|
||||
public int getWidth()
|
||||
{
|
||||
return this.width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the height of the color buffer.
|
||||
*
|
||||
* @return The height of this {@link ImageY}
|
||||
*
|
||||
* @since pre-alpha
|
||||
* */
|
||||
public int getHeight()
|
||||
{
|
||||
return this.height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a read-only view of the color buffer.
|
||||
*
|
||||
* @return This image's color {@link ByteBuffer}
|
||||
*
|
||||
* @since pre-alpha
|
||||
* */
|
||||
public ByteBuffer getData()
|
||||
{
|
||||
return this.data.asReadOnlyBuffer();
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@ package org.plutoengine.shader;
|
|||
import org.lwjgl.opengl.GL33;
|
||||
import org.plutoengine.shader.type.IShader;
|
||||
|
||||
public interface IShaderProgram
|
||||
public interface IShaderProgram extends AutoCloseable
|
||||
{
|
||||
int getID();
|
||||
|
||||
|
@ -27,5 +27,9 @@ public interface IShaderProgram
|
|||
GL33.glUseProgram(0);
|
||||
}
|
||||
|
||||
void dispose();
|
||||
default void close()
|
||||
{
|
||||
this.stop();
|
||||
GL33.glDeleteProgram(this.getID());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -127,8 +127,8 @@ public class RenderShaderBuilder
|
|||
|
||||
if (this.tempShaders)
|
||||
{
|
||||
this.vertexShader.dispose();
|
||||
this.fragmentShader.dispose();
|
||||
this.vertexShader.close();
|
||||
this.fragmentShader.close();
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -146,8 +146,8 @@ public class RenderShaderBuilder
|
|||
|
||||
if (this.tempShaders)
|
||||
{
|
||||
this.vertexShader.dispose();
|
||||
this.fragmentShader.dispose();
|
||||
this.vertexShader.close();
|
||||
this.fragmentShader.close();
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -158,8 +158,8 @@ public class RenderShaderBuilder
|
|||
|
||||
if (this.tempShaders)
|
||||
{
|
||||
this.vertexShader.dispose();
|
||||
this.fragmentShader.dispose();
|
||||
this.vertexShader.close();
|
||||
this.fragmentShader.close();
|
||||
}
|
||||
|
||||
for (var field : fields)
|
||||
|
@ -214,8 +214,13 @@ public class RenderShaderBuilder
|
|||
{
|
||||
AutomaticUniforms.VIEWPORT_PROJECTION.addListener(mat4 ->
|
||||
{
|
||||
if (program.getID() == 0)
|
||||
return false;
|
||||
|
||||
program.start();
|
||||
umat4.load(mat4);
|
||||
|
||||
return true;
|
||||
});
|
||||
Logger.logf(SmartSeverity.ADDED, "Uniform '%s' ID %d in '%s' ID %d now listens to AutomaticUniforms.VIEWPORT_PROJECTION.\n", uniformName, location, shaderClass.getCanonicalName(), programID);
|
||||
}
|
||||
|
|
|
@ -15,12 +15,13 @@ public abstract class ShaderBase implements IShaderProgram
|
|||
}
|
||||
|
||||
@Override
|
||||
public void dispose()
|
||||
public void close()
|
||||
{
|
||||
Logger.logf(SmartSeverity.REMOVED, "Disposing of shader ID %d of type %s...\n", this.getID(), this.getClass().getCanonicalName());
|
||||
|
||||
this.stop();
|
||||
GL33.glDeleteProgram(this.programID);
|
||||
IShaderProgram.super.close();
|
||||
|
||||
this.programID = 0;
|
||||
}
|
||||
|
||||
protected void bindAttribute(int attribute, String name)
|
||||
|
|
|
@ -2,11 +2,11 @@ package org.plutoengine.shader.type;
|
|||
|
||||
import org.lwjgl.opengl.GL33;
|
||||
|
||||
public interface IShader
|
||||
public interface IShader extends AutoCloseable
|
||||
{
|
||||
int getID();
|
||||
|
||||
default void dispose()
|
||||
default void close()
|
||||
{
|
||||
GL33.glDeleteShader(this.getID());
|
||||
}
|
||||
|
|
|
@ -53,9 +53,9 @@ public class PlutoSpriteSheetMod implements IModEntryPoint
|
|||
{
|
||||
FramebufferTiledSpriteSheet.setSpriteShader(null);
|
||||
|
||||
spriteSheetShader.dispose();
|
||||
shaderRectangle2D.dispose();
|
||||
shader2D.dispose();
|
||||
spriteSheetShader.close();
|
||||
shaderRectangle2D.close();
|
||||
shader2D.close();
|
||||
|
||||
RectangleRenderer2D.unload();
|
||||
Renderer2D.unload();
|
||||
|
|
|
@ -58,12 +58,12 @@ public class RectangleRenderer2D
|
|||
{
|
||||
if (standardQuad != null)
|
||||
{
|
||||
standardQuad.delete();
|
||||
standardQuad.close();
|
||||
}
|
||||
|
||||
if (centeredQuad != null)
|
||||
{
|
||||
centeredQuad.delete();
|
||||
centeredQuad.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -51,12 +51,12 @@ public class Renderer2D
|
|||
{
|
||||
if (standardQuad != null)
|
||||
{
|
||||
standardQuad.delete();
|
||||
standardQuad.close();
|
||||
}
|
||||
|
||||
if (centeredQuad != null)
|
||||
{
|
||||
centeredQuad.delete();
|
||||
centeredQuad.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ public class DisposableTextureSprite extends PartialTextureSprite implements Spr
|
|||
@Override
|
||||
public void delete()
|
||||
{
|
||||
this.spriteTexture.delete();
|
||||
this.spriteTexture.close();
|
||||
this.spriteTexture = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ public class FramebufferTiledSpriteSheet extends TiledSpriteSheet<RectangleTextu
|
|||
}
|
||||
|
||||
this.spriteFBO.unbind();
|
||||
this.spriteSheet.delete();
|
||||
this.spriteSheet.close();
|
||||
this.spriteSheet = newSpriteSheet;
|
||||
}
|
||||
|
||||
|
@ -88,7 +88,7 @@ public class FramebufferTiledSpriteSheet extends TiledSpriteSheet<RectangleTextu
|
|||
this.sampler.delete();
|
||||
this.sampler = null;
|
||||
|
||||
this.spriteSheet.delete();
|
||||
this.spriteSheet.close();
|
||||
|
||||
// this.spriteSheet = null;
|
||||
// Done by parent
|
||||
|
|
|
@ -13,7 +13,7 @@ import org.plutoengine.logger.SmartSeverity;
|
|||
import org.plutoengine.tpl.ImageABGR;
|
||||
import org.plutoengine.tpl.ImageLoader;
|
||||
|
||||
public abstract class Texture
|
||||
public abstract class Texture implements AutoCloseable
|
||||
{
|
||||
protected int glID;
|
||||
protected final int type;
|
||||
|
@ -49,7 +49,7 @@ public abstract class Texture
|
|||
GL33.glBindTexture(this.type, 0);
|
||||
}
|
||||
|
||||
public void delete()
|
||||
public void close()
|
||||
{
|
||||
Logger.logf(SmartSeverity.REMOVED, "Texture with ID %d of type '%s' deleted...\n", this.glID, this.getClass().getSimpleName());
|
||||
|
||||
|
|
Binary file not shown.
|
@ -6,6 +6,10 @@
|
|||
"icons": {
|
||||
"path": "icons",
|
||||
"type": "open"
|
||||
},
|
||||
"fonts": {
|
||||
"path": "fonts.zip",
|
||||
"type": "zip"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +1,26 @@
|
|||
#version 330 core
|
||||
|
||||
in vec2 uvCoordinates;
|
||||
flat in int atlasPage;
|
||||
|
||||
uniform sampler2DRect textureSampler;
|
||||
uniform sampler2DArray textureSampler;
|
||||
uniform vec4 recolor;
|
||||
|
||||
out vec4 out_Color;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
vec4 color = texture(textureSampler, uvCoordinates);
|
||||
vec3 textCoords = vec3(uvCoordinates, atlasPage);
|
||||
|
||||
out_Color = color * recolor;
|
||||
float threshold = 1 - 240.0 / 255.0;
|
||||
|
||||
float signedDist = 1 - texture(textureSampler, textCoords).r;
|
||||
|
||||
float fw = fwidth(signedDist);
|
||||
|
||||
vec4 col = recolor;
|
||||
|
||||
col.a *= smoothstep(threshold + 0.2, threshold, signedDist);
|
||||
|
||||
out_Color = col;
|
||||
}
|
|
@ -2,26 +2,21 @@
|
|||
|
||||
in vec2 position;
|
||||
in vec2 uvCoords;
|
||||
in int page;
|
||||
|
||||
out vec2 uvCoordinates;
|
||||
flat out int atlasPage;
|
||||
|
||||
uniform mat4 projection;
|
||||
uniform mat4 transformation;
|
||||
|
||||
uniform vec2 uvBase;
|
||||
uniform vec2 uvDelta;
|
||||
uniform mat3 transformation;
|
||||
|
||||
uniform int italic;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
vec2 pos = vec2(position.x, position.y);
|
||||
|
||||
if(italic == 1)
|
||||
{
|
||||
pos = vec2(position.x - position.y / 4.0, position.y);
|
||||
}
|
||||
|
||||
uvCoordinates = uvBase + uvCoords * uvDelta;
|
||||
gl_Position = projection * transformation * vec4(pos, 0.0, 1.0);
|
||||
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);
|
||||
gl_Position = projection * vec4(transformed, 1.0);
|
||||
}
|
|
@ -1,6 +1,9 @@
|
|||
package org.plutoengine.demo;
|
||||
|
||||
import org.plutoengine.graphics.PlutoGUIMod;
|
||||
import org.plutoengine.graphics.gui.STBTTFont;
|
||||
import org.plutoengine.mod.IModEntryPoint;
|
||||
import org.plutoengine.mod.Mod;
|
||||
import org.plutoengine.mod.ModEntry;
|
||||
|
||||
@ModEntry(
|
||||
|
@ -8,6 +11,19 @@ import org.plutoengine.mod.ModEntry;
|
|||
version = VersionInfo.GAME_VERSION,
|
||||
dependencies = { PlutoGUIMod.class }
|
||||
)
|
||||
public class BasicApplicationDemoMod
|
||||
public class BasicApplicationDemoMod implements IModEntryPoint
|
||||
{
|
||||
public static STBTTFont font;
|
||||
|
||||
@Override
|
||||
public void onLoad(Mod mod)
|
||||
{
|
||||
font = STBTTFont.load(mod.getResource("fonts$robotoregular#ttf"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnload()
|
||||
{
|
||||
font.close();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,29 @@
|
|||
package org.plutoengine.demo;
|
||||
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL33;
|
||||
|
||||
import org.plutoengine.PlutoApplication;
|
||||
import org.plutoengine.PlutoLocal;
|
||||
import org.plutoengine.display.Display;
|
||||
import org.plutoengine.display.Framerate;
|
||||
import org.plutoengine.gui.font.FontHelper;
|
||||
import org.plutoengine.gui.font.FontRenderer;
|
||||
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.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.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 java.awt.image.BufferedImage;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
public class Main extends PlutoApplication
|
||||
{
|
||||
|
@ -20,6 +32,7 @@ public class Main extends PlutoApplication
|
|||
public static void main(String[] args) throws Exception
|
||||
{
|
||||
var config = new PlutoApplication.StartupConfig();
|
||||
config.vsync(1);
|
||||
config.windowName("JSRClone " + VersionInfo.GAME_VERSION);
|
||||
|
||||
INSTANCE = new Main();
|
||||
|
@ -35,6 +48,82 @@ public class Main extends PlutoApplication
|
|||
var projection = ProjectionMatrix.createOrtho2D(this.display.getWidth(), this.display.getHeight());
|
||||
AutomaticUniforms.VIEWPORT_PROJECTION.fire(projection);
|
||||
|
||||
TestFontRenderer.drawString(BasicApplicationDemoMod.font, "Testing ");
|
||||
|
||||
if (InputBus.Keyboard.pressed(GLFW.GLFW_KEY_R))
|
||||
{
|
||||
var shader = PlutoGUIMod.fontShader;
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
|
@ -57,6 +146,8 @@ public class Main extends PlutoApplication
|
|||
|
||||
modNr++;
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
public static Display getDisplay()
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 7e3c146c9a70c2ec24ded435021f279a757c7aff
|
|
@ -2,7 +2,8 @@ rootProject.name = "plutoengine-sdk"
|
|||
|
||||
include("plutoengine",
|
||||
"plutoengine-ext",
|
||||
"plutoengine-demos")
|
||||
"plutoengine-demos",
|
||||
"libra")
|
||||
|
||||
project(":plutoengine").projectDir = file("./engine-core")
|
||||
project(":plutoengine-ext").projectDir = file("./engine-ext")
|
||||
|
|
Loading…
Reference in New Issue