Here ya go!
package nova;
import com.jme.app.VariableTimestepGame;
import com.jme.input.FirstPersonController;
import com.jme.input.InputController;
import com.jme.light.PointLight;
import com.jme.math.Vector3f;
import com.jme.renderer.Camera;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.LightNode;
import com.jme.scene.Node;
import com.jme.scene.TriMesh;
import com.jme.scene.state.LightState;
import com.jme.scene.state.WireframeState;
import com.jme.scene.state.ZBufferState;
import com.jme.system.DisplaySystem;
import com.jme.system.JmeException;
/**
* NovaMain! Whatever that turns out to be...
*
* @author Eric
*/
public class NovaMain extends VariableTimestepGame {
private Camera cam;
private InputController input;
private TriMesh pqtorus;
private Node scene, root;
Vector3f[] vertices;
protected void update(float interpolation) {
input.update(interpolation * 20);
scene.updateWorldData(interpolation);
}
protected void render(float interpolation) {
display.getRenderer().clearBuffers();
display.getRenderer().draw(scene);
}
protected void initSystem() {
try {
display = DisplaySystem.getDisplaySystem(properties.getRenderer());
display.createWindow(properties.getWidth(), properties.getHeight(),
properties.getDepth(), properties.getFreq(), properties.getFullscreen());
cam = display.getRenderer().getCamera(properties.getWidth(), properties.getHeight());
} catch (JmeException e) {
e.printStackTrace();
System.exit(1);
}
ColorRGBA blackColor = new ColorRGBA(0.0f, 0.0f, 0.0f, 1.0f);
display.getRenderer().setBackgroundColor(blackColor);
cam.setFrustum(1.0f, 1000.0f, -0.55f, 0.55f, 0.4125f, -0.4125f);
Vector3f loc = new Vector3f(0.0f, 0.0f, 75.0f);
Vector3f left = new Vector3f(-1.0f, 0.0f, 0.0f);
Vector3f up = new Vector3f(0.0f, 1.0f, 0.0f);
Vector3f dir = new Vector3f(0.0f, 0f, -1.0f);
cam.setFrame(loc, left, up, dir);
display.getRenderer().setCamera(cam);
input = new FirstPersonController(this, cam, properties.getRenderer());
}
protected void initGame() {
root = new Node("root");
scene = new Node("scene");
root.attachChild(scene);
pqtorus = genTorus(2, 1, 2.0f);
WireframeState wire = display.getRenderer().getWireframeState();
wire.setEnabled(false);
ZBufferState zbuf = display.getRenderer().getZBufferState();
zbuf.setFunction(ZBufferState.CF_LEQUAL);
zbuf.setEnabled(true);
scene.setRenderState(wire);
scene.setRenderState(zbuf);
scene.attachChild(pqtorus);
PointLight pl = new PointLight();
pl.setAmbient(new ColorRGBA(1, 1, 1, 1));
pl.setDiffuse(new ColorRGBA(.9f, .9f, .9f, 1));
pl.setSpecular(new ColorRGBA(0, 0, 1, 1));
pl.setEnabled(true);
LightState lightstate = display.getRenderer().getLightState();
LightNode lightNode = new LightNode("", lightstate);
lightNode.setLight(pl);
lightNode.setTarget(pqtorus);
lightNode.setForceView(true);
scene.attachChild(lightNode);
scene.updateGeometricState(0.0f, true);
}
private TriMesh genTorus(int p, int q, float radius) {
final float THETA_STEP = (float) (Math.PI * 2 / 64);
final float BETA_STEP = (float) (Math.PI * 2 / 8);
Vector3f[] toruspoints = new Vector3f[64];
vertices = new Vector3f[8 * 64];
Vector3f pointB = new Vector3f(), T = new Vector3f(), N = new Vector3f(), B = new Vector3f();
float r, x, y, z, theta = 0.0f, beta = 0.0f;
int vertex = 0;
//Move along the length of the torus
for (int i = 0; i < toruspoints.length; i++) {
theta += THETA_STEP;
//Find the point on the torus
r = (float) (0.5f * (2.0f + Math.sin(q * theta)) * radius);
x = (float) (r * Math.cos(p * theta) * radius);
y = (float) (r * Math.sin(p * theta) * radius);
z = (float) (r * Math.cos(q * theta) * radius);
toruspoints[i] = new Vector3f(x, y, z);
//Now find a point slightly farther along the torus
r = (float) (0.5f * (2.0f + Math.sin(q * (theta + 0.01f))) * radius);
x = (float) (r * Math.cos(p * (theta + 0.01f)) * radius);
y = (float) (r * Math.sin(p * (theta + 0.01f)) * radius);
z = (float) (r * Math.cos(q * (theta + 0.01f)) * radius);
pointB = new Vector3f(x, y, z);
T = pointB.subtract(toruspoints[i]);
N = toruspoints[i].add(pointB);
B = T.cross(N);
N = B.cross(T);
//Normalise the two vectors before use
N = N.normalize();
B = B.normalize();
//Create a circle oriented by these new vectors
beta = 0.0f;
for (int j = 0; j < 8; j++) {
beta += BETA_STEP;
float cx = (float) Math.cos(beta);
float cy = (float) Math.sin(beta);
vertices[vertex] = new Vector3f();
vertices[vertex].x = (cx * N.x + cy * B.x) + toruspoints[i].x;
vertices[vertex].y = (cx * N.y + cy * B.y) + toruspoints[i].y;
vertices[vertex].z = (cx * N.z + cy * B.z) + toruspoints[i].z;
vertex++;
}
}
int n = 7 * 64;
Vector3f[] normals = new Vector3f[n];
int[] indices = new int[6 * n];
//FIXME: what number should go here?!
Face[] faces = new Face[880];
int j = 0, k = 0;
//Create the array of indices/faces
for (int i = 8; i < n; i++) {
indices[j++] = i;
indices[j++] = i + 1;
indices[j++] = i - 8;
faces[k++] = new Face(i, i + 1, i - 8);
indices[j++] = i + 1;
indices[j++] = i - 8;
indices[j++] = i - 7;
faces[k++] = new Face(i + 1, i - 8, i - 7);
}
normals = computeNormals(faces, vertices);
return new TriMesh("pqtorus", vertices, normals, null, null, indices);
}
//Rrrrrrrrrip! ;)
//FIXME: replace with more torus-specific code
private Vector3f[] computeNormals(Face[] faces, Vector3f[] verts) {
Vector3f[] returnNormals = new Vector3f[verts.length];
Vector3f[] normals = new Vector3f[faces.length];
Vector3f[] tempNormals = new Vector3f[faces.length];
for (int i = 0; i < faces.length; i++) {
System.out.println("face " + i + ":" + faces[i]);
tempNormals[i] =
verts[faces[i].vertIndex[0]].subtract(
verts[faces[i].vertIndex[2]]).cross(
verts[faces[i].vertIndex[2]].subtract(
verts[faces[i].vertIndex[1]]));
normals[i] = tempNormals[i].normalize();
}
Vector3f sum = new Vector3f();
Vector3f zero = sum;
int shared = 0;
for (int i = 0; i < verts.length; i++) {
for (int j = 0; j < faces.length; j++) {
if (faces[j].vertIndex[0] == i
|| faces[j].vertIndex[1] == i
|| faces[j].vertIndex[2] == i) {
sum = sum.add(tempNormals[j]);
shared++;
}
}
returnNormals[i] = sum.divide(-shared);
returnNormals[i] = returnNormals[i].normalize().negate();
sum = zero;
shared = 0;
}
return returnNormals;
}
//Unused
protected void reinit() {}
protected void cleanup() {}
public static void main(String[] args) {
NovaMain app = new NovaMain();
app.setDialogBehaviour(NEVER_SHOW_PROPS_DIALOG);
app.start();
}
private class Face {
int[] vertIndex = new int[3];
Face(int a, int b, int c){
vertIndex[0] = a;
vertIndex[1] = b;
vertIndex[2] = c;
}
}
}