|
/*
DEVELOPING GAME IN JAVA
Caracteristiques
Editeur : NEW RIDERS
Auteur : BRACKEEN
Parution : 09 2003
Pages : 972
Isbn : 1-59273-005-1
Reliure : Paperback
Disponibilite : Disponible a la librairie
*/
import java.awt.AWTException;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.DisplayMode;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferUShort;
import java.awt.image.IndexColorModel;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import javax.imageio.ImageIO;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class ShadingTest2 extends TextureMapTest2 {
public static void main(String[] args) {
new ShadingTest2().run();
}
private List lights;
private float ambientLightIntensity;
public void init() {
ambientLightIntensity = .05f;
lights = new ArrayList();
lights.add(new PointLight3D(-100, 100, -975, 1f, 500));
lights.add(new PointLight3D(50, 150, -700, 1f, 500));
lights.add(new PointLight3D(2000, 2000, -2000, .1f, -1));
lights.add(new PointLight3D(-250, 250, -1200, 1f, 500));
super.init(LOW_RES_MODES);
}
public void setTexture(TexturedPolygon3D poly, Texture texture) {
ShadedSurface.createShadedSurface(poly, (ShadedTexture) texture,
lights, ambientLightIntensity);
}
public Texture loadTexture(String imageName) {
return Texture.createTexture(imageName, true);
}
public void createPolygonRenderer() {
viewWindow = new ViewWindow(0, 0, screen.getWidth(),
screen.getHeight(), (float) Math.toRadians(75));
Transform3D camera = new Transform3D(0, 100, 0);
polygonRenderer = new ShadedSurfacePolygonRenderer(camera, viewWindow);
}
}
/**
* The ShadedSurfacePolygonRenderer is a PolygonRenderer that renders polygons
* with ShadedSurfaces. It keeps track of built surfaces, and clears any
* surfaces that weren't used in the last rendered frame to save memory.
*/
class ShadedSurfacePolygonRenderer extends FastTexturedPolygonRenderer {
private List builtSurfaces = new LinkedList();
public ShadedSurfacePolygonRenderer(Transform3D camera,
ViewWindow viewWindow) {
this(camera, viewWindow, true);
}
public ShadedSurfacePolygonRenderer(Transform3D camera,
ViewWindow viewWindow, boolean eraseView) {
super(camera, viewWindow, eraseView);
}
public void endFrame(Graphics2D g) {
super.endFrame(g);
// clear all built surfaces that weren't used this frame.
Iterator i = builtSurfaces.iterator();
while (i.hasNext()) {
ShadedSurface surface = (ShadedSurface) i.next();
if (surface.isDirty()) {
surface.clearSurface();
i.remove();
} else {
surface.setDirty(true);
}
}
}
protected void drawCurrentPolygon(Graphics2D g) {
buildSurface();
super.drawCurrentPolygon(g);
}
/**
* Builds the surface of the polygon if it has a ShadedSurface that is
* cleared.
*/
protected void buildSurface() {
// build surface, if needed
if (sourcePolygon instanceof TexturedPolygon3D) {
Texture texture = ((TexturedPolygon3D) sourcePolygon).getTexture();
if (texture instanceof ShadedSurface) {
ShadedSurface surface = (ShadedSurface) texture;
if (surface.isCleared()) {
surface.buildSurface();
builtSurfaces.add(surface);
}
surface.setDirty(false);
}
}
}
}
class TextureMapTest2 extends GameCore3D {
public static void main(String[] args) {
new TextureMapTest2().run();
}
public void init() {
init(LOW_RES_MODES);
}
// create a house (convex polyhedra)
public void createPolygons() {
// create Textures
Texture wall = loadTexture("../images/wall1.png");
Texture roof = loadTexture("../images/roof1.png");
TexturedPolygon3D poly;
// walls
poly = new TexturedPolygon3D(new Vector3D(-200, 250, -1000),
new Vector3D(-200, 0, -1000), new Vector3D(200, 0, -1000),
new Vector3D(200, 250, -1000));
setTexture(poly, wall);
polygons.add(poly);
poly = new TexturedPolygon3D(new Vector3D(200, 250, -1400),
new Vector3D(200, 0, -1400), new Vector3D(-200, 0, -1400),
new Vector3D(-200, 250, -1400));
setTexture(poly, wall);
polygons.add(poly);
poly = new TexturedPolygon3D(new Vector3D(-200, 250, -1400),
new Vector3D(-200, 0, -1400), new Vector3D(-200, 0, -1000),
new Vector3D(-200, 250, -1000));
setTexture(poly, wall);
polygons.add(poly);
poly = new TexturedPolygon3D(new Vector3D(200, 250, -1000),
new Vector3D(200, 0, -1000), new Vector3D(200, 0, -1400),
new Vector3D(200, 250, -1400));
setTexture(poly, wall);
polygons.add(poly);
// roof
poly = new TexturedPolygon3D(new Vector3D(-200, 250, -1000),
new Vector3D(200, 250, -1000), new Vector3D(75, 400, -1200),
new Vector3D(-75, 400, -1200));
setTexture(poly, roof);
polygons.add(poly);
poly = new TexturedPolygon3D(new Vector3D(-200, 250, -1400),
new Vector3D(-200, 250, -1000), new Vector3D(-75, 400, -1200));
setTexture(poly, roof);
polygons.add(poly);
poly = new TexturedPolygon3D(new Vector3D(200, 250, -1400),
new Vector3D(-200, 250, -1400), new Vector3D(-75, 400, -1200),
new Vector3D(75, 400, -1200));
setTexture(poly, roof);
polygons.add(poly);
poly = new TexturedPolygon3D(new Vector3D(200, 250, -1000),
new Vector3D(200, 250, -1400), new Vector3D(75, 400, -1200));
setTexture(poly, roof);
polygons.add(poly);
}
public void setTexture(TexturedPolygon3D poly, Texture texture) {
Vector3D origin = poly.getVertex(0);
Vector3D dv = new Vector3D(poly.getVertex(1));
dv.subtract(origin);
Vector3D du = new Vector3D();
du.setToCrossProduct(poly.getNormal(), dv);
Rectangle3D textureBounds = new Rectangle3D(origin, du, dv, texture
.getWidth(), texture.getHeight());
poly.setTexture(texture, textureBounds);
}
public Texture loadTexture(String imageName) {
return Texture.createTexture(imageName);
}
public void createPolygonRenderer() {
viewWindow = new ViewWindow(0, 0, screen.getWidth(),
screen.getHeight(), (float) Math.toRadians(75));
Transform3D camera = new Transform3D(0, 100, 0);
polygonRenderer = new FastTexturedPolygonRenderer(camera, viewWindow);
}
}
/**
* The FastTexturedPolygonRenderer is a PolygonRenderer that efficiently renders
* Textures.
*/
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;
}
}
}
//================================================
// METHOD 4: reduce the number of divides
// (interpolate every 16 pixels)
// Also, apply a VM optimization by referring to
// the texture's class rather than it's parent class.
//================================================
// the following three ScanRenderers are the same, but refer
// to textures explicitly as either a PowerOf2Texture, a
// ShadedTexture, or a ShadedSurface.
// This allows HotSpot to do some inlining of the textures'
// getColor() method, which significantly increases
// performance.
public class PowerOf2TextureRenderer extends ScanRenderer {
public void render(int offset, int left, int right) {
PowerOf2Texture texture = (PowerOf2Texture) currentTexture;
float u = SCALE * a.getDotProduct(viewPos);
float v = SCALE * b.getDotProduct(viewPos);
float z = c.getDotProduct(viewPos);
float du = INTERP_SIZE * SCALE * a.x;
float dv = INTERP_SIZE * SCALE * b.x;
float dz = INTERP_SIZE * c.x;
int nextTx = (int) (u / z);
int nextTy = (int) (v / z);
int x = left;
while (x <= right) {
int tx = nextTx;
int ty = nextTy;
int maxLength = right - x + 1;
if (maxLength > INTERP_SIZE) {
u += du;
v += dv;
z += dz;
nextTx = (int) (u / z);
nextTy = (int) (v / z);
int dtx = (nextTx - tx) >> INTERP_SIZE_BITS;
int dty = (nextTy - ty) >> INTERP_SIZE_BITS;
int endOffset = offset + INTERP_SIZE;
while (offset < endOffset) {
doubleBufferData[offset++] = texture.getColor(
tx >> SCALE_BITS, ty >> SCALE_BITS);
tx += dtx;
ty += dty;
}
x += INTERP_SIZE;
} else {
// variable interpolation size
int interpSize = maxLength;
u += interpSize * SCALE * a.x;
v += interpSize * SCALE * b.x;
z += interpSize * c.x;
nextTx = (int) (u / z);
nextTy = (int) (v / z);
int dtx = (nextTx - tx) / interpSize;
int dty = (nextTy - ty) / interpSize;
int endOffset = offset + interpSize;
while (offset < endOffset) {
doubleBufferData[offset++] = texture.getColor(
tx >> SCALE_BITS, ty >> SCALE_BITS);
tx += dtx;
ty += dty;
}
x += interpSize;
}
}
}
}
public class ShadedTextureRenderer extends ScanRenderer {
public void render(int offset, int left, int right) {
ShadedTexture texture = (ShadedTexture) currentTexture;
float u = SCALE * a.getDotProduct(viewPos);
float v = SCALE * b.getDotProduct(viewPos);
float z = c.getDotProduct(viewPos);
float du = INTERP_SIZE * SCALE * a.x;
float dv = INTERP_SIZE * SCALE * b.x;
float dz = INTERP_SIZE * c.x;
int nextTx = (int) (u / z);
int nextTy = (int) (v / z);
int x = left;
while (x <= right) {
int tx = nextTx;
int ty = nextTy;
int maxLength = right - x + 1;
if (maxLength > INTERP_SIZE) {
u += du;
v += dv;
z += dz;
nextTx = (int) (u / z);
nextTy = (int) (v / z);
int dtx = (nextTx - tx) >> INTERP_SIZE_BITS;
int dty = (nextTy - ty) >> INTERP_SIZE_BITS;
int endOffset = offset + INTERP_SIZE;
while (offset < endOffset) {
doubleBufferData[offset++] = texture.getColor(
tx >> SCALE_BITS, ty >> SCALE_BITS);
tx += dtx;
ty += dty;
}
x += INTERP_SIZE;
} else {
// variable interpolation size
int interpSize = maxLength;
u += interpSize * SCALE * a.x;
v += interpSize * SCALE * b.x;
z += interpSize * c.x;
nextTx = (int) (u / z<
|