I am making a 3D game, and the way I used to draw to the screen was to add a JLabel
containing the "game screen" as a BufferedImage
to the JPanel
, and changing individual pixels using BufferedImage.setRGB()
. However, I decided to switch into drawing into the JPanel
directly using PaintComponent()
. After making the switch and not touching the texturing algorithm (outside of changes like adding the wanted pixel color into a screen buffer instead of directly changing the color, so if anything, it should be faster), it now runs really slowly (it takes about 0.3 seconds and sometimes even more to finish the texturing algorithm, with everything else being pretty much instant). Is there anyway to fix it? Also, if you see other problems with the code, feel free to let me know.
public CanvasPanel(JFrame frame, int width, int height)
{
this.frame = frame;
setPreferredSize(new Dimension(width, height));
setSize(width, height);
trianglesToDraw = new ArrayList<>();
//add(label);
keysPressed = new boolean[9];
addKeyListener(new KeyListener()
{
@Override
public void keyTyped(KeyEvent e)
{
}
@Override
public void keyPressed(KeyEvent e)
{
int key = e.getKeyCode();
if (key == KeyEvent.VK_W)
{
keysPressed[0] = true;
}
else if (key == KeyEvent.VK_A)
{
keysPressed[1] = true;
}
else if (key == KeyEvent.VK_S)
{
keysPressed[2] = true;
}
else if (key == KeyEvent.VK_D)
{
keysPressed[3] = true;
}
else if (key == KeyEvent.VK_SPACE)
{
keysPressed[4] = true;
}
}
@Override
public void keyReleased(KeyEvent e)
{
int key = e.getKeyCode();
if (key == KeyEvent.VK_W)
{
keysPressed[0] = false;
}
else if (key == KeyEvent.VK_A)
{
keysPressed[1] = false;
}
else if (key == KeyEvent.VK_S)
{
keysPressed[2] = false;
}
else if (key == KeyEvent.VK_D)
{
keysPressed[3] = false;
}
else if (key == KeyEvent.VK_SPACE)
{
keysPressed[4] = false;
}
}
});
objects = new ArrayList<>();
Hyperrectangle ground = new Hyperrectangle(new Vector(-10.0f, -10.0f, -10.0f), new Vector(10.0f, 10.0f, 10.0f), true);
objects.add(ground);
if (objects.get(0).textured)
{
try
{
objects.get(0).texture = ImageIO.read(new File(texturePath));
crosshair = ImageIO.read(new File("src/res/crosshair.png"));
}
catch (IOException e)
{
e.printStackTrace();
}
}
float aspectRatio = (float) getHeight() / (float) getWidth();
float zNear = 0.1f;
float zFar = 1000.0f;
float fov = 90.0f;
projectionMatrix = MathUtils.makeProjectionMatrix(fov, aspectRatio, zNear, zFar);
depthBuffer = new float[getWidth()][getHeight()];
screenBuffer = new int[getWidth()][getHeight()];
try
{
robot = new Robot();
}
catch (AWTException e)
{
e.printStackTrace();
}
BufferedImage cursorImg = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB);
Cursor blankCursor = Toolkit.getDefaultToolkit().createCustomCursor(cursorImg, new Point(0, 0), "blank cursor");
frame.getContentPane().setCursor(blankCursor);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int centerX = (int) screenSize.getWidth() / 2;
int centerY = (int) screenSize.getHeight() / 2;
robot.mouseMove(centerX, centerY);
for (int i = 0; i < getWidth(); i )
{
for (int j = 0; j < getHeight(); j )
{
depthBuffer[i][j] = 0.0f;
screenBuffer[i][j] = 0;
}
}
Timer timer = new Timer(30, e -> newFrame(projectionMatrix));
timer.start();
}
private void newFrame(float[][] projectionMatrix)
{
trianglesToDraw.clear();
for (int i = 0; i < getWidth(); i )
{
for (int j = 0; j < getHeight(); j )
{
depthBuffer[i][j] = 0.0f;
screenBuffer[i][j] = 0;
}
}
Vector up = new Vector(0.0f, 1.0f, 0.0f);
float cameraSpeed = 0.5f;
Vector target = MathUtils.multiply(camera.lookDirection, cameraSpeed);
target.y = 0.0f;
Point mousePosition = MouseInfo.getPointerInfo().getLocation();
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int centerX = (int) screenSize.getWidth() / 2;
int centerY = (int) screenSize.getHeight() / 2;
robot.mouseMove(centerX, centerY);
float dx = mousePosition.x - centerX;
float dy = mousePosition.y - centerY;
float newYaw = camera.yaw cameraSpeed * dx;
float newPitch = camera.pitch cameraSpeed * dy;
if (!Double.isNaN(dx))
{
camera.yaw = newYaw;
}
if (!Double.isNaN(dy))
{
if (newPitch < 180.0f && newPitch > -180.0f)
{
camera.pitch = newPitch;
}
}
if (keysPressed[0]) // W, move forward
{
camera.origin.add(target);
}
if (keysPressed[1]) // A, strafe left
{
Vector right = MathUtils.cross(target, up);
camera.origin.subtract(right);
}
if (keysPressed[2]) // S, move backward
{
camera.origin.subtract(target);
}
if (keysPressed[3]) // D, strafe right
{
Vector right = MathUtils.cross(target, up);
camera.origin.add(right);
}
if (keysPressed[4]) // SPACE, teleport to origin
{
camera.origin = new Vector(0.0f);
}
float[][] xWorldRotationMatrix = MathUtils.makeXRotationMatrix(0.0f);
float[][] yWorldRotationMatrix = MathUtils.makeYRotationMatrix(0.0f);
float[][] zWorldRotationMatrix = MathUtils.makeZRotationMatrix(0.0f);
float[][] worldRotationMatrix = MathUtils.multiply(MathUtils.multiply(yWorldRotationMatrix, xWorldRotationMatrix), zWorldRotationMatrix);
float[][] translationMatrix = MathUtils.makeTranslationMatrix(0.0f, 0.0f, 20.0f);
float[][] worldMatrix = MathUtils.multiply(worldRotationMatrix, translationMatrix);
float[][] xCameraRotationMatrix = MathUtils.makeXRotationMatrix(camera.pitch);
float[][] yCameraRotationMatrix = MathUtils.makeYRotationMatrix(camera.yaw);
float[][] zCameraRotationMatrix = MathUtils.makeZRotationMatrix(0.0f);
float[][] cameraRotationMatrix = MathUtils.multiply(MathUtils.multiply(yCameraRotationMatrix, xCameraRotationMatrix), zCameraRotationMatrix);
Vector forward = new Vector(0.0f, 0.0f, 1.0f);
camera.lookDirection = MathUtils.multiply(cameraRotationMatrix, forward);
forward = MathUtils.add(camera.origin, camera.lookDirection);
float[][] cameraMatrix = (MathUtils.inverseMatrix(camera.pointAt(forward, up)));
for (GameObject object : objects)
{
for (Triangle triangle : object.triangles)
{
calculateTriangles(triangle, worldMatrix, projectionMatrix, cameraMatrix);
}
}
for (Triangle t : trianglesToDraw)
{
t.draw(this, objects.get(0).texture);
}
repaint();
}
void draw(CanvasPanel viewport, BufferedImage texture)
{
int x1 = (int) this.points[0].x, x2 = (int) this.points[1].x, x3 = (int) this.points[2].x;
int y1 = (int) this.points[0].y, y2 = (int) this.points[1].y, y3 = (int) this.points[2].y;
float u1 = this.textureCoordinates[0].u, u2 = this.textureCoordinates[1].u, u3 = this.textureCoordinates[2].u;
float v1 = this.textureCoordinates[0].v, v2 = this.textureCoordinates[1].v, v3 = this.textureCoordinates[2].v;
float w1 = this.textureCoordinates[0].w, w2 = this.textureCoordinates[1].w, w3 = this.textureCoordinates[2].w;
int temp1;
float temp2;
if (y2 < y1)
{
temp1 = y1;
y1 = y2;
y2 = temp1;
temp1 = x1;
x1 = x2;
x2 = temp1;
temp2 = u1;
u1 = u2;
u2 = temp2;
temp2 = v1;
v1 = v2;
v2 = temp2;
temp2 = w1;
w1 = w2;
w2 = temp2;
}
if (y3 < y1)
{
temp1 = y1;
y1 = y3;
y3 = temp1;
temp1 = x1;
x1 = x3;
x3 = temp1;
temp2 = u1;
u1 = u3;
u3 = temp2;
temp2 = v1;
v1 = v3;
v3 = temp2;
temp2 = w1;
w1 = w3;
w3 = temp2;
}
if (y3 < y2)
{
temp1 = y2;
y2 = y3;
y3 = temp1;
temp1 = x2;
x2 = x3;
x3 = temp1;
temp2 = u2;
u2 = u3;
u3 = temp2;
temp2 = v2;
v2 = v3;
v3 = temp2;
temp2 = w2;
w2 = w3;
w3 = temp2;
}
int dy1 = y2 - y1;
int dx1 = x2 - x1;
float du1 = u2 - u1;
float dv1 = v2 - v1;
float dw1 = w2 - w1;
int dy2 = y3 - y1;
int dx2 = x3 - x1;
float dv2 = v3 - v1;
float du2 = u3 - u1;
float dw2 = w3 - w1;
float uTexture, vTexture, wTexture;
float dx1Step = 0, dx2Step = 0, du1Step = 0, du2Step = 0, dv1Step = 0, dv2Step = 0, dw1Step = 0, dw2Step = 0;
if (dy1 != 0)
{
dx1Step = dx1 / (float) Math.abs(dy1);
}
if (dy2 != 0)
{
dx2Step = dx2 / (float) Math.abs(dy2);
}
if (dy1 != 0)
{
du1Step = du1 / (float) Math.abs(dy1);
}
if (dy2 != 0)
{
du2Step = du2 / (float) Math.abs(dy2);
}
if (dy1 != 0)
{
dv1Step = dv1 / (float) Math.abs(dy1);
}
if (dy2 != 0)
{
dv2Step = dv2 / (float) Math.abs(dy2);
}
if (dy1 != 0)
{
dw1Step = dw1 / (float) Math.abs(dy1);
}
if (dy2 != 0)
{
dw2Step = dw2 / (float) Math.abs(dy2);
}
if (dy1 != 0)
{
for (int y = y1; y <= y2; y )
{
int xStart = (int) (x1 (y - y1) * dx1Step);
int xEnd = (int) (x1 (y - y1) * dx2Step);
float uStart = u1 (y - y1) * du1Step;
float uEnd = u1 (y - y1) * du2Step;
float vStart = v1 (y - y1) * dv1Step;
float vEnd = v1 (y - y1) * dv2Step;
float wStart = w1 (y - y1) * dw1Step;
float wEnd = w1 (y - y1) * dw2Step;
if (xStart > xEnd)
{
temp1 = xStart;
xStart = xEnd;
xEnd = temp1;
temp2 = uStart;
uStart = uEnd;
uEnd = temp2;
temp2 = vStart;
vStart = vEnd;
vEnd = temp2;
temp2 = wStart;
wStart = wEnd;
wEnd = temp2;
}
float tStep = 1.0f / (xEnd - xStart);
float t = 0.0f;
for (int x = xStart; x < xEnd; x )
{
uTexture = (1.0f - t) * uStart t * uEnd;
vTexture = (1.0f - t) * vStart t * vEnd;
wTexture = (1.0f - t) * wStart t * wEnd;
if (uTexture / wTexture >= 1 || vTexture / wTexture >= 1) // Temporary fix
{
uTexture = 0.0f;
vTexture = 0.0f;
}
int pixelColor;
if (textured)
{
int uTextureCoordinates = (int) ((uTexture / wTexture) * texture.getWidth());
int vTextureCoordinates = (int) ((vTexture / wTexture) * texture.getHeight());
try
{
pixelColor = texture.getRGB(uTextureCoordinates, vTextureCoordinates);
}
catch (ArrayIndexOutOfBoundsException e)
{
pixelColor = Color.black.getRGB();
}
}
else
{
pixelColor = color.getRGB();
}
if (wTexture > viewport.depthBuffer[x][y])
{
viewport.screenBuffer[x][y] = pixelColor;
viewport.depthBuffer[x][y] = wTexture;
}
t = tStep;
}
}
}
dy1 = y3 - y2;
dx1 = x3 - x2;
du1 = u3 - u2;
dv1 = v3 - v2;
dw1 = w3 - w2;
if (dy1 != 0)
{
dx1Step = dx1 / (float) Math.abs(dy1);
}
if (dy2 != 0)
{
dx2Step = dx2 / (float) Math.abs(dy2);
}
if (dy1 != 0)
{
du1Step = du1 / (float) Math.abs(dy1);
}
if (dy1 != 0)
{
dv1Step = dv1 / (float) Math.abs(dy1);
}
if (dy1 != 0)
{
dw1Step = dw1 / (float) Math.abs(dy1);
}
if (dy1 != 0)
{
for (int y = y2; y <= y3; y )
{
int xStart = (int) (x2 (y - y2) * dx1Step);
int xEnd = (int) (x1 (y - y1) * dx2Step);
float uStart = u2 (y - y2) * du1Step;
float uEnd = u1 (y - y1) * du2Step;
float vStart = v2 (y - y2) * dv1Step;
float vEnd = v1 (y - y1) * dv2Step;
float wStart = w2 (y - y2) * dw1Step;
float wEnd = w1 (y - y1) * dw2Step;
if (xStart > xEnd)
{
temp1 = xStart;
xStart = xEnd;
xEnd = temp1;
temp2 = uStart;
uStart = uEnd;
uEnd = temp2;
temp2 = vStart;
vStart = vEnd;
vEnd = temp2;
temp2 = wStart;
wStart = wEnd;
wEnd = temp2;
}
float tStep = 1.0f / (xEnd - xStart);
float t = 0.0f;
for (int x = xStart; x < xEnd; x )
{
uTexture = (1.0f - t) * uStart t * uEnd;
vTexture = (1.0f - t) * vStart t * vEnd;
wTexture = (1.0f - t) * wStart t * wEnd;
if (uTexture / wTexture >= 1 || vTexture / wTexture >= 1) // Temporary fix
{
uTexture = 0.0f;
vTexture = 0.0f;
}
int pixelColor;
if (textured)
{
int uTextureCoordinates = (int) ((uTexture / wTexture) * texture.getWidth());
int vTextureCoordinates = (int) ((vTexture / wTexture) * texture.getHeight());
try
{
pixelColor = texture.getRGB(uTextureCoordinates, vTextureCoordinates);
}
catch (ArrayIndexOutOfBoundsException e)
{
pixelColor = Color.black.getRGB();
}
}
else
{
pixelColor = color.getRGB();
}
if (wTexture > viewport.depthBuffer[x][y])
{
viewport.screenBuffer[x][y] = pixelColor;
viewport.depthBuffer[x][y] = wTexture;
}
t = tStep;
}
}
}
}
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
for (int i = 0; i < getWidth(); i )
{
for (int j = 0; j < getHeight(); j )
{
g.setColor(getColorFromRGBCode(screenBuffer[i][j]));
g.drawLine(i, j, i, j);
}
}
g.drawImage(crosshair, getWidth() / 2 - crosshair.getWidth() / 2, getHeight() / 2 - crosshair.getHeight() / 2, null);
}
CodePudding user response:
Well, it turns out that using BufferedImage
is just better, so I changed the screen buffer to one and it works well again.