/*
- * 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;
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,
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)
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() {
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<GlowingPoint> glowingPoints = Collections.newSetFromMap(new WeakHashMap<>());
private final Color color;
}
}
+
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) {
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;
import java.awt.image.DataBufferByte;
import java.awt.image.WritableRaster;
+import static java.util.Arrays.fill;
+
public class Texture {
public final TextureBitmap primaryBitmap;
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;
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) {
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) {
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;
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);
pixelCount++;
}
+ /**
+ * Resets the accumulator
+ */
public void reset() {
a = 0;
r = 0;
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);
public class TextureBitmap {
/**
- * Byte order: ABGR
+ * Byte order: Alpha, Blue, Green, Red
*/
public final byte[] bytes;
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) {