/* SpinApplet.java - A small 3d-engine * Copyright (C) 2000 Fredrik Ehnbom * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ import java.applet.*; import java.awt.*; /** * A small 3d-engine made for my homepage at * http://www.gjt.org/~fredde/ * * @author Fredrik Ehnbom */ public class SpinApplet extends Applet implements Runnable { private Graphics offscreen; private Image buffImage; private Color background = new Color(160, 160, 160); private Polyhedron poly; public void start() { Thread t = new Thread(this); buffImage = createImage(96, 96); offscreen = buffImage.getGraphics(); poly = new Polyhedron(96, 96, Math.PI/2); poly.vertices = new Point3D[22]; // specify the 'F' poly.vertices[0] = new Point3D(-1, -2, -2); poly.vertices[1] = new Point3D(-1, 2, -2); poly.vertices[2] = new Point3D( 1, 2, -2); poly.vertices[3] = new Point3D( 1, 1, -2); poly.vertices[4] = new Point3D( 2, 1, -2); poly.vertices[5] = new Point3D( 2, 0, -2); poly.vertices[6] = new Point3D( 1, 0, -2); poly.vertices[7] = new Point3D( 1, -1, -2); poly.vertices[8] = new Point3D( 2, -1, -2); poly.vertices[9] = new Point3D( 4, 0, -2); poly.vertices[10] = new Point3D( 4, -2, -2); poly.vertices[11] = new Point3D(-1, -2, 2); poly.vertices[12] = new Point3D(-1, 2, 2); poly.vertices[13] = new Point3D( 1, 2, 2); poly.vertices[14] = new Point3D( 1, 1, 2); poly.vertices[15] = new Point3D( 2, 1, 2); poly.vertices[16] = new Point3D( 2, 0, 2); poly.vertices[17] = new Point3D( 1, 0, 2); poly.vertices[18] = new Point3D( 1, -1, 2); poly.vertices[19] = new Point3D( 2, -1, 2); poly.vertices[20] = new Point3D( 4, 0, 2); poly.vertices[21] = new Point3D( 4, -2, 2); poly.scs = new Point[poly.vertices.length]; poly.ccs = new Point3D[poly.vertices.length]; for (int i = 0; i < poly.scs.length; i++) { poly.scs[i] = new Point(); poly.ccs[i] = new Point3D(0f, poly.vertices[i].y, 0f); } poly.polygons = new QPolygon[12]; poly.polygons[0] = new QPolygon(new int[] {8, 7, 18, 19}); poly.polygons[1] = new QPolygon(new int[] {19, 16, 5, 8}); poly.polygons[2] = new QPolygon(new int[] {10, 9, 20, 21}); poly.polygons[3] = new QPolygon(new int[] {10, 21, 11, 0}); poly.polygons[4] = new QPolygon(new int[] {11, 12, 1, 0}); poly.polygons[5] = new QPolygon(new int[] {2, 1, 12, 13}); poly.polygons[6] = new QPolygon(new int[] {3, 2, 13, 14}); poly.polygons[7] = new QPolygon(new int[] {5, 4, 15, 16}); poly.polygons[8] = new QPolygon(new int[] {6, 5, 16, 17}); poly.polygons[9] = new QPolygon(new int[] {7, 6, 17, 18}); poly.polygons[10] = new QPolygon(new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 5, 9, 10}); poly.polygons[11] = new QPolygon(new int[] {21, 20, 16, 19, 18, 17, 16, 15, 14, 13, 12, 11}); t.start(); } public void run() { try { long start = System.currentTimeMillis(); while (true) { offscreen.setColor(background); offscreen.fillRect(0, 0, 96, 96); poly.agl.y = (System.currentTimeMillis() - start) / 1000f; poly.paint(offscreen); repaint(); Thread.sleep(10); } } catch (Throwable t) { t.printStackTrace(); } } public void update(Graphics g) { g.drawImage(buffImage, 0, 0, this); } public void paint(Graphics g) { update(g); } } class QPolygon { private Polygon poly = null; public int[] vertices; public QPolygon(int[] vertices) { this.vertices = vertices; poly = new Polygon(new int[vertices.length], new int[vertices.length], vertices.length); } public final boolean visible(Polyhedron obj) { int p2x = obj.scs[vertices[1]].x; int p2y = obj.scs[vertices[1]].y; int v1x = obj.scs[vertices[2]].x - p2x; int v1y = obj.scs[vertices[2]].y - p2y; int v2x = obj.scs[vertices[0]].x - p2x; int v2y = obj.scs[vertices[0]].y - p2y; return ((v1x * v2y - v2x * v1y) < 0); } public final Point3D getNormalC(Polyhedron obj) { Point3D p = new Point3D(0f, 0f, 0f); Point3D p1 = obj.ccs[vertices[0]]; Point3D p2 = obj.ccs[vertices[1]]; Point3D p3 = obj.ccs[vertices[2]]; p.x = ((p1.y - p2.y) * (p1.z - p3.z)) - ((p1.z - p2.z) * (p1.y - p3.y)); p.y = ((p1.z - p2.z) * (p1.x - p3.x)) - ((p1.x - p2.x) * (p1.z - p3.z)); p.z = ((p1.x - p2.x) * (p1.y - p3.y)) - ((p1.y - p2.y) * (p1.x - p3.x)); double length = Math.sqrt(Math.pow(p.x, 2) + Math.pow(p.y, 2) + Math.pow(p.z, 2)); p.x /= length; p.y /= length; p.z /= length; return p; } public void paint(Graphics g, Polyhedron obj) { { Point3D normal = getNormalC(obj); int col = 60; int shade = (int) (normal.z * 128); col -= (96 + shade); col = (col > 255) ? 255 : (col < 0) ? 0 : col; g.setColor(new Color(col, col, col)); } for (int i = 0; i < vertices.length; i++) { poly.xpoints[i] = obj.scs[vertices[i]].x; poly.ypoints[i] = obj.scs[vertices[i]].y; } g.fillPolygon(poly); } } class Polyhedron { public Point3D[] vertices; public Point3D agl = new Point3D(0f, 0f, 0f); public Point[] scs; // screen coordinates public Point3D[] ccs; // camera coordinates /** * The polygons for this Polyhedron */ public QPolygon[] polygons; private double screendist; private int xO; private int yO; public Polyhedron(int width, int height, double viewAngle) { // screen origo xO = width / 2; yO = height / 2; // calc screen distance screendist = (double) xO/(Math.tan(viewAngle/2)); } /** * Paint the polyhedron */ public final void paint(Graphics g) { // rotate float ct = (float) Math.cos(agl.y); float st = (float) Math.sin(agl.y); for (int i = 0; i < vertices.length; i++) { float x = vertices[i].x; float z = vertices[i].z; ccs[i].x = x * ct + z * st; ccs[i].z = x * -st + z * ct + 6.5f; scs[i].x = (int) (screendist * ccs[i].x / ccs[i].z) + xO; scs[i].y = (int) (screendist * vertices[i].y / ccs[i].z) + yO; } for (int i = 0; i < polygons.length; i++) { if (polygons[i].visible(this)) { polygons[i].paint(g, this); } } } } class Point3D { public float x; public float y; public float z; public Point3D(float x, float y, float z) { this.x = x; this.y = y; this.z = z; } public void set(Point3D p) { x = p.x; y = p.y; z = p.z; } public boolean equals(Point3D p) { return (this.x == p.x) && (this.y == p.y) && (this.z == p.z); } }