Best way to boost the performance of my LWJGL game? - java
I am putting together a small project for school that involves rendering the periodic table. I chose to use LWJGL to do this. The problem is, however, that when I render the table, the game starts out at ~30fps(capped at 60fps), and quickly fluctuates to a single-digit fps. I believe that the problem could be a memory leak, but I am unsure. Can anybody see any glaring problems with my code? Here are the main classes involved in rendering the table:
EntityPeriodicTable: In charge of holding a huge array of EntityElement objects(see below), activating their logic(tick() and updateInput()).
package com.flafla2.periodicTable;
import org.lwjgl.opengl.GL11;
public class EntityPeriodicTable extends ClickableEntity { //ClickableEntity is an abstract class in charge of the tick(), updateInput(), and render() methods, as well as positioning
public EntityElement[] elements = {//This is unfinished, but you get the idea.
//new EntityElement(Atomic #, State, Metal, "Symbol", "Name", new Vector2D(posx,posy), this)
new EntityElement(1, 2, 2, "H", "Hydrogen", new Vector2D(1,1), this),
new EntityElement(2, 2, 2, "He", "Helium", new Vector2D(18,1), this),
new EntityElement(3, 0, 0, "Li", "Lithium", new Vector2D(1,2), this),
new EntityElement(4, 0, 0, "Be", "Beryllium", new Vector2D(2,2), this),
new EntityElement(5, 0, 1, "B", "Boron", new Vector2D(13,2), this),
new EntityElement(6, 0, 2, "C", "Carbon", new Vector2D(14,2), this),
new EntityElement(7, 2, 2, "N", "Nitrogen", new Vector2D(15,2), this),
new EntityElement(8, 2, 2, "O", "Oxygen", new Vector2D(16,2), this),
new EntityElement(9, 2, 2, "F", "Fluorine", new Vector2D(17,2), this),
new EntityElement(10,2, 2, "Ne", "Neon", new Vector2D(18,2), this),
new EntityElement(11, 0, 0, "Na", "Sodium", new Vector2D(1,3), this),
new EntityElement(12, 0, 0, "Mg", "Magnesium", new Vector2D(2,3), this),
new EntityElement(13, 0, 0, "Al", "Aluminum", new Vector2D(13,3), this),
new EntityElement(14, 0, 1, "Si", "Silicon", new Vector2D(14,3), this),
new EntityElement(15, 0, 2, "P", "Phosphorous", new Vector2D(15,3), this),
new EntityElement(16, 0, 2, "S", "Sulfur", new Vector2D(16,3), this),
new EntityElement(17, 2, 2, "Cl", "Chlorine", new Vector2D(17,3), this),
new EntityElement(18, 2, 2, "Ar", "Argon", new Vector2D(18,3), this),
new EntityElement(19, 0, 0, "K", "Potassium", new Vector2D(1,4), this),
new EntityElement(20, 0, 0, "Ca", "Calcium", new Vector2D(2,4), this),
new EntityElement(21, 0, 0, "Sc", "Scandium", new Vector2D(3,4), this),
new EntityElement(22, 0, 0, "Ti", "Hydrogen", new Vector2D(4,4), this),
new EntityElement(23, 0, 0, "V", "Hydrogen", new Vector2D(5,4), this),
new EntityElement(24, 0, 0, "Cr", "Hydrogen", new Vector2D(6,4), this),
new EntityElement(25, 0, 0, "Mn", "Hydrogen", new Vector2D(7,4), this),
new EntityElement(26, 0, 0, "Fe", "Hydrogen", new Vector2D(8,4), this),
new EntityElement(27, 0, 0, "Co", "Hydrogen", new Vector2D(9,4), this),
new EntityElement(28, 0, 0, "Ni", "Hydrogen", new Vector2D(10,4), this),
new EntityElement(29, 0, 0, "Cu", "Hydrogen", new Vector2D(11,4), this),
new EntityElement(30, 0, 0, "Zn", "Hydrogen", new Vector2D(12,4), this),
new EntityElement(31, 0, 0, "Ga", "Hydrogen", new Vector2D(13,4), this),
new EntityElement(32, 0, 1, "Ge", "Hydrogen", new Vector2D(14,4), this),
new EntityElement(33, 0, 1, "As", "Hydrogen", new Vector2D(15,4), this),
new EntityElement(34, 0, 2, "Se", "Hydrogen", new Vector2D(16,4), this),
new EntityElement(35, 1, 2, "Br", "Hydrogen", new Vector2D(17,4), this),
new EntityElement(36, 2, 2, "Kr", "Hydrogen", new Vector2D(18,4), this),
};
public final int ELEMENT_SIZE = 40;
public Vector2D mousePos = new Vector2D(0,0); //Simple 2D vector struct.
public double[] SOLID_RGB = {0,0,0};
public double[] LIQUID_RGB = {0,0,1};
public double[] GAS_RGB = {1,0,0};
public double[] METAL_RGB;
public double[] NONMETAL_RGB;
public double[] METALLOID_RGB;
public double[] RECENT_RGB;
public EntityPeriodicTable(Vector2D pos) {
this.pos = pos;
METAL_RGB = new double[3];
METAL_RGB[0] = 0.596078431; //152/255
METAL_RGB[1] = 0.984313725; //251/255
METAL_RGB[2] = 0.596078431; //152/255
NONMETAL_RGB = new double[3];
NONMETAL_RGB[0] = 1;
NONMETAL_RGB[1] = 0.647058824; //165/255
NONMETAL_RGB[2] = 0;
METALLOID_RGB = new double[3];
METALLOID_RGB[0] = 0.866666667; //221/255
METALLOID_RGB[1] = 0.62745098; //160/255
METALLOID_RGB[2] = 0.866666667; //221/255
RECENT_RGB = new double[3];
RECENT_RGB[0] = 0.803921569; //205/255
RECENT_RGB[1] = 0.788235294; //201/255
RECENT_RGB[2] = 0.788235294; //201/255
}
#Override
void render() {
GL11.glDisable(GL11.GL_TEXTURE_2D);
GL11.glDisable(GL11.GL_BLEND);
for(int x=0;x<elements.length;x++)
elements[x].render();
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glEnable(GL11.GL_BLEND);
for(int x=0;x<elements.length;x++)
elements[x].renderWithTex();
}
#Override
void tick() {
for(int x=0;x<elements.length;x++)
elements[x].tick();
}
#Override
public void updateInput(Vector2D mousePos)
{
this.mousePos = mousePos;
for(int x=0;x<elements.length;x++)
{
if(mousePos.isInBoundsWithDim(elements[x].pos.x, elements[x].pos.y, elements[x].dim.x, elements[x].dim.y))
elements[x].isSelected = true;
else
elements[x].isSelected = false;
}
}
#Override
void onEntityClicked() {
for(int x=0;x<elements.length;x++)
{
if(mousePos.isInBoundsWithDim(elements[x].pos.x, elements[x].pos.y, elements[x].dim.x, elements[x].dim.y))
elements[x].onEntityClicked();
}
}
}
EntityElement: Holds data of a specific element on the table, and renders it(render code is unfinished)
package com.flafla2.periodicTable;
import org.lwjgl.opengl.GL11;
public class EntityElement extends ClickableEntity {
String symbol;
String element;
int atomicNumber;
EntityPeriodicTable table;
int state;//0=solid, 1=liquid, 2=gas
int metalState;//0=metal, 1=metalloid, 2=nonmetal, 3=discovered recently
Vector2D gridPos;
public EntityElement(int an, int st, int ms, String sy, String en, Vector2D gp, EntityPeriodicTable pt)
{
symbol = sy;
element = en;
atomicNumber = an;
table = pt;
state = st;
metalState = ms;
gridPos = gp;
dim.x = table.ELEMENT_SIZE; dim.y = table.ELEMENT_SIZE;
pos.x = table.pos.x + table.ELEMENT_SIZE*(gridPos.x-1); pos.y = table.pos.y + table.ELEMENT_SIZE*(gridPos.y-1);
}
public double[] getStateColor()
{
switch(state)
{
case 0:
return table.SOLID_RGB;
case 1:
return table.LIQUID_RGB;
case 2:
return table.GAS_RGB;
default:
double[] d = {0.0d,0.0d,0.0d};
return d;
}
}
public double[] getMetalColor()
{
switch(metalState)
{
case 0:
return table.METAL_RGB;
case 1:
return table.METALLOID_RGB;
case 2:
return table.NONMETAL_RGB;
case 3:
return table.RECENT_RGB;
default:
double[] d = {0.0d,0.0d,0.0d};
return d;
}
}
#Override
void render() {
GL11.glPushMatrix();
GL11.glTranslatef(pos.x, pos.y, 0);
double[] d = getMetalColor();
GL11.glColor3d(d[0], d[1], d[2]);
GL11.glBegin(GL11.GL_QUADS);
{
GL11.glVertex2f(0, 0);//topleft
GL11.glVertex2f(dim.x, 0);//topright
GL11.glVertex2f(dim.x, dim.y);//bottomright
GL11.glVertex2f(0, dim.y);//bottomleft
}
GL11.glEnd();
GL11.glColor3d(1.0d, 1.0d, 1.0d);
GL11.glPopMatrix();
}
public void renderWithTex()
{
Font.drawString(symbol, new Vector2D(pos.x+dim.x/2-Font.getStringWidth(symbol,2)/2,pos.y+dim.y/2-Font.FONT_HEIGHT), 2);
}
#Override
void tick() {
if(isSelected)
{
dim.x = table.ELEMENT_SIZE+6; dim.y = table.ELEMENT_SIZE+6;
pos.x = table.pos.x + table.ELEMENT_SIZE*(gridPos.x-1)-3; pos.y = table.pos.y + table.ELEMENT_SIZE*(gridPos.y-1)-3;
} else
{
dim.x = table.ELEMENT_SIZE; dim.y = table.ELEMENT_SIZE;
pos.x = table.pos.x + table.ELEMENT_SIZE*(gridPos.x-1); pos.y = table.pos.y + table.ELEMENT_SIZE*(gridPos.y-1);
}
}
#Override
void onEntityClicked() {
}
}
Font: Handles rendering text onscreen:
package com.flafla2.periodicTable;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import org.lwjgl.opengl.GL11;
public class Font {
public static final String fontText = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789:;?!\"&',-.[]#()+ ";
public static final BufferedImage fontSheet = TextureLoader.loadTexture("/res/text.png");
public static final int FONT_WIDTH = 9;
public static final int FONT_HEIGHT = 8;
public static void drawString(String s, Vector2D pos, float dim)
{
drawString(s,pos,new Vector2D((int)Math.floor(dim*FONT_WIDTH),(int)Math.floor(dim*FONT_HEIGHT)));
}
public static void drawString(String s, Vector2D pos)
{
drawString(s,pos,new Vector2D(9,8));
}
public static void drawString(String s, Vector2D pos, Vector2D dim)
{
for(int x=0;x<s.length();x++)
{
drawLetter(s.charAt(x),new Vector2D(pos.x+dim.x*x,pos.y),dim);
}
}
public static int getStringWidth(String s)
{
return s.length()*FONT_WIDTH;
}
public static int getStringWidth(String s,float f)
{
return (int)Math.floor(s.length()*FONT_WIDTH*f);
}
public static Vector2D getPosOfLetterOnImg(Character c,int gridNumb)
{
int xOffset = 0;
int yOffset = 0;
if(!c.equals(' '))
{
int letterNumb = fontText.indexOf(c);
xOffset = (letterNumb%26)*FONT_WIDTH;
if(xOffset != 0)
xOffset -=1;
yOffset = 0;
int yGridOffset = (letterNumb < 26) ? 0 : ((letterNumb < 52) ? 1 : 2);
switch(gridNumb)
{
case 1:
yOffset = 34;
break;
case 2:
yOffset = 69;
break;
default:
yOffset = 0;
}
for(int x=0;x<yGridOffset;x++)
yOffset += FONT_HEIGHT+x+3;
} else
{
xOffset = 235;
yOffset = 92;
}
return new Vector2D(xOffset,yOffset);
}
public static void drawLetter(Character c, Vector2D pos, Vector2D dim)
{
if(fontSheet == null)
return;
Vector2D letterPos = getPosOfLetterOnImg(c,2);
BufferedImage letterImage = fontSheet.getSubimage(letterPos.x, letterPos.y, FONT_WIDTH, FONT_HEIGHT);
int textureID = TextureLoader.loadGLTexture(letterImage);
letterImage = null;
GL11.glPushMatrix();
GL11.glTranslatef(pos.x, pos.y, 0);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureID);
GL11.glBegin(GL11.GL_QUADS);
{
GL11.glTexCoord2f(0, 0);
GL11.glVertex2f(0, 0);
GL11.glTexCoord2f(1, 0);
GL11.glVertex2f(dim.x, 0);
GL11.glTexCoord2f(1, 1);
GL11.glVertex2f(dim.x, dim.y);
GL11.glTexCoord2f(0, 1);
GL11.glVertex2f(0, dim.y);
}
GL11.glEnd();
GL11.glPopMatrix();
}
}
TextureLoader: Loads textures(duh lol)
package com.flafla2.periodicTable;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.nio.ByteBuffer;
import javax.imageio.ImageIO;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
public class TextureLoader {
public static BufferedImage loadTexture(String texturePath)
{
try {
return ImageIO.read(PeriodicTable.class.getResource(texturePath));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
private static final int BYTES_PER_PIXEL = 4;
public static int loadGLTexture(BufferedImage image){
int[] pixels = new int[image.getWidth() * image.getHeight()];
image.getRGB(0, 0, image.getWidth(), image.getHeight(), pixels, 0, image.getWidth());
ByteBuffer buffer = BufferUtils.createByteBuffer(image.getWidth() * image.getHeight() * BYTES_PER_PIXEL); //4 for RGBA, 3 for RGB
for(int y = 0; y < image.getHeight(); y++){
for(int x = 0; x < image.getWidth(); x++){
int pixel = pixels[y * image.getWidth() + x];
buffer.put((byte) ((pixel >> 16) & 0xFF)); // Red component
buffer.put((byte) ((pixel >> 8) & 0xFF)); // Green component
buffer.put((byte) (pixel & 0xFF)); // Blue component
buffer.put((byte) ((pixel >> 24) & 0xFF)); // Alpha component. Only for RGBA
}
}
buffer.flip(); //FOR THE LOVE OF GOD DO NOT FORGET THIS
// You now have a ByteBuffer filled with the color data of each pixel.
// Now just create a texture ID and bind it. Then you can load it using
// whatever OpenGL method you want, for example:
int textureID = GL11.glGenTextures(); //Generate texture ID
GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureID); //Bind texture ID
//Setup wrap mode
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
//Setup texture scaling filtering
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
//Send texel data to OpenGL
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, image.getWidth(), image.getHeight(), 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer);
buffer = null;
//Return the texture ID so we can bind it later again
return textureID;
}
}
I know, it's a lot of code, but if anyone can help me out it would be greatly appreciated.
Thanks, Flafla2.
Thought you allready solved it there is more room for improvement. I see you have your font in an image and for each character you want to draw you get the part of the image with that letter load it into a texture and afterwards all that needs to be cleaned up.
Better to load the whole image into one big texture, keep that texture for the duration of your program and reuse it when rendering each frame. You can select the right characters to render by specifying the right texture coordinates.
You should be able to hit your 60fps cap with low cpu usage unless your MacBook is really old.
Alright, I found the problem.
In TextureLoader.java, I didn't use glDeleteTextures(textureID), so the textures used in Font.java weren't being unloaded from memory. Now, I am getting a stable 50+ fps(on my crappy macbook, of course).
Also, the other checked answer boosted my fps to ~60. In case anyone is wondering, here is the new drawLetter() method, with changes:
public static void drawLetter(Character c, Vector2D pos, Vector2D dim)
{
if(fontSheet == null)
return;
Vector2D letterPos = getPosOfLetterOnImg(c,2);
//BufferedImage letterImage = fontSheet.getSubimage(letterPos.x, letterPos.y, FONT_WIDTH, FONT_HEIGHT);
//int textureID = TextureLoader.loadGLTexture(letterImage);
//letterImage = null;
int width = fontSheet.getWidth(); int height = fontSheet.getHeight();
double d[] = {(double)letterPos.x/width, (double)letterPos.y/height, (double)(letterPos.x+FONT_WIDTH)/width, (double)(letterPos.y+FONT_HEIGHT)/height};
GL11.glPushMatrix();
GL11.glTranslatef(pos.x, pos.y, 0);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureID);
GL11.glBegin(GL11.GL_QUADS);
{
GL11.glTexCoord2d(d[0], d[1]);
GL11.glVertex2f(0, 0);
GL11.glTexCoord2d(d[2], d[1]);
GL11.glVertex2f(dim.x, 0);
GL11.glTexCoord2d(d[2], d[3]);
GL11.glVertex2f(dim.x, dim.y);
GL11.glTexCoord2d(d[0], d[3]);
GL11.glVertex2f(0, dim.y);
}
GL11.glEnd();
GL11.glPopMatrix();
//GL11.glDeleteTextures(textureID);
}
Related
LibGDX's Model made with ModelBuilder not rendering
I'm learning to use LibGDX and my goal is to create a cube, with which you can control the resolution (number of vertices along each face). I already did that, and managed to use MeshBuilder to make it out of 6 different meshes and then render the resulting Mesh successfully using basic shaders : Cube Mesh //creates a square face with a normal vector and resolution number of vertices along any edge of the face public Mesh createFace(Vector3 normal, int resolution) { //creates 2 vectors perpendicular to each other and to the vector normal Vector3 axisA = new Vector3(normal.y,normal.z,normal.x); Vector3 axis = u.crs(normal, axisA); Vector3 axisB = new Vector3(u.sqrt(axis.x),u.sqrt(axis.y),u.sqrt(axis.z)); //creates the arrays to hold the vertices and triangles Vector3[] vertices = new Vector3[resolution * resolution]; //code for triangles short[] triangles = new short[(resolution - 1) * (resolution - 1) * 6]; int triIndex = 0; //looping over each vertex in the face for (int y = 0; y < resolution; y++) { for (int x = 0; x < resolution; x++) { int vertexIndex = x + y * resolution; //vector representing how close to the end of the x or y axis the loop is Vector2 t = new Vector2(x / (resolution - 1f),y / (resolution - 1f)); //calculates the position of the vertex to place on the face Vector3 mulA = u.mul(axisA, (2*t.x - 1)); Vector3 mulB = u.mul(axisB, (2*t.y-1)); Vector3 point = u.add3(normal, mulA, mulB); //point = u.normalize(point); vertices[vertexIndex] = point; //puts the vertices into triangles if (x != resolution - 1 && y != resolution - 1) { triangles[triIndex + 0] = (short) vertexIndex; triangles[triIndex + 1] = (short) (vertexIndex + resolution + 1); triangles[triIndex + 2] = (short) (vertexIndex + resolution); triangles[triIndex + 3] = (short) vertexIndex; triangles[triIndex + 4] = (short) (vertexIndex + 1); triangles[triIndex + 5] = (short) (vertexIndex + resolution + 1); triIndex += 6; } } } float[] verticeList = u.vectorToList(vertices); Mesh m = new Mesh(true, resolution * resolution, triangles.length, new VertexAttribute(Usage.Position,3,"a_Position")); m.setIndices(triangles); m.setVertices(verticeList); return m; } //generates a cube Mesh with resolution vertices along each face public Mesh generateFaces(int resolution, float scale) { MeshBuilder meshBuilder = new MeshBuilder(); meshBuilder.begin(new VertexAttributes(new VertexAttribute (Usage.Position, 3 ,"a_Position"))); Vector3[] faceNormals = { new Vector3(0,1*scale,0), //up new Vector3(0,-1*scale,0), //down new Vector3(-1*scale,0,0), //left new Vector3(1*scale,0,0), //right new Vector3(0,0,1*scale), //forward new Vector3(0,0,-1*scale) //back }; for (int i = 0; i < faceNormals.length; i++) { meshBuilder.part("part"+ Integer.toString(i), GL20.GL_TRIANGLES); meshBuilder.addMesh(createFace(faceNormals[i], resolution)); } Mesh mesh = meshBuilder.end(); return mesh; } u is just a utilities class i created to store some math functions. I then render it like so: #Override public void render () { camController.update(); Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); Gdx.gl.glClearColor(0.5f, 0.5f, 0.5f, 0.5f); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT); shader.bind(); shader.setUniformMatrix("matViewProj", cam.combined); //rendering mesh mesh1.render(shader, GL20.GL_LINE_STRIP); [...] } I now want to make a model out of that mesh where each of the 6 faces will have a different color. I thus tried to do it using a ModelBuilder following the LibGDX wiki, like so: public Model generateModel(int resolution, float scale, Color[] colors) { Vector3[] faceNormals = { new Vector3(0,1*scale,0), //up new Vector3(0,-1*scale,0), //down new Vector3(-1*scale,0,0), //left new Vector3(1*scale,0,0), //right new Vector3(0,0,1*scale), //forward new Vector3(0,0,-1*scale) //back }; ModelBuilder modelBuilder = new ModelBuilder(); modelBuilder.begin(); for (int i = 0; i < faceNormals.length; i++) { Mesh mesh = createFace(faceNormals[i], resolution); MeshPart part = new MeshPart("part"+Integer.toString(i),mesh, 0, mesh.getNumVertices() ,GL20.GL_TRIANGLES); modelBuilder.node().parts.add(new NodePart(part, new Material(ColorAttribute.createDiffuse(colors[i])))); } Model m = modelBuilder.end(); return m; } And then i rendered it using a ModelBatch and ModelInstance : #Override public void create () { //creates an environment to handle lighting and such environment = new Environment(); environment.set(new ColorAttribute(ColorAttribute.AmbientLight,0.4f,0.4f,0.4f,1f)); environment.add(new DirectionalLight().set(0.8f,0.8f,0.8f,-1f,-0.8f,-0.2f)); modelBatch = new ModelBatch(); //handling the inputProcessors of the camera and stage(UI) multiplexer = new InputMultiplexer(); stage = new Stage(); multiplexer.addProcessor(stage); scroll = new ScrolledInputProcessor(); multiplexer.addProcessor(scroll); //camera (3D inputProcessor) cam = new PerspectiveCamera(67,Gdx.graphics.getWidth(),Gdx.graphics.getHeight()); cam.position.set(10f,10f,10f); cam.lookAt(0,0,0); cam.near = 1f; cam.far = 300f; cam.update(); camController = new CameraInputController(cam); multiplexer.addProcessor(camController); //shaders for every vertex and every pixel(fragment) shader = new ShaderProgram(Gdx.files.internal("shader/vertexshader.glsl").readString() ,Gdx.files.internal("shader/fragmentshader.glsl").readString()); shader2 = new ShaderProgram(Gdx.files.internal("shader/vertexshader.glsl").readString() ,Gdx.files.internal("shader/fragmentshader2.glsl").readString()); //The 2D box encompassing the screen (UI) table = new Table(); table.setFillParent(true); stage.addActor(table); //skins for UI skin = new Skin(Gdx.files.internal("uiskin.json")); //making a slider and dressing it in the skin Drawable knobDown = skin.newDrawable("default-slider-knob", Color.GRAY); SliderStyle sliderStyle = skin.get("default-horizontal", SliderStyle.class); sliderStyle.knobDown = knobDown; slider = new Slider(3.0f, 70.0f, 1.0f, false, sliderStyle); table.right().top(); table.add(slider).row(); //creates the unit cube and unit sphere model = generateModel(res, 1, colors); instance = new ModelInstance(model); font = new BitmapFont(Gdx.files.internal("uiskin.fnt")); batch = new SpriteBatch(); Gdx.input.setInputProcessor(multiplexer); } #Override public void render () { camController.update(); Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); Gdx.gl.glClearColor(0.5f, 0.5f, 0.5f, 0.5f); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT); shader.bind(); shader.setUniformMatrix("matViewProj", cam.combined); modelBatch.begin(cam); modelBatch.render(instance, environment); modelBatch.end(); batch.begin(); font.draw(batch, "Zoom Level : " + zoomLevel, 1000f, 100f); batch.end(); stage.act(Gdx.graphics.getDeltaTime()); stage.draw(); } However, when i run the program, nothing is rendered, just the gray void. Gray void of nothingness My question is: How do I get my model to render?
Issue in displaying multipass shader using FBOs for gaussian blur with Android/OpenGLES2
Environment is Android/OpenGLES2.0 I'm refactoring a 2-dimensional gaussian blur into 2 1-dimensional ones, using a horizontal & vertical pass, as described in: http://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/. This is blurring a video from a SurfaceTexture. I understand that this can be accomplished with framebuffers, and have implemented these with the required steps, however I cannot get the blur to display. Any help would be greatly appreciated, logging removed in code for clarity. Framebuffer setup, setupFramebuffers() is called: int[] newFbo = new int[2]; int[] newTex = new int[2]; void setupFramebuffers() { createFramebuffer(newFbo, newTex, 0); createFramebuffer(newFbo, newTex, 1); GLES20.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); } void createFramebuffer(int[] fbo, int[] fbtex, int offset) { GLES20.glGenTextures(1, fbtex, offset); GLES20.glBindTexture(GL_TEXTURE_2D, fbtex[offset]); GLES20.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1024, 1024, 0, GL_RGB, GL_UNSIGNED_BYTE, null); GLES20.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); GLES20.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); GLES20.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); GLES20.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); GLES20.glGenFramebuffers(1, fbo, offset); GLES20.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[offset]); GLES20.glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbtex[offset], 0); int status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER); switch (status) { case GL_FRAMEBUFFER_COMPLETE: break; case GL_FRAMEBUFFER_UNSUPPORTED: return; default: return; } } In the draw method, generateFbos() is called: private synchronized void drawToWindow() { for(ShaderSurfaceView shaderSurfaceView : this.shaderSurfaceViewsToRender) { if(!shaderSurfaceView.getStopRendering()) { final int program = shaderSurfaceView.getProgramId(); final WindowSurface windowSurface = shaderSurfaceView.getWindowSurface(); if (windowSurface == null) { continue; } windowSurface.makeCurrent(); GLES20.glViewport(0, 0, windowSurface.getWidth(), windowSurface.getHeight()); generateFbos(shaderSurfaceView, windowSurface); GLES20.glEnable(GLES20.GL_BLEND); windowSurface.swapBuffers(); } } } generateFbos: void generateFbos(ShaderSurfaceView shaderSurfaceView, WindowSurface windowSurface) { final int positionHandle = shaderSurfaceView.getPositionHandle(); final int textureHandle = shaderSurfaceView.getTextureHandle(); final int mvpMatrixHandle = shaderSurfaceView.getMvpMatrixHandle(); final int stMatrixHandle = shaderSurfaceView.getStMatrixHandle(); final int verticalProgramId = shaderSurfaceView.getVerticalProgramId(); final int horizontalProgramId = shaderSurfaceView.getHorizontalProgramId(); // create the mesh this.triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET); GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, this.triangleVertices); GLES20.glEnableVertexAttribArray(positionHandle); this.triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET); GLES20.glVertexAttribPointer(textureHandle, 3, GLES20.GL_FLOAT, false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, this.triangleVertices); GLES20.glEnableVertexAttribArray(textureHandle); Matrix.setIdentityM(this.mvpMatrix, 0); GLES20.glUniformMatrix4fv(mvpMatrixHandle, 1, false, this.mvpMatrix, 0); GLES20.glUniformMatrix4fv(stMatrixHandle, 1, false, this.stMatrix, 0); GLES20.glBindTexture(GL_TEXTURE_2D, textureHandle); GLES20.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, newFbo[0]); GLES20.glUseProgram(verticalProgramId); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); GLES20.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, newFbo[1]); GLES20.glBindTexture(GL_TEXTURE_2D, newTex[0]); GLES20.glUseProgram(horizontalProgramId); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); GLES20.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); // Re-enable the default window-system framebuffer for drawing GLES20.glBindTexture(GL_TEXTURE_2D, newTex[0]); }
Moving counters along a 2d array
I have three classes and am trying to make a game whereby users move along a grid depending on what is rolled by a die. I have my main BoardGame class, containing the GUI and the counters which currently are Jlabel images (i'm open to suggestions as to what I could use instead of a JLabel - i wasnt so sure myself). I have a Grid class which I have arranged into a 2D array and called an instance of in the BoardGame class, and I have a die class which rolls a random number from 1-6. I am trying to get me counters to start at the first square on the grid, and then advance in a left-to-right-right-to-left fashion. I am unsure however of how to make the counters move through the grid in the first place. Hopefully, if I can figure this out, I believe I can then implement them moving a specific amount via the die. Thanks for the help in advance GameBoard class: public class GameBoard extends javax.swing.JFrame { private JLabel Board; private JLabel GreenDot; private JLabel redDot; private JButton startButton; private Grid grid; private Die die; /** * Auto-generated main method to display this JFrame */ public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { Grid grid = new Grid(); GameBoard inst = new GameBoard(grid); inst.setLocationRelativeTo(null); inst.setVisible(true); } }); } public GameBoard(Grid grid) { super(); this.grid = grid; initGUI(); } private void initGUI() { try { setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); getContentPane().setLayout(null); { redDot = new JLabel(); getContentPane().add(redDot); redDot.setText("jLabel1"); redDot.setIcon(new ImageIcon(getClass().getClassLoader().getResource("images/download.png"))); redDot.setBounds(220, 434, 20, 12); redDot.setBorder(new LineBorder(new java.awt.Color(0,0,0), 1, false)); } { GreenDot = new JLabel(); getContentPane().add(GreenDot); GreenDot.setText("jLabel1"); GreenDot.setIcon(new ImageIcon(getClass().getClassLoader().getResource("images/3d-green-ball-th.png"))); GreenDot.setBounds(222, 453, 21, 13); GreenDot.setBorder(new LineBorder(new java.awt.Color(0,0,0), 1, false)); } { startButton = new JButton(); getContentPane().add(startButton); startButton.setText("Start Game"); startButton.setBounds(64, 443, 83, 23); } { Board = new JLabel(); getContentPane().add(Board); Board.setLayout(null); Board.setText("jLabel1"); Board.setIcon(new ImageIcon(getClass().getClassLoader().getResource("images/board.jpg"))); Board.setBounds(204, -1, 742, 484); } pack(); this.setSize(963, 523); } catch (Exception e) { //add your error handling code here e.printStackTrace(); } } } Grid class: public class Grid { int[][] multi = { { 0, 0,-1, 0, 0,-1, 0,-1, 0, 0}, { 0, 0, 0, 0, 0, 0,-1, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, { 0,-1, 0,-1, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0,-1, 0, 0, 1}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 1, 0, 0, 0, 0, 0, 0, 1, 0, 0}, { 0, 0, 0,-1, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 1, 0, 0, 0, 0, 1, 0} }; } Die class: public class Die { public Die() { } public void dieRoll() { int SIDES = 6; int roll = (int) (Math.random() * SIDES) + 1; System.out.println(roll); } }
A simple way of doing this would be to change the die class so it has { private int sides; public Die(int numSides){ sides = numSides; } public int roll(){ return (int) (Math.random() * SIDES) + 1 } } Then you can roll a six sided die like so //this creates the die Die sixSides = new Die(6); //this rolls the die and prints the roll System.out.print("That roll got you a " + sixSides.roll()); to move along a 2D array, all you need to do is use the modulus and you need to create a point object class public position move(int num, int x, int y){//input is dice roll int then current position for(int i = 0; i < num;i++){ if(i%10==0){ y++; x = 0; }else{ x++; } } return new point(x,y); } To access the x and y positions of the point object you would need to write get and set methods for the object. something like this point player1 = new point(0,0); int xposition = player1.getX
Can't initialize method initGeometry(gl); initLighting(gl); on class
Help me find my mistake. I make an example from book but may be author (or i)make some trouble and i cant find solve. First class is geometry of my object import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import javax.microedition.khronos.opengles.GL10; public class Planet { FloatBuffer m_VertexData; FloatBuffer m_NormalData; FloatBuffer m_ColorData; float m_Scale; float m_Squash; float m_Radius; int m_Stacks, m_Slices; public final static int SS_SUNLIGHT = GL10.GL_LIGHT0; public Planet(int stacks, int slices, float radius, float squash) { this.m_Stacks = stacks; //1 this.m_Slices = slices; this.m_Radius = radius; this.m_Squash=squash; init(m_Stacks,m_Slices,radius,squash,"dummy"); } private void init(int stacks,int slices, float radius, float squash, String textureFile) { float[] vertexData; float[] colorData; //2 float[] normalData; float colorIncrement=0f; float blue=0f; float red=1.0f; int numVertices=0; int vIndex=0; int cIndex=0; int nIndex =0; m_Scale=radius; m_Squash=squash; colorIncrement=1.0f/(float)stacks;//3 m_Stacks = stacks; m_Slices = slices; //vertices vertexData = new float[ 3*((m_Slices*2+2) * m_Stacks)]; //4 //color data colorData = new float[ (4*(m_Slices*2+2) * m_Stacks)]; //5 // Normalize data normalData = new float [ (3*(m_Slices*2+2)* m_Stacks)]; //1 int phiIdx, thetaIdx; //latitude for(phiIdx=0; phiIdx < m_Stacks; phiIdx++) //6 { //starts at -90 degrees (-1.57 radians) goes up to +90 degrees (or +1.57 radians) //the first circle float phi0 = (float)Math.PI * ((float)(phiIdx+0) * (1.0f/(float)(m_Stacks)) - 0.5f); //7 //the next, or second one. float phi1 = (float)Math.PI * ((float)(phiIdx+1) * (1.0f/(float)(m_Stacks)) - 0.5f); //8 float cosPhi0 = (float)Math.cos(phi0); //9 float sinPhi0 = (float)Math.sin(phi0); float cosPhi1 = (float)Math.cos(phi1); float sinPhi1 = (float)Math.sin(phi1); float cosTheta, sinTheta; //longitude for(thetaIdx=0;thetaIdx < m_Slices; thetaIdx++) { //increment along the longitude circle each "slice" float theta= (float) (-2.0f*(float)Math.PI * ((float)thetaIdx) *(1.0/(float)(m_Slices-1))); cosTheta = (float)Math.cos(theta); sinTheta = (float)Math.sin(theta); //we're generating a vertical pair of points, such //as the first point of stack 0 and the first point of stack 1 //above it. This is how TRIANGLE_STRIPS work, //taking a set of 4 vertices and essentially drawing two triangles //at a time. The first is v0-v1-v2 and the next is v2-v1-v3. Etc. //get x-y-z for the first vertex of stack vertexData[vIndex+0] = m_Scale*cosPhi0*cosTheta; //11 vertexData[vIndex+1] = m_Scale*(sinPhi0*m_Squash); vertexData[vIndex+2] = m_Scale*(cosPhi0*sinTheta); vertexData[vIndex+3] = m_Scale*cosPhi1*cosTheta; vertexData[vIndex+4] = m_Scale*(sinPhi1*m_Squash); vertexData[vIndex+5] = m_Scale*(cosPhi1*sinTheta); colorData[cIndex+0] = (float)red; //12 colorData[cIndex+1] = (float)0f; colorData[cIndex+2] = (float)blue; colorData[cIndex+4] = (float)red; colorData[cIndex+5] = (float)0f; colorData[cIndex+6] = (float)blue; colorData[cIndex+3] = (float)1.0; colorData[cIndex+7] = (float)1.0; // Normalize data pointers for lighting. normalData[nIndex + 0] = cosPhi0*cosTheta; normalData[nIndex + 1] = sinPhi0; normalData[nIndex + 2] = cosPhi0*sinTheta; normalData[nIndex + 3] = cosPhi1*cosTheta; normalData[nIndex + 4] = sinPhi1; normalData[nIndex + 5] = cosPhi1*sinTheta; cIndex+=2*4; //13 vIndex+=2*3; //14 nIndex+=2*3; } // blue+=colorIncrement; //15 red-=colorIncrement; // create a degenerate triangle to connect stacks and maintain winding order //16 vertexData[vIndex+0] = vertexData[vIndex+3] = vertexData[vIndex-3]; vertexData[vIndex+1] = vertexData[vIndex+4] = vertexData[vIndex-2]; vertexData[vIndex+2] = vertexData[vIndex+5] = vertexData[vIndex-1]; } m_VertexData = makeFloatBuffer(vertexData); //17 m_ColorData = makeFloatBuffer(colorData); m_NormalData = makeFloatBuffer(normalData); } protected static FloatBuffer makeFloatBuffer(float[] arr) { ByteBuffer bb = ByteBuffer.allocateDirect(arr.length*4); bb.order(ByteOrder.nativeOrder()); FloatBuffer fb = bb.asFloatBuffer(); fb.put(arr); fb.position(0); return fb; } public void draw(GL10 gl) { gl.glFrontFace(GL10.GL_CW); //1 gl.glVertexPointer(3, GL10.GL_FLOAT, 0, m_VertexData); //2 gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glColorPointer(4, GL10.GL_FLOAT, 0, m_ColorData); gl.glEnableClientState(GL10.GL_COLOR_ARRAY); //3 gl.glDrawArrays(GL10.GL_TRIANGLE_FAN, 0, (m_Slices+1)*2*(m_Stacks-1)+2); } private void initLighting(GL10 gl) { float[] diffuse = {0.0f, 1.0f, 0.0f, 1.0f}; //1 float[] pos = {0.0f, 10.0f, -3.0f, 1.0f}; //2 gl.glLightfv(SS_SUNLIGHT, GL10.GL_POSITION, makeFloatBuffer(pos)); //3 gl.glLightfv(SS_SUNLIGHT, GL10.GL_DIFFUSE, makeFloatBuffer(diffuse)); //4 gl.glShadeModel(GL10.GL_FLAT); //5 gl.glEnable(GL10.GL_LIGHTING); //6 gl.glEnable(SS_SUNLIGHT); //7 } } And second is a render: package com.example.solarsystem; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.opengl.GLSurfaceView; public class SolarSystemRenderer implements GLSurfaceView.Renderer{ private Planet mPlanet; public SolarSystemRenderer(boolean b) { mPlanet=new Planet(30,30,1.0f, 1.0f); } private float mTransY; private float mAngle; public void onDrawFrame(GL10 gl) { gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); gl.glClearColor(0.0f,0.0f,0.0f,1.0f); gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glLoadIdentity(); gl.glTranslatef(0.0f,(float)Math.sin(mTransY), -10.0f); gl.glRotatef(mAngle, 1, 0, 0); gl.glRotatef(mAngle, 0, 1, 0); mPlanet.draw(gl); mTransY+=.0f; mAngle+=.4; } public void onSurfaceChanged(GL10 gl, int width, int height) //11 { gl.glViewport(0, 0, width, height); //12 float aspectRatio; float zNear =3.2f; float zFar =1000; float fieldOfView = 30.0f/57.3f; //1 float size; gl.glEnable(GL10.GL_NORMALIZE); aspectRatio=(float)width/(float)height; //2 gl.glMatrixMode(GL10.GL_PROJECTION); //3 size = zNear * (float)(Math.tan((double)(fieldOfView/2.0f))); //4 gl.glFrustumf(-size, size, -size /aspectRatio, //5 size /aspectRatio, zNear, zFar); } public void onSurfaceCreated(GL10 gl, EGLConfig config) //15 { gl.glDisable(GL10.GL_DITHER); //16 gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST); //17 gl.glEnable(GL10.GL_CULL_FACE); gl.glCullFace(GL10.GL_BACK); gl.glShadeModel(GL10.GL_SMOOTH); gl.glEnable(GL10.GL_DEPTH_TEST); gl.glDepthMask(false); initGeometry(gl); initLighting(gl); } } So in surfaceCreated method i have mistake in initialization initGeometry(gl); initLighting(gl); Also if i cut my initLighting method from first class to render class i get mistake on initialization makeFloatBuffer.
I also had problems with this part of the book. Try commenting out a couple of lines in the onSurfaceCreated method in the renderer: initGeometry(gl); - because there is no method defined for this in the code. gl.glEnable(GL10.GL_DEPTH_TEST); - Not sure what the problem with this line, but commenting this one out made the whole thing work as described in the book.
Difficulty with Text in JOGL
I'm trying to display text in my OpenGL / JOGL app. Here is some of the code: private void display(GLAutoDrawable drawable) { GL2 gl = drawable.getGL().getGL2(); GLUgl2 glu = new GLUgl2(); float[] rgba = new float[4]; backgroundColor.getRGBComponents(rgba); gl.glClearColor(rgba[0], rgba[1], rgba[2], 1); gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT); gl.glMatrixMode(GL2.GL_MODELVIEW); gl.glLoadIdentity(); // Position the camera glu.gluLookAt(cameraPos.x, cameraPos.y, cameraPos.z, cameraPos.x + cameraForward.x, cameraPos.y + cameraForward.y, cameraPos.z + cameraForward.z, cameraUp.x, cameraUp.y, cameraUp.z); gl.glPushMatrix(); GLUT glut = new GLUT(); gl.glTranslatef(0, 0, 0); gl.glColor3f(1, 0, 0); glut.glutBitmapString(GLUT.BITMAP_HELVETICA_18, "We're going to the moon!"); gl.glPopMatrix(); // ... The text appears as soon as rendering begins, then runs off the right hand side of the screen. Why is this happening? I'd like to display text in front of the user, not as a model in the world.
You need to set the current raster position before invoking glutBitmapString. The raster position is the pixel where the string will start being drawn. You can set it using the glRasterPos... family of functions. The reason your text seems to "run off" the right side of the screen is that calling glutBitmapString implicitly moves the raster position to the end of the string (see here).
There is very easy solution but it "links" your app against awt, which might be bad if you try to stay out of it in favour of newt. Below example draws player osd using number of text renderers: one for play/pause glyph, one more for some information like time, etc., and yet another one for movie name. import com.jogamp.opengl.util.awt.TextRenderer; private static final Color DROP_SHADOW_COLOR = new Color(0, 0, 0, 0.5f); class CustomRenderDelegate implements TextRenderer.RenderDelegate { private int dropShadowDepth; private Color color; CustomRenderDelegate(int dropShadowDepth, Color color) { this.dropShadowDepth = dropShadowDepth; this.color = color; } public boolean intensityOnly() { return false; } public Rectangle2D getBounds(CharSequence str, Font font, FontRenderContext frc) { return getBounds(str.toString(), font, frc); } public Rectangle2D getBounds(String str, Font font, FontRenderContext frc) { return getBounds(font.createGlyphVector(frc, str), frc); } public Rectangle2D getBounds(GlyphVector gv, FontRenderContext frc) { Rectangle2D stringBounds = gv.getPixelBounds(frc, 0, 0); return new Rectangle2D.Double(stringBounds.getX(), stringBounds.getY(), stringBounds.getWidth() + dropShadowDepth, stringBounds.getHeight() + dropShadowDepth); } public void drawGlyphVector(Graphics2D graphics, GlyphVector str, int x, int y) { graphics.setColor(DROP_SHADOW_COLOR); graphics.drawGlyphVector(str, x + dropShadowDepth, y + dropShadowDepth); graphics.setColor(color); graphics.drawGlyphVector(str, x, y); } public void draw(Graphics2D graphics, String str, int x, int y) { graphics.setColor(DROP_SHADOW_COLOR); graphics.drawString(str, x + dropShadowDepth, y + dropShadowDepth); graphics.setColor(color); graphics.drawString(str, x, y); } } public void init(GLAutoDrawable drawable) { ... pathRenderer = new TextRenderer(new Font("Helvetica", Font.ITALIC, 16), true, true, new CustomRenderDelegate(0, Color.WHITE)); pathRenderer.setSmoothing(true); playRenderer = new TextRenderer(new Font("Helvetica", Font.BOLD, 65), true, true, new CustomRenderDelegate(0, Color.WHITE)); pauseRenderer = new TextRenderer(new Font("Helvetica", Font.BOLD, 50), true, true, new CustomRenderDelegate(0, Color.WHITE)); osdRenderer = new TextRenderer(new Font("Helvetica", Font.PLAIN, 32), true, true, new CustomRenderDelegate(0, Color.WHITE)); osdRenderer.setSmoothing(true); } private final void updateOsd() { Time t = currentFrame != null ? currentFrame.getTime() : new Time(0, 0, 0, 0, 0); String direction = video.direction == DirectionType.DIRECTION_LEFT ? "<" : ">"; String frameNumber = enterFrameNumber != null ? String.format("[%s]", enterFrameNumber) : String.format("(%d)", t.fn); osdText = String.format(" %s%s x %d # %.2f fps < %d/%d (%s) # %d fps", TimeUtil.hmsms(t.getPtsMillis()), frameNumber, (int) video.getPlaybackRate(), playbackFps, video.readyFrames(), maxFrames, direction, video.getFilterFps()); } private void renderOsd(GLAutoDrawable drawable) { updateOsd(); if (isOsdEnabled) { maxModeWidth = (int) Math.max(maxModeWidth, playRenderer.getBounds("\u25B8").getWidth()); maxOsdHeight = (int) Math.max(maxOsdHeight, playRenderer.getBounds("\u25B8").getHeight()); if (isPaused) { pauseRenderer.beginRendering(drawable.getWidth(), drawable.getHeight()); pauseRenderer.draw("\u2759", 10, drawable.getHeight() - maxOsdHeight - 2); int barWidth = (int) pauseRenderer.getBounds("\u2759").getWidth(); pauseRenderer.draw("\u2759", 10 + barWidth - 3, drawable.getHeight() - maxOsdHeight - 2); pauseRenderer.flush(); pauseRenderer.endRendering(); } else { playRenderer.beginRendering(drawable.getWidth(), drawable.getHeight()); playRenderer.draw("\u25B8", 10, drawable.getHeight() - maxOsdHeight - 5); playRenderer.flush(); playRenderer.endRendering(); } int y = (int) ((maxOsdHeight + 10) - osdRenderer.getBounds(osdText).getHeight() / 2); osdRenderer.beginRendering(drawable.getWidth(), drawable.getHeight()); osdRenderer.draw(osdText, maxModeWidth + 18, drawable.getHeight() - y - 2); osdRenderer.flush(); osdRenderer.endRendering(); if (isFullScreen) { pathRenderer.beginRendering(drawable.getWidth(), drawable.getHeight()); pathRenderer.draw(videoFile.getName(), 18, drawable.getHeight() - y - maxOsdHeight + 15); pathRenderer.flush(); pathRenderer.endRendering(); } } } public void display(GLAutoDrawable drawable) { GL gl = drawable.getGL(); gl.glClear(GL.GL_COLOR_BUFFER_BIT); gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); renderFrame(drawable); renderOsd(drawable); gl.glFinish(); }