Progress on the font/Texture system
This commit is contained in:
parent
55957d3640
commit
e5873759cc
|
@ -8,7 +8,6 @@ import javax.imageio.ImageIO;
|
|||
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import org.lwjgl.stb.STBImage;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import speiger.src.coreengine.assets.parsers.NativeMemoryParser;
|
||||
import speiger.src.coreengine.math.misc.ColorSpaces;
|
||||
|
|
|
@ -4,17 +4,21 @@ import java.util.List;
|
|||
|
||||
import speiger.src.collections.ints.maps.impl.hash.Int2ObjectOpenHashMap;
|
||||
import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap;
|
||||
import speiger.src.collections.objects.lists.ObjectArrayList;
|
||||
import speiger.src.coreengine.assets.AssetLocation;
|
||||
import speiger.src.coreengine.rendering.gui.font.glyth.Glyth;
|
||||
import speiger.src.coreengine.rendering.gui.font.glyth.GlythData;
|
||||
import speiger.src.coreengine.rendering.gui.font.glyth.IGlythSheetInfo;
|
||||
import speiger.src.coreengine.rendering.gui.font.providers.IFontProvider;
|
||||
|
||||
public class FontGroup {
|
||||
private static final int BOLD_FLAG = 1<<31;
|
||||
private static final int TEXTURE_SIZE = 2048;
|
||||
AssetLocation locations;
|
||||
List<IFontProvider> providers;
|
||||
Int2ObjectMap<Glyth> bakedGlyths = new Int2ObjectOpenHashMap<>();
|
||||
Int2ObjectMap<GlythData> dataGlyths = new Int2ObjectOpenHashMap<>();
|
||||
List<FontTexture> textures = new ObjectArrayList<>();
|
||||
|
||||
public FontGroup(AssetLocation locations, List<IFontProvider> providers) {
|
||||
this.locations = locations;
|
||||
|
@ -42,8 +46,19 @@ public class FontGroup {
|
|||
codepoint &= ~BOLD_FLAG;
|
||||
for(int i = 0,m=providers.size();i<m;i++) {
|
||||
GlythData data = providers.get(i).glythData(codepoint);
|
||||
if(data != null) return data.bake(bold);
|
||||
if(data != null) return data.bake(bold, this::stitch);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Glyth stitch(IGlythSheetInfo info, boolean bold) {
|
||||
for(int i = 0,m=textures.size();i<m;i++) {
|
||||
Glyth glyth = textures.get(i).build(info, bold);
|
||||
if(glyth != null) return glyth;
|
||||
}
|
||||
FontTexture texture = new FontTexture(TEXTURE_SIZE);
|
||||
textures.add(texture);
|
||||
Glyth glyth = texture.build(info, bold);
|
||||
return glyth;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
package speiger.src.coreengine.rendering.gui.font;
|
||||
|
||||
import org.lwjgl.opengl.GL45;
|
||||
|
||||
import speiger.src.coreengine.assets.base.IAssetProvider;
|
||||
import speiger.src.coreengine.rendering.gui.font.glyth.Glyth;
|
||||
import speiger.src.coreengine.rendering.gui.font.glyth.IGlythSheetInfo;
|
||||
import speiger.src.coreengine.rendering.textures.base.BaseTexture;
|
||||
import speiger.src.coreengine.rendering.utils.values.textures.GLTextureFormat;
|
||||
|
||||
public class FontTexture extends BaseTexture {
|
||||
int bounds;
|
||||
Slot slot;
|
||||
|
||||
public FontTexture(int bounds) {
|
||||
this.bounds = bounds;
|
||||
slot = new Slot(0, 0, bounds, bounds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(IAssetProvider provider) {
|
||||
GL45.glTextureStorage2D(id, 1, GLTextureFormat.RGBA.glValue(), bounds, bounds);
|
||||
}
|
||||
|
||||
public Glyth build(IGlythSheetInfo info, boolean bold) {
|
||||
Slot result = slot.insert(info);
|
||||
if(result != null) {
|
||||
info.upload(id(), result.x, result.y);
|
||||
float minU = (slot.x + 0.01F) / bounds;
|
||||
float maxU = (slot.x - 0.01F + info.width()) / bounds;
|
||||
float minV = (slot.y + 0.01F) / bounds;
|
||||
float maxV = (slot.y - 0.01F + info.height()) / bounds;
|
||||
return new Glyth(id(), bold, minU, minV, maxU, maxV);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int width() {
|
||||
return bounds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int height() {
|
||||
return bounds;
|
||||
}
|
||||
|
||||
public static class Slot {
|
||||
final int x;
|
||||
final int y;
|
||||
final int width;
|
||||
final int height;
|
||||
Slot[] children = null;
|
||||
boolean occupied;
|
||||
|
||||
public Slot(int x, int y, int width, int height) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public Slot insert(IGlythSheetInfo info) {
|
||||
if(children == null) {
|
||||
if(occupied) return null;
|
||||
int iw = info.width();
|
||||
int ih = info.height();
|
||||
if(iw > width || ih > height) return null;
|
||||
if(iw == width && ih == height) {
|
||||
occupied = true;
|
||||
return this;
|
||||
}
|
||||
int dw = width - iw;
|
||||
int dh = height - ih;
|
||||
children = new Slot[dw > 0 && dh > 0 ? 3 : 2];
|
||||
children[0] = new Slot(x, y, iw, ih);
|
||||
expandSlot(iw, ih, dw, dh);
|
||||
return children[0].insert(info);
|
||||
}
|
||||
for(int i = 0,m=children.length;i<m;i++) {
|
||||
Slot slot = children[i].insert(info);
|
||||
if(slot != null) return slot;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void expandSlot(int rw, int rh, int dw, int dh) {
|
||||
if(dw > 0 && dh > 0) {
|
||||
if(dw >= dh) {
|
||||
children[1] = new Slot(x + rw, y, dw, rh);
|
||||
children[2] = new Slot(x, y + rh, width, dh);
|
||||
}
|
||||
else {
|
||||
children[1] = new Slot(x, y + rh, rw, dh);
|
||||
children[2] = new Slot(x + rw, y, dw, height);
|
||||
}
|
||||
}
|
||||
else if(dw == 0) children[1] = new Slot(x, y + rh, rw, dh);
|
||||
else if(dh == 0) children[1] = new Slot(x + rw, y, dw, rh);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Slot[x="+x+", y="+y+", w="+width+", h="+height+"]";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,6 +2,9 @@ package speiger.src.coreengine.rendering.gui.font.glyth;
|
|||
|
||||
public interface GlythData {
|
||||
public float advance(boolean bold);
|
||||
public Glyth bake(boolean bold);
|
||||
public Glyth bake(boolean bold, GlythBaker baker);
|
||||
|
||||
public static interface GlythBaker {
|
||||
Glyth bake(IGlythSheetInfo info, boolean bold);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package speiger.src.coreengine.rendering.gui.font.glyth;
|
||||
|
||||
public interface IGlythSheetInfo {
|
||||
public int width();
|
||||
public int height();
|
||||
|
||||
public int upload(int texture, int x, int y);
|
||||
}
|
|
@ -1,24 +1,29 @@
|
|||
package speiger.src.coreengine.rendering.textures.custom;
|
||||
|
||||
import org.lwjgl.opengl.GL46;
|
||||
import org.lwjgl.stb.STBImage;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import speiger.src.coreengine.math.misc.ColorSpaces;
|
||||
import speiger.src.coreengine.rendering.utils.values.IGLValue.ITextureComponentFormat;
|
||||
|
||||
public class Drawable implements IDrawable, AutoCloseable {
|
||||
ITextureComponentFormat format;
|
||||
int width;
|
||||
int height;
|
||||
long components;
|
||||
long pixels;
|
||||
boolean isSTB;
|
||||
|
||||
public Drawable(int width, int height) {
|
||||
this(width, height, MemoryUtil.nmemAllocChecked(4L * width * height), false);
|
||||
public Drawable(ITextureComponentFormat format, int width, int height) {
|
||||
this(format, width, height, MemoryUtil.nmemAllocChecked(format.components() * width * height), false);
|
||||
}
|
||||
|
||||
public Drawable(int width, int height, long pixels, boolean isSTB) {
|
||||
public Drawable(ITextureComponentFormat format, int width, int height, long pixels, boolean isSTB) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.pixels = pixels;
|
||||
this.components = format.components();
|
||||
this.isSTB = isSTB;
|
||||
}
|
||||
|
||||
|
@ -30,6 +35,7 @@ public class Drawable implements IDrawable, AutoCloseable {
|
|||
public int height() { return height; }
|
||||
public long pixels() { return pixels; }
|
||||
public boolean isSTBImage() { return isSTB; }
|
||||
public ITextureComponentFormat format() { return format; }
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
@ -40,7 +46,7 @@ public class Drawable implements IDrawable, AutoCloseable {
|
|||
}
|
||||
|
||||
protected long offset(int x, int y) {
|
||||
return ((y * width()) + x) << 2;
|
||||
return ((y * width()) + x) * components;
|
||||
}
|
||||
|
||||
protected void ensureValid(int index) {
|
||||
|
@ -53,38 +59,48 @@ public class Drawable implements IDrawable, AutoCloseable {
|
|||
if(pixels == 0L) throw new IllegalStateException("Pixel Data doesn't exist");
|
||||
}
|
||||
|
||||
public void upload(int texture, int level, int x, int y, int texX, int texY, int width, int height) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(int index, int data) {
|
||||
ensureValid(index);
|
||||
if(components != 4) throw new IllegalStateException("Format has to be 4 components");
|
||||
MemoryUtil.memPutInt(index * 4L, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setR(int index, int red) {
|
||||
if(!format.hasRed()) throw new IllegalArgumentException("Format doesn't support Red/Luminance");
|
||||
ensureValid(index);
|
||||
MemoryUtil.memPutByte(index * 4L, (byte)(red & 0xFF));
|
||||
MemoryUtil.memPutByte(index * components + format.redOffset(), (byte)(red & 0xFF));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setG(int index, int green) {
|
||||
if(!format.hasGreen()) throw new IllegalArgumentException("Format doesn't support Green");
|
||||
ensureValid(index);
|
||||
MemoryUtil.memPutByte(index * 4L + 1L, (byte)(green & 0xFF));
|
||||
MemoryUtil.memPutByte(index * components + format.greenOffset(), (byte)(green & 0xFF));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setB(int index, int blue) {
|
||||
if(!format.hasBlue()) throw new IllegalArgumentException("Format doesn't support Blue");
|
||||
ensureValid(index);
|
||||
MemoryUtil.memPutByte(index * 4L + 2L, (byte)(blue & 0xFF));
|
||||
MemoryUtil.memPutByte(index * components + format.blueOffset(), (byte)(blue & 0xFF));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setA(int index, int alpha) {
|
||||
if(!format.hasAlpha()) throw new IllegalArgumentException("Format doesn't support Alpha");
|
||||
ensureValid(index);
|
||||
MemoryUtil.memPutByte(index * 4L + 3L, (byte)(alpha & 0xFF));
|
||||
MemoryUtil.memPutByte(index * components + format.alphaOffset(), (byte)(alpha & 0xFF));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fill(int x, int y, int width, int height, int data) {
|
||||
if(components != 4) throw new IllegalStateException("Format has to be 4 components");
|
||||
ensureValid(x, y);
|
||||
ensureValid(x+width, y+height);
|
||||
for(int yOff = 0;yOff<height;yOff++) {
|
||||
|
@ -94,30 +110,35 @@ public class Drawable implements IDrawable, AutoCloseable {
|
|||
|
||||
@Override
|
||||
public int get(int index) {
|
||||
if(components != 4) throw new IllegalStateException("Format has to be 4 components");
|
||||
ensureValid(index);
|
||||
return MemoryUtil.memGetInt(index * 4L);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getR(int index) {
|
||||
if(!format.hasRed()) throw new IllegalArgumentException("Format doesn't support Red/Luminance");
|
||||
ensureValid(index);
|
||||
return MemoryUtil.memGetByte(index * 4L);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getG(int index) {
|
||||
if(!format.hasGreen()) throw new IllegalArgumentException("Format doesn't support Green");
|
||||
ensureValid(index);
|
||||
return MemoryUtil.memGetByte(index * 4L + 1L);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getB(int index) {
|
||||
if(!format.hasBlue()) throw new IllegalArgumentException("Format doesn't support Blue");
|
||||
ensureValid(index);
|
||||
return MemoryUtil.memGetByte(index * 4L + 2L);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getA(int index) {
|
||||
if(!format.hasAlpha()) throw new IllegalArgumentException("Format doesn't support Alpha");
|
||||
ensureValid(index);
|
||||
return MemoryUtil.memGetByte(index * 4L + 3L);
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ public class SimpleTexture extends BaseTexture {
|
|||
@Override
|
||||
public void load(IAssetProvider provider) {
|
||||
long address = 0L;
|
||||
int channel = GLTextureFormat.toComponents(metadata.getExternalFormat());
|
||||
int channel = GLTextureFormat.stbComponents(metadata.getExternalFormat());
|
||||
try(IAsset asset = provider.getAsset(location)) {
|
||||
ByteBuffer buffer = asset.custom(NativeMemoryParser.INSTANCE);
|
||||
int[] width = new int[1];
|
||||
|
|
|
@ -18,7 +18,20 @@ public interface IGLValue {
|
|||
public static interface IShaderType extends IGLValue {};
|
||||
public static interface ITextureFormatType extends IGLValue {
|
||||
public boolean internal();
|
||||
public int components();
|
||||
public int componentMask();
|
||||
}
|
||||
public static interface ITextureComponentFormat extends ITextureFormatType {
|
||||
public default boolean hasRed() { return redOffset() != -1; }
|
||||
public default boolean hasGreen() { return greenOffset() != -1; }
|
||||
public default boolean hasBlue() { return blueOffset() != -1; }
|
||||
public default boolean hasAlpha() { return alphaOffset() != -1; }
|
||||
public int redOffset();
|
||||
public int greenOffset();
|
||||
public int blueOffset();
|
||||
public int alphaOffset();
|
||||
}
|
||||
|
||||
public static interface ITextureParameter extends IGLValue {
|
||||
public boolean isValid(int value);
|
||||
}
|
||||
|
|
|
@ -3,23 +3,36 @@ package speiger.src.coreengine.rendering.utils.values.textures;
|
|||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import speiger.src.coreengine.rendering.utils.values.IGLValue.ITextureFormatType;
|
||||
import speiger.src.coreengine.rendering.utils.values.IGLValue.ITextureComponentFormat;
|
||||
|
||||
public enum GLTextureFormat implements ITextureFormatType {
|
||||
R(GL11.GL_RED, true),
|
||||
RG(GL30.GL_RG, true),
|
||||
RGB(GL11.GL_RGB, true),
|
||||
RGBA(GL11.GL_RGBA, true),
|
||||
DEPTH(GL11.GL_DEPTH_COMPONENT, true),
|
||||
DEPTH_STENCIL(GL30.GL_DEPTH_STENCIL, true),
|
||||
LUMINANCE(GL11.GL_LUMINANCE, false),
|
||||
LUMINANCE_ALPHA(GL11.GL_LUMINANCE_ALPHA, false);
|
||||
public enum GLTextureFormat implements ITextureComponentFormat {
|
||||
R(GL11.GL_RED, 1, 0, -1, -1, -1, true),
|
||||
RG(GL30.GL_RG, 2, 0, 1, -1, -1, true),
|
||||
RGB(GL11.GL_RGB, 3, 0, 1, 2, -1, true),
|
||||
RGBA(GL11.GL_RGBA, 4, 0, 1, 2, 3, true),
|
||||
DEPTH(GL11.GL_DEPTH_COMPONENT, 1, 0, -1, -1, -1, true),
|
||||
DEPTH_STENCIL(GL30.GL_DEPTH_STENCIL, 2, 0, 1, -1, -1, true),
|
||||
LUMINANCE(GL11.GL_LUMINANCE, 1, 0, 0, 0, 0, false),
|
||||
LUMINANCE_ALPHA(GL11.GL_LUMINANCE_ALPHA, 2, 0, -1, -1, 1, false);
|
||||
|
||||
int glMode;
|
||||
boolean internal;
|
||||
|
||||
private GLTextureFormat(int glMode, boolean internal) {
|
||||
int components;
|
||||
int componentMask;
|
||||
int redOffset;
|
||||
int greenOffset;
|
||||
int blueOffset;
|
||||
int alphaOffset;
|
||||
|
||||
private GLTextureFormat(int glMode, int components, int redOffset, int greenOffset, int blueOffset, int alphaOffset, boolean internal) {
|
||||
this.glMode = glMode;
|
||||
this.components = components;
|
||||
this.componentMask = (1 << (components * 8)) - 1;
|
||||
this.redOffset = redOffset;
|
||||
this.greenOffset = greenOffset;
|
||||
this.blueOffset = blueOffset;
|
||||
this.alphaOffset = alphaOffset;
|
||||
this.internal = internal;
|
||||
}
|
||||
|
||||
|
@ -27,8 +40,17 @@ public enum GLTextureFormat implements ITextureFormatType {
|
|||
public int glValue() { return glMode; }
|
||||
@Override
|
||||
public boolean internal() { return internal; }
|
||||
@Override
|
||||
public int components() { return stbComponents(glMode); }
|
||||
@Override
|
||||
public int componentMask() { return componentMask; }
|
||||
|
||||
public static int toComponents(int glValue) {
|
||||
public int redOffset() { return redOffset; }
|
||||
public int greenOffset() { return greenOffset; }
|
||||
public int blueOffset() { return blueOffset; }
|
||||
public int alphaOffset() { return alphaOffset; }
|
||||
|
||||
public static int stbComponents(int glValue) {
|
||||
return switch(glValue) {
|
||||
case GL11.GL_LUMINANCE -> 1;
|
||||
case GL11.GL_LUMINANCE_ALPHA -> 2;
|
||||
|
|
Loading…
Reference in New Issue