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;
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;
16 * Base class for textures always facing the viewer.
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.
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
27 public class ForwardOrientedTexture extends AbstractCoordinateShape {
29 private static final double SCALE_MULTIPLIER = 0.005;
30 public final Texture texture;
33 * Scale of the texture object.
35 * Object rendered visible size on the screen depends on underlying texture size and scale.
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.
42 public ForwardOrientedTexture(final Point3D point, final double scale,
43 final Texture texture) {
44 super(new Vertex(point));
45 this.texture = texture;
50 * Paint the texture on the screen (targetRenderingArea)
52 * @param targetRenderingArea the screen to paint on
55 public void paint(final RenderingContext targetRenderingArea) {
57 // distance from camera/viewer to center of the texture
58 final double z = coordinates[0].transformedCoordinate.z;
60 // compute forward oriented texture visible distance from center
61 final double visibleHorizontalDistanceFromCenter = (targetRenderingArea.width
62 * scale * texture.primaryBitmap.width) / z;
64 final double visibleVerticalDistanceFromCenter = (targetRenderingArea.width
65 * scale * texture.primaryBitmap.height) / z;
67 // compute visible pixel density, and get appropriate bitmap
68 final double zoom = (visibleHorizontalDistanceFromCenter * 2)
69 / texture.primaryBitmap.width;
71 final TextureBitmap textureBitmap = texture.getZoomedBitmap(zoom);
73 final Point2D onScreenCoordinate = coordinates[0].onScreenCoordinate;
76 final int onScreenUncappedYStart = (int) (onScreenCoordinate.y - visibleVerticalDistanceFromCenter);
77 final int onScreenUncappedYEnd = (int) (onScreenCoordinate.y + visibleVerticalDistanceFromCenter);
78 final int onScreenUncappedHeight = onScreenUncappedYEnd - onScreenUncappedYStart;
80 int onScreenCappedYStart = onScreenUncappedYStart;
81 int onScreenCappedYEnd = onScreenUncappedYEnd;
83 // cap Y to upper screen border
84 if (onScreenCappedYStart < 0)
85 onScreenCappedYStart = 0;
87 // cap Y to lower screen border
88 if (onScreenCappedYEnd > targetRenderingArea.height)
89 onScreenCappedYEnd = targetRenderingArea.height;
92 final int onScreenUncappedXStart = (int) (onScreenCoordinate.x - visibleHorizontalDistanceFromCenter);
93 final int onScreenUncappedXEnd = (int) (onScreenCoordinate.x + visibleHorizontalDistanceFromCenter);
94 final int onScreenUncappedWidth = onScreenUncappedXEnd - onScreenUncappedXStart;
96 // cap X to left screen border
97 int onScreenCappedXStart = onScreenUncappedXStart;
98 if (onScreenCappedXStart < 0)
99 onScreenCappedXStart = 0;
101 // cap X to right screen border
102 int onScreenCappedXEnd = onScreenUncappedXEnd;
103 if (onScreenCappedXEnd > targetRenderingArea.width)
104 onScreenCappedXEnd = targetRenderingArea.width;
106 final byte[] targetRenderingAreaBytes = targetRenderingArea.pixels;
108 final int textureWidth = textureBitmap.width;
110 for (int y = onScreenCappedYStart; y < onScreenCappedYEnd; y++) {
112 final int sourceBitmapScanlinePixel = ((textureBitmap.height * (y - onScreenUncappedYStart)) / onScreenUncappedHeight)
115 int targetRenderingAreaOffset = ((y * targetRenderingArea.width) + onScreenCappedXStart) * 4;
117 for (int x = onScreenCappedXStart; x < onScreenCappedXEnd; x++) {
119 final int sourceBitmapPixelAddress = (sourceBitmapScanlinePixel + ((textureWidth * (x - onScreenUncappedXStart)) / onScreenUncappedWidth)) * 4;
121 textureBitmap.drawPixel(sourceBitmapPixelAddress, targetRenderingAreaBytes, targetRenderingAreaOffset);
123 targetRenderingAreaOffset += 4;
129 * Set the scale of the texture
131 * @param scale the scale of the texture
133 public void setScale(final double scale) {
134 this.scale = scale * SCALE_MULTIPLIER;
137 public Point3D getLocation() {
138 return coordinates[0].coordinate;