2 Questions!

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;
        }
    }
}

Cool, Eric. Looks like a good start. We’ll want to create a Torus object that extends TriMesh. However, we might as well put this off until 0.5 is released, so we can focus on finishing the webstart stuff. We can probably hammer out a working version after that.