|
/*
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.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 ShadingTest1 extends TextureMapTest2 {
public static void main(String[] args) {
new ShadingTest1().run();
}
private GameAction brighterLight = new GameAction("brighter");
private GameAction dimmerLight = new GameAction("dimmer");
private PointLight3D light;
public void init() {
init(LOW_RES_MODES);
inputManager.mapToKey(brighterLight, KeyEvent.VK_PLUS);
inputManager.mapToKey(brighterLight, KeyEvent.VK_ADD);
inputManager.mapToKey(brighterLight, KeyEvent.VK_EQUALS);
inputManager.mapToKey(dimmerLight, KeyEvent.VK_SUBTRACT);
inputManager.mapToKey(dimmerLight, KeyEvent.VK_MINUS);
}
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);
ShadedTexturedPolygonRenderer polygonRenderer = new ShadedTexturedPolygonRenderer(
camera, viewWindow);
light = new PointLight3D(-500, 500, 0, 1f);
light.setDistanceFalloff(2000);
polygonRenderer.setLightSource(light);
polygonRenderer.setAmbientLightIntensity(.05f);
this.polygonRenderer = polygonRenderer;
}
public void draw(Graphics2D g) {
super.draw(g);
g.setColor(Color.WHITE);
g.drawString("Press +/- to change the light intensity.", 5,
fontSize * 2);
}
public void update(long elapsedTime) {
super.update(elapsedTime);
if (brighterLight.isPressed()) {
light.setIntensity(Math.min(5, light.getIntensity() + .005f
* elapsedTime));
}
if (dimmerLight.isPressed()) {
light.setIntensity(Math.max(0, light.getIntensity() - .005f
* elapsedTime));
}
}
}
/**
* The ShadedTexturedPolygonRenderer class is a PolygonRenderer that renders
* ShadedTextured dynamically with one light source. By default, the ambient
* light intensity is 0.5 and there is no point light.
*/
class ShadedTexturedPolygonRenderer extends FastTexturedPolygonRenderer {
private PointLight3D lightSource;
private float ambientLightIntensity = 0.5f;
private Vector3D directionToLight = new Vector3D();
public ShadedTexturedPolygonRenderer(Transform3D camera,
ViewWindow viewWindow) {
this(camera, viewWindow, true);
}
public ShadedTexturedPolygonRenderer(Transform3D camera,
ViewWindow viewWindow, boolean clearViewEveryFrame) {
super(camera, viewWindow, clearViewEveryFrame);
}
/**
* Gets the light source for this renderer.
*/
public PointLight3D getLightSource() {
return lightSource;
}
/**
* Sets the light source for this renderer.
*/
public void setLightSource(PointLight3D lightSource) {
this.lightSource = lightSource;
}
/**
* Gets the ambient light intensity.
*/
public float getAmbientLightIntensity() {
return ambientLightIntensity;
}
/**
* Sets the ambient light intensity, generally between 0 and 1.
*/
public void setAmbientLightIntensity(float i) {
ambientLightIntensity = i;
}
protected void drawCurrentPolygon(Graphics2D g) {
// set the shade level of the polygon before drawing it
if (sourcePolygon instanceof TexturedPolygon3D) {
TexturedPolygon3D poly = ((TexturedPolygon3D) sourcePolygon);
Texture texture = poly.getTexture();
if (texture instanceof ShadedTexture) {
calcShadeLevel();
}
}
super.drawCurrentPolygon(g);
}
/**
* Calculates the shade level of the current polygon
*/
private void calcShadeLevel() {
TexturedPolygon3D poly = (TexturedPolygon3D) sourcePolygon;
float intensity = 0;
if (lightSource != null) {
// average all the vertices in the polygon
directionToLight.setTo(0, 0, 0);
for (int i = 0; i < poly.getNumVertices(); i++) {
directionToLight.add(poly.getVertex(i));
}
directionToLight.divide(poly.getNumVertices());
// make the vector from the average vertex
// to the light
directionToLight.subtract(lightSource);
directionToLight.multiply(-1);
// get the distance to the light for falloff
float distance = directionToLight.length();
// compute the diffuse reflect
directionToLight.normalize();
Vector3D normal = poly.getNormal();
intensity = lightSource.getIntensity(distance)
* directionToLight.getDotProduct(normal);
intensity = Math.min(intensity, 1);
intensity = Math.max(intensity, 0);
}
intensity += ambientLightIntensity;
intensity = Math.min(intensity, 1);
intensity = Math.max(intensity, 0);
int level = Math.round(intensity * ShadedTexture.MAX_LEVEL);
((ShadedTexture) poly.getTexture()).setDefaultShadeLevel(level);
}
}
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;
&nb
|