I was going to put this little example in the wiki anyway, but something strange is happening. The very simple premise is that at 0-degree x rotation, the lightsaber pointed up, the lightsaber's blade (a transparent plane on top of a 3d cylinder) is at its full height. At 90 degrees, it's invisible. So for every degree you rotate it, it shrinks height/90. That was going to work for the first 90 degrees, and I would apply it appropriately for each of the other three sets of 90, and then apply it on the second axis (it never needs to rotate on all 3). To prove that everything works I'm drawing squares around the edges of the blade and I wrote a shrinkOrGrow() method which successfully fully shrinks the blade plane and extends it again to its full height. Yet when I rotate the lightsaber by 10 degrees and shrink the plane by unitPerDegree*10, the plane doesn't shrink far enough. The lightsaber is Obi-wan's off of scifi3d.com.
/*
AGP, 2009
*/
import java.awt.*;
import java.awt.event.*;
import com.threed.jpct.*;
import com.threed.jpct.util.*;
public class LightsaberTest extends Frame implements WindowListener, KeyListener {
private World theWorld;
private FrameBuffer buffer;
private Camera theCamera;
private boolean keepGoing;
private Graphics g;
private Light light;
private Canvas canvasGL;
private Lightsaber lightsaber;
protected float xRotation;
public LightsaberTest() {
this.setTitle("BR's");
xRotation = 0.0f;
keepGoing = true;
Config.maxPolysVisible *= 16;
theWorld = new World();
lightsaber = new Lightsaber(theWorld);
theCamera = theWorld.getCamera();
buffer = new FrameBuffer(1024, 768, FrameBuffer.SAMPLINGMODE_NORMAL);
canvasGL = new Canvas();
// canvasGL = buffer.enableGLCanvasRenderer();
// buffer.disableRenderer(IRenderer.RENDERER_SOFTWARE);
this.add(canvasGL, BorderLayout.CENTER);
theWorld.setAmbientLight(145, 145, 145);
theCamera.setPosition(lightsaber.getTransformedCenter());
light = new Light(theWorld);
light.setIntensity(35, 35, 35);
light.setAttenuation(8f);
theCamera.moveCamera(Camera.CAMERA_MOVEOUT, 25f);
theCamera.lookAt(lightsaber.getTransformedCenter());
light.setPosition(theCamera.getPosition());
canvasGL.addKeyListener(this);
this.addKeyListener(this);
this.addWindowListener(this);
this.setSize(1024, 768);
this.setVisible(true);
drawLoop();
}
private void drawLoop() {
g = this.getGraphics();
while (keepGoing) {
try {
Thread.sleep(50);
}
catch (InterruptedException e) {}
if (canvasGL.getGraphics() == null)
continue;
buffer.clear();
lightsaber.rotateDegreesX(xRotation);
theWorld.renderScene(buffer);
theWorld.draw(buffer);
// buffer.display(g);
buffer.display(canvasGL.getGraphics());
lightsaber.drawEdges(theCamera, buffer, (Graphics2D)canvasGL.getGraphics());
// buffer.displayGLOnly();
// canvasGL.paint(canvasGL.getGraphics());//NEEDED ONLY BECAUSE displayGLOnly() DOESN'T GO AS FAR AS display()
}
}
public void windowClosing(WindowEvent e) {
if (e.getWindow() == this) {
keepGoing = false;
this.dispose();
System.exit(0);
}
}
public void windowClosed(WindowEvent e) {}
public void windowOpened(WindowEvent e) {}
public void windowActivated(WindowEvent e) {}
public void windowDeactivated(WindowEvent e) {}
public void windowIconified(WindowEvent e) {}
public void windowDeiconified(WindowEvent e) {}
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (e.isShiftDown() && keyCode == KeyEvent.VK_UP)
theCamera.moveCamera(Camera.CAMERA_MOVEIN, 5f);
else if (e.isShiftDown() && keyCode == KeyEvent.VK_DOWN)
theCamera.moveCamera(Camera.CAMERA_MOVEOUT, 5f);
else if (keyCode == KeyEvent.VK_UP)
xRotation += 10f;
else if (keyCode == KeyEvent.VK_DOWN)
xRotation -= 10f;
else if (keyCode == KeyEvent.VK_G)
lightsaber.bladeController.shrinkOrGrow();
}
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_DOWN)
xRotation = 0f;
}
public void keyTyped(KeyEvent e) {}
public static void main(String[] args) {
new LightsaberTest();
}
}
class Lightsaber {
private Object3D lightsaber, bladePlane;
protected VertexController bladeController;
private float totalX;
public Lightsaber(World theWorld) {
totalX = 0.00f;
TextureManager.getInstance().addTexture("PLATEOX2.JPG", new Texture("PLATEOX2.JPG"));
TextureManager.getInstance().addTexture("scratch.jpg", new Texture("scratch.jpg"));
lightsaber = Object3D.mergeAll(Loader.load3DS("saber.3ds", .1f));
TextureManager.getInstance().addTexture("Blade2.jpg", new Texture("Blade2.jpg"));
bladePlane = Primitives.getPlane(1, 8);
bladePlane.setTransparency(0);
bladePlane.setTransparencyMode(Object3D.TRANSPARENCY_MODE_ADD);
bladePlane.setTexture("Blade2.jpg");
bladePlane.setBillboarding(true);
theWorld.addObject(lightsaber);
theWorld.addObject(bladePlane);
theWorld.buildAllObjects();
bladePlane.translate(-1.4f, -19f, 0);
bladeController = new VertexController(bladePlane);
bladeController.scaleYandShift(8f);
}
public SimpleVector getTransformedCenter() {
return lightsaber.getTransformedCenter();//CHANGE LATER TO AVERAGE GRIP'S AND BLADE'S CENTERS
}
private void toWorldSpace(SimpleVector objectSpace) {
SimpleVector translation = bladePlane.getTranslation();
objectSpace.x += translation.x;
objectSpace.y += translation.y;
objectSpace.z += translation.z;
}
public void drawEdges(Camera cam, FrameBuffer buffer, Graphics2D g) {
bladeController.refreshMeshData();
VectorAndIndex[] topMost = bladeController.getTopMost();
toWorldSpace(topMost[0].vector=new SimpleVector(topMost[0].vector));
toWorldSpace(topMost[1].vector=new SimpleVector(topMost[1].vector));
SimpleVector[] top2D = new SimpleVector[]{Interact2D.project3D2D(cam, buffer, topMost[0].vector), Interact2D.project3D2D(cam, buffer, topMost[1].vector)};
VectorAndIndex[] bottomMost = bladeController.getBottomMost();
toWorldSpace(bottomMost[0].vector=new SimpleVector(bottomMost[0].vector));
toWorldSpace(bottomMost[1].vector=new SimpleVector(bottomMost[1].vector));
SimpleVector[] bottom2D = new SimpleVector[]{Interact2D.project3D2D(cam, buffer, bottomMost[0].vector), Interact2D.project3D2D(cam, buffer, bottomMost[1].vector)};
if (bottom2D[0].y > 0) {
g.setColor(Color.red);
g.drawRect((int)top2D[0].x-10, (int)top2D[0].y-10, 20, 20);
g.drawRect((int)top2D[1].x-10, (int)top2D[1].y-10, 20, 20);
g.setColor(new Color(255, 0, 128));
g.drawRect((int)bottom2D[0].x-10, (int)bottom2D[0].y-10, 20, 20);
g.drawRect((int)bottom2D[1].x-10, (int)bottom2D[1].y-10, 20, 20);
}
}
public void rotateDegreesX(float xRotation) {//.05 RADIANS==2.8647º, 90 DEGREES==1.5707 RADIANS
float rotationRadians = (float) Math.toRadians(xRotation);
lightsaber.rotateAxis(lightsaber.getXAxis(), rotationRadians);
if (xRotation != 0.0f) {
totalX += xRotation;
bladeController.shrinkByDegrees(xRotation);
}
}
}
class VertexController extends GenericVertexController {
protected Mesh controlled;
private float originalWidth = 0f;
private float unitPerDegree;//THE AMOUNT BY WHICH THE PLACE SHOULD SHRINK PER DEGREE's ROTATION
private boolean activated;
private float originalHeight;
public VertexController(Object3D toControl) {
controlled = toControl.getMesh();
this.init(controlled, true);
unitPerDegree = getHeight()/90.0f;
activated = true;
System.out.println("Height: "+getHeight() +", unitPerDegree: "+unitPerDegree);
}
public void scaleYandShift(float scale) {
SimpleVector[] vertices = this.getSourceMesh();
SimpleVector[] destination = this.getDestinationMesh();
VectorAndIndex[] topMost = getTopMost();
float height = getHeight();
topMost[0].vector.y -= height*scale;
topMost[1].vector.y -= height*scale;
destination[topMost[0].index] = topMost[0].vector;
destination[topMost[1].index] = topMost[1].vector;
this.updateMesh();
originalHeight = getHeight();
}
public void shrinkOrGrow() {
VectorAndIndex[] topMost = getTopMost();
SimpleVector[] destination = this.getDestinationMesh();
if (activated) {
float height = getHeight();
destination[topMost[0].index].y += height-.05;
destination[topMost[1].index].y += height-.05;
}
else {
destination[topMost[0].index].y -= originalHeight+.05;
destination[topMost[1].index].y -= originalHeight+.05;
}
activated = !activated;
this.updateMesh();
}
public void shrinkByDegrees(float degrees) {
VectorAndIndex[] topMost = getTopMost();
SimpleVector[] destination = this.getDestinationMesh();
System.out.println("Shrink factor: "+(unitPerDegree*degrees));
destination[topMost[0].index].y += (unitPerDegree*degrees);
destination[topMost[1].index].y += (unitPerDegree*degrees);
this.updateMesh();
}
protected VectorAndIndex[] getTopMost() {//POSITIVE y GOES *DOWN*
VectorAndIndex[] topMost = new VectorAndIndex[2];
SimpleVector[] vertices = this.getSourceMesh();
topMost[0] = new VectorAndIndex(vertices[0], 0);
for (int i = 1; i < vertices.length; i++)
if (topMost[0].vector.y > vertices[i].y)
topMost[0] = new VectorAndIndex(vertices[i], i);
topMost[1] = new VectorAndIndex(vertices[0], 0);
for (int i = 1; i < vertices.length; i++)
if (topMost[1].vector.y >= vertices[i].y && topMost[0].index != i)
topMost[1] = new VectorAndIndex(vertices[i], i);
return topMost;
}
public float getHeight() {
VectorAndIndex[] topMost = getTopMost();
VectorAndIndex[] bottomMost = getBottomMost();
float height = bottomMost[0].vector.y-topMost[0].vector.y;
return height;
}
public void apply() {}
protected VectorAndIndex[] getBottomMost() {//POSITIVE y GOES *DOWN*
VectorAndIndex[] bottomMost = new VectorAndIndex[2];
SimpleVector[] vertices = this.getSourceMesh();
bottomMost[0] = new VectorAndIndex(vertices[0], 0);
for (int i = 1; i < vertices.length; i++)
if (bottomMost[0].vector.y < vertices[i].y)
bottomMost[0] = new VectorAndIndex(vertices[i], i);
bottomMost[1] = new VectorAndIndex(vertices[0], 0);
for (int i = 1; i < vertices.length; i++)
if (bottomMost[1].vector.y <= vertices[i].y && bottomMost[1].index != i)
bottomMost[1] = new VectorAndIndex(vertices[i], i);
return bottomMost;
}
}
class VectorAndIndex {
protected SimpleVector vector;
protected int index;
public VectorAndIndex(SimpleVector vector, int index) {
this.vector = vector;
this.index = index;
}
}