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.geometry.Point3D;
9 import eu.svjatoslav.sixth.e3d.gui.RenderingContext;
10 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.AbstractCoordinateShape;
11 import eu.svjatoslav.sixth.e3d.renderer.raster.slicer.PolygonCoordinate;
12 import eu.svjatoslav.sixth.e3d.renderer.raster.texture.Texture;
13 import eu.svjatoslav.sixth.e3d.renderer.raster.texture.TextureBitmap;
17 import static eu.svjatoslav.sixth.e3d.geometry.Polygon.pointWithinPolygon;
18 import static java.util.Arrays.sort;
25 * This is how perspective-correct texture rendering is implemented:
26 * If polygon is sufficiently small, it is rendered without perspective correction.
27 * Otherwise, it is sliced into smaller polygons.
31 public class TexturedPolygon extends AbstractCoordinateShape {
33 public final Texture texture;
35 * Polygon texture coordinates.
37 public Point2D texturePoint1, texturePoint2, texturePoint3;
40 * If <code>true</code> then polygon borders will be drawn.
41 * It is used for debugging purposes.
43 public boolean showBorders = false;
44 private double totalTextureDistance = -1;
46 public TexturedPolygon(final Point3D p1, final Point3D p2,
47 final Point3D p3, final Point2D tp1, final Point2D tp2,
48 final Point2D tp3, final Texture texture) {
56 this.texture = texture;
59 public TexturedPolygon(final PolygonCoordinate pc1,
60 final PolygonCoordinate pc2, final PolygonCoordinate pc3,
61 final Texture texture) {
63 this(pc1.spaceCoordinate, pc2.spaceCoordinate, pc3.spaceCoordinate, pc1.textureCoordinate, pc2.textureCoordinate,
64 pc3.textureCoordinate, texture);
67 private void computeTotalTextureDistance() {
68 // compute total texture distance
69 totalTextureDistance = texturePoint1.getDistanceTo(texturePoint2);
70 totalTextureDistance += texturePoint1.getDistanceTo(texturePoint3);
71 totalTextureDistance += texturePoint2.getDistanceTo(texturePoint3);
74 private void drawHorizontalLine(final PolygonBorderInterpolator line1,
75 final PolygonBorderInterpolator line2, final int y,
76 final RenderingContext renderBuffer,
77 final TextureBitmap textureBitmap) {
82 int x1 = line1.getX();
83 int x2 = line2.getX();
85 final double tx2, ty2;
86 final double tx1, ty1;
90 tx1 = line1.getTX() * textureBitmap.multiplicationFactor;
91 ty1 = line1.getTY() * textureBitmap.multiplicationFactor;
93 tx2 = line2.getTX() * textureBitmap.multiplicationFactor;
94 ty2 = line2.getTY() * textureBitmap.multiplicationFactor;
101 tx1 = line2.getTX() * textureBitmap.multiplicationFactor;
102 ty1 = line2.getTY() * textureBitmap.multiplicationFactor;
104 tx2 = line1.getTX() * textureBitmap.multiplicationFactor;
105 ty2 = line1.getTY() * textureBitmap.multiplicationFactor;
108 final double realWidth = x2 - x1;
109 final double realX1 = x1;
114 if (x2 >= renderBuffer.width)
115 x2 = renderBuffer.width - 1;
117 int renderBufferOffset = ((y * renderBuffer.width) + x1) * 4;
118 final byte[] renderBufferBytes = renderBuffer.pixels;
120 final double twidth = tx2 - tx1;
121 final double theight = ty2 - ty1;
123 for (int x = x1; x < x2; x++) {
125 final double distance = x - realX1;
127 final double tx = tx1 + ((twidth * distance) / realWidth);
128 final double ty = ty1 + ((theight * distance) / realWidth);
130 final int textureOffset = textureBitmap.getAddress((int) tx,
133 textureBitmap.drawPixel(textureOffset, renderBufferBytes,
136 renderBufferOffset += 4;
142 public void paint(final RenderingContext renderBuffer) {
144 final Point2D projectedPoint1 = coordinates[0].onScreenCoordinate;
145 final Point2D projectedPoint2 = coordinates[1].onScreenCoordinate;
146 final Point2D projectedPoint3 = coordinates[2].onScreenCoordinate;
148 projectedPoint1.roundToInteger();
149 projectedPoint2.roundToInteger();
150 projectedPoint3.roundToInteger();
152 if (mouseInteractionController != null)
153 if (renderBuffer.getMouseEvent() != null)
154 if (pointWithinPolygon(
155 renderBuffer.getMouseEvent().coordinate, projectedPoint1,
156 projectedPoint2, projectedPoint3))
157 renderBuffer.setCurrentObjectUnderMouseCursor(mouseInteractionController);
159 // Show polygon boundaries (for debugging)
161 showBorders(renderBuffer);
163 // find top-most point
164 int yTop = (int) projectedPoint1.y;
166 if (projectedPoint2.y < yTop)
167 yTop = (int) projectedPoint2.y;
169 if (projectedPoint3.y < yTop)
170 yTop = (int) projectedPoint3.y;
175 // find bottom-most point
176 int yBottom = (int) projectedPoint1.y;
178 if (projectedPoint2.y > yBottom)
179 yBottom = (int) projectedPoint2.y;
181 if (projectedPoint3.y > yBottom)
182 yBottom = (int) projectedPoint3.y;
184 if (yBottom >= renderBuffer.height)
185 yBottom = renderBuffer.height - 1;
188 double totalVisibleDistance = projectedPoint1.getDistanceTo(projectedPoint2);
189 totalVisibleDistance += projectedPoint1.getDistanceTo(projectedPoint3);
190 totalVisibleDistance += projectedPoint2.getDistanceTo(projectedPoint3);
192 if (totalTextureDistance == -1)
193 computeTotalTextureDistance();
194 final double scaleFactor = (totalVisibleDistance / totalTextureDistance) * 1.2d;
196 final TextureBitmap zoomedBitmap = texture.getZoomedBitmap(scaleFactor);
198 final PolygonBorderInterpolator[] is = new PolygonBorderInterpolator[]{
199 new PolygonBorderInterpolator(), new PolygonBorderInterpolator(),
200 new PolygonBorderInterpolator()};
202 is[0].setPoints(projectedPoint1, projectedPoint2, texturePoint1,
204 is[1].setPoints(projectedPoint1, projectedPoint3, texturePoint1,
206 is[2].setPoints(projectedPoint2, projectedPoint3, texturePoint2,
211 for (int y = yTop; y < yBottom; y++)
212 if (is[0].containsY(y)) {
214 if (is[1].containsY(y))
215 drawHorizontalLine(is[0], is[1], y, renderBuffer,
217 else if (is[2].containsY(y))
218 drawHorizontalLine(is[0], is[2], y, renderBuffer,
220 } else if (is[1].containsY(y))
221 if (is[2].containsY(y))
222 drawHorizontalLine(is[1], is[2], y, renderBuffer,
227 private void showBorders(final RenderingContext renderBuffer) {
229 final Point2D projectedPoint1 = coordinates[0].onScreenCoordinate;
230 final Point2D projectedPoint2 = coordinates[1].onScreenCoordinate;
231 final Point2D projectedPoint3 = coordinates[2].onScreenCoordinate;
233 renderBuffer.graphics.setColor(Color.YELLOW);
234 renderBuffer.graphics.drawLine((int) projectedPoint1.x,
235 (int) projectedPoint1.y, (int) projectedPoint2.x,
236 (int) projectedPoint2.y);
237 renderBuffer.graphics.drawLine((int) projectedPoint3.x,
238 (int) projectedPoint3.y, (int) projectedPoint2.x,
239 (int) projectedPoint2.y);
240 renderBuffer.graphics.drawLine((int) projectedPoint1.x,
241 (int) projectedPoint1.y, (int) projectedPoint3.x,
242 (int) projectedPoint3.y);