📄 fasttexturedpolygonrenderer.java
字号:
package com.brackeen.javagamebook.graphics3D;
import java.awt.*;
import java.awt.image.*;
import java.util.HashMap;
import com.brackeen.javagamebook.math3D.*;
import com.brackeen.javagamebook.graphics3D.texture.*;
/**
The FastTexturedPolygonRenderer is a PolygonRenderer that
efficiently renders Textures.
*/
public class FastTexturedPolygonRenderer extends PolygonRenderer {
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 a = new Vector3D();
protected Vector3D b = new Vector3D();
protected Vector3D c = new Vector3D();
protected Vector3D viewPos = new Vector3D();
protected BufferedImage doubleBuffer;
protected short[] doubleBufferData;
protected HashMap scanRenderers;
public FastTexturedPolygonRenderer(Transform3D camera,
ViewWindow viewWindow)
{
this(camera, viewWindow, true);
}
public FastTexturedPolygonRenderer(Transform3D camera,
ViewWindow viewWindow, boolean clearViewEveryFrame)
{
super(camera, viewWindow, clearViewEveryFrame);
}
protected void init() {
destPolygon = new TexturedPolygon3D();
scanConverter = new ScanConverter(viewWindow);
// create renders for each texture (HotSpot optimization)
scanRenderers = new HashMap();
scanRenderers.put(PowerOf2Texture.class,
new PowerOf2TextureRenderer());
scanRenderers.put(ShadedTexture.class,
new ShadedTextureRenderer());
scanRenderers.put(ShadedSurface.class,
new ShadedSurfaceRenderer());
}
public void startFrame(Graphics2D g) {
// 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) {
if (!(sourcePolygon instanceof TexturedPolygon3D)) {
// not a textured polygon - return
return;
}
TexturedPolygon3D poly = (TexturedPolygon3D)destPolygon;
Texture texture = poly.getTexture();
ScanRenderer scanRenderer = (ScanRenderer)
scanRenderers.get(texture.getClass());
scanRenderer.setTexture(texture);
Rectangle3D textureBounds = poly.getTextureBounds();
a.setToCrossProduct(textureBounds.getDirectionV(),
textureBounds.getOrigin());
b.setToCrossProduct(textureBounds.getOrigin(),
textureBounds.getDirectionU());
c.setToCrossProduct(textureBounds.getDirectionU(),
textureBounds.getDirectionV());
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 {
protected Texture currentTexture;
public void setTexture(Texture texture) {
this.currentTexture = texture;
}
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)0x0007;
}
}
}
//================================================
// METHOD 1: access pixel buffers directly
// and use textures sizes that are a power of 2
//================================================
public class Method1 extends ScanRenderer {
public void render(int offset, int left, int right) {
for (int x=left; x<=right; x++) {
int tx = (int)(a.getDotProduct(viewPos) /
c.getDotProduct(viewPos));
int ty = (int)(b.getDotProduct(viewPos) /
c.getDotProduct(viewPos));
doubleBufferData[offset++] =
currentTexture.getColor(tx, ty);
viewPos.x++;
}
}
}
//================================================
// METHOD 2: avoid redundant calculations
//================================================
public class Method2 extends ScanRenderer {
public void render(int offset, int left, int right) {
float u = a.getDotProduct(viewPos);
float v = b.getDotProduct(viewPos);
float z = c.getDotProduct(viewPos);
float du = a.x;
float dv = b.x;
float dz = c.x;
for (int x=left; x<=right; x++) {
doubleBufferData[offset++] =
currentTexture.getColor(
(int)(u/z), (int)(v/z));
u+=du;
v+=dv;
z+=dz;
}
}
}
//================================================
// METHOD 3: use ints instead of floats
//================================================
public class Method3 extends ScanRenderer {
public void render(int offset, int left, int right) {
int u = (int)(SCALE * a.getDotProduct(viewPos));
int v = (int)(SCALE * b.getDotProduct(viewPos));
int z = (int)(SCALE * c.getDotProduct(viewPos));
int du = (int)(SCALE * a.x);
int dv = (int)(SCALE * b.x);
int dz = (int)(SCALE * c.x);
for (int x=left; x<=right; x++) {
doubleBufferData[offset++] =
currentTexture.getColor(u/z, v/z);
u+=du;
v+=dv;
z+=dz;
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -