• Java制作软光栅化渲染器_艾孜尔江撰


    1. Main.java :
    
    import java.io.IOException;
    
    /**
     * The sole purpose of this class is to hold the main method.
     *
     * Any other use should be placed in a separate class
     */
    public class Main
    {
    	// Lazy exception handling here. You can do something more interesting 
    	// depending on what you're doing
    	public static void main(String[] args) throws IOException
    	{
    		Display display = new Display(800, 600, "Software Rendering");
    		RenderContext target = display.GetFrameBuffer();
    
    		Bitmap texture = new Bitmap("./res/bricks.jpg");
    		Bitmap texture2 = new Bitmap("./res/bricks2.jpg");
    		Mesh monkeyMesh = new Mesh("./res/smoothMonkey0.obj");
    		Transform monkeyTransform = new Transform(new Vector4f(0,0.0f,3.0f));
    
    		Mesh terrainMesh = new Mesh("./res/terrain2.obj");
    		Transform terrainTransform = new Transform(new Vector4f(0,-1.0f,0.0f));
    
    		Camera camera = new Camera(new Matrix4f().InitPerspective((float)Math.toRadians(70.0f),
    					   	(float)target.GetWidth()/(float)target.GetHeight(), 0.1f, 1000.0f));
    		
    		float rotCounter = 0.0f;
    		long previousTime = System.nanoTime();
    		while(true)
    		{
    			long currentTime = System.nanoTime();
    			float delta = (float)((currentTime - previousTime)/1000000000.0);
    			previousTime = currentTime;
    
    			camera.Update(display.GetInput(), delta);
    			Matrix4f vp = camera.GetViewProjection();
    
    			monkeyTransform = monkeyTransform.Rotate(new Quaternion(new Vector4f(0,1,0), delta));
    
    			target.Clear((byte)0x00);
    			target.ClearDepthBuffer();
    			monkeyMesh.Draw(target, vp, monkeyTransform.GetTransformation(), texture2);
    			terrainMesh.Draw(target, vp, terrainTransform.GetTransformation(), texture);
    
    			display.SwapBuffers();
    		}
    	}
    }
    
    1. Bitmap.java :
    public class Bitmap
    {
    	/** The width, in pixels, of the image */
    	private final int  m_width;
    	/** The height, in pixels, of the image */
    	private final int  m_height;
    	/** Every pixel component in the image */
    	private final byte m_components[];
    
    	/** Basic getter */
    	public int GetWidth() { return m_width; }
    	/** Basic getter */
    	public int GetHeight() { return m_height; }
    
    	public byte GetComponent(int index) { return m_components[index]; }
    
    	/**
    	 * Creates and initializes a Bitmap.
    	 *
    	 * @param width The width, in pixels, of the image.
    	 * @param height The height, in pixels, of the image.
    	 */
    	public Bitmap(int width, int height)
    	{
    		m_width      = width;
    		m_height     = height;
    		m_components = new byte[m_width * m_height * 4];
    	}
    
    	public Bitmap(String fileName) throws IOException
    	{
    		int width = 0;
    		int height = 0;
    		byte[] components = null;
    
    		BufferedImage image = ImageIO.read(new File(fileName));
    
    		width = image.getWidth();
    		height = image.getHeight();
    
    		int imgPixels[] = new int[width * height];
    		image.getRGB(0, 0, width, height, imgPixels, 0, width);
    		components = new byte[width * height * 4];
    
    		for(int i = 0; i < width * height; i++)
    		{
    			int pixel = imgPixels[i];
    
    			components[i * 4]     = (byte)((pixel >> 24) & 0xFF); // A
    			components[i * 4 + 1] = (byte)((pixel      ) & 0xFF); // B
    			components[i * 4 + 2] = (byte)((pixel >> 8 ) & 0xFF); // G
    			components[i * 4 + 3] = (byte)((pixel >> 16) & 0xFF); // R
    		}
    
    		m_width = width;
    		m_height = height;
    		m_components = components;
    	}
    
    	/**
    	 * Sets every pixel in the bitmap to a specific shade of grey.
    	 *
    	 * @param shade The shade of grey to use. 0 is black, 255 is white.
    	 */
    	public void Clear(byte shade)
    	{
    		Arrays.fill(m_components, shade);
    	}
    
    	/**
    	 * Sets the pixel at (x, y) to the color specified by (a,b,g,r).
    	 *
    	 * @param x Pixel location in X
    	 * @param y Pixel location in Y
    	 * @param a Alpha component
    	 * @param b Blue component
    	 * @param g Green component
    	 * @param r Red component
    	 */
    	public void DrawPixel(int x, int y, byte a, byte b, byte g, byte r)
    	{
    		int index = (x + y * m_width) * 4;
    		m_components[index    ] = a;
    		m_components[index + 1] = b;
    		m_components[index + 2] = g;
    		m_components[index + 3] = r;
    	}
    
    	public void CopyPixel(int destX, int destY, int srcX, int srcY, Bitmap src, float lightAmt)
    	{
    		int destIndex = (destX + destY * m_width) * 4;
    		int srcIndex = (srcX + srcY * src.GetWidth()) * 4;
    		
    		m_components[destIndex    ] = (byte)((src.GetComponent(srcIndex) & 0xFF) * lightAmt);
    		m_components[destIndex + 1] = (byte)((src.GetComponent(srcIndex + 1) & 0xFF) * lightAmt);
    		m_components[destIndex + 2] = (byte)((src.GetComponent(srcIndex + 2) & 0xFF) * lightAmt);
    		m_components[destIndex + 3] = (byte)((src.GetComponent(srcIndex + 3) & 0xFF) * lightAmt);
    	}
    
    	/**
    	 * Copies the Bitmap into a BGR byte array.
    	 *
    	 * @param dest The byte array to copy into.
    	 */
    	public void CopyToByteArray(byte[] dest)
    	{
    		for(int i = 0; i < m_width * m_height; i++)
    		{
    			dest[i * 3    ] = m_components[i * 4 + 1];
    			dest[i * 3 + 1] = m_components[i * 4 + 2];
    			dest[i * 3 + 2] = m_components[i * 4 + 3];
    		}
    	}
    }
    
    1. Camera.java :
    import java.awt.event.KeyEvent;
    
    public class Camera
    {
    	private static final Vector4f Y_AXIS = new Vector4f(0,1,0);
    
    	private Transform m_transform;
    	private Matrix4f m_projection;
    
    	private Transform GetTransform()
    	{
    		return m_transform;
    	}
    
    	public Camera(Matrix4f projection)
    	{
    		this.m_projection = projection;
    		this.m_transform = new Transform();
    	}
    
    	public Matrix4f GetViewProjection()
    	{
    		Matrix4f cameraRotation = GetTransform().GetTransformedRot().Conjugate().ToRotationMatrix();
    		Vector4f cameraPos = GetTransform().GetTransformedPos().Mul(-1);
    
    		Matrix4f cameraTranslation = new Matrix4f().InitTranslation(cameraPos.GetX(), cameraPos.GetY(), cameraPos.GetZ());
    
    		return m_projection.Mul(cameraRotation.Mul(cameraTranslation));
    	}
    
    	public void Update(Input input, float delta)
    	{
    		// Speed and rotation amounts are hardcoded here.
    		// In a more general system, you might want to have them as variables.
    		final float sensitivityX = 2.66f * delta;
    		final float sensitivityY = 2.0f * delta;
    		final float movAmt = 5.0f * delta;
    
    		// Similarly, input keys are hardcoded here.
    		// As before, in a more general system, you might want to have these as variables.
    		if(input.GetKey(KeyEvent.VK_W))
    			Move(GetTransform().GetRot().GetForward(), movAmt);
    		if(input.GetKey(KeyEvent.VK_S))
    			Move(GetTransform().GetRot().GetForward(), -movAmt);
    		if(input.GetKey(KeyEvent.VK_A))
    			Move(GetTransform().GetRot().GetLeft(), movAmt);
    		if(input.GetKey(KeyEvent.VK_D))
    			Move(GetTransform().GetRot().GetRight(), movAmt);
    		
    		if(input.GetKey(KeyEvent.VK_RIGHT))
    			Rotate(Y_AXIS, sensitivityX);
    		if(input.GetKey(KeyEvent.VK_LEFT))
    			Rotate(Y_AXIS, -sensitivityX);
    		if(input.GetKey(KeyEvent.VK_DOWN))
    			Rotate(GetTransform().GetRot().GetRight(), sensitivityY);
    		if(input.GetKey(KeyEvent.VK_UP))
    			Rotate(GetTransform().GetRot().GetRight(), -sensitivityY);
    	}
    
    	private void Move(Vector4f dir, float amt)
    	{
    		m_transform = GetTransform().SetPos(GetTransform().GetPos().Add(dir.Mul(amt)));
    	}
    
    	private void Rotate(Vector4f axis, float angle)
    	{
    		m_transform = GetTransform().Rotate(new Quaternion(axis, angle));
    	}
    }
    
    
    1. Display.java :
    
    import java.awt.Canvas;
    
    import java.awt.Graphics;
    import java.awt.Dimension;
    import java.awt.image.BufferedImage;
    import java.awt.image.BufferStrategy;
    import java.awt.image.DataBufferByte;
    import javax.swing.JFrame;
    
    /**
     * Represents a window that can be drawn in using a software renderer.
     */
    public class Display extends Canvas
    {
    	/** The window being used for display */
    	private final JFrame         m_frame;
    	/** The bitmap representing the final image to display */
    	private final RenderContext  m_frameBuffer;
    	/** Used to display the framebuffer in the window */
    	private final BufferedImage  m_displayImage;
    	/** The pixels of the display image, as an array of byte components */
    	private final byte[]         m_displayComponents;
    	/** The buffers in the Canvas */
    	private final BufferStrategy m_bufferStrategy;
    	/** A graphics object that can draw into the Canvas's buffers */
    	private final Graphics       m_graphics;
    
    	private final Input          m_input;
    
    	public RenderContext GetFrameBuffer() { return m_frameBuffer; }
    	public Input GetInput() { return m_input; }
    
    	/**
    	 * Creates and initializes a new display.
    	 *
    	 * @param width  How wide the display is, in pixels.
    	 * @param height How tall the display is, in pixels.
    	 * @param title  The text displayed in the window's title bar.
    	 */
    	public Display(int width, int height, String title)
    	{
    		//Set the canvas's preferred, minimum, and maximum size to prevent
    		//unintentional resizing.
    		Dimension size = new Dimension(width, height);
    		setPreferredSize(size);
    		setMinimumSize(size);
    		setMaximumSize(size);
    
    		//Creates images used for display.
    		m_frameBuffer = new RenderContext(width, height);
    		m_displayImage = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
    		m_displayComponents = 
    			((DataBufferByte)m_displayImage.getRaster().getDataBuffer()).getData();
    
    		m_frameBuffer.Clear((byte)0x80);
    		m_frameBuffer.DrawPixel(100, 100, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF);
    
    		//Create a JFrame designed specifically to show this Display.
    		m_frame = new JFrame();
    		m_frame.add(this);
    		m_frame.pack();
    		m_frame.setResizable(false);
    		m_frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    		m_frame.setLocationRelativeTo(null);
    		m_frame.setTitle(title);
    		m_frame.setSize(width, height);
    		m_frame.setVisible(true);
    
    		//Allocates 1 display buffer, and gets access to it via the buffer
    		//strategy and a graphics object for drawing into it.
    		createBufferStrategy(1);
    		m_bufferStrategy = getBufferStrategy();
    		m_graphics = m_bufferStrategy.getDrawGraphics();
    		
    		m_input = new Input();
    		addKeyListener(m_input);
    		addFocusListener(m_input);
    		addMouseListener(m_input);
    		addMouseMotionListener(m_input);
    
    		setFocusable(true);
    		requestFocus();
    	}
    
    	/**
    	 * Displays in the window.
    	 */
    	public void SwapBuffers()
    	{
    		//Display components should be the byte array used for displayImage's pixels.
    		//Therefore, this call should effectively copy the frameBuffer into the
    		//displayImage.
    		m_frameBuffer.CopyToByteArray(m_displayComponents);
    		m_graphics.drawImage(m_displayImage, 0, 0, 
    			m_frameBuffer.GetWidth(), m_frameBuffer.GetHeight(), null);
    		m_bufferStrategy.show();
    	}
    }
    
    
    1. Edge.java :
    public class Edge
    {
    	private float m_x;
    	private float m_xStep;
    	private int m_yStart;
    	private int m_yEnd;
    	private float m_texCoordX;
    	private float m_texCoordXStep;
    	private float m_texCoordY;
    	private float m_texCoordYStep;
    	private float m_oneOverZ;
    	private float m_oneOverZStep;
    	private float m_depth;
    	private float m_depthStep;
    	private float m_lightAmt;
    	private float m_lightAmtStep;
    
    	public float GetX() { return m_x; }
    	public int GetYStart() { return m_yStart; }
    	public int GetYEnd() { return m_yEnd; }
    	public float GetTexCoordX() { return m_texCoordX; }
    	public float GetTexCoordY() { return m_texCoordY; }
    	public float GetOneOverZ() { return m_oneOverZ; }
    	public float GetDepth() { return m_depth; }
    	public float GetLightAmt() { return m_lightAmt; }
    
    	public Edge(Gradients gradients, Vertex minYVert, Vertex maxYVert, int minYVertIndex)
    	{
    		m_yStart = (int)Math.ceil(minYVert.GetY());
    		m_yEnd = (int)Math.ceil(maxYVert.GetY());
    
    		float yDist = maxYVert.GetY() - minYVert.GetY();
    		float xDist = maxYVert.GetX() - minYVert.GetX();
    
    		float yPrestep = m_yStart - minYVert.GetY();
    		m_xStep = (float)xDist/(float)yDist;
    		m_x = minYVert.GetX() + yPrestep * m_xStep;
    		float xPrestep = m_x - minYVert.GetX();
    
    		m_texCoordX = gradients.GetTexCoordX(minYVertIndex) +
    			gradients.GetTexCoordXXStep() * xPrestep +
    			gradients.GetTexCoordXYStep() * yPrestep;
    		m_texCoordXStep = gradients.GetTexCoordXYStep() + gradients.GetTexCoordXXStep() * m_xStep;
    
    		m_texCoordY = gradients.GetTexCoordY(minYVertIndex) +
    			gradients.GetTexCoordYXStep() * xPrestep +
    			gradients.GetTexCoordYYStep() * yPrestep;
    		m_texCoordYStep = gradients.GetTexCoordYYStep() + gradients.GetTexCoordYXStep() * m_xStep;
    
    		m_oneOverZ = gradients.GetOneOverZ(minYVertIndex) +
    			gradients.GetOneOverZXStep() * xPrestep +
    			gradients.GetOneOverZYStep() * yPrestep;
    		m_oneOverZStep = gradients.GetOneOverZYStep() + gradients.GetOneOverZXStep() * m_xStep;
    
    		m_depth = gradients.GetDepth(minYVertIndex) +
    			gradients.GetDepthXStep() * xPrestep +
    			gradients.GetDepthYStep() * yPrestep;
    		m_depthStep = gradients.GetDepthYStep() + gradients.GetDepthXStep() * m_xStep;
    
    		m_lightAmt = gradients.GetLightAmt(minYVertIndex) +
    			gradients.GetLightAmtXStep() * xPrestep +
    			gradients.GetLightAmtYStep() * yPrestep;
    		m_lightAmtStep = gradients.GetLightAmtYStep() + gradients.GetLightAmtXStep() * m_xStep;
    	}
    
    	public void Step()
    	{
    		m_x += m_xStep;
    		m_texCoordX += m_texCoordXStep;
    		m_texCoordY += m_texCoordYStep;
    		m_oneOverZ += m_oneOverZStep;
    		m_depth += m_depthStep;
    		m_lightAmt += m_lightAmtStep;
    	}
    }
    
    1. Gradients.java :
    public class Gradients
    {
    	private float[] m_texCoordX;
    	private float[] m_texCoordY;
    	private float[] m_oneOverZ;
    	private float[] m_depth;
    	private float[] m_lightAmt;
    
    	private float m_texCoordXXStep;
    	private float m_texCoordXYStep;
    	private float m_texCoordYXStep;
    	private float m_texCoordYYStep;
    	private float m_oneOverZXStep;
    	private float m_oneOverZYStep;
    	private float m_depthXStep;
    	private float m_depthYStep;
    	private float m_lightAmtXStep;
    	private float m_lightAmtYStep;
    
    	public float GetTexCoordX(int loc) { return m_texCoordX[loc]; }
    	public float GetTexCoordY(int loc) { return m_texCoordY[loc]; }
    	public float GetOneOverZ(int loc) { return m_oneOverZ[loc]; }
    	public float GetDepth(int loc) { return m_depth[loc]; }
    	public float GetLightAmt(int loc) { return m_lightAmt[loc]; }
    
    	public float GetTexCoordXXStep() { return m_texCoordXXStep; }
    	public float GetTexCoordXYStep() { return m_texCoordXYStep; }
    	public float GetTexCoordYXStep() { return m_texCoordYXStep; }
    	public float GetTexCoordYYStep() { return m_texCoordYYStep; }
    	public float GetOneOverZXStep() { return m_oneOverZXStep; }
    	public float GetOneOverZYStep() { return m_oneOverZYStep; }
    	public float GetDepthXStep() { return m_depthXStep; }
    	public float GetDepthYStep() { return m_depthYStep; }
    	public float GetLightAmtXStep() { return m_lightAmtXStep; }
    	public float GetLightAmtYStep() { return m_lightAmtYStep; }
    
    	private float CalcXStep(float[] values, Vertex minYVert, Vertex midYVert,
    			Vertex maxYVert, float oneOverdX)
    	{
    		return
    			(((values[1] - values[2]) *
    			(minYVert.GetY() - maxYVert.GetY())) -
    			((values[0] - values[2]) *
    			(midYVert.GetY() - maxYVert.GetY()))) * oneOverdX;
    	}
    
    	private float CalcYStep(float[] values, Vertex minYVert, Vertex midYVert,
    			Vertex maxYVert, float oneOverdY)
    	{
    		return
    			(((values[1] - values[2]) *
    			(minYVert.GetX() - maxYVert.GetX())) -
    			((values[0] - values[2]) *
    			(midYVert.GetX() - maxYVert.GetX()))) * oneOverdY;
    	}
    
    	private float Saturate(float val)
    	{
    		if(val > 1.0f)
    		{
    			return 1.0f;
    		}
    		if(val < 0.0f)
    		{
    			return 0.0f;
    		}
    		return val;
    	}
    
    	public Gradients(Vertex minYVert, Vertex midYVert, Vertex maxYVert)
    	{
    		float oneOverdX = 1.0f /
    			(((midYVert.GetX() - maxYVert.GetX()) *
    			(minYVert.GetY() - maxYVert.GetY())) -
    			((minYVert.GetX() - maxYVert.GetX()) *
    			(midYVert.GetY() - maxYVert.GetY())));
    
    		float oneOverdY = -oneOverdX;
    
    		m_oneOverZ = new float[3];
    		m_texCoordX = new float[3];
    		m_texCoordY = new float[3];
    		m_depth = new float[3];
    		m_lightAmt = new float[3];
    
    		m_depth[0] = minYVert.GetPosition().GetZ();
    		m_depth[1] = midYVert.GetPosition().GetZ();
    		m_depth[2] = maxYVert.GetPosition().GetZ();
    
    		Vector4f lightDir = new Vector4f(0,0,1);
    		m_lightAmt[0] = Saturate(minYVert.GetNormal().Dot(lightDir)) * 0.9f + 0.1f;
    		m_lightAmt[1] = Saturate(midYVert.GetNormal().Dot(lightDir)) * 0.9f + 0.1f;
    		m_lightAmt[2] = Saturate(maxYVert.GetNormal().Dot(lightDir)) * 0.9f + 0.1f;
    
    		// Note that the W component is the perspective Z value;
    		// The Z component is the occlusion Z value
    		m_oneOverZ[0] = 1.0f/minYVert.GetPosition().GetW();
    		m_oneOverZ[1] = 1.0f/midYVert.GetPosition().GetW();
    		m_oneOverZ[2] = 1.0f/maxYVert.GetPosition().GetW();
    
    		m_texCoordX[0] = minYVert.GetTexCoords().GetX() * m_oneOverZ[0];
    		m_texCoordX[1] = midYVert.GetTexCoords().GetX() * m_oneOverZ[1];
    		m_texCoordX[2] = maxYVert.GetTexCoords().GetX() * m_oneOverZ[2];
    
    		m_texCoordY[0] = minYVert.GetTexCoords().GetY() * m_oneOverZ[0];
    		m_texCoordY[1] = midYVert.GetTexCoords().GetY() * m_oneOverZ[1];
    		m_texCoordY[2] = maxYVert.GetTexCoords().GetY() * m_oneOverZ[2];
    
    		m_texCoordXXStep = CalcXStep(m_texCoordX, minYVert, midYVert, maxYVert, oneOverdX);
    		m_texCoordXYStep = CalcYStep(m_texCoordX, minYVert, midYVert, maxYVert, oneOverdY);
    		m_texCoordYXStep = CalcXStep(m_texCoordY, minYVert, midYVert, maxYVert, oneOverdX);
    		m_texCoordYYStep = CalcYStep(m_texCoordY, minYVert, midYVert, maxYVert, oneOverdY);
    		m_oneOverZXStep = CalcXStep(m_oneOverZ, minYVert, midYVert, maxYVert, oneOverdX);
    		m_oneOverZYStep = CalcYStep(m_oneOverZ, minYVert, midYVert, maxYVert, oneOverdY);
    		m_depthXStep = CalcXStep(m_depth, minYVert, midYVert, maxYVert, oneOverdX);
    		m_depthYStep = CalcYStep(m_depth, minYVert, midYVert, maxYVert, oneOverdY);
    		m_lightAmtXStep = CalcXStep(m_lightAmt, minYVert, midYVert, maxYVert, oneOverdX);
    		m_lightAmtYStep = CalcYStep(m_lightAmt, minYVert, midYVert, maxYVert, oneOverdY);
    	}
    }
    
    
    1. IndexedModel.java :
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class IndexedModel
    {
    	private List<Vector4f> m_positions;
    	private List<Vector4f> m_texCoords;
    	private List<Vector4f> m_normals;
    	private List<Vector4f> m_tangents;
    	private List<Integer>  m_indices;
    
    	public IndexedModel()
    	{
    		m_positions = new ArrayList<Vector4f>();
    		m_texCoords = new ArrayList<Vector4f>();
    		m_normals = new ArrayList<Vector4f>();
    		m_tangents = new ArrayList<Vector4f>();
    		m_indices = new ArrayList<Integer>();
    	}
    
    	public void CalcNormals()
    	{
    		for(int i = 0; i < m_indices.size(); i += 3)
    		{
    			int i0 = m_indices.get(i);
    			int i1 = m_indices.get(i + 1);
    			int i2 = m_indices.get(i + 2);
    
    			Vector4f v1 = m_positions.get(i1).Sub(m_positions.get(i0));
    			Vector4f v2 = m_positions.get(i2).Sub(m_positions.get(i0));
    
    			Vector4f normal = v1.Cross(v2).Normalized();
    
    			m_normals.set(i0, m_normals.get(i0).Add(normal));
    			m_normals.set(i1, m_normals.get(i1).Add(normal));
    			m_normals.set(i2, m_normals.get(i2).Add(normal));
    		}
    
    		for(int i = 0; i < m_normals.size(); i++)
    			m_normals.set(i, m_normals.get(i).Normalized());
    	}
    
    	public void CalcTangents()
    	{
    		for(int i = 0; i < m_indices.size(); i += 3)
    		{
    			int i0 = m_indices.get(i);
    			int i1 = m_indices.get(i + 1);
    			int i2 = m_indices.get(i + 2);
    
    			Vector4f edge1 = m_positions.get(i1).Sub(m_positions.get(i0));
    			Vector4f edge2 = m_positions.get(i2).Sub(m_positions.get(i0));
    
    			float deltaU1 = m_texCoords.get(i1).GetX() - m_texCoords.get(i0).GetX();
    			float deltaV1 = m_texCoords.get(i1).GetY() - m_texCoords.get(i0).GetY();
    			float deltaU2 = m_texCoords.get(i2).GetX() - m_texCoords.get(i0).GetX();
    			float deltaV2 = m_texCoords.get(i2).GetY() - m_texCoords.get(i0).GetY();
    
    			float dividend = (deltaU1*deltaV2 - deltaU2*deltaV1);
    			float f = dividend == 0 ? 0.0f : 1.0f/dividend;
    
    			Vector4f tangent = new Vector4f(
    					f * (deltaV2 * edge1.GetX() - deltaV1 * edge2.GetX()),
    					f * (deltaV2 * edge1.GetY() - deltaV1 * edge2.GetY()),
    					f * (deltaV2 * edge1.GetZ() - deltaV1 * edge2.GetZ()),
    					0);
    			
    			m_tangents.set(i0, m_tangents.get(i0).Add(tangent));
    			m_tangents.set(i1, m_tangents.get(i1).Add(tangent));
    			m_tangents.set(i2, m_tangents.get(i2).Add(tangent));
    		}
    
    		for(int i = 0; i < m_tangents.size(); i++)
    			m_tangents.set(i, m_tangents.get(i).Normalized());
    	}
    
    	public List<Vector4f> GetPositions() { return m_positions; }
    	public List<Vector4f> GetTexCoords() { return m_texCoords; }
    	public List<Vector4f> GetNormals() { return m_normals; }
    	public List<Vector4f> GetTangents() { return m_tangents; }
    	public List<Integer>  GetIndices() { return m_indices; }
    }
    
    
    1. Input.java :
    import java.awt.event.FocusEvent;
    import java.awt.event.FocusListener;
    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;
    
    /**
     * Stores the current state of any user input devices, and updates them with new
     * input events.
     * 
     * @author Benny Bobaganoosh (thebennybox@gmail.com)
     */
    public class Input implements KeyListener, FocusListener,
    		MouseListener, MouseMotionListener {
    	private boolean[] keys = new boolean[65536];
    	private boolean[] mouseButtons = new boolean[4];
    	private int mouseX = 0;
    	private int mouseY = 0;
    
    	/** Updates state when the mouse is dragged */
    	public void mouseDragged(MouseEvent e) {
    		mouseX = e.getX();
    		mouseY = e.getY();
    	}
    
    	/** Updates state when the mouse is moved */
    	public void mouseMoved(MouseEvent e) {
    		mouseX = e.getX();
    		mouseY = e.getY();
    	}
    
    	/** Updates state when the mouse is clicked */
    	public void mouseClicked(MouseEvent e) {
    	}
    
    	/** Updates state when the mouse enters the screen */
    	public void mouseEntered(MouseEvent e) {
    	}
    
    	/** Updates state when the mouse exits the screen */
    	public void mouseExited(MouseEvent e) {
    	}
    
    	/** Updates state when a mouse button is pressed */
    	public void mousePressed(MouseEvent e) {
    		int code = e.getButton();
    		if (code > 0 && code < mouseButtons.length)
    			mouseButtons[code] = true;
    	}
    
    	/** Updates state when a mouse button is released */
    	public void mouseReleased(MouseEvent e) {
    		int code = e.getButton();
    		if (code > 0 && code < mouseButtons.length)
    			mouseButtons[code] = false;
    	}
    
    	/** Updates state when the window gains focus */
    	public void focusGained(FocusEvent e) {
    	}
    
    	/** Updates state when the window loses focus */
    	public void focusLost(FocusEvent e) {
    		for (int i = 0; i < keys.length; i++)
    			keys[i] = false;
    		for (int i = 0; i < mouseButtons.length; i++)
    			mouseButtons[i] = false;
    	}
    
    	/** Updates state when a key is pressed */
    	public void keyPressed(KeyEvent e) {
    		int code = e.getKeyCode();
    		if (code > 0 && code < keys.length)
    			keys[code] = true;
    	}
    
    	/** Updates state when a key is released */
    	public void keyReleased(KeyEvent e) {
    		int code = e.getKeyCode();
    		if (code > 0 && code < keys.length)
    			keys[code] = false;
    	}
    
    	/** Updates state when a key is typed */
    	public void keyTyped(KeyEvent e) {
    	}
    
    	/**
    	 * Gets whether or not a particular key is currently pressed.
    	 * 
    	 * @param key The key to test
    	 * @return Whether or not key is currently pressed.
    	 */
    	public boolean GetKey(int key) {
    		return keys[key];
    	}
    
    	/**
    	 * Gets whether or not a particular mouse button is currently pressed.
    	 * 
    	 * @param button The button to test
    	 * @return Whether or not the button is currently pressed.
    	 */
    	public boolean GetMouse(int button) {
    		return mouseButtons[button];
    	}
    
    	/**
    	 * Gets the location of the mouse cursor on x, in pixels.
    	 * @return The location of the mouse cursor on x, in pixels
    	 */
    	public int GetMouseX() {
    		return mouseX;
    	}
    
    	/**
    	 * Gets the location of the mouse cursor on y, in pixels.
    	 * @return The location of the mouse cursor on y, in pixels
    	 */
    	public int GetMouseY() {
    		return mouseY;
    	}
    }
    
    
    1. Matrix4f.java :
    public class Matrix4f
    {
    	private float[][] m;
    
    	public Matrix4f()
    	{
    		m = new float[4][4];
    	}
    
    	public Matrix4f InitIdentity()
    	{
    		m[0][0] = 1;	m[0][1] = 0;	m[0][2] = 0;	m[0][3] = 0;
    		m[1][0] = 0;	m[1][1] = 1;	m[1][2] = 0;	m[1][3] = 0;
    		m[2][0] = 0;	m[2][1] = 0;	m[2][2] = 1;	m[2][3] = 0;
    		m[3][0] = 0;	m[3][1] = 0;	m[3][2] = 0;	m[3][3] = 1;
    
    		return this;
    	}
    
    	public Matrix4f InitScreenSpaceTransform(float halfWidth, float halfHeight)
    	{
    		m[0][0] = halfWidth;	m[0][1] = 0;	m[0][2] = 0;	m[0][3] = halfWidth - 0.5f;
    		m[1][0] = 0;	m[1][1] = -halfHeight;	m[1][2] = 0;	m[1][3] = halfHeight - 0.5f;
    		m[2][0] = 0;	m[2][1] = 0;	m[2][2] = 1;	m[2][3] = 0;
    		m[3][0] = 0;	m[3][1] = 0;	m[3][2] = 0;	m[3][3] = 1;
    
    		return this;
    	}
    
    	public Matrix4f InitTranslation(float x, float y, float z)
    	{
    		m[0][0] = 1;	m[0][1] = 0;	m[0][2] = 0;	m[0][3] = x;
    		m[1][0] = 0;	m[1][1] = 1;	m[1][2] = 0;	m[1][3] = y;
    		m[2][0] = 0;	m[2][1] = 0;	m[2][2] = 1;	m[2][3] = z;
    		m[3][0] = 0;	m[3][1] = 0;	m[3][2] = 0;	m[3][3] = 1;
    
    		return this;
    	}
    
    	public Matrix4f InitRotation(float x, float y, float z, float angle)
    	{
    		float sin = (float)Math.sin(angle);
    		float cos = (float)Math.cos(angle);
    
    		m[0][0] = cos+x*x*(1-cos); m[0][1] = x*y*(1-cos)-z*sin; m[0][2] = x*z*(1-cos)+y*sin; m[0][3] = 0;
    		m[1][0] = y*x*(1-cos)+z*sin; m[1][1] = cos+y*y*(1-cos);	m[1][2] = y*z*(1-cos)-x*sin; m[1][3] = 0;
    		m[2][0] = z*x*(1-cos)-y*sin; m[2][1] = z*y*(1-cos)+x*sin; m[2][2] = cos+z*z*(1-cos); m[2][3] = 0;
    		m[3][0] = 0;	m[3][1] = 0;	m[3][2] = 0;	m[3][3] = 1;
    
    		return this;
    	}
    
    	public Matrix4f InitRotation(float x, float y, float z)
    	{
    		Matrix4f rx = new Matrix4f();
    		Matrix4f ry = new Matrix4f();
    		Matrix4f rz = new Matrix4f();
    
    		rz.m[0][0] = (float)Math.cos(z);rz.m[0][1] = -(float)Math.sin(z);rz.m[0][2] = 0;				rz.m[0][3] = 0;
    		rz.m[1][0] = (float)Math.sin(z);rz.m[1][1] = (float)Math.cos(z);rz.m[1][2] = 0;					rz.m[1][3] = 0;
    		rz.m[2][0] = 0;					rz.m[2][1] = 0;					rz.m[2][2] = 1;					rz.m[2][3] = 0;
    		rz.m[3][0] = 0;					rz.m[3][1] = 0;					rz.m[3][2] = 0;					rz.m[3][3] = 1;
    
    		rx.m[0][0] = 1;					rx.m[0][1] = 0;					rx.m[0][2] = 0;					rx.m[0][3] = 0;
    		rx.m[1][0] = 0;					rx.m[1][1] = (float)Math.cos(x);rx.m[1][2] = -(float)Math.sin(x);rx.m[1][3] = 0;
    		rx.m[2][0] = 0;					rx.m[2][1] = (float)Math.sin(x);rx.m[2][2] = (float)Math.cos(x);rx.m[2][3] = 0;
    		rx.m[3][0] = 0;					rx.m[3][1] = 0;					rx.m[3][2] = 0;					rx.m[3][3] = 1;
    
    		ry.m[0][0] = (float)Math.cos(y);ry.m[0][1] = 0;					ry.m[0][2] = -(float)Math.sin(y);ry.m[0][3] = 0;
    		ry.m[1][0] = 0;					ry.m[1][1] = 1;					ry.m[1][2] = 0;					ry.m[1][3] = 0;
    		ry.m[2][0] = (float)Math.sin(y);ry.m[2][1] = 0;					ry.m[2][2] = (float)Math.cos(y);ry.m[2][3] = 0;
    		ry.m[3][0] = 0;					ry.m[3][1] = 0;					ry.m[3][2] = 0;					ry.m[3][3] = 1;
    
    		m = rz.Mul(ry.Mul(rx)).GetM();
    
    		return this;
    	}
    
    	public Matrix4f InitScale(float x, float y, float z)
    	{
    		m[0][0] = x;	m[0][1] = 0;	m[0][2] = 0;	m[0][3] = 0;
    		m[1][0] = 0;	m[1][1] = y;	m[1][2] = 0;	m[1][3] = 0;
    		m[2][0] = 0;	m[2][1] = 0;	m[2][2] = z;	m[2][3] = 0;
    		m[3][0] = 0;	m[3][1] = 0;	m[3][2] = 0;	m[3][3] = 1;
    
    		return this;
    	}
    
    	public Matrix4f InitPerspective(float fov, float aspectRatio, float zNear, float zFar)
    	{
    		float tanHalfFOV = (float)Math.tan(fov / 2);
    		float zRange = zNear - zFar;
    
    		m[0][0] = 1.0f / (tanHalfFOV * aspectRatio);	m[0][1] = 0;					m[0][2] = 0;	m[0][3] = 0;
    		m[1][0] = 0;						m[1][1] = 1.0f / tanHalfFOV;	m[1][2] = 0;	m[1][3] = 0;
    		m[2][0] = 0;						m[2][1] = 0;					m[2][2] = (-zNear -zFar)/zRange;	m[2][3] = 2 * zFar * zNear / zRange;
    		m[3][0] = 0;						m[3][1] = 0;					m[3][2] = 1;	m[3][3] = 0;
    
    
    		return this;
    	}
    
    	public Matrix4f InitOrthographic(float left, float right, float bottom, float top, float near, float far)
    	{
    		float width = right - left;
    		float height = top - bottom;
    		float depth = far - near;
    
    		m[0][0] = 2/width;m[0][1] = 0;	m[0][2] = 0;	m[0][3] = -(right + left)/width;
    		m[1][0] = 0;	m[1][1] = 2/height;m[1][2] = 0;	m[1][3] = -(top + bottom)/height;
    		m[2][0] = 0;	m[2][1] = 0;	m[2][2] = -2/depth;m[2][3] = -(far + near)/depth;
    		m[3][0] = 0;	m[3][1] = 0;	m[3][2] = 0;	m[3][3] = 1;
    
    		return this;
    	}
    
    	public Matrix4f InitRotation(Vector4f forward, Vector4f up)
    	{
    		Vector4f f = forward.Normalized();
    
    		Vector4f r = up.Normalized();
    		r = r.Cross(f);
    
    		Vector4f u = f.Cross(r);
    
    		return InitRotation(f, u, r);
    	}
    
    	public Matrix4f InitRotation(Vector4f forward, Vector4f up, Vector4f right)
    	{
    		Vector4f f = forward;
    		Vector4f r = right;
    		Vector4f u = up;
    
    		m[0][0] = r.GetX();	m[0][1] = r.GetY();	m[0][2] = r.GetZ();	m[0][3] = 0;
    		m[1][0] = u.GetX();	m[1][1] = u.GetY();	m[1][2] = u.GetZ();	m[1][3] = 0;
    		m[2][0] = f.GetX();	m[2][1] = f.GetY();	m[2][2] = f.GetZ();	m[2][3] = 0;
    		m[3][0] = 0;		m[3][1] = 0;		m[3][2] = 0;		m[3][3] = 1;
    
    		return this;
    	}
    
    	public Vector4f Transform(Vector4f r)
    	{
    		return new Vector4f(m[0][0] * r.GetX() + m[0][1] * r.GetY() + m[0][2] * r.GetZ() + m[0][3] * r.GetW(),
    		                    m[1][0] * r.GetX() + m[1][1] * r.GetY() + m[1][2] * r.GetZ() + m[1][3] * r.GetW(),
    		                    m[2][0] * r.GetX() + m[2][1] * r.GetY() + m[2][2] * r.GetZ() + m[2][3] * r.GetW(),
    							m[3][0] * r.GetX() + m[3][1] * r.GetY() + m[3][2] * r.GetZ() + m[3][3] * r.GetW());
    	}
    
    	public Matrix4f Mul(Matrix4f r)
    	{
    		Matrix4f res = new Matrix4f();
    
    		for(int i = 0; i < 4; i++)
    		{
    			for(int j = 0; j < 4; j++)
    			{
    				res.Set(i, j, m[i][0] * r.Get(0, j) +
    						m[i][1] * r.Get(1, j) +
    						m[i][2] * r.Get(2, j) +
    						m[i][3] * r.Get(3, j));
    			}
    		}
    
    		return res;
    	}
    
    	public float[][] GetM()
    	{
    		float[][] res = new float[4][4];
    
    		for(int i = 0; i < 4; i++)
    			for(int j = 0; j < 4; j++)
    				res[i][j] = m[i][j];
    
    		return res;
    	}
    
    	public float Get(int x, int y)
    	{
    		return m[x][y];
    	}
    
    	public void SetM(float[][] m)
    	{
    		this.m = m;
    	}
    
    	public void Set(int x, int y, float value)
    	{
    		m[x][y] = value;
    	}
    }
    
    1. Mesh.java :
    import java.util.List;
    import java.util.ArrayList;
    import java.io.IOException;
    
    public class Mesh
    {
    	private List<Vertex>  m_vertices;
    	private List<Integer> m_indices;
    	
    	public Mesh(String fileName) throws IOException
    	{
    		IndexedModel model = new OBJModel(fileName).ToIndexedModel();
    
    		m_vertices = new ArrayList<Vertex>();
    		for(int i = 0; i < model.GetPositions().size(); i++)
    		{
    			m_vertices.add(new Vertex(
    						model.GetPositions().get(i),
    						model.GetTexCoords().get(i),
    						model.GetNormals().get(i)));
    		}
    
    		m_indices = model.GetIndices();
    	}
    
    	public void Draw(RenderContext context, Matrix4f viewProjection, Matrix4f transform, Bitmap texture)
    	{
    		Matrix4f mvp = viewProjection.Mul(transform);
    		for(int i = 0; i < m_indices.size(); i += 3)
    		{
    			context.DrawTriangle(
    					m_vertices.get(m_indices.get(i)).Transform(mvp, transform),
    					m_vertices.get(m_indices.get(i + 1)).Transform(mvp, transform),
    					m_vertices.get(m_indices.get(i + 2)).Transform(mvp, transform),
    					texture);
    		}
    	}
    }
    
    
    1. OBJModel.java:
    import java.io.BufferedReader;
    import java.io.FileReader;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.HashMap;
    import java.util.Map;
    
    public class OBJModel
    {
    	private class OBJIndex
    	{
    		private int m_vertexIndex;
    		private int m_texCoordIndex;
    		private int m_normalIndex;
    
    		public int GetVertexIndex()   { return m_vertexIndex; }
    		public int GetTexCoordIndex() { return m_texCoordIndex; }
    		public int GetNormalIndex()   { return m_normalIndex; }
    
    		public void SetVertexIndex(int val)   { m_vertexIndex = val; }
    		public void SetTexCoordIndex(int val) { m_texCoordIndex = val; }
    		public void SetNormalIndex(int val)   { m_normalIndex = val; }
    
    		@Override
    		public boolean equals(Object obj)
    		{
    			OBJIndex index = (OBJIndex)obj;
    
    			return m_vertexIndex == index.m_vertexIndex
    					&& m_texCoordIndex == index.m_texCoordIndex
    					&& m_normalIndex == index.m_normalIndex;
    		}
    
    		@Override
    		public int hashCode()
    		{
    			final int BASE = 17;
    			final int MULTIPLIER = 31;
    
    			int result = BASE;
    
    			result = MULTIPLIER * result + m_vertexIndex;
    			result = MULTIPLIER * result + m_texCoordIndex;
    			result = MULTIPLIER * result + m_normalIndex;
    
    			return result;
    		}
    	}
    
    	private List<Vector4f> m_positions;
    	private List<Vector4f> m_texCoords;
    	private List<Vector4f> m_normals;
    	private List<OBJIndex> m_indices;
    	private boolean        m_hasTexCoords;
    	private boolean        m_hasNormals;
    
    	private static String[] RemoveEmptyStrings(String[] data)
    	{
    		List<String> result = new ArrayList<String>();
    		
    		for(int i = 0; i < data.length; i++)
    			if(!data[i].equals(""))
    				result.add(data[i]);
    		
    		String[] res = new String[result.size()];
    		result.toArray(res);
    		
    		return res;
    	}
    
    	public OBJModel(String fileName) throws IOException
    	{
    		m_positions = new ArrayList<Vector4f>();
    		m_texCoords = new ArrayList<Vector4f>();
    		m_normals = new ArrayList<Vector4f>();
    		m_indices = new ArrayList<OBJIndex>();
    		m_hasTexCoords = false;
    		m_hasNormals = false;
    
    		BufferedReader meshReader = null;
    
    		meshReader = new BufferedReader(new FileReader(fileName));
    		String line;
    
    		while((line = meshReader.readLine()) != null)
    		{
    			String[] tokens = line.split(" ");
    			tokens = RemoveEmptyStrings(tokens);
    
    			if(tokens.length == 0 || tokens[0].equals("#"))
    				continue;
    			else if(tokens[0].equals("v"))
    			{
    				m_positions.add(new Vector4f(Float.valueOf(tokens[1]),
    						Float.valueOf(tokens[2]),
    						Float.valueOf(tokens[3]),1));
    			}
    			else if(tokens[0].equals("vt"))
    			{
    				m_texCoords.add(new Vector4f(Float.valueOf(tokens[1]),
    						1.0f - Float.valueOf(tokens[2]),0,0));
    			}
    			else if(tokens[0].equals("vn"))
    			{
    				m_normals.add(new Vector4f(Float.valueOf(tokens[1]),
    						Float.valueOf(tokens[2]),
    						Float.valueOf(tokens[3]),0));
    			}
    			else if(tokens[0].equals("f"))
    			{
    				for(int i = 0; i < tokens.length - 3; i++)
    				{
    					m_indices.add(ParseOBJIndex(tokens[1]));
    					m_indices.add(ParseOBJIndex(tokens[2 + i]));
    					m_indices.add(ParseOBJIndex(tokens[3 + i]));
    				}
    			}
    		}
    
    		
    		meshReader.close();
    	}
    
    	public IndexedModel ToIndexedModel()
    	{
    		IndexedModel result = new IndexedModel();
    		IndexedModel normalModel = new IndexedModel();
    		Map<OBJIndex, Integer> resultIndexMap = new HashMap<OBJIndex, Integer>();
    		Map<Integer, Integer> normalIndexMap = new HashMap<Integer, Integer>();
    		Map<Integer, Integer> indexMap = new HashMap<Integer, Integer>();
    
    		for(int i = 0; i < m_indices.size(); i++)
    		{
    			OBJIndex currentIndex = m_indices.get(i);
    
    			Vector4f currentPosition = m_positions.get(currentIndex.GetVertexIndex());
    			Vector4f currentTexCoord;
    			Vector4f currentNormal;
    
    			if(m_hasTexCoords)
    				currentTexCoord = m_texCoords.get(currentIndex.GetTexCoordIndex());
    			else
    				currentTexCoord = new Vector4f(0,0,0,0);
    
    			if(m_hasNormals)
    				currentNormal = m_normals.get(currentIndex.GetNormalIndex());
    			else
    				currentNormal = new Vector4f(0,0,0,0);
    
    			Integer modelVertexIndex = resultIndexMap.get(currentIndex);
    
    			if(modelVertexIndex == null)
    			{
    				modelVertexIndex = result.GetPositions().size();
    				resultIndexMap.put(currentIndex, modelVertexIndex);
    
    				result.GetPositions().add(currentPosition);
    				result.GetTexCoords().add(currentTexCoord);
    				if(m_hasNormals)
    					result.GetNormals().add(currentNormal);
    			}
    
    			Integer normalModelIndex = normalIndexMap.get(currentIndex.GetVertexIndex());
    
    			if(normalModelIndex == null)
    			{
    				normalModelIndex = normalModel.GetPositions().size();
    				normalIndexMap.put(currentIndex.GetVertexIndex(), normalModelIndex);
    
    				normalModel.GetPositions().add(currentPosition);
    				normalModel.GetTexCoords().add(currentTexCoord);
    				normalModel.GetNormals().add(currentNormal);
    				normalModel.GetTangents().add(new Vector4f(0,0,0,0));
    			}
    
    			result.GetIndices().add(modelVertexIndex);
    			normalModel.GetIndices().add(normalModelIndex);
    			indexMap.put(modelVertexIndex, normalModelIndex);
    		}
    
    		if(!m_hasNormals)
    		{
    			normalModel.CalcNormals();
    
    			for(int i = 0; i < result.GetPositions().size(); i++)
    				result.GetNormals().add(normalModel.GetNormals().get(indexMap.get(i)));
    		}
    
    		normalModel.CalcTangents();
    
    		for(int i = 0; i < result.GetPositions().size(); i++)
    			result.GetTangents().add(normalModel.GetTangents().get(indexMap.get(i)));
    
    		return result;
    	}
    
    	private OBJIndex ParseOBJIndex(String token)
    	{
    		String[] values = token.split("/");
    
    		OBJIndex result = new OBJIndex();
    		result.SetVertexIndex(Integer.parseInt(values[0]) - 1);
    
    		if(values.length > 1)
    		{
    			if(!values[1].isEmpty())
    			{
    				m_hasTexCoords = true;
    				result.SetTexCoordIndex(Integer.parseInt(values[1]) - 1);
    			}
    
    			if(values.length > 2)
    			{
    				m_hasNormals = true;
    				result.SetNormalIndex(Integer.parseInt(values[2]) - 1);
    			}
    		}
    
    		return result;
    	}
    }
    
    
    1. Quaternion.java :
    public class Quaternion
    {
    	private float m_x;
    	private float m_y;
    	private float m_z;
    	private float m_w;
    
    	public Quaternion(float x, float y, float z, float w)
    	{
    		this.m_x = x;
    		this.m_y = y;
    		this.m_z = z;
    		this.m_w = w;
    	}
    
    	public Quaternion(Vector4f axis, float angle)
    	{
    		float sinHalfAngle = (float)Math.sin(angle / 2);
    		float cosHalfAngle = (float)Math.cos(angle / 2);
    
    		this.m_x = axis.GetX() * sinHalfAngle;
    		this.m_y = axis.GetY() * sinHalfAngle;
    		this.m_z = axis.GetZ() * sinHalfAngle;
    		this.m_w = cosHalfAngle;
    	}
    
    	public float Length()
    	{
    		return (float)Math.sqrt(m_x * m_x + m_y * m_y + m_z * m_z + m_w * m_w);
    	}
    	
    	public Quaternion Normalized()
    	{
    		float length = Length();
    		
    		return new Quaternion(m_x / length, m_y / length, m_z / length, m_w / length);
    	}
    	
    	public Quaternion Conjugate()
    	{
    		return new Quaternion(-m_x, -m_y, -m_z, m_w);
    	}
    
    	public Quaternion Mul(float r)
    	{
    		return new Quaternion(m_x * r, m_y * r, m_z * r, m_w * r);
    	}
    
    	public Quaternion Mul(Quaternion r)
    	{
    		float w_ = m_w * r.GetW() - m_x * r.GetX() - m_y * r.GetY() - m_z * r.GetZ();
    		float x_ = m_x * r.GetW() + m_w * r.GetX() + m_y * r.GetZ() - m_z * r.GetY();
    		float y_ = m_y * r.GetW() + m_w * r.GetY() + m_z * r.GetX() - m_x * r.GetZ();
    		float z_ = m_z * r.GetW() + m_w * r.GetZ() + m_x * r.GetY() - m_y * r.GetX();
    		
    		return new Quaternion(x_, y_, z_, w_);
    	}
    	
    	public Quaternion Mul(Vector4f r)
    	{
    		float w_ = -m_x * r.GetX() - m_y * r.GetY() - m_z * r.GetZ();
    		float x_ =  m_w * r.GetX() + m_y * r.GetZ() - m_z * r.GetY();
    		float y_ =  m_w * r.GetY() + m_z * r.GetX() - m_x * r.GetZ();
    		float z_ =  m_w * r.GetZ() + m_x * r.GetY() - m_y * r.GetX();
    		
    		return new Quaternion(x_, y_, z_, w_);
    	}
    
    	public Quaternion Sub(Quaternion r)
    	{
    		return new Quaternion(m_x - r.GetX(), m_y - r.GetY(), m_z - r.GetZ(), m_w - r.GetW());
    	}
    
    	public Quaternion Add(Quaternion r)
    	{
    		return new Quaternion(m_x + r.GetX(), m_y + r.GetY(), m_z + r.GetZ(), m_w + r.GetW());
    	}
    
    	public Matrix4f ToRotationMatrix()
    	{
    		Vector4f forward =  new Vector4f(2.0f * (m_x * m_z - m_w * m_y), 2.0f * (m_y * m_z + m_w * m_x), 1.0f - 2.0f * (m_x * m_x + m_y * m_y));
    		Vector4f up = new Vector4f(2.0f * (m_x * m_y + m_w * m_z), 1.0f - 2.0f * (m_x * m_x + m_z * m_z), 2.0f * (m_y * m_z - m_w * m_x));
    		Vector4f right = new Vector4f(1.0f - 2.0f * (m_y * m_y + m_z * m_z), 2.0f * (m_x * m_y - m_w * m_z), 2.0f * (m_x * m_z + m_w * m_y));
    
    		return new Matrix4f().InitRotation(forward, up, right);
    	}
    
    	public float Dot(Quaternion r)
    	{
    		return m_x * r.GetX() + m_y * r.GetY() + m_z * r.GetZ() + m_w * r.GetW();
    	}
    
    	public Quaternion NLerp(Quaternion dest, float lerpFactor, boolean shortest)
    	{
    		Quaternion correctedDest = dest;
    
    		if(shortest && this.Dot(dest) < 0)
    			correctedDest = new Quaternion(-dest.GetX(), -dest.GetY(), -dest.GetZ(), -dest.GetW());
    
    		return correctedDest.Sub(this).Mul(lerpFactor).Add(this).Normalized();
    	}
    
    	public Quaternion SLerp(Quaternion dest, float lerpFactor, boolean shortest)
    	{
    		final float EPSILON = 1e3f;
    
    		float cos = this.Dot(dest);
    		Quaternion correctedDest = dest;
    
    		if(shortest && cos < 0)
    		{
    			cos = -cos;
    			correctedDest = new Quaternion(-dest.GetX(), -dest.GetY(), -dest.GetZ(), -dest.GetW());
    		}
    
    		if(Math.abs(cos) >= 1 - EPSILON)
    			return NLerp(correctedDest, lerpFactor, false);
    
    		float sin = (float)Math.sqrt(1.0f - cos * cos);
    		float angle = (float)Math.atan2(sin, cos);
    		float invSin =  1.0f/sin;
    
    		float srcFactor = (float)Math.sin((1.0f - lerpFactor) * angle) * invSin;
    		float destFactor = (float)Math.sin((lerpFactor) * angle) * invSin;
    
    		return this.Mul(srcFactor).Add(correctedDest.Mul(destFactor));
    	}
    
    	//From Ken Shoemake's "Quaternion Calculus and Fast Animation" article
    	public Quaternion(Matrix4f rot)
    	{
    		float trace = rot.Get(0, 0) + rot.Get(1, 1) + rot.Get(2, 2);
    
    		if(trace > 0)
    		{
    			float s = 0.5f / (float)Math.sqrt(trace+ 1.0f);
    			m_w = 0.25f / s;
    			m_x = (rot.Get(1, 2) - rot.Get(2, 1)) * s;
    			m_y = (rot.Get(2, 0) - rot.Get(0, 2)) * s;
    			m_z = (rot.Get(0, 1) - rot.Get(1, 0)) * s;
    		}
    		else
    		{
    			if(rot.Get(0, 0) > rot.Get(1, 1) && rot.Get(0, 0) > rot.Get(2, 2))
    			{
    				float s = 2.0f * (float)Math.sqrt(1.0f + rot.Get(0, 0) - rot.Get(1, 1) - rot.Get(2, 2));
    				m_w = (rot.Get(1, 2) - rot.Get(2, 1)) / s;
    				m_x = 0.25f * s;
    				m_y = (rot.Get(1, 0) + rot.Get(0, 1)) / s;
    				m_z = (rot.Get(2, 0) + rot.Get(0, 2)) / s;
    			}
    			else if(rot.Get(1, 1) > rot.Get(2, 2))
    			{
    				float s = 2.0f * (float)Math.sqrt(1.0f + rot.Get(1, 1) - rot.Get(0, 0) - rot.Get(2, 2));
    				m_w = (rot.Get(2, 0) - rot.Get(0, 2)) / s;
    				m_x = (rot.Get(1, 0) + rot.Get(0, 1)) / s;
    				m_y = 0.25f * s;
    				m_z = (rot.Get(2, 1) + rot.Get(1, 2)) / s;
    			}
    			else
    			{
    				float s = 2.0f * (float)Math.sqrt(1.0f + rot.Get(2, 2) - rot.Get(0, 0) - rot.Get(1, 1));
    				m_w = (rot.Get(0, 1) - rot.Get(1, 0) ) / s;
    				m_x = (rot.Get(2, 0) + rot.Get(0, 2) ) / s;
    				m_y = (rot.Get(1, 2) + rot.Get(2, 1) ) / s;
    				m_z = 0.25f * s;
    			}
    		}
    
    		float length = (float)Math.sqrt(m_x * m_x + m_y * m_y + m_z * m_z + m_w * m_w);
    		m_x /= length;
    		m_y /= length;
    		m_z /= length;
    		m_w /= length;
    	}
    
    	public Vector4f GetForward()
    	{
    		return new Vector4f(0,0,1,1).Rotate(this);
    	}
    
    	public Vector4f GetBack()
    	{
    		return new Vector4f(0,0,-1,1).Rotate(this);
    	}
    
    	public Vector4f GetUp()
    	{
    		return new Vector4f(0,1,0,1).Rotate(this);
    	}
    
    	public Vector4f GetDown()
    	{
    		return new Vector4f(0,-1,0,1).Rotate(this);
    	}
    
    	public Vector4f GetRight()
    	{
    		return new Vector4f(1,0,0,1).Rotate(this);
    	}
    
    	public Vector4f GetLeft()
    	{
    		return new Vector4f(-1,0,0,1).Rotate(this);
    	}
    	
    	public float GetX()
    	{
    		return m_x;
    	}
    
    	public float GetY()
    	{
    		return m_y;
    	}
    
    	public float GetZ()
    	{
    		return m_z;
    	}
    
    	public float GetW()
    	{
    		return m_w;
    	}
    
    	public boolean equals(Quaternion r)
    	{
    		return m_x == r.GetX() && m_y == r.GetY() && m_z == r.GetZ() && m_w == r.GetW();
    	}
    }
    
    
    1. RenderContext.java :
    import java.util.List;
    import java.util.ArrayList;
    import java.util.Iterator;
    
    public class RenderContext extends Bitmap
    {
    	private float[] m_zBuffer;
    
    	public RenderContext(int width, int height)
    	{
    		super(width, height);
    		m_zBuffer = new float[width * height];
    	}
    
    	public void ClearDepthBuffer()
    	{
    		for(int i = 0; i < m_zBuffer.length; i++)
    		{
    			m_zBuffer[i] = Float.MAX_VALUE;
    		}
    	}
    
    	public void DrawTriangle(Vertex v1, Vertex v2, Vertex v3, Bitmap texture)
    	{
    		if(v1.IsInsideViewFrustum() && v2.IsInsideViewFrustum() && v3.IsInsideViewFrustum())
    		{
    			FillTriangle(v1, v2, v3, texture);
    			return;
    		}
    
    		List<Vertex> vertices = new ArrayList<>();
    		List<Vertex> auxillaryList = new ArrayList<>();
    		
    		vertices.add(v1);
    		vertices.add(v2);
    		vertices.add(v3);
    
    		if(ClipPolygonAxis(vertices, auxillaryList, 0) &&
    				ClipPolygonAxis(vertices, auxillaryList, 1) &&
    				ClipPolygonAxis(vertices, auxillaryList, 2))
    		{
    			Vertex initialVertex = vertices.get(0);
    
    			for(int i = 1; i < vertices.size() - 1; i++)
    			{
    				FillTriangle(initialVertex, vertices.get(i), vertices.get(i + 1), texture);
    			}
    		}
    	}
    
    	private boolean ClipPolygonAxis(List<Vertex> vertices, List<Vertex> auxillaryList,
    			int componentIndex)
    	{
    		ClipPolygonComponent(vertices, componentIndex, 1.0f, auxillaryList);
    		vertices.clear();
    
    		if(auxillaryList.isEmpty())
    		{
    			return false;
    		}
    
    		ClipPolygonComponent(auxillaryList, componentIndex, -1.0f, vertices);
    		auxillaryList.clear();
    
    		return !vertices.isEmpty();
    	}
    
    	private void ClipPolygonComponent(List<Vertex> vertices, int componentIndex, 
    			float componentFactor, List<Vertex> result)
    	{
    		Vertex previousVertex = vertices.get(vertices.size() - 1);
    		float previousComponent = previousVertex.Get(componentIndex) * componentFactor;
    		boolean previousInside = previousComponent <= previousVertex.GetPosition().GetW();
    
    		Iterator<Vertex> it = vertices.iterator();
    		while(it.hasNext())
    		{
    			Vertex currentVertex = it.next();
    			float currentComponent = currentVertex.Get(componentIndex) * componentFactor;
    			boolean currentInside = currentComponent <= currentVertex.GetPosition().GetW();
    
    			if(currentInside ^ previousInside)
    			{
    				float lerpAmt = (previousVertex.GetPosition().GetW() - previousComponent) /
    					((previousVertex.GetPosition().GetW() - previousComponent) - 
    					 (currentVertex.GetPosition().GetW() - currentComponent));
    
    				result.add(previousVertex.Lerp(currentVertex, lerpAmt));
    			}
    
    			if(currentInside)
    			{
    				result.add(currentVertex);
    			}
    
    			previousVertex = currentVertex;
    			previousComponent = currentComponent;
    			previousInside = currentInside;
    		}
    	}
    
    	private void FillTriangle(Vertex v1, Vertex v2, Vertex v3, Bitmap texture)
    	{
    		Matrix4f screenSpaceTransform = 
    				new Matrix4f().InitScreenSpaceTransform(GetWidth()/2, GetHeight()/2);
    		Matrix4f identity = new Matrix4f().InitIdentity();
    		Vertex minYVert = v1.Transform(screenSpaceTransform, identity).PerspectiveDivide();
    		Vertex midYVert = v2.Transform(screenSpaceTransform, identity).PerspectiveDivide();
    		Vertex maxYVert = v3.Transform(screenSpaceTransform, identity).PerspectiveDivide();
    
    		if(minYVert.TriangleAreaTimesTwo(maxYVert, midYVert) >= 0)
    		{
    			return;
    		}
    
    		if(maxYVert.GetY() < midYVert.GetY())
    		{
    			Vertex temp = maxYVert;
    			maxYVert = midYVert;
    			midYVert = temp;
    		}
    
    		if(midYVert.GetY() < minYVert.GetY())
    		{
    			Vertex temp = midYVert;
    			midYVert = minYVert;
    			minYVert = temp;
    		}
    
    		if(maxYVert.GetY() < midYVert.GetY())
    		{
    			Vertex temp = maxYVert;
    			maxYVert = midYVert;
    			midYVert = temp;
    		}
    
    		ScanTriangle(minYVert, midYVert, maxYVert, 
    				minYVert.TriangleAreaTimesTwo(maxYVert, midYVert) >= 0,
    				texture);
    	}
    
    	private void ScanTriangle(Vertex minYVert, Vertex midYVert, 
    			Vertex maxYVert, boolean handedness, Bitmap texture)
    	{
    		Gradients gradients = new Gradients(minYVert, midYVert, maxYVert);
    		Edge topToBottom    = new Edge(gradients, minYVert, maxYVert, 0);
    		Edge topToMiddle    = new Edge(gradients, minYVert, midYVert, 0);
    		Edge middleToBottom = new Edge(gradients, midYVert, maxYVert, 1);
    
    		ScanEdges(gradients, topToBottom, topToMiddle, handedness, texture);
    		ScanEdges(gradients, topToBottom, middleToBottom, handedness, texture);
    	}
    
    	private void ScanEdges(Gradients gradients, Edge a, Edge b, boolean handedness, Bitmap texture)
    	{
    		Edge left = a;
    		Edge right = b;
    		if(handedness)
    		{
    			Edge temp = left;
    			left = right;
    			right = temp;
    		}
    
    		int yStart = b.GetYStart();
    		int yEnd   = b.GetYEnd();
    		for(int j = yStart; j < yEnd; j++)
    		{
    			DrawScanLine(gradients, left, right, j, texture);
    			left.Step();
    			right.Step();
    		}
    	}
    
    	private void DrawScanLine(Gradients gradients, Edge left, Edge right, int j, Bitmap texture)
    	{
    		int xMin = (int)Math.ceil(left.GetX());
    		int xMax = (int)Math.ceil(right.GetX());
    		float xPrestep = xMin - left.GetX();
    
    //		float xDist = right.GetX() - left.GetX();
    //		float texCoordXXStep = (right.GetTexCoordX() - left.GetTexCoordX())/xDist;
    //		float texCoordYXStep = (right.GetTexCoordY() - left.GetTexCoordY())/xDist;
    //		float oneOverZXStep = (right.GetOneOverZ() - left.GetOneOverZ())/xDist;
    //		float depthXStep = (right.GetDepth() - left.GetDepth())/xDist;
    
    		// Apparently, now that stepping is actually on pixel centers, gradients are
    		// precise enough again.
    		float texCoordXXStep = gradients.GetTexCoordXXStep();
    		float texCoordYXStep = gradients.GetTexCoordYXStep();
    		float oneOverZXStep = gradients.GetOneOverZXStep();
    		float depthXStep = gradients.GetDepthXStep();
    		float lightAmtXStep = gradients.GetLightAmtXStep();
    
    		float texCoordX = left.GetTexCoordX() + texCoordXXStep * xPrestep;
    		float texCoordY = left.GetTexCoordY() + texCoordYXStep * xPrestep;
    		float oneOverZ = left.GetOneOverZ() + oneOverZXStep * xPrestep;
    		float depth = left.GetDepth() + depthXStep * xPrestep;
    		float lightAmt = left.GetLightAmt() + lightAmtXStep * xPrestep;
    
    		for(int i = xMin; i < xMax; i++)
    		{
    			int index = i + j * GetWidth();
    			if(depth < m_zBuffer[index])
    			{
    				m_zBuffer[index] = depth;
    				float z = 1.0f/oneOverZ;
    				int srcX = (int)((texCoordX * z) * (float)(texture.GetWidth() - 1) + 0.5f);
    				int srcY = (int)((texCoordY * z) * (float)(texture.GetHeight() - 1) + 0.5f);
    
    				CopyPixel(i, j, srcX, srcY, texture, lightAmt);
    			}
    
    			oneOverZ += oneOverZXStep;
    			texCoordX += texCoordXXStep;
    			texCoordY += texCoordYXStep;
    			depth += depthXStep;
    			lightAmt += lightAmtXStep;
    		}
    	}
    }
    
    
    1. Stars3D.java :
    import java.io.IOException;
    
    /**
     * Represents a 3D Star field that can be rendered into an image.
     */
    public class Stars3D
    {
    	/** How much the stars are spread out in 3D space, on average. */
    	private final float m_spread;
    	/** How quickly the stars move towards the camera */
    	private final float m_speed;
    
    	/** The star positions on the X axis */
    	private final float m_starX[];
    	/** The star positions on the Y axis */
    	private final float m_starY[];
    	/** The star positions on the Z axis */
    	private final float m_starZ[];
    
    	/**
    	 * Creates a new 3D star field in a usable state.
    	 *
    	 * @param numStars The number of stars in the star field
    	 * @param spread   How much the stars spread out, on average.
    	 * @param speed    How quickly the stars move towards the camera
    	 */
    	public Stars3D(int numStars, float spread, float speed) throws IOException
    	{
    		m_spread = spread;
    		m_speed = speed;
    
    		m_starX = new float[numStars];
    		m_starY = new float[numStars];
    		m_starZ = new float[numStars];
    
    		for(int i = 0; i < m_starX.length; i++)
    		{
    			InitStar(i);
    		}
    
    		m_bitmap = new Bitmap("./res/bricks.jpg");
    	}
    
    	private final Bitmap m_bitmap;
    
    	/**
    	 * Initializes a star to a new pseudo-random location in 3D space.
    	 *
    	 * @param i The index of the star to initialize.
    	 */
    	private void InitStar(int i)
    	{
    		//The random values have 0.5 subtracted from them and are multiplied
    		//by 2 to remap them from the range (0, 1) to (-1, 1).
    		m_starX[i] = 2 * ((float)Math.random() - 0.5f) * m_spread;
    		m_starY[i] = 2 * ((float)Math.random() - 0.5f) * m_spread;
    		//For Z, the random value is only adjusted by a small amount to stop
    		//a star from being generated at 0 on Z.
    		m_starZ[i] = ((float)Math.random() + 0.00001f) * m_spread;
    	}
    
    	/**
    	 * Updates every star to a new position, and draws the starfield in a
    	 * bitmap.
    	 *
    	 * @param target The bitmap to render to.
    	 * @param delta  How much time has passed since the last update.
    	 */
    	public void UpdateAndRender(RenderContext target, float delta)
    	{
    		final float tanHalfFOV = (float)Math.tan(Math.toRadians(90.0/2.0));
    		//Stars are drawn on a black background
    		target.Clear((byte)0x00);
    
    		float halfWidth  = target.GetWidth()/2.0f;
    		float halfHeight = target.GetHeight()/2.0f;
    		int triangleBuilderCounter = 0;
    
    		int x1 = 0;
    		int y1 = 0;
    		int x2 = 0;
    		int y2 = 0;
    		for(int i = 0; i < m_starX.length; i++)
    		{
    			//Update the Star.
    
    			//Move the star towards the camera which is at 0 on Z.
    			m_starZ[i] -= delta * m_speed;
    
    			//If star is at or behind the camera, generate a new position for
    			//it.
    			if(m_starZ[i] <= 0)
    			{
    				InitStar(i);
    			}
    
    			//Render the Star.
    
    			//Multiplying the position by (size/2) and then adding (size/2)
    			//remaps the positions from range (-1, 1) to (0, size)
    
    			//Division by z*tanHalfFOV moves things in to create a perspective effect.
    			int x = (int)((m_starX[i]/(m_starZ[i] * tanHalfFOV)) * halfWidth + halfWidth);
    			int y = (int)((m_starY[i]/(m_starZ[i] * tanHalfFOV)) * halfHeight + halfHeight);
    //
    //			int x = (int)((m_starX[i]) * halfWidth + halfWidth);
    //			int y = (int)((m_starY[i]) * halfHeight + halfHeight);
    
    
    			//If the star is not within range of the screen, then generate a
    			//new position for it.
    			if(x < 0 || x >= target.GetWidth() ||
    				(y < 0 || y >= target.GetHeight()))
    			{
    				InitStar(i);
    				continue;
    			}
    //			else
    //			{
    //				//Otherwise, it is safe to draw this star to the screen.
    //				target.DrawPixel(x, y, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF);
    //			}
    			triangleBuilderCounter++;
    			if(triangleBuilderCounter == 1)
    			{
    				x1 = x;
    				y1 = y;
    			}
    			else if(triangleBuilderCounter == 2)
    			{
    				x2 = x;
    				y2 = y;
    			}
    			else if(triangleBuilderCounter == 3)
    			{
    				triangleBuilderCounter = 0;
    //				Vertex v1 = new Vertex(new Vector4f(x1/400.0f - 1.0f, y1/300.0f - 1.0f, 0.0f, 1.0f), 
    //						new Vector4f(1.0f, 0.0f, 0.0f, 0.0f));
    //				Vertex v2 = new Vertex(new Vector4f(x2/400.0f - 1.0f, y2/300.0f - 1.0f, 0.0f, 1.0f), 
    //						new Vector4f(1.0f, 1.0f, 0.0f, 0.0f));
    //				Vertex v3 = new Vertex(new Vector4f(x/400.0f - 1.0f, y/300.0f - 1.0f, 0.0f, 1.0f), 
    //						new Vector4f(0.0f, 1.0f, 0.0f, 0.0f));
    //
    //				target.DrawTriangle(v1, v2, v3, m_bitmap);
    			}
    		}
    	}
    }
    
    
    1. Transform.java :
    public class Transform
    {
    	private Vector4f   m_pos;
    	private Quaternion m_rot;
    	private Vector4f   m_scale;
    
    	public Transform()
    	{
    		this(new Vector4f(0,0,0,0));
    	}
    
    	public Transform(Vector4f pos)
    	{
    		this(pos, new Quaternion(0,0,0,1), new Vector4f(1,1,1,1));
    	}
    
    	public Transform(Vector4f pos, Quaternion rot, Vector4f scale)
    	{
    		m_pos = pos;
    		m_rot = rot;
    		m_scale = scale;
    	}
    
    	public Transform SetPos(Vector4f pos)
    	{
    		return new Transform(pos, m_rot, m_scale);
    	}
    
    	public Transform Rotate(Quaternion rotation)
    	{
    		return new Transform(m_pos, rotation.Mul(m_rot).Normalized(), m_scale);
    	}
    
    	public Transform LookAt(Vector4f point, Vector4f up)
    	{
    		return Rotate(GetLookAtRotation(point, up));
    	}
    
    	public Quaternion GetLookAtRotation(Vector4f point, Vector4f up)
    	{
    		return new Quaternion(new Matrix4f().InitRotation(point.Sub(m_pos).Normalized(), up));
    	}
    
    	public Matrix4f GetTransformation()
    	{
    		Matrix4f translationMatrix = new Matrix4f().InitTranslation(m_pos.GetX(), m_pos.GetY(), m_pos.GetZ());
    		Matrix4f rotationMatrix = m_rot.ToRotationMatrix();
    		Matrix4f scaleMatrix = new Matrix4f().InitScale(m_scale.GetX(), m_scale.GetY(), m_scale.GetZ());
    
    		return translationMatrix.Mul(rotationMatrix.Mul(scaleMatrix));
    	}
    
    	public Vector4f GetTransformedPos()
    	{
    		return m_pos;
    	}
    
    	public Quaternion GetTransformedRot()
    	{
    		return m_rot;
    	}
    
    	public Vector4f GetPos()
    	{
    		return m_pos;
    	}
    
    	public Quaternion GetRot()
    	{
    		return m_rot;
    	}
    
    	public Vector4f GetScale()
    	{
    		return m_scale;
    	}
    }
    
    1. Vector4f.java :
    public class Vector4f
    {
    	private final float x;
    	private final float y;
    	private final float z;
    	private final float w;
    
    	public Vector4f(float x, float y, float z, float w)
    	{
    		this.x = x;
    		this.y = y;
    		this.z = z;
    		this.w = w;
    	}
    
    	public Vector4f(float x, float y, float z)
    	{
    		this(x, y, z, 1.0f);
    	}
    
    	public float Length()
    	{
    		return (float)Math.sqrt(x * x + y * y + z * z + w * w);
    	}
    
    	public float Max()
    	{
    		return Math.max(Math.max(x, y), Math.max(z, w));
    	}
    
    	public float Dot(Vector4f r)
    	{
    		return x * r.GetX() + y * r.GetY() + z * r.GetZ() + w * r.GetW();
    	}
    
    	public Vector4f Cross(Vector4f r)
    	{
    		float x_ = y * r.GetZ() - z * r.GetY();
    		float y_ = z * r.GetX() - x * r.GetZ();
    		float z_ = x * r.GetY() - y * r.GetX();
    
    		return new Vector4f(x_, y_, z_, 0);
    	}
    
    	public Vector4f Normalized()
    	{
    		float length = Length();
    
    		return new Vector4f(x / length, y / length, z / length, w / length);
    	}
    
    	public Vector4f Rotate(Vector4f axis, float angle)
    	{
    		float sinAngle = (float)Math.sin(-angle);
    		float cosAngle = (float)Math.cos(-angle);
    
    		return this.Cross(axis.Mul(sinAngle)).Add(           //Rotation on local X
    				(this.Mul(cosAngle)).Add(                     //Rotation on local Z
    						axis.Mul(this.Dot(axis.Mul(1 - cosAngle))))); //Rotation on local Y
    	}
    
    	public Vector4f Rotate(Quaternion rotation)
    	{
    		Quaternion conjugate = rotation.Conjugate();
    
    		Quaternion w = rotation.Mul(this).Mul(conjugate);
    
    		return new Vector4f(w.GetX(), w.GetY(), w.GetZ(), 1.0f);
    	}
    
    	public Vector4f Lerp(Vector4f dest, float lerpFactor)
    	{
    		return dest.Sub(this).Mul(lerpFactor).Add(this);
    	}
    
    	public Vector4f Add(Vector4f r)
    	{
    		return new Vector4f(x + r.GetX(), y + r.GetY(), z + r.GetZ(), w + r.GetW());
    	}
    
    	public Vector4f Add(float r)
    	{
    		return new Vector4f(x + r, y + r, z + r, w + r);
    	}
    
    	public Vector4f Sub(Vector4f r)
    	{
    		return new Vector4f(x - r.GetX(), y - r.GetY(), z - r.GetZ(), w - r.GetW());
    	}
    
    	public Vector4f Sub(float r)
    	{
    		return new Vector4f(x - r, y - r, z - r, w - r);
    	}
    
    	public Vector4f Mul(Vector4f r)
    	{
    		return new Vector4f(x * r.GetX(), y * r.GetY(), z * r.GetZ(), w * r.GetW());
    	}
    
    	public Vector4f Mul(float r)
    	{
    		return new Vector4f(x * r, y * r, z * r, w * r);
    	}
    
    	public Vector4f Div(Vector4f r)
    	{
    		return new Vector4f(x / r.GetX(), y / r.GetY(), z / r.GetZ(), w / r.GetW());
    	}
    
    	public Vector4f Div(float r)
    	{
    		return new Vector4f(x / r, y / r, z / r, w / r);
    	}
    
    	public Vector4f Abs()
    	{
    		return new Vector4f(Math.abs(x), Math.abs(y), Math.abs(z), Math.abs(w));
    	}
    
    	public String toString()
    	{
    		return "(" + x + ", " + y + ", " + z + ", " + w + ")";
    	}
    
    	public float GetX()
    	{
    		return x;
    	}
    
    	public float GetY()
    	{
    		return y;
    	}
    
    	public float GetZ()
    	{
    		return z;
    	}
    
    	public float GetW()
    	{
    		return w;
    	}
    
    	public boolean equals(Vector4f r)
    	{
    		return x == r.GetX() && y == r.GetY() && z == r.GetZ() && w == r.GetW();
    	}
    }
    
    1. Vertex.java :
    public class Vertex
    {
    	private Vector4f m_pos;
    	private Vector4f m_texCoords;
    	private Vector4f m_normal;
    
    	/** Basic Getter */
    	public float GetX() { return m_pos.GetX(); }
    	/** Basic Getter */
    	public float GetY() { return m_pos.GetY(); }
    
    	public Vector4f GetPosition() { return m_pos; }
    	public Vector4f GetTexCoords() { return m_texCoords; }
    	public Vector4f GetNormal() { return m_normal; }
    
    	/**
    	 * Creates a new Vertex in a usable state.
    	 */
    	public Vertex(Vector4f pos, Vector4f texCoords, Vector4f normal)
    	{
    		m_pos = pos;
    		m_texCoords = texCoords;
    		m_normal = normal;
    	}
    
    	public Vertex Transform(Matrix4f transform, Matrix4f normalTransform)
    	{
    		// The normalized here is important if you're doing scaling.
    		return new Vertex(transform.Transform(m_pos), m_texCoords, 
    				normalTransform.Transform(m_normal).Normalized());
    	}
    
    	public Vertex PerspectiveDivide()
    	{
    		return new Vertex(new Vector4f(m_pos.GetX()/m_pos.GetW(), m_pos.GetY()/m_pos.GetW(), 
    						m_pos.GetZ()/m_pos.GetW(), m_pos.GetW()),	
    				m_texCoords, m_normal);
    	}
    
    	public float TriangleAreaTimesTwo(Vertex b, Vertex c)
    	{
    		float x1 = b.GetX() - m_pos.GetX();
    		float y1 = b.GetY() - m_pos.GetY();
    
    		float x2 = c.GetX() - m_pos.GetX();
    		float y2 = c.GetY() - m_pos.GetY();
    
    		return (x1 * y2 - x2 * y1);
    	}
    
    	public Vertex Lerp(Vertex other, float lerpAmt)
    	{
    		return new Vertex(
    				m_pos.Lerp(other.GetPosition(), lerpAmt),
    				m_texCoords.Lerp(other.GetTexCoords(), lerpAmt),
    				m_normal.Lerp(other.GetNormal(), lerpAmt)
    				);
    	}
    
    	public boolean IsInsideViewFrustum()
    	{
    		return 
    			Math.abs(m_pos.GetX()) <= Math.abs(m_pos.GetW()) &&
    			Math.abs(m_pos.GetY()) <= Math.abs(m_pos.GetW()) &&
    			Math.abs(m_pos.GetZ()) <= Math.abs(m_pos.GetW());
    	}
    
    	public float Get(int index)
    	{
    		switch(index)
    		{
    			case 0:
    				return m_pos.GetX();
    			case 1:
    				return m_pos.GetY();
    			case 2:
    				return m_pos.GetZ();
    			case 3:
    				return m_pos.GetW();
    			default:
    				throw new IndexOutOfBoundsException();
    		}
    	}
    }
    
    


    运行方式
    • Open your preferred Java IDE, and create a new project.
    • Import everything under the src folder as source files.
    • Copy the res folder into your Java IDE's folder for your project.
    • Build and run
    mv res obj/res
    cd obj
    
    java Main
    
    mv res ../res
    cd ../
    




    艾孜尔江撰
    2021年6月22日

    Gitee上的源码

    Github上的源码

  • 相关阅读:
    VS中的 MD/MT设置 【转】
    VS2010/MFC编程入门之五十四(Ribbon界面开发:使用更多控件并为控件添加消息处理函数)
    VS2010/MFC编程入门之五十三(Ribbon界面开发:为Ribbon Bar添加控件)[转]
    [MFC]选择目录对话框和选择文件对话框 [转]
    NMM3DViewer 设计
    将可执行程序的内存空间扩展到3GB(windows)
    centos7 安装rocketmq(quick start)
    Centos7 安装 Maven 3.5.*
    ss命令
    强制重启Linux系统的几种方法
  • 原文地址:https://www.cnblogs.com/ezhar/p/14917021.html
Copyright © 2020-2023  润新知