Synchronize the paint() method of a Java Canvas instance? - swing

Basically I am running a java emulator at 60 FPS and each time the screen refreshes it draws pixels from an array and paints it to the screen. I am getting a java.util.ConcurrentModificationException i.e. the contents of the pixel array are getting modified sometime during the paint() method execution. Is there a way I can synchronize the paint method or the generatePalette method to avoid this? Or is there another/better solution?
Code:
public class LCD extends Canvas implements Runnable {
/**
*
*/
private static final long serialVersionUID = 1L;
private static final int FRAME_RATE = 1 / 60 * 1000;
private BufferedImage canvas;
private List<Pixel> pixels = new ArrayList<>();
private int tilemapStart, tilemapEnd;
private GBCpuBootRom cpu;
private GBBus bus;
public void init() {
setTileMap(bus.cpuRUnsigned8(0xFF40));
generatePalette(bus, bus.cpuRUnsigned8(0xFF40));
}
public LCD(int len, int hei, GBCpuBootRom cpu, GBBus bus) {
canvas = new BufferedImage(len,hei,BufferedImage.TYPE_INT_ARGB);
this.cpu = cpu;
this.bus = bus;
Thread t = new Thread(this);
t.start();
}
public void setTileMapExplicit(int start, int end) {
tilemapStart = start;
tilemapEnd = end;
}
public void setTileMap(int mFF40) {
/*
* Bit 3 (bit 5)
* 0: 9800 to 9BFF
* 1: 9C00 to 9FFF
*
* Bit 4 (bit 4)
* 0: 8800 to 97FF
* 1: 8000 to 8FFF
*/
if ( ((mFF40 & 0b00001000) >> 3) == 1) {
tilemapStart = 0x9C00;
tilemapEnd = 0xA000;
}
else {
tilemapStart = 0x9800;
tilemapEnd = 0x9C00;
}
}
public void paint(Graphics g) {
for(Pixel p:pixels)
try {
canvas.setRGB(p.getX(), p.getY(), p.getColor().getRGB());
}
catch(Exception e) {
/*
* If a pixel cannot be drawn
* skip it and move on to next.
*/
System.out.println("Cannot draw pixel: " + p.getX() + ", " +
p.getY());
}
draw(g);
}
private void draw(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(canvas, null, null);
}
public void generatePalette(GBBus bus, int mFF40) {
int hor = 0;
int ver = 0;
int x = 0;
int y = 0;
int inc = 8;
int pixelCount = 0;
/*
* maxRowPixels
* Each tile has 64 pixels
* if 256 x 256
* maxRowPixels = 2048 (32 * 64)
* else if 160 x 144
* maxRowPixels = 1280 (20 * 64)
*/
int maxRowPixels = canvas.getWidth() == 256 ? 2048 : 1280;
Pixel[] palettes;
Palette palette = new Palette();
for(int i = tilemapStart; i < tilemapEnd; i++) {
int dataAddr = spriteAddress((mFF40 & 0b00010000) >> 4, bus.cpuRUnsigned8(i));
/*
* Read range 0 to F
* e.g.
* 08000: 08000 to 0x800F
*
* 08000, 08002, 08004, 08006,
* 08008, 0800A, 0800C, 0800D,
*/
for(int k = 0; k < 16; k = k + 2) {
int hByte = bus.cpuRUnsigned8(dataAddr + k);
int lByte = bus.cpuRUnsigned8(dataAddr + (k + 1));
palette.setHB(hByte);
palette.setLB(lByte);
palette.setXY(x, y);
palette.init();
palettes = palette.getPixels();
for(Pixel pix: palettes)
pixels.add(pix);
x += inc;
pixelCount += inc;
if(x % 8 == 0) {
x = hor;
y++;
}
/*
* 1 tile completed
* 8 x 8 = 64p
* p means pixels
*/
if(pixelCount % 64 == 0) {
hor = hor + inc;
x = hor;
y = ver;
}
if(pixelCount % maxRowPixels == 0) {
x = 0;
hor = 0;
ver = ver + inc;
y = ver;
}
}
}
}
#Override
public void run() {
Thread.currentThread();
while(true) {
cpu.compute();
pixels.clear();
init();
try {
Thread.sleep(1000);
}
catch(Exception e) {
e.printStackTrace();
}
repaint();
}
}
}
This is the exception I get at line 66 (for loop in paint() method above)
Exception in thread "AWT-EventQueue-0" java.util.ConcurrentModificationException
at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1009)
at java.base/java.util.ArrayList$Itr.next(ArrayList.java:963)
at gameboyconcept.logo.LCD.paint(LCD.java:66)

Related

Libgdx : GestureListener - pan not working properly

I am new to libgdx and trying to move a circle vy draging it. But its not working properly.
class deceleration is as follows -
public class gScreen1 extends ScreenAdapter
implements GestureDetector.GestureListener {
private static final float WORLD_WIDTH = 640;
private static final float WORLD_HEIGHT = 480;
...
#Override
public void show() {
super.show();
camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
camera.position.set(WORLD_WIDTH / 2, WORLD_HEIGHT / 2, 0);
camera.update();
viewport = new FitViewport(WORLD_WIDTH, WORLD_HEIGHT, camera);
shapeRenderer = new ShapeRenderer();
batch = new SpriteBatch();
}
private void drawGrid() {
Gdx.gl.glLineWidth(2);
shapeRenderer.setProjectionMatrix(camera.projection);
shapeRenderer.setTransformMatrix(camera.view);
shapeRenderer.setColor(Color.YELLOW);
shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
size = points.length;
for (int i = 0; i < size; i++) {
shapeRenderer.circle(pointsr[i].x , pointsr[i].y ,10);
}
shapeRenderer.end();
}
#Override
public void render(float delta) {
super.render(delta);
clearScreen();
drawGrid();
}
#Override
public boolean pan(float x1, float y1, float deltaX, float deltaY) {
Vector3 tmpCoords = new Vector3(x1,y1, 0);
camera.unproject(tmpCoords);
int x = (int)tmpCoords.x;
int y = (int)tmpCoords.y;
for( i = 0; i < size ; i++) {
int x2 = pointsr[i].x;
int y2 = pointsr[i].y;
if( ((x2 -x)*(x2 -x) + (y2 -y)*(y2 -y) ) < 400 )
break;
}
if( i < size ) {
pointsr[i].x += deltaX;
pointsr[i].y += deltaY;
}
}
Circles are not following movement of finger, some time they move a little bit but in opposite y direction of touch movement.

LibGDX : Detecting where a Ray hits a 3D object

I am building a 3D game in LibGDX, I have used the following code to work out if I touch a object, but how do I find out at what point it collides. I am not using Bullet as I want to make a HTML5 port
public int getObject (int screenX, int screenY) {
int result = -1;
float distance = -1;
Ray ray = camera.getPickRay(screenX, screenY);
Vector3 pos = new Vector3(camera.position);
for (int i = 0; i < boxInstance.size; i++) {
GameObject instance = boxInstance.get(i);
instance.transform.getTranslation(pos);
float dist2 = ray.origin.dst2(pos);
if (distance >= 0f && dist2 > distance) continue;
if (Intersector.intersectRayBoundsFast(ray, pos, instance.dimensions)) {
result = i;
distance = dist2;
Vector3 v = new Vector3();
if (Intersector.intersectRayBounds(ray, instance.bounds, v))
{
boxInstance.get(result).materials.get(0).set(ColorAttribute.createDiffuse(Color.RED));
}
}
}
if (result > -1)
{
//boxInstance.removeIndex(result);
}
return 1;
};
The reason I need this is if I touch a large plane, I want to be able to plane a object where I touch.
Update
I found intersectRayBounds which should do what I want but it never fires
Here is my GameObject class. Maybe my BoundingBox is wrong?
public class GameObject extends ModelInstance {
public final Vector3 center = new Vector3();
public final Vector3 dimensions = new Vector3();
public static BoundingBox bounds = new BoundingBox();
public GameObject (Model model, float x, float y, float z) {
super(model, x,y,z);
calculateBoundingBox(bounds);
bounds.getCenter(center);
bounds.getDimensions(dimensions);
}
}
I am not sure why this is the fix but it appears you have to compute the Bounding box everytime. See code below.
public int getObject (int screenX, int screenY) {
int result = -1;
float distance = -1;
Ray ray = camera.getPickRay(screenX, screenY);
Vector3 pos = new Vector3(camera.position);
for (int i = 0; i < boxInstance.size; i++) {
GameObject instance = boxInstance.get(i);
instance.transform.getTranslation(pos);
instance.updateBox();
float dist2 = ray.origin.dst2(pos);
if (distance >= 0f && dist2 > distance) continue;
Vector3 v = new Vector3();
if (Intersector.intersectRayBounds(ray, instance.bounds, v))
{
result = i;
distance = dist2;
Gdx.app.log("MyTag 2", "x " + v.x + " z " + v.z);
}
}
if (result > -1)
{
boxInstance.get(result).materials.get(0).set(ColorAttribute.createDiffuse(Color.RED));
}
return 1;
};
public class GameObject extends ModelInstance {
public final Vector3 center = new Vector3();
public final Vector3 dimensions = new Vector3();
public static BoundingBox bounds = new BoundingBox();
private float x,y,z;
public GameObject (Model model, float x, float y, float z) {
super(model, x, y, z);
this.x = x;
this.y = y;
this.z = z;
updateBox();
}
public void updateBox()
{
calculateBoundingBox(bounds);
bounds.getCenter(center);
bounds.getDimensions(dimensions);
bounds.set(bounds.min.add(x,y,z), bounds.max.add(x,y,z));
Gdx.app.log("MyTag 2", "x " + bounds.min + " z " + bounds.max );
}
}

Capture Screen in libGdx

I want to capture screen in libGdx . I use this code for my problem. On Desktop it work . But when I run on my android , I can't find image capture screen. How can I fix it? Thanks for reading my question.
public class ScreenshotFactory {
private static int counter = 1;
public static void saveScreenshot() {
try {
FileHandle fh;
do {
if (Gdx.app.getType() == ApplicationType.Desktop)
Infor.linkScreenShot = "D://chupngoc" + counter + ".png";
else
Infor.linkScreenShot = Gdx.files.getExternalStoragePath()
+ counter + ".png";
Infor.nameImage = counter + ".png";
fh = new FileHandle(Infor.linkScreenShot);
counter++;
} while (fh.exists());
Pixmap pixmap = getScreenshot(0, 0, Gdx.graphics.getWidth(),
Gdx.graphics.getHeight(), true);
PixmapIO.writePNG(fh, pixmap);
pixmap.dispose();
} catch (Exception e) {
}
}
private static Pixmap getScreenshot(int x, int y, int w, int h,
boolean yDown) {
Gdx.gl.glPixelStorei(GL20.GL_PACK_ALIGNMENT, 1);
final Pixmap pixmap1 = new Pixmap(w, h, Format.RGBA8888);
ByteBuffer pixels1 = pixmap1.getPixels();
Gdx.gl.glReadPixels(x, y, w, h, GL20.GL_RGBA, GL20.GL_UNSIGNED_BYTE,
pixels1);
Pixmap pixmap = pixmap1;
if (yDown) {
// Flip the pixmap upside down
ByteBuffer pixels = pixmap.getPixels();
int numBytes = w * h * 4;
byte[] lines = new byte[numBytes];
int numBytesPerLine = w * 4;
for (int i = 0; i < h; i++) {
pixels.position((h - i - 1) * numBytesPerLine);
pixels.get(lines, i * numBytesPerLine, numBytesPerLine);
}
pixels.clear();
pixels.put(lines);
}
return pixmap;
}
}
Your problem lies here...
fh = new FileHandle(Infor.linkScreenShot);
This way of creating a FileHandle is for the desktop only and will not work on Android.
You should create a file using Gdx.files.external instead...
fh = Gdx.files.external(Infor.linkScreenShot);

Java Applet_Dubble Buffering Does Not Work

I'm coding a JApplet in Java, but dubble buffering doesn't remove the flickering!?
What should I do?
This is the important part of the code, I guess (tell me if you need to know more, please):
// Background (dubble buffering)
private Image backbuffer;
private Graphics backg;
//Init method
// Dubble-Buffering
backbuffer = createImage(getWidth(), getHeight());
backg = backbuffer.getGraphics();
backg.setColor(this.getBackground());
//Overrided update method
public void update( Graphics g ) {
g.drawImage( backbuffer, 0, 0, this );
getToolkit().sync();
}
I appreciate all help I can get! :)
I made an MCVE to give you a better insight of the problem (thanks to Andrew Thompson)!
"Main"-class:
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.Timer;
public class Test extends JApplet implements ActionListener {
JButton start;
int delay;
Timer tm;
// Falling balls
int n; // Total balls
Ball[] ball; // Array with the balls in
int score;
// The amount of balls falling at the same time (increases by one every
// 10:th score)
int ballNr;
// Comment to the game
String comment;
public void init() {
this.setLayout(null);
this.setSize(600, 500);
this.getContentPane().setBackground(Color.cyan);
this.setFocusable(true);
score = 0;
ballNr = 3;
comment = "Avoid the balls!";
// Buttons
start = new JButton("START");
add(start);
start.setBounds(0, 400, 200, 100);
start.addActionListener(this);
// The timer
delay = 12;
tm = new Timer(delay, this);
// The falling balls
n = 12; // Number of balls in total
ball = new Ball[n];
// Declaring twelve new instances of the ball-object with a
// "reference array"
for (int i = 0; i < n; i++) {
ball[i] = new Ball();
}
}
// Paint-method //
public void paint(Graphics g) {
super.paint(g);
// Every 10:th score, the game adds one ball until you reach 100 =
// win! (ballNr + (int)(score * 0.1) -> ballNr increases by one
// every 10:th score
for (int i = 0; i < ballNr + (int) (score * 0.1); i++) {
// Score can't be higher than 100
if (score < 100) {
g.setColor(ball[i].getCol());
g.fillOval(ball[i].getXLoc(), ball[i].getYLoc(),
ball[i].getSize(), ball[i].getSize());
}
}
// Draw the score and the comment
g.setColor(Color.black);
g.setFont(new Font("Arial", Font.BOLD, 24));
g.drawString("SCORE: " + score, 440, 40);
g.setColor(Color.red);
g.setFont(new Font("Arial", Font.BOLD + Font.ITALIC, 28));
g.drawString(comment, 0, 40);
}
// ACTIONLISTENER //
public void actionPerformed(ActionEvent e) {
if (e.getSource() == start) {
tm.start();
this.requestFocusInWindow(); // Make the runner component have focus
}
// Every 10:th score, the game adds one ball until you reach 100 =
// win! (ballNr + (int)(score * 0.1) -> ballNr increases by one
// every 10:th score
for (int i = 0; i < ballNr + (int) (score * 0.1); i++) {
// Score can't pass 100, because then you have won the game
if (score < 100) {
ball[i].setYLoc(ball[i].getYLoc() + ball[i].getVel());
}
}
// If any ball is out of bounds (then the score increases by one)
for (int i = 0; i < n; i++) {
if (outOfBounds(ball[i])) {
ball[i] = new Ball();
}
}
repaint();
}
// If the ball is out of the screen
public boolean outOfBounds(Ball ball) {
if (ball.getYLoc() >= 500) {
score++;
return true;
} else {
return false;
}
}
// Updates new balls
public void updateBalls() {
for (int i = 0; i < n; i++) {
ball[i] = new Ball();
}
}
}
Sub-class
import java.awt.Color;
import java.util.Random;
public class Ball {
// Private variables
private Random r; // Generating random positions and
// sizes;
private int size; // Size of the ball
private int vel; // Ball velocity
private int nrOfCol; // Color code (see ballColor-method)
private Color col;
private int xLoc;
private int yLoc;
// Constructor
public Ball() {
r = new Random();
size = r.nextInt(40) + 10; // 10px - 50 px
vel = r.nextInt(6) + 1; // Speed btw 1-5 px/delay
nrOfCol = r.nextInt(8) + 1; // Specific nr from 1-9
col = ballColor();
xLoc = r.nextInt(550);
yLoc = 0;}
public Ball(int xPos, int yPos, int size, int vel, Color col) {
this.xLoc = xPos;
this.yLoc = yPos;
this.size = size;
this.vel = vel;
this.col = col;
}
// A method to generate different colors of the balls
public Color ballColor() {
Color col;
switch (nrOfCol) {
case 1:
col = Color.black;
break;
case 2:
col = Color.red;
break;
case 3:
col = Color.green;
break;
case 4:
col = Color.yellow;
break;
case 5:
col = Color.pink;
break;
case 6:
col = Color.magenta;
break;
case 7:
col = Color.white;
break;
case 8:
col = Color.orange;
break;
case 9:
col = Color.blue;
break;
default:
col = Color.black;
// All colors except cyan as it is the background color
}
return col;
}
// Getters & setters
public int getXLoc() {
return xLoc;
}
public int getYLoc() {
return yLoc;
}
public void setYLoc(int y) {
yLoc = y;
}
public int getSize() {
return size;
}
public int getVel() {
return vel;
}
public Color getCol() {
return col;
}
}

Trying to create a circle with VBO's - LWJGL

Im trying to create a circle in LWJGL , using VBO's and VAO , and move it using an offset , but it seems one vertex is stuck in the center of the screen . I can't figure out how to move it to the new location . Any help is appreciated , thanks !
P.S : I have already tried debugging the program , but I can't locate the faulty vertex in my array
import java.nio.FloatBuffer;
import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.*;
public class Test {
// Setup variables
private int WIDTH = 800;
private int HEIGHT = 600;
private String title = "Circle";
// Quad variables
private int vbo = 0; // Vertex Buffer Object
private int vao = 0; // Vertex Array Object
int SUBDIVISIONS = 100;
float[] vertex = new float[(SUBDIVISIONS + 1) * 4];
public Test() {
// Initialize
setupOpenGL();
setupQuad();
while (!Display.isCloseRequested()) {
loop();
Display.update();
Display.sync(60);
}
Display.destroy();
}
public void setupOpenGL() {
try {
Display.setDisplayMode(new DisplayMode(WIDTH, HEIGHT));
Display.setTitle(title);
Display.create();
} catch (LWJGLException e) {
e.printStackTrace();
System.exit(-1); // If error , exit program
}
GL11.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
}
public void setupQuad() {
float r = 0.2f;
float x;
float y;
float offSetX = 0.3f;
float offSetY = 0.3f;
vertex[0] = (float) Math.sin(Math.PI*2*0/SUBDIVISIONS) * r + offSetX;
vertex[1] = (float) Math.cos(Math.PI*2*1/SUBDIVISIONS) * r + offSetY;
for (int i = 2; i < 360; i = i + 2) {
double angle = Math.PI * 2 * i / SUBDIVISIONS;
x = (float) Math.cos(angle) * r;
vertex[i] = x + offSetX;
}
for (int i = 3; i < 360; i = i + 2) {
double angle = Math.PI * 2 * i / SUBDIVISIONS;
y = (float) Math.sin(angle) * r;
vertex[i] = y + offSetY;
}
FloatBuffer vertexBuffer = BufferUtils.createFloatBuffer(vertex.length);
vertexBuffer.put(vertex);
vertexBuffer.flip();
vao = GL30.glGenVertexArrays();
GL30.glBindVertexArray(vao);
vbo = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER,vertexBuffer,GL15.GL_STATIC_DRAW);
GL20.glVertexAttribPointer(0, 2, GL11.GL_FLOAT, false, 0, 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL30.glBindVertexArray(0);
}
public void loop() {
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
GL30.glBindVertexArray(vao);
GL20.glEnableVertexAttribArray(0);
// Draw the vertices
GL11.glDrawArrays(GL11.GL_TRIANGLE_FAN, 0, vertex.length / 2);
// Put everything back to default (deselect)
GL20.glDisableVertexAttribArray(0);
GL30.glBindVertexArray(0);
}
public static void main(String[] args) {
new Test();
}
}
"I think I've found the problem . I was setting the positions of only 359 vertices out of 404 vertices (nr of subdivisions + 1 times 4) . It seems the rest of the vertices were stuck at 0,0 on the screen . Allowing both FOR statements to cycle up to 404 seems to solve the problem"