//
package render;
import java.awt.*;
/*
* Created on Jan 16, 2004
*
*/
/**
* @author Du Nguyen
*
*/
public class InteractiveRenderer extends InteractiveMesh {
private String notice = "Copyright 2001 Ken Perlin. All rights reserved.";
//--- PUBLIC DATA FIELDS
/**
* {@link Renderer}object
*/
public Renderer renderer;
/**
* root of the scene {@link Geometry}
*/
public Geometry world;
/**
* Enables level of detail computation for meshes.
*/
public boolean enableLod = false;
/**
* Euler angle for camera positioning (horizontal view rotation).
*/
public double theta = 0;
/**
* Euler angle for camera positioning (vertical view rotation).
*/
public double phi = 0;
public int n, m;
//--- PUBLIC METHODS
/**
* Override this to animate.
* @param time system time
*/
public void ianimate(double time) {
}
/**
* This animate gets called by the main renderer
* @see render.InteractiveMesh#animate(double)
*/
public void animate(double time) {
run();
}
/**
* prevents the renderer from redrawing the scene.
*/
public void pause() {
isDamage = false;
}
/**
* Forces a refresh of the renderer. Sets isDamage true.
*/
public void damage() {
renderer.refresh();
isDamage = true;
}
/**
* Sets the field of view value.
* @param value
* @see Renderer#setFOV(double value)
*/
public void setFOV(double value) {
renderer.setFOV(value);
}
/**
* Sets the camera's focal length.
* @param value focal length
* @see Renderer#setFL(double value)
*/
public void setFL(double value) {
renderer.setFL(value);
}
/**
* Sets the background color ( RGB values range: 0..1)
* @param r red component 0..1
* @param g green component 0..1
* @param b blue component 0..1
*/
public void setBgColor(double r, double g, double b) {
renderer.setBgColor(r, g, b);
}
/**
* Adds light source with direction (x, y, z) & color (r, g, b).
* Arguments x,y,z indicate light direction. Arguments r,g,b indicate light
* direction.
* @see Renderer#addLight(double x,double y,double z, double r,double
* g,double b)
*/
public void addLight(double x, double y, double z, // ADD A LIGHT SOURCE
double r, double g, double b) {
renderer.addLight(x, y, z, r, g, b);
}
// PUBLIC METHODS TO LET THE PROGRAMMER MANIPULATE A MATRIX STACK
/**
* Sets current matrix to the identity matrix.
*/
public void identity() {
m().identity();
}
/**
* Returns the matrix at the top of the stack.
* @return the top matrix on the stack
*/
public Matrix m() {
return matrix[top];
}
/**
* Pops the top matrix from the stack.
*/
public void pop() {
top--;
}
/**
* Pushes a copy of the top matrix onto the stack.
*/
public void push() {
matrix[top + 1].copy(matrix[top]);
top++;
}
/**
* Rotates the top matrix around the X axis by angle t (radians).
* @param t angle in radians
*/
public void rotateX(double t) {
m().rotateX(t);
}
/**
* Rotates the top matrix around the Y axis by angle t (radians).
* @param t angle in radians
*/
public void rotateY(double t) {
m().rotateY(t);
}
/**
* Rotates the top matrix around the Z axis by angle t (radians).
* @param t angle in radians
*/
public void rotateZ(double t) {
m().rotateZ(t);
}
/**
* Rotates the top matrix around an arbitrary axis by angle t (radians).
* @param t angle in radians
* @param x
* @param y
* @param z
*/
public void rotate(double t, double x, double y, double z) {
m().rotate(y, x, y, z);
}
/**
* Scales the top matrix by x, y, z in their respective dimensions.
* @param x x scale factor
* @param y y scale factor
* @param z z scale factor
*/
public void scale(double x, double y, double z) {
m().scale(x, y, z);
}
/**
* Applies the top transformation matrix to {@link Geometry}s.
* @param s Geometry object
*/
public void transform(Geometry s) {
s.setMatrix(m());
}
/**
* Translates the top matrix by vector v.
* @param v an array of three doubles representing translations in the x,y,z
* directions.
*/
public void translate(double v[]) {
translate(v[0], v[1], v[2]);
}
/**
* Translates the top matrix by x, y, z.
* @param x - translation in the x direction.
* @param y - translation in the y direction.
* @param z - translation in the z direction.
*/
public void translate(double x, double y, double z) {
m().translate(x, y, z);
}
// PUBLIC METHODS TO LET THE PROGRAMMER DEFORM AN OBJECT
/**
* Deforms a geometric shape according to the beginning, middle, and end
* parameters in each dimension. For each dimesion the three parameters
* indicate the amount of deformation at each position.
*
* 0 - beginning, 1 - middle, 2 - end. To indicate infinity (a constant
* transformation) set two adjacent parameters to the same value. Setting
* all three parameters to the same value transforms the shape geometry
* consistently across the entire axis of the parameters.
*
* @param s shape object to be deformed
* @param x0 location of beginning of deformation along the x axis
* @param x1 location of beginning of deformation along the x axis
* @param x2 location of beginning of deformation along the x axis
* @param y0 location of beginning of deformation along the y axis
* @param y1 location of beginning of deformation along the y axis
* @param y2 location of beginning of deformation along the y axis
* @param z0 location of beginning of deformation along the z axis
* @param z1 location of beginning of deformation along the z axis
* @param z2 location of beginning of deformation along the z axis
* @return 1 if pull operation was successful, 0 otherwise
* @see Geometry#pull
*/
public int pull(Geometry s, double x0, double x1, double x2, double y0, double y1, double y2, double z0, double z1, double z2) {
return s.pull(m(), x0, x1, x2, y0, y1, y2, z0, z1, z2);
}
//--- SYSTEM LEVEL PUBLIC METHODS ---
/**
* Initializes the applet and internal variables. To initialize components
* of the application program use {@link #initialize()}.
* @see #initialize()
*/
public void init() {
startTime = getCurrentTime();
world = renderer.getWorld(); // GET ROOT OF GEOMETRY
for (int i = 0; i < matrix.length; i++)
matrix[i] = new Matrix();
identity();
initialize();
if (world != null && world.child != null)
for (int n = 0; n < world.child.length; n++)
if (world.child[n] != null && world.child[n].material != null)
mat = world.child[n].material;
}
/**
* Override this to initialize the application program.
*/
public void initialize() {
}
/**
* main renderer
*/
public void run() {
// MEASURE ELAPSED TIME AND FRAMERATE
elapsed += getCurrentTime() - currentTime;
currentTime = getCurrentTime();
if (isDamage) {
frameRate = .9 * frameRate + .1 / elapsed;
elapsed = 0;
// LET THE APPLICATION PROGRAMMER MOVE THINGS INTO PLACE
identity(); // APPLIC. MATRIX STARTS UNTRANSFORMED
//isDamage = true;
renderer.rotateView(theta, phi);
theta = phi = 0;
ianimate(currentTime - startTime);
// APPLICATION ANIMATES THINGS
// SHADE AND SCAN CONVERT GEOMETRY INTO FRAME BUFFER
renderer.refresh();
renderer.render();
// KEEP REFINING LEVEL OF DETAIL UNTIL PERFECT (WHEN LOD=1)
if (renderer.lod > 1) {
isDamage = true;
renderer.lod--;
}
}
}
/**
* Returns the Geometry of the frontmost object at the point (x, y) in the
* image (like a z-buffer value of geometries).
* @param x x coordinate in the image
* @param y y coordinate in the image
* @return the geometry of the foremost object at that location
*/
public Geometry getGeometry(int x, int y) {
if (renderer.bufferg == false) {
renderer.bufferg = true;
isDamage = true;
}
return renderer.getGeometry(x, y);
}
/**
* Returns the location in world space of the point (x, y) on the screen.
* @param x x coordinate in the image
* @param y y coordinate in the image
* @return true if there is an object at x, y , false otherwise
*/
public boolean getPoint(int x, int y, double xyz[]) {
if (renderer.bufferg == false) {
renderer.bufferg = true;
isDamage = true;
}
return renderer.getPoint(x, y, xyz);
}
public boolean mouseDown(Event evt, double[] xyz, Geometry g) {
int[] p = this.getXY(xyz);
this.mouseDown(p[0], p[1]);
return true;
}
public boolean mouseUp(Event evt, double[] xyz) {
int[] p = this.getXY(xyz);
this.mouseUp(p[0], p[1]);
damage = true;
return true;
}
public boolean mouseDrag(Event evt, double[] xyz) {
int[] p = getXY(xyz);
this.mouseDrag(p[0], p[1]);
return true;
}
//--- INTERACTIVE VIEW ROTATION EVENT CALLBACKS
// IF MOUSE MOVES TO LOWER LEFT CORNER, THEN DISPLAY FRAME RATE
/**
* Listener for mouse movement. If mouse is placed in the lower left cornder
* it displays the framerate.
* @return true
*/
public boolean mouseMove(int x, int y) {
if (seeMaterial) {
Geometry tmp = getGeometry(x, y);
if (tmp != null)
mat = tmp.material;
}
return true;
}
/**
* Listener for mouse down. Mouse down starts a view rotation.
* @return true
*/
public boolean mouseDown(int x, int y) {
Renderer.setDragging(true);
mx = x;
my = y;
return true;
}
/**
* Dragging the mouse causes gradual view rotation in the phi and theta
* directions.
*
* @param event Event
* @param x new x coordinate
* @param y new y coordinate
*/
public boolean mouseDrag(int x, int y) {
if (Renderer.isDragging()) {
theta += .03 * (double) (x - mx); // HORIZONTAL VIEW ROTATION
phi += .03 * (double) (y - my); // VERTICAL VIEW ROTATION
mx = x;
my = y;
}
if (frameRate < 10 && renderer.lod < 4)
if (enableLod)
renderer.lod++;
isDamage = true;
return true;
}
/**
* Listens for mouse release and controls aspects of the renderer.
* A release in the upper left corner toggles {@link Renderer#tableMode}.
* A release in the upper right corner toggle visibility of the
* {@link Material#table}display. When true, the current material table is
* displayed in the upper left corner of the window. Position of the mouse
* determines current material.
* A release in the lower right toggles {@link Renderer#showMesh}
* @param event Event
* @param x current x coordinate
* @param y current y coordinate
* @return true
*/
public boolean mouseUp(int x, int y) {
Renderer.setDragging(false);
if (x < 35 && y < 35) {
Renderer.tableMode = !Renderer.tableMode;
}
if (x > W - 35 && y < 35) {
seeMaterial = !seeMaterial;
renderer.bufferg = !renderer.bufferg;
damage();
}
if (x > W - 35 && y > H - 35) {
renderer.showMesh = !renderer.showMesh;
damage();
}
return true;
}
/**
* KeyListener sends commands to the processKey function
* @see java.awt.Component#keyUp(java.awt.Event, int)
*/
public boolean keyUp(int key) {
return processCommand(key);
}
/**
* Handles commands received (generally for unicode commands from the
* KeyListener, but also for commands from any other sources, like buttons
* from webpages) : various default control keys to modify render style (Use
* CTRL + key).
*
* 'e' - toggles {@link Renderer#showMesh}, that just displays the shapes
* as mesh wireframes
'l' - toggles {@link Renderer#getOutline()}which
* produces a sketch-line drawing rendition of the scene
'm' - toggles
* {@link Renderer#seeMesh}which determines mesh visibility
't' -
* toggles global texture manipulation method (MIP on/off) (@link
* Texture#useMIP)
*
*
* @param event Event
* @param key value of the key released
* @return true if one of the above keys was just released, false otherwise.
*/
public boolean processCommand(int key) {
switch (key) {
case 'e' - ('a' - 1) :
renderer.showMesh = !renderer.showMesh;
damage();
return true;
case 'l' - ('a' - 1) :
renderer.outline(-renderer.getOutline());
damage();
return true;
case 'm' - ('a' - 1) :
renderer.seeMesh = !renderer.seeMesh;
damage();
return true;
case 't' - ('a' - 1) :
Texture.useMIP = !Texture.useMIP;
damage();
return true;
}
return false;
}
//--- PRIVATE METHODS
// GET THE CURRENT TIME IN SECONDS
private double getCurrentTime() {
return System.currentTimeMillis() / 1000.;
}
//--- PRIVATE DATA FIELDS
/**
* Current mouse position
*/
protected int mx, my;
/**
* Flag to force a renderer refresh when true.
*/
protected boolean isDamage = true; // WHETHER WE NEED TO RECOMPUTE IMAGE
/**
* Holds actual time of initialization.
*/
protected double startTime = 0;
/**
* Holds current system time. Used to compute time elapsed between frames.
*/
protected double currentTime = 0;
/**
* Measures time elapsed from initialization.
*/
protected double elapsed = 0;
/**
* Contains current frame rate of the renderer
*/
protected double frameRate = 10;
private Matrix matrix[] = new Matrix[10]; // THE MATRIX STACK
private int top = 0; // MATRIX STACK POINTER
private boolean seeMaterial = false;
private Material mat; //points to the current material
/**
*
*/
public InteractiveRenderer() {
super();
}
/**
* @param m
* @param n
* @param w
* @param h
* @param pix
*/
public InteractiveRenderer(int m, int n, int w, int h) {
W = w;
H = h;
renderer = new Renderer();
pixels = renderer.init(W, H);
Geometry g = this.add();
texture = new Texture(pixels, W, H, "grid", false);
Material material = new Material();
material.setTexture(texture);
g.setMaterial(material);
g.setDoubleSided(true);
g.mesh(m, n);
g.name = "mesh";
init();
}
}