Fixed git clone URL
[sixth-3d.git] / src / main / java / eu / svjatoslav / sixth / e3d / renderer / raster / shapes / basic / texturedpolygon / TexturedPolygon.java
1 /*
2  * Sixth 3D engine. Author: Svjatoslav Agejenko.
3  * This project is released under Creative Commons Zero (CC0) license.
4  */
5 package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.texturedpolygon;
6
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;
13
14 import java.awt.*;
15
16 import static eu.svjatoslav.sixth.e3d.geometry.Polygon.pointWithinPolygon;
17 import static java.util.Arrays.sort;
18
19 /**
20  * Textured polygon.
21  * <p>
22  *
23  * <pre>
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.
27  * </pre>
28  */
29
30 public class TexturedPolygon extends AbstractCoordinateShape {
31
32     public final Texture texture;
33
34     /**
35      * If <code>true</code> then polygon borders will be drawn.
36      * It is used for debugging purposes.
37      */
38     public boolean showBorders = false;
39
40     private double totalTextureDistance = -1;
41
42     public TexturedPolygon(Vertex p1, Vertex p2, Vertex p3, final Texture texture) {
43
44         super(p1, p2, p3);
45         this.texture = texture;
46     }
47
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);
53     }
54
55     private void drawHorizontalLine(final PolygonBorderInterpolator line1,
56                                     final PolygonBorderInterpolator line2, final int y,
57                                     final RenderingContext renderBuffer,
58                                     final TextureBitmap textureBitmap) {
59
60         line1.setCurrentY(y);
61         line2.setCurrentY(y);
62
63         int x1 = line1.getX();
64         int x2 = line2.getX();
65
66         final double tx2, ty2;
67         final double tx1, ty1;
68
69         if (x1 <= x2) {
70
71             tx1 = line1.getTX() * textureBitmap.multiplicationFactor;
72             ty1 = line1.getTY() * textureBitmap.multiplicationFactor;
73
74             tx2 = line2.getTX() * textureBitmap.multiplicationFactor;
75             ty2 = line2.getTY() * textureBitmap.multiplicationFactor;
76
77         } else {
78             final int tmp = x1;
79             x1 = x2;
80             x2 = tmp;
81
82             tx1 = line2.getTX() * textureBitmap.multiplicationFactor;
83             ty1 = line2.getTY() * textureBitmap.multiplicationFactor;
84
85             tx2 = line1.getTX() * textureBitmap.multiplicationFactor;
86             ty2 = line1.getTY() * textureBitmap.multiplicationFactor;
87         }
88
89         final double realWidth = x2 - x1;
90         final double realX1 = x1;
91
92         if (x1 < 0)
93             x1 = 0;
94
95         if (x2 >= renderBuffer.width)
96             x2 = renderBuffer.width - 1;
97
98         int renderBufferOffset = ((y * renderBuffer.width) + x1) * 4;
99         final byte[] renderBufferBytes = renderBuffer.pixels;
100
101         final double twidth = tx2 - tx1;
102         final double theight = ty2 - ty1;
103
104         for (int x = x1; x < x2; x++) {
105
106             final double distance = x - realX1;
107
108             final double tx = tx1 + ((twidth * distance) / realWidth);
109             final double ty = ty1 + ((theight * distance) / realWidth);
110
111             final int textureOffset = textureBitmap.getAddress((int) tx,
112                     (int) ty);
113
114             textureBitmap.drawPixel(textureOffset, renderBufferBytes,
115                     renderBufferOffset);
116
117             renderBufferOffset += 4;
118         }
119
120     }
121
122     @Override
123     public void paint(final RenderingContext renderBuffer) {
124
125         final Point2D projectedPoint1 = coordinates[0].onScreenCoordinate;
126         final Point2D projectedPoint2 = coordinates[1].onScreenCoordinate;
127         final Point2D projectedPoint3 = coordinates[2].onScreenCoordinate;
128
129         projectedPoint1.roundToInteger();
130         projectedPoint2.roundToInteger();
131         projectedPoint3.roundToInteger();
132
133         if (mouseInteractionController != null)
134             if (renderBuffer.getMouseEvent() != null)
135                 if (pointWithinPolygon(
136                         renderBuffer.getMouseEvent().coordinate, projectedPoint1,
137                         projectedPoint2, projectedPoint3))
138                     renderBuffer.setCurrentObjectUnderMouseCursor(mouseInteractionController);
139
140         // Show polygon boundaries (for debugging)
141         if (showBorders)
142             showBorders(renderBuffer);
143
144         // find top-most point
145         int yTop = (int) projectedPoint1.y;
146
147         if (projectedPoint2.y < yTop)
148             yTop = (int) projectedPoint2.y;
149
150         if (projectedPoint3.y < yTop)
151             yTop = (int) projectedPoint3.y;
152
153         if (yTop < 0)
154             yTop = 0;
155
156         // find bottom-most point
157         int yBottom = (int) projectedPoint1.y;
158
159         if (projectedPoint2.y > yBottom)
160             yBottom = (int) projectedPoint2.y;
161
162         if (projectedPoint3.y > yBottom)
163             yBottom = (int) projectedPoint3.y;
164
165         if (yBottom >= renderBuffer.height)
166             yBottom = renderBuffer.height - 1;
167
168         // paint
169         double totalVisibleDistance = projectedPoint1.getDistanceTo(projectedPoint2);
170         totalVisibleDistance += projectedPoint1.getDistanceTo(projectedPoint3);
171         totalVisibleDistance += projectedPoint2.getDistanceTo(projectedPoint3);
172
173         if (totalTextureDistance == -1)
174             computeTotalTextureDistance();
175         final double scaleFactor = (totalVisibleDistance / totalTextureDistance) * 1.2d;
176
177         final TextureBitmap zoomedBitmap = texture.getZoomedBitmap(scaleFactor);
178
179         final PolygonBorderInterpolator[] is = new PolygonBorderInterpolator[]{
180                 new PolygonBorderInterpolator(), new PolygonBorderInterpolator(),
181                 new PolygonBorderInterpolator()};
182
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);
192
193         sort(is);
194
195         for (int y = yTop; y < yBottom; y++)
196             if (is[0].containsY(y)) {
197
198                 if (is[1].containsY(y))
199                     drawHorizontalLine(is[0], is[1], y, renderBuffer,
200                             zoomedBitmap);
201                 else if (is[2].containsY(y))
202                     drawHorizontalLine(is[0], is[2], y, renderBuffer,
203                             zoomedBitmap);
204             } else if (is[1].containsY(y))
205                 if (is[2].containsY(y))
206                     drawHorizontalLine(is[1], is[2], y, renderBuffer,
207                             zoomedBitmap);
208
209     }
210
211     private void showBorders(final RenderingContext renderBuffer) {
212
213         final Point2D projectedPoint1 = coordinates[0].onScreenCoordinate;
214         final Point2D projectedPoint2 = coordinates[1].onScreenCoordinate;
215         final Point2D projectedPoint3 = coordinates[2].onScreenCoordinate;
216
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);
227     }
228
229 }