package advanced.models3D; import java.awt.event.KeyEvent; import javax.media.opengl.GL; import org.mt4j.MTApplication; import org.mt4j.components.MTComponent; import org.mt4j.components.MTLight; import org.mt4j.components.TransformSpace; import org.mt4j.components.visibleComponents.shapes.mesh.MTTriangleMesh; import org.mt4j.input.gestureAction.DefaultRotateAction; import org.mt4j.input.gestureAction.DefaultZoomAction; import org.mt4j.input.inputProcessors.IGestureEventListener; import org.mt4j.input.inputProcessors.MTGestureEvent; import org.mt4j.input.inputProcessors.componentProcessors.arcballProcessor.ArcBallGestureEvent; import org.mt4j.input.inputProcessors.componentProcessors.arcballProcessor.ArcballProcessor; import org.mt4j.input.inputProcessors.componentProcessors.rotateProcessor.RotateProcessor; import org.mt4j.input.inputProcessors.componentProcessors.scaleProcessor.ScaleEvent; import org.mt4j.input.inputProcessors.componentProcessors.scaleProcessor.ScaleProcessor; import org.mt4j.input.inputProcessors.componentProcessors.zoomProcessor.ZoomProcessor; import org.mt4j.input.inputProcessors.globalProcessors.CursorTracer; import org.mt4j.sceneManagement.AbstractScene; import org.mt4j.util.MT4jSettings; import org.mt4j.util.MTColor; import org.mt4j.util.math.Tools3D; import org.mt4j.util.math.Vector3D; import org.mt4j.util.modelImporter.ModelImporterFactory; import org.mt4j.util.opengl.GLMaterial; public class Models3DScene extends AbstractScene { private MTApplication mtApp; //TODO switch button/wireframe // private String modelsPath = System.getProperty("user.dir") + File.separator + "examples" + File.separator +"advanced"+ File.separator + "models3D" + File.separator + "data" + File.separator; private String modelsPath = "advanced" + MTApplication.separator + "models3D" + MTApplication.separator + "data" + MTApplication.separator; public Models3DScene(MTApplication mtApplication, String name) { super(mtApplication, name); mtApp = mtApplication; this.setClearColor(new MTColor(40,40,40,255)); this.registerGlobalInputProcessor(new CursorTracer(mtApp, this)); //Make canvas zoomable this.getCanvas().registerInputProcessor(new ZoomProcessor(mtApp)); this.getCanvas().addGestureListener(ZoomProcessor.class, new DefaultZoomAction()); if (!(MT4jSettings.getInstance().isOpenGlMode())){ System.err.println(this.getClass().getName() + " example can only be run in OpenGL mode."); return; } //Init light settings MTLight.enableLightningAndAmbient(mtApplication, 150, 150, 150, 255); //Create a light source //I think GL_LIGHT0 is used by processing! MTLight light = new MTLight(mtApplication, GL.GL_LIGHT3, new Vector3D(0,-300,0)); //Set up a material to react to the light GLMaterial material = new GLMaterial(Tools3D.getGL(mtApplication)); material.setAmbient(new float[]{ .5f, .5f, .5f, 1f }); material.setDiffuse(new float[]{ .8f, .8f, .8f, 1f } ); material.setEmission(new float[]{ .0f, .0f, .0f, 1f }); material.setSpecular(new float[]{ 0.9f, 0.9f, 0.9f, 1f }); // almost white: very reflective material.setShininess(110);// 0=no shine, 127=max shine //Group used to move to the screen center and to put the mesh group in MTComponent group1 = new MTComponent(mtApplication); //Create a group and set the light for the whole mesh group ->better for performance than setting light to more comps final MTComponent meshGroup = new MTComponent(mtApplication, "Mesh group"); meshGroup.setLight(light); //Desired position for the meshes to appear at Vector3D destinationPosition = new Vector3D(mtApplication.width/2, mtApplication.height/2, 50); //Desired scale for the meshes float destinationScale = mtApplication.width*0.85f; //Load the meshes with the ModelImporterFactory (A file can contain more than 1 mesh) //Loads 3ds model MTTriangleMesh[] meshes = ModelImporterFactory.loadModel(mtApplication, modelsPath + "jazz_Obj" + MTApplication.separator + "honda_jazz.obj", 180, true, false ); //Get the biggest mesh in the group to use as a reference for setting the position/scale final MTTriangleMesh biggestMesh = this.getBiggestMesh(meshes); Vector3D translationToScreenCenter = new Vector3D(destinationPosition); translationToScreenCenter.subtractLocal(biggestMesh.getCenterPointGlobal()); Vector3D scalingPoint = new Vector3D(biggestMesh.getCenterPointGlobal()); float biggestWidth = biggestMesh.getWidthXY(TransformSpace.GLOBAL); float scale = destinationScale/biggestWidth; //Move the group the the desired position group1.scaleGlobal(scale, scale, scale, scalingPoint); group1.translateGlobal(translationToScreenCenter); this.getCanvas().addChild(group1); group1.addChild(meshGroup); //Inverts the normals, if they are calculated pointing inside of the mesh instead of outside boolean invertNormals = true; for (int i = 0; i < meshes.length; i++) { MTTriangleMesh mesh = meshes[i]; meshGroup.addChild(mesh); mesh.unregisterAllInputProcessors(); //Clear previously registered input processors mesh.setPickable(true); if (invertNormals){ Vector3D[] normals = mesh.getGeometryInfo().getNormals(); for (int j = 0; j < normals.length; j++) { Vector3D vector3d = normals[j]; vector3d.scaleLocal(-1); } mesh.getGeometryInfo().setNormals(mesh.getGeometryInfo().getNormals(), mesh.isUseDirectGL(), mesh.isUseVBOs()); } //If the mesh has more than 20 vertices, use a display list for faster rendering if (mesh.getVertexCount() > 20) mesh.generateAndUseDisplayLists(); //Set the material to the mesh (determines the reaction to the lightning) if (mesh.getMaterial() == null) mesh.setMaterial(material); mesh.setDrawNormals(false); } //Register arcball gesture manipulation to the whole mesh-group meshGroup.setComposite(true); //-> Group gets picked instead of its children meshGroup.registerInputProcessor(new ArcballProcessor(mtApplication, biggestMesh)); meshGroup.addGestureListener(ArcballProcessor.class, new IGestureEventListener(){ //@Override public boolean processGestureEvent(MTGestureEvent ge) { ArcBallGestureEvent aEvt = (ArcBallGestureEvent)ge; meshGroup.transform(aEvt.getTransformationMatrix()); return false; } }); meshGroup.registerInputProcessor(new ScaleProcessor(mtApplication)); meshGroup.addGestureListener(ScaleProcessor.class, new IGestureEventListener(){ //@Override public boolean processGestureEvent(MTGestureEvent ge) { ScaleEvent se = (ScaleEvent)ge; meshGroup.scaleGlobal(se.getScaleFactorX(), se.getScaleFactorY(), se.getScaleFactorX(), biggestMesh.getCenterPointGlobal()); return false; } }); meshGroup.registerInputProcessor(new RotateProcessor(mtApplication)); meshGroup.addGestureListener(RotateProcessor.class, new DefaultRotateAction()); } public MTTriangleMesh getBiggestMesh(MTTriangleMesh[] meshes){ MTTriangleMesh currentBiggestMesh = null; //Get the biggest mesh and extract its width float currentBiggestWidth = Float.MIN_VALUE; for (int i = 0; i < meshes.length; i++) { MTTriangleMesh triangleMesh = meshes[i]; float width = triangleMesh.getWidthXY(TransformSpace.GLOBAL); if (width >= currentBiggestWidth || currentBiggestWidth == Float.MIN_VALUE){ currentBiggestWidth = width; currentBiggestMesh = triangleMesh; } } return currentBiggestMesh; } //@Override public void init() { mtApp.registerKeyEvent(this); } //@Override public void shutDown() { mtApp.unregisterKeyEvent(this); } public void keyEvent(KeyEvent e){ //System.out.println(e.getKeyCode()); int evtID = e.getID(); if (evtID != KeyEvent.KEY_PRESSED) return; switch (e.getKeyCode()){ case KeyEvent.VK_F: System.out.println("FPS: " + mtApp.frameRate); break; case KeyEvent.VK_PLUS: this.getSceneCam().moveCamAndViewCenter(0, 0, -10); break; case KeyEvent.VK_MINUS: this.getSceneCam().moveCamAndViewCenter(0, 0, +10); break; case KeyEvent.VK_F12: getMTApplication().saveFrame(); //Screenshot break; default: break; } } }