diff --git a/assets/Fonts/Tuffy_Bold.ttf b/assets/Fonts/Tuffy_Bold.ttf new file mode 100644 index 00000000..33fcc8af Binary files /dev/null and b/assets/Fonts/Tuffy_Bold.ttf differ diff --git a/buildNumber.properties b/buildNumber.properties index 551140c3..81ae460b 100644 --- a/buildNumber.properties +++ b/buildNumber.properties @@ -1,3 +1,3 @@ #maven.buildNumber.plugin properties file -#Mon Feb 26 10:41:04 EST 2024 -buildNumber=22 +#Wed Feb 28 20:29:21 EST 2024 +buildNumber=26 diff --git a/docs/src/fonts/Fonts.md b/docs/src/fonts/Fonts.md new file mode 100644 index 00000000..cddf0391 --- /dev/null +++ b/docs/src/fonts/Fonts.md @@ -0,0 +1,99 @@ +# Fonts + + + + +## High Level Overview +The font loading mechanism loads all fonts at engine startup. It leverages java.awt.Font to read font data from ttf files on disk, render them to a bitmap, and then display appropriately. +Globals has a font manager that can be leveraged to get fonts based on identification strings. + + + + + + + + + + +## Major Usage Notes + + + + + + + + + + + + + + + + + + + + + + + +## Main Classes + +[Font.java](@ref #electrosphere.renderer.ui.font.Font) - Holds all the data about a font: the glyphs and metadata about them, as well as the material containing the actual bitmap + +[FontManager.java](@ref #electrosphere.renderer.ui.font.FontManager) - Keeps track of all fonts loaded into the engine. + +[FontUtils.java](@ref #electrosphere.renderer.ui.font.FontUtils) - Utilities to load fonts from disk + +[BitmapCharacter.java](@ref #electrosphere.renderer.ui.font.bitmapchar.BitmapCharacter) - The main rendering component for text. Renders a single character of text based on a given font, font size, and character. + + + + + + + + + + + + +## Code Organization and Best Practices + +#### Startup + + +#### Usage + + + + + + + + + + + +## Terminology + + + + + + + + + +## Known Bugs To Fix + + + + +## Future Goals + + - Scan assets/fonts and load all fonts automatically + - Ability to specify font for a given label (currently it's always hard-set to "default") \ No newline at end of file diff --git a/src/main/java/electrosphere/engine/Globals.java b/src/main/java/electrosphere/engine/Globals.java index 02719b3c..fb6db3bf 100644 --- a/src/main/java/electrosphere/engine/Globals.java +++ b/src/main/java/electrosphere/engine/Globals.java @@ -57,8 +57,7 @@ import electrosphere.renderer.shader.ShaderOptionMap; import electrosphere.renderer.texture.TextureMap; import electrosphere.renderer.ui.ElementManager; import electrosphere.renderer.ui.elements.ImagePanel; -import electrosphere.renderer.ui.font.FontUtils; -import electrosphere.renderer.ui.font.RawFontMap; +import electrosphere.renderer.ui.font.FontManager; import electrosphere.script.ScriptEngine; import electrosphere.server.ai.AIManager; import electrosphere.server.content.ServerContentManager; @@ -231,6 +230,9 @@ public class Globals { public static String particleBillboardModel; public static ShaderOptionMap shaderOptionMap; + + //manages all loaded fonts + public static FontManager fontManager; @@ -435,9 +437,10 @@ public class Globals { materialDefault.set_diffuse("Textures/default_diffuse.png"); materialDefault.set_specular("Textures/default_specular.png"); //create default lights + //create font manager + fontManager = new FontManager(); + fontManager.loadFonts(); assetManager.registerModelToSpecificString(RenderUtils.createBitmapCharacter(), AssetDataStrings.BITMAP_CHARACTER_MODEL); - RawFontMap fontMap = FileUtils.loadObjectFromAssetPath("Textures/Fonts/myFont2Map.json", RawFontMap.class); - FontUtils.setFontDataMap(fontMap); //particle billboard model particleBillboardModel = assetManager.registerModel(RenderUtils.createParticleModel()); //black texture for backgrouns diff --git a/src/main/java/electrosphere/renderer/Model.java b/src/main/java/electrosphere/renderer/Model.java index 5dcf5396..33f9a844 100644 --- a/src/main/java/electrosphere/renderer/Model.java +++ b/src/main/java/electrosphere/renderer/Model.java @@ -515,7 +515,21 @@ public class Model { this.textureMap = textureMask; } + /** + * Sets the shader program to use drawing the modal + * @param program The shader program + */ public void setProgram(ShaderProgram program){ this.program = program; } + + /** + * Utility method to attempt overwriting the model's meshes with a new material + * @param material The material + */ + public void tryOverwriteMaterial(Material material){ + for(Mesh mesh : meshes){ + mesh.setMaterial(material); + } + } } diff --git a/src/main/java/electrosphere/renderer/RenderUtils.java b/src/main/java/electrosphere/renderer/RenderUtils.java index 25e3741a..b0cdd757 100644 --- a/src/main/java/electrosphere/renderer/RenderUtils.java +++ b/src/main/java/electrosphere/renderer/RenderUtils.java @@ -717,7 +717,10 @@ public class RenderUtils { - + /** + * Creates a model to use to show bitmap characters + * @return The model + */ public static Model createBitmapCharacter(){ Model rVal = new Model(); @@ -798,14 +801,6 @@ public class RenderUtils { m.parent = rVal; m.nodeID = AssetDataStrings.ASSET_STRING_BITMAP_FONT_MESH_NAME; - Material uiMat = new Material(); - Globals.assetManager.addTexturePathtoQueue("/Textures/Fonts/myfont2.png"); - uiMat.set_diffuse("/Textures/Fonts/myfont2.png"); - uiMat.set_specular("/Textures/Fonts/myfont2.png"); - m.setMaterial(uiMat); - rVal.materials = new ArrayList(); - rVal.materials.add(uiMat); - rVal.meshes.add(m); return rVal; diff --git a/src/main/java/electrosphere/renderer/texture/Texture.java b/src/main/java/electrosphere/renderer/texture/Texture.java index f4ba56ab..8bae4a3a 100644 --- a/src/main/java/electrosphere/renderer/texture/Texture.java +++ b/src/main/java/electrosphere/renderer/texture/Texture.java @@ -33,7 +33,7 @@ public class Texture { String path = ""; - public Texture(){ + private Texture(){ } @@ -41,6 +41,73 @@ public class Texture { this.texturePointer = pointer; } + /** + * Creates an in engine texture object from a java bufferedimage object + * @param bufferedImage The java bufferedimage object + */ + public Texture(BufferedImage bufferedImage){ + texturePointer = glGenTextures(); + //bind the new texture + glBindTexture(GL_TEXTURE_2D, texturePointer); + //how are we gonna wrap the texture?? + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT); + //set the border color to black + float borderColor[] = { 0.0f, 0.0f, 0.0f, 1.0f }; + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); + //set magnification and minification operation sampling strategies + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + //load the image here + ByteBuffer data; + width = 1; + height = 1; + BufferedImage image_data = bufferedImage; + if ( + image_data.getType() == BufferedImage.TYPE_3BYTE_BGR || + image_data.getType() == BufferedImage.TYPE_INT_RGB + ){ + hasTransparency = false; + } else if( + image_data.getType() == BufferedImage.TYPE_4BYTE_ABGR || + image_data.getType() == BufferedImage.TYPE_INT_ARGB + ){ + hasTransparency = true; + } + width = image_data.getWidth(); + height = image_data.getHeight(); + if(hasTransparency){ + data = BufferUtils.createByteBuffer(width * height * 4); + } else { + data = BufferUtils.createByteBuffer(width * height * 3); + } + for(int y = height - 1; y > -1; y--){ + for(int x = 0; x < width; x++){ + Color temp = new Color(image_data.getRGB(x, y), true); + + data.put((byte)temp.getRed()); + data.put((byte)temp.getGreen()); + data.put((byte)temp.getBlue()); + if(hasTransparency){ + data.put((byte)temp.getAlpha()); + } + } + } + data.flip(); + //call if width != height so opengl figures out how to unpack it properly + if(width != height){ + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + } + //buffer the texture information + if(hasTransparency){ + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + } else { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); + } + glGenerateMipmap(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + } + public Texture(String path){ this.path = path; if(!Globals.HEADLESS){ diff --git a/src/main/java/electrosphere/renderer/ui/elements/Label.java b/src/main/java/electrosphere/renderer/ui/elements/Label.java index 7ee4905b..95c06d5d 100644 --- a/src/main/java/electrosphere/renderer/ui/elements/Label.java +++ b/src/main/java/electrosphere/renderer/ui/elements/Label.java @@ -10,6 +10,7 @@ import electrosphere.renderer.debug.DebugRendering; import electrosphere.renderer.ui.DrawableElement; import electrosphere.renderer.ui.Element; import electrosphere.renderer.ui.events.Event; +import electrosphere.renderer.ui.font.Font; import electrosphere.renderer.ui.font.FontUtils; import electrosphere.renderer.ui.font.bitmapchar.BitmapCharacter; @@ -39,12 +40,15 @@ public class Label implements DrawableElement { List childrenElements = new LinkedList(); static final Vector3f windowDrawDebugColor = new Vector3f(1.0f,1.0f,1.0f); + + Font font; public Label(int x, int y, float fontSize){ this.positionX = x; this.positionY = y; this.width = 0; - this.height = (int)(FontUtils.getFontHeight() * fontSize); + this.font = Globals.fontManager.getFont("default"); + this.height = (int)(font.getFontHeight() * fontSize); this.fontSize = fontSize; } @@ -53,8 +57,8 @@ public class Label implements DrawableElement { int rollingOffset = 0; for(int i = 0; i < text.length(); i++){ char toDraw = text.charAt(i); - Vector3f bitMapDimension = FontUtils.getDimensionOfCharacterDiscrete(toDraw); - BitmapCharacter newLetter = new BitmapCharacter((int)(rollingOffset * fontSize) + positionX, positionY, (int)(bitMapDimension.x * fontSize), this.height, toDraw); + Vector3f bitMapDimension = this.font.getDimensionOfCharacterDiscrete(toDraw); + BitmapCharacter newLetter = new BitmapCharacter(this.font,(int)(rollingOffset * fontSize) + positionX, positionY, (int)(bitMapDimension.x * fontSize), this.height, toDraw); rollingOffset += (int)bitMapDimension.x; childrenElements.add(newLetter); } @@ -64,7 +68,7 @@ public class Label implements DrawableElement { this.text = text; textPixelWidth = 0; for(int i = 0; i < text.length(); i++){ - Vector3f bitMapDimension = FontUtils.getDimensionOfCharacterDiscrete(text.charAt(i)); + Vector3f bitMapDimension = this.font.getDimensionOfCharacterDiscrete(text.charAt(i)); textPixelWidth = textPixelWidth + (int)bitMapDimension.x; } generateLetters(); diff --git a/src/main/java/electrosphere/renderer/ui/elements/StringCarousel.java b/src/main/java/electrosphere/renderer/ui/elements/StringCarousel.java index 1b837e0e..502a3eab 100644 --- a/src/main/java/electrosphere/renderer/ui/elements/StringCarousel.java +++ b/src/main/java/electrosphere/renderer/ui/elements/StringCarousel.java @@ -5,6 +5,7 @@ import java.util.List; import org.joml.Vector3f; +import electrosphere.engine.Globals; import electrosphere.renderer.ui.DrawableElement; import electrosphere.renderer.ui.Element; import electrosphere.renderer.ui.FocusableElement; @@ -15,6 +16,7 @@ import electrosphere.renderer.ui.events.FocusEvent; import electrosphere.renderer.ui.events.MenuEvent; import electrosphere.renderer.ui.events.ValueChangeEvent; import electrosphere.renderer.ui.events.MenuEvent.MenuEventType; +import electrosphere.renderer.ui.font.Font; import electrosphere.renderer.ui.font.FontUtils; import electrosphere.renderer.ui.font.bitmapchar.BitmapCharacter; @@ -46,12 +48,15 @@ public class StringCarousel implements DrawableElement, MenuEventElement, Focusa float fontSize = 1.0f; List childrenElements = new LinkedList(); + + Font font; public StringCarousel(int x, int y, float fontSize){ this.positionX = x; this.positionY = y; this.width = 0; - this.height = (int)(FontUtils.getFontHeight() * fontSize); + this.font = Globals.fontManager.getFont("default"); + this.height = (int)(font.getFontHeight() * fontSize); this.fontSize = fontSize; } @@ -82,8 +87,8 @@ public class StringCarousel implements DrawableElement, MenuEventElement, Focusa int rollingOffset = 0; for(int i = 0; i < textCurrent.length(); i++){ char toDraw = textCurrent.charAt(i); - Vector3f bitMapDimension = FontUtils.getDimensionOfCharacterDiscrete(toDraw); - BitmapCharacter newLetter = new BitmapCharacter((int)(rollingOffset * fontSize) + positionX, positionY, (int)(bitMapDimension.x * fontSize), this.height, toDraw); + Vector3f bitMapDimension = this.font.getDimensionOfCharacterDiscrete(toDraw); + BitmapCharacter newLetter = new BitmapCharacter(this.font,(int)(rollingOffset * fontSize) + positionX, positionY, (int)(bitMapDimension.x * fontSize), this.height, toDraw); rollingOffset += (int)bitMapDimension.x; childrenElements.add(newLetter); } @@ -93,7 +98,7 @@ public class StringCarousel implements DrawableElement, MenuEventElement, Focusa this.textCurrent = text; textPixelWidth = 0; for(int i = 0; i < text.length(); i++){ - Vector3f bitMapDimension = FontUtils.getDimensionOfCharacterDiscrete(text.charAt(i)); + Vector3f bitMapDimension = this.font.getDimensionOfCharacterDiscrete(text.charAt(i)); textPixelWidth = textPixelWidth + (int)bitMapDimension.x; } generateLetters(); diff --git a/src/main/java/electrosphere/renderer/ui/elements/TextInput.java b/src/main/java/electrosphere/renderer/ui/elements/TextInput.java index ad29e4d4..abe425df 100644 --- a/src/main/java/electrosphere/renderer/ui/elements/TextInput.java +++ b/src/main/java/electrosphere/renderer/ui/elements/TextInput.java @@ -14,6 +14,7 @@ import electrosphere.renderer.ui.KeyEventElement; import electrosphere.renderer.ui.events.Event; import electrosphere.renderer.ui.events.FocusEvent; import electrosphere.renderer.ui.events.KeyboardEvent; +import electrosphere.renderer.ui.font.Font; import electrosphere.renderer.ui.font.FontUtils; import electrosphere.renderer.ui.font.bitmapchar.BitmapCharacter; @@ -54,12 +55,15 @@ public class TextInput implements DrawableElement, FocusableElement, KeyEventEle float fontSize = 1.0f; List childrenElements = new LinkedList(); + + Font font; public TextInput(int x, int y, float fontSize){ this.positionX = x; this.positionY = y; this.width = 0; - this.height = (int)(FontUtils.getFontHeight() * fontSize); + this.font = Globals.fontManager.getFont("default"); + this.height = (int)(this.font.getFontHeight() * fontSize); this.fontSize = fontSize; this.color = new Vector3f(1,1,1); } @@ -69,8 +73,8 @@ public class TextInput implements DrawableElement, FocusableElement, KeyEventEle int rollingOffset = 0; for(int i = 0; i < text.length(); i++){ char toDraw = text.charAt(i); - Vector3f bitMapDimension = FontUtils.getDimensionOfCharacterDiscrete(toDraw); - BitmapCharacter newLetter = new BitmapCharacter((int)(rollingOffset * fontSize) + positionX, positionY, (int)(bitMapDimension.x * fontSize), this.height, toDraw); + Vector3f bitMapDimension = this.font.getDimensionOfCharacterDiscrete(toDraw); + BitmapCharacter newLetter = new BitmapCharacter(this.font,(int)(rollingOffset * fontSize) + positionX, positionY, (int)(bitMapDimension.x * fontSize), this.height, toDraw); newLetter.setColor(color); rollingOffset += (int)bitMapDimension.x; childrenElements.add(newLetter); @@ -81,7 +85,7 @@ public class TextInput implements DrawableElement, FocusableElement, KeyEventEle this.text = text; textPixelWidth = 0; for(int i = 0; i < text.length(); i++){ - Vector3f bitMapDimension = FontUtils.getDimensionOfCharacterDiscrete(text.charAt(i)); + Vector3f bitMapDimension = this.font.getDimensionOfCharacterDiscrete(text.charAt(i)); textPixelWidth = textPixelWidth + (int)bitMapDimension.x; } generateLetters(); diff --git a/src/main/java/electrosphere/renderer/ui/font/Font.java b/src/main/java/electrosphere/renderer/ui/font/Font.java new file mode 100644 index 00000000..f2c8acc0 --- /dev/null +++ b/src/main/java/electrosphere/renderer/ui/font/Font.java @@ -0,0 +1,143 @@ +package electrosphere.renderer.ui.font; + +import java.util.HashMap; +import java.util.List; +import org.joml.Vector3f; + +import electrosphere.renderer.Material; + +/** + * A font + */ +public class Font { + + //the font image + Material fontMaterial; + //the list of glyphs for the font + public List glyphs; + //dimensions of the font image + public int imageWidth; + public int imageHeight; + + //the map of character->position in the image + HashMap positionMap = new HashMap(); + //the map of character->dimension of the character in pixels + HashMap dimensionMap = new HashMap(); + + /** + * Creates the font object + * @param fontMaterial + * @param glyphs + * @param imageWidth + * @param imageHeight + */ + protected Font( + Material fontMaterial, + List glyphs, + int imageWidth, + int imageHeight + ){ + this.fontMaterial = fontMaterial; + this.glyphs = glyphs; + this.imageHeight = imageHeight; + this.imageWidth = imageWidth; + } + + /** + * Data about a single glyph in the font + */ + public static class Glyph { + + public String symbol; + public int startX; + public int startY; + public int width; + public int height; + } + + /** + * Gets the position of a given character + * @param character The character + * @return the position + */ + public Vector3f getPositionOfCharacter(char character){ + Vector3f position; + if((position = positionMap.get(character))!=null){ + position = new Vector3f(position); + position.x = position.x / imageWidth; + position.y = position.y / imageHeight; + } else { + position = new Vector3f(); + } + return position; + } + + /** + * Gets the dimensions of a given character + * @param character the character + * @return the dimensions + */ + public Vector3f getDimensionOfCharacter(char character){ + Vector3f dimension; + if((dimension = dimensionMap.get(character))!=null){ + dimension = new Vector3f(dimension); + dimension.x = dimension.x / imageWidth; + dimension.y = dimension.y / imageHeight; + } else { + dimension = new Vector3f(0.5f,0.5f,0f); + } + return dimension; + } + + /** + * Gets the dimensions of a given character in discrete pixels + * @param character The character + * @return the dimensions in pixels + */ + public Vector3f getDimensionOfCharacterDiscrete(char character){ + Vector3f dimension; + if((dimension = dimensionMap.get(character))!=null){ + dimension = new Vector3f(dimension); + } else { + dimension = new Vector3f(12,14,0f); + } + return dimension; + } + + /** + * Gets the height of the font + * @return the height + */ + public int getFontHeight(){ + return imageHeight; + } + + + /** + * Processes the glyphs into structures that will contain instantly lookup-able data + */ + protected void process(){ + for(Glyph glyph : glyphs){ + char charVal = glyph.symbol.charAt(0); + Vector3f position = new Vector3f(glyph.startX,glyph.startY,0); + positionMap.put(charVal,position); + } + //fill dimension map + dimensionMap.clear(); + for(Glyph glyph : glyphs){ + char charVal = glyph.symbol.charAt(0); + Vector3f dimension = new Vector3f(glyph.width,glyph.height,0); + dimensionMap.put(charVal,dimension); + } + } + + /** + * Gets the material for the font + * @return The material + */ + public Material getMaterial(){ + return fontMaterial; + } + + +} diff --git a/src/main/java/electrosphere/renderer/ui/font/FontManager.java b/src/main/java/electrosphere/renderer/ui/font/FontManager.java new file mode 100644 index 00000000..8677aff5 --- /dev/null +++ b/src/main/java/electrosphere/renderer/ui/font/FontManager.java @@ -0,0 +1,55 @@ +package electrosphere.renderer.ui.font; + +import java.awt.FontFormatException; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import electrosphere.logger.LoggerInterface; +import electrosphere.renderer.Material; +import electrosphere.util.FileUtils; + +/** + * Manages all fonts loaded into the engine + */ +public class FontManager { + + //the default font + Font defaultFont; + + //maps names of fonts to font objects + Map fontMap = new HashMap(); + + + /** + * Gets a font based on an identifying string + * @param identifier the identifying string + * @return The font if it exists or null + */ + public Font getFont(String identifier){ + return fontMap.get(identifier); + } + + /** + * Loads fonts at engine startup during the init graphics resource phase + */ + public void loadFonts(){ + java.awt.Font font = null; + try { + font = java.awt.Font.createFont(java.awt.Font.TRUETYPE_FONT, FileUtils.getAssetFileAsStream("Fonts/Tuffy_Bold.ttf")); + font = font.deriveFont(24f); + } catch (FontFormatException e) { + LoggerInterface.loggerEngine.ERROR("Failed to load a font!", e); + } catch (IOException e) { + LoggerInterface.loggerEngine.ERROR("Failed to load a font!", e); + } + if(font==null){ + font = new java.awt.Font(java.awt.Font.MONOSPACED, java.awt.Font.PLAIN, 16); + } + if(font!=null){ + defaultFont = FontUtils.loadFont(font, false); + fontMap.put("default",defaultFont); + } + } + +} diff --git a/src/main/java/electrosphere/renderer/ui/font/FontUtils.java b/src/main/java/electrosphere/renderer/ui/font/FontUtils.java index 4c34dbc4..a2e335b7 100644 --- a/src/main/java/electrosphere/renderer/ui/font/FontUtils.java +++ b/src/main/java/electrosphere/renderer/ui/font/FontUtils.java @@ -1,87 +1,162 @@ package electrosphere.renderer.ui.font; -import electrosphere.engine.Globals; -import electrosphere.entity.Entity; -import electrosphere.entity.EntityDataStrings; -import electrosphere.entity.EntityUtils; -import electrosphere.renderer.Model; -import electrosphere.renderer.ModelUtils; -import electrosphere.renderer.actor.ActorUtils; -import electrosphere.renderer.ui.font.RawFontMap.Glyph; -import java.util.HashMap; -import org.joml.Vector3f; -import org.joml.Vector3i; +import electrosphere.renderer.Material; +import electrosphere.renderer.texture.Texture; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.awt.RenderingHints; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.util.LinkedList; +import java.util.List; + +import javax.imageio.ImageIO; + + /** - * - * @author amaterasu + * Utilities for loading fonts */ public class FontUtils { - - static RawFontMap rawFontMap; - - static HashMap positionMap = new HashMap(); - static HashMap dimensionMap = new HashMap(); - - static int width = 10; - static int height = 10; - - public static Vector3f getPositionOfCharacter(char character){ - Vector3f position; - if((position = positionMap.get(character))!=null){ - position = new Vector3f(position); - position.x = position.x / rawFontMap.imageWidth; - position.y = position.y / rawFontMap.imageHeight; - } else { - position = new Vector3f(); + + + /** + * Renders a single character to a buffered image + * @param font The java font object + * @param c the character + * @param antiAlias whether to antialias the font or not + * @return The buffered image with the rendered font + */ + private static BufferedImage createCharImage(java.awt.Font font, char c, boolean antiAlias) { + + //get the size of the character + BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); + Graphics2D g = image.createGraphics(); + if(antiAlias){ + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); } - return position; + g.setFont(font); + FontMetrics metrics = g.getFontMetrics(); + g.dispose(); + int charWidth = metrics.charWidth(c); + int charHeight = metrics.getHeight(); + + //return 0 width characters + if (charWidth == 0) { + return null; + } + + //render character to a properly sized buffered image + image = new BufferedImage(charWidth, charHeight, BufferedImage.TYPE_INT_ARGB); + g = image.createGraphics(); + if (antiAlias) { + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + } + g.setFont(font); + g.setPaint(java.awt.Color.WHITE); + g.drawString(String.valueOf(c), 0, metrics.getAscent()); + g.dispose(); + return image; } - - public static Vector3f getDimensionOfCharacter(char character){ - Vector3f dimension; - if((dimension = dimensionMap.get(character))!=null){ - dimension = new Vector3f(dimension); - dimension.x = dimension.x / rawFontMap.imageWidth; - dimension.y = dimension.y / rawFontMap.imageHeight; - } else { - dimension = new Vector3f(0.5f,0.5f,0f); + + /** + * Loads a java font object into an engine font object + * @param font The java font object + * @param antiAlias if true, antialias, otherwise dont + * @return The engine font object + */ + protected static Font loadFont(java.awt.Font font, boolean antiAlias) { + int imageWidth = 0; + int imageHeight = 0; + + + //data for parsing characters out of the font image + List glyphs = new LinkedList(); + + //iterate through ascii codes + for(int i = 32; i < 256; i++){ + if(i == 127){ + //skip del character + continue; + } + char c = (char)i; + BufferedImage ch = createCharImage(font, c, antiAlias); + if(ch == null){ + //skip characters with no images + continue; + } + + imageWidth += ch.getWidth(); + imageHeight = Math.max(imageHeight, ch.getHeight()); } - return dimension; - } - - public static Vector3f getDimensionOfCharacterDiscrete(char character){ - Vector3f dimension; - if((dimension = dimensionMap.get(character))!=null){ - dimension = new Vector3f(dimension); - } else { - dimension = new Vector3f(12,14,0f); + + int fontHeight = imageHeight; + + //create font bitmap + BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_ARGB); + Graphics2D g = image.createGraphics(); + + int x = 0; + + //loop through ascii codes + for (int i = 32; i < 256; i++) { + if (i == 127) { + //skip del character command + continue; + } + char c = (char) i; + BufferedImage charImage = createCharImage(font, c, antiAlias); + if (charImage == null) { + //don't render if the character is blank + continue; + } + + int charWidth = charImage.getWidth(); + int charHeight = charImage.getHeight(); + + //draw the glyph to the image + g.drawImage(charImage, x, 0, null); + + //create glyph and push it into the array + Font.Glyph glyph = new Font.Glyph(); + glyph.height = charHeight; + glyph.width = charWidth; + glyph.startX = x; + glyph.startY = 0; + glyph.symbol = (char)i + ""; + glyphs.add(glyph); + + //increment image + x += charWidth; } - return dimension; - } - - public static int getFontHeight(){ - return height; - } - - public static void setFontDataMap(RawFontMap map){ - rawFontMap = map; - width = map.imageWidth; - height = map.imageHeight; - //fill position map - positionMap.clear(); - for(Glyph glyph : rawFontMap.glyphs){ - char charVal = glyph.symbol.charAt(0); - Vector3f position = new Vector3f(glyph.startX,glyph.startY,0); - positionMap.put(charVal,position); - } - //fill dimension map - dimensionMap.clear(); - for(Glyph glyph : rawFontMap.glyphs){ - char charVal = glyph.symbol.charAt(0); - Vector3f dimension = new Vector3f(glyph.width,glyph.height,0); - dimensionMap.put(charVal,dimension); + + //uncomment if you need to flip the font + // AffineTransform transform = AffineTransform.getScaleInstance(1f, -1f); + // transform.translate(0, -image.getHeight()); + // AffineTransformOp operation = new AffineTransformOp(transform, AffineTransformOp.TYPE_NEAREST_NEIGHBOR); + // image = operation.filter(image, null); + + try { + ImageIO.write(image, "png", Files.newOutputStream(new File("C:/users/satellite/Pictures/testimg.png").toPath())); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); } + + + //create material with new font image + Material uiMat = new Material(); + Texture texture = new Texture(image); + uiMat.setTexturePointer(texture.getTexturePointer()); + + + //construct final font object and return + Font rVal = new Font(uiMat,glyphs,imageWidth,imageHeight); + rVal.process(); + return rVal; } diff --git a/src/main/java/electrosphere/renderer/ui/font/RawFontMap.java b/src/main/java/electrosphere/renderer/ui/font/RawFontMap.java deleted file mode 100644 index ba16b9bb..00000000 --- a/src/main/java/electrosphere/renderer/ui/font/RawFontMap.java +++ /dev/null @@ -1,27 +0,0 @@ -package electrosphere.renderer.ui.font; - -import java.util.HashMap; -import java.util.List; -import org.joml.Vector3f; - -/** - * - * @author amaterasu - */ -public class RawFontMap { - - public List glyphs; - public int imageWidth; - public int imageHeight; - - - public class Glyph { - public String symbol; - public int startX; - public int startY; - public int width; - public int height; - } - - -} diff --git a/src/main/java/electrosphere/renderer/ui/font/bitmapchar/BitmapCharacter.java b/src/main/java/electrosphere/renderer/ui/font/bitmapchar/BitmapCharacter.java index dc3d7944..d1c4cecb 100644 --- a/src/main/java/electrosphere/renderer/ui/font/bitmapchar/BitmapCharacter.java +++ b/src/main/java/electrosphere/renderer/ui/font/bitmapchar/BitmapCharacter.java @@ -2,10 +2,12 @@ package electrosphere.renderer.ui.font.bitmapchar; import electrosphere.engine.Globals; import electrosphere.engine.assetmanager.AssetDataStrings; +import electrosphere.renderer.Material; import electrosphere.renderer.Model; import electrosphere.renderer.ui.DrawableElement; import electrosphere.renderer.ui.events.Event; -import electrosphere.renderer.ui.font.FontUtils; +import electrosphere.renderer.ui.font.Font; +// import electrosphere.renderer.ui.font.FontUtils; import org.joml.Vector3f; /** @@ -17,15 +19,18 @@ public class BitmapCharacter implements DrawableElement { String text; Vector3f color = new Vector3f(0,0,0); + + Font font; - public BitmapCharacter(int posX, int posY, int width, int height, char toDraw){ + public BitmapCharacter(Font font, int posX, int posY, int width, int height, char toDraw){ this.positionX = posX; this.positionY = posY; this.width = width; this.height = height; this.text = "" + toDraw; + this.font = font; } @@ -56,13 +61,16 @@ public class BitmapCharacter implements DrawableElement { char toDraw = text.charAt(0); Vector3f characterPosition = new Vector3f(ndcX,ndcY,0); Vector3f characterDimensions = new Vector3f(ndcWidth,ndcHeight,0); - Vector3f bitMapPosition = FontUtils.getPositionOfCharacter(toDraw); - Vector3f bitMapDimension = FontUtils.getDimensionOfCharacter(toDraw); + Vector3f bitMapPosition = this.font.getPositionOfCharacter(toDraw); + Vector3f bitMapDimension = this.font.getDimensionOfCharacter(toDraw); // bitMapDimension.y = 1; // System.out.println(bitMapPosition); // System.out.println(bitMapDimension); // System.out.println("\n\n"); + //load model and try overwriting with font material Model charModel = Globals.assetManager.fetchModel(AssetDataStrings.BITMAP_CHARACTER_MODEL); + Material mat = this.font.getMaterial(); + charModel.tryOverwriteMaterial(mat); if(charModel != null && toDraw != ' '){ charModel.pushUniformToMesh(AssetDataStrings.ASSET_STRING_BITMAP_FONT_MESH_NAME, "mPosition", characterPosition); charModel.pushUniformToMesh(AssetDataStrings.ASSET_STRING_BITMAP_FONT_MESH_NAME, "mDimension", characterDimensions);