font loading update
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit
@ -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);
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
4
pom.xml
@ -7,8 +7,8 @@
|
||||
<packaging>jar</packaging>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<lwjgl.version>3.3.3</lwjgl.version>
|
||||
<joml.version>1.9.19</joml.version>
|
||||
<recast.version>1.5.7</recast.version>
|
||||
|
||||
@ -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));
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
|
||||
@ -276,6 +276,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
|
||||
* @param openGLState The opengl state
|
||||
|
||||
@ -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;
|
||||
|
||||
/**
|
||||
@ -23,6 +24,8 @@ public class BitmapCharacter extends StandardElement implements DrawableElement
|
||||
|
||||
Font font;
|
||||
|
||||
float fontSize = 1.0f;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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,7 +77,7 @@ 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);
|
||||
@ -73,6 +85,16 @@ public class Label extends StandardContainerElement implements DrawableElement {
|
||||
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;
|
||||
textPixelWidth = 0;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
@ -24,6 +26,8 @@ public class Font {
|
||||
//the map of character->dimension of the character in pixels
|
||||
HashMap<Character,Vector3f> dimensionMap = new HashMap<Character,Vector3f>();
|
||||
|
||||
Map<Character,Glyph> glyphMap = new HashMap<Character,Glyph>();
|
||||
|
||||
/**
|
||||
* Creates the font object
|
||||
* @param fontMaterial
|
||||
@ -53,6 +57,8 @@ public class Font {
|
||||
public int startY;
|
||||
public int width;
|
||||
public int height;
|
||||
public int offsetY = 0;
|
||||
public float quadScalingY = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -104,12 +110,44 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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<Font.Glyph> glyphs = new LinkedList<Font.Glyph>();
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 70 KiB |