diff --git a/assets/Shaders/ui/font/bitmapchar/bitmapchar.fs b/assets/Shaders/ui/font/bitmapchar/bitmapchar.fs
index 9b1948c3..d44a0a6c 100644
--- a/assets/Shaders/ui/font/bitmapchar/bitmapchar.fs
+++ b/assets/Shaders/ui/font/bitmapchar/bitmapchar.fs
@@ -14,5 +14,6 @@ void main(){
textColorModifier.y = 1;
textColorModifier.z = 1;
}
- FragColor = texture(screenTexture, TexCoords) * vec4(textColorModifier.xyz, 1.0);
+ vec4 sample = texture(screenTexture, TexCoords);
+ FragColor = vec4(sample.r) * vec4(textColorModifier.xyz, 1.0);
}
\ No newline at end of file
diff --git a/buildNumber.properties b/buildNumber.properties
index 41399377..7813d687 100644
--- a/buildNumber.properties
+++ b/buildNumber.properties
@@ -1,3 +1,3 @@
#maven.buildNumber.plugin properties file
-#Wed Nov 20 13:22:48 EST 2024
-buildNumber=392
+#Wed Nov 20 16:01:40 EST 2024
+buildNumber=398
diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md
index 2f825f71..a32883e6 100644
--- a/docs/src/progress/renderertodo.md
+++ b/docs/src/progress/renderertodo.md
@@ -1090,6 +1090,8 @@ Fix invalid normals from transvoxel rasterizer
Design storm engine icon
Fix edge-polygon generation for invalid cases
Add engine logo to title menu
+Use STBttf for font loading/remove dependency on java.awt.fonts
+Fix font height lookups in string carousel, text input, and word
# TODO
diff --git a/pom.xml b/pom.xml
index 716ff5ec..d5d223f6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,8 +7,8 @@
jar
UTF-8
- 21
- 21
+ 17
+ 17
3.3.3
1.9.19
1.5.7
diff --git a/src/main/java/electrosphere/client/ui/menu/mainmenu/MenuGeneratorsTitleMenu.java b/src/main/java/electrosphere/client/ui/menu/mainmenu/MenuGeneratorsTitleMenu.java
index 959ed6c7..404e90ac 100644
--- a/src/main/java/electrosphere/client/ui/menu/mainmenu/MenuGeneratorsTitleMenu.java
+++ b/src/main/java/electrosphere/client/ui/menu/mainmenu/MenuGeneratorsTitleMenu.java
@@ -43,31 +43,31 @@ public class MenuGeneratorsTitleMenu {
//label (title)
- Label titleLabel = Label.createLabel("ORPG");
+ Label titleLabel = Label.createLabel("ORPG",3.0f);
optionPanel.addChild(titleLabel);
//button (multiplayer)
- optionPanel.addChild(Button.createButtonCentered("Singleplayer", () -> {
+ optionPanel.addChild(Button.createButtonCentered("Singleplayer", 2.0f, () -> {
WindowUtils.replaceMainMenuContents(MenuGenerators.createWorldSelectMenu());
}).setOnClickAudio(AssetDataStrings.UI_TONE_BUTTON_TITLE));
//button (multiplayer)
- optionPanel.addChild(Button.createButtonCentered("Multiplayer", () -> {
+ optionPanel.addChild(Button.createButtonCentered("Multiplayer", 2.0f, () -> {
WindowUtils.replaceMainMenuContents(MenuGenerators.createMultiplayerMenu());
}).setOnClickAudio(AssetDataStrings.UI_TONE_BUTTON_TITLE));
//button (static level)
- optionPanel.addChild(Button.createButtonCentered("Level Editor", () -> {
+ optionPanel.addChild(Button.createButtonCentered("Level Editor", 2.0f, () -> {
WindowUtils.replaceMainMenuContents(MenuGeneratorsLevelEditor.createLevelEditorTopMenu());
}).setOnClickAudio(AssetDataStrings.UI_TONE_BUTTON_TITLE));
//button (options)
- optionPanel.addChild(Button.createButtonCentered("Options", () -> {
+ optionPanel.addChild(Button.createButtonCentered("Options", 2.0f, () -> {
WindowUtils.replaceMainMenuContents(MenuGenerators.createOptionsMainMenu());
}).setOnClickAudio(AssetDataStrings.UI_TONE_BUTTON_TITLE));
//button (sp debug)
- optionPanel.addChild(Button.createButtonCentered("Debug SP Quickstart", () -> {
+ optionPanel.addChild(Button.createButtonCentered("Debug SP Quickstart", 2.0f, () -> {
LoadingThread loadingThread = new LoadingThread(LoadingThreadType.DEBUG_RANDOM_SP_WORLD);
Globals.RUN_CLIENT = true;
Globals.RUN_SERVER = true;
@@ -75,7 +75,7 @@ public class MenuGeneratorsTitleMenu {
}).setOnClickAudio(AssetDataStrings.UI_TONE_BUTTON_TITLE));
//button (sp debug)
- optionPanel.addChild(Button.createButtonCentered("Load Test Generation Realm", () -> {
+ optionPanel.addChild(Button.createButtonCentered("Load Test Generation Realm", 2.0f, () -> {
LoadingThread loadingThread = new LoadingThread(LoadingThreadType.CHUNK_GENERATION_REALM);
Globals.RUN_CLIENT = true;
Globals.RUN_SERVER = true;
@@ -83,12 +83,12 @@ public class MenuGeneratorsTitleMenu {
}).setOnClickAudio(AssetDataStrings.UI_TONE_BUTTON_TITLE));
//button (ui testing)
- optionPanel.addChild(Button.createButtonCentered("UI Testing", () -> {
+ optionPanel.addChild(Button.createButtonCentered("UI Testing", 2.0f, () -> {
WindowUtils.replaceMainMenuContents(MenuGeneratorsUITesting.createUITestMenu());
}).setOnClickAudio(AssetDataStrings.UI_TONE_BUTTON_TITLE));
//button (Viewport Test)
- optionPanel.addChild(Button.createButtonCentered("Viewport Test", () -> {
+ optionPanel.addChild(Button.createButtonCentered("Viewport Test", 2.0f, () -> {
Globals.threadManager.start(new LoadingThread(LoadingThreadType.LOAD_VIEWPORT));
}).setOnClickAudio(AssetDataStrings.UI_TONE_BUTTON_TITLE));
diff --git a/src/main/java/electrosphere/renderer/RenderUtils.java b/src/main/java/electrosphere/renderer/RenderUtils.java
index 3823f00f..3bd3a836 100644
--- a/src/main/java/electrosphere/renderer/RenderUtils.java
+++ b/src/main/java/electrosphere/renderer/RenderUtils.java
@@ -677,22 +677,22 @@ public class RenderUtils {
//texture coords
FloatBuffer textureArrayBufferData = BufferUtils.createFloatBuffer(12);
textureArrayBufferData.put(0);
- textureArrayBufferData.put(0);
-
- textureArrayBufferData.put(0);
- textureArrayBufferData.put(1);
-
- textureArrayBufferData.put(1);
textureArrayBufferData.put(1);
textureArrayBufferData.put(0);
textureArrayBufferData.put(0);
textureArrayBufferData.put(1);
+ textureArrayBufferData.put(0);
+
+ textureArrayBufferData.put(0);
textureArrayBufferData.put(1);
textureArrayBufferData.put(1);
textureArrayBufferData.put(0);
+
+ textureArrayBufferData.put(1);
+ textureArrayBufferData.put(1);
textureArrayBufferData.flip();
diff --git a/src/main/java/electrosphere/renderer/texture/Texture.java b/src/main/java/electrosphere/renderer/texture/Texture.java
index fc53d6ad..bc37eee3 100644
--- a/src/main/java/electrosphere/renderer/texture/Texture.java
+++ b/src/main/java/electrosphere/renderer/texture/Texture.java
@@ -275,6 +275,44 @@ public class Texture {
}
}
}
+
+ /**
+ * Generates a texture based on a buffer (for use passing data to gpu)
+ * @param openGlState The OpenGL state
+ * @param buffer The buffer of data
+ * @param width the 'width' of the 'texture'
+ * @param height the 'height' of the 'texture'
+ */
+ public static Texture createBitmap(OpenGLState openGlState, ByteBuffer buffer, int width, int height){
+ Texture rVal = null;
+ if(!Globals.HEADLESS){
+ rVal = new Texture();
+ //generate the texture object on gpu
+ rVal.texturePointer = GL40.glGenTextures();
+ Globals.renderingEngine.checkError();
+ //bind the new texture
+ openGlState.glBindTexture(GL_TEXTURE_2D, rVal.getTexturePointer());
+ //how are we gonna wrap the texture??
+ rVal.setWrap(openGlState, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ rVal.setWrap(openGlState, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ //disable mipmap
+ rVal.setMinFilter(openGlState, GL_LINEAR);
+ rVal.setMagFilter(openGlState, GL_LINEAR);
+ //call if width != height so opengl figures out how to unpack it properly
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+ //GL_RED = 32bit r value
+ //buffer the texture information
+ rVal.pixelFormat = GL_RED;
+ rVal.datatype = GL_FLOAT;
+ rVal.glTexImage2D(openGlState, GL40.GL_RED, width, height, GL40.GL_RED, GL40.GL_UNSIGNED_BYTE, buffer);
+ //check build status
+ String errorMessage = RenderingEngine.getErrorInEnglish(Globals.renderingEngine.getError());
+ if(errorMessage != null){
+ LoggerInterface.loggerRenderer.ERROR(new IllegalStateException("Texture Constructor[from bytebuffer]: " + errorMessage));
+ }
+ }
+ return rVal;
+ }
/**
* Binds the texture to unit 0
diff --git a/src/main/java/electrosphere/renderer/ui/elements/BitmapCharacter.java b/src/main/java/electrosphere/renderer/ui/elements/BitmapCharacter.java
index b001a636..891174ec 100644
--- a/src/main/java/electrosphere/renderer/ui/elements/BitmapCharacter.java
+++ b/src/main/java/electrosphere/renderer/ui/elements/BitmapCharacter.java
@@ -10,6 +10,7 @@ import electrosphere.renderer.model.Model;
import electrosphere.renderer.ui.elementtypes.DrawableElement;
import electrosphere.renderer.ui.events.Event;
import electrosphere.renderer.ui.font.Font;
+
import org.joml.Vector3f;
/**
@@ -22,6 +23,8 @@ public class BitmapCharacter extends StandardElement implements DrawableElement
Vector3f color = new Vector3f(1.0f);
Font font;
+
+ float fontSize = 1.0f;
/**
@@ -33,12 +36,13 @@ public class BitmapCharacter extends StandardElement implements DrawableElement
* @param height
* @param toDraw
*/
- public BitmapCharacter(Font font, int width, int height, char toDraw){
+ public BitmapCharacter(Font font, int width, int height, float fontSize, char toDraw){
super();
setWidth(width);
setHeight(height);
this.text = "" + toDraw;
this.font = font;
+ this.fontSize = fontSize;
}
/**
@@ -52,7 +56,7 @@ public class BitmapCharacter extends StandardElement implements DrawableElement
this.font = font;
Vector3f discreteDims = this.font.getDimensionOfCharacterDiscrete(toDraw);
setMinWidth((int)discreteDims.x);
- setMinHeight((int)discreteDims.y);
+ setMinHeight((int)Math.max(discreteDims.y,this.font.getFontHeight()));
}
@@ -81,20 +85,14 @@ public class BitmapCharacter extends StandardElement implements DrawableElement
framebuffer.bind(openGLState);
openGLState.glViewport(framebuffer.getWidth(), framebuffer.getHeight());
float ndcX = (float)this.absoluteToFramebuffer(getAbsoluteX(),framebufferPosX)/framebuffer.getWidth();
- float ndcY = (float)this.absoluteToFramebuffer(getAbsoluteY(),framebufferPosY)/framebuffer.getHeight();
+ float ndcY = (float)this.absoluteToFramebuffer(getAbsoluteYPlacement(),framebufferPosY)/framebuffer.getHeight();
float ndcWidth = (float)getWidth()/framebuffer.getWidth();
- float ndcHeight = (float)getHeight()/framebuffer.getHeight();
-// float charWidth = ndcWidth/cols;
-// float charHeight = ndcHeight/rows;
+ float ndcHeight = (float)getHeightPlacement()/framebuffer.getHeight();
char toDraw = text.charAt(0);
Vector3f characterPosition = new Vector3f(ndcX,ndcY,0);
Vector3f characterDimensions = new Vector3f(ndcWidth,ndcHeight,0);
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();
@@ -109,6 +107,23 @@ public class BitmapCharacter extends StandardElement implements DrawableElement
}
}
+ /**
+ * Gets the absolute y to use for placement
+ * @return The absolute y to use for placement
+ */
+ public int getAbsoluteYPlacement(){
+ return super.getAbsoluteY() + (int)Math.ceil(this.font.getOffsetY(text.charAt(0)) * this.fontSize);
+ }
+
+
+ /**
+ * Gets the height to use for placement
+ * @return The height to use for placement
+ */
+ public int getHeightPlacement(){
+ return (int)(Math.ceil(super.getHeight() * this.font.getQuadScalingY(text.charAt(0))));
+ }
+
public boolean handleEvent(Event event){
return true;
}
diff --git a/src/main/java/electrosphere/renderer/ui/elements/Button.java b/src/main/java/electrosphere/renderer/ui/elements/Button.java
index fbf430c9..094216ec 100644
--- a/src/main/java/electrosphere/renderer/ui/elements/Button.java
+++ b/src/main/java/electrosphere/renderer/ui/elements/Button.java
@@ -119,6 +119,29 @@ public class Button extends StandardContainerElement implements DrawableElement,
return rVal;
}
+ /**
+ * Creates a button that fires a callback when clicked
+ * @param text The text for the button label
+ * @param fontSize The size of the font for the label
+ * @param callback The callback
+ * @return The button
+ */
+ public static Button createButtonCentered(String text, float fontSize, Runnable callback){
+ Button rVal = new Button();
+ Label rValLabel = Label.createLabel(text, fontSize);
+ rValLabel.setText(text);
+ rVal.addChild(rValLabel);
+ rVal.setOnClick(new ClickableElement.ClickEventCallback(){public boolean execute(ClickEvent event){
+ callback.run();
+ if(Globals.virtualAudioSourceManager != null && rVal.audioPathOnClick != null){
+ Globals.virtualAudioSourceManager.createUI(rVal.audioPathOnClick);
+ }
+ return false;
+ }});
+ rVal.setAlignSelf(YogaAlignment.Center);
+ return rVal;
+ }
+
public boolean getVisible() {
return visible;
}
diff --git a/src/main/java/electrosphere/renderer/ui/elements/Label.java b/src/main/java/electrosphere/renderer/ui/elements/Label.java
index 9ecb4da1..f3f77e16 100644
--- a/src/main/java/electrosphere/renderer/ui/elements/Label.java
+++ b/src/main/java/electrosphere/renderer/ui/elements/Label.java
@@ -43,6 +43,18 @@ public class Label extends StandardContainerElement implements DrawableElement {
return rVal;
}
+ /**
+ * Creates a label element
+ * @param text the text for the label
+ * @param fontSize The size of the font
+ * @return the label element
+ */
+ public static Label createLabel(String text, float fontSize){
+ Label rVal = new Label(fontSize);
+ rVal.setText(text);
+ return rVal;
+ }
+
/**
* Simplified constructor
* @param fontSize the size of the font (default is 1.0f)
@@ -65,13 +77,23 @@ public class Label extends StandardContainerElement implements DrawableElement {
for(int i = 0; i < text.length(); i++){
char toDraw = text.charAt(i);
Vector3f bitMapDimension = this.font.getDimensionOfCharacterDiscrete(toDraw);
- BitmapCharacter newLetter = new BitmapCharacter(this.font,(int)(bitMapDimension.x * fontSize), this.getHeight(), toDraw);
+ BitmapCharacter newLetter = new BitmapCharacter(this.font,(int)(bitMapDimension.x * fontSize), this.getHeight(), fontSize, toDraw);
accumulatingWidth += bitMapDimension.x * fontSize;
childList.add(newLetter);
Yoga.YGNodeInsertChild(yogaNode, newLetter.getYogaNode(), childList.size() - 1);
}
Yoga.YGNodeStyleSetWidth(yogaNode, accumulatingWidth);
}
+
+ /**
+ * Sets the size of the font for this label
+ * @param fontSize The size of the font
+ */
+ public void setFontSize(float fontSize){
+ this.fontSize = fontSize;
+ this.setHeight((int)(font.getFontHeight() * fontSize));
+ this.generateLetters();
+ }
public void setText(String text){
this.text = text;
diff --git a/src/main/java/electrosphere/renderer/ui/elements/StringCarousel.java b/src/main/java/electrosphere/renderer/ui/elements/StringCarousel.java
index 16104ebc..73003a4b 100644
--- a/src/main/java/electrosphere/renderer/ui/elements/StringCarousel.java
+++ b/src/main/java/electrosphere/renderer/ui/elements/StringCarousel.java
@@ -58,7 +58,7 @@ public class StringCarousel extends StandardContainerElement implements Drawable
super();
this.font = Globals.fontManager.getFont("default");
this.fontSize = fontSize;
- Yoga.YGNodeStyleSetMinHeight(this.yogaNode, font.imageHeight * fontSize);
+ Yoga.YGNodeStyleSetMinHeight(this.yogaNode, font.getFontHeight() * fontSize);
Yoga.YGNodeStyleSetMinWidth(this.yogaNode, 1);
}
@@ -68,7 +68,7 @@ public class StringCarousel extends StandardContainerElement implements Drawable
private StringCarousel(){
super();
this.font = Globals.fontManager.getFont("default");
- Yoga.YGNodeStyleSetMinHeight(this.yogaNode, font.imageHeight * fontSize);
+ Yoga.YGNodeStyleSetMinHeight(this.yogaNode, font.getFontHeight() * fontSize);
Yoga.YGNodeStyleSetMinWidth(this.yogaNode, 1);
}
@@ -120,7 +120,7 @@ public class StringCarousel extends StandardContainerElement implements Drawable
for(int i = 0; i < textCurrent.length(); i++){
char toDraw = textCurrent.charAt(i);
Vector3f bitMapDimension = this.font.getDimensionOfCharacterDiscrete(toDraw);
- BitmapCharacter newLetter = new BitmapCharacter(this.font,(int)(bitMapDimension.x * fontSize), this.getHeight(), toDraw);
+ BitmapCharacter newLetter = new BitmapCharacter(this.font,(int)(bitMapDimension.x * fontSize), this.getHeight(), fontSize, toDraw);
accumulatingWidth += bitMapDimension.x * fontSize;
addChild(newLetter);
}
diff --git a/src/main/java/electrosphere/renderer/ui/elements/TextInput.java b/src/main/java/electrosphere/renderer/ui/elements/TextInput.java
index a56e5bcf..88cf8ac3 100644
--- a/src/main/java/electrosphere/renderer/ui/elements/TextInput.java
+++ b/src/main/java/electrosphere/renderer/ui/elements/TextInput.java
@@ -88,7 +88,7 @@ public class TextInput extends StandardContainerElement implements DrawableEleme
this.color = new Vector3f(1,1,1);
setHeight((int)(font.getFontHeight() * fontSize));
Yoga.YGNodeStyleSetFlexDirection(this.yogaNode, Yoga.YGFlexDirectionRow);
- Yoga.YGNodeStyleSetMinHeight(this.yogaNode, font.imageHeight * fontSize);
+ Yoga.YGNodeStyleSetMinHeight(this.yogaNode, font.getFontHeight() * fontSize);
Yoga.YGNodeStyleSetMinWidth(this.yogaNode, 1);
}
@@ -100,7 +100,7 @@ public class TextInput extends StandardContainerElement implements DrawableEleme
for(int i = 0; i < text.length(); i++){
char toDraw = text.charAt(i);
Vector3f bitMapDimension = this.font.getDimensionOfCharacterDiscrete(toDraw);
- BitmapCharacter newLetter = new BitmapCharacter(this.font,(int)(bitMapDimension.x * fontSize), this.getHeight(), toDraw);
+ BitmapCharacter newLetter = new BitmapCharacter(this.font,(int)(bitMapDimension.x * fontSize), this.getHeight(), fontSize, toDraw);
newLetter.setColor(color);
this.addChild(newLetter);
}
diff --git a/src/main/java/electrosphere/renderer/ui/font/Font.java b/src/main/java/electrosphere/renderer/ui/font/Font.java
index 11c66d09..9d68c631 100644
--- a/src/main/java/electrosphere/renderer/ui/font/Font.java
+++ b/src/main/java/electrosphere/renderer/ui/font/Font.java
@@ -2,6 +2,8 @@ package electrosphere.renderer.ui.font;
import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+
import org.joml.Vector3f;
import electrosphere.renderer.model.Material;
@@ -23,6 +25,8 @@ public class Font {
HashMap positionMap = new HashMap();
//the map of character->dimension of the character in pixels
HashMap dimensionMap = new HashMap();
+
+ Map glyphMap = new HashMap();
/**
* Creates the font object
@@ -53,6 +57,8 @@ public class Font {
public int startY;
public int width;
public int height;
+ public int offsetY = 0;
+ public float quadScalingY = 1;
}
/**
@@ -103,13 +109,45 @@ public class Font {
}
return dimension;
}
+
+ /**
+ * Gets the offset y of the character
+ * @param c The character
+ * @return The offset y
+ */
+ public int getOffsetY(Character c){
+ int rVal = 0;
+ if(glyphMap.containsKey(c)){
+ rVal = glyphMap.get(c).offsetY;
+ }
+ return rVal;
+ }
+
+ /**
+ * Gets the quad y scaling of the character
+ * @param c The character
+ * @return The scaling
+ */
+ public float getQuadScalingY(Character c){
+ float rVal = 1.0f;
+ if(glyphMap.containsKey(c)){
+ rVal = glyphMap.get(c).quadScalingY;
+ }
+ return rVal;
+ }
/**
* Gets the height of the font
* @return the height
*/
public int getFontHeight(){
- return imageHeight;
+ int maxHeight = 0;
+ for(Glyph glyph : this.glyphs){
+ if(glyph.height > maxHeight){
+ maxHeight = glyph.height;
+ }
+ }
+ return maxHeight;
}
@@ -129,6 +167,10 @@ public class Font {
Vector3f dimension = new Vector3f(glyph.width,glyph.height,0);
dimensionMap.put(charVal,dimension);
}
+ //fill glyph map
+ for(Glyph glyph: glyphs){
+ this.glyphMap.put(glyph.symbol.charAt(0),glyph);
+ }
}
/**
diff --git a/src/main/java/electrosphere/renderer/ui/font/FontManager.java b/src/main/java/electrosphere/renderer/ui/font/FontManager.java
index 1fc7f0f4..816538d6 100644
--- a/src/main/java/electrosphere/renderer/ui/font/FontManager.java
+++ b/src/main/java/electrosphere/renderer/ui/font/FontManager.java
@@ -34,22 +34,24 @@ public class FontManager {
* 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(Globals.renderingEngine.getOpenGLState(), font, true);
- fontMap.put("default",defaultFont);
- }
+ // 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(Globals.renderingEngine.getOpenGLState(), font, true);
+ // fontMap.put("default",defaultFont);
+ // }
+ defaultFont = FontUtils.loadTTF(Globals.renderingEngine.getOpenGLState(), "Fonts/Tuffy_Bold.ttf");
+ 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 ad207027..9f0563f7 100644
--- a/src/main/java/electrosphere/renderer/ui/font/FontUtils.java
+++ b/src/main/java/electrosphere/renderer/ui/font/FontUtils.java
@@ -1,10 +1,24 @@
package electrosphere.renderer.ui.font;
+import electrosphere.logger.LoggerInterface;
import electrosphere.renderer.OpenGLState;
import electrosphere.renderer.model.Material;
import electrosphere.renderer.texture.Texture;
+import electrosphere.util.FileUtils;
+
+import org.lwjgl.BufferUtils;
+import org.lwjgl.stb.STBTTAlignedQuad;
+import org.lwjgl.stb.STBTTFontinfo;
+import org.lwjgl.stb.STBTTPackContext;
+import org.lwjgl.stb.STBTTPackedchar;
+import org.lwjgl.stb.STBTruetype;
+import org.lwjgl.system.MemoryStack;
import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
import java.awt.RenderingHints;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
@@ -17,6 +31,29 @@ import java.util.List;
*/
public class FontUtils {
+ public static final int TTF_CHAR_WIDTH = 32;
+ public static final int TTF_CHAR_HEIGHT = 24;
+ static final char FIRST_CHAR_TO_BAKE = 32;
+ static final char LAST_CHAR_TO_BAKE = 127;
+ static final int CHAR_COUNT = LAST_CHAR_TO_BAKE - FIRST_CHAR_TO_BAKE;
+ static final int TTF_BITMAP_WIDTH = 512;
+ static final int TTF_BITMAP_HEIGHT = 512;
+ static final int SPACE_WIDTH = 8;
+
+ /**
+ * Manual offset to align them
+ */
+ static final int MANUAL_OFFSET = -2;
+
+ /**
+ * The factor to adjust the descent by. Just intuiting this from how the data looks, not guaranteed to be correct
+ */
+ static final int DESCENT_DIVISOR = 100;
+
+ static float[] scale = new float[]{
+ TTF_CHAR_HEIGHT,
+ };
+
/**
* Renders a single character to a buffered image
@@ -157,5 +194,136 @@ public class FontUtils {
return rVal;
}
+ /**
+ * Loads the TTF font
+ * @param openGLState The opengl state
+ * @param path The path to the ttf font file
+ * @return The font if it loaded successfully, null otherwise
+ */
+ protected static Font loadTTF(OpenGLState openGLState, String path){
+
+ //used for cosntructing the font object to return
+ List glyphs = new LinkedList();
+ Material uiMat = new Material();
+
+ //read the file
+ ByteBuffer fileContent = null;
+ try {
+ fileContent = FileUtils.getAssetFileAsByteBuffer(path);
+ } catch (IOException e) {
+ LoggerInterface.loggerFileIO.ERROR("Failed to read font file", e);
+ }
+ if(fileContent == null){
+ return null;
+ }
+
+ //load info about the file
+ STBTTFontinfo info = STBTTFontinfo.create();
+ if(!STBTruetype.stbtt_InitFont(info, fileContent)){
+ throw new Error("Failed to init font info!");
+ }
+
+ int descent = 0;
+ try(MemoryStack stack = MemoryStack.stackPush()){
+ IntBuffer ascentBuff = stack.mallocInt(1);
+ IntBuffer descentBuff = stack.mallocInt(1);
+ IntBuffer lineGapBuff = stack.mallocInt(1);
+
+ //get data on the font
+ STBTruetype.stbtt_GetFontVMetrics(info, ascentBuff, descentBuff, lineGapBuff);
+
+ // int ascent = ascentBuff.get(0);
+ descent = descentBuff.get(0);
+ // int lineGap = lineGapBuff.get(0);
+ }
+
+ try(STBTTPackContext packContext = STBTTPackContext.malloc()){
+
+ //get char data
+ STBTTPackedchar.Buffer charData = STBTTPackedchar.malloc(6 * 128);
+ ByteBuffer bakedTextureData = BufferUtils.createByteBuffer(TTF_BITMAP_WIDTH * TTF_BITMAP_HEIGHT);
+ STBTruetype.stbtt_PackBegin(packContext, bakedTextureData, TTF_BITMAP_WIDTH, TTF_BITMAP_HEIGHT, 0, 1);
+
+ //make image
+ for(int i = 0; i < scale.length; i++){
+ int p = (i * 3 + 0) * CHAR_COUNT + FIRST_CHAR_TO_BAKE;
+ charData.limit(p + 95);
+ charData.position(p);
+ STBTruetype.stbtt_PackSetOversampling(packContext, 1, 1);
+ STBTruetype.stbtt_PackFontRange(packContext, fileContent, 0, scale[i], FIRST_CHAR_TO_BAKE, charData);
+
+ p = (i * 3 + 1) * CHAR_COUNT + FIRST_CHAR_TO_BAKE;
+ charData.limit(p + 95);
+ charData.position(p);
+ STBTruetype.stbtt_PackSetOversampling(packContext, 2, 2);
+ STBTruetype.stbtt_PackFontRange(packContext, fileContent, 0, scale[i], FIRST_CHAR_TO_BAKE, charData);
+
+ p = (i * 3 + 2) * CHAR_COUNT + FIRST_CHAR_TO_BAKE;
+ charData.limit(p + 95);
+ charData.position(p);
+ STBTruetype.stbtt_PackSetOversampling(packContext, 3, 1);
+ STBTruetype.stbtt_PackFontRange(packContext, fileContent, 0, scale[i], FIRST_CHAR_TO_BAKE, charData);
+ }
+ charData.clear();
+ STBTruetype.stbtt_PackEnd(packContext);
+
+ //create the bitmap image off of the raw texture data
+ Texture texture = Texture.createBitmap(openGLState, bakedTextureData, TTF_BITMAP_WIDTH, TTF_BITMAP_HEIGHT);
+ uiMat.setTexturePointer(texture.getTexturePointer());
+
+ //parse the glyphs
+ try(MemoryStack stack = MemoryStack.stackPush()){
+ STBTTAlignedQuad quad = STBTTAlignedQuad.malloc(stack);
+ FloatBuffer xPos = stack.floats(0.0f);
+ FloatBuffer yPos = stack.floats(0.0f);
+ for (int i = FIRST_CHAR_TO_BAKE; i < LAST_CHAR_TO_BAKE; i++) {
+ xPos.put(0,0);
+ yPos.put(0,0);
+ if (i == 127) {
+ //skip del character command
+ continue;
+ }
+ STBTruetype.stbtt_GetPackedQuad(charData, TTF_BITMAP_WIDTH, TTF_BITMAP_HEIGHT, i, xPos, yPos, quad, false);
+ // float charX = xPos.get(0);
+ // float charY = yPos.get(0);
+
+ // float x0 = quad.x0();
+ // float x1 = quad.x1();
+ // float y0 = quad.y0();
+ // float y1 = quad.y1();
+ // float s0 = quad.s0();
+ // float s1 = quad.s1();
+ // float t0 = quad.t0();
+ // float t1 = quad.t1();
+
+
+ //create glyph and push it into the array
+ Font.Glyph glyph = new Font.Glyph();
+ glyph.width = (int)((quad.s1() - quad.s0()) * TTF_BITMAP_WIDTH);
+ glyph.height = (int)Math.ceil((quad.t1() - quad.t0()) * TTF_BITMAP_HEIGHT);
+ glyph.startX = (int)(quad.s0() * TTF_BITMAP_WIDTH);
+ glyph.startY = (int)Math.ceil((quad.t0() * TTF_BITMAP_HEIGHT));
+ glyph.offsetY = (int)(TTF_CHAR_HEIGHT + quad.y0()) + (descent / DESCENT_DIVISOR) + MANUAL_OFFSET;
+ glyph.quadScalingY = (quad.y1() - quad.y0()) / (float)TTF_CHAR_HEIGHT;
+ glyph.symbol = (char)i + "";
+ if(i == 32){
+ glyph.width = SPACE_WIDTH;
+ }
+ glyphs.add(glyph);
+
+ // if(i == 110 || i == 100 || i == 76 || i == 82){
+ // System.out.println((char)i + "");
+ // }
+
+ }
+ }
+ }
+
+ //construct final font object and return
+ Font rVal = new Font(uiMat,glyphs,TTF_BITMAP_WIDTH,TTF_BITMAP_HEIGHT);
+ rVal.process();
+
+ return rVal;
+ }
}
diff --git a/src/main/java/electrosphere/util/FileUtils.java b/src/main/java/electrosphere/util/FileUtils.java
index d1bf60e0..eee6622d 100644
--- a/src/main/java/electrosphere/util/FileUtils.java
+++ b/src/main/java/electrosphere/util/FileUtils.java
@@ -21,6 +21,8 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.io.Serializable;
+import java.nio.ByteBuffer;
+import java.nio.channels.SeekableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -32,6 +34,8 @@ import java.util.concurrent.TimeUnit;
import javax.imageio.ImageIO;
+import org.lwjgl.BufferUtils;
+
/**
* Utilities for dealing with files
*/
@@ -186,6 +190,25 @@ public class FileUtils {
return targetFile;
}
+ /**
+ * Gets an assets file as a byte buffer
+ * @param pathName The relative path in the assets folder
+ * @return The byte buffer containing the contents of the asset file
+ * @throws IOException Thrown if there are any io issues
+ */
+ public static ByteBuffer getAssetFileAsByteBuffer(String pathName) throws IOException{
+ String sanitizedFilePath = sanitizeFilePath(pathName);
+ File targetFile = new File("./assets" + sanitizedFilePath);
+ ByteBuffer buff = null;
+ try(SeekableByteChannel byteChannel = Files.newByteChannel(targetFile.toPath())){
+ buff = BufferUtils.createByteBuffer((int)(byteChannel.size() + 1));
+ while(byteChannel.read(buff) != -1){
+ }
+ buff.flip();
+ }
+ return buff;
+ }
+
/**
* Gets a cache file
* @param pathName The relative path in the cache folder
diff --git a/src/test/java/electrosphere/renderer/ui/elements/BitmapCharacterTests.java b/src/test/java/electrosphere/renderer/ui/elements/BitmapCharacterTests.java
index 00c28b13..fa3572a0 100644
--- a/src/test/java/electrosphere/renderer/ui/elements/BitmapCharacterTests.java
+++ b/src/test/java/electrosphere/renderer/ui/elements/BitmapCharacterTests.java
@@ -14,7 +14,7 @@ public class BitmapCharacterTests extends UITestTemplate {
public void test_Create(){
//setup
this.setupBlankView();
- BitmapCharacter el = new BitmapCharacter(Globals.fontManager.getFont("default"), 16, 24, 'A');
+ BitmapCharacter el = new BitmapCharacter(Globals.fontManager.getFont("default"), 16, 24, 1.0f, 'A');
WindowUtils.replaceMainMenuContents(el);
diff --git a/test/java/renderer/ui/elements/bitmapchar1.png b/test/java/renderer/ui/elements/bitmapchar1.png
index cea8d0d5..1845c2dd 100644
Binary files a/test/java/renderer/ui/elements/bitmapchar1.png and b/test/java/renderer/ui/elements/bitmapchar1.png differ
diff --git a/test/java/renderer/ui/elements/button1.png b/test/java/renderer/ui/elements/button1.png
index c9ab7b01..933e2fec 100644
Binary files a/test/java/renderer/ui/elements/button1.png and b/test/java/renderer/ui/elements/button1.png differ
diff --git a/test/java/renderer/ui/elements/label1.png b/test/java/renderer/ui/elements/label1.png
index b0a8fc8a..a3895c22 100644
Binary files a/test/java/renderer/ui/elements/label1.png and b/test/java/renderer/ui/elements/label1.png differ
diff --git a/test/java/renderer/ui/elements/stringcarousel1.png b/test/java/renderer/ui/elements/stringcarousel1.png
index 2f2c72a6..eaee23ea 100644
Binary files a/test/java/renderer/ui/elements/stringcarousel1.png and b/test/java/renderer/ui/elements/stringcarousel1.png differ
diff --git a/test/java/renderer/ui/elements/textbox1.png b/test/java/renderer/ui/elements/textbox1.png
index 0c54636d..41a86053 100644
Binary files a/test/java/renderer/ui/elements/textbox1.png and b/test/java/renderer/ui/elements/textbox1.png differ
diff --git a/test/java/renderer/ui/elements/textinput1.png b/test/java/renderer/ui/elements/textinput1.png
index ae6d9f86..458f5507 100644
Binary files a/test/java/renderer/ui/elements/textinput1.png and b/test/java/renderer/ui/elements/textinput1.png differ
diff --git a/test/java/renderer/ui/elements/word1.png b/test/java/renderer/ui/elements/word1.png
index 6d7b716a..387a6f91 100644
Binary files a/test/java/renderer/ui/elements/word1.png and b/test/java/renderer/ui/elements/word1.png differ
diff --git a/test/java/renderer/ui/test_Screencapture_Match.png b/test/java/renderer/ui/test_Screencapture_Match.png
index 04b65f77..9ef61b5e 100644
Binary files a/test/java/renderer/ui/test_Screencapture_Match.png and b/test/java/renderer/ui/test_Screencapture_Match.png differ