package com.brackeen.javagamebook.graphics3D;
import java.awt.*;
import java.awt.image.*;
import com.brackeen.javagamebook.math3D.*;
public class SolidZBufferedRenderer extends PolygonRenderer {
/**
The minimum distance for z-buffering. Larger values give
more accurate calculations for further distances.
*/
protected static final int MIN_DISTANCE = 12;
protected ZBuffer zBuffer;
// used for calculating depth
protected float w;
public static final int SCALE_BITS = 12;
public static final int SCALE = 1 << SCALE_BITS;
public static final int INTERP_SIZE_BITS = 4;
public static final int INTERP_SIZE = 1 << INTERP_SIZE_BITS;
protected Vector3D c = new Vector3D();
protected Vector3D viewPos = new Vector3D();
protected BufferedImage doubleBuffer;
protected short[] doubleBufferData;
ScanRenderer scanRenderer;
public SolidZBufferedRenderer(Transform3D camera, ViewWindow viewWindow) {
super(camera, viewWindow, true);
}
protected void init() {
destPolygon = new SolidPolygon3D();
scanConverter = new ScanConverter(viewWindow);
scanRenderer = new Method1();
}
public void startFrame(Graphics2D g) {
super.startFrame(g);
// initialize depth buffer
if (zBuffer == null ||
zBuffer.getWidth() != viewWindow.getWidth() ||
zBuffer.getHeight() != viewWindow.getHeight())
{
zBuffer = new ZBuffer(
viewWindow.getWidth(), viewWindow.getHeight());
}
else if (clearViewEveryFrame) {
zBuffer.clear();
}
// initialize buffer
if (doubleBuffer == null ||
doubleBuffer.getWidth() != viewWindow.getWidth() ||
doubleBuffer.getHeight() != viewWindow.getHeight())
{
doubleBuffer = new BufferedImage(
viewWindow.getWidth(), viewWindow.getHeight(),
BufferedImage.TYPE_USHORT_565_RGB);
//doubleBuffer = g.getDeviceConfiguration().createCompatibleImage(
//viewWindow.getWidth(), viewWindow.getHeight());
DataBuffer dest =
doubleBuffer.getRaster().getDataBuffer();
doubleBufferData = ((DataBufferUShort)dest).getData();
}
// clear view
if (clearViewEveryFrame) {
for (int i=0; i<doubleBufferData.length; i++) {
doubleBufferData[i] = 0;
}
}
}
public void endFrame(Graphics2D g) {
// draw the double buffer onto the screen
g.drawImage(doubleBuffer, viewWindow.getLeftOffset(),
viewWindow.getTopOffset(), null);
}
protected void drawCurrentPolygon(Graphics2D g) {
//a.setToCrossProduct(textureBounds.getDirectionV(),
//textureBounds.getOrigin());
//b.setToCrossProduct(textureBounds.getOrigin(),
//textureBounds.getDirectionU());
c.setTo(destPolygon.getNormal());
// w is used to compute depth at each pixel
w = SCALE * MIN_DISTANCE * Short.MAX_VALUE /
(viewWindow.getDistance() *
c.getDotProduct(destPolygon.getNormal()));
int y = scanConverter.getTopBoundary();
viewPos.y = viewWindow.convertFromScreenYToViewY(y);
viewPos.z = -viewWindow.getDistance();
while (y<=scanConverter.getBottomBoundary()) {
ScanConverter.Scan scan = scanConverter.getScan(y);
if (scan.isValid()) {
viewPos.x = viewWindow.
convertFromScreenXToViewX(scan.left);
int offset = (y - viewWindow.getTopOffset()) *
viewWindow.getWidth() +
(scan.left - viewWindow.getLeftOffset());
scanRenderer.render(offset, scan.left, scan.right);
}
y++;
viewPos.y--;
}
}
/**
The ScanRenderer class is an abstract inner class of
FastTexturedPolygonRenderer that provides an interface for
rendering a horizontal scan line.
*/
public abstract class ScanRenderer {
public abstract void render(int offset,
int left, int right);
}
//================================================
// FASTEST METHOD: no texture (for comparison)
//================================================
public class Method0 extends ScanRenderer {
public void render(int offset, int left, int right) {
for (int x=left; x<=right; x++) {
doubleBufferData[offset++] = (short)((SolidPolygon3D)destPolygon).getColor().getRGB();
}
}
}
public class Method1 extends ScanRenderer {
public void render(int offset, int left, int right) {
float z = c.getDotProduct(viewPos);
float dz = INTERP_SIZE * c.x;
int depth = (int)(w*z);
int dDepth = (int)(w*c.x);
for (int x=left; x<=right; x++) {
if (zBuffer.checkDepth(offset,
(short)(depth >> SCALE_BITS)))
{
doubleBufferData[offset] =
(short) ((SolidPolygon3D)destPolygon).getColor().getRGB();
}
offset++;
depth+=dDepth;
}
}
}
}