diff --git a/src/main/java/speiger/src/coreengine/NewInputTest.java b/src/main/java/speiger/src/coreengine/NewInputTest.java index 2d0214c..96ac1b7 100644 --- a/src/main/java/speiger/src/coreengine/NewInputTest.java +++ b/src/main/java/speiger/src/coreengine/NewInputTest.java @@ -1,25 +1,27 @@ package speiger.src.coreengine; import java.util.List; +import java.util.function.BiConsumer; import org.lwjgl.glfw.GLFW; import org.lwjgl.opengl.GL; import org.lwjgl.opengl.GL11; -import com.google.gson.JsonObject; - +import speiger.src.collections.objects.lists.ObjectArrayList; import speiger.src.coreengine.assets.AssetLocation; import speiger.src.coreengine.assets.AssetManager; -import speiger.src.coreengine.assets.base.IAsset; import speiger.src.coreengine.assets.base.IAssetPackage; import speiger.src.coreengine.assets.base.IAssetProvider; +import speiger.src.coreengine.math.vector.matrix.Matrix4f; +import speiger.src.coreengine.rendering.gui.font.Font; +import speiger.src.coreengine.rendering.gui.font.FontManager; import speiger.src.coreengine.rendering.gui.font.glyth.Glyth; -import speiger.src.coreengine.rendering.gui.font.glyth.UnbakedGlyth; -import speiger.src.coreengine.rendering.gui.font.glyth.UnbakedGlyth.GlythBaker; +import speiger.src.coreengine.rendering.gui.font.glyth.GlythData; +import speiger.src.coreengine.rendering.gui.font.glyth.GlythData.GlythBaker; import speiger.src.coreengine.rendering.gui.font.glyth.IGlythSheetInfo; +import speiger.src.coreengine.rendering.gui.font.glyth.MissingGlyth; import speiger.src.coreengine.rendering.gui.font.providers.IFontProvider; import speiger.src.coreengine.rendering.gui.font.providers.STBTrueTypeProvider; -import speiger.src.coreengine.rendering.guiOld.renderer.provider.BitmapFontProvider; import speiger.src.coreengine.rendering.input.devices.FileDrop; import speiger.src.coreengine.rendering.input.devices.Joystick; import speiger.src.coreengine.rendering.input.devices.Keyboard; @@ -32,6 +34,8 @@ import speiger.src.coreengine.rendering.models.buffers.VertexArray; import speiger.src.coreengine.rendering.shader.Shader; import speiger.src.coreengine.rendering.shader.SimpleShader; import speiger.src.coreengine.rendering.shader.uniform.base.TextureUniform; +import speiger.src.coreengine.rendering.shader.uniform.vec.Matrix4fUniform; +import speiger.src.coreengine.rendering.tesselation.buffer.IVertexBuilder; import speiger.src.coreengine.rendering.tesselation.buffer.VertexBuilder; import speiger.src.coreengine.rendering.tesselation.format.VertexTypes; import speiger.src.coreengine.rendering.textures.custom.Drawable; @@ -48,7 +52,9 @@ public class NewInputTest { EventBus bus = new EventBus(); WindowManager manager = new WindowManager(); AssetManager assets = new AssetManager(List.of(IAssetPackage.of(IOUtils.getBaseLocation()))); + FontManager fonts = new FontManager(); private Shader shaderTest = Shader.create(TestShader::new); + private Shader guiShader = Shader.create(GuiShader::new); public static void main(String[] args) { @@ -65,15 +71,17 @@ public class NewInputTest { manager.addDevices(Mouse.INSTANCE, Keyboard.INSTANCE, Joystick.INSTANCE, FileDrop.INSTANCE); Window window = manager.builder().title("Testing Engine").build(); shaderTest.register(); + guiShader.register(); assets.addListener(GLStateTracker.instance().shaders); assets.addListener(GLStateTracker.TEXTURE_TRACKER); + assets.addListener(fonts); assets.reload(); System.out.println("Testing: "+GL.getCapabilities().OpenGL46); System.out.println("Testing: "+Integer.divideUnsigned(-1, 255)); IFontProvider provider = STBTrueTypeProvider.create(AssetLocation.of("font/roboto/font.json"), assets); - BitmapFontProvider bit = loadProvider(AssetLocation.of("font/roboto.json"), 18.5F); + guiShader.get().proView.set(new Matrix4f().ortho(0, 0, window.width(), window.height(), 1000, -1000)); int size = 512; int half = size >> 1; @@ -87,12 +95,10 @@ public class NewInputTest { texture.fill(0, half, half, half, 0, 0, 255, 255); texture.process(true); window.visible(true); - var instance = bit.getCharacter("C".codePointAt(0), false); - System.out.println("Testing: "+instance.getWidth()+","+instance.getHeight()); bus.register(KeyEvent.Key.class, T -> { if(T.key() == GLFW.GLFW_KEY_T) { T.cancel(); - texture.bind(); +// texture.bind(); Drawable drawable = new Drawable(GLTextureFormat.RGBA, half, half); drawable.fill(0, 0, half, half, 255, 255, 0, 255); drawable.upload(base * 3, base * 3, base, base, base * 2, base * 2); @@ -101,8 +107,8 @@ public class NewInputTest { else if(T.key() == GLFW.GLFW_KEY_Z) { T.cancel(); System.out.println("Testing"); - texture.bind(); - UnbakedGlyth data = provider.glythData("C".codePointAt(0), 0); +// texture.bind(); + GlythData data = provider.glythData("C".codePointAt(0), 0, 18.5F, 2F); data.bake(new GlythBaker() { @Override public Glyth bake(IGlythSheetInfo info) { @@ -112,7 +118,20 @@ public class NewInputTest { System.out.println("Test2: "+width+", "+height); return null; } - }, 1F, 1F); + }); + } + else if(T.key() == GLFW.GLFW_KEY_U) { + MissingGlyth glyth = new MissingGlyth(18.5F, 4F); + glyth.bake(new GlythBaker() { + @Override + public Glyth bake(IGlythSheetInfo info) { + int width = info.width(); + int height = info.height(); + info.upload(half - (width >> 1), half - (height >> 1)); + System.out.println("Test3: "+width+", "+height); + return null; + } + }); } }); @@ -125,7 +144,15 @@ public class NewInputTest { builder.pos(-0.5F, 0.5F, 0).tex(0F, 0F).rgba(-1).endVertex(); builder.pos(-0.5F, -0.5F, 0).tex(0F, 1F).rgba(-1).endVertex(); + Font font = fonts.createFont(18.5F); TestModel model = new TestModel(builder.getBytes()); + TestModel[] guiModel = new TestModel[1]; + List draws = new ObjectArrayList<>(); + font.drawText("Testing my Text", 50, 50, -1, new TexturedBuffer((K, V) -> { + draws.addAll(K); + guiModel[0] = new TestModel(V); + System.out.println("Testing: "+V.length+" bytes, "+K.size()); + })); GL11.glClearColor(0.2F, 0.55F, 0.66F, 1F); while(!window.shouldClose()) { @@ -136,8 +163,16 @@ public class NewInputTest { shaderTest.get().texture.set(texture); model.bindArray(); GLStateTracker.drawArrays(GLMode.TRIANGLES.glValue(), 6); - model.unbindArray(); + guiModel[0].bindArray(); + guiShader.bind(); + for(GLDraw draw : draws) { +// System.out.println("Draw: "+draw.texture()+", "+draw.startVertex()+", "+draw.vertexCount()); + guiShader.get().texture.set(draw.texture()); + GLStateTracker.drawArrays(GLMode.TRIANGLES.glValue(), draw.startVertex(), draw.vertexCount()); + } + guiModel[0].unbindArray(); + window.handleInput(); window.finishFrame(); try { Thread.sleep(100); } @@ -147,15 +182,56 @@ public class NewInputTest { manager.destroy(); } - private BitmapFontProvider loadProvider(AssetLocation location, float desiredSize) - { - try(IAsset asset = assets.getAsset(location)) - { - JsonObject obj = asset.json(); - return BitmapFontProvider.load(obj, desiredSize, null); + public static class TexturedBuffer implements Font.TexturedBuffer { + int previousId = -1; + int lastVertex = 0; + List draws = new ObjectArrayList<>(); + VertexBuilder builder; + BiConsumer, byte[]> callbacks; + + public TexturedBuffer(BiConsumer, byte[]> callbacks) { + this.callbacks = callbacks; } - catch(Exception e) { e.printStackTrace(); } - return null; + + @Override + public IVertexBuilder builderForTexture(int textureId) { + if(builder == null) { + builder = new VertexBuilder(100000); + builder.start(GLMode.TRIANGLES, VertexTypes.TESTING); + previousId = textureId; + System.out.println("Texture: "+textureId); + } + else if(previousId != textureId) { + int count = builder.size() - lastVertex; + draws.add(new GLDraw(previousId, lastVertex, count)); + lastVertex+=count; + previousId = textureId; + System.out.println("Texture: "+textureId); + } + return builder; + } + + @Override + public void end() { + int count = builder.size() - lastVertex; + draws.add(new GLDraw(previousId, lastVertex, count)); + callbacks.accept(draws, builder.getBytes()); + } + + } + + public static record GLDraw(int texture, int startVertex, int vertexCount) { + + } + + public static class GuiShader extends SimpleShader { + public TextureUniform texture = uniforms.addTexture("texture", 0); + public Matrix4fUniform proView = uniforms.addMat("proViewMatrix", new Matrix4f()); + + public GuiShader(IAssetProvider provider) { + super(provider, "gui_shader", AssetLocation.of("shader/testGui/vertex.vs"), AssetLocation.of("shader/testGui/fragment.fs"), "in_position", "in_tex", "in_color"); + } + } public static class TestShader extends SimpleShader { diff --git a/src/main/java/speiger/src/coreengine/assets/AssetFilter.java b/src/main/java/speiger/src/coreengine/assets/AssetFilter.java index 3995087..17889d3 100644 --- a/src/main/java/speiger/src/coreengine/assets/AssetFilter.java +++ b/src/main/java/speiger/src/coreengine/assets/AssetFilter.java @@ -32,4 +32,8 @@ public record AssetFilter(String prefix, String extension) { return provider.listAllAssets(prefix, T -> T.endsWith(extension)); } + public Map multiRoot(IAssetProvider provider) { + return provider.listAllAssets(prefix, T -> T.endsWith(extension) && !T.isInFolder(prefix)); + } + } diff --git a/src/main/java/speiger/src/coreengine/assets/AssetLocation.java b/src/main/java/speiger/src/coreengine/assets/AssetLocation.java index 4de8426..5bdfae6 100644 --- a/src/main/java/speiger/src/coreengine/assets/AssetLocation.java +++ b/src/main/java/speiger/src/coreengine/assets/AssetLocation.java @@ -26,6 +26,10 @@ public final class AssetLocation implements Comparable { public boolean isInFolder() { return location.contains("/"); } public boolean isInFolder(String prefix) { return location.indexOf("/", prefix.length()+1) >= 0; } + public AssetLocation prefix(String prefix) { + return of(domain, prefix+"/"+location); + } + public AssetLocation subAsset(String subPath) { return of(domain+":"+location+"/"+subPath); } @@ -67,8 +71,40 @@ public final class AssetLocation implements Comparable { return LOCATION.computeIfAbsent(location.indexOf(":") == -1 ? "base:"+location : location, AssetLocation::compute); } + public static final AssetLocation tryOf(String location) { + try { return of(location); } + catch(Exception e) {} + return null; + } + private static final AssetLocation compute(String s) { int index = s.indexOf(":"); - return new AssetLocation(s.substring(0, index), s.substring(index + 1)); + String domain = s.substring(0, index); + String path = s.substring(index + 1); + if(!isValidDomain(domain)) throw new IllegalArgumentException("Non [a-zA-Z0-9_.-] Character found in domain of location ["+domain+":"+path+"]"); + if(!isValidPath(path)) throw new IllegalArgumentException("Non [a-zA-Z0-9/_.-] Character found in path of location ["+domain+":"+path+"]"); + return new AssetLocation(domain, path); + } + + private static boolean isValidDomain(String s) { + for(int i = 0,m=s.length();i= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); } } diff --git a/src/main/java/speiger/src/coreengine/assets/base/MultiAsset.java b/src/main/java/speiger/src/coreengine/assets/base/MultiAsset.java index 1f80c17..cb434ee 100644 --- a/src/main/java/speiger/src/coreengine/assets/base/MultiAsset.java +++ b/src/main/java/speiger/src/coreengine/assets/base/MultiAsset.java @@ -1,7 +1,11 @@ package speiger.src.coreengine.assets.base; import java.io.Closeable; +import java.io.IOException; +import java.util.Iterator; +import java.util.NoSuchElementException; import java.util.function.Consumer; +import java.util.function.Supplier; import speiger.src.collections.ints.functions.consumer.IntObjectConsumer; import speiger.src.collections.objects.collections.ObjectIterable; @@ -37,6 +41,24 @@ public class MultiAsset implements Closeable, ObjectIterable { return assets.get(index); } + public Iterable map(AssetMapper mappingFunction, Supplier defaultValue) { + return () -> new Iterator() { + int index = 0; + int size = assets.size(); + @Override + public boolean hasNext() { return index < size; } + @Override + public T next() { + if(!hasNext()) throw new NoSuchElementException(); + try { return mappingFunction.apply(assets.get(index++)); } + catch(IOException e) { + e.printStackTrace(); + return defaultValue.get(); + } + } + }; + } + @Override public void forEach(Consumer action) { assets.forEach(action); @@ -56,4 +78,8 @@ public class MultiAsset implements Closeable, ObjectIterable { public ObjectIterator iterator() { return assets.iterator(); } + + public static interface AssetMapper { + public T apply(IAsset asset) throws IOException; + } } diff --git a/src/main/java/speiger/src/coreengine/assets/impl/FolderAssetPackage.java b/src/main/java/speiger/src/coreengine/assets/impl/FolderAssetPackage.java index b656c51..ce0abf1 100644 --- a/src/main/java/speiger/src/coreengine/assets/impl/FolderAssetPackage.java +++ b/src/main/java/speiger/src/coreengine/assets/impl/FolderAssetPackage.java @@ -28,8 +28,8 @@ public class FolderAssetPackage implements IAssetPackage { @Override public void getModifiedTime(String domain, BiConsumer accept) { try { - Path start = baseFolder.resolve("assets"); - for(Path path : IterableWrapper.wrap(Files.walk(start.resolve(domain)).filter(Files::isRegularFile).iterator())) { + Path start = baseFolder.resolve("assets/").resolve(domain); + for(Path path : IterableWrapper.wrap(Files.walk(start).filter(Files::isRegularFile).iterator())) { accept.accept(AssetLocation.of(domain, start.relativize(path).toString().replace("\\", "/")), Files.getLastModifiedTime(path)); } } diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/font/Font.java b/src/main/java/speiger/src/coreengine/rendering/gui/font/Font.java index b1d2417..5390da4 100644 --- a/src/main/java/speiger/src/coreengine/rendering/gui/font/Font.java +++ b/src/main/java/speiger/src/coreengine/rendering/gui/font/Font.java @@ -1,24 +1,39 @@ package speiger.src.coreengine.rendering.gui.font; import java.util.Map; +import java.util.function.Consumer; 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.UnbakedGlyth.GlythBaker; +import speiger.src.coreengine.rendering.gui.font.glyth.GlythData.GlythBaker; +import speiger.src.coreengine.rendering.gui.font.glyth.MissingGlyth; +import speiger.src.coreengine.rendering.tesselation.buffer.IVertexBuilder; public class Font { + private static final AssetLocation DEFAULT = AssetLocation.of("default"); + Map fonts; GlythBaker baker; float size; float oversample; GlythCache[] styledCache = new GlythCache[] {new GlythCache(this, 0), new GlythCache(this, 1), new GlythCache(this, 2), new GlythCache(this, 3)}; + MissingGlyth missingGlyth; - protected Font(Map fonts, GlythBaker baker, float size, float oversample) { + protected Font(Map fonts, GlythBaker baker, float size, float oversample, Consumer clearing) { this.fonts = fonts; this.baker = baker; this.size = size; this.oversample = oversample; + this.missingGlyth = new MissingGlyth(size, oversample * 2F); + clearing.accept(this::reset); + } + + private void reset() { + for(int i = 0;i<4;i++) { + styledCache[i].reset(); + } + missingGlyth.cleanCache(); } public GlythData data(AssetLocation font, int codepoint, int style) { @@ -32,4 +47,39 @@ public class Font { protected FontGroup font(AssetLocation font) { return fonts.get(font); } + + public void drawText(String text, float x, float y, int color, TexturedBuffer buffer) { + int previousCodepoint = -1; + for(int i = 0,m=text.length();i providers; - StyledFont[] cache = new StyledFont[] {new StyledFont(0), new StyledFont(1), new StyledFont(2), new StyledFont(3)}; public FontGroup(AssetLocation locations, List providers) { this.locations = locations; this.providers = providers; } - - public UnbakedGlyth unbaked(int codepoint, int style) { - return cache[style & 0x3].unbaked(codepoint); + + public GlythData data(int codepoint, int style, float size, float oversample) { + for(int i = 0,m=providers.size();i dataGlyths = new Int2ObjectOpenHashMap<>(); - - public StyledFont(int style) { - this.style = style; - } - - public UnbakedGlyth unbaked(int codepoint) { - return dataGlyths.computeIfAbsent(codepoint, this::compute); - } - - private UnbakedGlyth compute(int codepoint) { - for(int i = 0,m=providers.size();i>> { +public class FontManager extends SteppedReloadableAsset>> { private static final int TEXTURE_SIZE = 2048; + private static final AssetFilter FILTER = AssetFilter.json("font"); Map fonts = Object2ObjectMap.builder().linkedMap(); + Map> fontParsers = Object2ObjectMap.builder().map(); + List listeners = new ObjectArrayList<>(); List textures = new ObjectArrayList<>(); + public FontManager() { + registerParser("stb-ttf", STBTrueTypeProvider::create); + } + + public void registerParser(String id, BiFunction parser) { + fontParsers.putIfAbsent(id, parser); + } + @Override - public String getName() { return null; } - + public String getName() { return "Font Manager"; } + + @Override + protected Map> prepare(IAssetProvider provider) { + Map loadingCache = Object2ObjectMap.builder().linkedMap(); + Object2ObjectMap> providers = Object2ObjectMap.builder().linkedMap(); + for(Entry entry : FILTER.multiRoot(provider).entrySet()) { + AssetLocation id = entry.getKey(); + for(JsonObject obj : entry.getValue().map(IAsset::json, JsonObject::new)) { + JsonUtil.iterateValues(obj.get("providers"), T -> { + AssetLocation location = AssetLocation.tryOf(T.getAsString()); + if(location == null || !location.isInFolder()) return; + IFontProvider font = loadingCache.computeIfAbsent(location.prefix("font"), E -> getFont(E, provider)); + if(font == null) return; + providers.supplyIfAbsent(FILTER.id(id), ObjectArrayList::new).add(font); + }); + } + } + return providers; + } + + @Override + protected void apply(Map> value, IAssetProvider provider) { + reset(); + value.forEach((K, V) -> fonts.put(K, new FontGroup(K, V))); + } + @Override public void destroy() { - + reset(); + listeners.clear(); } - @Override - protected Map> prepare(IAssetProvider provider) { - return null; - } - - @Override - protected void apply(Map> value, IAssetProvider provider) { - + private void reset() { + listeners.forEach(Runnable::run); + textures.forEach(FontTexture::delete); + textures.clear(); + fonts.values().forEach(FontGroup::close); + fonts.clear(); } public Font createFont(float size) { @@ -42,7 +85,7 @@ public class FontManager extends SteppedReloadableAsset builder = fontParsers.get(obj.get("type").getAsString()); + return builder == null ? null : builder.apply(obj, provider); + } + catch(Exception e) { + e.printStackTrace(); + } + return null; + } } \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/font/FontTexture.java b/src/main/java/speiger/src/coreengine/rendering/gui/font/FontTexture.java index 3f10e77..8609c2b 100644 --- a/src/main/java/speiger/src/coreengine/rendering/gui/font/FontTexture.java +++ b/src/main/java/speiger/src/coreengine/rendering/gui/font/FontTexture.java @@ -1,5 +1,7 @@ package speiger.src.coreengine.rendering.gui.font; +import org.lwjgl.system.MemoryUtil; + 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; @@ -43,7 +45,11 @@ public class FontTexture extends BaseTexture { createTexture(); TextureMetadata metadata = metadataByColor(color); metadata.applyArguments(textureType()); - GLFunctions.upload2DImage(textureType(), 0, metadata, bounds, bounds, 0, 0L); + long allocate = MemoryUtil.nmemAlloc(bounds * bounds * formatByColor(color).components()); + ITextureComponentFormat format = formatByColor(color); + GLFunctions.upload2DImage(textureType(), 0, format, bounds, bounds, 0, format, GLDataType.UNSIGNED_BYTE, allocate); +// GLFunctions.upload2DImage(textureType(), 0, metadata, bounds, bounds, 0, allocate); + MemoryUtil.nmemFree(allocate); } public Glyth build(IGlythSheetInfo info) { diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/font/GlythCache.java b/src/main/java/speiger/src/coreengine/rendering/gui/font/GlythCache.java index 7c12d4a..fb22c36 100644 --- a/src/main/java/speiger/src/coreengine/rendering/gui/font/GlythCache.java +++ b/src/main/java/speiger/src/coreengine/rendering/gui/font/GlythCache.java @@ -19,6 +19,10 @@ public class GlythCache { this.style = style; } + public void reset() { + cache.clear(); + } + private FontCache cache(AssetLocation font) { return cache.computeIfAbsent(font, FontCache::new); } @@ -39,7 +43,7 @@ public class GlythCache { public FontCache(AssetLocation fontId) { this.fontId = fontId; } - + public GlythData data(int codepoint) { return data.computeIfAbsent(codepoint, this::compute); } @@ -49,12 +53,12 @@ public class GlythCache { } private GlythData compute(int codepoint) { - return new GlythData(font.font(fontId).unbaked(codepoint, style), font.size); + GlythData data = font.font(fontId).data(codepoint, style, font.size, font.oversample); + return data == null ? font.missingGlyth : data; } private Glyth bake(int codepoint) { - return font.font(fontId).unbaked(codepoint, style).bake(font.baker, font.size, font.oversample); + return data(codepoint).bake(font.baker); } - } } \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/font/glyth/Glyth.java b/src/main/java/speiger/src/coreengine/rendering/gui/font/glyth/Glyth.java index aa93c76..f5e09c3 100644 --- a/src/main/java/speiger/src/coreengine/rendering/gui/font/glyth/Glyth.java +++ b/src/main/java/speiger/src/coreengine/rendering/gui/font/glyth/Glyth.java @@ -10,4 +10,6 @@ public record Glyth(int texture, float minU, float minV, float maxU, float maxV, public Glyth(int texture, float minU, float minV, float maxU, float maxV) { this(texture, minU, minV, maxU, maxV, 0, 0, 0, 0); } + + public boolean isValid() { return texture != -1; } } diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/font/glyth/GlythData.java b/src/main/java/speiger/src/coreengine/rendering/gui/font/glyth/GlythData.java index eaaa04e..8385c93 100644 --- a/src/main/java/speiger/src/coreengine/rendering/gui/font/glyth/GlythData.java +++ b/src/main/java/speiger/src/coreengine/rendering/gui/font/glyth/GlythData.java @@ -1,26 +1,20 @@ package speiger.src.coreengine.rendering.gui.font.glyth; -import speiger.src.collections.ints.functions.function.Int2FloatFunction; -import speiger.src.collections.ints.maps.impl.hash.Int2FloatOpenHashMap; -import speiger.src.collections.ints.maps.interfaces.Int2FloatMap; - -public class GlythData { - float advance; - float shadowOffset; - Int2FloatMap kernlings = new Int2FloatOpenHashMap(); - Int2FloatFunction kernling; +public interface GlythData { + public float advance(); + public float kernling(int codepoint); + public default float shadowOffset() { return 1F; } + public Glyth bake(GlythBaker baker); - public GlythData(float advance, float shadowOffset, Int2FloatFunction kernling) { - this.advance = advance; - this.shadowOffset = shadowOffset; - this.kernling = kernling; + + public static record EmptyGlythData(float advance) implements GlythData { + @Override + public Glyth bake(GlythBaker baker) { return Glyth.EMPTY; } + @Override + public float kernling(int codepoint) { return 0F; } } - public GlythData(UnbakedGlyth glyth, float size) { - this(glyth.advance(size), glyth.shadowOffset(), glyth::kernling); + public static interface GlythBaker { + Glyth bake(IGlythSheetInfo info); } - - public float advance() { return advance; } - public float shadowOffset() { return shadowOffset; } - public float kernling(int codepoint) { return kernlings.computeFloatIfAbsent(codepoint, kernling); } -} +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/font/glyth/GlythDataOld.java b/src/main/java/speiger/src/coreengine/rendering/gui/font/glyth/GlythDataOld.java new file mode 100644 index 0000000..f9c921c --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/font/glyth/GlythDataOld.java @@ -0,0 +1,26 @@ +package speiger.src.coreengine.rendering.gui.font.glyth; + +import speiger.src.collections.ints.functions.function.Int2FloatFunction; +import speiger.src.collections.ints.maps.impl.hash.Int2FloatOpenHashMap; +import speiger.src.collections.ints.maps.interfaces.Int2FloatMap; + +public class GlythDataOld { + float advance; + float shadowOffset; + Int2FloatMap kernlings = new Int2FloatOpenHashMap(); + Int2FloatFunction kernling; + + public GlythDataOld(float advance, float shadowOffset, Int2FloatFunction kernling) { + this.advance = advance; + this.shadowOffset = shadowOffset; + this.kernling = kernling; + } + + public GlythDataOld(GlythData glyth) { + this(glyth.advance(), glyth.shadowOffset(), glyth::kernling); + } + + public float advance() { return advance; } + public float shadowOffset() { return shadowOffset; } + public float kernling(int codepoint) { return kernlings.computeFloatIfAbsent(codepoint, kernling); } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/font/glyth/IGlythSheetInfo.java b/src/main/java/speiger/src/coreengine/rendering/gui/font/glyth/IGlythSheetInfo.java index 0b42b55..981e4fc 100644 --- a/src/main/java/speiger/src/coreengine/rendering/gui/font/glyth/IGlythSheetInfo.java +++ b/src/main/java/speiger/src/coreengine/rendering/gui/font/glyth/IGlythSheetInfo.java @@ -3,6 +3,15 @@ package speiger.src.coreengine.rendering.gui.font.glyth; public interface IGlythSheetInfo { public int width(); public int height(); + public default float xOffset() { return 0F; } + public default float yOffset() { return 3F; } + public float oversample(); public boolean isColored(); public void upload(int x, int y); + + public default float left() { return xOffset(); } + public default float right() { return xOffset() + width() / oversample(); } + public default float top() { return yOffset(); } + public default float bottom() { return yOffset() + height() / oversample(); } + } diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/font/glyth/MissingGlyth.java b/src/main/java/speiger/src/coreengine/rendering/gui/font/glyth/MissingGlyth.java new file mode 100644 index 0000000..5340cb3 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/font/glyth/MissingGlyth.java @@ -0,0 +1,64 @@ +package speiger.src.coreengine.rendering.gui.font.glyth; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.rendering.gui.font.FontTexture; +import speiger.src.coreengine.rendering.textures.custom.Drawable; + +public class MissingGlyth implements GlythData { + private static final int WIDTH = 10; + private static final int HEIGHT = 57; + float size; + float oversample; + float advance; + float scale; + int width; + int height; + Glyth cached; + + public MissingGlyth(float size, float oversample) { + this.size = size; + this.oversample = oversample; + this.scale = (size * oversample) / HEIGHT; + this.advance = WIDTH * (size / HEIGHT); + this.width = (int)(WIDTH * scale); + this.height = (int)(HEIGHT * scale); + } + + public void cleanCache() { + cached = null; + } + + @Override + public float advance() { return advance; } + @Override + public float kernling(int codepoint) { return 0; } + @Override + public Glyth bake(GlythBaker baker) { + if(cached == null) { + cached = baker.bake(new IGlythSheetInfo() { + @Override + public int width() { return width; } + @Override + public int height() { return height; } + @Override + public void upload(int x, int y) { + Drawable drawable = new Drawable(FontTexture.formatByColor(false), width, height); + draw(drawable); + drawable.upload(x, y, 0, 0, width, height); + drawable.close(); + } + @Override + public boolean isColored() { return false; } + @Override + public float oversample() { return oversample; } + }); + } + return cached; + } + + private void draw(Drawable texture) { + texture.fill(0, 0, width, height, 255); + int offset = MathUtils.ceil(width / 7.5F); + texture.fill(offset, offset, width - offset * 2, height - offset * 2, 0); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/font/glyth/UnbakedGlyth.java b/src/main/java/speiger/src/coreengine/rendering/gui/font/glyth/UnbakedGlyth.java deleted file mode 100644 index 89f1ac1..0000000 --- a/src/main/java/speiger/src/coreengine/rendering/gui/font/glyth/UnbakedGlyth.java +++ /dev/null @@ -1,22 +0,0 @@ -package speiger.src.coreengine.rendering.gui.font.glyth; - -public interface UnbakedGlyth { - public float advance(float size); - public float kernling(int codepoint); - public default float shadowOffset() { return 1F; } - public Glyth bake(GlythBaker baker, float size, float oversample); - - - public static record EmptyGlythData(float advance) implements UnbakedGlyth { - @Override - public Glyth bake(GlythBaker baker, float size, float oversample) { return Glyth.EMPTY; } - @Override - public float advance(float size) { return advance; } - @Override - public float kernling(int codepoint) { return 0F; } - } - - public static interface GlythBaker { - Glyth bake(IGlythSheetInfo info); - } -} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/font/providers/BitmapProivder.java b/src/main/java/speiger/src/coreengine/rendering/gui/font/providers/BitmapProivder.java deleted file mode 100644 index 5fa9699..0000000 --- a/src/main/java/speiger/src/coreengine/rendering/gui/font/providers/BitmapProivder.java +++ /dev/null @@ -1,41 +0,0 @@ -package speiger.src.coreengine.rendering.gui.font.providers; - -import speiger.src.coreengine.rendering.gui.font.glyth.Glyth; -import speiger.src.coreengine.rendering.gui.font.glyth.UnbakedGlyth; - -public class BitmapProivder implements IFontProvider { - - - @Override - public UnbakedGlyth glythData(int codepoint, int style) { - return null; - } - - @Override - public void close() { - - } - - private static class BitmapGlyth implements UnbakedGlyth { - int minX; - int minY; - int maxX; - int maxY; - int glyth; - - @Override - public Glyth bake(GlythBaker baker, float size, float oversample) { - return null; - } - - @Override - public float advance(float size) { - return 0; - } - - @Override - public float kernling(int codepoint) { - return 0; - } - } -} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/font/providers/IFontProvider.java b/src/main/java/speiger/src/coreengine/rendering/gui/font/providers/IFontProvider.java index 1a6585d..7e38696 100644 --- a/src/main/java/speiger/src/coreengine/rendering/gui/font/providers/IFontProvider.java +++ b/src/main/java/speiger/src/coreengine/rendering/gui/font/providers/IFontProvider.java @@ -1,15 +1,14 @@ package speiger.src.coreengine.rendering.gui.font.providers; -import speiger.src.coreengine.rendering.gui.font.glyth.UnbakedGlyth; +import speiger.src.coreengine.rendering.gui.font.glyth.GlythData; -public interface IFontProvider extends AutoCloseable { +public interface IFontProvider { public static final int REGULAR = 0; public static final int BOLD = 1; public static final int ITALIC = 2; public static final int ITALIC_BOLD = 3; - public UnbakedGlyth glythData(int codepoint, int style); - @Override - void close(); + public GlythData glythData(int codepoint, int style, float size, float oversample); + public void close(); } diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/font/providers/STBTrueTypeProvider.java b/src/main/java/speiger/src/coreengine/rendering/gui/font/providers/STBTrueTypeProvider.java index dba0a59..89c759a 100644 --- a/src/main/java/speiger/src/coreengine/rendering/gui/font/providers/STBTrueTypeProvider.java +++ b/src/main/java/speiger/src/coreengine/rendering/gui/font/providers/STBTrueTypeProvider.java @@ -18,11 +18,11 @@ import speiger.src.coreengine.assets.base.IAssetProvider; import speiger.src.coreengine.assets.parsers.NativeMemoryParser; import speiger.src.coreengine.rendering.gui.font.FontTexture; import speiger.src.coreengine.rendering.gui.font.glyth.Glyth; -import speiger.src.coreengine.rendering.gui.font.glyth.UnbakedGlyth; -import speiger.src.coreengine.rendering.gui.font.glyth.UnbakedGlyth.EmptyGlythData; +import speiger.src.coreengine.rendering.gui.font.glyth.GlythData; +import speiger.src.coreengine.rendering.gui.font.glyth.GlythData.EmptyGlythData; import speiger.src.coreengine.rendering.gui.font.glyth.IGlythSheetInfo; import speiger.src.coreengine.rendering.textures.custom.Drawable; -import speiger.src.coreengine.rendering.utils.values.textures.GLTextureFormat; +import speiger.src.coreengine.rendering.utils.GLStateTracker; import speiger.src.coreengine.utils.helpers.JsonUtil; public class STBTrueTypeProvider implements IFontProvider { @@ -53,7 +53,6 @@ public class STBTrueTypeProvider implements IFontProvider { private static TrueTypeInstance create(int style, JsonObject obj, IAssetProvider provider) { if(obj == null || !obj.has("file")) return null; AssetLocation location = AssetLocation.of(obj.get("file").getAsString()); - float size = JsonUtil.getOrDefault(obj, "size", 24F); float oversample = JsonUtil.getOrDefault(obj, "oversample", 1F); float shadowOffset = JsonUtil.getOrDefault(obj, "shadowOffset", 1F); float xOff = 0; @@ -74,7 +73,7 @@ public class STBTrueTypeProvider implements IFontProvider { info.free(); return null; } - return new TrueTypeInstance(style, MemoryUtil.memAddress(buffer), info, size, oversample, xOff, yOff, shadowOffset, builder.toString()); + return new TrueTypeInstance(style, MemoryUtil.memAddress(buffer), info, oversample, xOff, yOff, shadowOffset, builder.toString()); } catch(Exception e) { e.printStackTrace(); @@ -91,10 +90,9 @@ public class STBTrueTypeProvider implements IFontProvider { final float xOff; final float yOff; final float shadowOffset; - final float pointScale; final float ascent; - public TrueTypeInstance(int style, long data, STBTTFontinfo info, float size, float oversample, float xOff, float yOff, float shadowOffset, String skip) { + public TrueTypeInstance(int style, long data, STBTTFontinfo info, float oversample, float xOff, float yOff, float shadowOffset, String skip) { this.style = style; this.data = data; this.info = info; @@ -103,10 +101,9 @@ public class STBTrueTypeProvider implements IFontProvider { this.xOff = xOff; this.yOff = yOff; this.shadowOffset = shadowOffset; - pointScale = STBTruetype.stbtt_ScaleForPixelHeight(info, size * oversample); int[] ascent = new int[1]; STBTruetype.stbtt_GetFontVMetrics(info, ascent, new int[1], new int[1]); - this.ascent = pointScale * ascent[0]; + this.ascent = ascent[0]; } public void free() { @@ -116,37 +113,39 @@ public class STBTrueTypeProvider implements IFontProvider { data = 0L; } - public UnbakedGlyth glythData(int codepoint) { + public GlythData glythData(int codepoint, float size, float oversample) { if(skip.contains(codepoint)) return null; int glyth = STBTruetype.nstbtt_FindGlyphIndex(info.address(), codepoint); if(glyth == 0) return null; + oversample *= this.oversample; + float scale = STBTruetype.stbtt_ScaleForPixelHeight(info, size * oversample); try(MemoryStack stack = MemoryStack.stackPush()) { IntBuffer left = stack.mallocInt(1); IntBuffer bottom = stack.mallocInt(1); IntBuffer right = stack.mallocInt(1); IntBuffer top = stack.mallocInt(1); IntBuffer advance = stack.mallocInt(1); - IntBuffer leftSideBearing = stack.mallocInt(1); + IntBuffer leftSideBearing = stack.mallocInt(1); STBTruetype.stbtt_GetGlyphHMetrics(info, glyth, advance, leftSideBearing); - STBTruetype.stbtt_GetGlyphBitmapBoxSubpixel(info, glyth, pointScale, pointScale, xOff, yOff, left, bottom, right, top); + STBTruetype.stbtt_GetGlyphBitmapBoxSubpixel(info, glyth, scale, scale, xOff, yOff, left, bottom, right, top); int minX = left.get(0); int minY = -top.get(0); int maxX = right.get(0); int maxY = -bottom.get(0); - if(maxX - minX <= 0 || maxY - minY <= 0) return new EmptyGlythData(advance.get(0) * pointScale / oversample); - return new STBGlyth(this, minX, minY, maxX, maxY, advance.get(0) * pointScale, leftSideBearing.get(0) * pointScale, glyth); + if(maxX - minX <= 0 || maxY - minY <= 0) return new EmptyGlythData(advance.get(0) * scale / oversample); + return new STBGlyth(this, minX, minY, maxX, maxY, advance.get(0), leftSideBearing.get(0), scale, oversample, glyth); } - catch(Throwable exception) { - exception.printStackTrace(); + catch(Exception e) { + e.printStackTrace(); return null; } } } @Override - public UnbakedGlyth glythData(int codepoint, int style) { + public GlythData glythData(int codepoint, int style, float size, float oversample) { TrueTypeInstance instance = instances[style & 0x3]; - return instance == null ? null : instance.glythData(codepoint); + return instance == null ? null : instance.glythData(codepoint, size, oversample); } @Override @@ -159,48 +158,56 @@ public class STBTrueTypeProvider implements IFontProvider { instances = null; } - private static class STBGlyth implements UnbakedGlyth { + private static class STBGlyth implements GlythData { final TrueTypeInstance owner; + final float xOffset; + final float yOffset; final int width; final int height; + final float oversample; + final float scale; final float advance; final int glyth; - public STBGlyth(TrueTypeInstance owner, int minX, int minY, int maxX, int maxY, float advance, float leftPadding, int glyth) { + public STBGlyth(TrueTypeInstance owner, int minX, int minY, int maxX, int maxY, float advance, float leftPadding, float scale, float oversample, int glyth) { this.owner = owner; this.width = maxX - minX; this.height = maxY - minY; - this.advance = advance / owner.oversample; + this.scale = scale; + this.oversample = oversample; + this.xOffset = ((leftPadding * scale) + minX + owner.xOff) / oversample; + this.yOffset = ((owner.ascent * scale) - maxY + owner.yOff) / oversample; + this.advance = advance * scale / oversample; this.glyth = glyth; } @Override - public float advance(float size) { return advance; } + public float advance() { return advance; } @Override public float shadowOffset() { return owner.shadowOffset; } @Override - public float kernling(int codepoint) { return STBTruetype.stbtt_GetCodepointKernAdvance(owner.info, codepoint, glyth); } + public float kernling(int codepoint) { return STBTruetype.stbtt_GetCodepointKernAdvance(owner.info, codepoint, glyth) * scale / oversample; } @Override - public Glyth bake(GlythBaker baker, float size, float oversample) { + public Glyth bake(GlythBaker baker) { return baker.bake(new IGlythSheetInfo() { + @Override + public float xOffset() { return xOffset; } + @Override + public float yOffset() { return yOffset; } @Override public int width() { return width; } @Override public int height() { return height; } @Override + public float oversample() { return oversample; } + @Override public boolean isColored() { return false; } @Override public void upload(int x, int y) { + System.out.println("Testing: "+GLStateTracker.instance().textures.getTextureId(0)); Drawable drawable = new Drawable(FontTexture.formatByColor(false), width, height); - drawable.drawFont(owner.info, glyth, owner.xOff, owner.yOff, 0, 0, width, height, owner.pointScale, owner.pointScale); - Drawable upload = new Drawable(GLTextureFormat.RGBA, width, height); - int pattern = Integer.divideUnsigned(-1, 255); - for(int i = 0,m=width*height;i