Better JavaDoc
[sixth-3d.git] / src / main / java / eu / svjatoslav / sixth / e3d / renderer / raster / shapes / basic / ForwardOrientedTexture.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;
6
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.math.Vertex;
11 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.AbstractCoordinateShape;
12 import eu.svjatoslav.sixth.e3d.renderer.raster.texture.Texture;
13 import eu.svjatoslav.sixth.e3d.renderer.raster.texture.TextureBitmap;
14
15 /**
16  * Base class for textures always facing the viewer.
17  * <p>
18  * This class implements the "billboard" rendering technique where the texture
19  * remains oriented towards the camera regardless of 3D position. The visible size
20  * is calculated based on distance from viewer (z-coordinate) and scale factor.
21  * <p>
22  * The texture mapping algorithm:
23  * 1. Calculates screen coverage based on perspective
24  * 2. Clips to viewport boundaries
25  * 3. Maps texture pixels to screen pixels using proportional scaling
26  */
27 public class ForwardOrientedTexture extends AbstractCoordinateShape {
28
29     private static final double SCALE_MULTIPLIER = 0.005;
30     public final Texture texture;
31
32     /**
33      * Scale of the texture object.
34      * <p>
35      * Object rendered visible size on the screen depends on underlying texture size and scale.
36      * <p>
37      * 0 means that object will be infinitely small.
38      * 1 in recommended value to maintain sharpness of the texture as seen by the viewer.
39      */
40     private double scale;
41
42     public ForwardOrientedTexture(final Point3D point, final double scale,
43                                   final Texture texture) {
44         super(new Vertex(point));
45         this.texture = texture;
46         setScale(scale);
47     }
48
49     /**
50      * Paint the texture on the screen (targetRenderingArea)
51      *
52      * @param targetRenderingArea the screen to paint on
53      */
54     @Override
55     public void paint(final RenderingContext targetRenderingArea) {
56
57         // distance from camera/viewer to center of the texture
58         final double z = coordinates[0].transformedCoordinate.z;
59
60         // compute forward oriented texture visible distance from center
61         final double visibleHorizontalDistanceFromCenter = (targetRenderingArea.width
62                 * scale * texture.primaryBitmap.width) / z;
63
64         final double visibleVerticalDistanceFromCenter = (targetRenderingArea.width
65                 * scale * texture.primaryBitmap.height) / z;
66
67         // compute visible pixel density, and get appropriate bitmap
68         final double zoom = (visibleHorizontalDistanceFromCenter * 2)
69                 / texture.primaryBitmap.width;
70
71         final TextureBitmap textureBitmap = texture.getZoomedBitmap(zoom);
72
73         final Point2D onScreenCoordinate = coordinates[0].onScreenCoordinate;
74
75         // compute Y
76         final int onScreenUncappedYStart = (int) (onScreenCoordinate.y - visibleVerticalDistanceFromCenter);
77         final int onScreenUncappedYEnd = (int) (onScreenCoordinate.y + visibleVerticalDistanceFromCenter);
78         final int onScreenUncappedHeight = onScreenUncappedYEnd - onScreenUncappedYStart;
79
80         int onScreenCappedYStart = onScreenUncappedYStart;
81         int onScreenCappedYEnd = onScreenUncappedYEnd;
82
83         // cap Y to upper screen border
84         if (onScreenCappedYStart < 0)
85             onScreenCappedYStart = 0;
86
87         // cap Y to lower screen border
88         if (onScreenCappedYEnd > targetRenderingArea.height)
89             onScreenCappedYEnd = targetRenderingArea.height;
90
91         // compute X
92         final int onScreenUncappedXStart = (int) (onScreenCoordinate.x - visibleHorizontalDistanceFromCenter);
93         final int onScreenUncappedXEnd = (int) (onScreenCoordinate.x + visibleHorizontalDistanceFromCenter);
94         final int onScreenUncappedWidth = onScreenUncappedXEnd - onScreenUncappedXStart;
95
96         // cap X to left screen border
97         int onScreenCappedXStart = onScreenUncappedXStart;
98         if (onScreenCappedXStart < 0)
99             onScreenCappedXStart = 0;
100
101         // cap X to right screen border
102         int onScreenCappedXEnd = onScreenUncappedXEnd;
103         if (onScreenCappedXEnd > targetRenderingArea.width)
104             onScreenCappedXEnd = targetRenderingArea.width;
105
106         final byte[] targetRenderingAreaBytes = targetRenderingArea.pixels;
107
108         final int textureWidth = textureBitmap.width;
109
110         for (int y = onScreenCappedYStart; y < onScreenCappedYEnd; y++) {
111
112             final int sourceBitmapScanlinePixel = ((textureBitmap.height * (y - onScreenUncappedYStart)) / onScreenUncappedHeight)
113                     * textureWidth;
114
115             int targetRenderingAreaOffset = ((y * targetRenderingArea.width) + onScreenCappedXStart) * 4;
116
117             for (int x = onScreenCappedXStart; x < onScreenCappedXEnd; x++) {
118
119                 final int sourceBitmapPixelAddress = (sourceBitmapScanlinePixel + ((textureWidth * (x - onScreenUncappedXStart)) / onScreenUncappedWidth)) * 4;
120
121                 textureBitmap.drawPixel(sourceBitmapPixelAddress, targetRenderingAreaBytes, targetRenderingAreaOffset);
122
123                 targetRenderingAreaOffset += 4;
124             }
125         }
126     }
127
128     /**
129      * Set the scale of the texture
130      *
131      * @param scale the scale of the texture
132      */
133     public void setScale(final double scale) {
134         this.scale = scale * SCALE_MULTIPLIER;
135     }
136
137     public Point3D getLocation() {
138         return coordinates[0].coordinate;
139     }
140
141 }