/*
* Renderer.java
*
* A scene renderer. Transforms vertices and may apply a special vertex
* transformation program.
*
* 2012 Brandon Reiss. All Rights Reserved. Do not duplicate or distribute
* without the author's consent.
*/
package com.brandonreiss.geometry;
import com.brandonreiss.math.*;
import java.awt.*;
import java.util.EnumSet;
import java.util.Arrays;
/// An 3d renderer.
public class Renderer {
public enum RenderFlags {
CULL_BACKFACE, CULL_DEPTH,
WIRE_FACES, WIRE_TRIANGLE,
ALPHA_BLEND,
SHADOW_MAP,
}
/// Vertex processing to occur just before rasterization.
public interface VertexProgram {
public void run(Matrix4x4 modelViewMat,
Vector3[] in, Vector3[] out);
}
/// The NOP vertex program.
private class ModelViewTransform implements VertexProgram {
@Override
public void run(Matrix4x4 modelViewMat,
Vector3[] in, Vector3[] out) {
final int numVertices = in.length;
assert(numVertices <= out.length);
for (int i = 0; i < numVertices; ++i) {
out[i].set(in[i]).transformCoord(modelViewMat);
}
}
}
/// The view matrix inverse.
public Matrix4x4 viewMatInv = Matrix4x4.matrixIdentity();
/// The projection matrix.
public Matrix4x4 projMat = Matrix4x4.matrixIdentity();
/// The screen pixel coordinates matrix.
private Matrix4x4 screenMat = Matrix4x4.matrixIdentity();
/// Width of target screen.
public Vector2 screenSize = new Vector2(640, 480);
/// The canvas.
public Graphics g = null;
public Color overrideColor = null;
/// Set of render flags.
//public EnumSet renderFlags = new EnumSet.noneOf(RenderFlags.class);
/// Vertex transform capacity.
private int tformVerticesCapacity = 3;
/// Transformed vertices.
private Vector3[] tformVertices = new Vector3[tformVerticesCapacity];
/// Projected vertex.
private Vector3 projectTmp = new Vector3();
int screenPtsCapacity = 3;
int[] screenPtsX = new int[screenPtsCapacity];
int[] screenPtsY = new int[screenPtsCapacity];
/// The default vertex shader.
private VertexProgram defaultVertexProgram = new ModelViewTransform();
/// Vertex shader program.
public VertexProgram vertexProgram = defaultVertexProgram;
/// Restore the default vertex program.
public void resetVertexProgram() {
vertexProgram = defaultVertexProgram;
}
public Renderer() {
for (int i = 0; i < tformVerticesCapacity; ++i) {
tformVertices[i] = new Vector3();
}
}
/// Render the mesh.
public void render(Mesh mesh) {
// Transform and light vertices.
Vector3[] vertices = mesh.vertices;
assert(null != vertices);
final int verticesLength = vertices.length;
if (verticesLength > tformVerticesCapacity) {
tformVerticesCapacity = verticesLength;
tformVertices = new Vector3[tformVerticesCapacity];
for (int i = 0; i < tformVerticesCapacity; ++i) {
tformVertices[i] = new Vector3();
}
}
Matrix4x4 modelViewMat = Matrix4x4.mul(viewMatInv, mesh.transform);
vertexProgram.run(modelViewMat, vertices, tformVertices);
// Set the color. This will eventually be per-vertex.
if (null != overrideColor) {
g.setColor(overrideColor);
}
else {
g.setColor(mesh.color);
}
// Screen space transform.
final double halfScreenW = screenSize.x / 2.0;
final double halfScreenH = screenSize.y / 2.0;
screenMat.identity();
screenMat.translate(halfScreenW, halfScreenH, 0);
screenMat.scale(halfScreenW, -halfScreenH, 1);
// Draw the faces.
int[][] faces = mesh.faces;
assert(null != faces);
for (int[] face : faces) {
assert(null != face);
final int faceLength = face.length;
assert(faceLength >= 3);
if (faceLength > screenPtsCapacity) {
screenPtsCapacity = faceLength;
screenPtsX = new int[faceLength];
screenPtsY = new int[faceLength];
}
// Project.
// TODO(reissb) -- 20120226 -- Impl Sutherland-Hodgman clip algorithm.
for (int fIdx = 0; fIdx < faceLength; ++fIdx) {
projectTmp.set(tformVertices[face[fIdx]]).transformCoord(projMat);
projectTmp.transformCoord(screenMat);
screenPtsX[fIdx] = (int)projectTmp.x;
screenPtsY[fIdx] = (int)projectTmp.y;
}
g.drawPolygon(screenPtsX, screenPtsY, faceLength);
}
}
}