All pastes #57759 Raw Edit

Someone

public text v1 · immutable
#57759 ·published 2006-05-21 21:54 UTC
rendered paste body
/*
 * 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 + 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));
//				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;
		}
	}
}