2 * Sixth 3D engine. Author: Svjatoslav Agejenko.
3 * This project is released under Creative Commons Zero (CC0) license.
5 package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.texturedpolygon;
7 import eu.svjatoslav.sixth.e3d.geometry.Point2D;
8 import eu.svjatoslav.sixth.e3d.gui.RenderingContext;
9 import eu.svjatoslav.sixth.e3d.math.Vertex;
10 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.AbstractCoordinateShape;
11 import eu.svjatoslav.sixth.e3d.renderer.raster.texture.Texture;
12 import eu.svjatoslav.sixth.e3d.renderer.raster.texture.TextureBitmap;
16 import static eu.svjatoslav.sixth.e3d.geometry.Polygon.pointWithinPolygon;
17 import static java.util.Arrays.sort;
24 * This is how perspective-correct texture rendering is implemented:
25 * If polygon is sufficiently small, it is rendered without perspective correction.
26 * Otherwise, it is sliced into smaller polygons.
30 public class TexturedPolygon extends AbstractCoordinateShape {
32 public final Texture texture;
35 * If <code>true</code> then polygon borders will be drawn.
36 * It is used for debugging purposes.
38 public boolean showBorders = false;
40 private double totalTextureDistance = -1;
42 public TexturedPolygon(Vertex p1, Vertex p2, Vertex p3, final Texture texture) {
45 this.texture = texture;
48 private void computeTotalTextureDistance() {
49 // compute total texture distance
50 totalTextureDistance = coordinates[0].textureCoordinate.getDistanceTo(coordinates[1].textureCoordinate);
51 totalTextureDistance += coordinates[0].textureCoordinate.getDistanceTo(coordinates[2].textureCoordinate);
52 totalTextureDistance += coordinates[1].textureCoordinate.getDistanceTo(coordinates[2].textureCoordinate);
55 private void drawHorizontalLine(final PolygonBorderInterpolator line1,
56 final PolygonBorderInterpolator line2, final int y,
57 final RenderingContext renderBuffer,
58 final TextureBitmap textureBitmap) {
63 int x1 = line1.getX();
64 int x2 = line2.getX();
66 final double tx2, ty2;
67 final double tx1, ty1;
71 tx1 = line1.getTX() * textureBitmap.multiplicationFactor;
72 ty1 = line1.getTY() * textureBitmap.multiplicationFactor;
74 tx2 = line2.getTX() * textureBitmap.multiplicationFactor;
75 ty2 = line2.getTY() * textureBitmap.multiplicationFactor;
82 tx1 = line2.getTX() * textureBitmap.multiplicationFactor;
83 ty1 = line2.getTY() * textureBitmap.multiplicationFactor;
85 tx2 = line1.getTX() * textureBitmap.multiplicationFactor;
86 ty2 = line1.getTY() * textureBitmap.multiplicationFactor;
89 final double realWidth = x2 - x1;
90 final double realX1 = x1;
95 if (x2 >= renderBuffer.width)
96 x2 = renderBuffer.width - 1;
98 int renderBufferOffset = ((y * renderBuffer.width) + x1) * 4;
99 final byte[] renderBufferBytes = renderBuffer.pixels;
101 final double twidth = tx2 - tx1;
102 final double theight = ty2 - ty1;
104 for (int x = x1; x < x2; x++) {
106 final double distance = x - realX1;
108 final double tx = tx1 + ((twidth * distance) / realWidth);
109 final double ty = ty1 + ((theight * distance) / realWidth);
111 final int textureOffset = textureBitmap.getAddress((int) tx,
114 textureBitmap.drawPixel(textureOffset, renderBufferBytes,
117 renderBufferOffset += 4;
123 public void paint(final RenderingContext renderBuffer) {
125 final Point2D projectedPoint1 = coordinates[0].onScreenCoordinate;
126 final Point2D projectedPoint2 = coordinates[1].onScreenCoordinate;
127 final Point2D projectedPoint3 = coordinates[2].onScreenCoordinate;
129 projectedPoint1.roundToInteger();
130 projectedPoint2.roundToInteger();
131 projectedPoint3.roundToInteger();
133 if (mouseInteractionController != null)
134 if (renderBuffer.getMouseEvent() != null)
135 if (pointWithinPolygon(
136 renderBuffer.getMouseEvent().coordinate, projectedPoint1,
137 projectedPoint2, projectedPoint3))
138 renderBuffer.setCurrentObjectUnderMouseCursor(mouseInteractionController);
140 // Show polygon boundaries (for debugging)
142 showBorders(renderBuffer);
144 // find top-most point
145 int yTop = (int) projectedPoint1.y;
147 if (projectedPoint2.y < yTop)
148 yTop = (int) projectedPoint2.y;
150 if (projectedPoint3.y < yTop)
151 yTop = (int) projectedPoint3.y;
156 // find bottom-most point
157 int yBottom = (int) projectedPoint1.y;
159 if (projectedPoint2.y > yBottom)
160 yBottom = (int) projectedPoint2.y;
162 if (projectedPoint3.y > yBottom)
163 yBottom = (int) projectedPoint3.y;
165 if (yBottom >= renderBuffer.height)
166 yBottom = renderBuffer.height - 1;
169 double totalVisibleDistance = projectedPoint1.getDistanceTo(projectedPoint2);
170 totalVisibleDistance += projectedPoint1.getDistanceTo(projectedPoint3);
171 totalVisibleDistance += projectedPoint2.getDistanceTo(projectedPoint3);
173 if (totalTextureDistance == -1)
174 computeTotalTextureDistance();
175 final double scaleFactor = (totalVisibleDistance / totalTextureDistance) * 1.2d;
177 final TextureBitmap zoomedBitmap = texture.getZoomedBitmap(scaleFactor);
179 final PolygonBorderInterpolator[] is = new PolygonBorderInterpolator[]{
180 new PolygonBorderInterpolator(), new PolygonBorderInterpolator(),
181 new PolygonBorderInterpolator()};
183 is[0].setPoints(projectedPoint1, projectedPoint2,
184 coordinates[0].textureCoordinate,
185 coordinates[1].textureCoordinate);
186 is[1].setPoints(projectedPoint1, projectedPoint3,
187 coordinates[0].textureCoordinate,
188 coordinates[2].textureCoordinate);
189 is[2].setPoints(projectedPoint2, projectedPoint3,
190 coordinates[1].textureCoordinate,
191 coordinates[2].textureCoordinate);
195 for (int y = yTop; y < yBottom; y++)
196 if (is[0].containsY(y)) {
198 if (is[1].containsY(y))
199 drawHorizontalLine(is[0], is[1], y, renderBuffer,
201 else if (is[2].containsY(y))
202 drawHorizontalLine(is[0], is[2], y, renderBuffer,
204 } else if (is[1].containsY(y))
205 if (is[2].containsY(y))
206 drawHorizontalLine(is[1], is[2], y, renderBuffer,
211 private void showBorders(final RenderingContext renderBuffer) {
213 final Point2D projectedPoint1 = coordinates[0].onScreenCoordinate;
214 final Point2D projectedPoint2 = coordinates[1].onScreenCoordinate;
215 final Point2D projectedPoint3 = coordinates[2].onScreenCoordinate;
217 renderBuffer.graphics.setColor(Color.YELLOW);
218 renderBuffer.graphics.drawLine((int) projectedPoint1.x,
219 (int) projectedPoint1.y, (int) projectedPoint2.x,
220 (int) projectedPoint2.y);
221 renderBuffer.graphics.drawLine((int) projectedPoint3.x,
222 (int) projectedPoint3.y, (int) projectedPoint2.x,
223 (int) projectedPoint2.y);
224 renderBuffer.graphics.drawLine((int) projectedPoint1.x,
225 (int) projectedPoint1.y, (int) projectedPoint3.x,
226 (int) projectedPoint3.y);