package late.raster;
import java.awt.Graphics;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.util.Arrays;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
@SuppressWarnings("serial")
public class Raster extends JPanel implements ComponentListener {
public static final float[][] triangles = {
// first triangle (x, y, z, r, g, b)
{ 100, 50, 0.5f, 250, 0, 0 },
{ 500, 300, 0.7f, 0, 250, 0 },
{ 200, 500, 0.5f, 0, 0, 250 },
//second triangle (x, y, z, r, g, b)
{ 500, 90, 0.1f, 0, 250, 0 },
{ 800, 150, 0.1f, 0, 0, 250 },
{ 100, 400, 0.7f, 250, 0, 0 }};
private BufferedImage image;
private int width;
private int height;
int[] pixels;
float[] zbuffer;
public Raster() {
super();
image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
addComponentListener(this);
}
@Override
protected void paintComponent(final Graphics g) {
super.paintComponent(g);
synchronized (image) {
g.drawImage(image, 0, 0, image.getWidth(), image.getHeight(), null);
}
}
@Override
public void componentResized(final ComponentEvent e) {
width = getWidth();
height = getHeight();
if (width != image.getWidth() || height != image.getHeight()) {
synchronized (image) {
image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
zbuffer = new float[pixels.length];
Arrays.fill(zbuffer, 1);
}
}
draw();
}
/*
* draw our scene
*/
public void draw() {
for (int i = 0; i < triangles.length; i += 3)
drawTriangel(triangles[i + 0], triangles[i + 1], triangles[i + 2]);
}
/*
* draw a triangle (v1, v2, v3) with v = (x, y, z, r, g, b)
* x, y must be in screen space coordinates
* z must be normalized (0 - 1)
* z, r, g, b will be linear interpolated in screen space
*/
private void drawTriangel(float[] v1, float[] v2, float[] v3) {
//sort v1.y < v2.y < v3.y, because we need a upper and lower triangle part
if (v1[1] > v2[1]) {
final float[] v = v1;
v1 = v2;
v2 = v;
}
if (v2[1] > v3[1]) {
final float[] v = v2;
v2 = v3;
v3 = v;
}
if (v1[1] > v2[1]) {
final float[] v = v1;
v1 = v2;
v2 = v;
}
// calculate delta x from a to b
final float dx12 = (v2[0] - v1[0]) / (v2[1] - v1[1]);
final float dx13 = (v3[0] - v1[0]) / (v3[1] - v1[1]);
final float dx32 = (v2[0] - v3[0]) / (v2[1] - v3[1]);
//calculate delta z from a to b
final float dz12 = (v2[2] - v1[2]) / (v2[1] - v1[1]);
final float dz13 = (v3[2] - v1[2]) / (v3[1] - v1[1]);
final float dz32 = (v2[2] - v3[2]) / (v2[1] - v3[1]);
//calculate delta r from a to b
final float dr12 = (v2[3] - v1[3]) / (v2[1] - v1[1]);
final float dr13 = (v3[3] - v1[3]) / (v3[1] - v1[1]);
final float dr32 = (v2[3] - v3[3]) / (v2[1] - v3[1]);
//calculate delta g from a to b
final float dg12 = (v2[4] - v1[4]) / (v2[1] - v1[1]);
final float dg13 = (v3[4] - v1[4]) / (v3[1] - v1[1]);
final float dg32 = (v2[4] - v3[4]) / (v2[1] - v3[1]);
//calculate delta b from a to b
final float db12 = (v2[5] - v1[5]) / (v2[1] - v1[1]);
final float db13 = (v3[5] - v1[5]) / (v3[1] - v1[1]);
final float db32 = (v2[5] - v3[5]) / (v2[1] - v3[1]);
// draw upper triangle part from v1 to v2
final int signum12 = (int) Math.signum(v2[1] - v1[1]);
for (int y = (int) v1[1]; y != (v2[1] + signum12); y += signum12) {
final float d = y - v1[1];
final int x1 = (int) (v1[0] + dx13 * d);
final float z1 = (v1[2] + dz13 * d);
final int r1 = (int) (v1[3] + dr13 * d);
final int g1 = (int) (v1[4] + dg13 * d);
final int b1 = (int) (v1[5] + db13 * d);
final int x2 = (int) (v1[0] + dx12 * d);
final float z2 = (v1[2] + dz12 * d);
final int r2 = (int) (v1[3] + dr12 * d);
final int g2 = (int) (v1[4] + dg12 * d);
final int b2 = (int) (v1[5] + db12 * d);
drawLine(y, x1, z1, r1, g1, b1, x2, z2, r2, g2, b2);
}
//draw lower triangle part from v3 to v2
final int signum32 = (int) Math.signum(v2[1] - v3[1]);
for (int y = (int) v3[1]; y != (v2[1] + signum32); y += signum32) {
final float d = y - v3[1];
final int x1 = (int) (v3[0] + dx13 * d);
final float z1 = (v3[2] + dz13 * d);
final int r1 = (int) (v3[3] + dr13 * d);
final int g1 = (int) (v3[4] + dg13 * d);
final int b1 = (int) (v3[5] + db13 * d);
final int x2 = (int) (v3[0] + dx32 * d);
final float z2 = (v3[2] + dz32 * d);
final int r2 = (int) (v3[3] + dr32 * d);
final int g2 = (int) (v3[4] + dg32 * d);
final int b2 = (int) (v3[5] + db32 * d);
drawLine(y, x1, z1, r1, g1, b1, x2, z2, r2, g2, b2);
}
}
/*
* draw a horizontal scan line from (x1, y, z1) to (x2, y, z2) with color (r1, g1, b1) to (r2, g2, b2)
* x, y must be in screen space coordinates
* z must be normalized (0 - 1)
* z, r, g, b will be linear interpolated in screen space
*/
private void drawLine(final int y, final int x1, final float z1, final int r1, final int g1, final int b1, final int x2, final float z2, final int r2, final int g2, final int b2) {
final float d = x2 - x1;
final int signum = (int) Math.signum(d);
final float dz = (z2 - z1) / d;
final float dr = (r2 - r1) / d;
final float dg = (g2 - g1) / d;
final float db = (b2 - b1) / d;
for (int x = x1; x != (x2 + signum); x += signum) {
final float l = x - x1;
final float z = z1 + dz * l;
final int r = (int) (r1 + dr * l);
final int g = (int) (g1 + dg * l);
final int b = (int) (b1 + db * l);
drawPixel(x, y, z, r, g, b);
}
}
/*
* draw a pixel (x, y, z) with color (r, g, b) with respect to zbuffer
* x, y must be in screen space coordinates
* z must be normalized (0 - 1)
*/
private void drawPixel(final int x, final int y, final float z, final int r, final int g, final int b) {
if (0 <= x && x < width && 0 <= y && y < height) {
final int c = (r << 16) | (g << 8) | (b << 0);
final int pixel = y * width + x;
synchronized (image) {
if (0 <= z && z < zbuffer[pixel]) {
pixels[pixel] = c;
zbuffer[pixel] = z;
}
}
}
}
public static void main(final String[] args) {
final Raster raster = new Raster();
final JFrame frame = new JFrame();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(raster);
frame.setSize(800, 500);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
@Override public void componentHidden(final ComponentEvent e) { }
@Override public void componentMoved(final ComponentEvent e) { }
@Override public void componentShown(final ComponentEvent e) { }
}