/* * Copyright (c) 2006 World of Mystery Project Team * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package de.worldofmystery.client.world; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.ObjectInputStream; import java.net.URL; import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.util.logging.Logger; import javax.swing.JFileChooser; import javax.swing.filechooser.FileFilter; import com.jme.app.SimpleGame; import com.jme.bounding.BoundingBox; import com.jme.image.Image; import com.jme.image.Texture; import com.jme.input.InputHandler; import com.jme.input.KeyInput; import com.jme.input.MouseInput; import com.jme.input.action.InputAction; import com.jme.input.action.InputActionEvent; import com.jme.light.Light; import com.jme.light.PointLight; import com.jme.math.Vector3f; import com.jme.renderer.ColorRGBA; import com.jme.renderer.pass.BasicPassManager; import com.jme.renderer.pass.RenderPass; import com.jme.scene.Node; import com.jme.scene.Point; import com.jme.scene.Spatial; import com.jme.scene.lod.AreaClodMesh; import com.jme.scene.state.AlphaState; import com.jme.scene.state.FragmentProgramState; import com.jme.scene.state.TextureState; import com.jme.system.DisplaySystem; import com.jme.util.LoggingSystem; import com.jme.util.TextureManager; import com.jme.util.geom.BufferUtils; import com.jmex.model.XMLparser.BinaryToXML; import com.jmex.model.XMLparser.JmeBinaryWriter; import de.worldofmystery.common.l3dt.AlphaData; import de.worldofmystery.common.l3dt.HeightFieldFile; import de.worldofmystery.common.l3dt.L3DTGroupFile; /** * test program to convert a L3DT terrain to jME node * @author galun * @version $Id: L3DTLoader.java,v 1.20 2006/05/21 20:26:08 galun Exp $ */ public class L3DTLoader extends SimpleGame { private final static String DATAOUT = "/tmp/scene"; private String sourceDirectory = System.getProperty("user.dir", "."); private Logger log = LoggingSystem.getLogger(); private Node terrainNode; private Node pointNode; //private L3DTGroupFile prop = null; private AlphaData alphaData = null; private int tiles = 16; private int mosaics = 1; private boolean splatted = true; private String loadedFile; protected BasicPassManager pManager; public static void main(String[] args) { L3DTLoader loader = new L3DTLoader(); loader.start(); } protected void simpleInitGame() { pManager = new BasicPassManager(); display.setTitle( "jME-Desktop test" ); display.getRenderer().setBackgroundColor(ColorRGBA.blue); File cachedScene = new File(DATAOUT); InputAction togglePoints = new InputAction() { public void performAction(InputActionEvent evt) { if (evt.getTriggerCharacter() == '1') { int cm = terrainNode.getCullMode(); if (cm == Spatial.CULL_DYNAMIC) cm = Spatial.CULL_ALWAYS; else cm = Spatial.CULL_DYNAMIC; terrainNode.setCullMode(cm); } else if (evt.getTriggerCharacter() == '2') { int cm = pointNode.getCullMode(); if (cm == Spatial.CULL_DYNAMIC) cm = Spatial.CULL_ALWAYS; else cm = Spatial.CULL_DYNAMIC; pointNode.setCullMode(cm); } } }; input.addAction(togglePoints, InputHandler.DEVICE_KEYBOARD, KeyInput.KEY_1, InputHandler.AXIS_NONE, false); input.addAction(togglePoints, InputHandler.DEVICE_KEYBOARD, KeyInput.KEY_2, InputHandler.AXIS_NONE, false); try { if (cachedScene.canRead()) { long started = System.currentTimeMillis(); ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(new FileInputStream(cachedScene))); terrainNode = (Node)ois.readObject(); ois.close(); terrainNode.updateGeometricState(0, true); log.info("loading scene took "+(System.currentTimeMillis()-started)+"ms"); rootNode.attachChild(terrainNode); } else { if (properties.get("SourceDirectory") != null) sourceDirectory = properties.get("SourceDirectory"); long started = System.currentTimeMillis(); L3DTGroupFile prop = loadGroupFile(); HeightFieldFile hffTotal = new HeightFieldFile(); hffTotal.load(prop.getFileName("HF")); log.info("hffTotal=" + hffTotal.toString()); Tile[] mosaic = new Tile[mosaics]; if (mosaics == 1) { mosaic[0] = new Tile(hffTotal, prop.getProperty("TX"), prop.getFileName("TX"), prop.getDirectory() + File.separator); } for (int i = 0; i < mosaic.length; i++) { if (tiles > 0) { if (splatted) terrainNode = createSplattedTerrainNode(mosaic[i], tiles); else terrainNode = createTiledTerrainNode(mosaic[i], tiles); } else terrainNode = createTerrainNode(mosaic[i]); pointNode = createPointNode(hffTotal); terrainNode.updateGeometricState(0, true); pointNode.updateGeometricState(0, true); BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(DATAOUT + i + ".jme")); JmeBinaryWriter jmeWriter = new JmeBinaryWriter(); jmeWriter.writeScene(terrainNode, os); os.close(); FileWriter writer = new FileWriter(DATAOUT + i + ".xml"); new BinaryToXML().sendBinarytoXML(new BufferedInputStream(new FileInputStream(DATAOUT + i + ".jme")), writer); rootNode.attachChild(terrainNode); rootNode.attachChild(pointNode); } log.info("creating scene took "+(System.currentTimeMillis()-started)+"ms"); } } catch (Exception ex) { ex.printStackTrace(); } rootNode.updateGeometricState(0, true); rootNode.updateRenderState(); lightState.setGlobalAmbient(ColorRGBA.lightGray); cam.setFrustumFar(16000); cam.getLocation().y = 8000; cam.update(); for (int i = 0; i < lightState.getQuantity(); i++) { log.info("Light#" + i + ": " + lightInfo(lightState.get(i))); if (lightState.get(i) instanceof PointLight) ((PointLight)lightState.get(i)).setLocation(new Vector3f(100, 10000, 100)); } log.info("global ambient=" + lightState.getGlobalAmbient().toString()); log.info("init done"); log.info("max units: " + TextureState.getNumberOfUnits()); log.info("non-power-of-2 supported=" + TextureState.isSupportingNonPowerOfTwoTextureSize()); } protected void simpleUpdate() { if (pManager != null) pManager.updatePasses(tpf); } private String lightInfo(Light l) { String add = ""; if (l instanceof PointLight) add = " pos="+((PointLight)l).getLocation().toString() + " constant="+((PointLight)l).getConstant() + " linear="+((PointLight)l).getLinear(); return l.getType()+" ambient="+l.getAmbient()+" diffuse="+l.getDiffuse()+" specular="+l.getSpecular()+add; } private L3DTGroupFile loadGroupFile() { L3DTGroupFile group = new L3DTGroupFile(); String filename = null; MouseInput.get().setCursorVisible(true); JFileChooser chooser = new JFileChooser(sourceDirectory); chooser.setFileFilter(new FileFilter() { public boolean accept(File f) { return f.isDirectory() || f.getName().endsWith(".mgf"); } public String getDescription() { return "L3DT Map Group File (*.mgf)"; } }); if (chooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) filename = chooser.getSelectedFile().getAbsolutePath(); else return null; try { group.load(filename); loadedFile = filename; System.out.println(group.toString()); } catch (Exception ex) { ex.printStackTrace(); } MouseInput.get().setCursorVisible(false); return group; } private Node createPointNode(HeightFieldFile hff) throws IOException { Node node = new Node("points"); FloatBuffer vertices = BufferUtils.createFloatBuffer(hff.getVertices()); Point p = new Point("terrain", vertices, null, null, null); node.attachChild(p); return node; } private Node createTerrainNode(Tile tile) throws IOException { Node node = new Node("terrain"); FloatBuffer vertices = BufferUtils.createFloatBuffer(tile.hff.getVertices()); IntBuffer indices = BufferUtils.createIntBuffer(tile.hff.getIndices()); FloatBuffer texs = BufferUtils.createFloatBuffer(tile.hff.getTexCoords()); AreaClodMesh terrainBlock = new AreaClodMesh("heightMap", vertices, null, null, texs, indices, null); //TriMesh terrainBlock = new TriMesh("heightMap", vertices, null, null, null, indices); terrainBlock.setDefaultColor(ColorRGBA.white); TextureState textureState = DisplaySystem.getDisplaySystem().getRenderer().createTextureState(); Texture texture = TextureManager.loadTexture(tile.texturePath, Texture.MM_LINEAR, Texture.FM_LINEAR, 1.0f, false); //Texture lightMap = TextureManager.loadTexture(prop.getFileName("LM"), Texture.MM_LINEAR, Texture.FM_LINEAR, 1.0f, false); log.fine("set texture location to " + tile.textureName); texture.setImageLocation(tile.textureName); texture.setApply(Texture.AM_COMBINE); //lightMap.setApply(Texture.AM_COMBINE); textureState.setTexture(texture, 0); //textureState.setTexture(lightMap, 1); textureState.setEnabled(true); terrainBlock.setRenderState(textureState); terrainBlock.setModelBound(new BoundingBox()); terrainBlock.updateModelBound(); terrainBlock.updateGeometricState(0, true); node.attachChild(terrainBlock); return node; } private Node createTiledTerrainNode(Tile tile, int tiles) throws IOException { Node node = new Node("terrain"); float tileWidth = (float)tile.hff.getWidth() / tiles; for (int zz = 0; zz < tiles; zz++) for (int xx = 0; xx < tiles; xx++) { HeightFieldFile hff = tile.hff.getTileAt(xx, zz, tiles); FloatBuffer vertices = BufferUtils.createFloatBuffer(hff.getVertices()); IntBuffer indices = BufferUtils.createIntBuffer(hff.getIndices()); FloatBuffer texs = BufferUtils.createFloatBuffer(hff.getTexCoords(xx, zz, tiles)); AreaClodMesh terrainBlock = new AreaClodMesh("heightMap-"+xx+"-"+zz, vertices, null, null, texs, indices, null); //TriMesh terrainBlock = new TriMesh("heightMap", vertices, null, null, null, indices); terrainBlock.setDefaultColor(ColorRGBA.white); TextureState textureState = DisplaySystem.getDisplaySystem().getRenderer().createTextureState(); Texture texture = TextureManager.loadTexture(tile.texturePath, Texture.MM_LINEAR, Texture.FM_LINEAR, 1.0f, true); //Texture lightMap = TextureManager.loadTexture(prop.getFileName("LM"), Texture.MM_LINEAR, Texture.FM_LINEAR, 1.0f, false); log.info("set texture location to " + tile.textureName); texture.setImageLocation("file:data/meshes/" + tile.textureName); texture.setApply(Texture.AM_COMBINE); //lightMap.setApply(Texture.AM_COMBINE); textureState.setTexture(texture, 0); //textureState.setTexture(lightMap, 1); textureState.setEnabled(true); float posX = (-tiles/2 + xx + 0.5f) * tileWidth * tile.hff.getHorizontalScale(); float posZ = (-tiles/2 + zz + 0.5f) * tileWidth * tile.hff.getHorizontalScale(); terrainBlock.setLocalTranslation(new Vector3f(posX, 0, posZ)); terrainBlock.setRenderState(textureState); terrainBlock.setModelBound(new BoundingBox()); terrainBlock.updateModelBound(); terrainBlock.updateGeometricState(0, true); node.attachChild(terrainBlock); } return node; } private Node createSplattedTerrainNode(Tile tile, int tiles) throws IOException { Node node = new Node("terrain"); Image[] alphaMaps; String basename = loadedFile.replaceAll("\\.mgf$", ""); if (alphaData == null) { alphaData = new AlphaData(); alphaData.load(basename + ".xml"); System.err.println(alphaData.toString()); } alphaMaps = alphaData.getCombinedAlphaImages(); System.err.println("getCombinedAlphaImages() returns " + alphaMaps.length + " maps."); for (int i = 0; i < alphaMaps.length; i++) ImageWriter.write(basename + "-alpha-" + i, alphaMaps[i]); float tileWidth = (float)tile.hff.getWidth() / tiles; for (int zz = 0; zz < tiles; zz++) for (int xx = 0; xx < tiles; xx++) { HeightFieldFile hff = tile.hff.getTileAt(xx, zz, tiles); FloatBuffer vertices = BufferUtils.createFloatBuffer(hff.getVertices()); IntBuffer indices = BufferUtils.createIntBuffer(hff.getIndices()); // these texture coordinates are spread over the whole terrain - one tile gets a fraction of the // texture (1 / (tiles*tiles) FloatBuffer texs = BufferUtils.createFloatBuffer(hff.getTexCoords(xx, zz, tiles)); AreaClodMesh terrainBlock = new AreaClodMesh("heightMap-"+xx+"-"+zz, vertices, null, null, texs, indices, null); //TriMesh terrainBlock = new TriMesh("heightMap", vertices, null, null, null, indices); terrainBlock.setDefaultColor(ColorRGBA.white); terrainBlock.copyTextureCoords(0, 0, 1); TextureState textureState = DisplaySystem.getDisplaySystem().getRenderer().createTextureState(); Texture alphaMap = createTexture(alphaMaps[0]); textureState.setTexture(alphaMap, 0); Texture tex1 = TextureManager.loadTexture(tile.baseDir + alphaData.getImages().get(0).layers.get(0).textures.get(0), Texture.MM_LINEAR, Texture.FM_LINEAR, 1.0f, false); //tex1.setScale(new Vector3f(3,3,3)); tex1.setWrap(Texture.WM_WRAP_S_WRAP_T); textureState.setTexture(tex1, 1); terrainBlock.copyTextureCoords(0, 0, 2, tiles); Texture tex2 = TextureManager.loadTexture(tile.baseDir + alphaData.getImages().get(1).layers.get(0).textures.get(0), Texture.MM_LINEAR, Texture.FM_LINEAR, 1.0f, false); //tex2.setScale(new Vector3f(3,3,3)); tex2.setWrap(Texture.WM_WRAP_S_WRAP_T); textureState.setTexture(tex2, 2); terrainBlock.copyTextureCoords(0, 0, 3, tiles); Texture tex3 = TextureManager.loadTexture(tile.baseDir + alphaData.getImages().get(2).layers.get(0).textures.get(0), Texture.MM_LINEAR, Texture.FM_LINEAR, 1.0f, false); //tex3.setScale(new Vector3f(3,3,3)); tex3.setWrap(Texture.WM_WRAP_S_WRAP_T); textureState.setTexture(tex3, 3); terrainBlock.copyTextureCoords(0, 0, 4, tiles); Texture tex4 = TextureManager.loadTexture(tile.baseDir + alphaData.getImages().get(3).layers.get(0).textures.get(0), Texture.MM_LINEAR, Texture.FM_LINEAR, 1.0f, false); //tex4.setScale(new Vector3f(3,3,3)); tex4.setWrap(Texture.WM_WRAP_S_WRAP_T); textureState.setTexture(tex4, 4); terrainBlock.copyTextureCoords(0, 0, 5, tiles); FragmentProgramState fp = DisplaySystem.getDisplaySystem().getRenderer().createFragmentProgramState(); URL prog = L3DTLoader.class.getClassLoader().getResource("de/worldofmystery/client/world/glsl/splatting_fp.fp"); fp.load(prog); fp.setEnabled(true); // log.info("set texture location to "+prop.getProperty("TX")); // texture.setImageLocation("file:data/meshes/"+prop.getProperty("TX")); // texture.setApply(Texture.AM_COMBINE); //lightMap.setApply(Texture.AM_COMBINE); // textureState.setTexture(alphaMap, 0); //textureState.setTexture(lightMap, 1); textureState.setEnabled(true); float posX = (-tiles/2 + xx + 0.5f) * tileWidth * tile.hff.getHorizontalScale(); float posZ = (-tiles/2 + zz + 0.5f) * tileWidth * tile.hff.getHorizontalScale(); terrainBlock.setLocalTranslation(new Vector3f(posX, 0, posZ)); // terrainBlock.setRenderState(textureState); // terrainBlock.setRenderState(fp); RenderPass rp1 = new RenderPass(); rp1.setPassState(textureState); rp1.setPassState(fp); rp1.add(terrainBlock); pManager.add(rp1); AlphaState as = display.getRenderer().createAlphaState(); as.setBlendEnabled(true); as.setSrcFunction(AlphaState.SB_DST_COLOR); as.setDstFunction(AlphaState.DB_SRC_COLOR); TextureState textureState2 = DisplaySystem.getDisplaySystem().getRenderer().createTextureState(); Texture alphaMap2 = createTexture(alphaMaps[1]); textureState2.setTexture(alphaMap2, 0); Texture tex5 = TextureManager.loadTexture(tile.baseDir + alphaData.getImages().get(4).layers.get(0).textures.get(0), Texture.MM_LINEAR, Texture.FM_LINEAR, 1.0f, false); //tex1.setScale(new Vector3f(3,3,3)); tex5.setWrap(Texture.WM_WRAP_S_WRAP_T); textureState2.setTexture(tex5, 1); terrainBlock.copyTextureCoords(0, 0, 6, tiles); Texture tex6 = TextureManager.loadTexture(tile.baseDir + alphaData.getImages().get(5).layers.get(0).textures.get(0), Texture.MM_LINEAR, Texture.FM_LINEAR, 1.0f, false); //tex2.setScale(new Vector3f(3,3,3)); tex6.setWrap(Texture.WM_WRAP_S_WRAP_T); textureState2.setTexture(tex6, 2); terrainBlock.copyTextureCoords(0, 0, 7, tiles); Texture tex7 = TextureManager.loadTexture(tile.baseDir + alphaData.getImages().get(6).layers.get(0).textures.get(0), Texture.MM_LINEAR, Texture.FM_LINEAR, 1.0f, false); //tex3.setScale(new Vector3f(3,3,3)); tex7.setWrap(Texture.WM_WRAP_S_WRAP_T); textureState2.setTexture(tex7, 3); terrainBlock.copyTextureCoords(0, 0, 8, tiles); Texture tex8 = TextureManager.loadTexture(tile.baseDir + "dummy.png", Texture.MM_LINEAR, Texture.FM_LINEAR, 1.0f, false); //tex3.setScale(new Vector3f(3,3,3)); tex8.setWrap(Texture.WM_WRAP_S_WRAP_T); textureState2.setTexture(tex8, 4); terrainBlock.copyTextureCoords(0, 0, 9, tiles); // terrainBlock.setRenderState(textureState2); // terrainBlock.setRenderState(fp); RenderPass rp2 = new RenderPass(); rp2.setPassState(textureState2); rp1.setPassState(fp); rp2.setPassState(as); rp2.setZFactor(0f); rp2.setZOffset(-5f); rp2.add(terrainBlock); pManager.add(rp2); terrainBlock.setModelBound(new BoundingBox()); terrainBlock.updateModelBound(); terrainBlock.updateGeometricState(0, true); log.info("created terrain block from " + hff.toString()); node.attachChild(terrainBlock); } return node; } private Texture createTexture(Image image) { Texture texture = new Texture(); texture.setImage(image); texture.setCorrection(Texture.CM_PERSPECTIVE); texture.setFilter(Texture.FM_LINEAR); texture.setMipmapState(Texture.MM_LINEAR); return texture; } class Tile { public HeightFieldFile hff; public String textureName; public String texturePath; public String baseDir; public Tile(HeightFieldFile hff, String textureName, String texturePath, String baseDir) { this.hff = hff; this.textureName = textureName; this.texturePath = texturePath; this.baseDir = baseDir; } } }