/******************************************************************************* * Copyright (c) 2007 VTT Technical Research Centre of Finland and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.proconf.g3d.common; import java.net.URL; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.Path; import org.simantics.proconf.g3d.Activator; import org.simantics.proconf.g3d.base.JmeRenderingComponent; import org.simantics.proconf.g3d.preferences.PreferenceConstants; import com.jme.image.Texture; import com.jme.light.DirectionalLight; import com.jme.math.Vector3f; import com.jme.renderer.Camera; import com.jme.renderer.ColorRGBA; import com.jme.renderer.pass.BasicPassManager; import com.jme.renderer.pass.RenderPass; import com.jme.renderer.pass.ShadowedRenderPass; import com.jme.renderer.swt.SWTRenderer; import com.jme.scene.Node; import com.jme.scene.Spatial; import com.jme.scene.Text; import com.jme.scene.state.AlphaState; import com.jme.scene.state.LightState; import com.jme.scene.state.TextureState; import com.jme.scene.state.WireframeState; import com.jme.scene.state.ZBufferState; import com.jme.system.DisplaySystem; import com.jme.util.TextureManager; import com.jme.util.Timer; import com.jme.util.geom.Debugger; import com.jmex.effects.glsl.BloomRenderPass; import com.jmex.effects.glsl.SketchRenderPass; public class JmeSinglePassRenderingComponent extends JmeRenderingComponent { protected DisplaySystem displaySystem; protected Timer timer; protected Node rootNode = new Node("Root"); protected Node shadowRootNode = new Node("Shadow"); protected Node noCastShadowRootNode = new Node("No Cast Shadow"); protected Node noShadowRootNode = new Node("No Shadow"); protected Camera cam; protected float near = .1f; protected float far = 3000f; protected float fov = 55f; protected int projectionPolicy; /** The root node of our text. */ protected Node orthoNode = new Node("ortho"); /** Displays all the lovely information at the bottom. */ protected Text fps; protected Text debug; protected String debugText = ""; public static String fontLocation = "data/defaultfont.tga";//AppProperties.PATH_DEFAULT_FONT; protected BasicPassManager pManager = new BasicPassManager(); private boolean showBounds = false; private boolean showNormals = false; private WireframeState ws = null; private boolean projectionUpdated = false; public JmeSinglePassRenderingComponent() { } public void init(DisplaySystem displaySystem) { this.displaySystem = displaySystem; cam = displaySystem.getRenderer().createCamera( displaySystem.getRenderer().getWidth(), displaySystem.getRenderer().getHeight()); displaySystem.getRenderer().setBackgroundColor(new ColorRGBA(0.2f,0.2f,0.2f,0.f));//(0.357F, 0.647F, 0.890F, 1.0F)); displaySystem.getRenderer().getQueue().setTwoPassTransparency(true); cam.setFrustumPerspective(fov, (float) displaySystem.getRenderer().getWidth()/ (float) displaySystem.getRenderer().getHeight(),near, far); projectionPolicy = PERSPECTIVE_PROJECTION; Vector3f loc = new Vector3f(0.0f, 0.0f, 10.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); /** Move our camera to a correct place and orientation. */ cam.setFrame(loc, left, up, dir); /** Signal that we've changed our camera's location/frustum. */ cam.update(); displaySystem.getRenderer().setCamera(cam); timer = Timer.getTimer(); displaySystem.setTitle("ShapeEditor"); displaySystem.getRenderer().enableStatistics(true); initRoot(); } protected Texture loadFontTexture() { URL url = FileLocator.find(org.simantics.proconf.g3d.Activator.getDefault().getBundle(),new Path(fontLocation),null); return TextureManager.loadTexture(url, Texture.MM_LINEAR, Texture.FM_LINEAR); } protected void initRoot() { ZBufferState buf = displaySystem.getRenderer().createZBufferState(); buf.setEnabled(true); buf.setFunction(ZBufferState.CF_LEQUAL); //buf.setWritable(false); rootNode.setRenderState(buf); rootNode.attachChild(noShadowRootNode); rootNode.attachChild(noCastShadowRootNode); rootNode.attachChild(shadowRootNode); noShadowRootNode.setCullMode(Spatial.CULL_NEVER); //PointLight light = new PointLight(); DirectionalLight light = new DirectionalLight(); light.setDiffuse( new ColorRGBA( 0.75f, 0.75f, 0.75f, 0.75f ) ); light.setAmbient( new ColorRGBA( 0.5f, 0.5f, 0.5f, 0.5f ) ); //light.setLocation( new Vector3f( 100, 100, 100 ) ); light.setDirection(new Vector3f( -100, -150, -100 )); light.setEnabled( true ); light.setShadowCaster(true); LightState lightState = displaySystem.getRenderer().createLightState(); lightState.setEnabled( true ); lightState.attach( light ); lightState.setSeparateSpecular(true); lightState.setTwoSidedLighting(false); rootNode.setRenderState( lightState ); ws = displaySystem.getRenderer().createWireframeState(); ws.setEnabled(false); rootNode.setRenderState(ws); AlphaState as1 = displaySystem.getRenderer().createAlphaState(); as1.setBlendEnabled(true); as1.setSrcFunction(AlphaState.SB_SRC_ALPHA); as1.setDstFunction(AlphaState.DB_ONE); as1.setTestEnabled(true); as1.setTestFunction(AlphaState.TF_GREATER); as1.setEnabled(true); TextureState font = displaySystem.getRenderer().createTextureState(); /** The texture is loaded from fontLocation */ font.setTexture(loadFontTexture()); font.setEnabled(true); // Then our font Text object. /** This is what will actually have the text at the bottom. */ fps = new Text("FPS label", ""); fps.setCullMode(Spatial.CULL_NEVER); fps.setTextureCombineMode(TextureState.REPLACE); debug = new Text("Debug", "Debug"); debug.setCullMode(Spatial.CULL_NEVER); debug.setTextureCombineMode(TextureState.REPLACE); debug.setLocalTranslation(new Vector3f(1.f,10.f,0.f)); // Finally, a stand alone node (not attached to root on purpose) Node fpsNode = new Node("FPS node"); fpsNode.attachChild(fps); fpsNode.attachChild(debug); fpsNode.setRenderState(font); fpsNode.setRenderState(as1); fpsNode.setCullMode(Spatial.CULL_NEVER); orthoNode.attachChild(fpsNode); rootNode.updateGeometricState(0.0f, true); rootNode.updateRenderState(); orthoNode.updateGeometricState(0.0f, true); orthoNode.updateRenderState(); if (Activator.getDefault().getPreferenceStore().getBoolean(PreferenceConstants.SHADOWS)) { ShadowedRenderPass shadowRootPass = new ShadowedRenderPass(); shadowRootPass.add(shadowRootNode); shadowRootPass.add(noCastShadowRootNode); shadowRootPass.addOccluder(shadowRootNode); pManager.add(shadowRootPass); //rootPass.setRenderShadows(false); shadowRootPass.setShadowColor(new ColorRGBA(0.1f,0.1f,0.1f,0.9f)); shadowRootPass.setLightingMethod(ShadowedRenderPass.MODULATIVE); RenderPass rootPass = new RenderPass(); rootPass.add(noShadowRootNode); pManager.add(rootPass); } else { RenderPass rootPass = new RenderPass(); rootPass.add(rootNode); pManager.add(rootPass); } String postProcess = Activator.getDefault().getPreferenceStore().getString(PreferenceConstants.POST_PROCESS); if (postProcess.startsWith("bloom")) { BloomRenderPass bloomRenderPass = new BloomRenderPass(cam, 4); if (bloomRenderPass.isSupported()) { bloomRenderPass.add(rootNode); bloomRenderPass.setUseCurrentScene(false); pManager.add(bloomRenderPass); } } else if (postProcess.startsWith("sketch")) { SketchRenderPass sketchRenderPass = new SketchRenderPass(cam, 4); if (sketchRenderPass.isSupported()) { sketchRenderPass.add(rootNode); pManager.add(sketchRenderPass); } } RenderPass fpsPass = new RenderPass(); fpsPass.add(orthoNode); pManager.add(fpsPass); } public void render() { displaySystem.setCurrent(); /** Recalculate the framerate. */ timer.update(); /** Update tpf to time per frame according to the Timer. */ float tpf = timer.getTimePerFrame(); /** Send the fps to our fps bar at the bottom. */ fps.print("FPS: " + (int) timer.getFrameRate() + " - " + displaySystem.getRenderer().getStatistics()); /** * Update the physics for this world. */ debug.print(debugText); /** Update controllers/render states/transforms/bounds for rootNode. */ rootNode.updateGeometricState(tpf, true); rootNode.updateRenderState(); orthoNode.updateGeometricState(tpf, true); orthoNode.updateRenderState(); displaySystem.getRenderer().clearStatistics(); /** Clears the previously rendered information. */ displaySystem.getRenderer().clearBuffers(); pManager.updatePasses(tpf); pManager.renderPasses(displaySystem.getRenderer()); if ( showBounds ) { Debugger.drawBounds( shadowRootNode, displaySystem.getRenderer(), true ); } if ( showNormals ) { Debugger.drawNormals( shadowRootNode, displaySystem.getRenderer()); } displaySystem.getRenderer().displayBackBuffer(); //swap buffers ((SWTRenderer)displaySystem.getRenderer()).swap(); } @Override public void resize(int width, int height) { updateProjection(); } @Override public Node getShadowRoot() { return shadowRootNode; } @Override public Node getNoCastRoot() { return noCastShadowRootNode; } @Override public Node getRoot() { return rootNode; } @Override public Node getOrthoNode() { return orthoNode; } @Override public Node getNoShadowRoot() { return noShadowRootNode; } // public void setRootNode(Node node) { // rootNode = node; // initRoot(); // } @Override public int getProjectionPolicy() { return projectionPolicy; } @Override public void setProjectionPolicy(int policy) { if (policy != projectionPolicy) { projectionPolicy = policy; updateProjection(); } } private void updateProjection() { switch (projectionPolicy) { case PERSPECTIVE_PROJECTION: cam.setParallelProjection(false); cam.setFrustumPerspective(fov, (float) displaySystem.getRenderer().getWidth() / (float) displaySystem.getRenderer().getHeight(),near, far); break; case PARALLEL_PROJECTION: cam.setParallelProjection(true); break; } cam.update(); projectionUpdated = true; } @Override public float getScreenScale() { //System.out.println(cam.getFrustumLeft() + " " + cam.getFrustumRight() + " " + cam.getFrustumBottom() + " " + cam.getFrustumTop()+ " " + cam.getFrustumNear() + " " + cam.getFrustumFar()); return Math.abs(cam.getFrustumTop()); } @Override public void setScreenScale(float screenScale) { float aspect = (float) displaySystem.getRenderer().getWidth() / (float) displaySystem.getRenderer().getHeight(); cam.setFrustum(-screenScale*8.f, cam.getFrustumFar(), -screenScale*aspect, screenScale*aspect, -screenScale, screenScale); } @Override public float getFieldOfView() { return fov; } @Override public void dispose() { pManager.cleanUp(); rootNode.dispose(); rootNode = null; noShadowRootNode = null; noCastShadowRootNode = null; orthoNode = null; shadowRootNode = null; } @Override public boolean update() { if (!projectionUpdated) { return false; } projectionUpdated = false; return true; } @Override public Camera getCamera() { return cam; } @Override public DisplaySystem getDisplaySystem() { return displaySystem; } public void setDebugText(String text) { this.debugText = text; //System.out.println("JmeSinglePass.setDebugText() " + text); } public void setShowNormals(boolean b) { showNormals = b; } public void setShowBounds(boolean b) { showBounds = b; } public void setShowWireframe(boolean b) { ws.setEnabled(b); } public boolean isShowNormals() { return showNormals; } public boolean isShowBounds() { return showBounds; } public boolean isShowWireframe() { return ws.isEnabled(); } }