/** @author Szymon */ public class Lamp extends AbstractTransformChangeNotifier { double ks = 0.1, kd = 1; double m = 1.2; int d0 = 0; enum Shader { FLAT, GOURAUD_TRI, GOURAUD_RECT, PHONG } private ComboBoxModel model = new DefaultComboBoxModel(Shader.values()) { @Override public void setSelectedItem(Object anObject) { super.setSelectedItem(anObject); shader = Shader.valueOf(anObject.toString()); recalc(); frame.recalc(); frame.repaint(); } }; protected Shader shader = Shader.FLAT; private Matrix1x4 viewer; private static final double[][] CORNERS = {{0, -2 * Brick.SIZE, -2 * Brick.SIZE, 1}}; public Lamp() { super(CORNERS); recalc(); } public int calculateBrithness(Matrix1x4 point, Vector vector) { Vector e = new Vector(point, viewer).normalize(); Vector l = new Vector(point, corners3D[0]); double len = l.length(); double brightness = (kd * vector.cosNorm(l) + ks * Math.pow(e.cosNorm(l), m)) / (len * len); brightness *= 256 * 256; if (brightness < 0) { brightness = 0; } return (int) brightness; } public int applyBrigthness(int color, int brightness) { int checkMask = 0x01000000; if ((color & checkMask) != 0 || (color & 0xff000000) == 0) { return color; } int mask = 0xff; int tmp; for (int k = 0; k < 19; k += 8) { tmp = ((color & mask) >> k) + 1; tmp *= brightness; tmp >>>= 8; if (tmp > 255) { tmp = 255; } else if (tmp < 0) { tmp = 0; } color = (color & ~mask) | (tmp << k); mask <<= 8; } return color | checkMask; } public static void sortIndexes2D(int[] indexes, int[][] corners) { for (int i = 0; i < indexes.length; ++i) { for (int k = i + 1; k < indexes.length; ++k) { int[] ci = corners[indexes[i]]; int[] ck = corners[indexes[k]]; if (ck[1] < ci[1] || (ck[1] == ci[1] && ck[0] < ci[0])) { int tmp = indexes[k]; indexes[k] = indexes[i]; indexes[i] = tmp; } } } } public static void testSortedIndexes(int[] indexes, int[][] corners) { int a = corners[indexes[0]][1]; int b = corners[indexes[1]][1]; int c = corners[indexes[2]][1]; if (a > b || b > c || a > c) { System.err.println("Zle sorotwanie indeksow: " + a + " " + b + " " + c); } } public static double safeDivide(double a, double b) { try { return a / b; } catch (ArithmeticException e) { System.err.println("Div 0"); return Double.POSITIVE_INFINITY; } } private void gradientLine(int[] buff, int offset, int steps, int v1, int v2) { int end = offset + steps; double gradientStep = safeDivide(v2 - v1, Math.abs(steps) + 1); double gradVal = v1; int tmp; if (steps > 0) { ++end; for (; offset <= end; ++offset) { try { buff[offset] = applyBrigthness(buff[offset], (int) gradVal); } catch (ArrayIndexOutOfBoundsException e) { } gradVal += gradientStep; } } else if (steps < 0) { ++offset; for (; offset >= end; --offset) { try { buff[offset] = applyBrigthness(buff[offset], (int) gradVal); } catch (ArrayIndexOutOfBoundsException e) { } gradVal += gradientStep; } } else { try { buff[offset] = applyBrigthness(buff[offset], v1); } catch (ArrayIndexOutOfBoundsException e) { } } } private void gradientLine( int[] buff, int offset, int steps, Matrix1x4 p1, Matrix1x4 p2, Vector wallNormVec) { int end = offset + steps; Vector vectorStep = stepVector(p1, p2, Math.abs(steps) + 1); Matrix1x4 tmpPoint = p1.clone(); if (steps > 0) { ++end; for (; offset <= end; ++offset) { try { buff[offset] = applyBrigthness(buff[offset], calculateBrithness(tmpPoint, wallNormVec)); } catch (ArrayIndexOutOfBoundsException e) { } tmpPoint.accumulate(vectorStep); } } else if (steps < 0) { ++offset; for (; offset >= end; --offset) { try { buff[offset] = applyBrigthness(buff[offset], calculateBrithness(tmpPoint, wallNormVec)); } catch (ArrayIndexOutOfBoundsException e) { } tmpPoint.accumulate(vectorStep); } } } public static int diffY(int i1, int i2, int[] indexes, int[][] corners) { return corners[indexes[i1]][1] - corners[indexes[i2]][1]; } public static double stepX(int i1, int i2, int[] indexes, int[][] corners) { return safeDivide( corners[indexes[i1]][0] - corners[indexes[i2]][0], diffY(i1, i2, indexes, corners)); } public static double stepB(int i1, int i2, int[] indexes, int[] brightnesses, int[][] corners) { return safeDivide( brightnesses[indexes[i1]] - brightnesses[indexes[i2]], diffY(i1, i2, indexes, corners)); } public static Vector stepVector(Matrix1x4 from, Matrix1x4 to, double steps) { Vector v = new Vector(from, to); steps = Math.abs(steps); for (int i = 0; i < 4; ++i) { v.data[i] /= steps; } return v; } public static Vector stepVector( int i1, int i2, int[] indexes, Matrix1x4[] corners3D, int[][] corners2D) { double steps = diffY(i1, i2, indexes, corners2D); return stepVector(corners3D[indexes[i2]], corners3D[indexes[i1]], steps); } /** * @param buff * @param width buff's row width * @param indexes sorted indexes * @param wall.corners2D unsorted corners * @param brightnesses unsorted brightnesses */ private void gouraudTriangle( Wall wall, int offset, int width, int[] indexes, int[] brightnesses) { int diffY_0_1 = diffY(1, 0, indexes, wall.corners2D); int diffY_0_2 = diffY(2, 0, indexes, wall.corners2D); double bStep0_1 = stepB(1, 0, indexes, brightnesses, wall.corners2D); double bStep0_2 = stepB(2, 0, indexes, brightnesses, wall.corners2D); double bStep1_2 = stepB(2, 1, indexes, brightnesses, wall.corners2D); double xStep0_1 = stepX(1, 0, indexes, wall.corners2D); double xStep0_2 = stepX(2, 0, indexes, wall.corners2D); double xStep1_2 = stepX(2, 1, indexes, wall.corners2D); double x1 = 0, x2 = 0; double b1, b2; b1 = b2 = brightnesses[indexes[0]]; for (int y = 0; y < diffY_0_1; ++y) { gradientLine(wall.buff, (int) (offset + x1), (int) (x2 - x1 + 0.5), (int) b1, (int) b2); x1 += xStep0_1; b1 += bStep0_1; x2 += xStep0_2; b2 += bStep0_2; offset += width; } b1 = brightnesses[indexes[1]]; if (diffY_0_1 == 0) { x1 += width; } for (int y = diffY_0_1; y < diffY_0_2; ++y) { gradientLine(wall.buff, (int) (offset + x1), (int) (x2 - x1 + 0.5), (int) b1, (int) b2); x1 += xStep1_2; b1 += bStep1_2; x2 += xStep0_2; b2 += bStep0_2; offset += width; } } private void phongTriangle(Wall wall, int offset, int width, int[] indexes) { int diffY_0_1 = diffY(1, 0, indexes, wall.corners2D); int diffY_0_2 = diffY(2, 0, indexes, wall.corners2D); double xStep_0_1 = stepX(1, 0, indexes, wall.corners2D); double xStep_0_2 = stepX(2, 0, indexes, wall.corners2D); double xStep_1_2 = stepX(2, 1, indexes, wall.corners2D); Vector vStep_0_1 = stepVector(1, 0, indexes, wall.corners3D, wall.corners2D); Vector vStep_0_2 = stepVector(2, 0, indexes, wall.corners3D, wall.corners2D); Vector vStep_1_2 = stepVector(2, 1, indexes, wall.corners3D, wall.corners2D); double x1 = 0, x2 = 0; Matrix1x4 p1 = wall.corners3D[indexes[0]].clone(); Matrix1x4 p2 = wall.corners3D[indexes[0]].clone(); for (int y = 0; y < diffY_0_1; ++y) { gradientLine(wall.buff, (int) (offset + x1), (int) (x2 - x1 + 0.5), p1, p2, wall.vector); x1 += xStep_0_1; p1.accumulate(vStep_0_1); x2 += xStep_0_2; p2.accumulate(vStep_0_2); offset += width; } p1 = wall.corners3D[indexes[1]].clone(); if (diffY_0_1 == 0) { x1 += width; } for (int y = diffY_0_1; y < diffY_0_2; ++y) { gradientLine(wall.buff, (int) (offset + x1), (int) (x2 - x1 + 0.5), p1, p2, wall.vector); x1 += xStep_1_2; p1.accumulate(vStep_1_2); x2 += xStep_0_2; p2.accumulate(vStep_0_2); offset += width; } } public static boolean sameRow(int x1, int x2, int width) { boolean val = (x1 / width) == (x2 / width); if (!val) { System.err.print("ROWS: X1: " + x1 / width + " "); System.err.println("X2: " + x2 / width + " " + val); } return val; } private void enlightTriangles(Wall wall, int width) { int brights[] = new int[4]; for (int i = 0; i < 4; ++i) { brights[i] = calculateBrithness(wall.corners3D[i], wall.vector); } // podzial na 2 trojkaty int[][] triangleIndexes = {{0, 1, 2}, {0, 2, 3}}; Polygon p = new Polygon(); for (int[] corner : wall.corners2D) { p.addPoint(corner[0], corner[1]); } int minX = p.getBounds().x; int minY = p.getBounds().y; for (int[] indexes : triangleIndexes) { sortIndexes2D(indexes, wall.corners2D); int xoffset = wall.corners2D[indexes[0]][0] - minX; int yoffset = wall.corners2D[indexes[0]][1] - minY; int offset = xoffset + yoffset * width; if (shader == Shader.PHONG) { phongTriangle(wall, offset, width, indexes); } else { gouraudTriangle(wall, offset, width, indexes, brights); } } } /** * Niedokonczona implementacja usredniania Gourouda dla calego czworokata * * @param wall * @param width */ private void gouraudRectangle(Wall wall, int width) { int brights[] = new int[4]; for (int i = 0; i < 4; ++i) { brights[i] = calculateBrithness(wall.corners3D[i], wall.vector); } int[] indexes = {0, 1, 2, 3}; sortIndexes2D(indexes, wall.corners2D); Polygon p = new Polygon(); for (int[] corner : wall.corners2D) { p.addPoint(corner[0], corner[1]); } int xoffset = wall.corners2D[indexes[0]][0] - p.getBounds().x; int yoffset = wall.corners2D[indexes[0]][1] - p.getBounds().y; int offset = xoffset + (yoffset) * width; int diffY_0_1 = diffY(1, 0, indexes, wall.corners2D); int diffY_1_2 = diffY(2, 1, indexes, wall.corners2D); int diffY_2_3 = diffY(3, 2, indexes, wall.corners2D); double stepX_0_1 = stepX(1, 0, indexes, wall.corners2D); double stepX_1_3 = stepX(3, 1, indexes, wall.corners2D); double stepX_0_2 = stepX(2, 0, indexes, wall.corners2D); double stepX_2_3 = stepX(3, 2, indexes, wall.corners2D); double stepB_0_1 = stepB(1, 0, indexes, brights, wall.corners2D); double stepB_1_3 = stepB(3, 1, indexes, brights, wall.corners2D); double stepB_0_2 = stepB(2, 0, indexes, brights, wall.corners2D); double stepB_2_3 = stepB(3, 2, indexes, brights, wall.corners2D); double x1 = 0; double x2 = 0; double b1; double b2; b1 = b2 = brights[indexes[0]]; int y; for (y = 0; y < diffY_0_1; ++y) { gradientLine(wall.buff, (int) (offset + x1), (int) (x2 - x1 + 0.5), (int) b1, (int) b2); x1 += stepX_0_2; b1 += stepB_0_2; x2 += stepX_0_1; b2 += stepB_0_1; offset += width; } if (diffY_0_1 == 0) { x2 = width; } for (y = 0; y < diffY_1_2; ++y) { gradientLine(wall.buff, (int) (offset + x1), (int) (x2 - x1 + 0.5), (int) b1, (int) b2); x1 += stepX_0_2; b1 += stepB_0_2; x2 += stepX_1_3; b2 += stepB_1_3; offset += width; } for (y = 0; y < diffY_2_3; ++y) { gradientLine(wall.buff, (int) (offset + x1), (int) (x2 - x1 + 0.5), (int) b1, (int) b2); x1 += stepX_2_3; b1 += stepB_2_3; x2 += stepX_1_3; b2 += stepB_1_3; offset += width; } } /** * @param wall * @return true if wall is enlighten at all */ public boolean enlight(Wall wall, Matrix1x4 viewer, int width, int height) { this.viewer = viewer; switch (shader) { case GOURAUD_TRI: case PHONG: enlightTriangles(wall, width); break; case GOURAUD_RECT: gouraudRectangle(wall, width); break; case FLAT: default: // obliczanie sredniego punktu: Matrix1x4 point = new Matrix1x4(0, 0, 0, 0); for (Matrix1x4 m : wall.corners3D) { for (int i = 0; i < 4; ++i) { point.data[i] += m.data[i]; } } for (int i = 0; i < 4; ++i) { point.data[i] /= 4; } int bright = calculateBrithness(point, wall.vector); int size = width * height; for (int i = 0; i < size; ++i) { wall.buff[i] = applyBrigthness(wall.buff[i], bright); } break; } return true; } public void recalcThis() {} @Override public void paint(Graphics2D g, int width, int height) { g.setColor(Color.YELLOW); g.fillOval(corners2D[0][0] + width / 2 - 3, corners2D[0][1] + height / 2 - 3, 6, 6); } // reszta to juz tylko gettery i settery public ComboBoxModel getModel() { return model; } public int getD0() { return d0; } public void setD0(int d0) { this.d0 = d0; } public double getKd() { return kd; } public void setKd(double kd) { this.kd = kd; } public double getKs() { return ks; } public void setKs(double ks) { this.ks = ks; } public double getM() { return m; } public void setM(double m) { this.m = m; } protected BrickFrame frame; public BrickFrame getFrame() { return frame; } public void setFrame(BrickFrame frame) { this.frame = frame; } }