From 24e68b2f99037dec8a3442030dcb15013c930652 Mon Sep 17 00:00:00 2001 From: Svjatoslav Agejenko Date: Wed, 22 Feb 2023 23:23:52 +0200 Subject: [PATCH] Improved code readability --- .../shapes/basic/ForwardOrientedTexture.java | 89 ++++++++++++------- .../raster/shapes/basic/GlowingPoint.java | 13 +++ .../e3d/renderer/raster/texture/Texture.java | 55 ++++++++++-- .../raster/texture/TextureBitmap.java | 55 +++++++----- 4 files changed, 149 insertions(+), 63 deletions(-) diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/ForwardOrientedTexture.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/ForwardOrientedTexture.java index 9467e92..7f13783 100644 --- a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/ForwardOrientedTexture.java +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/ForwardOrientedTexture.java @@ -1,5 +1,5 @@ /* - * Sixth 3D engine. Author: Svjatoslav Agejenko. + * Sixth 3D engine. Author: Svjatoslav Agejenko. * This project is released under Creative Commons Zero (CC0) license. */ package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic; @@ -13,8 +13,17 @@ import eu.svjatoslav.sixth.e3d.renderer.raster.texture.TextureBitmap; public class ForwardOrientedTexture extends AbstractCoordinateShape { - private static final double SIZE_MULTIPLIER = 0.005; + private static final double SCALE_MULTIPLIER = 0.005; public final Texture texture; + + /** + * Scale of the texture object. + * + * Object rendered visible size on the screen depends on underlying texture size and scale. + * + * 0 means that object will be infinitely small. + * 1 in recommended value to maintain sharpness of the texture as seen by the viewer. + */ private double scale; public ForwardOrientedTexture(final Point3D point, final double scale, @@ -24,18 +33,23 @@ public class ForwardOrientedTexture extends AbstractCoordinateShape { setScale(scale); } + /** + * Paint the texture on the screen (targetRenderingArea) + * + * @param targetRenderingArea the screen to paint on + */ @Override public void paint(final RenderingContext targetRenderingArea) { + // distance from camera/viewer to center of the texture final double z = coordinates[0].transformedCoordinate.z; + // compute forward oriented texture visible distance from center final double visibleHorizontalDistanceFromCenter = (targetRenderingArea.width - * scale * texture.primaryBitmap.width) - / z; + * scale * texture.primaryBitmap.width) / z; final double visibleVerticalDistanceFromCenter = (targetRenderingArea.width - * scale * texture.primaryBitmap.height) - / z; + * scale * texture.primaryBitmap.height) / z; // compute visible pixel density, and get appropriate bitmap final double zoom = (visibleHorizontalDistanceFromCenter * 2) @@ -43,61 +57,68 @@ public class ForwardOrientedTexture extends AbstractCoordinateShape { final TextureBitmap textureBitmap = texture.getZoomedBitmap(zoom); - final Point2D onScreenLocation = coordinates[0].onScreenCoordinate; + final Point2D onScreenCoordinate = coordinates[0].onScreenCoordinate; // compute Y - final int initialYStart = (int) (onScreenLocation.y - visibleVerticalDistanceFromCenter); - final int initialYEnd = (int) (onScreenLocation.y + visibleVerticalDistanceFromCenter); - final int maxYDistance = initialYEnd - initialYStart; + final int onScreenUncappedYStart = (int) (onScreenCoordinate.y - visibleVerticalDistanceFromCenter); + final int onScreenUncappedYEnd = (int) (onScreenCoordinate.y + visibleVerticalDistanceFromCenter); + final int onScreenUncappedHeight = onScreenUncappedYEnd - onScreenUncappedYStart; - int yStart = initialYStart; - int yEnd = initialYEnd; + int onScreenCappedYStart = onScreenUncappedYStart; + int onScreenCappedYEnd = onScreenUncappedYEnd; - if (yStart < 0) - yStart = 0; + // cap Y to upper screen border + if (onScreenCappedYStart < 0) + onScreenCappedYStart = 0; - if (yEnd > targetRenderingArea.height) - yEnd = targetRenderingArea.height; + // cap Y to lower screen border + if (onScreenCappedYEnd > targetRenderingArea.height) + onScreenCappedYEnd = targetRenderingArea.height; // compute X - final int initialXStart = (int) (onScreenLocation.x - visibleHorizontalDistanceFromCenter); - final int initialXEnd = (int) (onScreenLocation.x + visibleHorizontalDistanceFromCenter); - final int maxXDistance = initialXEnd - initialXStart; - - int xStart = initialXStart; - int xEnd = initialXEnd; + final int onScreenUncappedXStart = (int) (onScreenCoordinate.x - visibleHorizontalDistanceFromCenter); + final int onScreenUncappedXEnd = (int) (onScreenCoordinate.x + visibleHorizontalDistanceFromCenter); + final int onScreenUncappedWidth = onScreenUncappedXEnd - onScreenUncappedXStart; - if (xStart < 0) - xStart = 0; + // cap X to left screen border + int onScreenCappedXStart = onScreenUncappedXStart; + if (onScreenCappedXStart < 0) + onScreenCappedXStart = 0; - if (xEnd > targetRenderingArea.width) - xEnd = targetRenderingArea.width; + // cap X to right screen border + int onScreenCappedXEnd = onScreenUncappedXEnd; + if (onScreenCappedXEnd > targetRenderingArea.width) + onScreenCappedXEnd = targetRenderingArea.width; final byte[] targetRenderingAreaBytes = targetRenderingArea.pixels; final int textureWidth = textureBitmap.width; - for (int y = yStart; y < yEnd; y++) { + for (int y = onScreenCappedYStart; y < onScreenCappedYEnd; y++) { - final int relativeTextureOffset = ((textureBitmap.height * (y - initialYStart)) / maxYDistance) + final int sourceBitmapScanlinePixel = ((textureBitmap.height * (y - onScreenUncappedYStart)) / onScreenUncappedHeight) * textureWidth; - int targetRenderingAreaOffset = ((y * targetRenderingArea.width) + xStart) * 4; + int targetRenderingAreaOffset = ((y * targetRenderingArea.width) + onScreenCappedXStart) * 4; - for (int x = xStart; x < xEnd; x++) { + for (int x = onScreenCappedXStart; x < onScreenCappedXEnd; x++) { - final int textureOffset = (relativeTextureOffset + ((textureWidth * (x - initialXStart)) / maxXDistance)) * 4; + final int sourceBitmapPixelAddress = (sourceBitmapScanlinePixel + ((textureWidth * (x - onScreenUncappedXStart)) / onScreenUncappedWidth)) * 4; - textureBitmap.drawPixel(textureOffset, - targetRenderingAreaBytes, targetRenderingAreaOffset); + textureBitmap.drawPixel(sourceBitmapPixelAddress, targetRenderingAreaBytes, targetRenderingAreaOffset); targetRenderingAreaOffset += 4; } } } + /** + * Set the scale of the texture + * + * @param scale the scale of the texture + */ public void setScale(final double scale) { - this.scale = scale * SIZE_MULTIPLIER; + this.scale = scale * SCALE_MULTIPLIER; } public Point3D getLocation() { diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/GlowingPoint.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/GlowingPoint.java index a7b4784..14e213c 100644 --- a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/GlowingPoint.java +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/GlowingPoint.java @@ -18,6 +18,10 @@ import static java.lang.Math.sqrt; public class GlowingPoint extends ForwardOrientedTexture { private static final int TEXTURE_RESOLUTION_PIXELS = 100; + /** + * A set of all existing glowing points. + * Used to reuse textures of glowing points of the same color. + */ private static final Set glowingPoints = Collections.newSetFromMap(new WeakHashMap<>()); private final Color color; @@ -31,10 +35,15 @@ public class GlowingPoint extends ForwardOrientedTexture { } } + private static double computeScale(double pointSize) { return pointSize / ((double) (TEXTURE_RESOLUTION_PIXELS / 50f)); } + /** + * Returns a texture for a glowing point of the given color. + * The texture is a circle with a gradient from transparent to the given color. + */ private static Texture getTexture(final Color color) { // attempt to reuse texture from existing glowing point of the same color synchronized (glowingPoints) { @@ -47,6 +56,10 @@ public class GlowingPoint extends ForwardOrientedTexture { return createTexture(color); } + /** + * Creates a texture for a glowing point of the given color. + * The texture is a circle with a gradient from transparent to the given color. + */ private static Texture createTexture(final Color color) { final Texture texture = new Texture(TEXTURE_RESOLUTION_PIXELS, TEXTURE_RESOLUTION_PIXELS, 1); int halfResolution = TEXTURE_RESOLUTION_PIXELS / 2; diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/texture/Texture.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/texture/Texture.java index 0a677bd..6009bb8 100644 --- a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/texture/Texture.java +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/texture/Texture.java @@ -11,6 +11,8 @@ import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; import java.awt.image.WritableRaster; +import static java.util.Arrays.fill; + public class Texture { public final TextureBitmap primaryBitmap; @@ -59,6 +61,12 @@ public class Texture { return upSampled.length - 1; } + /** + * Downscale given bitmap by factor of 2. + * + * @param originalBitmap Bitmap to downscale. + * @return Downscaled bitmap. + */ public TextureBitmap downscaleBitmap(final TextureBitmap originalBitmap) { int newWidth = originalBitmap.width / 2; int newHeight = originalBitmap.height / 2; @@ -103,6 +111,11 @@ public class Texture { return downSampled[scaleFactor]; } + /** + * Returns the bitmap that should be used for rendering at the given zoom + * @param scaleFactor The upscale factor + * @return The bitmap + */ public TextureBitmap getUpscaledBitmap(final int scaleFactor) { if (upSampled[scaleFactor] == null) { @@ -118,6 +131,11 @@ public class Texture { return upSampled[scaleFactor]; } + /** + * Returns the bitmap that should be used for rendering at the given zoom + * @param zoomLevel The zoom level + * @return The bitmap + */ public TextureBitmap getZoomedBitmap(final double zoomLevel) { if (zoomLevel < 1) { @@ -136,14 +154,20 @@ public class Texture { return primaryBitmap; } + /** + * Resets the cache of resampled bitmaps + */ public void resetResampledBitmapCache() { - for (int i = 0; i < upSampled.length; i++) - upSampled[i] = null; + fill(upSampled, null); - for (int i = 0; i < downSampled.length; i++) - downSampled[i] = null; + fill(downSampled, null); } + /** + * Upscales the given bitmap by a factor of 2 + * @param originalBitmap The bitmap to upscale + * @return The upscaled bitmap + */ public TextureBitmap upscaleBitmap(final TextureBitmap originalBitmap) { final int newWidth = originalBitmap.width * 2; final int newHeight = originalBitmap.height * 2; @@ -180,10 +204,22 @@ public class Texture { return upScaled; } - public class ColorAccumulator { + /** + * A helper class that accumulates color values for a given area of a bitmap + */ + public static class ColorAccumulator { + // Accumulated color values public int r, g, b, a; + + // Number of pixels that have been accumulated public int pixelCount = 0; + /** + * Accumulates the color values of the given pixel + * @param bitmap The bitmap + * @param x The x coordinate of the pixel + * @param y The y coordinate of the pixel + */ public void accumulate(final TextureBitmap bitmap, final int x, final int y) { int address = bitmap.getAddress(x, y); @@ -202,6 +238,9 @@ public class Texture { pixelCount++; } + /** + * Resets the accumulator + */ public void reset() { a = 0; r = 0; @@ -210,6 +249,12 @@ public class Texture { pixelCount = 0; } + /** + * Stores the accumulated color values in the given bitmap + * @param bitmap The bitmap + * @param x The x coordinate of the pixel + * @param y The y coordinate of the pixel + */ public void storeResult(final TextureBitmap bitmap, final int x, final int y) { int address = bitmap.getAddress(x, y); diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/texture/TextureBitmap.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/texture/TextureBitmap.java index 00d0625..fe1e304 100644 --- a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/texture/TextureBitmap.java +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/texture/TextureBitmap.java @@ -9,7 +9,7 @@ import eu.svjatoslav.sixth.e3d.renderer.raster.Color; public class TextureBitmap { /** - * Byte order: ABGR + * Byte order: Alpha, Blue, Green, Red */ public final byte[] bytes; @@ -34,47 +34,54 @@ public class TextureBitmap { this(width, height, new byte[width * height * 4], multiplicationFactor); } - public void drawPixel(int textureOffset, - final byte[] targetRenderingAreaBytes, int targetRenderingAreaOffset) { + /** + * Transfer (render) one pixel from current {@link TextureBitmap} to target raster bitmap. + * + * @param sourceBitmapPixelAddress Pixel address within current {@link TextureBitmap} as indicated by its offset. + * @param targetBitmap Bitmap of the target image where pixel should be rendered to. + * @param targetBitmapPixelAddress Pixel location within target image where pixel should be rendered to. + */ + public void drawPixel(int sourceBitmapPixelAddress, + final byte[] targetBitmap, int targetBitmapPixelAddress) { - final int textureAlpha = bytes[textureOffset] & 0xff; + final int textureAlpha = bytes[sourceBitmapPixelAddress] & 0xff; if (textureAlpha == 0) return; if (textureAlpha == 255) { // skip reading of background for fully opaque pixels - targetRenderingAreaBytes[targetRenderingAreaOffset] = (byte) 255; + targetBitmap[targetBitmapPixelAddress] = (byte) 255; - targetRenderingAreaOffset++; - textureOffset++; - targetRenderingAreaBytes[targetRenderingAreaOffset] = bytes[textureOffset]; + targetBitmapPixelAddress++; + sourceBitmapPixelAddress++; + targetBitmap[targetBitmapPixelAddress] = bytes[sourceBitmapPixelAddress]; - targetRenderingAreaOffset++; - textureOffset++; - targetRenderingAreaBytes[targetRenderingAreaOffset] = bytes[textureOffset]; + targetBitmapPixelAddress++; + sourceBitmapPixelAddress++; + targetBitmap[targetBitmapPixelAddress] = bytes[sourceBitmapPixelAddress]; - targetRenderingAreaOffset++; - textureOffset++; - targetRenderingAreaBytes[targetRenderingAreaOffset] = bytes[textureOffset]; + targetBitmapPixelAddress++; + sourceBitmapPixelAddress++; + targetBitmap[targetBitmapPixelAddress] = bytes[sourceBitmapPixelAddress]; return; } final int backgroundAlpha = 255 - textureAlpha; - textureOffset++; + sourceBitmapPixelAddress++; - targetRenderingAreaBytes[targetRenderingAreaOffset] = (byte) 255; - targetRenderingAreaOffset++; + targetBitmap[targetBitmapPixelAddress] = (byte) 255; + targetBitmapPixelAddress++; - targetRenderingAreaBytes[targetRenderingAreaOffset] = (byte) ((((targetRenderingAreaBytes[targetRenderingAreaOffset] & 0xff) * backgroundAlpha) + ((bytes[textureOffset] & 0xff) * textureAlpha)) / 256); - textureOffset++; - targetRenderingAreaOffset++; + targetBitmap[targetBitmapPixelAddress] = (byte) ((((targetBitmap[targetBitmapPixelAddress] & 0xff) * backgroundAlpha) + ((bytes[sourceBitmapPixelAddress] & 0xff) * textureAlpha)) / 256); + sourceBitmapPixelAddress++; + targetBitmapPixelAddress++; - targetRenderingAreaBytes[targetRenderingAreaOffset] = (byte) ((((targetRenderingAreaBytes[targetRenderingAreaOffset] & 0xff) * backgroundAlpha) + ((bytes[textureOffset] & 0xff) * textureAlpha)) / 256); - textureOffset++; - targetRenderingAreaOffset++; + targetBitmap[targetBitmapPixelAddress] = (byte) ((((targetBitmap[targetBitmapPixelAddress] & 0xff) * backgroundAlpha) + ((bytes[sourceBitmapPixelAddress] & 0xff) * textureAlpha)) / 256); + sourceBitmapPixelAddress++; + targetBitmapPixelAddress++; - targetRenderingAreaBytes[targetRenderingAreaOffset] = (byte) ((((targetRenderingAreaBytes[targetRenderingAreaOffset] & 0xff) * backgroundAlpha) + ((bytes[textureOffset] & 0xff) * textureAlpha)) / 256); + targetBitmap[targetBitmapPixelAddress] = (byte) ((((targetBitmap[targetBitmapPixelAddress] & 0xff) * backgroundAlpha) + ((bytes[sourceBitmapPixelAddress] & 0xff) * textureAlpha)) / 256); } public void drawPixel(final int x, final int y, final Color color) { -- 2.20.1