I'm trying to always show the front of a 3D font to the user. I tried rotating the font when rotating the camera, but never could get it to work.
I currently have this:
I'm trying to do this (font always faces front):
TrueTypeFont.java
package com.displee.render.font;
import lombok.AllArgsConstructor;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/**
* A TrueType font implementation originally for Slick, edited for Bobjob's Engine
* #original author James Chambers (Jimmy)
* #original author Jeremy Adams (elias4444)
* #original author Kevin Glass (kevglass)
* #original author Peter Korzuszek (genail)
* #new version edited by David Aaron Muhar (bobjob)
*/
public class TrueTypeFont {
public final static int ALIGN_LEFT = 0, ALIGN_RIGHT = 1, ALIGN_CENTER = 2;
/**
* Array that holds necessary information about the font characters
*/
private IntObject[] charArray = new IntObject[256];
/**
* Map of user defined font characters (Character <-> IntObject)
*/
private Map customChars = new HashMap();
/**
* Boolean flag on whether AntiAliasing is enabled or not
*/
private boolean antiAlias;
/**
* Font's size
*/
private int fontSize = 0;
/**
* Font's height
*/
private int fontHeight = 0;
/**
* Texture used to cache the font 0-255 characters
*/
private int fontTextureID;
/**
* Default font texture width
*/
private int textureWidth = 512;
/**
* Default font texture height
*/
private int textureHeight = 512;
/**
* A reference to Java's AWT Font that we create our font texture from
*/
private Font font;
/**
* The font metrics for our Java AWT font
*/
private FontMetrics fontMetrics;
private int correctL = 9, correctR = 8;
private class IntObject {
/**
* Character's width
*/
public int width;
/**
* Character's height
*/
public int height;
/**
* Character's stored x position
*/
public int storedX;
/**
* Character's stored y position
*/
public int storedY;
}
public TrueTypeFont(Font font, boolean antiAlias, char[] additionalChars) {
this.font = font;
this.fontSize = font.getSize() + 3;
this.antiAlias = antiAlias;
createSet(additionalChars);
fontHeight -= 1;
if (fontHeight <= 0) {
fontHeight = 1;
}
}
public TrueTypeFont(Font font, boolean antiAlias) {
this(font, antiAlias, null);
}
public void setCorrection(boolean on) {
if (on) {
correctL = 2;
correctR = 1;
} else {
correctL = 0;
correctR = 0;
}
}
private BufferedImage getFontImage(char ch) {
// Create a temporary image to extract the character's size
BufferedImage tempfontImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = (Graphics2D) tempfontImage.getGraphics();
if (antiAlias == true) {
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
}
g.setFont(font);
fontMetrics = g.getFontMetrics();
int charwidth = fontMetrics.charWidth(ch) + 8;
if (charwidth <= 0) {
charwidth = 7;
}
int charheight = fontMetrics.getHeight() + 3;
if (charheight <= 0) {
charheight = fontSize;
}
// Create another image holding the character we are creating
BufferedImage fontImage;
fontImage = new BufferedImage(charwidth, charheight, BufferedImage.TYPE_INT_ARGB);
Graphics2D gt = (Graphics2D) fontImage.getGraphics();
if (antiAlias == true) {
gt.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
}
gt.setFont(font);
gt.setColor(Color.WHITE);
int charx = 3;
int chary = 1;
gt.drawString(String.valueOf(ch), (charx), (chary) + fontMetrics.getAscent());
//fontImage = ImageUtils.flipVertically(fontImage);
//fontImage = ImageUtils.flipHorizontally(fontImage);
//fontImage = ImageUtils.flipHorizontallyAndVertically(fontImage);
return fontImage;
}
private void createSet(char[] customCharsArray) {
// If there are custom chars then I expand the font texture twice
if (customCharsArray != null && customCharsArray.length > 0) {
textureWidth *= 2;
}
// In any case this should be done in other way. Texture with size 512x512
// can maintain only 256 characters with resolution of 32x32. The texture
// size should be calculated dynamicaly by looking at character sizes.
try {
BufferedImage imgTemp = new BufferedImage(textureWidth, textureHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = (Graphics2D) imgTemp.getGraphics();
g.setColor(new Color(0, 0, 0, 1));
g.fillRect(0, 0, textureWidth, textureHeight);
int rowHeight = 0;
int positionX = 0;
int positionY = 0;
int customCharsLength = (customCharsArray != null) ? customCharsArray.length : 0;
for (int i = 0; i < 256 + customCharsLength; i++) {
// get 0-255 characters and then custom characters
char ch = (i < 256) ? (char) i : customCharsArray[i - 256];
BufferedImage fontImage = getFontImage(ch);
IntObject newIntObject = new IntObject();
newIntObject.width = fontImage.getWidth();
newIntObject.height = fontImage.getHeight();
if (positionX + newIntObject.width >= textureWidth) {
positionX = 0;
positionY += rowHeight;
rowHeight = 0;
}
newIntObject.storedX = positionX;
newIntObject.storedY = positionY;
if (newIntObject.height > fontHeight) {
fontHeight = newIntObject.height;
}
if (newIntObject.height > rowHeight) {
rowHeight = newIntObject.height;
}
// Draw it here
g.drawImage(fontImage, positionX, positionY, null);
positionX += newIntObject.width;
if (i < 256) { // standard characters
charArray[i] = newIntObject;
} else { // custom characters
customChars.put(new Character(ch), newIntObject);
}
fontImage = null;
}
fontTextureID = loadImage(imgTemp);
//.getTexture(font.toString(), imgTemp);
} catch (Exception e) {
System.err.println("Failed to create font.");
e.printStackTrace();
}
}
private void drawQuad(float drawX, float drawY, float drawX2, float drawY2, float srcX, float srcY, float srcX2, float srcY2, float z) {
float DrawWidth = drawX2 - drawX;
float DrawHeight = drawY2 - drawY;
float TextureSrcX = srcX / textureWidth;
float TextureSrcY = srcY / textureHeight;
float SrcWidth = srcX2 - srcX;
float SrcHeight = srcY2 - srcY;
float RenderWidth = (SrcWidth / textureWidth);
float RenderHeight = (SrcHeight / textureHeight);
GL11.glTexCoord2f(TextureSrcX, TextureSrcY);
GL11.glVertex3f(drawX, drawY, z);
GL11.glTexCoord2f(TextureSrcX, TextureSrcY + RenderHeight);
GL11.glVertex3f(drawX, drawY + DrawHeight, z);
GL11.glTexCoord2f(TextureSrcX + RenderWidth, TextureSrcY + RenderHeight);
GL11.glVertex3f(drawX + DrawWidth, drawY + DrawHeight, z);
GL11.glTexCoord2f(TextureSrcX + RenderWidth, TextureSrcY);
GL11.glVertex3f(drawX + DrawWidth, drawY, z);
}
public int getWidth(String whatchars) {
int totalwidth = 0;
IntObject intObject = null;
int currentChar = 0;
for (int i = 0; i < whatchars.length(); i++) {
currentChar = whatchars.charAt(i);
if (currentChar < 256) {
intObject = charArray[currentChar];
} else {
intObject = (IntObject) customChars.get(new Character((char) currentChar));
}
if (intObject != null) {
totalwidth += intObject.width;
}
}
return totalwidth;
}
public int getHeight() {
return fontHeight;
}
public int getHeight(String HeightString) {
return fontHeight;
}
public int getLineHeight() {
return fontHeight;
}
public void drawString(float x, float y, float z, String whatchars, float scaleX, float scaleY) {
drawString(x, y, z, whatchars, 0, whatchars.length() - 1, scaleX, scaleY, ALIGN_LEFT);
}
public void drawString(float x, float y, float z, String whatchars, float scaleX, float scaleY, int format) {
drawString(x, y, z, whatchars, 0, whatchars.length() - 1, scaleX, scaleY, format);
}
public void drawString(float x, float y, float z, String whatchars, int startIndex, int endIndex, float scaleX, float scaleY, int format) {
IntObject intObject = null;
int charCurrent;
int totalwidth = 0;
int i = startIndex, d, c;
float startY = 0;
switch (format) {
case ALIGN_RIGHT: {
d = -1;
c = correctR;
while (i < endIndex) {
if (whatchars.charAt(i) == '\n') {
startY -= fontHeight;
}
i++;
}
break;
}
case ALIGN_CENTER: {
for (int l = startIndex; l <= endIndex; l++) {
charCurrent = whatchars.charAt(l);
if (charCurrent == '\n') {
break;
}
if (charCurrent < 256) {
intObject = charArray[charCurrent];
} else {
intObject = (IntObject) customChars.get(new Character((char) charCurrent));
}
totalwidth += intObject.width - correctL;
}
totalwidth /= -2;
}
case ALIGN_LEFT:
default: {
d = 1;
c = correctL;
break;
}
}
java.util.List<QuadObject> list = new ArrayList<>(endIndex - startIndex);
while (i >= startIndex && i <= endIndex) {
charCurrent = whatchars.charAt(i);
if (charCurrent < 256) {
intObject = charArray[charCurrent];
} else {
intObject = (IntObject) customChars.get(new Character((char) charCurrent));
}
if (intObject != null) {
if (d < 0) {
totalwidth += (intObject.width - c) * d;
}
if (charCurrent == '\n') {
startY -= fontHeight * d;
totalwidth = 0;
if (format == ALIGN_CENTER) {
for (int l = i + 1; l <= endIndex; l++) {
charCurrent = whatchars.charAt(l);
if (charCurrent == '\n') {
break;
}
if (charCurrent < 256) {
intObject = charArray[charCurrent];
} else {
intObject = (IntObject) customChars.get(new Character((char) charCurrent));
}
totalwidth += intObject.width - correctL;
}
totalwidth /= -2;
}
//if center get next lines total width/2;
} else {
QuadObject quad = new QuadObject((totalwidth + intObject.width) * scaleX + x, startY * scaleY + y, totalwidth * scaleX + x, (startY + intObject.height) * scaleY + y, intObject.storedX + intObject.width, intObject.storedY + intObject.height, intObject.storedX, intObject.storedY, z);
list.add(quad);
if (d > 0) {
totalwidth += (intObject.width - c) * d;
}
}
i += d;
}
}
float centerX = 0;
for(QuadObject quad : list) {
centerX += quad.drawX + (quad.drawX2 - quad.drawX);
}
centerX /= 2.0f;
float centerY = 0;
for(QuadObject quad : list) {
centerY += quad.drawY + (quad.drawY2 - quad.drawY);
}
centerY /= 2.0f;
//GL11.glTranslatef( -centerX, -centerY, -z);
//GL11.glTranslatef(0, 0, -z);
GL11.glRotatef(-Test3DFont.rotation.x, 0.0f, 0.0f, 0.0f);
GL11.glRotatef(-Test3DFont.rotation.y, 0.0f, 1.0f, 0.0f);
//GL11.glRotatef(-Test3DFont.rotation.z, 0.0f, 0.0f, 1.0f);
//GL11.glTranslatef(0, 0, z);
//GL11.glTranslatef(centerX, centerY, z); // M1 - 2nd translation
GL11.glEnable(GL11.GL_TEXTURE_2D); // Enable Texture Mapping
GL11.glBindTexture(GL11.GL_TEXTURE_2D, fontTextureID);
GL11.glBegin(GL11.GL_QUADS);
for(QuadObject quad : list) {
drawQuad(quad.drawX, quad.drawY, quad.drawX2, quad.drawY2, quad.srcX, quad.srcY, quad.srcX2, quad.srcY2, quad.z);
}
GL11.glDisable(GL11.GL_TEXTURE_2D);
GL11.glEnd();
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
}
public static int loadImage(BufferedImage bufferedImage) {
try {
short width = (short) bufferedImage.getWidth();
short height = (short) bufferedImage.getHeight();
//textureLoader.bpp = bufferedImage.getColorModel().hasAlpha() ? (byte)32 : (byte)24;
int bpp = (byte) bufferedImage.getColorModel().getPixelSize();
ByteBuffer byteBuffer;
DataBuffer db = bufferedImage.getData().getDataBuffer();
if (db instanceof DataBufferInt) {
int intI[] = ((DataBufferInt) (bufferedImage.getData().getDataBuffer())).getData();
byte newI[] = new byte[intI.length * 4];
for (int i = 0; i < intI.length; i++) {
byte b[] = intToByteArray(intI[i]);
int newIndex = i * 4;
newI[newIndex] = b[1];
newI[newIndex + 1] = b[2];
newI[newIndex + 2] = b[3];
newI[newIndex + 3] = b[0];
}
byteBuffer = ByteBuffer.allocateDirect(width * height * (bpp / 8)).order(ByteOrder.nativeOrder()).put(newI);
} else {
byteBuffer = ByteBuffer.allocateDirect(width * height * (bpp / 8)).order(ByteOrder.nativeOrder()).put(((DataBufferByte) (bufferedImage.getData().getDataBuffer())).getData());
}
byteBuffer.flip();
int internalFormat = GL11.GL_RGBA8, format = GL11.GL_RGBA;
IntBuffer textureId = BufferUtils.createIntBuffer(1);
;
GL11.glGenTextures(textureId);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureId.get(0));
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
GL11.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL11.GL_UNSIGNED_BYTE, byteBuffer.order(ByteOrder.nativeOrder()));
return textureId.get(0);
} catch (Exception e) {
e.printStackTrace();
System.exit(-1);
}
return -1;
}
public static boolean isSupported(String fontname) {
Font font[] = getFonts();
for (int i = font.length - 1; i >= 0; i--) {
if (font[i].getName().equalsIgnoreCase(fontname)) {
return true;
}
}
return false;
}
public static Font[] getFonts() {
return GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
}
public static byte[] intToByteArray(int value) {
return new byte[]{(byte) (value >>> 24), (byte) (value >>> 16), (byte) (value >>> 8), (byte) value};
}
public void destroy() {
IntBuffer scratch = BufferUtils.createIntBuffer(1);
scratch.put(0, fontTextureID);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
GL11.glDeleteTextures(scratch);
}
#AllArgsConstructor
private class QuadObject {
private float drawX;
private float drawY;
private float drawX2;
private float drawY2;
private float srcX;
private float srcY;
private float srcX2;
private float srcY2;
private float z;
}
}
Test3DFont.java
package com.displee.render.font;
import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.glu.GLU;
import org.lwjgl.util.vector.Vector3f;
import java.awt.*;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.util.glu.GLU.gluPerspective;
public class Test3DFont {
private static final int WIDTH = 800;
private static final int HEIGHT = 600;
private static final float FOV = 45f;
private static final float NEAR = 0.1f;
private static final float FAR = 1000f;
private static boolean mousePressed;
private static Vector3f startCoordinations = new Vector3f();
private static float scale = 0.05f;
public static Vector3f rotation = new Vector3f(0, 0, 0);
private static Vector3f startRotation = new Vector3f();
private static TrueTypeFont font;
private static boolean running = true;
public static void main(String[] args) throws Exception {
initializeDisplay();
font = new TrueTypeFont(new Font("serif", Font.PLAIN, 30), true);
initializeGL();
while(running) {
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
handleMouse();
font.drawString(0, 0, 0, "Test", 0.1f, 0.1f);
loadDefaultRotation();
setViewport();
drawGrid();
Display.sync(60);
Display.update();
if (Display.isCloseRequested()) {
break;
}
}
font.destroy();
Display.destroy();
}
private static void initializeDisplay() throws LWJGLException {
Display.setDisplayMode(new DisplayMode(WIDTH, HEIGHT));
Display.create();
setViewport();
}
public static void set2DMode() {
//GL11.glDisable(GL11.GL_DEPTH_TEST);
GL11.glMatrixMode(GL11.GL_PROJECTION); // Select The Projection Matrix
GL11.glPushMatrix(); // Store The Projection Matrix
GL11.glLoadIdentity(); // Reset The Projection Matrix
GL11.glOrtho(0, WIDTH, 0, HEIGHT, -1, 1); // Set Up An Ortho Screen
GL11.glMatrixMode(GL11.GL_MODELVIEW); // Select The Modelview Matrix
GL11.glPushMatrix(); // Store The Modelview Matrix
GL11.glLoadIdentity(); // Reset The Modelview Matrix
}
public static void set3DMode() {
GL11.glMatrixMode(GL11.GL_PROJECTION); // Select The Projection Matrix
GL11.glPopMatrix(); // Restore The Old Projection Matrix
GL11.glMatrixMode(GL11.GL_MODELVIEW); // Select The Modelview Matrix
GL11.glPopMatrix(); // Restore The Old Projection Matrix
//GL11.glEnable(GL11.GL_DEPTH_TEST);
}
private static void setViewport() {
glViewport(0, 0, WIDTH, HEIGHT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(FOV, (float) WIDTH / (float) HEIGHT, NEAR, FAR);
glMatrixMode(GL_MODELVIEW);
}
private static void initializeGL() {
glShadeModel(GL_SMOOTH);
glEnable(GL_NORMALIZE);
glEnable(GL_BLEND);
glCullFace(GL_BACK);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
private static void handleMouse() {
scale += Mouse.getDWheel() > 0 ? 0.005f : Mouse.getDWheel() < 0 ? -0.005f : 0;
int x = Mouse.getY();
int y = Mouse.getX();
if (!mousePressed) {
mousePressed = Mouse.isButtonDown(0);
if (mousePressed) {
startCoordinations.set((float) x, (float) y, 0.0f);
startRotation = new Vector3f(rotation);
}
} else if (!Mouse.isButtonDown(0)) {
mousePressed = false;
}
if (!mousePressed) {
return;
}
float differenceX = x - startCoordinations.x;
float differenceY = y - startCoordinations.y;
rotation.set(startRotation.x - (differenceX * 0.5F), startRotation.y + (differenceY * 0.5F), 0);
}
private static void loadDefaultRotation() {
glLoadIdentity();
Vector3f cameraPosition = new Vector3f();
glTranslatef(cameraPosition.x, cameraPosition.y, -10);
glRotatef(rotation.x, 1.0F, 0.0F, 0.0F);
glRotatef(rotation.y, 0.0F, 1.0F, 0.0F);
glRotatef(rotation.z, 0.0F, 0.0F, 1.0F);
glScalef(scale, scale, scale);
}
private static void drawGrid() {
glColor4f(0.7176471f, 0.7176471f, 0.7176471f, 1.0f);
glBegin(GL_LINES);
float size = 50;
float step = 10;
for (float i = -size; i <= size; i += step) {
glVertex3f(i, 0, size);
glVertex3f(i, 0, -size);
glVertex3f(size, 0, i);
glVertex3f(-size, 0, i);
}
glEnd();
}
public static int[] getScreenCoords(double x, double y, double z) {
FloatBuffer screenCoords = BufferUtils.createFloatBuffer(4);
IntBuffer viewport = BufferUtils.createIntBuffer(16);
FloatBuffer modelView = BufferUtils.createFloatBuffer(16);
FloatBuffer projection = BufferUtils.createFloatBuffer(16);
GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, modelView);
GL11.glGetFloat(GL11.GL_PROJECTION_MATRIX, projection);
GL11.glGetInteger(GL11.GL_VIEWPORT, viewport);
boolean result = GLU.gluProject((float) x, (float) y, (float) z, modelView, projection, viewport, screenCoords);
if (result) {
return new int[] { (int) screenCoords.get(0), (int) screenCoords.get(1) };
}
return null;
}
}
Can anyone help me with this? How can I make it so I always see the front of the font?
Update 1:
I've got it almost working by rotating the quads. I've added the following code in the drawString method before enabling texture 2D:
GL11.glRotatef(-Test3DFont.rotation.x, 0.0f, 0.0f, 0.0f);
GL11.glRotatef(-Test3DFont.rotation.y, 0.0f, 1.0f, 0.0f);
I've updated the code. It currently looks like this:
I finally fixed it by pushing a matrix and using the original x, y and z coords in the translation. I also had to subtract 180 from the rotation because that's my starting rotation. Ended up with the following code:
GL11.glPushMatrix();
GL11.glTranslatef(x, y, z);
GL11.glRotatef(180 - rotation.y, 0.0f, 1.0f, 0.0f);
GL11.glTranslatef(-x, -y, -z);
GL11.glEnable(GL11.GL_TEXTURE_2D); // Enable Texture Mapping
GL11.glBindTexture(GL11.GL_TEXTURE_2D, fontTextureID);
GL11.glBegin(GL11.GL_QUADS);
for(QuadObject quad : list) {
drawQuad(quad.drawX, quad.drawY, quad.drawX2, quad.drawY2, quad.srcX, quad.srcY, quad.srcX2, quad.srcY2, quad.z);
}
GL11.glDisable(GL11.GL_TEXTURE_2D);
GL11.glEnd();
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
GL11.glPopMatrix();
I have tried to follow the Wikipedia page on 3d projection to draw my own 3d line using java and AWT with Swing.
The output file that is created as a result of the rendering doesn't contain anything.
What have I done wrong?
Why isn't this producing an image file that has points on it?
Are my formulas off?
I was following this Wikipedia page as my reference: https://en.wikipedia.org/wiki/3D_projection#Perspective_projection
I have a set of points which are generated using a loop.
I then calculate the position of the 3d coordinate to 2d without using matrices, like one of the suggested method in the link.
Have I miss-interpreted something or not implemented something?
Any help and feedback are welcome.
I known nothing is drawn to the window. That isn't a priority at the moment. The priority it's getting the algorithms to work.
public class Window {
JFrame f = new JFrame();
public Window() {
JPanel p = new JPanel();
render();
f.add(p);
f.setSize(500, 500);
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
Window w = new Window();
}
void render() {
BufferedImage image = new BufferedImage(300, 300, BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
g.setColor(Color.red);
for (int i = 0; i < 10; i++) {
Vector3 point = new Vector3(1 + i, 2, 1 + i);
int x = calculateBX(point, new Vector3(0, 0, 0));
int y = calculateBY(point, new Vector3(1, 1, 1));
g.drawLine(x, y, x, y);
}
try {
File outputfile = new File("saved.png");
ImageIO.write(image, "png", outputfile);
} catch (IOException e) {
e.printStackTrace();
}
}
double calculatDX(Vector3 v, Vector3 camera) {
double cx = Math.cos(0);
double cy = Math.cos(0);
double cz = Math.cos(0);
double sx = Math.sin(0);
double sy = Math.sin(0);
double sz = Math.sin(0);
int x = v.getX() - camera.getX();
int y = v.getY() - camera.getY();
int z = v.getZ() - camera.getZ();
double dx = cy * ((sz) * (y) + (cz) * (x)) - (sy) * (z);
return dx;
}
double calculatDY(Vector3 v, Vector3 camera) {
double cx = Math.cos(0);
double cy = Math.cos(0);
double cz = Math.cos(0);
double sx = Math.sin(0);
double sy = Math.sin(0);
double sz = Math.sin(0);
int x = v.getX() - camera.getX();
int y = v.getY() - camera.getY();
int z = v.getZ() - camera.getZ();
double dy = sx * ((cy) * (z) + (sy) * ((sz) * (y) + (cz) * (x))) + (cx) * (cz * (y) - sz * x);
return dy;
}
double calculatDZ(Vector3 v, Vector3 camera) {
double cx = Math.cos(0);
double cy = Math.cos(0);
double cz = Math.cos(0);
double sx = Math.sin(0);
double sy = Math.sin(0);
double sz = Math.sin(0);
int x = v.getX() - camera.getX();
int y = v.getY() - camera.getY();
int z = v.getZ() - camera.getZ();
double dz = cx * ((cy) * (z) + (sy) * ((sz) * (y) + (cz) * (x))) - (sx) * (cz * (y) - sz * x);
return dz;
}
int calculateBX(Vector3 v, Vector3 camera) {
int ez = camera.getZ();
int ex = camera.getX();
double dz = calculatDZ(v, camera);
double dx = calculatDX(v, camera);
return (int) ((ez / dz) * dx) - ex;
}
int calculateBY(Vector3 v, Vector3 camera) {
int ez = camera.getZ();
int ey = camera.getY();
double dz = calculatDZ(v, camera);
double dy = calculatDY(v, camera);
return (int) ((ez / dz) * dy) - ey;
}
}
Vector Class
public class Vector3 {
private int x, y, z;
public Vector3(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getZ() {
return z;
}
}
I'm trying to draw a circle in LWJGL, but when I draw I try to draw it, it makes a shape that's more like an oval rather than a circle. Also, when I change my circleVertexCount 350+, the shape like flips out. I'm really not sure how the code works that creates the vertices(I have taken Geometry and I know the basic trig ratios). I haven't really found that good of tutorials on creating circles. Here's my code:
public class Circles {
// Setup variables
private int WIDTH = 800;
private int HEIGHT = 600;
private String title = "Circle";
private float fXOffset;
private int vbo = 0;
private int vao = 0;
int circleVertexCount = 300;
float[] vertexData = new float[(circleVertexCount + 1) * 4];
public Circles() {
setupOpenGL();
setupQuad();
while (!Display.isCloseRequested()) {
loop();
adjustVertexData();
Display.update();
Display.sync(60);
}
Display.destroy();
}
public void setupOpenGL() {
try {
Display.setDisplayMode(new DisplayMode(WIDTH, HEIGHT));
Display.setTitle(title);
Display.create();
} catch (LWJGLException e) {
e.printStackTrace();
System.exit(-1);
}
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
}
public void setupQuad() {
float r = 0.1f;
float x;
float y;
float offSetX = 0f;
float offSetY = 0f;
double theta = 2.0 * Math.PI;
vertexData[0] = (float) Math.sin(theta / circleVertexCount) * r + offSetX;
vertexData[1] = (float) Math.cos(theta / circleVertexCount) * r + offSetY;
for (int i = 2; i < 400; i += 2) {
double angle = theta * i / circleVertexCount;
x = (float) Math.cos(angle) * r;
vertexData[i] = x + offSetX;
}
for (int i = 3; i < 404; i += 2) {
double angle = Math.PI * 2 * i / circleVertexCount;
y = (float) Math.sin(angle) * r;
vertexData[i] = y + offSetY;
}
FloatBuffer vertexBuffer = BufferUtils.createFloatBuffer(vertexData.length);
vertexBuffer.put(vertexData);
vertexBuffer.flip();
vao = glGenVertexArrays();
glBindVertexArray(vao);
vbo = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER,vertexBuffer, GL_STATIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
public void loop() {
glClear(GL_COLOR_BUFFER_BIT);
glBindVertexArray(vao);
glEnableVertexAttribArray(0);
glDrawArrays(GL_TRIANGLE_FAN, 0, vertexData.length / 2);
glDisableVertexAttribArray(0);
glBindVertexArray(0);
}
public static void main(String[] args) {
new Circles();
}
private void adjustVertexData() {
float newData[] = new float[vertexData.length];
System.arraycopy(vertexData, 0, newData, 0, vertexData.length);
if(Keyboard.isKeyDown(Keyboard.KEY_W)) {
fXOffset += 0.05f;
} else if(Keyboard.isKeyDown(Keyboard.KEY_S)) {
fXOffset -= 0.05f;
}
for(int i = 0; i < vertexData.length; i += 2) {
newData[i] += fXOffset;
}
FloatBuffer newDataBuffer = BufferUtils.createFloatBuffer(newData.length);
newDataBuffer.put(newData);
newDataBuffer.flip();
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferSubData(GL_ARRAY_BUFFER, 0, newDataBuffer);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
}
300 Vertex Count(This is my main problem)
400 Vertex Count - I removed this image, it's bugged out, should be a tiny sliver cut out from the right, like a secant
500 Vertex Count
Each 100, it removes more and more of the circle, and so on.
One of your problems is this:
for (int i = 2; i < 400; i += 2) {
double angle = theta * i / circleVertexCount;
x = (float) Math.cos(angle) * r;
vertexData[i] = x + offSetX;
}
for (int i = 3; i < 404; i += 2) {
double angle = Math.PI * 2 * i / circleVertexCount;
y = (float) Math.sin(angle) * r;
vertexData[i] = y + offSetY;
}
You are using a different value for angle for the x and y position of each vertex.
You could try this instead:
for (int i = 0; i <= circleVertexCount; i++) {
double angle = i * theta / circleVertexCount;
x = (float) Math.cos(angle) * r;
y = (float) Math.sin(angle) * r;
vertexData[i * 2] = x + offSetX;
vertexData[i * 2 + 1] = y + offSetY;
}
The reason part of your circle was being cut out at higher vertex counts was the i < 400 in your for loops, so I have changed it to i <= circleVertexCount.
Another problem is that your window is not square, and you are not using a shader (or the deprecated built in matrices) to correct this. This means that one unit up looks a different length than one unit right, resulting in an oval instead of a circle. To fix this you could multiply your vertex x position by your display height divided by your display width, preferably in a shader.
I have a png font, and I can correctly get the row/ column of each letter the problem is what to with that information. Each character is 8 by 8. Here is the code I have so far.
If anyone could give me some help or even just some advice it would be greatly appreciated.
private double xsize, ysize, x, y, rotate, xh, yh;
private int lxpos, lypos;
private Texture texture;
String text;
public TextRenderer(int fontsize, int xlocation, int ylocation,
int rotates, String path, String Text) {
text = Text;
rotate = rotates;
setXsize(fontsize);
ysize = fontsize;
x = xlocation;
y = ylocation;
setXh(getXsize() / 2);
yh = ysize / 2;
try {
texture = TextureLoader.getTexture(".PNG", new FileInputStream(
new File(path)));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
texture.bind();
update();
}
public void update() {
char[] c = text.toCharArray();
int letter;
// 16 collumns 16 rows
for (int i = 0; i < c.length; i++) {
letter = c[i];
System.out.println(c[i]);
lypos = letter % 16;
lypos++;
System.out.println("Collumn :" + lypos);
lxpos = letter / 16;
System.out.println("Row :" + lxpos);
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glPushMatrix();
GL11.glTranslated(x, y, 0);
GL11.glTranslatef(10.0f, 10.5f, -0.0f); // back to previous position
GL11.glRotated(rotate, 0.0f, 0.0f, -1.0f); // rotate
GL11.glTranslatef(-10.0f, -10.5f, 0.0f); // to the origin
GL11.glTranslated(-x, -y, 0);
// above is for movement/rotation of text
// 128 px total
lxpos = lxpos * 8;
lxpos = lxpos * 8;
int startx = lxpos * 8;
int starty = lypos * 8;
double endx = startx + 8;
double endy = starty + 8;
GL11.glBegin(GL11.GL_QUADS);
GL11.glTexCoord2d(startx, starty);
GL11.glVertex2d(x - getXh(), y - yh);
GL11.glTexCoord2d(startx, endy);
GL11.glVertex2d(x - getXh(), y + yh);
GL11.glTexCoord2d(endx, endy);
GL11.glVertex2d(x + getXh(), y + yh);
GL11.glTexCoord2d(endx, starty);
GL11.glVertex2d(x + getXh(), y - yh);
GL11.glEnd();
GL11.glPopMatrix();
GL11.glDisable(GL11.GL_TEXTURE_2D);
Here is what I did:
public class Fsc {
public static Fsc cA = new Fsc((XCOORDINSHEET), (XCOORDINSHEET), (WIDTHOFCHAR), (HEIGHTOFCHAR));
etc...
public int ix, iy, width, height;
public Fsc(int ixa, int iya, int widtha, int heighta) {
ix = ixa;
iy = iya;
width = widtha;
height = heighta;
}
public void render(int xa, int ya, float scale) {
int x2 = ix + width;
int y2 = iy + height;
glBegin(GL_QUADS);
glTexCoord2f(ix, iy);
glVertex2f(xa, ya);
glTexCoord2f(x2, iy);
glVertex2f(xa + width * scale, ya);
glTexCoord2f(x2, y2);
glVertex2f(xa + width * scale, ya + height * scale);
glTexCoord2f(ix, y2);
glVertex2f(xa, ya + height * scale);
glEnd();
}
}
public class Fontsheet {
public int sheet;
public Fontsheet(String location) {
sheet = ImagingTools.glLoadTextureLinear(location);
}
public void render(int x, int y, float scale, String text) {
glEnable(GL_TEXTURE_RECTANGLE_ARB);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, sheet);
float lwidth = 0;
int x2 = x;
for(int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
x2 += lwidth;
switch(c) {
case 'A':
Fsc.cA.render(x2, y2, scale);lwidth = Fsc.cA.width * scale;
break;
case 'B': etc...
}
}
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
glDisable(GL_TEXTURE_RECTANGLE_ARB);
}
}
Sorry for it not being formatted. I know for a fact that it works because I use it actively. I may have forgot a bit of code when copying it over. If it doesn't work for you, just tell me.
Hope it helps you.