My Framerate on my "game" is extremely bad so i need some assistance - lwjgl

My game is extremely laggy in framerate. I'm not sure exactly what it is, but my camera jumps around stupidly. I was wondering if anyone could help me. They only render what's on screen and don't render the sides you cannot see.
This code is for my first class called GE which is my main class
import static org.lwjgl.opengl.GL11.*;
import java.text.Format.Field;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.newdawn.slick.opengl.Texture;
public class GE {
public static void main(String[] args) {
initDisplay();
gameLoop();
cleanUp();
}
public static void gameLoop() {
Texture front = Block.loadTexture(Block.getFront());
Texture back = Block.loadTexture(Block.getBack());
Texture Tright = Block.loadTexture(Block.getTright());
Texture Tleft = Block.loadTexture(Block.getTleft());
Texture top = Block.loadTexture(Block.getTop());
Texture bottom = Block.loadTexture(Block.getBottom());
Camera cam = new Camera(70, (float) Display.getWidth()
/ (float) Display.getHeight(), 0.3f, 1000);
while (!Display.isCloseRequested()) {
boolean forward = Keyboard.isKeyDown(Keyboard.KEY_W)
|| Keyboard.isKeyDown(Keyboard.KEY_UP);
boolean backward = Keyboard.isKeyDown(Keyboard.KEY_S)
|| Keyboard.isKeyDown(Keyboard.KEY_DOWN);
boolean left = Keyboard.isKeyDown(Keyboard.KEY_A);
boolean right = Keyboard.isKeyDown(Keyboard.KEY_D);
boolean crouch = Keyboard.isKeyDown(Keyboard.KEY_LSHIFT);
boolean jump = Keyboard.isKeyDown(Keyboard.KEY_SPACE);
if (forward)
cam.moveZ(2f);
if (backward)
cam.moveZ(-2f);
if (left)
cam.moveX(0.2f);// cam.rotateY(-0.1f);
if (right)
cam.moveX(-0.2f);// cam.rotateY(0.1f);
if (crouch)
cam.moveY(0.2f);
if (jump)
cam.moveY2(-0.2f);
if (Keyboard.isKeyDown(Keyboard.KEY_LEFT))
cam.rotateY(-2f);
if (Keyboard.isKeyDown(Keyboard.KEY_RIGHT))
cam.rotateY(2f);
if (Keyboard.isKeyDown(Keyboard.KEY_Q))
cam.rotateX(-0.2f);
if (Keyboard.isKeyDown(Keyboard.KEY_Z))
cam.rotateX(0.2f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
cam.useView();
for(int x = 0; x < 100; x++){
for(int z = 0; z <100 ;z++){
glPushMatrix();
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_NEAREST);
front.bind();
glBegin(GL_QUADS);
{
// FrontFace
glTexCoord2f(1, 1);
glVertex3f(x-1, -2, z);
glTexCoord2f(0, 1);
glVertex3f(x, -2, z);
glTexCoord2f(0, 0);
glVertex3f(x, -1, z);
glTexCoord2f(1, 0);
glVertex3f(x-1, -1, z);
}
glEnd();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_NEAREST);
back.bind();
glBegin(GL_QUADS);
{
// BackFace
glTexCoord2f(1, 1);
glVertex3f(x, -2, z-1);
glTexCoord2f(0, 1);
glVertex3f(x-1, -2, z-1);
glTexCoord2f(0, 0);
glVertex3f(x-1, -1, z-1);
glTexCoord2f(1, 0);
glVertex3f(x, -1, z-1);
}
glEnd();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_NEAREST);
top.bind();
glBegin(GL_QUADS);
{
// Top Face
glTexCoord2f(0, 0);
glVertex3f(x-1, -1, z-1);
glTexCoord2f(0, 1);
glVertex3f(x, -1, z-1);
glTexCoord2f(1, 1);
glVertex3f(x, -1, z);
glTexCoord2f(1, 0);
glVertex3f(x-1, -1, z);
}
glEnd();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_NEAREST);
Tright.bind();
glBegin(GL_QUADS);
{
// RightFace
glTexCoord2f(0, 0);
glVertex3f(x, -1, z-1);
glTexCoord2f(0, 1);
glVertex3f(x, -2, z-1);
glTexCoord2f(1, 1);
glVertex3f(x, -2, z);
glTexCoord2f(1, 0);
glVertex3f(x, -1, z);
}
glEnd();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_NEAREST);
Tleft.bind();
glBegin(GL_QUADS);
{
// LeftFace
glTexCoord2f(0, 0);
glVertex3f(x-1, -1, z);
glTexCoord2f(0, 1);
glVertex3f(x-1, -2, z);
glTexCoord2f(1, 1);
glVertex3f(x-1, -2, z-1);
glTexCoord2f(1, 0);
glVertex3f(x-1, -1, z-1);
}
glEnd();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_NEAREST);
bottom.bind();
glBegin(GL_QUADS);
{
// BottomFace
glTexCoord2f(0, 0);
glVertex3f(x-1, -2, z-1);
glTexCoord2f(0, 1);
glVertex3f(x, -2, z-1);
glTexCoord2f(1, 1);
glVertex3f(x, -2, z);
glTexCoord2f(1, 0);
glVertex3f(x-1, -2, z);
}
glEnd();
}
glPopMatrix();
}
}
Display.update();
}
}
public static void cleanUp() {
Display.destroy();
}
public static void initDisplay() {
try {
Display.setDisplayMode(new DisplayMode(800, 600));
Display.setInitialBackground(135, 206, 260 );
Display.setTitle("#NervousBreakdown - Alpha 0.01");
Display.setResizable(true);
Display.create();
} catch (LWJGLException ex) {
Logger.getLogger(GE.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
and this is my second class Camera
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.util.glu.GLU.*;
public class Camera {
private float x;
private float y;
private float z;
private float rx;
private float ry;
private float rz;
private float fov;
private float aspect;
private float near;
private float far;
public Camera(float fov, float aspect, float near, float far) {
x = 0;
y = 0;
z = 0;
rx = 0;
ry = 0;
rz = 0;
this.fov = fov;
this.aspect = aspect;
this.near = near;
this.far = far;
initProjection();
}
private void initProjection() {
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(fov, aspect, near, far);
glMatrixMode(GL_MODELVIEW);
glEnable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}
public void useView() {
glRotatef(rx, 1, 0, 0);
glRotatef(ry, 0, 1, 0);
glRotatef(rz, 0, 0, 1);
glTranslatef(x, y, z);
}
public float getX() {
return x;
}
public float getY() {
return y;
}
public float getZ() {
return z;
}
public void setX(float x) {
this.x = x;
}
public void setY(float y) {
this.y = y;
}
public void setZ(float z) {
this.z = z;
}
public float getRX() {
return rx;
}
public float getRY() {
return ry;
}
public float getRZ() {
return rz;
}
public void setRX(float rx) {
this.rx = rx;
}
public void setRY(float ry) {
this.ry = ry;
}
public void setRZ(float rz) {
this.rz = rz;
}
public void moveZ(float amt) {
z += amt * Math.sin(Math.toRadians(ry + 90));// *
// Math.sin(Math.toRadians(rx
// + 90));
x += amt * Math.cos(Math.toRadians(ry + 90));
// y += amt * Math.sin(Math.toRadians(rx));
}
public void moveX(float amt) {
z += amt * Math.sin(Math.toRadians(ry));
x += amt * Math.cos(Math.toRadians(ry));
}
public void moveY(float amt){
y= (float) (y + 0.002);
}
public void moveY2(float amt){
y= (float) (y - 0.002);
}
public void rotateY(float amt) {
ry += amt;
}
public void rotateX(float amt) {
rx += amt;
}
public void rotateZ(float amt) {
rz += amt;
}
And finally my last class block, which sets the textures for each of the sides in the main block:
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.opengl.TextureLoader;
public class Block {
static String block = "Grass";
static String Front;
static String Back;
static String Top;
static String Tright;
static String Tleft;
static String Bottom;
public static Texture loadTexture(String key) {
try {
return TextureLoader.getTexture("png", new FileInputStream(
new File("res/textures/" + key + ".png")));
} catch (IOException ex) {
Logger.getLogger(GE.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
public static String getFront(){
if(block == "Grass"){
Front = "SideGrass";
}
if(block == "Wood"){
Front = "Wood";
}
return Front;
}
public static String getBack(){
if(block == "Grass"){
Back = "SideGrass";
}
if(block == "Wood"){
Back = "Wood";
}
return Back;
}
public static String getBottom(){
if(block == "Grass"){
Bottom = "Dirt";
}
if(block == "Wood"){
Bottom = "TopWood";
}
return Bottom;
}
public static String getTleft(){
if(block == "Grass"){
Tleft = "SideGrass";
}
if(block == "Wood"){
Tleft = "Wood";
}
return Tleft;
}
public static String getTright(){
if(block == "Grass"){
Tright = "SideGrass";
}
if(block == "Wood"){
Tright = "Wood";
}
return Tright;
}
public static String getTop(){
if(block == "Grass"){
Top = "Grass";
}
if(block == "Wood"){
Top = "TopWood";
}
return Top;
}
}

Well for a start you're using immediate mode. Use display lists or VBOs instead...
http://www.youtube.com/watch?v=-89BM2y3WIA&list=SP19F2453814E0E315&index=27
(this guy has a whole series of LWJGL tutorials BTW)
You're also using separate textures for each side of the block. Part of why Minecraft renders so fast and uses such little memory because every block shares the same texture. Rather than having a different texture, each block type should have different texture coordinates.
Although its a more advanced optimization, you might also want to think about culling. Simple frustum culling (i.e. don't draw what you can't see anyway) should make a significant improvement.

Try to remove as much as you can in while the method is rendering, for example load your textures into the the class method, e.g:
public GE() {
//load textures and stuff you don't need to continuously loop

I see you are using immediate mode. This way of coding is deprecated for a good reason. It is ok to use it because I don't think support will drop for it anytime soon, but it is a bad idea to start learnig it now. Take an OpengGl reference card and look up what is deprecated and what is not. Also I do encourage you to set your context to forward compatibility (org.lwjgl.opengl.ContextAttribs), and use the right tutorials. Remember the only reason to use OpenGl is performance so don't waste your time to learn an interface that is too slow. Drawing a single qual with glBegin/glEnd is as expensive as drawing a full mesh.
Here is one: http://www.arcsynthesis.org/gltut/

You should move :
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
where the textures are loaded, (called only once per texture).
Another thing which might help, don't compare Strings this way, use .equals() instead :
if(block == "Wood"){
Front = "Wood";
}
becomes
if(block.equals("Wood")){
Front = "Wood";
}
And in fact, you shouldn't use strings at all when performances matters. You may try an enum type instead.

Related

Libgdx custom shader per-vertex attribute

After several days of struggling I came here. I'm trying to pass a custom per-vertex vec3 attribute to a custom shader based on this tutorial. The tutorial describes how to pass a custom uniform which actually works fine. However when I'm trying to modify the code to pass my custom per-vertex attribute it seems that nothing is transferred to vertex shader and I can't figure out how to make it to work.
So far I've done the following:
I've created several boxes with modelBuilder.createBox() (so I know for sure every model has 24 vertexes)
Then I'v generated a FloatBuffer containing actual attribute data like this:
int[] data = new int[]{x1, y1, z1, x1, y1, z1, ...}
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(data.length * 4);
byteBuffer.order(ByteOrder.nativeOrder());
mAttributeBuffer = byteBuffer.asFloatBuffer();
mAttributeBuffer.put(data);
mAttributeBuffer.position(0);
Then I'm initializing the corresponding attribute location variable (successfully, a_coord >= 0):
a_coord = program.getAttributeLocation("a_coord");
After that on libgdx side in custom shader's render(Renderable) method I'm passing the buffer to OpenGL like this:
program.setVertexAttribute(a_coord, 3, Gdx.gl20.GL_FLOAT, false, 0, mAttributeBuffer);
My custom vertex shader is as the following:
attribute vec3 a_position;
attribute vec3 a_normal;
attribute vec2 a_texCoord0;
uniform mat4 u_worldTrans;
uniform mat4 u_projTrans;
varying vec2 v_texCoord0;
//my custom attribute
attribute vec2 a_coord;
void main() {
v_texCoord0 = a_texCoord0;
float posY = a_position.y + a_coord.y;
gl_Position = u_projTrans * u_worldTrans * vec4(a_position.x, posY, a_position.z, 1.0);
}
The problem
At the moment a_coord is 0 for every vertex. What am I missing and how to correctly pass custom attribute to vertex shader?
I'm guessing the problem is somewhere in VBO field and the way libGDX passes attribute data to vertexes but I still can't figure out how to make it work.
I'll be glad if anyone can point me in the right direction on this question.
Complete code:
Main AplicationListener class:
public class ProtoGame implements ApplicationListener {
public ProtoGame()
{
super();
}
public PerspectiveCamera cam;
public CameraInputController camController;
public Model model;
public Array<ModelInstance> instances = new Array<ModelInstance>();
public ModelBatch modelBatch;
#Override
public void create () {
cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
cam.position.set(0f, 8f, 8f);
cam.lookAt(0,0,0);
cam.near = 1f;
cam.far = 300f;
cam.update();
camController = new CameraInputController(cam);
Gdx.input.setInputProcessor(camController);
ModelBuilder modelBuilder = new ModelBuilder();
model = modelBuilder.createBox(1f, 1f, 1f,
new Material(),
VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal | VertexAttributes.Usage.TextureCoordinates);
Color colorU = new Color(), colorV = new Color();
for (int x = -5; x <= 5; x+=2) {
for (int z = -5; z<=5; z+=2) {
ModelInstance instance = new ModelInstance(model, x, 0, z);
//this is where I'll put per-vertex attribute data for every instance
//but for now it's hardcoded in the Shader class so the data is the same across instances
TestShader.DoubleColorAttribute attr = new TestShader.DoubleColorAttribute(TestShader.DoubleColorAttribute.DiffuseUV,
colorU.set((x+5f)/10f, 1f - (z+5f)/10f, 0, 1),
colorV.set(1f - (x+5f)/10f, 0, (z+5f)/10f, 1));
instance.materials.get(0).set(attr);
instances.add(instance);
}
}
modelBatch = new ModelBatch(new BaseShaderProvider() {
#Override
protected Shader createShader(Renderable renderable) {
return new TestShader();
}
});
}
#Override
public void render () {
camController.update();
Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
modelBatch.begin(cam);
for (ModelInstance instance : instances)
modelBatch.render(instance);
modelBatch.end();
}
#Override
public void dispose () {
model.dispose();
modelBatch.dispose();
}
}
Custom libgdx shader class:
public class TestShader implements Shader {
private FloatBuffer mAttributeBuffer;
ShaderProgram program;
Camera camera;
RenderContext context;
int u_projTrans;
int u_worldTrans;
int u_colorU;
int u_colorV;
int a_coord;
private static String getCustomVertexShader() {
return Gdx.files.internal("shader/test.vertex.glsl").readString();
}
private static String getCustomFragmentShader() {
return Gdx.files.internal("shader/test.fragment.glsl").readString();
}
#Override
public void init() {
program = new ShaderProgram(getCustomVertexShader(), getCustomFragmentShader());
if (!program.isCompiled())
throw new GdxRuntimeException(program.getLog());
//tutorial's logic to init custom uniform locations
u_projTrans = program.getUniformLocation("u_projTrans");
u_worldTrans = program.getUniformLocation("u_worldTrans");
u_colorU = program.getUniformLocation("u_colorU");
u_colorV = program.getUniformLocation("u_colorV");
//initing custom attribute location
a_coord = program.getAttributeLocation("a_coord");
//generating data and passing it to nio Buffer
float data[] = generateData();
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(data.length * 4);
byteBuffer.order(ByteOrder.nativeOrder());
mAttributeBuffer = byteBuffer.asFloatBuffer();
mAttributeBuffer.put(data);
mAttributeBuffer.position(0);
}
private float[] generateData() {
Vector3[] dataArray = new Vector3[1];
dataArray[0] = new Vector3(2, 2, 2);
int components = 3;
int vertexPerModel = 24;
float[] data = new float[dataArray.length * components * vertexPerModel];
for(int i = 0; i < dataArray.length; ++i){
int i3 = i * components;
for(int j = 0; j < vertexPerModel; ++j) {
int j3 = j * components;
data[i3 + 0 + j3] = dataArray[i].x;
data[i3 + 1 + j3] = dataArray[i].y;
data[i3 + 2 + j3] = dataArray[i].z;
}
}
return data;
}
#Override
public void dispose() {
program.dispose();
}
#Override
public void begin(Camera camera, RenderContext context) {
this.camera = camera;
this.context = context;
program.begin();
program.setUniformMatrix(u_projTrans, camera.combined);
context.setDepthTest(GL20.GL_LEQUAL);
context.setCullFace(GL20.GL_BACK);
}
#Override
public void render(Renderable renderable) {
program.setUniformMatrix(u_worldTrans, renderable.worldTransform);
//tutorial's logic to pass uniform
DoubleColorAttribute attribute = ((DoubleColorAttribute) renderable.material.get(DoubleColorAttribute.DiffuseUV));
program.setUniformf(u_colorU, attribute.color1.r, attribute.color1.g, attribute.color1.b);
program.setUniformf(u_colorV, attribute.color2.r, attribute.color2.g, attribute.color2.b);
//passing my custom attributes to the vertex shader
program.setVertexAttribute(a_coord, 3, Gdx.gl20.GL_FLOAT, false, 0, mAttributeBuffer);
renderable.mesh.render(program, renderable.primitiveType,
renderable.meshPartOffset, renderable.meshPartSize);
}
#Override
public void end() {
program.end();
}
#Override
public int compareTo(Shader other) {
return 0;
}
#Override
public boolean canRender(Renderable renderable) {
return renderable.material.has(DoubleColorAttribute.DiffuseUV);
}
}
Finally I was able to pass a custom attribute to vertex shader! Thanks a lot to #Xoppa for pointing me in the right direction.
This is the working solution I've got so far (I'm open for any further advices on how to implement it in a more elegant way):
First of all, as Xoppa stated in the comment it's required to create a model providing custom vertex structure while building it. So model creation may look like this:
VertexAttribute posAttr = new VertexAttribute(VertexAttributes.Usage.Position, 3, ShaderProgram.POSITION_ATTRIBUTE);
...
VertexAttribute customVertexAttr = new VertexAttribute(512, 3, "a_custom");
VertexAttributes vertexAttributes = new VertexAttributes(
posAttr,
...
customVertexAttr);
ModelBuilder modelBuilder = new ModelBuilder();
modelBuilder.begin();
modelBuilder.
part("box", GL20.GL_TRIANGLES, vertexAttributes, new Material()).
box(1f, 1f, 1f);
model = modelBuilder.end();
Or the same with MeshBuilder:
MeshBuilder meshBuilder = new MeshBuilder();
VertexAttributes vertexAttributes = new VertexAttributes(...);
meshBuilder.begin(vertexAttributes);
meshBuilder.part("box", GL20.GL_TRIANGLES);
meshBuilder.setColor(color);
meshBuilder.box(1f, 1f, 1f);
Mesh mesh = meshBuilder.end();
This code will create model with vertices containing additional data according to the provided attributes. It's time to fill the corresponding vertex array. You need a mesh for this - it stores vertices array - a flat array of packed attributes one after another vertex by vertex. So what you need is a number of attributes per vertex as well as an offset for attribute which needs to be modified. Mesh stores all that data:
Mesh mesh = model.meshes.get(0);
int numVertices = mesh.getNumVertices();
// vertex size and offset are in byte so we need to divide it by 4
int vertexSize = mesh.getVertexAttributes().vertexSize / 4;
//it's possible to use usage int here passed previously to VertexAttribute constructor.
VertexAttribute customAttribute = mesh.getVertexAttribute(512)
int offset = customAttribute.offset / 4;
float[] vertices = new float[numVertices * vertexSize];
mesh.getVertices(vertices);
We are ready to pass the data:
List<Vector3> customData ...
for(int i = 0; i < numVertices; ++i){
int index = i * vertexSize + offset;
vertices[index + 0] = customData.get(i).x;
vertices[index + 1] = customData.get(i).y;
vertices[index + 2] = customData.get(i).z;
}
And don't forget to pass the updated vertices array back to the mesh:
mesh.updateVertices(0, vertices);
That's it.
Here's also an implementation of a helper method to create a mix of default attributes using Usage flags alongside with custom attributes:
private VertexAttributes createMixedVertexAttribute(int defaultAtributes, List<VertexAttribute> customAttributes){
VertexAttributes defaultAttributes = MeshBuilder.createAttributes(defaultAtributes);
List<VertexAttribute> attributeList = new ArrayList<VertexAttribute>();
for(VertexAttribute attribute: defaultAttributes){
attributeList.add(attribute);
}
attributeList.addAll(customAttributes);
VertexAttribute[] typeArray = new VertexAttribute[0];
VertexAttributes mixedVertexAttributes = new VertexAttributes(attributeList.toArray(typeArray));
return mixedVertexAttributes;
}
The full source:
public class ProtoGame implements ApplicationListener {
private static final int CUSTOM_ATTRIBUTE_USAGE = 512;
public ProtoGame()
{
super();
}
public PerspectiveCamera cam;
public CameraInputController camController;
public Model model;
public Array<ModelInstance> instances = new Array<ModelInstance>();
public ModelBatch modelBatch;
#Override
public void create () {
cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
cam.position.set(0f, 8f, 8f);
cam.lookAt(0, 0, 0);
cam.near = 1f;
cam.far = 300f;
cam.update();
camController = new CameraInputController(cam);
Gdx.input.setInputProcessor(camController);
Model model = createModelWithCustomAttributes();
Mesh mesh = model.meshes.get(0);
setCustomAttributeData(mesh);
Color colorU = new Color(), colorV = new Color();
for (int x = -5; x <= 5; x+=2) {
for (int z = -5; z<=5; z+=2) {
ModelInstance instance = new ModelInstance(model, x, 0, z);
TestShader.DoubleColorAttribute attr = new TestShader.DoubleColorAttribute(TestShader.DoubleColorAttribute.DiffuseUV,
colorU.set((x+5f)/10f, 1f - (z+5f)/10f, 0, 1),
colorV.set(1f - (x+5f)/10f, 0, (z+5f)/10f, 1));
instance.materials.get(0).set(attr);
instances.add(instance);
}
}
modelBatch = new ModelBatch(new BaseShaderProvider() {
#Override
protected Shader createShader(Renderable renderable) {
return new TestShader();
}
});
}
#Override
public void render () {
camController.update();
Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
modelBatch.begin(cam);
for (ModelInstance instance : instances)
modelBatch.render(instance);
modelBatch.end();
}
private Model createModelWithCustomAttributes() {
int defaultAttributes = VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal | VertexAttributes.Usage.TextureCoordinates;
VertexAttribute customVertexAttr = new VertexAttribute(CUSTOM_ATTRIBUTE_USAGE, 3, "a_custom");
List<VertexAttribute> customAttributeList = new ArrayList<VertexAttribute>();
customAttributeList.add(customVertexAttr);
VertexAttributes vertexAttributes = createMixedVertexAttribute(defaultAttributes, customAttributeList);
ModelBuilder modelBuilder = new ModelBuilder();
modelBuilder.begin();
modelBuilder.
part("box", GL20.GL_TRIANGLES, vertexAttributes, new Material()).
box(1f, 1f, 1f);
return modelBuilder.end();
}
private void setCustomAttributeData(Mesh mesh) {
int numVertices = mesh.getNumVertices();
int vertexSize = mesh.getVertexAttributes().vertexSize / 4;
int offset = mesh.getVertexAttribute(CUSTOM_ATTRIBUTE_USAGE).offset / 4;
float[] vertices = new float[numVertices * vertexSize];
mesh.getVertices(vertices);
for(int i = 0; i < numVertices; ++i){
int index = i * vertexSize + offset;
vertices[index + 0] = i;
vertices[index + 1] = i;
vertices[index + 2] = i;
}
mesh.updateVertices(0, vertices);
}
#Override
public void dispose () {
model.dispose();
modelBatch.dispose();
}
private VertexAttributes createMixedVertexAttribute(int defaultAtributes, List<VertexAttribute> customAttributes){
VertexAttributes defaultAttributes = MeshBuilder.createAttributes(defaultAtributes);
List<VertexAttribute> attributeList = new ArrayList<VertexAttribute>();
for(VertexAttribute attribute: defaultAttributes){
attributeList.add(attribute);
}
attributeList.addAll(customAttributes);
VertexAttribute[] typeArray = new VertexAttribute[0];
VertexAttributes mixedVertexAttributes = new VertexAttributes(attributeList.toArray(typeArray));
return mixedVertexAttributes;
}
#Override
public void resize(int width, int height) {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
}
Vertex shader:
attribute vec3 a_position;
attribute vec3 a_normal;
attribute vec2 a_texCoord0;
uniform mat4 u_worldTrans;
uniform mat4 u_projTrans;
varying vec2 v_texCoord0;
attribute vec3 a_custom;
void main() {
v_texCoord0 = a_texCoord0;
float posX = a_position.x + a_custom.x;
float posY = a_position.y + a_custom.y;
float posZ = a_position.z + a_custom.z;
gl_Position = u_projTrans * u_worldTrans * vec4(posX, posY, posZ, 1.0);
}
Fragment shader
#ifdef GL_ES
precision mediump float;
#endif
uniform vec3 u_colorU;
uniform vec3 u_colorV;
varying vec2 v_texCoord0;
void main() {
gl_FragColor = vec4(v_texCoord0.x * u_colorU + v_texCoord0.y * u_colorV, 1.0);
}
Based on Xoppa's comment, I managed to get custom attributes working. It's actually so simple! Here is an ES 3.0 example:
#version 300 es
//v30.glsl
in vec3 vertexPosition;
in vec3 vertexColor;
out vec3 outColor;
void main()
{
outColor = vertexColor;
gl_Position = vec4(vertexPosition, 1.0);
}
fragment shader:
#version 300 es
//f30.glsl
precision mediump float;
in vec3 outColor;
out vec4 fragColor;
void main()
{
fragColor = vec4(outColor, 1.0);
}
Gl30Mesh.java
package com.example.jgles;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL30;
import com.badlogic.gdx.graphics.Mesh;
import com.badlogic.gdx.graphics.VertexAttribute;
import com.badlogic.gdx.graphics.VertexAttributes;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
public class Gl30Mesh extends ApplicationAdapter
{
private Mesh triangleMesh;
private ShaderProgram passShader;
#Override
public void create()
{
final float [] combinedData = {
-0.8f, -0.8f, 0.0f, 1.0f, 0.0f, 0.0f,
0.8f, -0.8f, 0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.8f, 0.0f, 0.0f, 0.0f, 1.0f
};
VertexAttribute vertexChannel = new VertexAttribute(VertexAttributes.Usage.Generic, 3, "vertexPosition");
VertexAttribute colorChannel = new VertexAttribute(VertexAttributes.Usage.Generic, 3, "vertexColor");
String vertexShader = Gdx.files.internal("v30.glsl").readString();
String fragmentShader = Gdx.files.internal("f30.glsl").readString();
passShader = new ShaderProgram(vertexShader, fragmentShader);
if (!passShader.isCompiled()){
throw new IllegalStateException(passShader.getLog());
}
passShader.bind();
triangleMesh = new Mesh(true, 3, 0, vertexChannel, colorChannel);
triangleMesh.setVertices(combinedData);
}
#Override
public void render()
{
Gdx.gl.glViewport(0, 0, 640, 480);
Gdx.gl.glClearColor(0, 0, 1.0f, 0);
Gdx.gl.glClear(GL30.GL_COLOR_BUFFER_BIT);
triangleMesh.render(passShader, GL30.GL_TRIANGLES);
}
}
It seems to me when you are already using custom shaders, you use whatever is named in your shaders instead of POSITION_ATTRIBUTE, NORMAL_ATTRIBUTE etc. And as Xoppa said, ShaderProgram.setVertexAttribute simply won't work.
Actually it is possible to call setVertexAttributes directly and have it work without needing a mesh. Just need to extend ShaderProgram like so
public class ShaderProgramExtended extends ShaderProgram {
public ShaderProgramExtended(String v, String f){
super(v,f);
}
/*
This is VERY NAUGHTY. Mario and Nathan probably made it private for a reason
*/
public int getProgram(){
int result;
try{
Field field = ShaderProgram.class.getDeclaredField("program");
field.setAccessible(true);
Object value = field.get(this);
field.setAccessible(false);
if (value == null) {
result = 0;
}else{
result = (Integer) value;
}
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
return result;
}
public void begin(int program){
Gdx.gl20.glUseProgram(program);
}
public void draw(int mode, int first, int count){
Gdx.gl20.glDrawArrays(mode,first,count);
}
}
And then call it as you would normally with the "minor" change of sending the shader object integer to the new begin method
public class TestlibGDXv2 extends ApplicationAdapter {
private final String tag = (this).getClass().getSimpleName();
String message = "";
private static final int FLOAT_BYTES = 4;
FloatBuffer vertexData;
FloatBuffer colorData;
ShaderProgramExtended shader;
private static final String COLOR_ATTRIBUTE = ShaderProgram.COLOR_ATTRIBUTE;
private int aColourLocation;
private static final String POSITION_ATTRIBUTE = ShaderProgram.POSITION_ATTRIBUTE;
private int aPositionLocation;
/*
Anti-clockwise winding order. Note, we could share two of the vertices. Haven't for clarity.
*/
float[] vertices = {
-0.5f, -0.5f,
0.5f, -0.5f,
-0.5f, 0.5f,
-0.5f, 0.5f,
0.5f, -0.5f,
0.5f, 0.5f
};
/*
Need to colour each vertex, so need 6.
*/
float[] colors = {1.0f, 0.0f, 0.0f, 1.0f,
0.0f,1.0f,0.0f,1.0f,
0.0f,0.0f,1.0f,1.0f,
0.0f,0.0f,1.0f,1.0f,
0.0f,1.0f,0.0f,1.0f,
1.0f,0.0f,0.0f,1.0f
};
#Override
public void create() {
/*
Convert from Dalvik VM to OpenGL native
*/
vertexData = ByteBuffer.allocateDirect(vertices.length * FLOAT_BYTES)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
colorData = ByteBuffer.allocateDirect(colors.length * FLOAT_BYTES)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
vertexData.put(vertices).position(0);
colorData.put(colors).position(0);
initialiseShaders();
}
#Override
public void render() {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
shader.begin(shader.getProgram());
shader.setVertexAttribute(aPositionLocation, 2, GL20.GL_FLOAT, false, 0, vertexData);
shader.enableVertexAttribute(aPositionLocation);
shader.setVertexAttribute(aColourLocation, 4, GL20.GL_FLOAT, false, 0, colorData);
shader.enableVertexAttribute(aColourLocation);
shader.draw(GL20.GL_TRIANGLES, 0, 6);
shader.end();
}
private void initialiseShaders() {
String vertexShaderSource =
"#version 130\n" +
"attribute vec4 " + POSITION_ATTRIBUTE + ";\n" + // x,y,z and w
"attribute vec4 " + COLOR_ATTRIBUTE + ";\n" + // r,g,b and a
"varying vec4 v_color;\n" + // pass to fragment shader
"void main(){\n" +
" v_color = "+ COLOR_ATTRIBUTE + ";\n" +
" gl_Position = " + POSITION_ATTRIBUTE + ";\n" +
"}";
String fragmentShaderSource =
"#version 130\n" +
"#ifdef GL_ES\n" +
" precision mediump float;\n" + // medium a good balance between speed and quality
"#endif\n" +
"varying vec4 v_color;\n" + // incoming from vertex shader
"void main(){\n" +
" gl_FragColor = v_color;\n" +
"}";
shader = new ShaderProgramExtended(vertexShaderSource,fragmentShaderSource);
aPositionLocation = shader.getAttributeLocation(POSITION_ATTRIBUTE);
aColourLocation = shader.getAttributeLocation(COLOR_ATTRIBUTE);
}
}
The attribute "a_color" (from ShaderProgram.COLOR_ATTRIBUTE) could just as easily have been "a_custom".
I used this method when I was trying to learn OpenGL ES from WebGL books - there are a lot more of them than libGDX OpenGL books. However, only use the above for learning, the shader is now no longer being managed by libGDX and so will not work nicely on Android when context is lost. OK for desktop, though.
Cheers
John

Libgdx collision using box2d working for desktop but failed to collide in Android emulator

My code for collision of a dynamic body to a static body works perfectly for desktop , but when running in Android emulator it can not able to detect collision ,Dynamic body goes down without collision.
I used Libgdx version : 1.5.0 .
Code :
package com.kg.game;
import java.util.Random;
import aurelienribon.tweenengine.BaseTween;
import aurelienribon.tweenengine.Tween;
import aurelienribon.tweenengine.TweenCallback;
import aurelienribon.tweenengine.TweenManager;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputAdapter;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.Texture.TextureFilter;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.physics.box2d.CircleShape;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;
public class MyGdxGame extends ApplicationAdapter implements InputProcessor {
private static final float VIEWPORT_WIDTH = 10;
private static final float BALL_RADIUS = 0.15f;
private static final int MAX_BALLS = 200;
private World world;
private Body[] ballModels;
private Texture bottleTexture;
private Texture ballTexture;
private Sprite[] ballSprites;
private Texture whiteTexture;
private Sprite groundSprite;
private SpriteBatch batch;
private BitmapFont font;
private OrthographicCamera camera;
private final Random rand = new Random();
float w;
float h;
Box2DDebugRenderer debugRenderer;
private final TweenManager tweenManager = new TweenManager();
#Override
public void create() {
debugRenderer = new Box2DDebugRenderer();
world = new World(new Vector2(0, -10), true);
createGround();
createBalls();
batch = new SpriteBatch();
font = new BitmapFont();
font.setColor(Color.BLACK);
w = Gdx.graphics.getWidth();
h = Gdx.graphics.getHeight();
camera = new OrthographicCamera(VIEWPORT_WIDTH, VIEWPORT_WIDTH * h / w);
camera.position.set(0, (VIEWPORT_WIDTH * h / w) / 2, 0);
camera.update();
createSprites();
Gdx.input.setInputProcessor(new InputAdapter() {
#Override
public boolean touchDown(int x, int y, int pointer, int button) {
restart();
return true;
}
});
restart();
}
private void createGround() {
BodyDef bd = new BodyDef();
bd.position.set(0, 0);
bd.type = BodyType.StaticBody;
PolygonShape shape = new PolygonShape();
shape.setAsBox(100, 4);
FixtureDef fd = new FixtureDef();
fd.density = 1;
fd.friction = 0.5f;
fd.restitution = 0.5f;
fd.shape = shape;
world.createBody(bd).createFixture(fd);
shape.dispose();
}
private void createBalls() {
BodyDef ballBodyDef = new BodyDef();
ballBodyDef.type = BodyType.DynamicBody;
CircleShape shape = new CircleShape();
shape.setRadius(BALL_RADIUS);
FixtureDef fd = new FixtureDef();
fd.density = 1;
fd.friction = 0.5f;
fd.restitution = 0.5f;
fd.shape = shape;
ballModels = new Body[MAX_BALLS];
for (int i = 0; i < MAX_BALLS; i++) {
ballModels[i] = world.createBody(ballBodyDef);
ballModels[i].createFixture(fd);
}
shape.dispose();
}
private void createSprites() {
ballTexture = new Texture(Gdx.files.internal("ball.png"));
ballTexture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
ballSprites = new Sprite[MAX_BALLS];
for (int i = 0; i < MAX_BALLS; i++) {
ballSprites[i] = new Sprite(ballTexture);
ballSprites[i].setSize(BALL_RADIUS * 2, BALL_RADIUS * 2);
ballSprites[i].setOrigin(BALL_RADIUS, BALL_RADIUS);
}
whiteTexture = new Texture(Gdx.files.internal("ground.png"));
groundSprite = new Sprite(whiteTexture);
groundSprite.setSize(100, 4);
groundSprite.setPosition(-VIEWPORT_WIDTH / 2, 0);
groundSprite.setColor(Color.BLACK);
}
private float elapsed = 0;
#Override
public void render() {
tweenManager.update(1 / 60f);
world.step(1 / 60f, 10, 10);
debugRenderer.render(world, camera.combined);
float w = Gdx.graphics.getWidth();
float h = Gdx.graphics.getHeight();
GL20 gl = Gdx.gl20;
gl.glClearColor(1, 1, 1, 1);
gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
// Update
for (int i = 0; i < MAX_BALLS; i++) {
Vector2 ballPos = ballModels[i].getPosition();
ballSprites[i].setPosition(ballPos.x - ballSprites[i].getWidth()
/ 2, ballPos.y - ballSprites[i].getHeight() / 2);
ballSprites[i].setRotation(ballModels[i].getAngle()
* MathUtils.radiansToDegrees);
}
// Render
batch.setProjectionMatrix(camera.combined);
batch.begin();
groundSprite.draw(batch);
for (int i = 0; i < MAX_BALLS; i++)
ballSprites[i].draw(batch);
batch.end();
// batch.getProjectionMatrix().setToOrtho2D(0, 0, w, h);
batch.begin();
font.draw(batch, "Touch the screen to restart", 5, h - 5);
batch.end();
}
#Override
public void dispose() {
bottleTexture.dispose();
ballTexture.dispose();
batch.dispose();
font.dispose();
world.dispose();
}
#Override
public boolean keyDown(int keycode) {
return false;
}
#Override
public boolean keyUp(int keycode) {
return true;
}
#Override
public boolean keyTyped(char character) {
return false;
}
#Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
return true;
}
#Override
public boolean touchUp(int screenX, int screenY, int pointer, int button) {
return false;
}
#Override
public boolean touchDragged(int screenX, int screenY, int pointer) {
return false;
}
#Override
public boolean mouseMoved(int screenX, int screenY) {
return false;
}
#Override
public boolean scrolled(int amount) {
return false;
}
private void restart() {
Vector2 vec = new Vector2();
for (int i = 0; i < MAX_BALLS; i++) {
float tx = rand.nextFloat() * 1.0f - 0.5f;
float ty = VIEWPORT_WIDTH * h / w;
float angle = rand.nextFloat() * MathUtils.PI * 2;
ballModels[i].setActive(false);
ballModels[i].setLinearVelocity(vec.set(0, 0));
ballModels[i].setAngularVelocity(0);
ballModels[i].setTransform(vec.set(tx, ty), angle);
}
tweenManager.killAll();
Tween.call(new TweenCallback() {
private int idx = 0;
#Override
public void onEvent(int type, BaseTween<?> source) {
if (idx < ballModels.length) {
ballModels[idx].setAwake(true);
ballModels[idx].setActive(true);
idx += 1;
}
}
}).repeat(-1, 0.1f).start(tweenManager);
}
}
Screensots :
this is an idea, looking at the proportions of your images, I think the error may be out there, I would look at these lines:
groundSprite.setSize(100, 4); //<--especially here
groundSprite.setPosition(-VIEWPORT_WIDTH / 2, 0); //<--especially here
.
private void createGround() {
BodyDef bd = new BodyDef();
bd.position.set(0, 0); //<--especially here
bd.type = BodyType.StaticBody;
PolygonShape shape = new PolygonShape();
shape.setAsBox(100, 4); //<--especially here
especially the position and size, if it is standing in the same place, with the change of screen "device".
You tried to render debug? I see out there, if so, you can see your object in the emulated device?
I hope I understand, but not if that be the error

How can I move balls in random directions and how can I keep them bouncing inside the frame?

Here is the code:
public class BallGame extends JPanel implements Runnable {
JPanel panel1 = new JPanel();
private int ballX = 10, ballY = 110, ...;
Thread aThread;
int toRight=5;
int toLeft= -5;
int upWard=5;
int downWard= -5;
int widthBall, heightBall;
public BallGame(){
game=true;
aThread=new Thread(this);
aThread.start();
}
public void paintComponent(Graphics g){
setOpaque(false);
super.paintComponent(g);
g.setColor(Color.RED);
g.fillOval(ballX, ballY, 7,7);
g.setColor(Color.BLUE);
g.fillOval(ballX + 15, ballY + 10, 7,7);
g.setColor(Color.GREEN);
g.fillOval(ballY - 10, ballY - 15, 7,7);
}
public void positionBall(int sx, int sy)
{
ballX = sx;
ballY = sy;
this.widthBall = this.getWidth();
this.heightBall = this.getHeight();
repaint();
}
public void run() {
boolean leftRight = false;
boolean upDown = false;
while(true){
if(game){
if (leftRight)
{
ballX += toRight;
if (ballX >= (widthBall - 5))
leftRight= false;
}
else
{
ballX += toLeft;
if ( ballX <= 0)
leftRight = true;
}
if (upDown)
{
ballY += upWard;
if (ballY >= (heightBall - 5))
upDown = false;
}
else
{
ballY += downWard;
if ( ballY <= 0)
upDown = true;
}
positionBall(ballX, ballY);
try
{
Thread.sleep(70);
}
catch(InterruptedException ex)
{
}
I don't know if the part where I drew the balls was right. The balls move in the same path. How can I move them in different directions and how can I limit them inside the frame? I need this for our case study immediately. Thank you for your time!
In order for the balls to move independently you need to treat them as 3 balls.
The reason why they always go the same direction, is that you use the same delta, just inverting the sign of delta x and delta y, thus you will always keep the same speed, and bounce at 90 degrees.
In the code below which is basically the same as you had, I keep the state of each ball in separate instances, change speed of delta x and delta y once a side is touched, and use the Swing Timer which is a better approach with respect to timing in Swing, as pointed out by Robin above.
I have updated the example, so that 4 balls start in the middle, and they move away from each other. This should give you enough information to adapt it to your requirements. The picture below is produced by only allowing 10 iterations, and setting
ballGame.setOpaque(true);
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.*;
public class BallGame extends JPanel {
private class Ball {
private int x;
private int y;
private int width;
private int height;
private Color color;
private boolean leftRight;
private boolean upDown;
private int deltaX;
private int deltaY;
Ball(Color color, int x, int y, int width, int height) {
this(color, x, y, width, height, false, false);
}
Ball(Color color, int x, int y, int width, int height, boolean leftRight, boolean upDown) {
this.color = color;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.leftRight = leftRight;
this.upDown = upDown;
updateDelta();
}
private void updateDelta() {
final int minimumMovement = 5;
final int maxExtra = 10;
deltaY = minimumMovement + (int) (Math.random() * maxExtra);
deltaX = minimumMovement + (int) (Math.random() * maxExtra);
}
public void positionBall() {
if (leftRight) {
x += deltaX;
if (x >= (BallGame.this.getWidth() - width / 2)) {
leftRight = false;
updateDelta();
}
} else {
x += -deltaX;
if (x <= 0) {
leftRight = true;
updateDelta();
}
}
if (upDown) {
y += deltaY;
upDown = !(y >= (BallGame.this.getHeight() - height / 2));
if (y >= (BallGame.this.getHeight() - height / 2)) {
upDown = false;
updateDelta();
}
} else {
y += -deltaY;
if (y <= 0) {
upDown = true;
updateDelta();
}
}
}
public Color getColor() {
return color;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
}
private ArrayList<Ball> balls = new ArrayList<>(3);
public BallGame() {
createBalls();
startGame();
}
private void startGame() {
int framesPerSecond = 30;
int timeToWait = 1000 / framesPerSecond;
Timer timer = new Timer(timeToWait, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (Ball ball : balls) {
ball.positionBall();
}
repaint();
}
});
timer.start();
}
private void createBalls() {
int startX = 400;
int startY = 200;
balls.add(new Ball(Color.green, startX, startY, 10, 10));
balls.add(new Ball(Color.blue, startX, startY, 15, 15, true, true));
balls.add(new Ball(Color.red, startX, startY, 20, 20, false, true));
balls.add(new Ball(Color.orange, startX, startY, 20, 20, true, false));
}
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
for (Ball ball : balls) {
g2.setColor(ball.getColor());
g2.fillOval(ball.getX(), ball.getY(), ball.getWidth(), ball.getHeight());
g2.setColor(ball.getColor().darker());
g2.drawOval(ball.getX(), ball.getY(), ball.getWidth(), ball.getHeight());
}
g2.dispose();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Ball Game");
BallGame ballGame = new BallGame();
ballGame.setOpaque(false);
frame.getContentPane().add(ballGame);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setMinimumSize(new Dimension(800, 450));
frame.setLocationRelativeTo(null); // Center
frame.pack();
frame.setVisible(true);
}
});
}
}

SwingWorker gui repaint

Im trying to use the SwingWorker to update my gui without calling repaint().
I want the SwigWorker to update the status of each GridGraphic[i][j] and the have the gui be repsonsive to the changes without calling repaint().
GridGraphic.java
import java.awt.*;
import javax.swing.*;
public class GridGraphic extends JComponent {
private int intensity = 0;
public GridGraphic() {
//setBorder(BorderFactory.createLineBorder(Color.BLUE));
}
public void paintComponent(Graphics g) {
//paints the GridGraphic black
if (intensity == 0){
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillRect(0, 0, getWidth(), getHeight());
}
//paints the GridGraphic black with a yellow dot
else if (intensity == 5){
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.YELLOW);
g.fillOval(3, 3, getWidth()/2, getHeight()/2);
}
}
public Dimension getPreferredSize() {
return new Dimension(10, 10);
}
public void setAgent(){
intensity = 5;
}
public void setDefault(){
intensity = 0;
}
public int getIntensity(){
return intensity;
}
}

Trying To Render Scene To FBO Returns White

So this is what I'm trying to do. I'm trying to make a 2d game using LWJGL that has a dynamically generated terrain using images as tiles. It works fine, but when I try to do a zoom, the tiles get a dark shade on the edges and I get an extra pixel in-between tiles. Here's a screenshot of before and after zoom:
Before and After Zoom Screenshot: http://postimage.org/image/rhuc9744/
I've looked and looked, and from what little I've gathered on the internet, I think the gist is that zoom is causing pixel-precision issues which gets worse after blending. I saw a blog saying I can overlap some pixels between tiles, but it seems too complicated. I've tried all blending options, and none of them worked. So I figured, I should just render all the tiles into a buffer, an FBO, and then apply it as one big texture, so even when I zoom, I won't see dark edges because it would all be just one giant picture. So I've read a lot of tutorials on FBO, but most if not all of them are meant for 3D, which I actually got working. The problem is when I apply it to 2d ortho. The code breaks and all I get is a white box. I've been stuck on this problem for days, and I can't seem to find an answer using google. Here's a sample code I'm t trying to get working:
package com.helgravis;
import static org.lwjgl.opengl.EXTFramebufferObject.GL_COLOR_ATTACHMENT0_EXT;
import static org.lwjgl.opengl.EXTFramebufferObject.GL_DEPTH_ATTACHMENT_EXT;
import static org.lwjgl.opengl.EXTFramebufferObject.GL_FRAMEBUFFER_EXT;
import static org.lwjgl.opengl.EXTFramebufferObject.GL_RENDERBUFFER_EXT;
import static org.lwjgl.opengl.EXTFramebufferObject.glBindFramebufferEXT;
import static org.lwjgl.opengl.EXTFramebufferObject.glBindRenderbufferEXT;
import static org.lwjgl.opengl.EXTFramebufferObject.glFramebufferRenderbufferEXT;
import static org.lwjgl.opengl.EXTFramebufferObject.glFramebufferTexture2DEXT;
import static org.lwjgl.opengl.EXTFramebufferObject.glGenFramebuffersEXT;
import static org.lwjgl.opengl.EXTFramebufferObject.glGenRenderbuffersEXT;
import static org.lwjgl.opengl.EXTFramebufferObject.glRenderbufferStorageEXT;
import static org.lwjgl.opengl.GL11.GL_INT;
import static org.lwjgl.opengl.GL11.GL_RGBA;
import static org.lwjgl.opengl.GL11.GL_RGBA8;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D;
import static org.lwjgl.opengl.GL11.glBindTexture;
import static org.lwjgl.opengl.GL11.glGenTextures;
import static org.lwjgl.opengl.GL11.glTexImage2D;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.Hashtable;
import javax.swing.JPanel;
import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL14;
public class AnFBOExample
{
private int width, height, canvasWidth = 800, canvasHeight = 600;
private String title;
private boolean bFullscreen;
public int FRAMERATE = 60;
public int framebufferID;
public int framebufferTextureID, spriteTextureID;
public int depthRenderBufferID;
public int fboWidth = 100, fboHeight = 100;
public AnFBOExample()
{
bFullscreen = false;
}
public AnFBOExample(boolean bFullscreen)
{
this.bFullscreen = bFullscreen;
}
public void setTitle(String title)
{
this.title = title;
if(Display.isCreated())
Display.setTitle(title);
}
public String getTitle()
{
return title;
}
public void setResolution(int x, int y)
{
width = x;
height = y;
}
public void setCanvasSize(int x, int y)
{
canvasWidth = x;
canvasHeight = y;
}
private boolean initDisplayMode() throws Exception
{
if(bFullscreen)
Display.setFullscreen(true);
try
{
DisplayMode[] dm = org.lwjgl.util.Display.getAvailableDisplayModes(width, height, -1, -1, -1, -1, 60, 60);
org.lwjgl.util.Display.setDisplayMode(dm, new String[] {
"width=" + width,
"height=" + height,
"freq=" + FRAMERATE,
"bpp="+ org.lwjgl.opengl.Display.getDisplayMode().getBitsPerPixel()
}
);
return true;
}
catch(Exception e)
{
e.printStackTrace();
System.out.println("Unable to enter fullscreen, continuing in windowed mode");
}
return false;
}
public void init() throws Exception
{
try
{
initDisplayMode();
Display.create();
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glEnable(GL11.GL_ALPHA_TEST);
GL11.glAlphaFunc(GL11.GL_GREATER, 0.5f);
GL11.glDisable(GL11.GL_DEPTH_TEST);
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
GL11.glOrtho(0, canvasWidth, canvasHeight, 0, -1, 1);
int framebufferID = glGenFramebuffersEXT();
int colorTextureID = glGenTextures();
int depthRenderBufferID = glGenRenderbuffersEXT();
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebufferID);
glBindTexture(GL_TEXTURE_2D, colorTextureID);
GL11.glTexParameteri(GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, fboWidth, fboHeight, 0, GL_RGBA, GL_INT, (java.nio.ByteBuffer) null);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,GL_COLOR_ATTACHMENT0_EXT,GL_TEXTURE_2D, colorTextureID, 0);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthRenderBufferID);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL14.GL_DEPTH_COMPONENT24, fboWidth, fboHeight);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,GL_DEPTH_ATTACHMENT_EXT,GL_RENDERBUFFER_EXT, depthRenderBufferID);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
BufferedImage image = loadImage("resources/lamp.png");
spriteTextureID = getTexture(image);
}
catch(LWJGLException le)
{
le.printStackTrace();
}
}
public void draw()
{
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebufferID);
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
GL11.glPushMatrix();
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, spriteTextureID);
GL11.glBegin(GL11.GL_QUADS);
{
GL11.glTexCoord2f(0, 0);
GL11.glVertex2f(0, 0);
GL11.glTexCoord2f(0, 1f);
GL11.glVertex2f(0, 50f);
GL11.glTexCoord2f(1f, 1f);
GL11.glVertex2f(50f, 50f);
GL11.glTexCoord2f(1f, 0);
GL11.glVertex2f(50f, 0);
}
GL11.glEnd();
GL11.glPopMatrix();
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
GL11.glPushMatrix();
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, framebufferTextureID);
GL11.glTranslatef(100f, 100f, 0);
GL11.glBegin(GL11.GL_QUADS);
{
GL11.glTexCoord2f(0, 0);
GL11.glVertex2f(0, 0);
GL11.glTexCoord2f(0, 1f);
GL11.glVertex2f(0, 100f);
GL11.glTexCoord2f(1f, 1f);
GL11.glVertex2f(100f, 100f);
GL11.glTexCoord2f(1f, 0);
GL11.glVertex2f(100f, 0);
}
GL11.glEnd();
GL11.glPopMatrix();
Display.update();
Display.sync(FRAMERATE);
}
public void draw2()
{
// glBindTexture(GL_TEXTURE_2D, 0);
// glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebufferID);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
GL11.glPushMatrix();
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, spriteTextureID);
GL11.glBegin(GL11.GL_QUADS);
{
GL11.glTexCoord2f(0, 0);
GL11.glVertex2f(0, 0);
GL11.glTexCoord2f(0, 1f);
GL11.glVertex2f(0, 50f);
GL11.glTexCoord2f(1f, 1f);
GL11.glVertex2f(50f, 50f);
GL11.glTexCoord2f(1f, 0);
GL11.glVertex2f(50f, 0);
}
GL11.glEnd();
GL11.glPopMatrix();
// glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
// GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
// GL11.glPushMatrix();
// GL11.glEnable(GL11.GL_TEXTURE_2D);
// GL11.glBindTexture(GL11.GL_TEXTURE_2D, framebufferTextureID);
// GL11.glTranslatef(100f, 100f, 0);
// GL11.glBegin(GL11.GL_QUADS);
// {
// GL11.glTexCoord2f(0, 0);
// GL11.glVertex2f(0, 0);
// GL11.glTexCoord2f(0, 1f);
// GL11.glVertex2f(0, 100f);
// GL11.glTexCoord2f(1f, 1f);
// GL11.glVertex2f(100f, 100f);
// GL11.glTexCoord2f(1f, 0);
// GL11.glVertex2f(100f, 0);
// }
// GL11.glEnd();
// GL11.glPopMatrix();
//
// Display.update();
// Display.sync(FRAMERATE);
}
public void cleanup()
{
Display.destroy();
}
public void run()
{
while(!Thread.interrupted())
{
progress();
handleEvents();
draw();
//draw2();
}
}
public void handleEvents()
{
while(Keyboard.next())
if(Keyboard.getEventKey() == Keyboard.KEY_ESCAPE)
quit();
if(Display.isCloseRequested())
quit();
}
public void quit()
{
System.exit(0);
}
public void progress()
{
//Add game logic here
}
protected static boolean waitForImage(Image image, Component c)
{
Image[] images = new Image[1];
images[0] = image;
return waitForImages(images, c);
}
protected static boolean waitForImages(Image[] images, Component c)
{
MediaTracker tracker = new MediaTracker(c);
for(int i=0; i<images.length; i++)
tracker.addImage(images[i], 0);
try
{
tracker.waitForAll();
}
catch(InterruptedException ie) {}
return !tracker.isErrorAny();
}
public static BufferedImage loadImage(String imageFile) throws Exception
{
Image image = null;
JPanel buffer = new JPanel();
image = buffer.getToolkit().getImage(imageFile);
waitForImage(image, buffer);
int width = image.getWidth(null);
int height = image.getHeight(null);
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = bufferedImage.createGraphics();
g2d.drawImage(image, 0, 0, width, height, buffer);
g2d.dispose();
return bufferedImage;
}
public int getTexture(BufferedImage image) throws Exception
{
return getTexture(image,
GL11.GL_TEXTURE_2D,
GL11.GL_RGBA,
GL11.GL_LINEAR,
GL11.GL_LINEAR);
}
public int getTexture(String resourceName,
int target,
int dstPixelFormat,
int minFilter,
int magFilter) throws Exception
{
BufferedImage bufferedImage = loadImage(resourceName);
return getTexture(bufferedImage, target, dstPixelFormat, minFilter, magFilter);
}
private int createTextureID()
{
ByteBuffer temp = ByteBuffer.allocateDirect(4 * 1);
temp.order(ByteOrder.nativeOrder());
IntBuffer tmp = temp.asIntBuffer();
GL11.glGenTextures(tmp);
return tmp.get(0);
}
public int getTexture(BufferedImage bufferedImage,
int target,
int dstPixelFormat,
int minFilter,
int magFilter) throws Exception
{
int srcPixelFormat = 0;
int textureID = createTextureID();
GL11.glBindTexture(target, textureID);
if(bufferedImage.getColorModel().hasAlpha())
srcPixelFormat = GL11.GL_RGBA;
else
srcPixelFormat = GL11.GL_RGB;
ByteBuffer textureBuffer = convertImageData(bufferedImage);
if(target == GL11.GL_TEXTURE_2D)
{
GL11.glTexParameteri(target, GL11.GL_TEXTURE_MIN_FILTER, minFilter);
GL11.glTexParameteri(target, GL11.GL_TEXTURE_MAG_FILTER, magFilter);
}
GL11.glTexImage2D(target,
0,
dstPixelFormat,
get2Fold(bufferedImage.getWidth()),
get2Fold(bufferedImage.getHeight()),
0,
srcPixelFormat,
GL11.GL_UNSIGNED_BYTE,
textureBuffer);
return textureID;
}
private int get2Fold(int fold)
{
int ret = 2;
while (ret < fold)
ret *= 2;
return ret;
}
#SuppressWarnings("rawtypes")
private ByteBuffer convertImageData(BufferedImage bufferedImage)
{
ComponentColorModel glAlphaColorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
new int[] {8,8,8,8},
true,
false,
ComponentColorModel.TRANSLUCENT,
DataBuffer.TYPE_BYTE);
ComponentColorModel glColorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
new int[] {8,8,8,0},
false,
false,
ComponentColorModel.OPAQUE,
DataBuffer.TYPE_BYTE);
ByteBuffer imageBuffer = null;
WritableRaster raster;
BufferedImage texImage;
int texWidth = 2;
int texHeight = 2;
while (texWidth < bufferedImage.getWidth())
texWidth *= 2;
while (texHeight < bufferedImage.getHeight())
texHeight *= 2;
if(bufferedImage.getColorModel().hasAlpha())
{
raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, texWidth, texHeight, 4, null);
texImage = new BufferedImage(glAlphaColorModel, raster, false, new Hashtable());
}
else
{
raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, texWidth, texHeight, 3, null);
texImage = new BufferedImage(glColorModel, raster, false, new Hashtable());
}
Graphics g = texImage.getGraphics();
g.setColor(new Color(0f,0f,0f,0f));
g.fillRect(0,0,texWidth,texHeight);
g.drawImage(bufferedImage,0,0,null);
byte[] data = ((DataBufferByte) texImage.getRaster().getDataBuffer()).getData();
imageBuffer = ByteBuffer.allocateDirect(data.length);
imageBuffer.order(ByteOrder.nativeOrder());
imageBuffer.put(data, 0, data.length);
imageBuffer.flip();
return imageBuffer;
}
public static void main(String args[]) throws Exception
{
AnFBOExample window = new AnFBOExample(false);
window.setResolution(800, 600);
window.setCanvasSize(800, 600);
window.init();
window.setTitle("FBO Test");
window.run();
}
}
The code loads an image in resource/lamp.png, which can be any .png file, and tries to render it to an FBO, then applies it as a texture in a 2d quad. For some reason, I only get a white blank quad when I try to bind the FBO as a texture. I'm not sure if I'm not rendering to the FBO properly, or I'm not binding it correctly. You can check the method draw() so you know what I'm talking about. Like I said, I've been stuck in this problem for day, so any help would be very much appreciated.
resources/lamp.png: http://s3.postimage.org/rhpdn5ms/lamp.png?noCache=1315464566
framebufferTextureID, shouldn't it be colourTextureID?