Bugfixes and new features.

-Improved: DynamicTexture is now a Interface so STB support can be added.
-Added: UTF-8 Support for font rendering. SingleTexture requirement is still a thing. But that may change later. Most of the requirements are already added.
-Added: ModelLoader/ModelCache into the Engine that handles old and new formats of the Game Engine but can now be used for other stuff too.
-Added: HigherQuality/UTF Supporting font of Roboto-Medium into the engine so it looks a lot better.
-Fixed: A bug within AssetPackages breaking paths.
This commit is contained in:
Speiger 2021-10-03 21:09:21 +02:00
parent b155fb5d92
commit 6577428b1f
25 changed files with 2355 additions and 487 deletions

View File

@ -73,7 +73,7 @@ public class FolderAssetPackage implements IAssetPackage
{
for(Path path : IterableWrapper.wrap(stream.iterator()))
{
result.add(folder.subAsset(start.relativize(path).toString()));
result.add(folder.subAsset(start.relativize(path).toString().replace("\\", "/")));
}
}
catch(IOException e)

View File

@ -106,7 +106,7 @@ public class ZipAssetPackage implements IAssetPackage
{
for(Path path : IterableWrapper.wrap(stream.iterator()))
{
result.add(folder.subAsset(start.relativize(path).toString()));
result.add(folder.subAsset(start.relativize(path).toString().replace("\\", "/")));
}
}
catch(IOException e) { e.printStackTrace(); }

View File

@ -155,7 +155,7 @@ public abstract class GuiBase
return true;
}
public boolean onKeyTyped(char letter, int keyCode)
public boolean onKeyTyped(char letter, int codepoint)
{
return false;
}

View File

@ -259,11 +259,11 @@ public class GuiScreenBase extends GuiBase
}
@Override
public boolean onKeyTyped(char letter, int keyCode)
public boolean onKeyTyped(char letter, int codepoint)
{
for(IKeyComponent comp : findKeyPopups())
{
if(comp.isAcceptingInput() && comp.onKeyTyped(letter, keyCode))
if(comp.isAcceptingInput() && comp.onKeyTyped(letter, codepoint))
{
return true;
}

View File

@ -8,5 +8,5 @@ public interface IKeyComponent extends IInputComponent
public boolean onKeyPressed(int key);
public default boolean onKeyTyped(char letter, int keyCode) {return false;}
public default boolean onKeyTyped(char letter, int codepoint) {return false;}
}

View File

@ -505,9 +505,9 @@ public class TextFieldComponent extends GuiComponent
}
@Override
public boolean onKeyTyped(char letter, int keyCode)
public boolean onKeyTyped(char letter, int codepoint)
{
return isFlagSet(FLAG_FOCUS) && text.getFont().isCharValid(letter) && writeText(Character.toString(letter));
return isFlagSet(FLAG_FOCUS) && text.getFont().isCharValid(codepoint) && writeText(new String(Character.toChars(codepoint)));
}
public boolean deleteAtCurser(int amount)

View File

@ -497,9 +497,9 @@ public class TextPanelComponent extends GuiComponent implements IButtonComponent
}
@Override
public boolean onKeyTyped(char letter, int keyCode)
public boolean onKeyTyped(char letter, int codepoint)
{
return isFlagSet(FLAG_FOCUS) && text.getFont().isCharValid(letter) && writeText(Character.toString(letter));
return isFlagSet(FLAG_FOCUS) && text.getFont().isCharValid(codepoint) && writeText(new String(Character.toChars(codepoint)));
}
public boolean deleteAtCurser(int amount)

View File

@ -17,6 +17,8 @@ import java.util.function.Consumer;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import speiger.src.collections.ints.collections.IntIterator;
import speiger.src.collections.ints.utils.IntIterators;
import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap;
import speiger.src.collections.objects.misc.pairs.ObjectObjectPair;
@ -35,7 +37,12 @@ public class FontBuilder
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);
return createBitmapFont(stream, size, "ISO-8859-1");
}
public static ObjectObjectPair<BufferedImage, JsonObject> createBitmapFont(InputStream stream, float size, String charset)
{
ObjectObjectPair<ObjectObjectPair<Vec2i, BufferedImage>, List<WrittenChar>> result = createBitmapFont(stream, charset, PLAIN | BOLD, size);
if(result == null) return null;
JsonArray array = new JsonArray();
result.getValue().forEach(T -> array.add(T.seralize()));
@ -70,7 +77,6 @@ public class FontBuilder
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);
@ -79,7 +85,6 @@ public class FontBuilder
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);
@ -93,7 +98,7 @@ public class FontBuilder
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()));
graphics.drawString(new String(Character.toChars(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())));
@ -118,9 +123,11 @@ public class FontBuilder
{
CharsetEncoder encoder = Charset.forName(s).newEncoder();
StringBuilder builder = new StringBuilder();
for(char c = 0;c < Character.MAX_VALUE;c++)
for(int c = 0;c < 0x110000;c++)
{
if(encoder.canEncode(c) && font.canDisplay(c)) builder.append(c);
if(!Character.isDefined(c)) continue;
char[] chars = Character.toChars(c);
if(encoder.canEncode(new String(chars)) && font.canDisplay(c)) builder.append(chars);
}
return builder.toString();
}
@ -135,11 +142,14 @@ public class FontBuilder
FontMetrics metric = graphics.getFontMetrics();
int extraHeight = 0;
List<CharData> data = new ObjectArrayList<>();
for(char letter : chars.toCharArray())
int c = 0;
for(IntIterator iter = IntIterators.wrap(chars.codePoints().iterator());iter.hasNext();c++)
{
Rectangle rect = font.layoutGlyphVector(graphics.getFontRenderContext(), new char[] {letter }, 0, 1, 0).getGlyphPixelBounds(0, graphics.getFontRenderContext(), 0.0F, 0.0F);
int next = iter.nextInt();
Rectangle rect = font.layoutGlyphVector(graphics.getFontRenderContext(), Character.toChars(next), 0, Character.charCount(next), 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()));
data.add(new CharData(font, next, rect, metric.charWidth(next), metric.getHeight(), font.isBold()));
if(c % 1000 == 0) System.out.println("Rendered: "+c+" / "+chars.length()+" Chars");
}
extraHeight = -(extraHeight+metric.getAscent());
for(int i = 0,m=data.size();i<m;listener.accept(data.get(i++).offset(extraHeight)));
@ -148,14 +158,14 @@ public class FontBuilder
public static class WrittenChar
{
char letter;
int 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)
public WrittenChar(int letter, int x, int y, int width, int height, boolean bold)
{
this.letter = letter;
this.x = x;
@ -168,7 +178,7 @@ public class FontBuilder
public JsonObject seralize()
{
JsonObject obj = new JsonObject();
obj.addProperty("char", (int)letter);
obj.addProperty("char", letter);
obj.addProperty("minX", x);
obj.addProperty("minY", y);
obj.addProperty("maxX", x+width);
@ -186,14 +196,14 @@ public class FontBuilder
private static class CharData
{
Font font;
char letter;
int 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)
public CharData(Font font, int letter, Rectangle bounds, int width, int height, boolean bold)
{
this.font = font;
this.letter = letter;
@ -228,14 +238,14 @@ public class FontBuilder
return extraY + asent;
}
public char getLetter()
public int getLetter()
{
return letter;
}
public AssetLocation asLocation()
{
return AssetLocation.of("base", font.getFontName().replaceAll(" ", "_") + (bold ? "_Bold" : "") + "_" + ((int)letter));
return AssetLocation.of("base", font.getFontName().replaceAll(" ", "_") + (bold ? "_Bold" : "") + "_" + (letter));
}
}
}

View File

@ -30,10 +30,10 @@ import speiger.src.coreengine.utils.helpers.TextUtil;
public class FontRenderer implements IFontRenderer
{
public static final String INVALID_SEARCH = new String(" §<");
public static final char EMPTY = (char)0;
public static final char SPACE = ' ';
public static final char TAB = '\t';
public static final char LINE_SEPERATOR = '\n';
public static final int EMPTY = (char)0;
public static final int SPACE = ' ';
public static final int TAB = '\t';
public static final int LINE_SEPERATOR = '\n';
Tesselator bufferBuilder = new Tesselator(655340);
IFontProvider provider;
@ -46,9 +46,9 @@ public class FontRenderer implements IFontRenderer
}
@Override
public CharInstance getInstance(char letter, boolean isBold)
public CharInstance getInstance(int codepoint, boolean isBold)
{
return provider.getCharacter(letter, isBold);
return provider.getCharacter(codepoint, isBold);
}
@Override
@ -302,16 +302,16 @@ public class FontRenderer implements IFontRenderer
}
@Override
public float getCharLength(char letter, boolean bold)
public float getCharLength(int codepoint, boolean bold)
{
switch(letter)
switch(codepoint)
{
case SPACE:
return provider.getSpaceWidth();
case TAB:
return provider.getTabWidth();
default:
CharInstance instance = getInstance(letter, bold);
CharInstance instance = getInstance(codepoint, bold);
return instance == null ? 0F : instance.getXAdvance();
}
}
@ -401,9 +401,9 @@ public class FontRenderer implements IFontRenderer
}
@Override
public boolean isCharValid(char letter)
public boolean isCharValid(int codepoint)
{
return provider.isCharacterValid(letter);
return provider.isCharacterValid(codepoint);
}
@Override

View File

@ -11,15 +11,15 @@ public interface IFontRenderer
public float getFontHeight();
public float getBaseLine();
public ITexture getTexture();
public CharInstance getInstance(char letter, boolean bold);
public CharInstance getInstance(int codepoint, boolean bold);
public void updateText(TextComponent component);
public default String trimStringToWidth(String text, float limit){return trimStringToWidth(text, limit, false);}
public String trimStringToWidth(String text, float limit, boolean reverse);
public default float getCharLength(char letter){return getCharLength(letter, false);};
public float getCharLength(char letter, boolean bold);
public default float getCharLength(int codepoint){return getCharLength(codepoint, false);};
public float getCharLength(int codepoint, boolean bold);
public default float getTextLength(String text){return getTextLength(text, 0);};
public float getTextLength(String text, int flags);
@ -30,7 +30,7 @@ public interface IFontRenderer
public String[] splitLines(String text, float maxWidth, int flags);
public boolean isCharValid(char letter);
public boolean isCharValid(int codepoint);
public default String clearInvalidLetters(String text)
{
if(text == null || text.isEmpty())
@ -38,13 +38,11 @@ public interface IFontRenderer
return "";
}
StringBuilder builder = new StringBuilder(text.length());
for(int i = 0,m=text.length();i<m;i++)
for(int i = 0,m=text.length();i<m;)
{
char letter = text.charAt(i);
if(isCharValid(letter))
{
builder.append(letter);
}
int codePoint = text.codePointAt(i);
if(isCharValid(codePoint)) builder.append(Character.toChars(codePoint));
i += Character.charCount(codePoint);
}
return builder.toString();
}
@ -52,7 +50,7 @@ public interface IFontRenderer
public static final class CharInstance
{
char character;
int character;
float width;
float height;
float minU;
@ -62,13 +60,13 @@ public interface IFontRenderer
float xAdvance;
boolean bold;
public CharInstance(char character, boolean bold)
public CharInstance(int 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)
public CharInstance(int character, int width, int height, float minU, float minV, float maxU, float maxV, int xAdvance, boolean bold)
{
this.character = character;
this.width = width;
@ -88,7 +86,7 @@ public interface IFontRenderer
xAdvance *= scale;
}
public char getCharacter()
public int getCharacter()
{
return character;
}

View File

@ -46,6 +46,16 @@ public class TextLexer
}
current.setSpecail(type);
}
if(Character.isHighSurrogate(letter) && i + 1 < m)
{
char extra = text.charAt(i + 1);
if(Character.isLowSurrogate(extra))
{
i++;
current.addLetter(render.getInstance(Character.toCodePoint(letter, extra), context.isBold()));
continue;
}
}
current.addLetter(render.getInstance(letter, context.isBold()));
}
if(!current.isEmpty())
@ -60,7 +70,7 @@ public class TextLexer
{
return ObjectLists.empty();
}
List<Line> lines = new ObjectArrayList<Line>();
List<Line> lines = new ObjectArrayList<>();
getWords(text, context, helper);
Line line = new Line(maxWidth);
for(int i = 0;i<helper.size();i++)

View File

@ -1,7 +1,7 @@
package speiger.src.coreengine.rendering.gui.renderer.lexer;
import speiger.src.collections.chars.collections.CharIterable;
import speiger.src.collections.chars.collections.CharIterator;
import speiger.src.collections.ints.collections.IntIterable;
import speiger.src.collections.ints.collections.IntIterator;
import speiger.src.collections.objects.collections.ObjectIterator;
import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.collections.objects.lists.ObjectList;
@ -9,7 +9,7 @@ import speiger.src.coreengine.rendering.gui.renderer.IFontRenderer.CharInstance;
import speiger.src.coreengine.rendering.gui.renderer.lexer.TextContext.WordContext;
import speiger.src.coreengine.utils.collections.iterators.IterableWrapper;
public class Word implements CharIterable
public class Word implements IntIterable
{
final float scale;
ObjectList<CharInstance> letters = new ObjectArrayList<CharInstance>();
@ -90,7 +90,7 @@ public class Word implements CharIterable
StringBuilder builder = new StringBuilder();
for(int i = 0,m=letters.size();i<m;i++)
{
builder.append(letters.get(i).getCharacter());
builder.append(Character.toChars(letters.get(i).getCharacter()));
}
return builder.toString();
}
@ -143,9 +143,9 @@ public class Word implements CharIterable
}
@Override
public CharIterator iterator()
public IntIterator iterator()
{
return new CharIterator(){
return new IntIterator(){
int index = 0;
@Override
public boolean hasNext()
@ -154,7 +154,7 @@ public class Word implements CharIterable
}
@Override
public char nextChar()
public int nextInt()
{
return letters.get(index++).getCharacter();
}

View File

@ -5,9 +5,9 @@ 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.ints.maps.impl.hash.Int2ObjectOpenHashMap;
import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap;
import speiger.src.collections.ints.utils.maps.Int2ObjectMaps;
import speiger.src.collections.objects.misc.pairs.ObjectObjectPair;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.assets.AssetManager;
@ -24,10 +24,10 @@ public class BitmapFontProvider implements IFontProvider
{
FontInfo info;
ITexture texture;
Char2ObjectMap<CharInstance>[] instances;
Int2ObjectMap<CharInstance>[] instances;
float space;
public BitmapFontProvider(FontInfo info, ITexture texture, Char2ObjectMap<CharInstance>[] instances)
public BitmapFontProvider(FontInfo info, ITexture texture, Int2ObjectMap<CharInstance>[] instances)
{
this.info = info;
this.texture = texture;
@ -53,16 +53,16 @@ public class BitmapFontProvider implements IFontProvider
}
@Override
public boolean isCharacterValid(char value)
public boolean isCharacterValid(int codepoint)
{
return instances[0].containsKey(value) || instances[1].containsKey(value);
return instances[0].containsKey(codepoint) || instances[1].containsKey(codepoint);
}
@Override
public CharInstance getCharacter(char value, boolean bold)
public CharInstance getCharacter(int codepoint, boolean bold)
{
Char2ObjectMap<CharInstance> map = instances[bold ? 1 : 0];
return (map.isEmpty() ? instances[bold ? 0 : 1] : map).get(value);
Int2ObjectMap<CharInstance> map = instances[bold ? 1 : 0];
return (map.isEmpty() ? instances[bold ? 0 : 1] : map).get(codepoint);
}
@Override
@ -93,14 +93,14 @@ public class BitmapFontProvider implements IFontProvider
{
FontInfo info = new FontInfo(object.getAsJsonObject("info"));
float multiplier = info.setDesiredHeight(desiredSize);
Char2ObjectMap<CharInstance>[] maps = new Char2ObjectMap[]{new Char2ObjectOpenHashMap<CharInstance>(), new Char2ObjectOpenHashMap<CharInstance>()};
Int2ObjectMap<CharInstance>[] maps = new Int2ObjectMap[]{new Int2ObjectOpenHashMap<CharInstance>(), new Int2ObjectOpenHashMap<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();
if(maps[0].isEmpty()) maps[0] = Int2ObjectMaps.empty();
if(maps[1].isEmpty()) maps[1] = Int2ObjectMaps.empty();
return new BitmapFontProvider(info, ITexture.simple(AssetLocation.of(object.get("file").getAsString())), maps);
}
@ -121,15 +121,15 @@ public class BitmapFontProvider implements IFontProvider
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>()};
Int2ObjectMap<CharInstance>[] maps = new Int2ObjectMap[]{new Int2ObjectOpenHashMap<CharInstance>(), new Int2ObjectOpenHashMap<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();
if(maps[0].isEmpty()) maps[0] = Int2ObjectMaps.empty();
if(maps[1].isEmpty()) maps[1] = Int2ObjectMaps.empty();
return new BitmapFontProvider(fontInfo, ITexture.direct(image), maps);
}
catch(Exception e)

View File

@ -9,8 +9,8 @@ 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 boolean isCharacterValid(int codePoint);
public CharInstance getCharacter(int codePoint, boolean bold);
public float getFontHeight();
public float getBaseLine();
@ -51,10 +51,10 @@ public interface IFontProvider
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));
return create(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)
public CharInstance create(int 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);
}

View File

@ -0,0 +1,82 @@
package speiger.src.coreengine.rendering.models;
import java.util.List;
import java.util.Map;
import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.assets.AssetManager;
import speiger.src.coreengine.assets.reloader.IReloadableResource;
import speiger.src.coreengine.rendering.models.loader.MergedModelData;
import speiger.src.coreengine.rendering.models.loader.ModelLoader;
import speiger.src.coreengine.rendering.models.loader.SimpleModelData;
public class ModelCache implements IReloadableResource
{
AssetManager manager;
Map<AssetLocation, List<SimpleModelData>> models = new Object2ObjectLinkedOpenHashMap<>();
public void setManager(AssetManager manager)
{
this.manager = manager;
}
@Override
public void reload()
{
models.clear();
load();
}
@Override
public void destroy()
{
models.clear();
}
public void load()
{
for(AssetLocation model : manager.gatherAssets("models", 512, this::isValidModel))
{
if(model.getLocation().endsWith(".json")) addModel(model, ModelLoader.readModelData(model, manager));
else if(model.getLocation().endsWith(".spm")) addModel(model, ModelLoader.readLegacyModelData(model, manager));
}
}
protected void addModel(AssetLocation location, List<SimpleModelData> data)
{
if(data.isEmpty())
{
System.out.println("["+location+"]Found Empty Model Data");
return;
}
models.put(location, data);
}
protected boolean isValidModel(String fileName)
{
return fileName.endsWith(".json") || fileName.endsWith(".spm");
}
public SimpleModelData getModel(AssetLocation location)
{
List<SimpleModelData> data = models.get(location);
return data == null ? null : data.get(0);
}
public List<SimpleModelData> getModels(AssetLocation location)
{
return models.get(location);
}
public MergedModelData getMergedModel(AssetLocation location)
{
return getMergedModel(location, "MergedModel");
}
public MergedModelData getMergedModel(AssetLocation location, String newName)
{
List<SimpleModelData> data = models.get(location);
return data == null ? null : MergedModelData.merge(newName, data);
}
}

View File

@ -0,0 +1,58 @@
package speiger.src.coreengine.rendering.models.loader;
import java.util.List;
import speiger.src.collections.bytes.lists.ByteArrayList;
import speiger.src.collections.bytes.lists.ByteList;
import speiger.src.collections.ints.lists.IntArrayList;
import speiger.src.collections.ints.lists.IntList;
public class MergedModelData extends SimpleModelData
{
int[] indexes;
protected MergedModelData(String name, byte[] data, int[] indecies, int[] indexes)
{
super(name, null, data, indecies);
this.indexes = indexes;
}
public int[] getVertexOffsets()
{
return indexes;
}
public int getVertexCount(int index)
{
return indexes[index];
}
public static MergedModelData merge(String newName, List<SimpleModelData> data)
{
return merge(newName, 28, data);
}
public static MergedModelData merge(String newName, int vertexBytes, List<SimpleModelData> data)
{
System.out.println("Test: "+data.size()+", Name="+newName);
ByteList byteData = new ByteArrayList();
IntList indeciesData = new IntArrayList();
int[] offsets = new int[data.size() * 2];
int offset = 0;
int indeciesOffset = 0;
for(int i = 0,m=data.size();i<m;i++)
{
offset = byteData.size() / vertexBytes;
byteData.addAll(data.get(i).getModelData());
int[] indexes = data.get(i).getIndecies();
offsets[(i * 2) + 1] = indeciesOffset * 4;
offsets[i * 2] = indexes.length;
indeciesOffset += indexes.length;
for(int x = 0,n=indexes.length;x<n;x++)
{
indeciesData.add(indexes[x] + offset);
}
}
return new MergedModelData(newName, byteData.toByteArray(), indeciesData.toIntArray(), offsets);
}
}

View File

@ -0,0 +1,103 @@
package speiger.src.coreengine.rendering.models.loader;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.List;
import java.util.UUID;
import com.google.gson.JsonObject;
import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.collections.objects.utils.ObjectLists;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.assets.AssetManager;
import speiger.src.coreengine.assets.IAsset;
import speiger.src.coreengine.math.misc.ColorObject;
import speiger.src.coreengine.utils.collections.iterators.IterableWrapper;
import speiger.src.coreengine.utils.helpers.JsonUtil;
public class ModelLoader
{
public static List<SimpleModelData> readModelData(AssetLocation location, AssetManager manager)
{
try(IAsset asset = manager.getAsset(location))
{
List<SimpleModelData> result = new ObjectArrayList<>();
JsonObject obj = asset.getJsonObject();
JsonUtil.iterate(obj.get("models"), T -> {
JsonObject info = T.getAsJsonObject("info");
JsonObject format = T.getAsJsonObject("format");
JsonObject data = T.getAsJsonObject("data");
List<VertexEntry> entries = VertexLoader.loadVertexFormat(format);
ByteBuffer buffer = VertexLoader.parseVertexData(data, info.get("vertecies").getAsInt(), entries, true);
result.add(new SimpleModelData(info.get("name").getAsString(), UUID.fromString(info.get("id").getAsString()), buffer.array(), JsonUtil.parseIntArray(data.getAsJsonArray("indecies"))));
});
return result;
}
catch(Exception e)
{
e.printStackTrace();
}
return ObjectLists.empty();
}
public static List<SimpleModelData> readLegacyModelData(AssetLocation location, AssetManager manager)
{
List<SimpleModelData> resultModels = new ObjectArrayList<SimpleModelData>();
ByteBuffer buffer = null;
int[] indexes = null;
String currentName = null;
try(IAsset asset = manager.getAsset(location))
{
boolean flag = false;
for(String line : IterableWrapper.wrap(asset.getStringReader()))
{
if(line.startsWith("{"))
{
if(currentName != null)
{
resultModels.add(new SimpleModelData(currentName, null, buffer.array(), indexes));
currentName = null;
}
currentName = line.substring(1, line.length() - 1);
flag = true;
}
else if(flag)
{
flag = false;
String[] bounds = line.split(";");
buffer = ByteBuffer.allocate(Integer.parseInt(bounds[0]) * 28).order(ByteOrder.nativeOrder());
indexes = new int[Integer.parseInt(bounds[1])];
}
else if(line.startsWith("<"))
{
String[] data = line.substring(1, line.length() - 1).split(" ");
String[] position = data[0].split(";");
String[] normal = data[2].split(";");
buffer.putFloat(Float.parseFloat(position[0])).putFloat(Float.parseFloat(position[1])).putFloat(Float.parseFloat(position[2]));
ColorObject.pack(Integer.parseInt(data[1]), true, buffer);
buffer.putFloat(Float.parseFloat(normal[0])).putFloat(Float.parseFloat(normal[1])).putFloat(Float.parseFloat(normal[2]));
}
else if(line.startsWith("["))
{
String[] data = line.substring(1, line.length() - 1).split(";");
for(int j = 0;j<data.length;j++)
{
indexes[j] = Integer.parseInt(data[j]);
}
}
}
if(currentName != null)
{
resultModels.add(new SimpleModelData(currentName, null, buffer.array(), indexes));
currentName = null;
}
}
catch(Exception e)
{
e.printStackTrace();
}
return resultModels;
}
}

View File

@ -0,0 +1,43 @@
package speiger.src.coreengine.rendering.models.loader;
import java.util.UUID;
public class SimpleModelData
{
String name;
UUID id;
byte[] data;
int[] indecies;
public SimpleModelData(String name, UUID id, byte[] data, int[] indecies)
{
this.name = name;
this.data = data;
this.indecies = indecies;
}
public UUID getId()
{
return id;
}
public String getName()
{
return name;
}
public byte[] getModelData()
{
return data;
}
public int[] getIndecies()
{
return indecies;
}
public int getVertexCount()
{
return indecies.length;
}
}

View File

@ -52,6 +52,18 @@ public class VertexEntry
return optional;
}
public static int caculateByteSize(List<VertexEntry> array, boolean excludeOptional)
{
int result = 0;
for(int i = 0,m=array.size();i<m;i++)
{
VertexEntry entry = array.get(i);
if(entry.isOptional() && excludeOptional) continue;
result += entry.getSize() * entry.getType().getByteSize();
}
return result;
}
public static int calculateStride(List<VertexEntry> array, boolean excludeOptional)
{
return excludeOptional ? calculateNonOptionalStride(array) : caclulateStride(array);

View File

@ -1,6 +1,7 @@
package speiger.src.coreengine.rendering.models.loader;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.List;
import com.google.gson.JsonObject;
@ -21,7 +22,7 @@ public class VertexLoader
public static ByteBuffer parseVertexData(JsonObject obj, int vertexCount, List<VertexEntry> entries, boolean excludeOptional)
{
List<Number> numbers = parseVertexData(obj, entries, excludeOptional);
ByteBuffer buffer = ByteBuffer.allocate(vertexCount * VertexEntry.calculateStride(entries, excludeOptional));
ByteBuffer buffer = ByteBuffer.allocate(vertexCount * VertexEntry.caculateByteSize(entries, excludeOptional)).order(ByteOrder.nativeOrder());
for(int i = 0,offset=0,m=vertexCount*entries.size();i<m;i++)
{
VertexEntry entry = entries.get(i % entries.size());

View File

@ -0,0 +1,39 @@
package speiger.src.coreengine.rendering.textures.custom;
import speiger.src.coreengine.rendering.textures.base.ITexture;
public interface IDynamicTexture extends ITexture
{
public void markDirty(int x, int z);
public default void markDirty(int index) { markDirty(index % getWidth(), index / getWidth()); }
public default void setData(int x, int z, int value) { setData((z * getWidth()) + x, value); }
public void setData(int index, int value);
public default void setRGB(int x, int z, int red, int green, int blue, int alpha) { setRGB((z * getWidth()) + x, red, green, blue, alpha); }
public default void setRGB(int index, int red, int green, int blue, int alpha) { setData(index, ((alpha & 0xFF) << 24) | ((red & 0xFF) << 16) | ((green & 0xFF) << 8) | (blue & 0xFF)); }
public default void setRed(int x, int z, int red) { setRed((z * getWidth()) + x, red); }
public void setRed(int index, int red);
public default void setGreen(int x, int z, int green) { setGreen((z * getWidth()) + x, green); }
public void setGreen(int index, int green);
public default void setBlue(int x, int z, int blue) { setBlue((z * getWidth()) + x, blue); }
public void setBlue(int index, int blue);
public default void setAlpha(int x, int z, int alpha) { setAlpha((z * getWidth()) + x, alpha); }
public void setAlpha(int index, int alpha);
public default int getRGB(int x, int z) { return getRGB((z * getWidth()) + x); }
public int getRGB(int index);
public default int getRed(int x, int z) { return getRed((z * getWidth()) + x); }
public int getRed(int index);
public default int getGreen(int x, int z) { return getGreen((z * getWidth()) + x); }
public int getGreen(int index);
public default int getBlue(int x, int z) { return getBlue((z * getWidth()) + x); }
public int getBlue(int index);
public default int getAlpha(int x, int z) { return getAlpha((z * getWidth()) + x); }
public int getAlpha(int index);
public boolean isDirty();
public void processChanges(boolean full);
}

View File

@ -13,7 +13,6 @@ 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>

View File

@ -1,6 +1,7 @@
package speiger.src.coreengine.rendering.textures.custom;
package speiger.src.coreengine.rendering.textures.normal;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Iterator;
import org.lwjgl.opengl.GL11;
@ -8,7 +9,7 @@ import org.lwjgl.system.MemoryStack;
import org.lwjgl.system.MemoryUtil;
import speiger.src.collections.ints.collections.IntIterator;
import speiger.src.collections.ints.maps.impl.hash.Int2ObjectOpenHashMap;
import speiger.src.collections.ints.maps.impl.hash.Int2ObjectLinkedOpenHashMap;
import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap;
import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap.Entry;
import speiger.src.collections.ints.sets.IntOpenHashSet;
@ -18,15 +19,19 @@ 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.textures.custom.IDynamicTexture;
import speiger.src.coreengine.rendering.utils.AllocationTracker;
public class DynamicTexture extends AbstractTexture
public class DynamicTexture extends AbstractTexture implements IDynamicTexture
{
Int2ObjectMap<IntSet> dirtyChunks = new Int2ObjectOpenHashMap<IntSet>();
public static final int R = 0xFF << 16;
public static final int G = 0xFF << 8;
public static final int B = 0xFF;
public static final int A = 0xFF << 24;
Int2ObjectMap<IntSet> dirtyChunks = new Int2ObjectLinkedOpenHashMap<IntSet>();
int[] data;
int width;
int height;
boolean first = true;
public DynamicTexture(int width, int height)
{
@ -36,11 +41,19 @@ public class DynamicTexture extends AbstractTexture
data = new int[width * height];
}
public DynamicTexture(int width, int height, int defaultValue)
{
this(width, height);
Arrays.fill(data, defaultValue);
processChanges(true);
}
public int[] getTextureData()
{
return data;
}
@Override
public void markDirty(int x, int z)
{
int chunkIndex = BitUtil.toInt(x >> 4, z >> 4);
@ -50,23 +63,88 @@ public class DynamicTexture extends AbstractTexture
set = new IntOpenHashSet();
dirtyChunks.put(chunkIndex, set);
}
set.add(BitUtil.toInt(x, z));
set.add((x & 15) << 4 | (z & 15));
}
public void setData(int x, int z, int value)
@Override
public void setData(int index, int value)
{
data[(z * width) + x] = value;
markDirty(x, z);
data[index] = value;
markDirty(index);
}
@Override
public void setRed(int index, int red)
{
if(getRed(index) == red) return;
data[index] = (data[index] & ~R) | (red & 0xFF) << 16;
markDirty(index);
}
public boolean hasTasks()
@Override
public void setGreen(int index, int green)
{
return dirtyChunks.size() > 0 || first;
if(getGreen(index) == green) return;
data[index] = (data[index] & ~G) | (green & 0xFF) << 8;
markDirty(index);
}
@Override
public void setBlue(int index, int blue)
{
if(getBlue(index) == blue) return;
data[index] = (data[index] & ~B) | (blue & 0xFF);
markDirty(index);
}
@Override
public void setAlpha(int index, int alpha)
{
if(getAlpha(index) == alpha) return;
data[index] = (data[index] & ~A) | (alpha & 0xFF) << 24;
markDirty(index);
}
public void updateData()
@Override
public int getRGB(int index)
{
if(first)
return data[index];
}
@Override
public int getRed(int index)
{
return (data[index] >> 16) & 0xFF;
}
@Override
public int getGreen(int index)
{
return (data[index] >> 8) & 0xFF;
}
@Override
public int getBlue(int index)
{
return data[index] & 0xFF;
}
@Override
public int getAlpha(int index)
{
return (data[index] >> 24) & 0xFF;
}
@Override
public boolean isDirty()
{
return dirtyChunks.size() > 0;
}
@Override
public void processChanges(boolean full)
{
if(full)
{
ByteBuffer buffer = MemoryUtil.memAlloc(data.length * 4);
for(int i = 0;i<data.length;i++)
@ -83,7 +161,6 @@ public class DynamicTexture extends AbstractTexture
GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
AllocationTracker.INSTANCE.addGPUBytes(buffer.remaining());
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, width, height, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer);
first = false;
MemoryUtil.memFree(buffer);
dirtyChunks.clear();
}
@ -102,8 +179,8 @@ public class DynamicTexture extends AbstractTexture
for(IntIterator subIt = values.getValue().iterator();subIt.hasNext();)
{
int index = subIt.nextInt();
width = Math.max(width, BitUtil.toFirstShort(index) - chunkX + 1);
height = Math.max(height, BitUtil.toSecondShort(index) - chunkZ + 1);
width = Math.max(width, ((index >> 4) & 0xF) + 1);
height = Math.max(height, (index & 0xF) + 1);
}
try(MemoryStack stack = MemoryStack.stackPush())
{
@ -137,9 +214,7 @@ public class DynamicTexture extends AbstractTexture
int old = getTextureID();
setTextureID(GL11.glGenTextures());
TextureManager.INSTANCE.removeTexture(old);
dirtyChunks.clear();
first = true;
updateData();
processChanges(true);
}
@Override

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 186 KiB

After

Width:  |  Height:  |  Size: 1.0 MiB