Reworked a couple systems.

-Added: STBImage support.
-Added: Dynamic Font Loader that supports Bitmap/TTF fonts (ttf using java not STB, because small deadline)
-Added: NativeMemory Loader as optional parser into asset loading.
-Reworked: How Images are made reloadable.
-Added: A dynamic AtlasBuilder
This commit is contained in:
2021-09-28 01:35:07 +02:00
parent 9c947c4898
commit 4763e62b53
56 changed files with 1976 additions and 653 deletions
@@ -1,6 +1,7 @@
package speiger.src.coreengine.application;
import java.io.File;
import java.nio.ByteBuffer;
import java.util.function.IntConsumer;
import java.util.function.ObjLongConsumer;
@@ -11,6 +12,7 @@ import speiger.src.coreengine.assets.AssetManager;
import speiger.src.coreengine.assets.reloader.ResourceReloader;
import speiger.src.coreengine.rendering.gui.GuiManager;
import speiger.src.coreengine.rendering.gui.base.DebugOverlay;
import speiger.src.coreengine.rendering.gui.renderer.provider.FontManager;
import speiger.src.coreengine.rendering.input.Keyboard;
import speiger.src.coreengine.rendering.input.Mouse;
import speiger.src.coreengine.rendering.input.camera.Camera;
@@ -18,7 +20,8 @@ import speiger.src.coreengine.rendering.input.window.Window;
import speiger.src.coreengine.rendering.input.window.WindowProvider;
import speiger.src.coreengine.rendering.shader.ProjectionBuffer;
import speiger.src.coreengine.rendering.shader.ShaderTracker;
import speiger.src.coreengine.rendering.textures.TextureManager;
import speiger.src.coreengine.rendering.textures.base.NativeMemoryParser;
import speiger.src.coreengine.rendering.textures.base.TextureManager;
import speiger.src.coreengine.rendering.utils.Cursor;
import speiger.src.coreengine.utils.counters.timers.FPSTimer;
import speiger.src.coreengine.utils.eventbus.EventBus;
@@ -41,6 +44,7 @@ public abstract class Application
protected ResourceReloader reloader = new ResourceReloader();
protected AssetManager assetManager;
protected FontManager fonts = new FontManager();
protected ProjectionBuffer projectionBuffer;
protected GuiManager uiManager;
@@ -49,10 +53,11 @@ public abstract class Application
GLFWErrorCallback.createPrint(System.err).set();
if(!GLFW.glfwInit()) throw new IllegalStateException("OpenGL can't be loaded");
provider.init();
boolean initEarly = earlyUILoad();
try
{
mainWindow = createWindow(provider);
mainWindow.finishWindow();
if(initEarly) mainWindow.finishWindow();
}
catch(Exception e)
{
@@ -68,6 +73,7 @@ public abstract class Application
file = file.getName().endsWith(".jar") ? file : new File("bin/main");
internalInit(file);
init(file);
if(!initEarly) mainWindow.finishWindow();
executor.start(mainWindow);
mainWindow.destroy();
reloader.deleteResources();
@@ -78,8 +84,12 @@ public abstract class Application
protected void internalInit(File file)
{
assetManager = reloader.addReloadableResource(new AssetManager(file), true);
assetManager.registerAssetParser(ByteBuffer.class, new NativeMemoryParser());
reloader.addReloadableResource(fonts);
fonts.setAssetManager(assetManager);
ShaderTracker.INSTANCE.init(assetManager);
TextureManager.INSTANCE.init(assetManager);
preinit();
reloader.addReloadableResource(ShaderTracker.INSTANCE);
reloader.addReloadableResource(TextureManager.INSTANCE);
camera = new Camera(mainWindow);
@@ -95,8 +105,10 @@ public abstract class Application
public void addExtraTickRates(IntConsumer ticks) {};
public void addExtraTimers(ObjLongConsumer<String> profiler) {};
public boolean initUI() { return true; }
public boolean earlyUILoad() { return true; }
public DebugOverlay createCustomDebug() { return null; }
public abstract Window createWindow(WindowProvider provider) throws Exception;
public void preinit() {}
public abstract void init(File file);
public abstract void update();
public abstract void render(float particalTicks);
@@ -13,7 +13,7 @@ public class BaseUIManager extends GuiManager
public BaseUIManager(Application application)
{
super(application.mainWindow, application.eventBus);
super(application.mainWindow, application.eventBus, application.fonts);
this.application = application;
}
@@ -7,9 +7,9 @@ import java.util.function.Function;
import speiger.src.collections.objects.maps.impl.hash.Object2ObjectOpenHashMap;
import speiger.src.collections.objects.utils.maps.Object2ObjectMaps;
public final class AssetLocation
public final class AssetLocation implements Comparable<AssetLocation>
{
static final Map<String, AssetLocation> LOCATION = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<String, AssetLocation>());
static final Map<String, AssetLocation> LOCATION = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>());
static final Function<String, AssetLocation> BUILDER = AssetLocation::compute;
final String domain;
final String location;
@@ -74,6 +74,13 @@ public final class AssetLocation
return false;
}
@Override
public int compareTo(AssetLocation o)
{
int result = domain.compareToIgnoreCase(o.domain);
return result != 0 ? result : location.compareToIgnoreCase(location);
}
public boolean matches(AssetLocation location)
{
return location.domain.equals(domain) && location.location.equals(this.location);
@@ -5,21 +5,25 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import java.util.zip.ZipFile;
import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap;
import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap;
import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap.Entry;
import speiger.src.collections.objects.sets.ObjectLinkedOpenHashSet;
import speiger.src.collections.objects.utils.maps.Object2ObjectMaps;
import speiger.src.coreengine.assets.impl.FolderAssetPackage;
import speiger.src.coreengine.assets.impl.ZipAssetPackage;
import speiger.src.coreengine.assets.reloader.IReloadableResource;
public class AssetManager implements IReloadableResource
{
Map<String, DomainAssets> domains = new Object2ObjectLinkedOpenHashMap<>();
Object2ObjectMap<String, DomainAssets> domains = new Object2ObjectLinkedOpenHashMap<>();
Object2ObjectMap<Class<?>, IAssetParser<?>> parsers = new Object2ObjectLinkedOpenHashMap<>();
Path path;
@@ -121,11 +125,21 @@ public class AssetManager implements IReloadableResource
DomainAssets asset = domains.get(location.getDomain());
if(asset == null)
{
throw new FileNotFoundException("File["+location.toString()+"] not found");
throw new FileNotFoundException("Domain & File["+location.toString()+"] not found");
}
return asset.getAllAssets(location);
}
public Collection<AssetLocation> gatherAssets(String location, int maxDepth, Predicate<String> filter)
{
Set<AssetLocation> result = new ObjectLinkedOpenHashSet<>();
for(Entry<String, DomainAssets> asset : Object2ObjectMaps.fastIterable(domains))
{
asset.getValue().gatherAssets(AssetLocation.of(asset.getKey(), location), maxDepth, filter, result);
}
return result;
}
protected boolean isZipFile(Path file)
{
try(ZipFile zip = new ZipFile(file.toFile())) { return true; }
@@ -185,5 +199,13 @@ public class AssetManager implements IReloadableResource
}
return new MultiAsset(assets.toArray(new IAsset[assets.size()]));
}
public void gatherAssets(AssetLocation folder, int maxDepth, Predicate<String> filter, Collection<AssetLocation> result)
{
for(IAssetPackage entry : packages)
{
entry.getAllAssets(folder, filter, maxDepth, result);
}
}
}
}
@@ -5,6 +5,7 @@ import java.io.BufferedReader;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import com.google.gson.JsonObject;
@@ -16,11 +17,13 @@ public interface IAsset extends Closeable
public InputStream getStream() throws IOException;
public BufferedImage getTexture() throws Exception;
public ByteBuffer getBytes() throws IOException;
public BufferedReader getStringReader() throws IOException;
public JsonObject getJsonObject() throws IOException;
public BufferedImage getTexture() throws Exception;
public <T> T getCustom(Class<T> clz) throws IOException;
}
@@ -1,11 +1,14 @@
package speiger.src.coreengine.assets;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
public interface IAssetPackage
{
public void setParsers(Function<Class<?>, IAssetParser<?>> parsers);
public List<String> getDomains();
public IAsset getAsset(AssetLocation location);
public void getAllAssets(AssetLocation folder, Predicate<String> fileNames, int maxDepth, Collection<AssetLocation> result);
}
@@ -1,10 +1,11 @@
package speiger.src.coreengine.assets;
import java.io.Closeable;
import java.io.IOException;
import java.nio.file.Path;
import java.util.function.Consumer;
public interface IAssetParser<T>
{
public T parseAsset(Path path, Consumer<Closeable> autoCloser);
public T parseAsset(Path path, Consumer<Closeable> autoCloser) throws IOException;
}
@@ -5,6 +5,7 @@ import java.io.BufferedReader;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
@@ -76,6 +77,12 @@ public class FolderAsset implements IAsset
return markClosed(Files.newInputStream(path));
}
@Override
public ByteBuffer getBytes() throws IOException
{
return ByteBuffer.wrap(Files.readAllBytes(path));
}
@Override
public BufferedImage getTexture() throws Exception
{
@@ -5,14 +5,18 @@ import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.assets.IAsset;
import speiger.src.coreengine.assets.IAssetPackage;
import speiger.src.coreengine.assets.IAssetParser;
import speiger.src.coreengine.utils.collections.iterators.IterableWrapper;
public class FolderAssetPackage implements IAssetPackage
{
@@ -59,4 +63,22 @@ public class FolderAssetPackage implements IAssetPackage
Path path = baseFolder.resolve(location.getActualLocation());
return Files.exists(path) ? new FolderAsset(location, path, parsers) : null;
}
@Override
public void getAllAssets(AssetLocation folder, Predicate<String> fileNames, int maxDepth, Collection<AssetLocation> result)
{
Path start = baseFolder.resolve(folder.getActualLocation());
if(Files.notExists(start)) return;
try(Stream<Path> stream = Files.walk(start, maxDepth).filter(Files::isRegularFile).filter(T -> fileNames.test(T.getFileName().toString())))
{
for(Path path : IterableWrapper.wrap(stream.iterator()))
{
result.add(folder.subAsset(start.relativize(path).toString()));
}
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
@@ -4,6 +4,7 @@ import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.function.Function;
@@ -71,6 +72,12 @@ public class ZipAsset implements IAsset
return Files.newInputStream(path);
}
@Override
public ByteBuffer getBytes() throws IOException
{
return ByteBuffer.wrap(Files.readAllBytes(path));
}
@Override
public BufferedImage getTexture() throws Exception
{
@@ -7,15 +7,19 @@ import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.assets.IAsset;
import speiger.src.coreengine.assets.IAssetPackage;
import speiger.src.coreengine.assets.IAssetParser;
import speiger.src.coreengine.utils.collections.iterators.IterableWrapper;
public class ZipAssetPackage implements IAssetPackage
{
@@ -90,4 +94,23 @@ public class ZipAssetPackage implements IAssetPackage
}
catch(Exception e) { return null; }
}
@Override
public void getAllAssets(AssetLocation folder, Predicate<String> fileNames, int maxDepth, Collection<AssetLocation> result)
{
try(FileSystem system = FileSystems.newFileSystem(baseFolder, null))
{
Path start = system.getPath(folder.getActualLocation());
if(Files.notExists(start)) return;
try(Stream<Path> stream = Files.walk(start, maxDepth).filter(Files::isRegularFile).filter(T -> fileNames.test(T.getFileName().toString())))
{
for(Path path : IterableWrapper.wrap(stream.iterator()))
{
result.add(folder.subAsset(start.relativize(path).toString()));
}
}
catch(IOException e) { e.printStackTrace(); }
}
catch(Exception e) { e.printStackTrace(); }
}
}
@@ -6,7 +6,7 @@ import speiger.src.collections.objects.sets.ObjectLinkedOpenHashSet;
public class ResourceReloader
{
Set<IReloadableResource> resources = new ObjectLinkedOpenHashSet<IReloadableResource>();
Set<IReloadableResource> resources = new ObjectLinkedOpenHashSet<>();
boolean globalRemoval = false;
boolean reloading = false;
@@ -316,6 +316,16 @@ public class ColorObject
return "Color[r=" + getRedFloat() + ", g=" + getGreenFloat() + ", b=" + getBlueFloat() + ", a=" + getAlphaFloat() + "]";
}
public static byte[] toByteArray(int color, boolean alpha)
{
byte[] data = new byte[alpha ? 4 : 3];
data[0] = (byte)((color >> 16) & 0xFF);
data[1] = (byte)((color >> 8) & 0xFF);
data[2] = (byte)(color & 0xFF);
if(alpha) data[3] = (byte)((color >> 24) & 0xFF);
return data;
}
public static void pack(int color, boolean alpha, ByteBuffer buffer)
{
buffer.put((byte)((color >> 16) & 0xFF)).put((byte)((color >> 8) & 0xFF)).put((byte)(color & 0xFF));
@@ -1,118 +0,0 @@
package speiger.src.coreengine.rendering.gui;
import java.io.BufferedReader;
import java.util.HashMap;
import java.util.Map;
import speiger.src.collections.chars.maps.impl.hash.Char2ObjectOpenHashMap;
import speiger.src.collections.chars.maps.interfaces.Char2ObjectMap;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.assets.IAsset;
import speiger.src.coreengine.rendering.gui.renderer.FontRenderer;
import speiger.src.coreengine.rendering.gui.renderer.IFontRenderer.CharInstance;
import speiger.src.coreengine.rendering.textures.SimpleTexture;
import speiger.src.coreengine.rendering.textures.TextureManager;
public class FontLoader
{
public static FontRenderer createFont(AssetLocation location, String name)
{
return createFont(location, name, 0.35F);
}
public static FontRenderer createFont(AssetLocation location, String name, float scale)
{
try(IAsset asset = TextureManager.INSTANCE.getManager().getAsset(location.subAsset(name+".fnt")))
{
Char2ObjectMap<CharInstance>[] maps = new Char2ObjectMap[]{new Char2ObjectOpenHashMap<CharInstance>(), new Char2ObjectOpenHashMap<CharInstance>()};
BufferedReader reader = asset.getStringReader();
FontInfo info = new FontInfo(convert(reader.readLine().split(", ")));
String value = null;
while((value = getNextValidLine(reader)) != null)
{
Map<String, Integer> dataMap = convert(value.split(", "));
CharInstance instance = createChar(dataMap, info);
instance.scale(scale);
maps[instance.isBold() ? 1 : 0].putIfAbsent(instance.getCharacter(), instance);
}
info.scale(scale);
return new FontRenderer(maps, new SimpleTexture(location.subAsset(name+"-Texture.png")), info.fontHeight, info.lineHeight);
}
catch(Exception e)
{
e.printStackTrace();
}
return null;
}
static String getNextValidLine(BufferedReader reader) throws Exception
{
String line = reader.readLine();
if(line == null)
{
return null;
}
else if(line.isEmpty() || line.startsWith("//"))
{
return getNextValidLine(reader);
}
return line;
}
static CharInstance createChar(Map<String, Integer> data, FontInfo info)
{
char character = (char)data.get("letter").intValue();
int minX = data.get("minX");
int minY = data.get("minY");
int maxX = data.get("maxX");
int maxY = data.get("maxY");
return new CharInstance(character, maxX - minX, maxY - minY, info.getTextureU(minX), info.getTextureV(minY), info.getTextureU(maxX), info.getTextureV(maxY), maxX - minX, data.getOrDefault("bold", 0).intValue() == 1);
}
static Map<String, Integer> convert(String[] data)
{
Map<String, Integer> map = new HashMap<String, Integer>();
for(String s : data)
{
int index = s.indexOf("=");
if(index != -1)
{
String[] split = s.split("=");
map.put(split[0], Integer.parseInt(split[1]));
}
}
return map;
}
public static class FontInfo
{
int height;
int width;
float fontHeight;
float lineHeight;
public FontInfo(Map<String, Integer> data)
{
width = data.get("textureWidth");
height = data.get("textureHeight");
fontHeight = data.get("fontHeight");
lineHeight = data.get("base");
}
public void scale(float scale)
{
fontHeight *= scale;
lineHeight *= scale;
}
public float getTextureU(float value)
{
return value / width;
}
public float getTextureV(float value)
{
return value / height;
}
}
}
@@ -8,6 +8,7 @@ import speiger.src.coreengine.rendering.gui.base.DebugOverlay;
import speiger.src.coreengine.rendering.gui.renderer.FontRenderer;
import speiger.src.coreengine.rendering.gui.renderer.GuiShader;
import speiger.src.coreengine.rendering.gui.renderer.UIRenderer;
import speiger.src.coreengine.rendering.gui.renderer.provider.FontManager;
import speiger.src.coreengine.rendering.input.events.KeyEvent.CharTypeEvent;
import speiger.src.coreengine.rendering.input.events.KeyEvent.KeyPressEvent;
import speiger.src.coreengine.rendering.input.events.MouseEvent;
@@ -31,12 +32,13 @@ public abstract class GuiManager implements IWindowListener
protected ScaledResolution res;
protected long globalClock = 0L;
protected boolean isReloading = false;
protected FontRenderer font = FontLoader.createFont(AssetLocation.of("font"), "Roboto-Font");
protected FontRenderer font;
protected GuiShader shader = ShaderTracker.INSTANCE.register(GuiShader::create, T -> shader = T);
public GuiManager(Window window, EventBus bus)
public GuiManager(Window window, EventBus bus, FontManager manager)
{
this.window = window;
font = manager.loadFont(AssetLocation.of("font/roboto.json"), 18.5F);
bus.register(MouseEvent.class, this::onMouseEvent);
bus.register(KeyPressEvent.class, (T) -> T.setCanceled(onKeyPressed(T.key)));
bus.register(CharTypeEvent.class, (T) -> T.setCanceled(onCharTyped(T.character, T.codePoint)));
@@ -1,19 +1,24 @@
package speiger.src.coreengine.rendering.gui;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.rendering.textures.ITexture;
import speiger.src.coreengine.rendering.textures.SimpleTexture;
import speiger.src.coreengine.rendering.textures.base.ITexture;
public class UITextures
{
public static final AssetLocation TEXTURE_LOCATION = AssetLocation.of("textures");
// public static final ITexture OK_SYMBOL = new SimpleTexture(sub("okSymbol.png"));
// public static final ITexture CANCLE_SYMBOL = new SimpleTexture(sub("cancelSymbol.png"));
public static final ITexture COLOR_WHEEL = new SimpleTexture(sub("colorWheel.png"));
public static final ITexture COLOR_WHEEL = ITexture.simple(sub("colorWheel.png")).makeReloadable();
public static AssetLocation sub(String name)
{
public static ITexture createReloadable(String name) {
return ITexture.simple(sub(name)).makeReloadable();
}
public static ITexture create(String name) {
return ITexture.simple(sub(name));
}
public static AssetLocation sub(String name) {
return TEXTURE_LOCATION.subAsset(name);
}
}
@@ -2,7 +2,7 @@ package speiger.src.coreengine.rendering.gui.components;
import speiger.src.coreengine.math.misc.ColorObject;
import speiger.src.coreengine.rendering.gui.GuiComponent;
import speiger.src.coreengine.rendering.textures.ITexture;
import speiger.src.coreengine.rendering.textures.base.ITexture;
public class IconComponent extends GuiComponent
{
@@ -52,6 +52,7 @@ public class ListComponent<T extends IListEntry> extends GuiComponent
protected int selectionMode = 1;
protected int updateMode = 1;
protected float entryHeight;
protected float cachedWidth = 0F;
protected ScrollBarComponent verticalBar = new ScrollBarComponent(ColorObject.LIGHT_GRAY);
protected ScrollBarComponent horizontalBar = new ScrollBarComponent(ColorObject.LIGHT_GRAY).setHorizontal(true);
protected Vec2i lastMouse = Vec2i.newMutable();
@@ -416,6 +417,11 @@ public class ListComponent<T extends IListEntry> extends GuiComponent
return rangeIterator(start, MathUtils.clamp(0, entries.size() - 1, start + getIndexWidth()));
}
public float getCachedWidth()
{
return cachedWidth;
}
@Override
protected void repaint()
{
@@ -434,6 +440,7 @@ public class ListComponent<T extends IListEntry> extends GuiComponent
{
width = Math.max(width, entries.get(i).getWidth());
}
this.cachedWidth = width;
boolean lastVertical = this.verticalBar.isInUse();
boolean lastHorizontal = this.horizontalBar.isInUse();
verticalBar.setScrollMax(MathUtils.ceil(this.entries.size() * entryHeight));
@@ -2,7 +2,7 @@ package speiger.src.coreengine.rendering.gui.components.icon;
import speiger.src.coreengine.math.misc.ColorObject;
import speiger.src.coreengine.rendering.gui.renderer.UIRenderer;
import speiger.src.coreengine.rendering.textures.ITexture;
import speiger.src.coreengine.rendering.textures.base.ITexture;
public class TexturedIcon implements IIcon
{
@@ -0,0 +1,241 @@
package speiger.src.coreengine.rendering.gui.helper;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap;
import speiger.src.collections.objects.misc.pairs.ObjectObjectPair;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.math.vector.ints.Vec2i;
import speiger.src.coreengine.rendering.gui.renderer.IFontRenderer.CharInstance;
import speiger.src.coreengine.rendering.textures.custom.TextureAtlas;
import speiger.src.coreengine.rendering.textures.custom.TextureAtlas.AtlasEntry;
import speiger.src.coreengine.rendering.textures.custom.TextureAtlas.Builder;
public class FontBuilder
{
public static final int LITERAL = 1;
public static final int PLAIN = 2;
public static final int BOLD = 4;
public static ObjectObjectPair<BufferedImage, JsonObject> createBitmapFont(InputStream stream, float size)
{
ObjectObjectPair<ObjectObjectPair<Vec2i, BufferedImage>, List<WrittenChar>> result = createBitmapFont(stream, "ISO-8859-1", PLAIN | BOLD, size);
if(result == null) return null;
JsonArray array = new JsonArray();
result.getValue().forEach(T -> array.add(T.seralize()));
JsonObject info = new JsonObject();
ObjectObjectPair<Vec2i, BufferedImage> key = result.getKey();
info.addProperty("width", key.getValue().getWidth());
info.addProperty("height", key.getValue().getHeight());
info.addProperty("base", key.getKey().getX());
info.addProperty("charHeight", key.getKey().getY());
info.addProperty("tabs", 4);
JsonObject data = new JsonObject();
data.addProperty("type", "bitmap");
data.addProperty("file", "?");
data.add("info", info);
data.add("chars", array);
return ObjectObjectPair.of(key.getValue(), data);
}
public static ObjectObjectPair<ObjectObjectPair<Vec2i, BufferedImage>, List<WrittenChar>> createBitmapFont(InputStream ttf, String characters, int flags, float size)
{
try
{
Map<AssetLocation, CharData> toDraw = new Object2ObjectLinkedOpenHashMap<>();
Builder builder = TextureAtlas.create();
Consumer<CharData> data = T -> {
AssetLocation location = T.asLocation();
toDraw.put(location, T);
if(T.width > 0 && !builder.add(location, T.width, T.getExtraY(T.height))) throw new IllegalStateException("Character: " + location + " isnt Accepted, W=" + T.width + ", H=" + T.height);
};
Font font = Font.createFont(Font.TRUETYPE_FONT, ttf).deriveFont(size);
String validChars = (flags & LITERAL) != 0 ? characters : getChars(characters, font);
if((flags & PLAIN) != 0) loadFontData(font, validChars, data);
if((flags & BOLD) != 0) loadFontData(font.deriveFont(Font.BOLD), validChars, data);
ObjectObjectPair<ObjectObjectPair<Vec2i, BufferedImage>, List<WrittenChar>> result = ObjectObjectPair.mutableValue(new ObjectArrayList<>());
builder.buildHollow((K, V) -> {
BufferedImage image = new BufferedImage(K.getX(), K.getY(), BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = image.createGraphics();
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
graphics.setFont(font);
FontMetrics metric = graphics.getFontMetrics();
int extra = 0;
for(AtlasEntry entry : V)
{
CharData pair = toDraw.remove(entry.getLocation());
extra = pair.extraY;
graphics.setFont(pair.getFont());
graphics.setColor(Color.WHITE);
graphics.drawString(Character.toString(pair.getLetter()), entry.getX()-pair.xOffset, pair.getExtraY(entry.getY()+metric.getAscent()));
result.getValue().add(new WrittenChar(pair.getLetter(), entry.getX(), entry.getY(), entry.getWidth(), entry.getHeight(), pair.isBold()));
}
toDraw.values().forEach(T -> result.getValue().add(new WrittenChar(T.getLetter(), 0, 0, 0, 0, T.isBold())));
result.setKey(ObjectObjectPair.of(Vec2i.newVec(metric.getAscent()+extra, metric.getHeight()+extra), image));
graphics.dispose();
});
return result;
}
catch(Exception e)
{
e.printStackTrace();
return null;
}
}
private static float convert(float pos, float width)
{
return pos / width;
}
private static String getChars(String s, Font font)
{
CharsetEncoder encoder = Charset.forName(s).newEncoder();
StringBuilder builder = new StringBuilder();
for(char c = 0;c < Character.MAX_VALUE;c++)
{
if(encoder.canEncode(c) && font.canDisplay(c)) builder.append(c);
}
return builder.toString();
}
private static void loadFontData(Font font, String chars, Consumer<CharData> listener)
{
BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = (Graphics2D)image.getGraphics();
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
graphics.setFont(font);
FontMetrics metric = graphics.getFontMetrics();
int extraHeight = 0;
List<CharData> data = new ObjectArrayList<>();
for(char letter : chars.toCharArray())
{
Rectangle rect = font.layoutGlyphVector(graphics.getFontRenderContext(), new char[] {letter }, 0, 1, 0).getGlyphPixelBounds(0, graphics.getFontRenderContext(), 0.0F, 0.0F);
extraHeight = Math.min(extraHeight, rect.y);
data.add(new CharData(font, letter, rect, metric.charWidth(letter), metric.getHeight(), font.isBold()));
}
extraHeight = -(extraHeight+metric.getAscent());
for(int i = 0,m=data.size();i<m;listener.accept(data.get(i++).offset(extraHeight)));
graphics.dispose();
}
public static class WrittenChar
{
char letter;
int x;
int y;
int width;
int height;
boolean bold;
public WrittenChar(char letter, int x, int y, int width, int height, boolean bold)
{
this.letter = letter;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.bold = bold;
}
public JsonObject seralize()
{
JsonObject obj = new JsonObject();
obj.addProperty("char", (int)letter);
obj.addProperty("minX", x);
obj.addProperty("minY", y);
obj.addProperty("maxX", x+width);
obj.addProperty("maxY", y+height);
obj.addProperty("bold", bold);
return obj;
}
public CharInstance create(int textureWidth, int textureHeight)
{
return new CharInstance(letter, width, height, convert(x, textureWidth), convert(y, textureHeight), convert(x+width, textureWidth), convert(y+height, textureHeight), width, bold);
}
}
private static class CharData
{
Font font;
char letter;
int xOffset;
int width;
int height;
boolean bold;
int extraY = 0;
public CharData(Font font, char letter, Rectangle bounds, int width, int height, boolean bold)
{
this.font = font;
this.letter = letter;
if(bounds.x >= 0) this.width = width;
else
{
this.width = bounds.width == 0 ? width : bounds.width;
xOffset = bounds.x;
}
this.height = height;
this.bold = bold;
}
private CharData offset(int offset)
{
extraY += offset;
return this;
}
public boolean isBold()
{
return bold;
}
public Font getFont()
{
return font;
}
public int getExtraY(int asent)
{
return extraY + asent;
}
public char getLetter()
{
return letter;
}
public AssetLocation asLocation()
{
return AssetLocation.of("base", font.getFontName().replaceAll(" ", "_") + (bold ? "_Bold" : "") + "_" + ((int)letter));
}
}
}
@@ -7,6 +7,7 @@ public class TextFilter
public static final Predicate<String> INTEGER_ONLY = T -> {
try {
if(T == null || T.isEmpty());
else if(T.length() == 1 && T.charAt(0) == '-');
else Integer.parseInt(T);
return true;
}
@@ -16,6 +17,7 @@ public class TextFilter
public static final Predicate<String> FLOAT_ONLY = T -> {
try {
if(T == null || T.isEmpty());
else if(T.length() == 1 && T.charAt(0) == '-');
else Float.parseFloat(T);
return true;
}
@@ -5,7 +5,6 @@ import java.util.Locale;
import org.lwjgl.opengl.GL11;
import speiger.src.collections.chars.maps.interfaces.Char2ObjectMap;
import speiger.src.collections.floats.lists.FloatArrayList;
import speiger.src.collections.floats.lists.FloatList;
import speiger.src.collections.objects.lists.ObjectArrayList;
@@ -20,11 +19,12 @@ import speiger.src.coreengine.rendering.gui.renderer.lexer.Line;
import speiger.src.coreengine.rendering.gui.renderer.lexer.TextContext;
import speiger.src.coreengine.rendering.gui.renderer.lexer.TextContext.WordContext;
import speiger.src.coreengine.rendering.gui.renderer.lexer.TextLexer;
import speiger.src.coreengine.rendering.gui.renderer.provider.IFontProvider;
import speiger.src.coreengine.rendering.models.DrawCall;
import speiger.src.coreengine.rendering.tesselation.IVertexBuilder;
import speiger.src.coreengine.rendering.tesselation.Tesselator;
import speiger.src.coreengine.rendering.tesselation.VertexType;
import speiger.src.coreengine.rendering.textures.ITexture;
import speiger.src.coreengine.rendering.textures.base.ITexture;
import speiger.src.coreengine.utils.helpers.TextUtil;
public class FontRenderer implements IFontRenderer
@@ -36,45 +36,51 @@ public class FontRenderer implements IFontRenderer
public static final char LINE_SEPERATOR = '\n';
Tesselator bufferBuilder = new Tesselator(655340);
IFontProvider provider;
final TextLexer lexer = new TextLexer(this);
final DelayedRenderBuffer lineBuffer = new DelayedRenderBuffer();
final Char2ObjectMap<CharInstance>[] chars;
final ITexture texture;
final float height;
final float baseLine;
final float space;
public FontRenderer(Char2ObjectMap<CharInstance>[] chars, ITexture texture, float height, float baseLine)
public void setProvider(IFontProvider provider)
{
this.chars = chars;
this.texture = texture;
this.height = height;
this.baseLine = baseLine;
space = chars[0].get(' ').getXAdvance();
this.provider = provider;
}
@Override
public CharInstance getInstance(char letter, boolean isBold)
{
return chars[isBold ? 1 : 0].get(letter);
return provider.getCharacter(letter, isBold);
}
@Override
public float getFontHeight()
{
return height;
return provider.getFontHeight();
}
@Override
public float getBaseLine()
{
return baseLine;
return provider.getBaseLine();
}
@Override
public ITexture getTexture()
{
return texture;
return provider.getTexture();
}
public IFontProvider getProvider()
{
return provider;
}
public void destory()
{
if(provider != null)
{
provider.destroy();
provider = null;
}
}
public List<DrawCall> renderText(String text, float x, float y, float z)
@@ -96,12 +102,12 @@ public class FontRenderer implements IFontRenderer
{
xOffset += renderChar(letter, xOffset, yOffset, context.getScale(), effects.italic, effects.flipped, textColor, builder, true);
}
yOffset += height * context.getScale();
yOffset += getFontHeight() * context.getScale();
}
bufferBuilder.finishData();
if(bufferBuilder.getVertexCount() > 0)
{
drawCalls.add(bufferBuilder.getDrawCall(texture.getTextureID()));
drawCalls.add(bufferBuilder.getDrawCall(getTexture().getTextureID()));
}
bufferBuilder.setOffset(0F, 0F, 0F);
return drawCalls;
@@ -125,8 +131,8 @@ public class FontRenderer implements IFontRenderer
return;
}
bufferBuilder.begin(GL11.GL_TRIANGLES, VertexType.UI);
int maxLanes = component.isHeightLimited() ? Math.min((int)(boxHeight / (height * context.getScale())), lines.size()) : lines.size();
float maxHeight = maxLanes * height * context.getScale();
int maxLanes = component.isHeightLimited() ? Math.min((int)(boxHeight / (getFontHeight() * context.getScale())), lines.size()) : lines.size();
float maxHeight = maxLanes * getFontHeight() * context.getScale();
float maxWidth = 0F;
float yOffset = component.getVertical().align(boxHeight, maxHeight);
float startX = component.getHorizontal().align(boxWidth, lines.get(0).getWidth());
@@ -182,11 +188,11 @@ public class FontRenderer implements IFontRenderer
{
addUnderline(underline, xOffset - underline, yOffset, textColor, lineBuffer, false);
}
yOffset += height * context.getScale();
yOffset += getFontHeight() * context.getScale();
component.getMetadata().addLine(lines.get(i));
}
maxWidth /= 2;
buffer.finishShape(texture.getTextureID(), bufferBuilder);
buffer.finishShape(getTexture().getTextureID(), bufferBuilder);
if(lineBuffer.hasData())
{
Tesselator tes = buffer.start(GL11.GL_TRIANGLES, VertexType.UI).offset(0F, 0F, 0.001F);
@@ -201,9 +207,9 @@ public class FontRenderer implements IFontRenderer
switch(instance.getCharacter())
{
case TAB:
return space * 4 * scale;
return provider.getTabWidth() * scale;
case SPACE:
return space * scale;
return provider.getSpaceWidth() * scale;
}
if(instance.getXAdvance() <= 0F)
{
@@ -231,7 +237,7 @@ public class FontRenderer implements IFontRenderer
{
float lineWidth = lines.get(i).getWidth();
float xOffset = align.align(width, lineWidth);
float maxY = flipPos ? yPos - height : yPos + height;
float maxY = flipPos ? yPos - getFontHeight() : yPos + getFontHeight();
tes.pos(xOffset, maxY, 0.0F).tex(0F, 0F).color4f(color).endVertex();
tes.pos(xOffset, yPos, 0.0F).tex(0F, 0F).color4f(color).endVertex();
tes.pos(xOffset + lineWidth, maxY, 0.0F).tex(0F, 0F).color4f(color).endVertex();
@@ -244,12 +250,12 @@ public class FontRenderer implements IFontRenderer
protected void addUnderline(float xStart, float width, float yStart, ColorObject color, IVertexBuilder buffer, boolean flipPos)
{
float minY = yStart + baseLine + 0.5F;
float maxY = yStart + baseLine + 1.5F;
float minY = yStart + getBaseLine() + 0.5F;
float maxY = yStart + getBaseLine() + 1.5F;
if(flipPos)
{
minY = yStart - baseLine - 0.5F;
maxY = yStart - baseLine - 1.5F;
minY = yStart - getBaseLine() - 0.5F;
maxY = yStart - getBaseLine() - 1.5F;
}
buffer.pos(xStart, maxY, 0F).tex(0F, 0F).color4f(color).endVertex();
buffer.pos(xStart, minY, 0F).tex(0F, 0F).color4f(color).endVertex();
@@ -261,8 +267,8 @@ public class FontRenderer implements IFontRenderer
protected void addStrikeThrough(float xStart, float width, float yStart, ColorObject color, IVertexBuilder buffer)
{
float minY = yStart + height / 2.0F;
float maxY = yStart + height / 2.0F + 1.4F;
float minY = yStart + getFontHeight() / 2.0F;
float maxY = yStart + getFontHeight() / 2.0F + 1.4F;
buffer.pos(xStart, maxY, 0.0F).tex(0F, 0F).color4f(color).endVertex();
buffer.pos(xStart, minY, 0.0F).tex(0F, 0F).color4f(color).endVertex();
buffer.pos(xStart + width, maxY, 0.0F).tex(0F, 0F).color4f(color).endVertex();
@@ -301,9 +307,9 @@ public class FontRenderer implements IFontRenderer
switch(letter)
{
case SPACE:
return space;
return provider.getSpaceWidth();
case TAB:
return space * 4;
return provider.getTabWidth();
default:
CharInstance instance = getInstance(letter, bold);
return instance == null ? 0F : instance.getXAdvance();
@@ -321,7 +327,7 @@ public class FontRenderer implements IFontRenderer
char character = text.charAt(i);
if(LINE_SEPERATOR == character)
{
result = Math.max(result, current += space);
result = Math.max(result, current += provider.getSpaceWidth());
current = 0.0F;
continue;
}
@@ -391,13 +397,13 @@ public class FontRenderer implements IFontRenderer
@Override
public float getTextHeight(String text, int flags)
{
return getTextLengths(text, flags).length - 1 * height;
return getTextLengths(text, flags).length - 1 * getFontHeight();
}
@Override
public boolean isCharValid(char letter)
{
return chars[0].containsKey(letter);
return provider.isCharacterValid(letter);
}
@Override
@@ -1,7 +1,7 @@
package speiger.src.coreengine.rendering.gui.renderer;
import speiger.src.coreengine.rendering.gui.components.TextComponent;
import speiger.src.coreengine.rendering.textures.ITexture;
import speiger.src.coreengine.rendering.textures.base.ITexture;
public interface IFontRenderer
{
@@ -62,6 +62,12 @@ public interface IFontRenderer
float xAdvance;
boolean bold;
public CharInstance(char character, boolean bold)
{
this.character = character;
this.bold = bold;
}
public CharInstance(char character, int width, int height, float minU, float minV, float maxU, float maxV, int xAdvance, boolean bold)
{
this.character = character;
@@ -28,8 +28,8 @@ import speiger.src.coreengine.rendering.shader.uniforms.UniformVec2f;
import speiger.src.coreengine.rendering.tesselation.GLCall;
import speiger.src.coreengine.rendering.tesselation.Tesselator;
import speiger.src.coreengine.rendering.tesselation.VertexType;
import speiger.src.coreengine.rendering.textures.ITexture;
import speiger.src.coreengine.rendering.textures.TextureManager;
import speiger.src.coreengine.rendering.textures.base.ITexture;
import speiger.src.coreengine.rendering.textures.base.TextureManager;
import speiger.src.coreengine.rendering.utils.GLUtils;
import speiger.src.coreengine.utils.collections.pools.SimplePool;
@@ -0,0 +1,141 @@
package speiger.src.coreengine.rendering.gui.renderer.provider;
import java.awt.image.BufferedImage;
import java.util.List;
import com.google.gson.JsonObject;
import speiger.src.collections.chars.maps.impl.hash.Char2ObjectOpenHashMap;
import speiger.src.collections.chars.maps.interfaces.Char2ObjectMap;
import speiger.src.collections.chars.utils.maps.Char2ObjectMaps;
import speiger.src.collections.objects.misc.pairs.ObjectObjectPair;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.assets.AssetManager;
import speiger.src.coreengine.assets.IAsset;
import speiger.src.coreengine.math.vector.ints.Vec2i;
import speiger.src.coreengine.rendering.gui.helper.FontBuilder;
import speiger.src.coreengine.rendering.gui.helper.FontBuilder.WrittenChar;
import speiger.src.coreengine.rendering.gui.renderer.IFontRenderer.CharInstance;
import speiger.src.coreengine.rendering.textures.base.ITexture;
import speiger.src.coreengine.rendering.textures.base.TextureManager;
import speiger.src.coreengine.utils.helpers.JsonUtil;
public class BitmapFontProvider implements IFontProvider
{
FontInfo info;
ITexture texture;
Char2ObjectMap<CharInstance>[] instances;
float space;
public BitmapFontProvider(FontInfo info, ITexture texture, Char2ObjectMap<CharInstance>[] instances)
{
this.info = info;
this.texture = texture;
this.instances = instances;
if(instances[0].containsKey(' ')) space = instances[0].get(' ').getXAdvance();
else if(instances[1].containsKey(' ')) space = instances[1].get(' ').getXAdvance();
}
@Override
public void destroy()
{
if(texture != null)
{
texture.destroy();
texture = null;
}
}
@Override
public ITexture getTexture()
{
return texture;
}
@Override
public boolean isCharacterValid(char value)
{
return instances[0].containsKey(value) || instances[1].containsKey(value);
}
@Override
public CharInstance getCharacter(char value, boolean bold)
{
Char2ObjectMap<CharInstance> map = instances[bold ? 1 : 0];
return (map.isEmpty() ? instances[bold ? 0 : 1] : map).get(value);
}
@Override
public float getFontHeight()
{
return info.fontHeight;
}
@Override
public float getBaseLine()
{
return info.fontBase;
}
@Override
public float getSpaceWidth()
{
return space;
}
@Override
public float getTabWidth()
{
return space * info.tabs;
}
public static IFontProvider load(JsonObject object, float desiredSize, AssetManager manager)
{
FontInfo info = new FontInfo(object.getAsJsonObject("info"));
float multiplier = info.setDesiredHeight(desiredSize);
Char2ObjectMap<CharInstance>[] maps = new Char2ObjectMap[]{new Char2ObjectOpenHashMap<CharInstance>(), new Char2ObjectOpenHashMap<CharInstance>()};
JsonUtil.iterate(object.get("chars"), T -> {
CharInstance instance = info.create(T);
instance.scale(multiplier);
maps[instance.isBold() ? 1 : 0].put(instance.getCharacter(), instance);
});
if(maps[0].isEmpty()) maps[0] = Char2ObjectMaps.empty();
if(maps[1].isEmpty()) maps[1] = Char2ObjectMaps.empty();
return new BitmapFontProvider(info, ITexture.simple(AssetLocation.of(object.get("file").getAsString())), maps);
}
public static IFontProvider create(JsonObject object, float desiredSize, AssetManager manager)
{
try(IAsset asset = TextureManager.INSTANCE.getManager().getAsset(AssetLocation.of(object.get("file").getAsString())))
{
JsonObject info = object.getAsJsonObject("info");
int tabs = JsonUtil.getOrDefault(info, "tabs", 4);
boolean literal = JsonUtil.getOrDefault(info, "literal", false);
boolean plain = JsonUtil.getOrDefault(info, "plain", true);
boolean bold = JsonUtil.getOrDefault(info, "bold", true);
if(!plain && !bold) throw new IllegalStateException("You need a plain or bold font at the very least");
int flags = (literal ? FontBuilder.LITERAL : 0) | (plain ? FontBuilder.PLAIN : 0) | (bold ? FontBuilder.BOLD : 0);
ObjectObjectPair<ObjectObjectPair<Vec2i, BufferedImage>, List<WrittenChar>> written = FontBuilder.createBitmapFont(asset.getStream(), info.get("charset").getAsString(), flags, info.get("size").getAsFloat());
BufferedImage image = written.getKey().getValue();
Vec2i size = written.getKey().getKey();
FontInfo fontInfo = new FontInfo(image.getWidth(), image.getHeight(), size.getY(), size.getX(), tabs);
float mulitplier = fontInfo.setDesiredHeight(desiredSize);
Char2ObjectMap<CharInstance>[] maps = new Char2ObjectMap[]{new Char2ObjectOpenHashMap<CharInstance>(), new Char2ObjectOpenHashMap<CharInstance>()};
for(WrittenChar entry : written.getValue())
{
CharInstance instance = entry.create(fontInfo.textureWidth, fontInfo.textureHeight);
instance.scale(mulitplier);
maps[instance.isBold() ? 1 : 0].put(instance.getCharacter(), instance);
}
if(maps[0].isEmpty()) maps[0] = Char2ObjectMaps.empty();
if(maps[1].isEmpty()) maps[1] = Char2ObjectMaps.empty();
return new BitmapFontProvider(fontInfo, ITexture.direct(image), maps);
}
catch(Exception e)
{
e.printStackTrace();
}
return null;
}
}
@@ -0,0 +1,100 @@
package speiger.src.coreengine.rendering.gui.renderer.provider;
import java.util.List;
import java.util.Map;
import com.google.gson.JsonObject;
import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.collections.objects.maps.impl.hash.Object2FloatLinkedOpenHashMap;
import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap;
import speiger.src.collections.objects.maps.impl.hash.Object2ObjectOpenHashMap;
import speiger.src.collections.objects.maps.interfaces.Object2FloatMap;
import speiger.src.collections.objects.maps.interfaces.Object2FloatMap.Entry;
import speiger.src.collections.objects.utils.maps.Object2FloatMaps;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.assets.AssetManager;
import speiger.src.coreengine.assets.IAsset;
import speiger.src.coreengine.assets.reloader.IReloadableResource;
import speiger.src.coreengine.rendering.gui.renderer.FontRenderer;
public class FontManager implements IReloadableResource
{
Map<AssetLocation, FontRenderer> fontRenders = new Object2ObjectLinkedOpenHashMap<>();
Object2FloatMap<AssetLocation> fontSizes = new Object2FloatLinkedOpenHashMap<>();
Map<String, IFontLoader> loaders = new Object2ObjectOpenHashMap<>();
AssetManager manager;
public FontManager()
{
registerFontLoader("bitmap", BitmapFontProvider::load);
registerFontLoader("java-ttf", BitmapFontProvider::create);
}
public void setAssetManager(AssetManager manager)
{
this.manager = manager;
}
public void registerFontLoader(String id, IFontLoader provider)
{
loaders.put(id, provider);
}
public FontRenderer loadFont(AssetLocation location, float desiredSize)
{
FontRenderer render = fontRenders.get(location);
if(render == null)
{
IFontProvider provider = loadProvider(location, desiredSize);
if(provider != null)
{
render = new FontRenderer();
render.setProvider(provider);
fontRenders.put(location, render);
fontSizes.putIfAbsent(location, desiredSize);
}
}
return render;
}
private IFontProvider loadProvider(AssetLocation location, float desiredSize)
{
try(IAsset asset = manager.getAsset(location))
{
JsonObject obj = asset.getJsonObject();
IFontLoader loader = loaders.get(obj.get("type").getAsString());
if(loader == null) return null;
return loader.create(obj, desiredSize, manager);
}
catch(Exception e) { e.printStackTrace(); }
return null;
}
public static interface IFontLoader
{
public IFontProvider create(JsonObject obj, float desiredSize, AssetManager loader);
}
@Override
public void reload()
{
List<IFontProvider> providers = new ObjectArrayList<>();
for(Entry<AssetLocation> entry : Object2FloatMaps.fastIterable(fontSizes))
{
AssetLocation location = entry.getKey();
FontRenderer font = fontRenders.get(location);
providers.add(font.getProvider());
font.setProvider(loadProvider(location, entry.getFloatValue()));
}
for(int i = 0,m=providers.size();i<m;providers.get(i++).destroy());
}
@Override
public void destroy()
{
fontRenders.values().forEach(FontRenderer::destory);
fontRenders.clear();
}
}
@@ -0,0 +1,72 @@
package speiger.src.coreengine.rendering.gui.renderer.provider;
import com.google.gson.JsonObject;
import speiger.src.coreengine.rendering.gui.renderer.IFontRenderer.CharInstance;
import speiger.src.coreengine.rendering.textures.base.ITexture;
import speiger.src.coreengine.utils.helpers.JsonUtil;
public interface IFontProvider
{
public ITexture getTexture();
public boolean isCharacterValid(char value);
public CharInstance getCharacter(char value, boolean bold);
public float getFontHeight();
public float getBaseLine();
public float getSpaceWidth();
public float getTabWidth();
public void destroy();
public static class FontInfo
{
public int textureWidth;
public int textureHeight;
public float fontHeight;
public float fontBase;
public int tabs;
public FontInfo(JsonObject obj)
{
this(obj.get("width").getAsInt(), obj.get("height").getAsInt(), obj.get("charHeight").getAsInt(), obj.get("base").getAsInt(), JsonUtil.getOrDefault(obj, "tabs", 4));
}
public FontInfo(int textureWidth, int textureHeight, float fontHeight, float fontBase, int tabs)
{
this.textureWidth = textureWidth;
this.textureHeight = textureHeight;
this.fontHeight = fontHeight;
this.fontBase = fontBase;
this.tabs = tabs;
}
public float setDesiredHeight(float desired)
{
float multiplier = desired / fontHeight;
fontHeight *= multiplier;
fontBase *= multiplier;
return multiplier;
}
public CharInstance create(JsonObject obj)
{
return create((char)obj.get("char").getAsInt(), obj.get("minX").getAsInt(), obj.get("minY").getAsInt(), obj.get("maxX").getAsInt(), obj.get("maxY").getAsInt(), JsonUtil.getOrDefault(obj, "bold", false));
}
public CharInstance create(char character, int minX, int minY, int maxX, int maxY, boolean bold)
{
return new CharInstance(character, maxX - minX, maxY - minY, getTextureU(minX), getTextureV(minY), getTextureU(maxX), getTextureV(maxY), maxX - minX, bold);
}
public float getTextureU(float value)
{
return value / textureWidth;
}
public float getTextureV(float value)
{
return value / textureHeight;
}
}
}
@@ -0,0 +1,53 @@
package speiger.src.coreengine.rendering.models;
import java.util.Map;
import speiger.src.collections.objects.maps.impl.hash.Object2ObjectOpenHashMap;
public enum DataType
{
BYTE("byte", 1),
SHORT("short", 2),
INT("int", 4),
LONG("long", 8),
FLOAT("float", 4),
DOUBLE("double", 8);
static final Map<String, DataType> BY_ID = new Object2ObjectOpenHashMap<>();
final String type;
final int byteSize;
private DataType(String type, int byteSize)
{
this.type = type;
this.byteSize = byteSize;
}
public String getType()
{
return type;
}
public int getByteSize()
{
return byteSize;
}
public boolean isFloatingPoint()
{
return this == FLOAT || this == DOUBLE;
}
public static DataType byID(String id)
{
return BY_ID.get(id);
}
static
{
for(DataType type : values())
{
BY_ID.put(type.getType(), type);
}
}
}
@@ -1,42 +1,56 @@
package speiger.src.coreengine.rendering.models;
import java.util.Map;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL30;
import speiger.src.collections.ints.maps.impl.hash.Int2ObjectOpenHashMap;
import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap;
import speiger.src.collections.objects.maps.impl.hash.Object2ObjectOpenHashMap;
public class GLDataType
{
static final Int2ObjectMap<GLDataType> ID_TO_TYPE = new Int2ObjectOpenHashMap<>();
static final Map<String, GLDataType> NAME_TO_TYPE = new Object2ObjectOpenHashMap<>();
//Normal Types
public static final GLDataType BYTE = new GLDataType(GL11.GL_BYTE, 1);
public static final GLDataType UNSIGNED_BYTE = new GLDataType(GL11.GL_UNSIGNED_BYTE, 1);
public static final GLDataType SHORT = new GLDataType(GL11.GL_SHORT, 2);
public static final GLDataType UNSIGNED_SHORT = new GLDataType(GL11.GL_UNSIGNED_SHORT, 2);
public static final GLDataType INT = new GLDataType(GL11.GL_INT, 4);
public static final GLDataType UNSIGNED_INT = new GLDataType(GL11.GL_UNSIGNED_INT, 4);
public static final GLDataType FLOAT = new GLDataType(GL11.GL_FLOAT, 4);
public static final GLDataType BYTE = new GLDataType(GL11.GL_BYTE, DataType.BYTE, "byte");
public static final GLDataType UNSIGNED_BYTE = new GLDataType(GL11.GL_UNSIGNED_BYTE, DataType.BYTE, "u_byte");
public static final GLDataType SHORT = new GLDataType(GL11.GL_SHORT, DataType.SHORT, "short");
public static final GLDataType UNSIGNED_SHORT = new GLDataType(GL11.GL_UNSIGNED_SHORT, DataType.SHORT, "u_short");
public static final GLDataType INT = new GLDataType(GL11.GL_INT, DataType.INT, "int");
public static final GLDataType UNSIGNED_INT = new GLDataType(GL11.GL_UNSIGNED_INT, DataType.INT, "u_int");
public static final GLDataType FLOAT = new GLDataType(GL11.GL_FLOAT, DataType.FLOAT, "float");
public static final GLDataType DOUBLE = new GLDataType(GL11.GL_DOUBLE, DataType.DOUBLE, "double");
//Compression Types
public static final GLDataType UNSIGNED_INT_10_10_10_2 = new GLDataType(GL12.GL_UNSIGNED_INT_10_10_10_2, 4, true);
public static final GLDataType UNSIGNED_INT_2_10_10_10_REV = new GLDataType(GL12.GL_UNSIGNED_INT_2_10_10_10_REV, 4, true);
public static final GLDataType UNSIGNED_INT_10_10_10_2 = new GLDataType(GL12.GL_UNSIGNED_INT_10_10_10_2, DataType.INT, true, "u_int_10_10_10_2");
public static final GLDataType UNSIGNED_INT_2_10_10_10_REV = new GLDataType(GL12.GL_UNSIGNED_INT_2_10_10_10_REV, DataType.INT, true, "u_int_2_10_10_10_rev");
//Special Types
public static final GLDataType UNSIGNED_INT_10F_11F_11F_REV = new GLDataType(GL30.GL_UNSIGNED_INT_10F_11F_11F_REV, 4, true);
public static final GLDataType UNSIGNED_INT_5_9_9_9_REV = new GLDataType(GL30.GL_UNSIGNED_INT_5_9_9_9_REV, 4, true);
public static final GLDataType UNSIGNED_INT_10F_11F_11F_REV = new GLDataType(GL30.GL_UNSIGNED_INT_10F_11F_11F_REV, DataType.INT, true, "u_int_10_11_11_rev");
public static final GLDataType UNSIGNED_INT_5_9_9_9_REV = new GLDataType(GL30.GL_UNSIGNED_INT_5_9_9_9_REV, DataType.INT, true, "u_int_5_9_9_9_rev");
final int glType;
final int byteSize;
final DataType dataType;
final boolean ignoreAttributeSize;
final String name;
public GLDataType(int glType, int byteSize)
public GLDataType(int glType, DataType dataType, String name)
{
this(glType, byteSize, false);
this(glType, dataType, false, name);
}
public GLDataType(int glType, int byteSize, boolean ignoreAttributeSize)
public GLDataType(int glType, DataType dataType, boolean ignoreAttributeSize, String name)
{
this.glType = glType;
this.byteSize = byteSize;
this.dataType = dataType;
this.ignoreAttributeSize = ignoreAttributeSize;
this.name = name;
ID_TO_TYPE.put(glType, this);
NAME_TO_TYPE.put(name, this);
}
public int getGLType()
@@ -44,13 +58,28 @@ public class GLDataType
return glType;
}
public String getName()
{
return name;
}
public int getByteSize()
{
return byteSize;
return dataType.getByteSize();
}
public boolean isSpecialType()
{
return ignoreAttributeSize;
}
public DataType getDataType()
{
return dataType;
}
public int calulateSize(int attributeSize)
{
return ignoreAttributeSize ? byteSize : attributeSize * byteSize;
return ignoreAttributeSize ? dataType.getByteSize() : attributeSize * dataType.getByteSize();
}
}
@@ -9,7 +9,7 @@ import java.util.function.Consumer;
import org.lwjgl.opengl.GL15;
import org.lwjgl.system.MemoryUtil;
import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap.Entry;
import speiger.src.collections.ints.misc.pairs.IntObjectPair;
import speiger.src.coreengine.rendering.utils.AllocationTracker;
import speiger.src.coreengine.utils.io.GameLog;
@@ -213,12 +213,12 @@ public class VertexBuffer
return this;
}
public VertexBuffer fillBuffer(List<Entry<byte[]>> data)
public VertexBuffer fillBuffer(List<IntObjectPair<byte[]>> data)
{
return fillBuffer(0, data);
}
public VertexBuffer fillBuffer(int byteOffset, List<Entry<byte[]>> data)
public VertexBuffer fillBuffer(int byteOffset, List<IntObjectPair<byte[]>> data)
{
if(data.isEmpty())
{
@@ -228,7 +228,7 @@ public class VertexBuffer
ByteBuffer buffer = GL15.glMapBuffer(bufferType, GL15.GL_WRITE_ONLY);
for(int i = 0,m=data.size();i<m;i++)
{
Entry<byte[]> entry = data.get(i);
IntObjectPair<byte[]> entry = data.get(i);
buffer.position(entry.getIntKey()+byteOffset);
buffer.put(entry.getValue());
totalData += entry.getValue().length;
@@ -7,7 +7,7 @@ import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL32;
import speiger.src.coreengine.rendering.textures.TextureManager;
import speiger.src.coreengine.rendering.textures.base.TextureManager;
public class TextureAttachment implements IFrameAttachment
{
@@ -1,7 +1,7 @@
package speiger.src.coreengine.rendering.shader.uniforms;
import speiger.src.coreengine.rendering.textures.ITexture;
import speiger.src.coreengine.rendering.textures.TextureManager;
import speiger.src.coreengine.rendering.textures.base.ITexture;
import speiger.src.coreengine.rendering.textures.base.TextureManager;
public class UniformTexture extends UniformInt
{
@@ -1,22 +0,0 @@
package speiger.src.coreengine.rendering.textures;
import speiger.src.coreengine.assets.reloader.IReloadableResource;
public interface ITexture extends IReloadableResource
{
public int getTextureID();
public void bindTexture();
public void deleteTexture();
public int getWidth();
public int getHeight();
public float getUMin();
public float getVMin();
public float getUMax();
public float getVMax();
public default float getInterpolatedU(float u){return getUMin() + ((getUMax() - getUMin()) * u);}
public default float getINterpolatedV(float v){return getUMin() + ((getUMax() - getUMin()) * v);}
}
@@ -1,12 +1,14 @@
package speiger.src.coreengine.rendering.textures;
package speiger.src.coreengine.rendering.textures.base;
public abstract class AbstractTexture implements ITexture
{
int textureID;
protected int textureID;
public AbstractTexture()
@Override
public ITexture makeReloadable()
{
TextureManager.INSTANCE.addTexture(this);
return this;
}
public void setTextureID(int textureID)
@@ -0,0 +1,56 @@
package speiger.src.coreengine.rendering.textures.base;
import java.awt.image.BufferedImage;
import java.nio.ByteBuffer;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.assets.reloader.IReloadableResource;
import speiger.src.coreengine.rendering.textures.normal.DirectTexture;
import speiger.src.coreengine.rendering.textures.normal.SimpleTexture;
import speiger.src.coreengine.rendering.textures.stb.STBDirectTexture;
import speiger.src.coreengine.rendering.textures.stb.STBTexture;
public interface ITexture extends IReloadableResource
{
public int getTextureID();
public void bindTexture();
public void deleteTexture();
public int getWidth();
public int getHeight();
public float getUMin();
public float getVMin();
public float getUMax();
public float getVMax();
public ITexture makeReloadable();
public default float getInterpolatedU(float u){return getUMin() + ((getUMax() - getUMin()) * u);}
public default float getInterpolatedV(float v){return getUMin() + ((getUMax() - getUMin()) * v);}
public static ITexture simple(AssetLocation location) {
return new STBTexture(location);
}
public static ITexture direct(ByteBuffer stbImageData, int width, int height) {
return new STBDirectTexture(stbImageData, width, height);
}
public static ITexture direct(long stbImageData, int width, int height) {
return new STBDirectTexture(stbImageData, width, height);
}
public static ITexture direct(BufferedImage imageData) {
return new STBDirectTexture(imageData);
}
public static ITexture awtSimple(AssetLocation location) {
return new SimpleTexture(location);
}
public static ITexture awtDirect(BufferedImage imageData) {
return new DirectTexture(imageData);
}
}
@@ -0,0 +1,27 @@
package speiger.src.coreengine.rendering.textures.base;
import java.io.Closeable;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.function.Consumer;
import org.lwjgl.system.MemoryUtil;
import speiger.src.coreengine.assets.IAssetParser;
public class NativeMemoryParser implements IAssetParser<ByteBuffer>
{
@Override
public ByteBuffer parseAsset(Path path, Consumer<Closeable> autoCloser) throws IOException
{
byte[] data = Files.readAllBytes(path);
ByteBuffer buffer = MemoryUtil.memAlloc(data.length);
buffer.put(data);
buffer.flip();
return buffer;
}
}
@@ -1,4 +1,4 @@
package speiger.src.coreengine.rendering.textures;
package speiger.src.coreengine.rendering.textures.base;
import java.awt.image.BufferedImage;
import java.nio.ByteBuffer;
@@ -13,6 +13,8 @@ import speiger.src.coreengine.assets.AssetManager;
import speiger.src.coreengine.assets.IAsset;
import speiger.src.coreengine.assets.reloader.IReloadableResource;
import speiger.src.coreengine.assets.reloader.ResourceReloader;
import speiger.src.coreengine.rendering.textures.base.ITexture;
import speiger.src.coreengine.rendering.textures.base.TextureManager;
import speiger.src.coreengine.rendering.utils.GLUtils;
import speiger.src.coreengine.rendering.utils.states.GLState;
import speiger.src.coreengine.utils.io.GameLog;
@@ -1,4 +1,4 @@
package speiger.src.coreengine.rendering.textures;
package speiger.src.coreengine.rendering.textures.base;
public class WrappedTexture implements ITexture
{
@@ -8,7 +8,13 @@ public class WrappedTexture implements ITexture
{
this.textureId = textureId;
}
@Override
public ITexture makeReloadable()
{
return this;
}
@Override
public void reload()
{
@@ -1,4 +1,4 @@
package speiger.src.coreengine.rendering.textures;
package speiger.src.coreengine.rendering.textures.custom;
import java.nio.ByteBuffer;
import java.util.Iterator;
@@ -16,6 +16,8 @@ import speiger.src.collections.ints.sets.IntSet;
import speiger.src.collections.ints.utils.maps.Int2ObjectMaps;
import speiger.src.collections.utils.ITrimmable;
import speiger.src.coreengine.math.BitUtil;
import speiger.src.coreengine.rendering.textures.base.AbstractTexture;
import speiger.src.coreengine.rendering.textures.base.TextureManager;
import speiger.src.coreengine.rendering.utils.AllocationTracker;
public class DynamicTexture extends AbstractTexture
@@ -132,8 +134,9 @@ public class DynamicTexture extends AbstractTexture
@Override
public void reload()
{
TextureManager.INSTANCE.removeTexture(getTextureID());
int old = getTextureID();
setTextureID(GL11.glGenTextures());
TextureManager.INSTANCE.removeTexture(old);
dirtyChunks.clear();
first = true;
updateData();
@@ -0,0 +1,304 @@
package speiger.src.coreengine.rendering.textures.custom;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;
import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap;
import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap;
import speiger.src.collections.objects.misc.pairs.ObjectObjectPair;
import speiger.src.collections.objects.sets.ObjectOpenHashSet;
import speiger.src.collections.objects.utils.ObjectIterators;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.math.vector.ints.Vec2i;
import speiger.src.coreengine.rendering.textures.custom.TextureAtlas;
/**
* Inspired by: <a href=https://github.com/lukaszdk/texture-atlas-generator/blob/master/AtlasGenerator.java>AtlasGenerator</a>
*/
public class TextureAtlas
{
int width;
int height;
Object2ObjectMap<AssetLocation, AtlasEntry> textures;
protected TextureAtlas(Vec2i bounds, Object2ObjectMap<AssetLocation, AtlasEntry> textures)
{
width = bounds.getX();
height = bounds.getY();
this.textures = textures;
}
public int getWidth()
{
return width;
}
public int getHeight()
{
return height;
}
public AtlasEntry getTexture(AssetLocation texture)
{
return textures.getObject(texture);
}
public Iterator<AtlasEntry> getContents()
{
return ObjectIterators.unmodifiable(textures.values().iterator());
}
public static Builder create()
{
return new Builder();
}
public static class Builder
{
Set<AssetLocation> names = new ObjectOpenHashSet<>();
List<Record> records = new ObjectArrayList<>();
int pixelsUsed = 0;
public boolean add(AssetLocation location, int width, int height)
{
return add(location, width, height, 0);
}
public boolean add(AssetLocation location, int width, int height, int padding)
{
if(location == null || width <= 0 || height <= 0 || padding < 0 || !names.add(location)) return false;
records.add(new Record(location, width, height, padding));
pixelsUsed += (width + padding) * (height + padding);
return true;
}
private ObjectObjectPair<Vec2i, Object2ObjectMap<AssetLocation, AtlasEntry>> stitch()
{
int textureWidth = 2;
int textureHeight = 2;
boolean height = false;
for(;textureHeight * textureWidth <= pixelsUsed;height = !height)
{
if(height) textureHeight *= 2;
else textureWidth *= 2;
}
records.sort(null);
int attempts = 0;
while(attempts < 50)
{
Slot slot = new Slot(0, 0, textureWidth, textureHeight);
boolean failed = false;
for(int i = 0,m=records.size();i<m;i++)
{
if(slot.addRecord(records.get(i))) continue;
failed = true;
break;
}
if(failed)
{
if(height) textureHeight *= 2;
else textureWidth *= 2;
attempts++;
height = !height;
continue;
}
Object2ObjectMap<AssetLocation, AtlasEntry> entries = new Object2ObjectLinkedOpenHashMap<>();
slot.build(entries::put);
return ObjectObjectPair.of(Vec2i.newVec(textureWidth, textureHeight), entries);
}
throw new IllegalStateException("Couldn't fit Texture Atlas after growing it 5 Times");
}
public void buildHollow(BiConsumer<Vec2i, Iterable<AtlasEntry>> builder)
{
ObjectObjectPair<Vec2i, Object2ObjectMap<AssetLocation, AtlasEntry>> pairs = stitch();
builder.accept(pairs.getKey(), pairs.getValue().values());
}
public TextureAtlas build(BiConsumer<Vec2i, Iterable<AtlasEntry>> builder)
{
ObjectObjectPair<Vec2i, Object2ObjectMap<AssetLocation, AtlasEntry>> pairs = stitch();
builder.accept(pairs.getKey(), pairs.getValue().values());
return new TextureAtlas(pairs.getKey(), pairs.getValue());
}
public TextureAtlas build()
{
ObjectObjectPair<Vec2i, Object2ObjectMap<AssetLocation, AtlasEntry>> pairs = stitch();
return new TextureAtlas(pairs.getKey(), pairs.getValue());
}
}
public static class AtlasEntry
{
AssetLocation location;
int x;
int y;
int width;
int height;
private AtlasEntry(Slot slot)
{
Record record = slot.record;
location = record == null ? null : record.getLocation();
x = slot.x;
y = slot.y;
width = record == null ? slot.width : record.width;
height = record == null ? slot.height : record.height;
}
public AssetLocation getLocation()
{
return location;
}
public int getX()
{
return x;
}
public int getY()
{
return y;
}
public int getWidth()
{
return width;
}
public int getHeight()
{
return height;
}
}
private static class Slot
{
int x;
int y;
int width;
int height;
Record record;
Slot[] children = null;
public Slot(int x, int y, int width, int height)
{
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public boolean isLeaf()
{
return children == null;
}
public void build(BiConsumer<AssetLocation, AtlasEntry> builder)
{
if(!isLeaf())
{
for(int i = 0,m=children.length;i<m;i++)
{
children[i].build(builder);
}
return;
}
if(record == null) return;
builder.accept(record.getLocation(), new AtlasEntry(this));
}
public boolean addRecord(Record record)
{
if(isLeaf())
{
int rw = record.getWidth();
int rh = record.getHeight();
if(this.record != null || rw > width || rh > height) return false;
if(rw == width && rh == height)
{
this.record = record;
return true;
}
int p = record.getPadding();
int dw = width - rw;
int dh = height - rh;
children = new Slot[dw > 0 && dh > 0 ? 3 : 2];
children[0] = new Slot(x, y, rw, rh);
if(dw > 0 && dh > 0)
{
if(dw > dh)
{
children[1] = new Slot(x + rw + p, y, dw - p, rh);
children[2] = new Slot(x, y + rh + p, width, dh - p);
}
else
{
children[1] = new Slot(x, y + rh + p, rw, dh - p);
children[2] = new Slot(x + rw + p, y, dw - p, height);
}
}
else if(dw == 0) children[1] = new Slot(x, y + rh + p, rw, dh - p);
else if(dh == 0) children[1] = new Slot(x + rw + p, y, dw - p, rh);
return children[0].addRecord(record);
}
for(int i = 0,m=children.length;i<m;i++)
{
if(children[i].addRecord(record)) return true;
}
return false;
}
}
private static class Record implements Comparable<Record>
{
AssetLocation location;
int width;
int height;
int padding;
public Record(AssetLocation location, int width, int height, int padding)
{
this.location = location;
this.width = width;
this.height = height;
this.padding = padding;
}
public int getHeight()
{
return height;
}
public int getWidth()
{
return width;
}
public int getPadding()
{
return padding;
}
public AssetLocation getLocation()
{
return location;
}
private int getPixelCount()
{
return (width + padding) * (height + padding);
}
@Override
public int compareTo(Record o)
{
int result = Integer.compare(o.getPixelCount(), getPixelCount());
return result == 0 ? getLocation().compareTo(o.getLocation()) : result;
}
}
}
@@ -1,4 +1,4 @@
package speiger.src.coreengine.rendering.textures;
package speiger.src.coreengine.rendering.textures.normal;
import java.awt.image.BufferedImage;
import java.nio.ByteBuffer;
@@ -8,6 +8,9 @@ import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL30;
import org.lwjgl.system.MemoryUtil;
import speiger.src.coreengine.rendering.textures.base.AbstractTexture;
import speiger.src.coreengine.rendering.textures.base.TextureManager;
public class DirectTexture extends AbstractTexture
{
BufferedImage image;
@@ -93,7 +96,8 @@ public class DirectTexture extends AbstractTexture
@Override
public void reload()
{
TextureManager.INSTANCE.removeTexture(textureID);
int old = textureID;
loadTexture();
TextureManager.INSTANCE.removeTexture(old);
}
}
@@ -1,4 +1,4 @@
package speiger.src.coreengine.rendering.textures;
package speiger.src.coreengine.rendering.textures.normal;
import java.awt.image.BufferedImage;
import java.nio.ByteBuffer;
@@ -10,6 +10,8 @@ import org.lwjgl.system.MemoryUtil;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.assets.IAsset;
import speiger.src.coreengine.rendering.textures.base.AbstractTexture;
import speiger.src.coreengine.rendering.textures.base.TextureManager;
public class SimpleTexture extends AbstractTexture
{
@@ -66,8 +68,9 @@ public class SimpleTexture extends AbstractTexture
@Override
public void reload()
{
TextureManager.INSTANCE.removeTexture(textureID);
int old = textureID;
loadTexture();
TextureManager.INSTANCE.removeTexture(old);
}
@Override
@@ -0,0 +1,109 @@
package speiger.src.coreengine.rendering.textures.stb;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import javax.imageio.ImageIO;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL30;
import org.lwjgl.stb.STBImage;
import org.lwjgl.system.MemoryUtil;
import speiger.src.coreengine.rendering.textures.base.AbstractTexture;
import speiger.src.coreengine.rendering.textures.base.TextureManager;
public class STBDirectTexture extends AbstractTexture
{
long imageData;
int width;
int height;
public STBDirectTexture(BufferedImage imageData)
{
this(convert(imageData), imageData.getWidth(), imageData.getHeight());
}
public STBDirectTexture(ByteBuffer imageData, int width, int height)
{
this(MemoryUtil.memAddress(imageData), width, height);
}
public STBDirectTexture(long imageData, int width, int height)
{
this.imageData = imageData;
this.width = width;
this.height = height;
loadTexture();
}
protected void loadTexture()
{
setTextureID(GL11.glGenTextures());
bindTexture();
GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL13.GL_CLAMP_TO_BORDER);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL13.GL_CLAMP_TO_BORDER);
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, width, height, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, imageData);
GL30.glGenerateMipmap(GL11.GL_TEXTURE_2D);
}
@Override
public int getWidth()
{
return width;
}
@Override
public int getHeight()
{
return height;
}
@Override
public void reload()
{
int old = textureID;
loadTexture();
TextureManager.INSTANCE.removeTexture(old);
}
@Override
public void deleteTexture()
{
if(imageData != 0)
{
STBImage.nstbi_image_free(imageData);
imageData = 0;
}
super.deleteTexture();
}
private static ByteBuffer convert(BufferedImage image)
{
ByteArrayOutputStream stream = new ByteArrayOutputStream();
try
{
ImageIO.write(image, "png", stream);
ByteBuffer buffer = MemoryUtil.memAlloc(stream.size());
buffer.put(stream.toByteArray());
buffer.flip();
ByteBuffer result = STBImage.stbi_load_from_memory(buffer, new int[1], new int[1], new int[1], 4);
if(result == null) {
MemoryUtil.memFree(buffer);
throw new IOException("Could not load image: " + STBImage.stbi_failure_reason());
}
MemoryUtil.memFree(buffer);
return result;
}
catch(Exception e)
{
e.printStackTrace();
}
return null;
}
}
@@ -0,0 +1,98 @@
package speiger.src.coreengine.rendering.textures.stb;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL30;
import org.lwjgl.stb.STBImage;
import org.lwjgl.system.MemoryUtil;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.assets.IAsset;
import speiger.src.coreengine.rendering.textures.base.AbstractTexture;
import speiger.src.coreengine.rendering.textures.base.TextureManager;
public class STBTexture extends AbstractTexture
{
AssetLocation location;
long imageData;
int width;
int height;
public STBTexture(AssetLocation location)
{
this.location = location;
loadTexture();
}
private void loadTexture()
{
try(IAsset asset = TextureManager.INSTANCE.getManager().getAsset(location))
{
ByteBuffer buffer = asset.getCustom(ByteBuffer.class);
int[] width = new int[1];
int[] height = new int[1];
int[] fileChannels = new int[1];
ByteBuffer image = STBImage.stbi_load_from_memory(buffer, width, height, fileChannels, 4);
if(image == null) {
MemoryUtil.memFree(buffer);
throw new IOException("Could not load image: " + STBImage.stbi_failure_reason());
}
imageData = MemoryUtil.memAddress(image);
this.width = width[0];
this.height = height[0];
MemoryUtil.memFree(buffer);
}
catch(Exception e)
{
e.printStackTrace();
return;
}
setTextureID(GL11.glGenTextures());
bindTexture();
GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL13.GL_CLAMP_TO_BORDER);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL13.GL_CLAMP_TO_BORDER);
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, width, height, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, imageData);
GL30.glGenerateMipmap(GL11.GL_TEXTURE_2D);
}
@Override
public int getWidth()
{
return width;
}
@Override
public int getHeight()
{
return height;
}
@Override
public void reload()
{
if(imageData != 0)
{
STBImage.nstbi_image_free(imageData);
imageData = 0;
}
int old = textureID;
loadTexture();
TextureManager.INSTANCE.removeTexture(old);
}
@Override
public void deleteTexture()
{
super.deleteTexture();
if(imageData != 0)
{
STBImage.nstbi_image_free(imageData);
imageData = 0;
}
}
}
@@ -12,7 +12,7 @@ import speiger.src.collections.objects.utils.maps.Object2ObjectMaps;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.assets.reloader.IReloadableResource;
import speiger.src.coreengine.rendering.input.window.Window;
import speiger.src.coreengine.rendering.textures.TextureManager;
import speiger.src.coreengine.rendering.textures.base.TextureManager;
public final class Cursor implements IReloadableResource
{
@@ -37,7 +37,7 @@ public class CollectionUtils
List<T>[] list = new List[size];
for(int i = 0;i<size;i++)
{
list[i] = new ObjectArrayList<T>();
list[i] = new ObjectArrayList<>();
}
return list;
}
@@ -6,10 +6,9 @@ import java.util.Set;
import speiger.src.collections.ints.collections.IntIterator;
import speiger.src.collections.ints.maps.abstracts.AbstractInt2IntMap.BasicEntry;
import speiger.src.collections.ints.maps.abstracts.AbstractInt2ObjectMap;
import speiger.src.collections.ints.maps.interfaces.Int2IntMap;
import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap;
import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap.Entry;
import speiger.src.collections.ints.misc.pairs.IntObjectPair;
import speiger.src.collections.ints.sets.IntAVLTreeSet;
import speiger.src.collections.ints.sets.IntSet;
import speiger.src.collections.objects.lists.ObjectArrayList;
@@ -166,7 +165,7 @@ public class DynamicDataManager<T>
}
return;
}
List<Entry<byte[]>> list = new ObjectArrayList<>();
List<IntObjectPair<byte[]>> list = new ObjectArrayList<>();
while(!changedSlots.isEmpty())
{
DataSlot start = null;
@@ -196,7 +195,7 @@ public class DynamicDataManager<T>
bytesAllocated += start.getUsedBytes();
start = start.next;
}
list.add(new AbstractInt2ObjectMap.BasicEntry<>(byteOffset, data));
list.add(IntObjectPair.of(byteOffset, data));
}
if(!list.isEmpty())
{
@@ -2,10 +2,10 @@ package speiger.src.coreengine.utils.collections.managers.dynamic;
import java.util.List;
import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap.Entry;
import speiger.src.collections.ints.misc.pairs.IntObjectPair;
public interface IDynamicDataHandler<T>
{
public byte[] toBytes(T entry);
public void uploadBytes(List<Entry<byte[]>> list, int newArraySize);
public void uploadBytes(List<IntObjectPair<byte[]>> list, int newArraySize);
}
@@ -10,10 +10,9 @@ import speiger.src.collections.ints.collections.IntIterator;
import speiger.src.collections.ints.functions.IntComparator;
import speiger.src.collections.ints.lists.IntArrayList;
import speiger.src.collections.ints.lists.IntList;
import speiger.src.collections.ints.maps.abstracts.AbstractInt2ObjectMap.BasicEntry;
import speiger.src.collections.ints.maps.impl.hash.Int2IntOpenHashMap;
import speiger.src.collections.ints.maps.interfaces.Int2IntMap;
import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap.Entry;
import speiger.src.collections.ints.misc.pairs.IntObjectPair;
import speiger.src.collections.ints.sets.IntAVLTreeSet;
import speiger.src.collections.ints.sets.IntLinkedOpenHashSet;
import speiger.src.collections.ints.sets.IntSet;
@@ -284,7 +283,7 @@ public class FixedDataManager
{
ObjectArrays.quickSort(slots);
List<FixedSlot> fixed = new ObjectArrayList<FixedSlot>();
List<Entry<byte[]>> data = new ObjectArrayList<Entry<byte[]>>();
List<IntObjectPair<byte[]>> data = new ObjectArrayList<>();
FixedSlot first = null;
FixedSlot last = null;
for(int i = 0,m=slots.length;i<m;i++)
@@ -304,7 +303,7 @@ public class FixedDataManager
ByteBuffer buffer = ByteBuffer.allocate(fixed.size() * bytes).order(ByteOrder.nativeOrder());
handler.createData(buffer, fixed.size(), fixed);
fixed.clear();
data.add(new BasicEntry<byte[]>(first.index * bytes, buffer.array()));
data.add(IntObjectPair.of(first.index * bytes, buffer.array()));
i--;
first = last = null;
}
@@ -313,7 +312,7 @@ public class FixedDataManager
ByteBuffer buffer = ByteBuffer.allocate(fixed.size() * bytes).order(ByteOrder.nativeOrder());
handler.createData(buffer, fixed.size(), fixed);
fixed.clear();
data.add(new BasicEntry<byte[]>(first.index * bytes, buffer.array()));
data.add(IntObjectPair.of(first.index * bytes, buffer.array()));
}
SLOTS.accept(slots);
return new FinishingTask(handler, data, size);
@@ -339,10 +338,10 @@ public class FixedDataManager
static class FinishingTask implements Runnable
{
IFixedDataHandler handler;
List<Entry<byte[]>> data;
List<IntObjectPair<byte[]>> data;
int size;
public FinishingTask(IFixedDataHandler handler, List<Entry<byte[]>> data, int size)
public FinishingTask(IFixedDataHandler handler, List<IntObjectPair<byte[]>> data, int size)
{
this.handler = handler;
this.data = data;
@@ -3,11 +3,11 @@ package speiger.src.coreengine.utils.collections.managers.fixed;
import java.nio.ByteBuffer;
import java.util.List;
import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap.Entry;
import speiger.src.collections.ints.misc.pairs.IntObjectPair;
public interface IFixedDataHandler
{
public void createData(ByteBuffer data, int size, List<FixedSlot> slots);
public void updateData(List<Entry<byte[]>> list, int newSize);
public void updateData(List<IntObjectPair<byte[]>> list, int newSize);
}
@@ -8,6 +8,8 @@ import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
@@ -55,6 +57,32 @@ public class JsonUtil
}
}
public static void iterate(JsonElement element, Consumer<JsonObject> listener)
{
if(element.isJsonObject()) listener.accept(element.getAsJsonObject());
else if(element.isJsonArray())
{
JsonArray array = element.getAsJsonArray();
for(int i = 0,m=array.size();i<m;i++)
{
iterate(array.get(i), listener);
}
}
}
public static <T> void iterate(JsonElement element, T input, BiConsumer<JsonObject, T> listener)
{
if(element.isJsonObject()) listener.accept(element.getAsJsonObject(), input);
else if(element.isJsonArray())
{
JsonArray array = element.getAsJsonArray();
for(int i = 0,m=array.size();i<m;i++)
{
iterate(array.get(i), input, listener);
}
}
}
public static boolean getOrDefault(JsonObject obj, String name, boolean defaultValue)
{
JsonElement el = obj.get(name);
@@ -103,6 +131,7 @@ public class JsonUtil
return el == null ? defaultValue : el.getAsString();
}
public static JsonArray toArray(byte[] values)
{
JsonArray array = new JsonArray();
@@ -113,7 +142,28 @@ public class JsonUtil
return array;
}
public static byte[] parseArray(JsonArray array)
public static JsonArray toArray(float[] values)
{
JsonArray array = new JsonArray();
for(int i = 0,m=values.length;i<m;i++)
{
array.add(values[i]);
}
return array;
}
public static JsonArray toArray(Number[] values)
{
JsonArray array = new JsonArray();
for(int i = 0,m=values.length;i<m;i++)
{
array.add(values[i]);
}
return array;
}
public static byte[] parseByteArray(JsonArray array)
{
byte[] data = new byte[array.size()];
for(int i = 0,m=data.length;i<m;i++)
@@ -122,4 +172,14 @@ public class JsonUtil
}
return data;
}
public static float[] parseFloatArray(JsonArray array)
{
float[] data = new float[array.size()];
for(int i = 0,m=data.length;i<m;i++)
{
data[i] = array.get(i).getAsFloat();
}
return data;
}
}