Index: java/client_3D/.classpath
===================================================================
--- java/client_3D/.classpath	(revision 65)
+++ java/client_3D/.classpath	(revision 65)
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src">
+		<attributes>
+			<attribute name="org.eclipse.jdt.launching.CLASSPATH_ATTR_LIBRARY_PATH_ENTRY" value="framsticks-jogl/static/win"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="src" path="res"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="lib" path="static/shared/lib/foxtrot.jar"/>
+	<classpathentry kind="lib" path="static/shared/lib/gluegen-rt.jar">
+		<attributes>
+			<attribute name="org.eclipse.jdt.launching.CLASSPATH_ATTR_LIBRARY_PATH_ENTRY" value="frams-3d-client/static/win/lib"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="lib" path="static/shared/lib/jogl.jar">
+		<attributes>
+			<attribute name="org.eclipse.jdt.launching.CLASSPATH_ATTR_LIBRARY_PATH_ENTRY" value="frams-3d-client/static/win/lib"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
Index: java/client_3D/.project
===================================================================
--- java/client_3D/.project	(revision 65)
+++ java/client_3D/.project	(revision 65)
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>framsticks-jogl</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+	<filteredResources>
+		<filter>
+			<id>1283974572265</id>
+			<name></name>
+			<type>26</type>
+			<matcher>
+				<id>org.eclipse.ui.ide.multiFilter</id>
+				<arguments>1.0-name-matches-false-false-.svn</arguments>
+			</matcher>
+		</filter>
+	</filteredResources>
+</projectDescription>
Index: java/client_3D/build.bat
===================================================================
--- java/client_3D/build.bat	(revision 65)
+++ java/client_3D/build.bat	(revision 65)
@@ -0,0 +1,4 @@
+rem "manual" build for experienced users. otherwise use eclipse!
+mkdir bin
+set LIBDIR=static/shared/lib
+javac -classpath %LIBDIR%/jogl.jar;%LIBDIR%/foxtrot.jar;%LIBDIR%/gluegen-rt.jar -sourcepath src -d bin src\com\framsticks\net\client3D\App.java
Index: java/client_3D/build.xml
===================================================================
--- java/client_3D/build.xml	(revision 65)
+++ java/client_3D/build.xml	(revision 65)
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="iso-8859-2"?>
+<project name="framsticks-client-3d" basedir=".">
+	<description>
+	   This buildfile prepares distributions of the Framsticks 3D Client for windows, mac and linux.
+	</description>
+	<property name="src" value="src"/>
+	<property name="build" value="build"/>
+	<property name="build-classes" value="build/classes"/>
+	<property name="build-lib" value="build/lib"/>
+	<property name="jar-file" value="framsticks-jogl.jar" />
+	<property name="build-jar" value="build/${jar-file}"/>
+	<property name="dist" value="dist"/>
+	<property name="dist-src-zip" value="dist/client3d-src.zip"/>
+	<property name="dist-osx" value="dist/osx"/>
+	<property name="dist-osx-lib" value="dist/osx/Client3d.app/Contents/Resources/Java"/>
+	<property name="dist-osx-res" value="dist/osx/Client3d.app/Contents/Resources/Java/res"/>
+	<property name="dist-osx-java-runner" value="dist/osx/Client3d.app/Contents/MacOS/JavaApplicationStub"/>
+	<property name="dist-osx-zip" value="dist/client3d-osx.zip"/>
+	<property name="dist-osx-dmg" value="dist/client3d-osx.dmg"/>
+	<property name="dist-win" value="dist/win"/>
+	<property name="dist-win-lib" value="dist/win/lib"/>
+	<property name="dist-win-res" value="dist/win/res"/>
+	<property name="dist-win-zip" value="dist/client3d-win.zip"/>
+	<property name="dist-other" value="dist/other"/>
+	<property name="dist-other-lib" value="dist/other/lib"/>
+	<property name="dist-other-res" value="dist/other/res"/>
+	<property name="dist-other-zip" value="dist/client3d-other.zip"/>
+	<property name="static" value="static"/>
+	<property name="static-osx" value="static/osx"/>
+	<property name="static-win" value="static/win"/>
+	<property name="static-shared-res" value="static/shared/res"/>
+	<property name="static-shared-lib" value="static/shared/lib"/>
+	
+	<target name="init">
+		<mkdir dir="${build-classes}" />
+    	<mkdir dir="${dist-osx-lib}" />
+        <mkdir dir="${dist-osx-res}" />
+    	<mkdir dir="${dist-win-lib}" />
+    	<mkdir dir="${dist-win-res}" />
+		<mkdir dir="${dist-other-lib}" />
+    	<mkdir dir="${dist-other-res}" />
+	</target>
+	
+	<target name="clean">
+		<delete failonerror="false" includeemptydirs="true">
+			<fileset dir="${build}"/>
+			<fileset dir="${dist}" />
+		</delete>
+	</target>
+	
+	<target name="compile" depends="init" >
+        <javac srcdir="${src}" destdir="${build-classes}" target="1.5">
+        	<classpath>
+        		<fileset dir="${static-shared-lib}" />
+        	</classpath>
+		</javac>
+    </target>
+	
+	<target name="jar" depends="compile">
+		<jar destfile="${build-jar}" >
+			<fileset dir="${build-classes}" />
+		</jar>
+	</target>
+	
+	<target name="dist-osx" depends="jar">
+		<copy todir="${dist-osx}">
+			<fileset dir="${static-osx}"/>
+		</copy>
+		<!-- 
+			Eleganckie rozwiazanie z Client3d.app wymaga aby plik uruchomiajacy jave byl plikiem wykonywalnym
+			Problem pojawia sie jezeli kompilacja bedzie przebiegac pod windowsem bo nie ma tam jak takich uprawinien ustawic
+			Pozostaje skladanie paczki pod unix-owym systemem albo rozwiazanie ze skryptem uruchomiajacym tak jak run.bat dla win
+		 --> 
+		<chmod perm="u+x">
+			  <fileset file="${dist-osx-java-runner}/" />
+		</chmod>
+		<copy todir="${dist-osx-lib}">
+			<fileset file="${build-jar}"/>
+			<fileset dir="${static-shared-lib}"/>
+		</copy>
+		<copy todir="${dist-osx-res}">
+			<fileset dir="${static-shared-res}" />
+		</copy>
+	</target>
+	
+	<target name="dist-win" depends="jar">
+		<copy todir="${dist-win}">
+			<fileset dir="${static-win}"/>
+		</copy>
+		<copy todir="${dist-win-lib}">
+			<fileset file="${build-jar}"/>
+			<fileset dir="${static-shared-lib}"/>
+		</copy>
+		<copy todir="${dist-win-res}">
+			<fileset dir="${static-shared-res}" />
+		</copy>
+	</target>
+	
+	<target name="dist-other" depends="jar">
+		<copy todir="${dist-other-lib}">
+			<fileset file="${build-jar}"/>
+			<fileset dir="${static-shared-lib}"/>
+		</copy>
+		<copy todir="${dist-other-res}">
+			<fileset dir="${static-shared-res}" />
+		</copy>
+	</target>
+	
+	<target name="dist" depends="dist-osx, dist-win, dist-other"
+		description="dist-osx, dist-win, dist-other">
+	</target>
+	
+	<target name="pack"  depends="dist">
+		<!-- 
+			Tutaj pojawia sie ten sam problem z plikiem wykonywalnym
+			Nie mozna sobie po prostu zrobic zip'a (ant nie zachowuje uprawnien plikow)
+			Tworzymy obraz dysku z nasza aplikacja (standardowy sposob dystrybucji aplikacji pod Mac OS X )
+		-->
+		<exec executable="hdiutil" os="Mac OS X">
+			<arg line="create -srcfolder ${dist-osx} ${dist-osx-dmg}"/>
+		</exec>
+		<zip destfile="${dist-win-zip}">
+		    <fileset dir="${dist-win}"/>
+		</zip>
+		<zip destfile="${dist-other-zip}">
+		    <fileset dir="${dist-other}"/>
+		</zip>
+		<!-- sources available on public SVN
+		<zip destfile="${dist-src-zip}">
+			<zipfileset dir="${src}" prefix="${src}"/>
+			<zipfileset dir="${static}" prefix="${static}"/>
+			<fileset file=".classpath"/>
+			<fileset file=".project"/>
+			<fileset file="build.xml"/>
+		</zip-->
+	</target>
+	
+	<target name="default" depends="clean, init, compile, jar, dist, pack"
+		description="clean, init, compile, jar, dist, pack">
+	</target>
+	
+</project>
Index: java/client_3D/run.bat
===================================================================
--- java/client_3D/run.bat	(revision 65)
+++ java/client_3D/run.bat	(revision 65)
@@ -0,0 +1,4 @@
+rem use 'build.xml' with eclipse to properly create distributions for windows/macos/linux
+rem **** NOTE ****  copy ./static/shared/res to ./ if you want to run Client3D like this. 
+set LIBDIR=static/shared/lib
+java -classpath bin;%LIBDIR%/jogl.jar;%LIBDIR%/foxtrot.jar;%LIBDIR%/gluegen-rt.jar -Djava.library.path=static/win/lib  com.framsticks.net.client3D.App
Index: java/client_3D/src/com/framsticks/net/client3D/App.java
===================================================================
--- java/client_3D/src/com/framsticks/net/client3D/App.java	(revision 65)
+++ java/client_3D/src/com/framsticks/net/client3D/App.java	(revision 65)
@@ -0,0 +1,517 @@
+package com.framsticks.net.client3D;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JFrame;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JScrollPane;
+import javax.swing.JTextField;
+import javax.swing.JTextPane;
+import javax.swing.SwingUtilities;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Style;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.StyleContext;
+import javax.swing.text.StyledDocument;
+
+import foxtrot.Task;
+import foxtrot.Worker;
+
+/**
+ * The main application class.
+ * 
+ * Since formerly the main interface of the program was the command line,
+ * all the actions are executed via text commands. Menu options simply execute
+ * the parseCommand method with the command specified.
+ */
+// TODO: console scrollLock
+public class App extends JFrame {
+	static final long serialVersionUID = 1;
+
+	private Client client;
+	private Viewer viewer;
+	private StyledDocument styledDocument;
+	private ArrayList<String> commandHistory;
+	private JTextPane textPane;
+	private JTextField inputLine;
+	private boolean lockScroll = false;
+	private JMenu viewMenu;
+	private JMenuBar menuBar;
+	private JMenuItem connectItem;
+	private JMenuItem disconnectItem;
+	private JCheckBoxMenuItem loggingItem;
+	private JCheckBoxMenuItem autorefreshItem;
+	private JMenu styleMenu;
+
+	private final String DEFAULT_HOST = "127.0.0.1"; // "192.168.10.3";
+														// //192.168.1.102
+	private final String DEFAULT_PORT = "9009";
+
+	private class SwitchCreatureAction implements ActionListener {
+		private App console;
+		private int group;
+		private int index;
+
+		public SwitchCreatureAction(App console, int group, int index) {
+			this.console = console;
+			this.group = group;
+			this.index = index;
+		}
+
+		public void actionPerformed(ActionEvent e) {
+			console.parseCommand("viewcreature " + group + " " + index);
+		}
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public App() {
+		super("Framsticks 3D Client");
+		init();
+	}
+
+	private void init() {
+		JFrame.setDefaultLookAndFeelDecorated(true);
+		commandHistory = new ArrayList<String>();
+		createChildren();
+		createMenuBar();
+		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+		setVisible(true);
+
+		client = new Client();
+		try
+		{
+			viewer = new Viewer(this);
+			viewer.showInFrame();
+		}
+		// An exception thrown by Viewer's constructor.
+		catch (IOException e)
+		{
+			Log.getInstance().log("err", "Couldn't initialize the viewer window: " + e.toString());
+			e.printStackTrace();
+		}
+	}
+
+	private void createChildren() {
+		JFrame.setDefaultLookAndFeelDecorated(true);
+		setSize(500, 500);
+		setLocationRelativeTo(null);
+		setLocation(this.getX() + 255, this.getY());
+		setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
+
+		getContentPane().setLayout(new BorderLayout());
+		createTextArea();
+		// since all options are now available in the menu,
+		// the textual input line for commands has been hidden:
+		// createInputLine();
+
+		Log.getInstance().addLoggerListener(new ILogListener() {
+			public void onMesssage(String category, String text) {
+				try {
+					styledDocument.insertString(styledDocument.getLength(),
+							text + "\n", styledDocument.getStyle(category));
+					if (!lockScroll)
+						textPane.scrollRectToVisible(textPane.getVisibleRect());
+				} catch (BadLocationException ble) {
+					System.err
+							.println("Couldn't insert initial text into text pane.");
+				}
+			}
+		});
+
+	}
+
+	public void setInputLineText(String text) {
+		inputLine.setText(text);
+	}
+
+	private void createTextArea() {
+		textPane = new JTextPane();
+		textPane.setEditable(false);
+		styledDocument = textPane.getStyledDocument();
+		addStylesToDocument(styledDocument);
+
+		JScrollPane paneScrollPane = new JScrollPane(textPane);
+		paneScrollPane
+				.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
+
+		getContentPane().add(paneScrollPane, BorderLayout.CENTER);
+	}
+
+	class StyleListener implements ActionListener {
+		String styleName;
+
+		StyleListener(String name) {
+			styleName = name;
+		}
+
+		public void actionPerformed(ActionEvent event) {
+			parseCommand("style " + styleName);
+		}
+	}
+
+	/**
+	 * Adds a new style.
+	 * 
+	 * @param name
+	 *            Name of the new style.
+	 */
+	public void AddStyle(String name) {
+		JMenuItem item = new JMenuItem(name);
+		styleMenu.add(item);
+		item.addActionListener(new StyleListener(name));
+	}
+
+	private void createMenuBar() {
+		menuBar = new JMenuBar();
+
+		JMenu serverItem = new JMenu("Server");
+		menuBar.add(serverItem);
+
+		JMenuItem testModeItem = new JMenuItem("Test mode");
+		serverItem.add(testModeItem);
+
+		testModeItem.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent event) {
+				parseCommand("connect mock");
+			}
+		});
+
+		connectItem = new JMenuItem("Connect");
+		serverItem.add(connectItem);
+
+		final App window = this;
+		connectItem.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent event) {
+				String params = (String) JOptionPane.showInputDialog(window,
+						"Enter server address (IP:port)\n", "Connect",
+						JOptionPane.PLAIN_MESSAGE, null, null, DEFAULT_HOST
+								+ ":" + DEFAULT_PORT);
+				if (params != null) {
+					params = params.replaceAll(":", " ");
+					parseCommand("connect " + params);
+				}
+			}
+		});
+
+		disconnectItem = new JMenuItem("Disonnect");
+		serverItem.add(disconnectItem);
+		disconnectItem.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent event) {
+				parseCommand("disconnect");
+			}
+		});
+
+		JMenu simulationItem = new JMenu("Simulation");
+		menuBar.add(simulationItem);
+
+		JMenuItem initItem = new JMenuItem("Init");
+		simulationItem.add(initItem);
+		initItem.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent event) {
+				parseCommand("> call /simulator init");
+			}
+		});
+
+		JMenuItem startItem = new JMenuItem("Start");
+		simulationItem.add(startItem);
+		startItem.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent event) {
+				parseCommand("> set /simulator running 1");
+			}
+		});
+
+		JMenuItem stopItem = new JMenuItem("Stop");
+		simulationItem.add(stopItem);
+		stopItem.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent event) {
+				parseCommand("> set /simulator running 0");
+			}
+		});
+
+		JMenuItem refreshCreaturesItem = new JMenuItem("Refresh creatures");
+		simulationItem.add(refreshCreaturesItem);
+		refreshCreaturesItem.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent event) {
+				parseCommand("refreshcreatures");
+			}
+		});
+
+		JMenuItem refreshWorldItem = new JMenuItem("Refresh world");
+		simulationItem.add(refreshWorldItem);
+		refreshWorldItem.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent event) {
+				parseCommand("refreshworld");
+			}
+		});
+
+		styleMenu = new JMenu("Style");
+		menuBar.add(styleMenu);
+
+		viewMenu = new JMenu("View");
+		menuBar.add(viewMenu);
+		rebuildViewMenu(null);
+
+		JMenu optionsMenu = new JMenu("Options");
+		menuBar.add(optionsMenu);
+
+		autorefreshItem = new JCheckBoxMenuItem("Autorefresh");
+		autorefreshItem.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				switchAutorefresh();
+			}
+		});
+		optionsMenu.add(autorefreshItem);
+
+		loggingItem = new JCheckBoxMenuItem("Logging");
+		loggingItem.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				switchLogging();
+			}
+		});
+		optionsMenu.add(loggingItem);
+		loggingItem.setSelected(true);
+
+		setJMenuBar(menuBar);
+	}
+
+	/**
+	 * Turns logging on and off.
+	 */
+	public void switchLogging() {
+		Log.getInstance().setEnabled(!Log.getInstance().isEnabled());
+	}
+
+	/**
+	 * Turns autorefreshing on and off.
+	 */
+	public void switchAutorefresh() {
+		if (autorefreshItem.isSelected()) {
+			parseCommand("refreshcreatures");
+		}
+	}
+
+	/**
+	 * Rebuilds the 'View' menu with a specified list of creatures.
+	 * 
+	 * @param creatures
+	 *            Creatures to include in the 'View' menu.
+	 */
+	public void rebuildViewMenu(Creature[] creatures) {
+		viewMenu.removeAll();
+		JMenuItem allMenuItem = new JMenuItem("World");
+		allMenuItem.addActionListener(new SwitchCreatureAction(this, -1, -1));
+		viewMenu.add(allMenuItem);
+		if (creatures != null) {
+			for (Creature creature : creatures) {
+				JMenuItem item = new JMenuItem("Creature "
+						+ creature.toString());
+				item.addActionListener(new SwitchCreatureAction(this, creature
+						.getGroup(), creature.getIndex()));
+				viewMenu.add(item);
+			}
+		}
+	}
+
+	/**
+	 * Sets font styles in a specified document.
+	 * 
+	 * @param doc
+	 *            A document object.
+	 */
+	protected void addStylesToDocument(StyledDocument doc) {
+		// Initialize some styles
+		Style def = StyleContext.getDefaultStyleContext().getStyle(
+				StyleContext.DEFAULT_STYLE);
+
+		Style s = doc.addStyle("dbg", def);
+		StyleConstants.setForeground(s, new Color(150, 150, 150));
+
+		s = doc.addStyle("wrn", def);
+		StyleConstants.setForeground(s, new Color(255, 150, 0));
+
+		s = doc.addStyle("err", def);
+		StyleConstants.setForeground(s, new Color(255, 0, 0));
+
+		s = doc.addStyle("cmd", def);
+		StyleConstants.setForeground(s, new Color(0, 150, 0));
+
+		s = doc.addStyle("<<<", def);
+		StyleConstants.setForeground(s, new Color(100, 40, 200));
+		// StyleConstants.setFontFamily(s, "Courier New");
+
+		s = doc.addStyle(">>>", def);
+		StyleConstants.setForeground(s, new Color(30, 100, 200));
+		// StyleConstants.setFontFamily(s, "Courier New");
+	}
+
+	private void parseCommand(String line) {
+		commandHistory.add(line);
+		commandHistory.size();
+
+		int endIndex;
+		if (line.contains(" "))
+			endIndex = line.indexOf(" ");
+		else
+			endIndex = line.length();
+
+		String command = line.substring(0, endIndex).trim();
+		String params = line.substring(endIndex).trim();
+
+		handleCommand(command, params);
+	}
+
+	private void handleCommand(String command, String params) {
+		Log.getInstance().log("cmd", command + " " + params);
+		if (command.equals("connect") || command.equals("c")) {
+			if (params.equals("mock")) {
+				connectMockAction();
+			} else {
+				String[] paramList = params.split(" ");
+				String host = "";
+				int port = 9009;
+				if (paramList.length > 0)
+					host = paramList[0];
+				try {
+					if (paramList.length > 1)
+						port = Integer.parseInt(paramList[1]);
+				} catch (NumberFormatException e) {
+					Log.getInstance().log("err",
+							"Invalid port, setting default value 9009");
+				}
+				connectHostAction(host, port);
+			}
+		} else if (command.equals("disconnect")) {
+			viewer.setCreatures(null);
+			viewer.setWorld(null);
+			client.closeConnection();
+		} else if (command.equals("refreshcreatures") || command.equals("rc")) {
+			refreshCreaturesAction();
+		} else if (command.equals("viewcreature")) {
+			viewCreatureAction(params);
+		} else if (command.equals("refreshworld") || command.equals("rw")) {
+			refreshWorldAction();
+		} else if (command.equals("style") || command.equals("s")) {
+			styleAction(params);
+		} else if (command.equals(">")) {
+			serverRequestAction(params);
+		} else if (command.equals("quit") || command.equals("q")) {
+			// TODO: quit command
+		}
+	}
+
+	private void connectMockAction() {
+		client.initConnectionMock();
+		refreshWorldAction();
+		refreshCreaturesAction();
+	}
+
+	private void connectHostAction(String host, int port) {
+		client.initConnection(host, port);
+		if (client.isConnected()) {
+			refreshWorldAction();
+			refreshCreaturesAction();
+		}
+	}
+
+	private void refreshCreaturesAction() {
+		Log.getInstance().log("dbg", "refreshCreaturesAction");
+		try {
+			Creature[] creatures = (Creature[]) Worker.post(new Task() {
+				public Object run() throws Exception {
+					return client.readCreatures();
+				}
+			});
+			viewer.setCreatures(creatures);
+			rebuildViewMenu(creatures);
+		} catch (Exception e) {
+			Log.getInstance().log("err", e.toString());
+			e.printStackTrace();
+		}
+		Log.getInstance().log(
+				"autorefresh " + autorefreshItem.isSelected() + " "
+						+ client.isConnected());
+		if (autorefreshItem.isSelected() && client.isConnected()) {
+			SwingUtilities.invokeLater(new Runnable() {
+				public void run() {
+					refreshCreaturesAction();
+				}
+			});
+		}
+	}
+
+	private void refreshWorldAction() {
+		Log.getInstance().log("dbg", "refreshWorldAction");
+		try {
+			World world = (World) Worker.post(new Task() {
+				public Object run() throws Exception {
+					return client.readWorld();
+				}
+			});
+			viewer.setWorld(world);
+		} catch (Exception e) {
+			Log.getInstance().log("err", e.toString());
+			e.printStackTrace();
+		}
+	}
+
+	private void styleAction(String styleName) {
+		Log.getInstance().log("dbg", "SetStyle " + styleName);
+		viewer.setStyle(styleName);
+	}
+
+	private void viewCreatureAction(String params) {
+		int group = -1;
+		int index = -1;
+		try {
+			String[] arr = params.split(" ");
+			if (arr.length > 0) {
+				group = Integer.parseInt(arr[0]);
+			}
+			if (arr.length > 1) {
+				index = Integer.parseInt(arr[1]);
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+
+		viewer.setView(group, index);
+	}
+
+	private void serverRequestAction(String request) {
+		Log.getInstance().log("dbg", "serverRequestAction \"" + request + "\"");
+		final String finalRequest = request;
+		try {
+			Worker.post(new Task() {
+				public Object run() throws Exception {
+					client.send(finalRequest);
+					return null;
+				}
+			});
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		Log.getInstance().log("dbg",
+				"serverRequestAction finished \"" + request + "\"");
+	}
+
+	/**
+	 * The application entry-point.
+	 * 
+	 * @param args
+	 *            Command-line argument.
+	 */
+	public static void main(String[] args) {
+		new App();
+	}
+}
Index: java/client_3D/src/com/framsticks/net/client3D/Client.java
===================================================================
--- java/client_3D/src/com/framsticks/net/client3D/Client.java	(revision 65)
+++ java/client_3D/src/com/framsticks/net/client3D/Client.java	(revision 65)
@@ -0,0 +1,382 @@
+package com.framsticks.net.client3D;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import pl.vorg.mowa.core.graphics.FloatVertexAttrib;
+import pl.vorg.mowa.core.graphics.Geometry;
+import pl.vorg.mowa.core.graphics.GeometryUtils;
+import pl.vorg.mowa.core.graphics.IndexBuffer;
+import pl.vorg.mowa.core.graphics.PrimitiveType;
+import pl.vorg.mowa.core.graphics.VertexStream;
+
+import com.framsticks.net.client3D.Creature.ModelType;
+
+//TODO: ACT przejscie klienta na eventHandlery (simulation start/stop, creatures, error)
+/**
+ * Framstick network client. It sends requests to the server and interprets the
+ * responses.
+ * 
+ * @author vorg
+ */
+public class Client {
+	private Communication comm;
+
+	public Client() {
+	}
+
+	public void send(String request) {
+		try {
+			printResponse(sendRequest(request));
+		} catch (Exception e) {
+			Log.getInstance().log("err", e.toString());
+		}
+	}
+
+	private ArrayList<String> sendRequest(String request) throws IOException,
+			CommunicationErrorException {
+		if (comm == null) {
+			Log.getInstance().log("wrn",
+					"Sending request failed. You are not connected!");
+			return new ArrayList<String>();
+		}
+		comm.sendMessage(request);
+		ArrayList<String> respond = null;
+		try {
+			respond = comm.readMessage();
+		} catch (InterruptedException e) {
+			Log.getInstance().log("err", e.getMessage());
+			return new ArrayList<String>();
+		}
+		return respond;
+	}
+
+	private void printResponse(ArrayList<String> response) {
+		if (response == null) {
+			Log.getInstance().log("err", "Response is null");
+			return;
+		}
+		for (int i = 0; i < response.size(); i++) {
+			Log.getInstance().log("<<<", response.get(i));
+		}
+	}
+
+	/**
+	 * Inits connection with the mock server.
+	 */
+	public void initConnectionMock() {
+		comm = new CommunicationMock();
+		Log.getInstance().log("dbg", "Client.init");
+		try {
+			Log.getInstance().log("dbg", "Connecting to mock");
+			comm.connect("127.0.0.1", 9009);
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+
+	public void initConnection(String host, int port) {
+		comm = new Communication();
+		Log.getInstance().log("dbg", "Client.init");
+		try {
+			Log.getInstance().log("dbg", "Connecting to " + host + ":" + port);
+			comm.connect(host, port);
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+
+	public void closeConnection() {
+		try {
+			if (comm != null) {
+				comm.disconnect();
+				comm = null;
+			}
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+	}
+
+	public Creature[] readCreatures() throws IOException,
+			CommunicationErrorException {
+		return readCreatures(-1, -1, true);
+	}
+
+	public Creature[] readCreatures(int groupNo, int creatureNo, boolean neurons)
+			throws IOException, CommunicationErrorException {
+		ArrayList<Creature> creatures = new ArrayList<Creature>();
+		String request;
+		String creatureFields = neurons ? "name,parts{x,y,z},mechparts{x,y,z},joints{p1,p2},neurodefs{p,j,d}"
+				: "name,parts{x,y,z},mechparts{x,y,z},joints{p1,p2}";
+		if (groupNo == -1 || creatureNo == -1) {
+			request = "get /simulator/populations/groups/+/creatures/+/("
+					+ creatureFields + ")";
+		} else {
+			request = "get /simulator/populations/groups/"
+					+ Integer.toString(groupNo) + "/creatures/"
+					+ Integer.toString(creatureNo) + "/(" + creatureFields
+					+ ")";
+		}
+		String line = null;
+		ArrayList<String> response = null;
+		response = sendRequest(request);
+		printResponse(response);
+		final int INIT = 0;
+		final int PARTS = 1;
+		final int MECHPARTS = 2;
+		final int JOINTS = 3;
+		final int NEURODEFS = 4;
+		int mode = INIT;
+		Creature creature = null;
+		ArrayList<float[]> parts = null;
+		ArrayList<float[]> mechParts = null;
+		ArrayList<int[]> joints = null;
+		ArrayList<NeuroDef> neurodefs = null;
+		float[] part = new float[3];
+		float[] mechPart = new float[3];
+		int[] joint = new int[2];
+		NeuroDef ndef = new NeuroDef();
+		for (int k = 0; k < response.size(); ++k) {
+			line = response.get(k).toString();
+			if (line.startsWith("file")) {
+				String groupsStr = "groups/";
+				int groupBegin = line.indexOf(groupsStr) + groupsStr.length();
+				int groupEnd = line.indexOf("/", groupBegin);
+				if (groupBegin < 0 || groupEnd < 0) {
+
+					System.out.println(line);
+					System.out.println("groupBegin=" + groupBegin
+							+ "; groupEnd=" + groupEnd);
+					System.out.println("request=" + request);
+					System.out.println("groupNo=" + groupNo + "; creatureNo="
+							+ creatureNo);
+
+				}
+
+				int group = Integer.parseInt(line.substring(groupBegin,
+						groupEnd));
+				String creaturesStr = "creatures/";
+				int creatureIndexBegin = line.indexOf(creaturesStr)
+						+ creaturesStr.length();
+				int creatureIndexEnd = line.indexOf("/", creatureIndexBegin);
+				int creatureIndex = Integer.parseInt(line.substring(
+						creatureIndexBegin, creatureIndexEnd));
+
+				creature = null;
+				for (Creature c : creatures) {
+					if ((c.getGroup() == group)
+							&& (c.getIndex() == creatureIndex)) {
+						creature = c;
+						break;
+					}
+				}
+				if (creature == null) {
+					creature = new Creature("", group, creatureIndex);
+					creatures.add(creature);
+				}
+
+				if (line.indexOf("/parts") > -1) {
+					mode = PARTS;
+					parts = new ArrayList<float[]>();
+				} else if (line.indexOf("/mechparts") > -1) {
+					mode = MECHPARTS;
+					mechParts = new ArrayList<float[]>();
+				} else if (line.indexOf("/joints") > -1) {
+					mode = JOINTS;
+					joints = new ArrayList<int[]>();
+				} else if (line.indexOf("/neurodefs") > -1) {
+					mode = NEURODEFS;
+					neurodefs = new ArrayList<NeuroDef>();
+				}
+			} else if (line.startsWith("x")) {
+				float value = Float.parseFloat(line
+						.substring(line.indexOf(":") + 1));
+				if (mode == PARTS)
+					part[0] = value;
+				else if (mode == MECHPARTS)
+					mechPart[0] = value;
+			} else if (line.startsWith("y")) {
+				float value = Float.parseFloat(line
+						.substring(line.indexOf(":") + 1));
+				if (mode == PARTS)
+					part[2] = value; // y and z swap
+				else if (mode == MECHPARTS)
+					mechPart[2] = value; // y and z swap
+			} else if (line.startsWith("z")) {
+				float value = Float.parseFloat(line
+						.substring(line.indexOf(":") + 1));
+				if (mode == PARTS) {
+					part[1] = value; // z and y swap
+					parts.add(part);
+					part = new float[3];
+				} else if (mode == MECHPARTS) {
+					mechPart[1] = value; // z and y swap
+					mechParts.add(mechPart);
+					mechPart = new float[3];
+				}
+			} else if (line.startsWith("p1")) {
+				int p1 = Integer
+						.parseInt(line.substring(line.indexOf(":") + 1));
+				joint[0] = p1;
+			} else if (line.startsWith("p2")) {
+				int p2 = Integer
+						.parseInt(line.substring(line.indexOf(":") + 1));
+				joint[1] = p2;
+				joints.add(joint);
+				joint = new int[2];
+			} else if ((mode == NEURODEFS) && (line.startsWith("p:"))) {
+				int index = Integer.parseInt(line
+						.substring(line.indexOf(":") + 1));
+				if (index >= 0) {
+					ndef.locationIndex = index;
+					ndef.locationType = NeuroDef.LocationType.Part;
+				}
+			} else if ((mode == NEURODEFS) && (line.startsWith("j:"))) {
+				int index = Integer.parseInt(line
+						.substring(line.indexOf(":") + 1));
+				if (index >= 0) {
+					ndef.locationIndex = index;
+					ndef.locationType = NeuroDef.LocationType.Joint;
+				}
+			} else if ((mode == NEURODEFS) && (line.startsWith("d:"))) {
+				ndef.def = line.substring(2);
+				neurodefs.add(ndef);
+				ndef = new NeuroDef();
+			} else if (line.startsWith("eof")) {
+				if (mode == PARTS) {
+					creature.setParts(parts.toArray(new float[][] {}),
+							Creature.ModelType.Parts);
+				} else if (mode == MECHPARTS) {
+					creature.setParts((mechParts.toArray(new float[][] {})),
+							Creature.ModelType.MechParts);
+				} else if (mode == JOINTS) {
+					creature.setJoints(joints.toArray(new int[][] {}));
+				} else if (mode == NEURODEFS) {
+					creature.setNeuroDefs(neurodefs.toArray(new NeuroDef[] {}));
+				}
+			}
+		}
+		Log.getInstance().log("dbg",
+				"" + creatures.size() + " creatures loaded");
+
+		ArrayList<Creature> invalidCreatures = new ArrayList<Creature>();
+		for (Creature c : creatures) {
+			if ((c.getJoints() == null)
+					|| (c.getParts(ModelType.Parts) == null)
+					|| (c.getParts(ModelType.MechParts) == null)) {
+				invalidCreatures.add(c);
+				Log.getInstance().log("wrn", "Invalid creature found");
+			}
+		}
+		for (Creature c : invalidCreatures) {
+			creatures.remove(c);
+		}
+		invalidCreatures.clear();
+		return creatures.toArray(new Creature[] {});
+	}
+
+	public World readWorld() throws IOException, CommunicationErrorException {
+		final int INIT = 0;
+		final int POINTS = 1;
+		final int FACES = 2;
+		World world = new World();
+
+		String line = null;
+		String request = "get /simulator/world wrldbnd,wrldwat,faces";
+		ArrayList<String> response = null;
+
+		response = sendRequest(request);
+		printResponse(response);
+
+		ArrayList<Float> points = new ArrayList<Float>();
+		ArrayList<Integer> faces = new ArrayList<Integer>();
+		PrimitiveType primitiveType = PrimitiveType.None;
+		PrimitiveType facePrimitiveType;
+		int mode = INIT;
+
+		for (int k = 0; k < response.size(); ++k) {
+			line = response.get(k);
+			if (line.startsWith("wrldbnd")) {
+				world.setBoundaries(Integer.parseInt(line.substring(line
+						.indexOf(":") + 1)));
+				continue;
+			} else if (line.startsWith("wrldwat")) {
+				world.setWaterLevel(Float.parseFloat(line.substring(line
+						.indexOf(":") + 1)));
+				continue;
+			} else if (line.trim().equals("p")) {
+				mode = POINTS;
+				continue;
+			} else if (line.trim().equals("f")) {
+				mode = FACES;
+				continue;
+			} else if (line.trim().equals("~")) {
+				break;
+			}
+
+			if (mode == POINTS) {
+				String[] tokens = line.split(" ");
+				points.add(Float.parseFloat(tokens[0]));
+				points.add(Float.parseFloat(tokens[2])); // y and z swap
+				points.add(Float.parseFloat(tokens[1])); // z and y swap
+			} else if (mode == FACES) {
+				String[] tokens = line.split(" ");
+				if (tokens.length == 3)
+					facePrimitiveType = PrimitiveType.Triangles;
+				else if (tokens.length == 4)
+					facePrimitiveType = PrimitiveType.Quads;
+				else
+					facePrimitiveType = PrimitiveType.None;
+
+				if (primitiveType == PrimitiveType.None) {
+					primitiveType = facePrimitiveType;
+				} else if (primitiveType != facePrimitiveType) {
+					Log.getInstance().log(
+							"err",
+							"Unsupported terrain type (" + primitiveType + "!="
+									+ primitiveType + ")");
+					return world;
+				}
+
+				for (String token : tokens) {
+					faces.add(Integer.parseInt(token));
+				}
+			}
+		}
+
+		float[] positions = new float[points.size()];
+		for (int i = 0; i < positions.length; i++) {
+			positions[i] = points.get(i);
+		}
+
+		int[] indices = new int[faces.size()];
+		for (int i = 0; i < indices.length; i++) {
+			indices[i] = faces.get(i);
+		}
+
+		Geometry geometry = new Geometry();
+		VertexStream vs = new VertexStream();
+		geometry.setVertexStream(vs);
+		geometry.setPrimitiveType(primitiveType);
+
+		IndexBuffer ib = new IndexBuffer();
+		ib.setBuffer(indices);
+		vs.setIndexBuffer(ib);
+
+		FloatVertexAttrib posAttrib = new FloatVertexAttrib("pos");
+		posAttrib.setBuffer(positions);
+		vs.addAttrib(posAttrib);
+
+		geometry.updateBoundingBox();
+
+		GeometryUtils.generateNormals(geometry);
+
+		world.setGeometry(geometry);
+
+		return world;
+	}
+
+	public Boolean isConnected() {
+		return comm.isConnected();
+	}
+}
Index: java/client_3D/src/com/framsticks/net/client3D/Communication.java
===================================================================
--- java/client_3D/src/com/framsticks/net/client3D/Communication.java	(revision 65)
+++ java/client_3D/src/com/framsticks/net/client3D/Communication.java	(revision 65)
@@ -0,0 +1,140 @@
+package com.framsticks.net.client3D;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.net.Socket;
+import java.util.ArrayList;
+
+/**
+ * Communication class.
+ * 
+ * @author MoMaT (modified by vorg @ 2007/01/25)
+ */
+public class Communication {
+	private Socket socket = null;
+	private PrintWriter out = null;
+	private BufferedReader in = null;
+	protected boolean connected = false;
+
+	private ArrayList<String> log = new ArrayList<String>();
+
+	/**
+	 * Connect to a server.
+	 * 
+	 * @param ip
+	 * @param port
+	 * @throws IOException
+	 *             Thrown on I/O error.
+	 */
+	public void connect(String ip, int port) throws IOException {
+		try {
+			socket = new Socket(ip, port);
+			out = new PrintWriter(socket.getOutputStream(), true);
+			in = new BufferedReader(new InputStreamReader(
+					socket.getInputStream()));
+
+			connected = true;
+			sendMessage("version 3");
+			String message = in.readLine();
+			log.add(">  " + message);
+
+		} catch (Exception e) {
+			Log.getInstance().log("err", e.toString());
+		}
+	}
+
+	/**
+	 * Close connection.
+	 * 
+	 * @throws IOException
+	 *             Thrown on socket close error.
+	 */
+	public void disconnect() throws IOException {
+		connected = false;
+		out.close();
+		in.close();
+		socket.close();
+	}
+
+	/**
+	 * Read a server response.
+	 * 
+	 * @return server answer
+	 * @throws IOException
+	 *             Thrown on read error.
+	 * @throws InterruptedException
+	 *             Thrown on thread sleep interruption.
+	 * @throws CommunicationErrorException
+	 *             Thrown on protocol error.
+	 */
+	public ArrayList<String> readMessage() throws IOException,
+			InterruptedException, CommunicationErrorException {
+		if (!connected) {
+			Log.getInstance().log("wrn",
+					"Reading message failed. You are not connected!");
+			return new ArrayList<String>();
+		}
+		String message = null;
+		ArrayList<String> buffer = new ArrayList<String>();
+
+		// gentle wait for input on socket
+		// elapsedTime added by vorg to avoid infinite loop
+		int elapsedTime = 0;
+		while (false == in.ready()) {
+			Thread.sleep(100);
+			elapsedTime++;
+			if (elapsedTime > 100) {
+				throw new InterruptedException("Request timeout");
+			}
+		}
+
+		// read server answer
+		while ((message = in.readLine()) != null) {
+			log.add("> " + message);
+			// stop if reading complete
+			if (message.startsWith("ok")) {
+				// add ok line if it contains additional info
+				if (3 < message.length()) {
+					buffer.add(message);
+				}
+				break;
+			} else if (message.startsWith("error")) {
+				String error = message.substring(message.indexOf(' '));
+				throw new CommunicationErrorException(error);
+			}
+			buffer.add(message);
+		}
+		return buffer;
+	}
+
+	/**
+	 * Send request to a server.
+	 * 
+	 * @param message
+	 */
+	public void sendMessage(String message) {
+		Log.getInstance().log(">>>", message);
+		log.add(message);
+		if (!connected) {
+			Log.getInstance().log("wrn",
+					"Sending message failed. You are not connected!");
+			return;
+		}
+		out.println(message);
+	}
+
+	/**
+	 * Log getter.
+	 * 
+	 * @return log array
+	 */
+	public ArrayList<String> getLog() {
+		return log;
+	}
+
+	public Boolean isConnected() {
+		return connected;
+	}
+}
Index: java/client_3D/src/com/framsticks/net/client3D/CommunicationErrorException.java
===================================================================
--- java/client_3D/src/com/framsticks/net/client3D/CommunicationErrorException.java	(revision 65)
+++ java/client_3D/src/com/framsticks/net/client3D/CommunicationErrorException.java	(revision 65)
@@ -0,0 +1,25 @@
+package com.framsticks.net.client3D;
+
+
+/** 
+ * Thrown when protocol error occurs.
+ * @author MoMaT
+ */
+public class CommunicationErrorException extends Exception {
+	static final long serialVersionUID = 1;
+	
+	/**
+	 * Communication error exception constructor.
+	 */
+	public CommunicationErrorException() {
+		super();
+	}
+
+	/**
+	 * Communication error exception with message passing constructor.
+	 * @param string
+	 */
+	public CommunicationErrorException(String string) {
+		super(string);
+	}
+}
Index: java/client_3D/src/com/framsticks/net/client3D/CommunicationMock.java
===================================================================
--- java/client_3D/src/com/framsticks/net/client3D/CommunicationMock.java	(revision 65)
+++ java/client_3D/src/com/framsticks/net/client3D/CommunicationMock.java	(revision 65)
@@ -0,0 +1,68 @@
+package com.framsticks.net.client3D;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * Mock server connection. Instead of connecting through the network it gets
+ * data from text files.
+ * 
+ * @author vorg
+ */
+public class CommunicationMock extends Communication {
+	private String message;
+
+	public void connect(String ip, int port) throws IOException {
+		connected = true;
+	}
+
+	public void disconnect() throws IOException {
+		connected = false;
+	}
+
+	public ArrayList<String> readMessage() throws IOException,
+			InterruptedException, CommunicationErrorException {
+		ArrayList<String> buffer = new ArrayList<String>();
+		if (message.indexOf("name,genotype,parts,joints") > -1) {
+			buffer = readFile("res/mock/creature_Oafuz_info.txt");
+		} else if (message.indexOf("/joints p1,p2") > -1) {
+			buffer = readFile("res/mock/creature_Oafuz_joints.txt");
+		} else if (message.indexOf("/parts x,y,z") > -1) {
+			buffer = readFile("res/mock/creature_Oafuz_parts.txt");
+		} else if (message.indexOf("/neurodefs") > -1) {
+			buffer = readFile("res/mock/creature_Oafuz_neurons.txt");
+		} else if (message.indexOf("/groups/+/creatures/+/") > -1) {
+			buffer = readFile("res/mock/creatures.txt");
+		} else if (message.indexOf("/world") > -1) {
+			buffer = readFile("res/mock/world.txt");
+		}
+		return buffer;
+	}
+
+	public void sendMessage(String message) {
+		Log.getInstance().log(">>>", message);
+		this.message = message;
+	}
+
+	public ArrayList<String> getLog() {
+		return new ArrayList<String>();
+	}
+
+	private ArrayList<String> readFile(String path) {
+		ArrayList<String> result = new ArrayList<String>();
+		try {
+			BufferedReader reader = new BufferedReader(new FileReader(path));
+			String line = null;
+			while ((line = reader.readLine()) != null) {
+				result.add(line);
+			}
+		} catch (Exception e) {
+			Log.getInstance().log("err", e.toString());
+		}
+
+		return result;
+	}
+
+}
Index: java/client_3D/src/com/framsticks/net/client3D/Creature.java
===================================================================
--- java/client_3D/src/com/framsticks/net/client3D/Creature.java	(revision 65)
+++ java/client_3D/src/com/framsticks/net/client3D/Creature.java	(revision 65)
@@ -0,0 +1,166 @@
+package com.framsticks.net.client3D;
+
+import pl.vorg.mowa.core.graphics.BoundingBox;
+import pl.vorg.mowa.core.graphics.Vec3;
+
+/**
+ * Framstick creature body class.
+ * @author MoMaT, modified by Vorg @ 2007-01-01
+ */
+public class Creature {
+	private String name;
+	private String genotype;
+	
+	public static enum ModelType {
+		Parts,
+		MechParts
+	}
+
+	private int[][] joints = null;
+	private float[][] parts = null;
+	private float[][] mechParts = null;
+	private NeuroDef[] neuroDefs = null;
+	private BoundingBox boundingBox;
+	private BoundingBox mechBoundingBox;
+	
+	private int group;
+	private int index;
+	
+	/**
+	 * Class constructor. Initializes data structure.
+	 * @param name
+	 * @param partsCount number of parts
+	 * @param jointsCount number of joints
+	 * @param group creature group
+	 * @param index position in a group
+	 */
+	public Creature(String name, int group, int index) {
+		this.name = name;
+		this.group = group;
+		this.index = index;
+	}
+	
+	public String toString() {
+		return "/" + group + "/" + index + "/" + name;		
+	}	
+	
+	public String getName() {
+		return name;
+	}
+	
+	public int getGroup() {
+		return group;
+	}
+
+	public int getIndex() {
+		return index;
+	}
+
+	public String getGenotype() {
+		return genotype;
+	}
+
+	public void setGenotype(String genotype) {
+		this.genotype = genotype;
+	}
+
+	public int[][] getJoints() {
+		return joints;
+	}
+
+	public void setJoints(int[][] joints) {
+		this.joints = joints;
+	}
+
+	public float[][] getParts(ModelType modelType) {
+		return (modelType == ModelType.Parts) ? parts : mechParts;
+	}
+
+	public void setParts(float[][] value, ModelType modelType) {
+		if (modelType == ModelType.Parts)
+			parts = value;
+		else {
+			mechParts = value;
+		}
+	}
+
+	public void setNeuroDefs(NeuroDef[] defs)
+	{ neuroDefs=defs; }
+
+	public NeuroDef[] getNeuroDefs()
+	{ return neuroDefs; }
+	
+	/**
+	 * Gets translation vector to the middle point of a joint.
+	 * @param index index of joint in a joints array
+	 * @return translation vector
+	 */
+	public float[] jointTranslation(int index, ModelType modelType) {
+		float[][] arr = (modelType == ModelType.Parts) ? parts : mechParts;
+		Vec3 p1 = new Vec3(arr[joints[index][0]]);
+		Vec3 p2 = new Vec3(arr[joints[index][1]]);		
+		Vec3 vector = Vec3.sub(p2, p1);
+		
+		vector.mul(0.5f);
+		p1.add(vector);		
+		return p1.getVector();
+	}	
+	/**
+	 * Calculates the length of vector between two joint points. 
+	 * @param index index of joint in a joints array
+	 * @return length of vector
+	 */
+	public float jointLength(int index, ModelType modelType) {
+		float[][] arr = (modelType == ModelType.Parts) ? parts : mechParts;
+		Vec3 p1 = new Vec3(arr[joints[index][0]]);
+		Vec3 p2 = new Vec3(arr[joints[index][1]]);		
+		Vec3 vector = Vec3.sub(p2, p1);
+		
+		return vector.length();
+	}	
+	/**
+	 * Calculates the angle and the rotation axis between a joint and the base vector. 
+	 * @param index index of joint in a joints array
+	 * @return the array of angle and axis x, y, z coordinates
+	 */
+	public float[] jointRotation(int index, ModelType modelType) {
+		float[][] arr = (modelType == ModelType.Parts) ? parts : mechParts;
+		Vec3 p1 = new Vec3(arr[joints[index][0]]);
+		Vec3 p2 = new Vec3(arr[joints[index][1]]);		
+		//Vec3 vector = p1.vector(p2);
+		Vec3 vector = Vec3.sub(p2, p1);
+		Vec3 base = new Vec3(0, 0, 1);
+
+		float cos = Vec3.dot(vector, base)/vector.length();
+		float angle = (float)(-Math.acos(cos)*180/Math.PI);
+
+		Vec3 cross = Vec3.cross(vector,base);				
+		float[] v = cross.getVector();
+		float[] result = new float[4];		
+		result[0] = angle;
+		result[1] = v[0];
+		result[2] = v[1];
+		result[3] = v[2];
+		return result;
+	}
+	
+	/**
+	 * Calculates boundingBox of creature's model
+	 * @author vorg
+	 * @param modelType
+	 * @return boudingBox
+	 */
+	public BoundingBox getBoundingBox(ModelType modelType) {
+		BoundingBox bbox = (modelType == ModelType.Parts) ? boundingBox : mechBoundingBox;
+		if (bbox == null) {
+			bbox = new BoundingBox();
+			float[][] arr = (modelType == ModelType.Parts) ? parts : mechParts;
+			for(int i=0; i<arr.length; i++) {
+				bbox.add(arr[i][0], arr[i][1], arr[i][2]);
+			}
+			if (modelType == ModelType.Parts) boundingBox = bbox;
+			else mechBoundingBox = bbox;
+		}
+		return bbox;
+	}
+}
Index: java/client_3D/src/com/framsticks/net/client3D/ICommandListener.java
===================================================================
--- java/client_3D/src/com/framsticks/net/client3D/ICommandListener.java	(revision 65)
+++ java/client_3D/src/com/framsticks/net/client3D/ICommandListener.java	(revision 65)
@@ -0,0 +1,9 @@
+package com.framsticks.net.client3D;
+
+/**
+ * 
+ * @author vorg
+ */
+public interface ICommandListener {
+	public void onCommand(String command, String params);
+}
Index: java/client_3D/src/com/framsticks/net/client3D/ILogListener.java
===================================================================
--- java/client_3D/src/com/framsticks/net/client3D/ILogListener.java	(revision 65)
+++ java/client_3D/src/com/framsticks/net/client3D/ILogListener.java	(revision 65)
@@ -0,0 +1,10 @@
+package com.framsticks.net.client3D;
+
+/**
+ * 
+ * @author vorg
+ *
+ */
+public interface ILogListener {
+	public void onMesssage(String category, String text);
+}
Index: java/client_3D/src/com/framsticks/net/client3D/Log.java
===================================================================
--- java/client_3D/src/com/framsticks/net/client3D/Log.java	(revision 65)
+++ java/client_3D/src/com/framsticks/net/client3D/Log.java	(revision 65)
@@ -0,0 +1,55 @@
+package com.framsticks.net.client3D;
+
+//import java.io.*;
+import java.util.ArrayList;
+
+/**
+ * Logger
+ * 
+ * @author vorg
+ */
+public class Log {
+   ///private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy.dd.MM HH:mm");
+	private static Log instance;
+	private ArrayList<ILogListener> listeners;
+	private Boolean enabled = true;
+   
+	private Log() {
+		listeners = new ArrayList<ILogListener>();
+	}
+   
+	public void addLoggerListener(ILogListener listener) {
+		listeners.add(listener);
+	}
+   
+	public void removeLoggerListener(ILogListener listener) {
+		listeners.remove(listener);
+	}
+
+	public void log(String text) {
+		log("   ", text);
+	}
+   
+	public void log(String category, String text) {
+		if (enabled) {
+			for(ILogListener listener : listeners) {
+				listener.onMesssage(category, text);
+			}
+		}
+	}
+   
+	public static Log getInstance() {
+		if (instance == null) {
+			instance = new Log();
+		}
+	   return instance;
+	}
+	
+	public void setEnabled(Boolean state) {
+		enabled = state;
+	}
+	
+	public Boolean isEnabled() {
+		return enabled;
+	}
+}
Index: java/client_3D/src/com/framsticks/net/client3D/NeuroDef.java
===================================================================
--- java/client_3D/src/com/framsticks/net/client3D/NeuroDef.java	(revision 65)
+++ java/client_3D/src/com/framsticks/net/client3D/NeuroDef.java	(revision 65)
@@ -0,0 +1,11 @@
+package com.framsticks.net.client3D;
+
+public class NeuroDef {
+	public static enum LocationType {
+		None, Part, Joint
+	};
+
+	public LocationType locationType = LocationType.None;
+	public int locationIndex;
+	public String def;
+}
Index: java/client_3D/src/com/framsticks/net/client3D/Renderer.java
===================================================================
--- java/client_3D/src/com/framsticks/net/client3D/Renderer.java	(revision 65)
+++ java/client_3D/src/com/framsticks/net/client3D/Renderer.java	(revision 65)
@@ -0,0 +1,510 @@
+package com.framsticks.net.client3D;
+
+import javax.media.opengl.GL;
+import javax.media.opengl.GLAutoDrawable;
+import javax.media.opengl.GLEventListener;
+import javax.media.opengl.glu.GLU;
+
+import pl.vorg.mowa.core.graphics.BoundingBox;
+import pl.vorg.mowa.core.graphics.SkyBox;
+import pl.vorg.mowa.core.graphics.Vec3;
+
+import com.sun.opengl.util.FPSAnimator;
+
+/**
+ * Class responsible for rendering of framsticks and the world.
+ * 
+ * @author vorg
+ */
+public class Renderer implements GLEventListener {
+	public static final float AUTO_ROTATION_STEP = 0.5f;
+	private float rotationY = 0;
+	private float rotationX = 0;
+	private float translation = -1.0f;
+	private FPSAnimator animator;
+	private World world;
+	private Creature[] creatures;
+	private Creature.ModelType modelType = Creature.ModelType.MechParts;
+	private cgfxEffect style;
+	private SkyBox skyBox;
+
+	public SkyBox getSkyBox() {
+		return skyBox;
+	}
+
+	public void setSkyBox(SkyBox box) {
+		skyBox = box;
+	}
+
+	public cgfxEffect getStyle() {
+		return style;
+	}
+
+	/**
+	 * Sets the currently used framstick rendering style.
+	 * 
+	 * @param style
+	 */
+	public void setStyle(cgfxEffect style) {
+		this.style = style;
+	}
+
+	public Creature[] getCreatures() {
+		return creatures;
+	}
+
+	/**
+	 * Sets the number of creatures to display. To display a single creature in
+	 * a zoomed view pass a single element array and set the modelType to
+	 * Creature.ModelType.Parts.
+	 * 
+	 * @param creatures
+	 */
+	public void setCreatures(Creature[] creatures) {
+		this.creatures = creatures;
+	}
+
+	public World getWorld() {
+		return world;
+	}
+
+	/**
+	 * Sets the world to be displayed.
+	 * 
+	 * @param world
+	 */
+	public void setWorld(World world) {
+		this.world = world;
+	}
+
+	public Creature.ModelType getModelType() {
+		return modelType;
+	}
+
+	/**
+	 * Sets how the framsticks should be rendered.
+	 * 
+	 * @param modelType
+	 *            Creature.ModelType.Parts - the way they were created,
+	 *            Creature.ModelType.MechParts - the way they currently look in
+	 *            their world (in accordance with physics)
+	 */
+	public void setModelType(Creature.ModelType modelType) {
+		this.modelType = modelType;
+	}
+
+	public float getTranslation() {
+		return translation;
+	}
+
+	public void setTranslation(float t) {
+		translation = t;
+		if (translation < 0.1)
+			translation = 0.1f;
+	}
+
+	public void resetTranslation() {
+		translation = -1.0f;
+	}
+
+	public float getRotationX() {
+		return rotationX;
+	}
+
+	public float getRotationY() {
+		return rotationY;
+	}
+
+	public void setRotationX(float rotation) {
+		this.rotationX = rotation;
+		if (this.rotationX > 85.0)
+			this.rotationX = 85.0f;
+		else if (this.rotationX < 0)
+			this.rotationX = 0.0f;
+	}
+
+	public void setRotationY(float rotation) {
+		this.rotationY = rotation;
+	}
+
+	public void display(GLAutoDrawable drawable) {
+		style.setSkyBox(skyBox);
+		GL gl = drawable.getGL();
+
+		gl.glLoadIdentity();
+		gl.glClear(GL.GL_DEPTH_BUFFER_BIT | GL.GL_COLOR_BUFFER_BIT);
+
+		if ((creatures != null) && (creatures.length == 1)
+				&& (modelType == Creature.ModelType.Parts)) {
+			// renderowanie pojedynczego framsticka
+
+			drawCreature(gl);
+		} else {
+			// renderowanie calaego swiata wraz z framstickami z nim zyjacymi
+			drawWorld(gl);
+		}
+		gl.glFlush();
+	}
+
+	public void drawWorld(GL gl) {
+
+		// szescian otaczajacy swiat
+		// wyliczamy go po to aby ustawic kamere tak aby byla w stanie objac
+		// wszystko
+		BoundingBox bbox;
+		if ((world != null) && (world.getGeometry() != null)) {
+			bbox = world.getGeometry().getBoundingBox();
+
+		} else {
+			bbox = new BoundingBox();
+			if (creatures != null) {
+				for (Creature c : creatures) {
+					bbox.add(c.getBoundingBox(Creature.ModelType.MechParts));
+				}
+			}
+		}
+
+		Vec3 bboxSize = bbox.getSize();
+		Vec3 bboxCenter = Vec3
+				.mul(0.5f, Vec3.add(bbox.getMax(), bbox.getMin()));
+
+		if (translation < 0 || translation > 100000000.0f) {
+			translation = (float) Math.sqrt(bboxSize.getX() * bboxSize.getX()
+					/ 4 + bboxSize.getZ() * bboxSize.getZ() / 4) * 1.5f;
+		}
+
+		gl.glTranslatef(0, 0, -1.0f * translation);
+		// gl.glRotatef(30, 1, 0, 0);
+
+		gl.glRotatef(rotationX, 1, 0, 0);
+		gl.glRotatef(rotationY, 0, 1, 0);
+		gl.glTranslatef(-bboxCenter.getX(), -bboxCenter.getY() - 2, -bboxCenter
+				.getZ());
+		if (skyBox != null)
+			skyBox.Draw(gl, 1000.0f, new Vec3(rotationX, rotationY, 0));
+		// renderowanie swiata
+		if ((world != null) && (world.getGeometry() != null)
+				&& !world.getGeometry().getBoundingBox().isEmpty()) {
+			BoundingBox wrldBox = world.getGeometry().getBoundingBox();
+			Vec3 wmin = wrldBox.getMin();
+			Vec3 wmax = wrldBox.getMax();
+
+			drawWorldGeometry(gl);
+			drawWater(gl, wmin, wmax);
+			if (world.getBoundaries() == 0) {
+				drawWorldNoBounduaries(gl, wmin, wmax);
+			} else if (world.getBoundaries() == 1) {
+				drawWorldFence(gl, wmin, wmax);
+			} else if (world.getBoundaries() == 2) {
+				drawWorldTeleports(gl, wmin, wmax);
+			}
+
+		}
+
+		// renderowanie stworow
+		if ((creatures != null) && (style != null)) {
+			if (!style.isInitialized()) {
+				style.Init();
+				if (!style.isInitialized()) {
+					setStyle(null);
+					return;
+				}
+			}
+			style.render(gl, creatures, Creature.ModelType.MechParts);
+		}
+	}
+
+	/**
+	 * Rendering of the world surface.
+	 * 
+	 * @param gl
+	 */
+	private void drawWorldGeometry(GL gl) {
+		gl.glEnable(GL.GL_LIGHTING);
+		gl.glEnable(GL.GL_LIGHT0);
+		gl.glColor4f(0.9f, 0.8f, 0.2f, 0.0f);
+		gl.glEnable(GL.GL_COLOR_MATERIAL);
+		gl.glDisable(GL.GL_BLEND);
+		world.getGeometry().display(gl);
+		gl.glDisable(GL.GL_COLOR_MATERIAL);
+		gl.glDisable(GL.GL_LIGHTING);
+		// gl.glEnable(GL.GL_CULL_FACE);
+
+		gl.glColor4f(0.7f, 0.7f, 0.7f, 0.1f);
+		gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_LINE);
+		gl.glEnable(GL.GL_LINE_SMOOTH);
+		gl.glEnable(GL.GL_BLEND);
+
+		// gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
+		gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_DST_COLOR);
+		gl.glHint(GL.GL_LINE_SMOOTH_HINT, GL.GL_DONT_CARE);
+		gl.glLineWidth(2.5f);
+		world.getGeometry().display(gl);
+		gl.glDisable(GL.GL_BLEND);
+		gl.glDisable(GL.GL_LINE_SMOOTH);
+		gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_FILL);
+	}
+
+	/**
+	 * Rendering of the world boundaries which consist of teleports (you get in
+	 * on one side of the world and get out on the opposite side).
+	 * 
+	 * @param gl
+	 * @param wmin
+	 * @param wmax
+	 */
+	private void drawWorldTeleports(GL gl, Vec3 wmin, Vec3 wmax) {
+		gl.glEnable(GL.GL_BLEND);
+		gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
+		gl.glColor4f(0.0f, 0.4f, 0.9f, 0.5f);
+		gl.glBegin(GL.GL_TRIANGLES);
+		for (float z = -wmin.getZ(); z < wmax.getZ(); z += 1) {
+			gl.glVertex3f(wmin.getX(), 0, z);
+			gl.glVertex3f(wmin.getX(), 0, z + 1);
+			gl.glVertex3f(wmin.getX(), 1, z + 0.5f);
+		}
+		for (float z = -wmin.getZ(); z < wmax.getZ(); z += 1) {
+			gl.glVertex3f(wmax.getX(), 0, z);
+			gl.glVertex3f(wmax.getX(), 0, z + 1);
+			gl.glVertex3f(wmax.getX(), 1, z + 0.5f);
+		}
+		for (float x = -wmin.getX(); x < wmax.getX(); x += 1) {
+			gl.glVertex3f(x, 0, wmin.getZ());
+			gl.glVertex3f(x + 1, 0, wmin.getZ());
+			gl.glVertex3f(x + 0.5f, 1, wmin.getZ());
+		}
+		for (float x = -wmin.getX(); x < wmax.getX(); x += 1) {
+			gl.glVertex3f(x, 0, wmax.getZ());
+			gl.glVertex3f(x + 1, 0, wmax.getZ());
+			gl.glVertex3f(x + 0.5f, 1, wmax.getZ());
+		}
+		gl.glEnd();
+		gl.glDisable(GL.GL_BLEND);
+	}
+
+	/**
+	 * Rendering of the world boundaries which consist of a fence.
+	 * 
+	 * @param gl
+	 * @param wmin
+	 * @param wmax
+	 */
+	private void drawWorldFence(GL gl, Vec3 wmin, Vec3 wmax) {
+		gl.glEnable(GL.GL_BLEND);
+		gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
+		gl.glColor4f(1.0f, 0.2f, 0.0f, 0.5f);
+		gl.glBegin(GL.GL_QUADS);
+		for (float z = -wmin.getZ(); z < wmax.getZ(); z += 1) {
+			gl.glVertex3f(wmin.getX(), 0, z);
+			gl.glVertex3f(wmin.getX(), 0, z + 1);
+			gl.glVertex3f(wmin.getX(), 1, z + 1);
+			gl.glVertex3f(wmin.getX(), 1, z);
+		}
+		for (float z = -wmin.getZ(); z < wmax.getZ(); z += 1) {
+			gl.glVertex3f(wmax.getX(), 0, z);
+			gl.glVertex3f(wmax.getX(), 0, z + 1);
+			gl.glVertex3f(wmax.getX(), 1, z + 1);
+			gl.glVertex3f(wmax.getX(), 1, z);
+		}
+		for (float x = -wmin.getX(); x < wmax.getX(); x += 1) {
+			gl.glVertex3f(x, 0, wmin.getZ());
+			gl.glVertex3f(x + 1, 0, wmin.getZ());
+			gl.glVertex3f(x + 1, 1, wmin.getZ());
+			gl.glVertex3f(x, 1, wmin.getZ());
+		}
+		for (float x = -wmin.getX(); x < wmax.getX(); x += 1) {
+			gl.glVertex3f(x, 0, wmax.getZ());
+			gl.glVertex3f(x + 1, 0, wmax.getZ());
+			gl.glVertex3f(x + 1, 1, wmax.getZ());
+			gl.glVertex3f(x, 1, wmax.getZ());
+		}
+		gl.glEnd();
+		gl.glDisable(GL.GL_BLEND);
+	}
+
+	/**
+	 * Rendering of the unbounded world boundaries.
+	 * 
+	 * @param gl
+	 * @param wmin
+	 * @param wmax
+	 */
+	private void drawWorldNoBounduaries(GL gl, Vec3 wmin, Vec3 wmax) {
+		gl.glEnable(GL.GL_BLEND);
+		gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
+		gl.glBegin(GL.GL_QUADS);
+		for (float z = -wmin.getZ(); z < wmax.getZ(); z += 1) {
+			gl.glColor4f(1.0f, 0.8f, 0.0f, 0.5f);
+			gl.glVertex3f(wmin.getX(), 0, z);
+			gl.glVertex3f(wmin.getX(), 0, z + 1);
+			gl.glColor4f(1.0f, 0.8f, 0.0f, 0.0f);
+			gl.glVertex3f(wmin.getX() - 2, 0, z + 1);
+			gl.glVertex3f(wmin.getX() - 2, 0, z);
+		}
+		for (float z = -wmin.getZ(); z < wmax.getZ(); z += 1) {
+			gl.glColor4f(1.0f, 0.8f, 0.0f, 0.5f);
+			gl.glVertex3f(wmax.getX(), 0, z);
+			gl.glVertex3f(wmax.getX(), 0, z + 1);
+			gl.glColor4f(1.0f, 0.8f, 0.0f, 0.0f);
+			gl.glVertex3f(wmax.getX() + 2, 0, z + 1);
+			gl.glVertex3f(wmax.getX() + 2, 0, z);
+		}
+		for (float x = -wmin.getX(); x < wmax.getX(); x += 1) {
+			gl.glColor4f(1.0f, 0.8f, 0.0f, 0.5f);
+			gl.glVertex3f(x, 0, wmin.getZ());
+			gl.glVertex3f(x + 1, 0, wmin.getZ());
+			gl.glColor4f(1.0f, 0.8f, 0.0f, 0.0f);
+			gl.glVertex3f(x + 1, 0, wmin.getZ() - 2);
+			gl.glVertex3f(x, 0, wmin.getZ() - 2);
+		}
+		for (float x = -wmin.getX(); x < wmax.getX(); x += 1) {
+			gl.glColor4f(1.0f, 0.8f, 0.0f, 0.5f);
+			gl.glVertex3f(x, 0, wmax.getZ());
+			gl.glVertex3f(x + 1, 0, wmax.getZ());
+			gl.glColor4f(1.0f, 0.8f, 0.0f, 0.0f);
+			gl.glVertex3f(x + 1, 0, wmax.getZ() + 2);
+			gl.glVertex3f(x, 0, wmax.getZ() + 2);
+		}
+		gl.glEnd();
+
+		gl.glBegin(GL.GL_TRIANGLES);
+
+		gl.glColor4f(1.0f, 0.8f, 0.0f, 0.5f);
+		gl.glVertex3f(wmin.getX(), 0, wmin.getX());
+		gl.glColor4f(1.0f, 0.8f, 0.0f, 0.0f);
+		gl.glVertex3f(wmin.getX(), 0, wmin.getX() - 2);
+		gl.glVertex3f(wmin.getX() - 2, 0, wmin.getX());
+
+		gl.glColor4f(1.0f, 0.8f, 0.0f, 0.5f);
+		gl.glVertex3f(wmax.getX(), 0, wmin.getX());
+		gl.glColor4f(1.0f, 0.8f, 0.0f, 0.0f);
+		gl.glVertex3f(wmax.getX(), 0, wmin.getX() - 2);
+		gl.glVertex3f(wmax.getX() + 2, 0, wmin.getX());
+
+		gl.glColor4f(1.0f, 0.8f, 0.0f, 0.5f);
+		gl.glVertex3f(wmax.getX(), 0, wmax.getX());
+		gl.glColor4f(1.0f, 0.8f, 0.0f, 0.0f);
+		gl.glVertex3f(wmax.getX(), 0, wmax.getX() + 2);
+		gl.glVertex3f(wmax.getX() + 2, 0, wmax.getX());
+
+		gl.glColor4f(1.0f, 0.8f, 0.0f, 0.5f);
+		gl.glVertex3f(wmin.getX(), 0, wmax.getX());
+		gl.glColor4f(1.0f, 0.8f, 0.0f, 0.0f);
+		gl.glVertex3f(wmin.getX(), 0, wmax.getX() + 2);
+		gl.glVertex3f(wmin.getX() - 2, 0, wmax.getX());
+
+		gl.glEnd();
+		gl.glDisable(GL.GL_BLEND);
+	}
+
+	/**
+	 * Rendering of the water surface.
+	 * 
+	 * @param gl
+	 * @param wmin
+	 * @param wmax
+	 */
+	private void drawWater(GL gl, Vec3 wmin, Vec3 wmax) {
+		float wlev = world.getWaterLevel();
+		gl.glEnable(GL.GL_BLEND);
+		gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
+		gl.glBegin(GL.GL_QUADS);
+		gl.glColor4f(0.1f, 0.5f, 0.9f, 0.3f);
+		gl.glVertex3f(wmin.getX() - 5, wlev - 0.02f, wmin.getZ() - 5);
+		gl.glColor4f(0.1f, 0.8f, 0.9f, 0.3f);
+		gl.glVertex3f(wmin.getX() - 5, wlev - 0.02f, wmax.getZ() + 5);
+		gl.glColor4f(0.1f, 0.5f, 0.9f, 0.3f);
+		gl.glVertex3f(wmax.getX() + 5, wlev - 0.02f, wmax.getZ() + 5);
+		gl.glColor4f(0.1f, 0.8f, 0.9f, 0.3f);
+		gl.glVertex3f(wmax.getX() + 5, wlev - 0.02f, wmin.getZ() - 5);
+
+		gl.glColor4f(0.0f, 0.0f, 0.0f, 0.2f);
+		gl.glVertex3f(wmin.getX() - 0.5f, wlev - 0.01f, wmin.getZ() - 0.5f);
+		gl.glVertex3f(wmin.getX() - 0.5f, wlev - 0.01f, wmax.getZ() + 0.5f);
+		gl.glVertex3f(wmax.getX() + 0.5f, wlev - 0.01f, wmax.getZ() + 0.5f);
+		gl.glVertex3f(wmax.getX() + 0.5f, wlev - 0.01f, wmin.getZ() - 0.5f);
+		gl.glEnd();
+		gl.glDisable(GL.GL_BLEND);
+	}
+
+	/**
+	 * Rendering of a single creature.
+	 * 
+	 * @param gl
+	 */
+	private void drawCreature(GL gl) {
+		BoundingBox bbox = creatures[0]
+				.getBoundingBox(Creature.ModelType.Parts);
+
+		Vec3 bboxSize = bbox.getSize();
+		Vec3 bboxCenter = Vec3
+				.mul(0.5f, Vec3.add(bbox.getMax(), bbox.getMin()));
+
+		if (translation < 0) {
+			translation = (float) Math.sqrt(bboxSize.getX() * bboxSize.getX()
+					/ 4 + bboxSize.getZ() * bboxSize.getZ() / 4);
+		}
+
+		gl.glTranslatef(0, 0, -1.0f * translation - 1);
+		// gl.glRotatef(30, 1, 0, 0);
+		gl.glRotatef(rotationX, 1, 0, 0);
+		gl.glRotatef(rotationY, 0, 1, 0);
+		gl.glTranslatef(-bboxCenter.getX(), -bboxCenter.getY(), -bboxCenter
+				.getZ());
+		if (skyBox != null)
+			skyBox.Draw(gl, 1000.0f, new Vec3(rotationX, rotationY, 0));
+
+		if (style != null) {
+			if (!style.isInitialized()) {
+				style.Init();
+				if (!style.isInitialized()) {
+					setStyle(null);
+					return;
+				}
+			}
+
+			// effect.render(gl, creatures, Creature.ModelType.Parts);
+			style.render(gl, creatures, Creature.ModelType.Parts);
+		}
+	}
+
+	public void displayChanged(GLAutoDrawable drawable, boolean modeChanged,
+			boolean deviceChanged) {
+	}
+
+	public void init(GLAutoDrawable drawable) {
+		Log.getInstance().log("dbg", "Renderer.init");
+		Log.getInstance().log("dbg", "Renderer.init creating animator");
+		animator = new FPSAnimator(30);
+		animator.add(drawable);
+		animator.start();
+		animator.setRunAsFastAsPossible(false);
+		rotationX = 30.0f;
+		rotationY = 0.0f;
+		Log.getInstance().log("dbg", "Renderer.init initializing opengl");
+		GL gl = drawable.getGL();
+		gl.glLoadIdentity();
+		gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+		gl.glColor3f(1.0f, 1.0f, 1.0f);
+		gl.glClearDepth(1.0);
+		gl.glEnable(GL.GL_DEPTH_TEST);
+		gl.glDepthFunc(GL.GL_LEQUAL);
+		skyBox = new SkyBox();
+		skyBox.Init(gl);
+		// style.Init();
+		Log.getInstance().log("dbg", "Renderer.init done");
+	}
+
+	/**
+	 * Adjust the view to the application's window size.
+	 */
+	public void reshape(GLAutoDrawable drawable, int x, int y, int width,
+			int height) {
+		GL gl = drawable.getGL();
+		GLU glu = new GLU();
+		gl.glMatrixMode(GL.GL_PROJECTION);
+		gl.glLoadIdentity();
+		glu.gluPerspective(90, (float) width / (float) height, 0.015, 1000);
+		gl.glMatrixMode(GL.GL_MODELVIEW);
+	}
+}
Index: java/client_3D/src/com/framsticks/net/client3D/Viewer.java
===================================================================
--- java/client_3D/src/com/framsticks/net/client3D/Viewer.java	(revision 65)
+++ java/client_3D/src/com/framsticks/net/client3D/Viewer.java	(revision 65)
@@ -0,0 +1,285 @@
+package com.framsticks.net.client3D;
+
+import java.awt.BorderLayout;
+import java.awt.Container;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.awt.event.MouseWheelEvent;
+import java.awt.event.MouseWheelListener;
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Set;
+
+import javax.media.opengl.GLCanvas;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+
+/**
+ * A window rendering the whole scene using OpenGL.
+ * 
+ * @author vorg
+ */
+public class Viewer extends JPanel implements MouseListener,
+		MouseMotionListener {
+	static final long serialVersionUID = 1;
+	private Renderer renderer;
+	private HashMap<String, cgfxEffect> styles;
+	private Creature[] creatures;
+	private int clickX;
+	private int clickY;
+	private float clickRotY;
+	private float clickRotX;
+	private App app = null;
+
+	/**
+	 * Constructor.
+	 * 
+	 * @param app
+	 *            An application instance.
+	 */
+	public Viewer(App app) throws IOException {
+		this.app = app;
+		init();
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public Viewer() throws IOException {
+		init();
+	}
+
+	/**
+	 * Creates a rendering window.
+	 * 
+	 * @return A window's handler.
+	 */
+	public JFrame showInFrame() {
+		JFrame frame = new JFrame("Framsticks 3D View");
+		JFrame.setDefaultLookAndFeelDecorated(true);
+		frame.setSize(500, 500);
+		if (app != null) {
+			frame.setLocationRelativeTo(app);
+			frame.setLocation(app.getX() - frame.getSize().width, app.getY());
+		}
+		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+		frame.setVisible(true);
+		Container contentPane = frame.getContentPane();
+		contentPane.setLayout(new BorderLayout());
+		contentPane.add(this, BorderLayout.CENTER);
+		return frame;
+	}
+
+	private void init() throws IOException {
+		styles = new HashMap<String, cgfxEffect>();
+		createCanvas();
+
+		MouseWheelListener listener = new MouseWheelListener() {
+			// float zoomFactor = 0.1f;
+			public void mouseWheelMoved(MouseWheelEvent e) {
+				renderer.setTranslation(renderer.getTranslation()
+						* (float) (e.getWheelRotation() * 0.1 + 1.0));
+			}
+		};
+		this.addMouseWheelListener(listener);
+		// Log.getInstance().addLoggerListener(new ILogListener() {
+		// public void onMesssage(String category, String text) {
+		// System.out.println(category + " > " + text );
+		// }
+		// });
+
+	}
+
+	/**
+	 * Creates a surface object for OpenGL rendering.
+	 */
+	private void createCanvas() throws IOException {
+		GLCanvas canvas = new GLCanvas();
+		renderer = new Renderer();
+
+		canvas.addGLEventListener(renderer);
+		canvas.addMouseListener(this);
+		canvas.addMouseMotionListener(this);
+
+		canvas.setVisible(true);
+
+		this.setOpaque(true);
+
+		this.setLayout(new BorderLayout());
+		this.add(canvas, BorderLayout.CENTER);
+		this.setVisible(true);
+
+		final String shadersPath = "res/shaders";
+		File dir = new File(shadersPath);
+		if (!dir.canRead()) {
+			throw new IOException("Cannot access " + shadersPath);
+		}
+		if (!dir.isDirectory()) {
+			throw new IOException(shadersPath + " is not a directory.");
+		}
+
+		// It is also possible to filter the list of returned files.
+		// This example does not return any files that start with `.'.
+		FilenameFilter filter = new FilenameFilter() {
+			public boolean accept(File dir, String name) {
+				return name.matches(".*[cC][gG][fF][xX]$");
+			}
+		};
+		String[] children = dir.list(filter);
+		dir = new File("../..");
+		for (int i = 0; i < children.length; i++) {
+			String name = children[i].substring(0, children[i].length() - 5);
+			cgfxEffect effect = new cgfxEffect(name);
+
+			styles.put(name, effect);
+			if (app != null) {
+				app.AddStyle(name);
+			}
+			// effect.LoadByName(name, context)
+			// children[i];
+
+			Log.getInstance().log("dbg", name);
+		}
+		setStyle("basic"); //seems even simpler (and thus supported) than "default"
+	}
+
+	/**
+	 * Sets creatures to render.
+	 * 
+	 * @param creatures
+	 *            Creatures to render.
+	 */
+	public void setCreatures(Creature[] creatures) {
+		this.creatures = creatures;
+		renderer.setCreatures(creatures);
+	}
+
+	/**
+	 * Sets a world to render.
+	 * 
+	 * @param world
+	 *            A world to render.
+	 */
+	public void setWorld(World world) {
+		renderer.setWorld(world);
+	}
+
+	/**
+	 * Gets a set of style names.
+	 * 
+	 * @return A set of style names.
+	 */
+	public Set<String> getStyleNames() {
+		return styles.keySet();
+	}
+
+	/**
+	 * Sets a currently activated style. If a style doesn't exist, it prints a
+	 * warning and a list of available styles.
+	 * 
+	 * @param styleName
+	 *            Name of a style to turn on.
+	 */
+	public void setStyle(String styleName) {
+		if (styles.containsKey(styleName)) {
+			renderer.setStyle(styles.get(styleName));
+		} else {
+			Log.getInstance().log("wrn", "Style: " + styleName + " not found");
+			Log.getInstance().log("dbg", "Available styles:");
+			for (String key : styles.keySet()) {
+				Log.getInstance().log("dbg", key);
+			}
+		}
+	}
+
+	/**
+	 * Switches the view to a creature from a specified group, with a specified
+	 * index. Passing value <value>1</value> in both parameters turns on the
+	 * general view of the whole world.
+	 * 
+	 * @param group
+	 *            Number of a group.
+	 * @param index
+	 *            Index of a creature.
+	 */
+	public void setView(int group, int index) {
+		if (creatures == null) {
+			return;
+		}
+		renderer.resetTranslation();
+		if ((group == -1) || (index == -1)) {
+			// if ((creatures != null) && creatures.length > 0)
+			renderer.setCreatures(creatures);
+			renderer.setModelType(Creature.ModelType.MechParts);
+		} else {
+			Creature creature = null;
+			for (Creature c : creatures) {
+				if ((c.getGroup() == group) && (c.getIndex() == index)) {
+					creature = c;
+					break;
+				}
+			}
+			if (creature != null) {
+				renderer.setCreatures(new Creature[] { creature });
+				renderer.setModelType(Creature.ModelType.Parts);
+			} else {
+				renderer.setCreatures(creatures);
+				renderer.setModelType(Creature.ModelType.MechParts);
+			}
+
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
+	 */
+	public void mouseClicked(MouseEvent e) {
+	}
+
+	/* (non-Javadoc)
+	 * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
+	 */
+	public void mouseEntered(MouseEvent e) {
+	}
+
+	/* (non-Javadoc)
+	 * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
+	 */
+	public void mouseExited(MouseEvent e) {
+	}
+
+	/* (non-Javadoc)
+	 * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
+	 */
+	public void mousePressed(MouseEvent e) {
+		clickX = e.getX();
+		clickY = e.getY();
+		clickRotY = renderer.getRotationY();
+		clickRotX = renderer.getRotationX();
+	}
+
+	/* (non-Javadoc)
+	 * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
+	 */
+	public void mouseReleased(MouseEvent e) {
+	}
+
+	/* (non-Javadoc)
+	 * @see java.awt.event.MouseMotionListener#mouseDragged(java.awt.event.MouseEvent)
+	 */
+	public void mouseDragged(MouseEvent e) {
+		float difX = (e.getX() - clickX) / (float) getWidth();
+		float difY = (e.getY() - clickY) / (float) getHeight();
+		renderer.setRotationY(clickRotY + 180 * difX);
+		renderer.setRotationX(clickRotX + 180 * difY);
+	}
+
+	/* (non-Javadoc)
+	 * @see java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent)
+	 */
+	public void mouseMoved(MouseEvent e) {
+	}
+}
Index: java/client_3D/src/com/framsticks/net/client3D/World.java
===================================================================
--- java/client_3D/src/com/framsticks/net/client3D/World.java	(revision 65)
+++ java/client_3D/src/com/framsticks/net/client3D/World.java	(revision 65)
@@ -0,0 +1,33 @@
+package com.framsticks.net.client3D;
+
+import pl.vorg.mowa.core.graphics.Geometry;
+
+/**
+ * Data describing a Framsticks' world.
+ * 
+ * @author vorg
+ */
+public class World  {
+	private Geometry geometry = null;
+	private float waterLevel = -1;
+	private int boundaries = 1;
+
+	public Geometry getGeometry() {
+		return geometry;
+	}
+	public void setGeometry(Geometry geometry) {
+		this.geometry = geometry;
+	}
+	public int getBoundaries() {
+		return boundaries;
+	}
+	public void setBoundaries(int boundaries) {
+		this.boundaries = boundaries;
+	}
+	public float getWaterLevel() {
+		return waterLevel;
+	}
+	public void setWaterLevel(float waterLevel) {
+		this.waterLevel = waterLevel;
+	}	
+}
Index: java/client_3D/src/com/framsticks/net/client3D/cgfxEffect.java
===================================================================
--- java/client_3D/src/com/framsticks/net/client3D/cgfxEffect.java	(revision 65)
+++ java/client_3D/src/com/framsticks/net/client3D/cgfxEffect.java	(revision 65)
@@ -0,0 +1,433 @@
+package com.framsticks.net.client3D;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import javax.media.opengl.GL;
+
+import pl.vorg.mowa.core.graphics.GeometryGroup;
+import pl.vorg.mowa.core.graphics.SkyBox;
+
+import com.framsticks.net.client3D.graphics.loaders.OBJLoader;
+import com.sun.opengl.cg.CGannotation;
+import com.sun.opengl.cg.CGcontext;
+import com.sun.opengl.cg.CGeffect;
+import com.sun.opengl.cg.CGparameter;
+import com.sun.opengl.cg.CGpass;
+import com.sun.opengl.cg.CGtechnique;
+import com.sun.opengl.cg.CgGL;
+import com.sun.opengl.util.texture.Texture;
+import com.sun.opengl.util.texture.TextureIO;
+
+public class cgfxEffect {
+	static CGcontext cgContext;
+
+	public class cgParam {
+		cgParam() {
+			use = false;
+		}
+
+		CGparameter param;
+		CGannotation ann;
+		boolean use;
+	}
+
+	public class cgTexture {
+		Texture glTex;
+		CGparameter param;
+
+		void LoadTex(String fileName) throws Exception {
+			glTex = TextureIO.newTexture(new File(fileName), false);
+			glTex.bind();
+			CgGL.cgGLSetTextureParameter(param, glTex.getTextureObject());
+			CgGL.cgSetSamplerState(param);
+			// glTex.setTexParameteri(GL.GL_TEXTURE_WRAP_S, GL.GL_REPEAT);
+			// glTex.setTexParameteri(GL.GL_TEXTURE_WRAP_T, GL.GL_REPEAT);
+			// glTex.bind();
+			// glTex.enable();
+			// CgGL.cgGLSetupSampler(param, glTex.getTextureObject());
+
+			// CgGL.cgGLSetTextureParameter(param, glTex.getTextureObject());
+		}
+	}
+
+	void setSkyBox(SkyBox box) {
+		this.box = box;
+	}
+
+	SkyBox box;
+	final String DEFAULT_MATERIAL = "res/shaders/default.cgfx";
+	final String SHADERS_PATH = "res/shaders/";
+	final String IMAGES_PATH = "res/img/";
+	final String OBJS_PATH = "res/obj/";
+	final String DEFAULT_TEXTURE = "res/img/stone.jpg";
+	final String DEFAULT_PART_GEO = "res/obj/part.obj";
+	final String DEFAULT_JOINT_GEO = "res/obj/stick5red.obj";
+	cgParam m_cgModelView;
+	cgParam m_cgModelViewProj;
+	cgParam m_cgModelViewIT;
+	ArrayList<cgTexture> texArray;
+	cgParam m_cgDetailScale;
+	cgParam m_cgShiness;
+	cgParam m_cgLights;
+	CGparameter cgSkybox;
+
+	private GeometryGroup partGeo;
+	private GeometryGroup jointGeo;
+
+	CGtechnique m_cgPartTech;
+	CGtechnique m_cgJointTech;
+	CGtechnique m_cgTechnique;
+
+	CGeffect m_cgEffect;
+	boolean m_bIsInited;
+
+	/**
+	 * Informs whether the style is initialized properly.
+	 * 
+	 * @return Boolean value indicating whether the style is initialized
+	 *         properly.
+	 */
+	boolean isInitialized() {
+		return m_bIsInited;
+	}
+
+	String name;
+
+	public boolean Init() {
+		return LoadByName(name);
+	}
+
+	public cgfxEffect(String name) {
+		m_bIsInited = false;
+		this.name = name;
+	}
+
+	/**
+	 * Renders creatures, all in the same style.
+	 * 
+	 * @param gl
+	 *            OpenGL handler.
+	 * @param creatures
+	 *            An array of creatures to render.
+	 * @param modelType
+	 *            Type of model.
+	 */
+	public void render(GL gl, Creature[] creatures, Creature.ModelType modelType) {
+
+		// gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, new float[] { 5, -5, 5},
+		// 0);
+
+		gl.glEnable(GL.GL_LIGHT0);
+		gl.glEnable(GL.GL_LIGHTING);
+
+		// parts
+		if (texArray != null && texArray.size() > 0) {
+			texArray.get(0).glTex.enable();
+			texArray.get(0).glTex.bind();
+		}
+
+		CGpass pass = CgGL.cgGetFirstPass(m_cgPartTech);
+		int passNum = 0;
+		while (pass != null) {
+			gl.glPushAttrib(GL.GL_ALL_ATTRIB_BITS);
+			CgGL.cgSetPassState(pass);
+			int j = 0;
+			for (Creature creature : creatures) {
+				float[][] parts = creature.getParts(modelType);
+				for (int i = 0; i < creature.getParts(modelType).length; i++) {
+					j = i;
+					if (j >= creature.getJoints().length)
+						j--;
+					float[] r = creature.jointRotation(j, modelType);
+					gl.glPushMatrix();
+					gl.glTranslatef(parts[i][0], parts[i][1], parts[i][2]);
+					gl.glRotatef(r[0], r[1], r[2], r[3]);
+					gl.glScalef(0.7f, 0.7f, 0.7f);
+
+					if (m_cgModelView.use)
+						CgGL.cgGLSetStateMatrixParameter(m_cgModelView.param,
+								CgGL.CG_GL_MODELVIEW_MATRIX,
+								CgGL.CG_GL_MATRIX_IDENTITY);
+					if (m_cgModelViewIT.use)
+						CgGL.cgGLSetStateMatrixParameter(m_cgModelViewIT.param,
+								CgGL.CG_GL_MODELVIEW_MATRIX,
+								CgGL.CG_GL_MATRIX_INVERSE_TRANSPOSE);
+					if (m_cgModelViewProj.use)
+						CgGL.cgGLSetStateMatrixParameter(
+								m_cgModelViewProj.param,
+								CgGL.CG_GL_MODELVIEW_PROJECTION_MATRIX,
+								CgGL.CG_GL_MATRIX_IDENTITY);
+
+					partGeo.display(gl);
+					gl.glPopMatrix();
+				}
+			}
+			CgGL.cgResetPassState(pass);
+			gl.glPopAttrib();
+			pass = CgGL.cgGetNextPass(pass);
+
+			passNum++;
+		}
+
+		if (texArray != null && texArray.size() > 0) {
+			texArray.get(0).glTex.disable();
+		}
+		// joints
+
+		if (texArray != null && texArray.size() > 1) {
+			texArray.get(1).glTex.enable();
+			texArray.get(1).glTex.bind();
+		}
+
+		pass = CgGL.cgGetFirstPass(m_cgJointTech);
+		while (pass != null) {
+			CgGL.cgSetPassState(pass);
+			for (Creature creature : creatures) {
+				int[][] joints = creature.getJoints();
+				for (int i = 0; i < joints.length; i++) {
+					gl.glPushMatrix();
+					float[] t = creature.jointTranslation(i, modelType);
+					float[] r = creature.jointRotation(i, modelType);
+					float l = creature.jointLength(i, modelType);
+					gl.glTranslatef(t[0], t[1], t[2]);
+					gl.glRotatef(r[0], r[1], r[2], r[3]);
+					gl.glScalef(1.0f, 1.0f, l);
+
+					if (m_cgModelView.use)
+						CgGL.cgGLSetStateMatrixParameter(m_cgModelView.param,
+								CgGL.CG_GL_MODELVIEW_MATRIX,
+								CgGL.CG_GL_MATRIX_IDENTITY);
+					if (m_cgModelViewIT.use)
+						CgGL.cgGLSetStateMatrixParameter(m_cgModelViewIT.param,
+								CgGL.CG_GL_MODELVIEW_MATRIX,
+								CgGL.CG_GL_MATRIX_INVERSE_TRANSPOSE);
+					if (m_cgModelViewProj.use)
+						CgGL.cgGLSetStateMatrixParameter(
+								m_cgModelViewProj.param,
+								CgGL.CG_GL_MODELVIEW_PROJECTION_MATRIX,
+								CgGL.CG_GL_MATRIX_IDENTITY);
+
+					jointGeo.display(gl);
+					gl.glPopMatrix();
+				}
+			}
+			CgGL.cgResetPassState(pass);
+			pass = CgGL.cgGetNextPass(pass);
+		}
+		if (texArray != null && texArray.size() > 1) {
+			texArray.get(1).glTex.disable();
+		}
+
+		gl.glDisable(GL.GL_LIGHT0);
+		gl.glDisable(GL.GL_LIGHTING);
+	}
+
+	public boolean LoadFromFile(String fileName) {
+		if (cgContext == null) {
+			cgContext = CgGL.cgCreateContext();
+			CgGL.cgGLSetManageTextureParameters(cgContext, true);
+			CgGL.cgGLRegisterStates(cgContext);
+		}
+		// check if material file exist.
+		FileInputStream f;
+		try {
+			f = new FileInputStream(fileName);
+			if (f != null)
+				f.close();
+		} catch (Exception e) {
+			fileName = DEFAULT_MATERIAL;
+		}
+
+		m_cgEffect = CgGL.cgCreateEffectFromFile(cgContext, fileName, null);
+		if (m_cgEffect == null) {
+			Log.getInstance().log(
+					"err",
+					"cgfxEffect: Error while loading effect file! "
+							+ CgGL.cgGetLastListing(cgContext));
+
+			return false;
+		}
+
+		// initing parameters
+		CGparameter p = CgGL.cgGetFirstEffectParameter(m_cgEffect);
+		while (p != null) {
+			if (CgGL.cgGetParameterType(p) == CgGL.CG_SAMPLER2D) {
+				// check what type of texture is it ;]
+				Log.getInstance().log("dbg", CgGL.cgGetParameterSemantic(p));
+				CGannotation ann = CgGL.cgGetFirstParameterAnnotation(p);
+				cgTexture newTex = new cgTexture();
+				newTex.param = p;
+				try {
+					newTex.LoadTex(IMAGES_PATH
+							+ CgGL.cgGetStringAnnotationValue(ann));
+				} catch (Exception e) {
+					Log.getInstance().log(
+							"err",
+							"Texture with file name "
+									+ CgGL.cgGetStringAnnotationValue(ann)
+									+ " not found! " + e.getMessage());
+					try {
+						newTex.LoadTex(DEFAULT_TEXTURE);
+					} catch (Exception ex) {
+						Log.getInstance().log(
+								"err",
+								"Default texture file not found! "
+										+ ex.getMessage());
+					}
+				}
+
+				if (texArray == null)
+					texArray = new ArrayList<cgTexture>();
+				texArray.add(newTex);
+
+			} else if (CgGL.cgGetParameterType(p) == CgGL.CG_SAMPLERCUBE) {
+				CgGL.cgGLSetupSampler(p, box.getCube());
+				cgSkybox = p;
+				Log.getInstance().log("assigning skybox");
+			} else if (CgGL.cgGetParameterType(p) == CgGL.CG_FLOAT4x4) {
+				if (CgGL.cgGetParameterSemantic(p).equals("MODELVIEW")) {
+					m_cgModelView = new cgParam();
+					m_cgModelView.param = p;
+					m_cgModelView.use = true;
+				} else if (CgGL.cgGetParameterSemantic(p).equals(
+						"MODELVIEWPROJECTION")) {
+					m_cgModelViewProj = new cgParam();
+					m_cgModelViewProj.param = p;
+					m_cgModelViewProj.use = true;
+				} else if (CgGL.cgGetParameterSemantic(p).equals("MODELVIEWIT")) {
+					m_cgModelViewIT = new cgParam();
+					m_cgModelViewIT.param = p;
+					m_cgModelViewIT.use = true;
+				}
+
+			} else if (CgGL.cgGetParameterType(p) == CgGL.CG_STRING) {
+				if (CgGL.cgGetParameterName(p).equals("partGeo")) {
+					try {
+						partGeo = OBJLoader.load(OBJS_PATH
+								+ CgGL.cgGetStringParameterValue(p));
+					} catch (IOException ioe) {
+						Log.getInstance().log(
+								"err",
+								"Object with file name "
+										+ CgGL.cgGetStringParameterValue(p)
+										+ " not found!");
+						partGeo = null;
+					}
+				} else if (CgGL.cgGetParameterName(p).equals("jointGeo")) {
+					try {
+						jointGeo = OBJLoader.load(OBJS_PATH
+								+ CgGL.cgGetStringParameterValue(p));
+					} catch (IOException ioe) {
+						Log.getInstance().log(
+								"err",
+								"Object with file name "
+										+ CgGL.cgGetStringParameterValue(p)
+										+ " not found!");
+						jointGeo = null;
+					}
+				}
+
+			} else // (cgGetParameterType(p) ==
+					// cgGetNamedUserType(m_cgEffectVec[lightNum],"LIGHT"))
+			{
+				if (CgGL.cgGetParameterSemantic(p).equals("lights"))
+				// || strcmp(cgGetParameterSemantic(p),"LIGHTS")==0)
+				{
+					m_cgLights = new cgParam();
+					m_cgLights.param = p;
+					m_cgLights.use = true;
+					// CGparameter array;
+					// CGtype t =
+					// cgGetNamedUserType(g_CEffectManager.GetDefaultEffect(),"PointLight");
+					// array =
+					// cgCreateParameterArray(g_CEffectManager.GetCGContext(),
+					// t, 2);
+
+					// cgConnectParameter(CEffectLight::GetLightsParam(lightNum),
+					// m_cgLights[lightNum].param);
+				} else
+					Log.getInstance().log("err",
+							"cgfxEffect: Unknown semantic ");
+			}
+			p = CgGL.cgGetNextParameter(p);
+		}
+
+		m_cgTechnique = CgGL.cgGetFirstTechnique(m_cgEffect);
+		while (m_cgTechnique != null) {
+			if (!CgGL.cgValidateTechnique(m_cgTechnique))
+				Log.getInstance().log(
+						"err",
+						"Technique " + CgGL.cgGetTechniqueName(m_cgTechnique)
+								+ " did not validate.  Skipping.\n");
+			else {
+				CGannotation ann = CgGL
+						.cgGetFirstTechniqueAnnotation(m_cgTechnique);
+				Log.getInstance().log("dbg",
+						CgGL.cgGetStringAnnotationValue(ann));
+				if (CgGL.cgGetStringAnnotationValue(ann).equals("parts")) {
+					// m_cgJointTech = m_cgTechnique;
+					m_cgPartTech = m_cgTechnique;
+				} else if (CgGL.cgGetStringAnnotationValue(ann)
+						.equals("joints")) {
+					m_cgJointTech = m_cgTechnique;
+					// m_cgPartTech = m_cgTechnique;
+				} else if (CgGL.cgGetStringAnnotationValue(ann).equals("both")) {
+					m_cgPartTech = m_cgTechnique;
+					m_cgJointTech = m_cgTechnique;
+
+				}
+				if (m_cgJointTech != null && m_cgPartTech != null)
+					break;
+
+			}
+			m_cgTechnique = CgGL.cgGetNextTechnique(m_cgTechnique);
+		}
+
+		if (m_cgJointTech == null) {
+			Log.getInstance().log("err",
+					"No valid 'joints' technique in effect file!");
+			return false;
+		}
+		if (m_cgPartTech == null) {
+			Log.getInstance().log("err",
+					"No valid 'parts' technique in effect file!");
+			return false;
+		}
+		if (partGeo == null) {
+			try {
+				partGeo = OBJLoader.load(DEFAULT_PART_GEO);
+			} catch (IOException ioe) {
+				Log.getInstance().log(
+						"err",
+						"Object with file name " + DEFAULT_PART_GEO
+								+ " not found!");
+				partGeo = null;
+			}
+		}
+		if (jointGeo == null) {
+			try {
+				jointGeo = OBJLoader.load(DEFAULT_JOINT_GEO);
+			} catch (IOException ioe) {
+				Log.getInstance().log(
+						"err",
+						"Object with file name " + DEFAULT_PART_GEO
+								+ " not found!");
+				jointGeo = null;
+			}
+		}
+		// m_cgTechnique = CgGL.cgGetFirstTechnique(m_cgEffect);
+		m_bIsInited = true;
+		return true;
+	}
+
+	public boolean LoadByName(String name) {
+		String path;
+		path = SHADERS_PATH;
+		path += name;
+		path += ".cgfx";
+		return LoadFromFile(path);
+	}
+}
Index: java/client_3D/src/com/framsticks/net/client3D/graphics/loaders/OBJFace.java
===================================================================
--- java/client_3D/src/com/framsticks/net/client3D/graphics/loaders/OBJFace.java	(revision 65)
+++ java/client_3D/src/com/framsticks/net/client3D/graphics/loaders/OBJFace.java	(revision 65)
@@ -0,0 +1,18 @@
+package com.framsticks.net.client3D.graphics.loaders;
+
+/**
+ * A single polygon being a part of a 3d mesh.
+ * 
+ * @author vorg
+ */
+public class OBJFace {
+	private OBJFaceVertex[] vertices;
+
+	public OBJFaceVertex[] getVertices() {
+		return vertices;
+	}
+	public void setVertices(OBJFaceVertex[] vertices) {
+		this.vertices = vertices;
+	}
+	
+}
Index: java/client_3D/src/com/framsticks/net/client3D/graphics/loaders/OBJFaceVertex.java
===================================================================
--- java/client_3D/src/com/framsticks/net/client3D/graphics/loaders/OBJFaceVertex.java	(revision 65)
+++ java/client_3D/src/com/framsticks/net/client3D/graphics/loaders/OBJFaceVertex.java	(revision 65)
@@ -0,0 +1,47 @@
+package com.framsticks.net.client3D.graphics.loaders;
+
+/**
+ * A vertice and its attributes. The real data about the attributes are stored
+ * in the ObjMesh class - only indexes to the array are stored here.
+ * 
+ * @author vorg
+ * 
+ */
+public class OBJFaceVertex {
+	private int normalIndex;
+	private int texCoordIndex;
+	private int vertexIndex;
+
+	public int getNormalIndex() {
+		return normalIndex;
+	}
+
+	public void setNormalIndex(int normalIndex) {
+		this.normalIndex = normalIndex;
+	}
+
+	public int getTexCoordIndex() {
+		return texCoordIndex;
+	}
+
+	public void setTexCoordIndex(int texCoordIndex) {
+		this.texCoordIndex = texCoordIndex;
+	}
+
+	public int getVertexIndex() {
+		return vertexIndex;
+	}
+
+	public void setVertexIndex(int vertexIndex) {
+		this.vertexIndex = vertexIndex;
+	}
+
+	@Override
+	public boolean equals(java.lang.Object obj) {
+		OBJFaceVertex v = (OBJFaceVertex) obj;
+		return (vertexIndex == v.getVertexIndex())
+				&& (normalIndex == v.getNormalIndex())
+				&& (texCoordIndex == v.getTexCoordIndex());
+	}
+
+}
Index: java/client_3D/src/com/framsticks/net/client3D/graphics/loaders/OBJLoader.java
===================================================================
--- java/client_3D/src/com/framsticks/net/client3D/graphics/loaders/OBJLoader.java	(revision 65)
+++ java/client_3D/src/com/framsticks/net/client3D/graphics/loaders/OBJLoader.java	(revision 65)
@@ -0,0 +1,103 @@
+package com.framsticks.net.client3D.graphics.loaders;
+
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.StringTokenizer;
+
+import pl.vorg.mowa.core.graphics.GeometryGroup;
+import pl.vorg.mowa.core.graphics.Vec2;
+import pl.vorg.mowa.core.graphics.Vec3;
+
+import com.framsticks.net.client3D.Log;
+
+/**
+ * The OBJ files loader.
+ * 
+ * For now, it supports only objects containing only triangles with defined
+ * vertex positions, texture coordinates and normal vectors.
+ * 
+ */
+public class OBJLoader {
+	public static GeometryGroup load(String filePath) throws IOException {
+		Log.getInstance().log("dbg", "OBJLoader.load \"" + filePath + "\"");
+		FileReader reader = null;
+		try {
+			reader = new FileReader(filePath);
+		} catch (FileNotFoundException e) {
+		}
+		if (reader == null) {
+			Log.getInstance().log("err", "OBJLoader.load error");
+			return new GeometryGroup();
+		}
+		return load(new BufferedReader(reader));
+	}
+
+	public static GeometryGroup load(BufferedReader reader) throws IOException {
+		OBJMesh objMesh = parse(reader);
+		OBJMeshConverter converter = new OBJMeshConverter();
+		return converter.convert(objMesh);
+	}
+
+	private static OBJMesh parse(BufferedReader reader) throws IOException {
+		Log.getInstance().log("dbg", "OBJLoader.parse");
+		OBJMesh mesh = new OBJMesh();
+
+		ArrayList<Vec3> vertexList = mesh.getVertices();
+		ArrayList<Vec3> normalList = mesh.getNormals();
+		ArrayList<Vec2> texCoordList = mesh.getTexCoords();
+		ArrayList<OBJFace> faceList = mesh.getFaces();
+
+		String line;
+		while ((line = reader.readLine()) != null) {
+			StringTokenizer tokens = new StringTokenizer(line, " ");
+			if (!tokens.hasMoreTokens()) {
+				continue;
+			}
+			String section = tokens.nextToken();
+			if (section.equals("o")) {
+				if (tokens.hasMoreTokens()) {
+					mesh.setName(tokens.nextToken());
+				}
+			} else if (section.equals("v")) {
+				vertexList.add(new Vec3(Float.parseFloat(tokens.nextToken()),
+						Float.parseFloat(tokens.nextToken()), Float
+								.parseFloat(tokens.nextToken())));
+			} else if (section.equals("vt")) {
+				texCoordList.add(new Vec2(Float.parseFloat(tokens.nextToken()),
+						Float.parseFloat(tokens.nextToken())));
+			} else if (section.equals("vn")) {
+				normalList.add(new Vec3(Float.parseFloat(tokens.nextToken()),
+						Float.parseFloat(tokens.nextToken()), Float
+								.parseFloat(tokens.nextToken())));
+			} else if (section.equals("f")) {
+				int numTokens = tokens.countTokens();
+				OBJFaceVertex[] faceVertices = new OBJFaceVertex[numTokens];
+				String faceStr;
+				String[] indices;
+				for (int i = 0; i < numTokens; i++) {
+					faceStr = tokens.nextToken();
+					indices = faceStr.split("/");
+					faceVertices[i] = new OBJFaceVertex();
+					if (!indices[0].equals(""))
+						faceVertices[i].setVertexIndex(Integer
+								.parseInt(indices[0]) - 1);
+					if (!indices[1].equals(""))
+						faceVertices[i].setTexCoordIndex(Integer
+								.parseInt(indices[1]) - 1);
+					if (!indices[2].equals(""))
+						faceVertices[i].setNormalIndex(Integer
+								.parseInt(indices[2]) - 1);
+				}
+				OBJFace face = new OBJFace();
+				face.setVertices(faceVertices);
+				faceList.add(face);
+			}
+		}
+
+		Log.getInstance().log("dbg", "OBJLoader.load done");
+		return mesh;
+	}
+}
Index: java/client_3D/src/com/framsticks/net/client3D/graphics/loaders/OBJMesh.java
===================================================================
--- java/client_3D/src/com/framsticks/net/client3D/graphics/loaders/OBJMesh.java	(revision 65)
+++ java/client_3D/src/com/framsticks/net/client3D/graphics/loaders/OBJMesh.java	(revision 65)
@@ -0,0 +1,51 @@
+package com.framsticks.net.client3D.graphics.loaders;
+
+import java.util.ArrayList;
+
+import pl.vorg.mowa.core.graphics.Vec2;
+import pl.vorg.mowa.core.graphics.Vec3;
+
+/**
+ * 3d mesh.
+ * 
+ * @author vorg
+ */
+public class OBJMesh {
+	private String name;	
+	private ArrayList<OBJFace> faces = new ArrayList<OBJFace>();
+	private ArrayList<Vec3> normals = new ArrayList<Vec3>();
+	private ArrayList<Vec2> texCoords = new ArrayList<Vec2>();
+	private ArrayList<Vec3> vertices = new ArrayList<Vec3>();
+	
+	public ArrayList<OBJFace> getFaces() {
+		return faces;
+	}
+	public void setFaces(ArrayList<OBJFace> faces) {
+		this.faces = faces;
+	}
+	public String getName() {
+		return name;
+	}
+	public void setName(String name) {
+		this.name = name;
+	}
+	public ArrayList<Vec3> getNormals() {
+		return normals;
+	}
+	public void setNormals(ArrayList<Vec3> normals) {
+		this.normals = normals;
+	}
+	public ArrayList<Vec2> getTexCoords() {
+		return texCoords;
+	}
+	public void setTexCoords(ArrayList<Vec2> texCoords) {
+		this.texCoords = texCoords;
+	}
+	public ArrayList<Vec3> getVertices() {
+		return vertices;
+	}
+	public void setVertices(ArrayList<Vec3> vertices) {
+		this.vertices = vertices;
+	}
+	
+}
Index: java/client_3D/src/com/framsticks/net/client3D/graphics/loaders/OBJMeshConverter.java
===================================================================
--- java/client_3D/src/com/framsticks/net/client3D/graphics/loaders/OBJMeshConverter.java	(revision 65)
+++ java/client_3D/src/com/framsticks/net/client3D/graphics/loaders/OBJMeshConverter.java	(revision 65)
@@ -0,0 +1,144 @@
+package com.framsticks.net.client3D.graphics.loaders;
+
+import java.util.ArrayList;
+
+import pl.vorg.mowa.core.graphics.FloatVertexAttrib;
+import pl.vorg.mowa.core.graphics.Geometry;
+import pl.vorg.mowa.core.graphics.GeometryGroup;
+import pl.vorg.mowa.core.graphics.IndexBuffer;
+import pl.vorg.mowa.core.graphics.PrimitiveType;
+import pl.vorg.mowa.core.graphics.Vec2;
+import pl.vorg.mowa.core.graphics.Vec3;
+import pl.vorg.mowa.core.graphics.VertexStream;
+
+import com.framsticks.net.client3D.Log;
+
+/**
+ * A class converting OBJMesh to GeometryGroup, vertex arrays to VertexStreams,
+ * etc.
+ * 
+ * For now, it supports only uniform meshes (i.e., it contains only triangles).
+ * 
+ * @author vorg
+ */
+public class OBJMeshConverter {
+	public GeometryGroup convert(OBJMesh mesh) {
+		Log.getInstance().log("dbg", "OBJMeshConverter.convert");
+		GeometryGroup group = new GeometryGroup();
+		ArrayList<OBJFace> faces = mesh.getFaces();
+
+		int faceVertexCount = 0;
+		for (OBJFace face : mesh.getFaces()) {
+			if (faceVertexCount == 0) {
+				faceVertexCount = face.getVertices().length;
+			} else if (faceVertexCount != face.getVertices().length) {
+				// Two different face types in the same mesh. The mesh
+				// would have to be divided into smaller ones.
+				Log.getInstance().log(
+						"err",
+						"OBJMeshConverter.convert unsupported mesh type"
+								+ "(faceVertexCount:"
+								+ face.getVertices().length + "!="
+								+ faceVertexCount + ")");
+				return group;
+			}
+		}
+
+		// Build indexes.
+		ArrayList<Integer> indexList = new ArrayList<Integer>();
+		ArrayList<OBJFaceVertex> faceVertexList = new ArrayList<OBJFaceVertex>();
+
+		Geometry geometry = new Geometry();
+
+		if (faceVertexCount == 3) {
+			geometry.setPrimitiveType(PrimitiveType.Triangles);
+		} else if (faceVertexCount == 4) {
+			geometry.setPrimitiveType(PrimitiveType.Quads);
+		} else {
+			Log.getInstance()
+					.log("err",
+							"OBJMeshConverter.convert only triangles and quads are supported (faceVertexCount:"
+									+ faceVertexCount + ")");
+			return group;
+		}
+
+		for (int i = 0; i < faces.size(); i++) {
+			OBJFaceVertex[] faceVertices = faces.get(i).getVertices();
+			for (int j = 0; j < faceVertexCount; j++) {
+				OBJFaceVertex v = faceVertices[j];
+				int index = faceVertexList.indexOf(v);
+				if (index == -1) {
+					faceVertexList.add(v);
+					index = faceVertexList.size() - 1;
+				}
+				indexList.add(index);
+			}
+		}
+
+		VertexStream vertexStream = buildBuffers(mesh, indexList,
+				faceVertexList);
+		geometry.setVertexStream(vertexStream);
+
+		group.getChildren().add(geometry);
+		group.updateBoundingBox();
+
+		return group;
+	}
+
+	private VertexStream buildBuffers(OBJMesh mesh,
+			ArrayList<Integer> indexList,
+			ArrayList<OBJFaceVertex> faceVertexList) {
+		VertexStream vertexStream = new VertexStream();
+
+		IndexBuffer indexBuffer = new IndexBuffer();
+		int[] idxBuf = new int[indexList.size()];
+		for (int i = 0; i < indexList.size(); i++) {
+			idxBuf[i] = indexList.get(i);
+		}
+		indexBuffer.setBuffer(idxBuf);
+		vertexStream.setIndexBuffer(indexBuffer);
+
+		ArrayList<Vec3> vertices = mesh.getVertices();
+		FloatVertexAttrib posAttrib = new FloatVertexAttrib("pos");
+		float[] posBuf = new float[3 * faceVertexList.size()];
+		for (int i = 0; i < faceVertexList.size(); i++) {
+			Vec3 v = vertices.get(faceVertexList.get(i).getVertexIndex());
+			posBuf[i * 3] = v.getX();
+			posBuf[i * 3 + 1] = v.getY();
+			posBuf[i * 3 + 2] = v.getZ();
+		}
+		posAttrib.setBuffer(posBuf);
+		vertexStream.addAttrib(posAttrib);
+
+		ArrayList<Vec3> normals = mesh.getNormals();
+		if (normals.size() > 0) {
+			FloatVertexAttrib normalAttrib = new FloatVertexAttrib("normal");
+			float[] normalBuf = new float[3 * faceVertexList.size()];
+			for (int i = 0; i < faceVertexList.size(); i++) {
+				Vec3 v = normals.get(faceVertexList.get(i).getNormalIndex());
+				normalBuf[i * 3] = v.getX();
+				normalBuf[i * 3 + 1] = v.getY();
+				normalBuf[i * 3 + 2] = v.getZ();
+			}
+			normalAttrib.setBuffer(normalBuf);
+			vertexStream.addAttrib(normalAttrib);
+		}
+
+		ArrayList<Vec2> texCoords = mesh.getTexCoords();
+		if (texCoords.size() > 0) {
+			FloatVertexAttrib texCoord0Attrib = new FloatVertexAttrib(
+					"texCoord0");
+			float[] texCoord0Buf = new float[2 * texCoords.size()];
+			for (int i = 0; i < texCoords.size(); i++) {
+				Vec2 tc = texCoords.get(faceVertexList.get(i)
+						.getTexCoordIndex());
+				texCoord0Buf[i * 2] = tc.getX();
+				texCoord0Buf[i * 2 + 1] = tc.getY();
+			}
+			texCoord0Attrib.setBuffer(texCoord0Buf);
+			vertexStream.addAttrib(texCoord0Attrib);
+		}
+
+		return vertexStream;
+	}
+}
Index: java/client_3D/src/pl/vorg/mowa/core/graphics/BoundingBox.java
===================================================================
--- java/client_3D/src/pl/vorg/mowa/core/graphics/BoundingBox.java	(revision 65)
+++ java/client_3D/src/pl/vorg/mowa/core/graphics/BoundingBox.java	(revision 65)
@@ -0,0 +1,68 @@
+package pl.vorg.mowa.core.graphics;
+
+
+/**
+ * A cube containing an object in 3d space.
+ * 
+ * @author vorg
+ */
+public class BoundingBox {
+	private Vec3 min;
+	private Vec3 max;
+	
+	public BoundingBox() {
+		empty();
+	}
+	
+	public Vec3 getMax() {
+		return max;
+	}
+	
+	public void setMax(Vec3 max) {
+		this.max = max;
+	}
+	
+	public Vec3 getMin() {
+		return min;
+	}
+	
+	public void setMin(Vec3 min) {
+		this.min = min;
+	}
+	
+	public Vec3 getSize() {
+		return Vec3.sub(max, min);
+	}
+	
+	public void empty() {
+		min = new Vec3(Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE);
+		max = new Vec3(Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE);
+	}
+	
+	public boolean isEmpty() {
+		return (min.getX() > max.getX()) || (min.getY() > max.getY()) || (min.getZ() > max.getZ());
+	}
+	
+	public void add(float x, float y, float z) {
+		if (x < min.getX()) min.setX(x);
+		if (y < min.getY()) min.setY(y);
+		if (z < min.getZ()) min.setZ(z);
+		if (x > max.getX()) max.setX(x);
+		if (y > max.getY()) max.setY(y);
+		if (z > max.getZ()) max.setZ(z);
+	}
+	
+	public void add(Vec3 v) {
+		add(v.getX(), v.getY(), v.getZ());
+	}
+	
+	public void add(BoundingBox box) {
+		add(box.getMin());
+		add(box.getMax());		
+	}
+	
+	@Override
+	public String toString() {
+		return "BoundingBox : min("+min.getX()+","+min.getY()+","+min.getZ()+") max("+max.getX()+","+max.getY()+","+max.getZ()+")";
+	}	
+}
Index: java/client_3D/src/pl/vorg/mowa/core/graphics/FloatVertexAttrib.java
===================================================================
--- java/client_3D/src/pl/vorg/mowa/core/graphics/FloatVertexAttrib.java	(revision 65)
+++ java/client_3D/src/pl/vorg/mowa/core/graphics/FloatVertexAttrib.java	(revision 65)
@@ -0,0 +1,23 @@
+package pl.vorg.mowa.core.graphics;
+
+/**
+ * A buffer of a vertice's attributes (e.g., position, color) represented with
+ * floats.
+ * 
+ * @author vorg
+ */
+public class FloatVertexAttrib extends VertexAttrib {
+	private float[] buffer;
+
+	public FloatVertexAttrib(String name) {
+		super(name);
+	}
+
+	public float[] getBuffer() {
+		return buffer;
+	}
+
+	public void setBuffer(float[] buffer) {
+		this.buffer = buffer;
+	}
+}
Index: java/client_3D/src/pl/vorg/mowa/core/graphics/Geometry.java
===================================================================
--- java/client_3D/src/pl/vorg/mowa/core/graphics/Geometry.java	(revision 65)
+++ java/client_3D/src/pl/vorg/mowa/core/graphics/Geometry.java	(revision 65)
@@ -0,0 +1,206 @@
+package pl.vorg.mowa.core.graphics;
+
+import javax.media.opengl.GL;
+
+/**
+ * An 3d object. Contains information about its vertex points, whether its faces
+ * are triangles or quads, etc. Enables displaying the object.
+ * 
+ * @author vorg
+ */
+public class Geometry {
+	private VertexStream vertexStream;
+	private String name;
+	private PrimitiveType primitiveType = PrimitiveType.None;
+	private BoundingBox boundingBox = new BoundingBox();
+	private static final int TANGENT_ATTRIB_LOCATION = 0;
+
+	// vbo
+	int[] ids = new int[3];
+	boolean vboInit = false;
+
+	public VertexStream getVertexStream() {
+		return vertexStream;
+	}
+
+	public void setVertexStream(VertexStream vertexStream) {
+		this.vertexStream = vertexStream;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public PrimitiveType getPrimitiveType() {
+		return primitiveType;
+	}
+
+	public void setPrimitiveType(PrimitiveType type) {
+		this.primitiveType = type;
+	}
+
+	public void display(GL gl) {
+		displayImmediately(gl);
+	}
+
+	private void displayImmediately(GL gl) {
+		if (vertexStream == null) {
+			return;
+		}
+		int[] indices = vertexStream.getIndexBuffer().getBuffer();
+		FloatVertexAttrib posAttrib = (FloatVertexAttrib) vertexStream
+				.getAttribByName("pos");
+		float[] positions = (posAttrib != null) ? posAttrib.getBuffer() : null;
+		FloatVertexAttrib normalAttrib = (FloatVertexAttrib) vertexStream
+				.getAttribByName("normal");
+		float[] normals = (normalAttrib != null) ? normalAttrib.getBuffer()
+				: null;
+		FloatVertexAttrib texCoord0Attrib = (FloatVertexAttrib) vertexStream
+				.getAttribByName("texCoord0");
+		float[] texCoords0 = (texCoord0Attrib != null) ? texCoord0Attrib
+				.getBuffer() : null;
+		FloatVertexAttrib tangentAttrib = (FloatVertexAttrib) vertexStream
+				.getAttribByName("tangent");
+		float[] tangents = (tangentAttrib != null) ? tangentAttrib.getBuffer()
+				: null;
+
+		gl.glBegin(primitiveType.toGL());
+		for (int i = 0; i < indices.length; i++) {
+			if (normals != null) {
+				gl.glNormal3fv(normals, indices[i] * 3);
+			}
+			if (texCoords0 != null) {
+				gl.glTexCoord2fv(texCoords0, indices[i] * 2);
+			}
+			if (tangents != null) {
+				gl.glVertexAttrib3fv(TANGENT_ATTRIB_LOCATION, tangents,
+						indices[i] * 3);
+			}
+			gl.glVertex3fv(positions, indices[i] * 3);
+		}
+		gl.glEnd();
+
+		/*
+		 * //to powinno byc zrobione na VBO albo chociaz VertexArrays ale na
+		 * razie nie sa one supportowane w JOGL dla OSX if (!vboInit) { vboInit
+		 * = true; gl.glGenBuffers( 3, ids, 0); // get names
+		 * 
+		 * FloatBuffer posFB = BufferUtil.newFloatBuffer(positions.length);
+		 * FloatBuffer normalFB = BufferUtil.newFloatBuffer(normals.length);
+		 * FloatBuffer texCoord0FB =
+		 * BufferUtil.newFloatBuffer(texCoords0.length);
+		 * 
+		 * posFB.put(positions); normalFB.put(normals);
+		 * texCoord0FB.put(texCoords0);
+		 * 
+		 * gl.glBindBuffer( GL.GL_ARRAY_BUFFER, ids[0] ); gl.glBufferData(
+		 * GL.GL_ARRAY_BUFFER, positions.length*BufferUtil.SIZEOF_FLOAT, posFB,
+		 * GL.GL_STATIC_DRAW ); gl.glVertexPointer( 3, GL.GL_FLOAT, 0, (Buffer)
+		 * null );
+		 * 
+		 * //gl.glBindBuffer( GL.GL_ARRAY_BUFFER, ids[1] ); //gl.glBufferData(
+		 * GL.GL_ARRAY_BUFFER, normals.length*BufferUtil.SIZEOF_FLOAT, normalFB,
+		 * GL.GL_STATIC_DRAW ); //gl.glNormalPointer( 3, 0, (Buffer) null );
+		 * 
+		 * //gl.glBindBuffer( GL.GL_ARRAY_BUFFER, ids[2] ); //gl.glBufferData(
+		 * GL.GL_ARRAY_BUFFER, texCoords0.length*BufferUtil.SIZEOF_FLOAT,
+		 * texCoord0FB, GL.GL_STATIC_DRAW ); //gl.glTexCoordPointer( 3,
+		 * GL.GL_FLOAT, 0, (Buffer) null );
+		 * 
+		 * } else { gl.glEnableClientState (GL.GL_VERTEX_ARRAY);
+		 * gl.glEnableClientState (GL.GL_NORMAL_ARRAY); gl.glEnableClientState
+		 * (GL.GL_TEXTURE_COORD_ARRAY);
+		 * 
+		 * //gl.glBindBufferARB( GL.GL_ARRAY_BUFFER_ARB, ids[0] );
+		 * //gl.glBindBufferARB( GL.GL_ARRAY_BUFFER_ARB, ids[1] );
+		 * //gl.glBindBufferARB( GL.GL_ARRAY_BUFFER_ARB, ids[2] );
+		 * 
+		 * gl.glDrawArrays(GL.GL_TRIANGLES, 0, positions.length); // draw
+		 * everything (six vertices)
+		 * 
+		 * gl.glDisableClientState (GL.GL_VERTEX_ARRAY); gl.glDisableClientState
+		 * (GL.GL_NORMAL_ARRAY); gl.glDisableClientState
+		 * (GL.GL_TEXTURE_COORD_ARRAY); }
+		 */
+
+		// if (positions != null) {
+		// gl.glEnableClientState(GL.GL_VERTEX_ARRAY);
+		// FloatBuffer buf =
+		// BufferUtil.newFloatBuffer(positions.length);//FloatBuffer.allocate(positions.length);
+		// buf.put(positions);
+		// gl.glVertexPointer(3, GL.GL_FLOAT, 0, buf);
+		// }
+
+		// if (normals != null) {
+		// gl.glEnableClientState(GL.GL_NORMAL_ARRAY);
+		// FloatBuffer buf = FloatBuffer.wrap(normals);
+		// gl.glNormalPointer(GL.GL_FLOAT, 3, buf);
+		// }
+
+		// if (texCoords0 != null) {
+		// gl.glClientActiveTexture(GL.GL_TEXTURE0);
+		// gl.glEnableClientState(GL.GL_TEXTURE_COORD_ARRAY);
+		// FloatBuffer buf = FloatBuffer.wrap(texCoords0);
+		// gl.glTexCoordPointer(2, GL.GL_FLOAT, 2, buf);
+		// }
+
+		// gl.glDrawElements(primitiveType.toGL(),
+		// vertexStream.getIndexBuffer().getBuffer().length, GL.GL_INT,
+		// stream.getIndices());
+
+		// attrib = stream.getAttrib("TANGENT", TYPE_VEC3);
+		// if (attrib) {
+		// glEnableVertexAttribArrayARB(3);
+		// glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, attrib->getStride(),
+		// attrib->getBuffer());
+		// }
+
+		// //normals
+		// gl.glDisable(GL.GL_LIGHTING);
+		// gl.glDisable(GL.GL_TEXTURE_2D);
+		// gl.glColor3ub((byte)255, (byte)0, (byte)0);
+		// gl.glBegin(GL.GL_LINES);
+		// for(int i=0; i<indices.length; i++) {
+		// gl.glVertex3fv(positions, indices[i]*3);
+		// if (normals != null) {
+		// gl.glVertex3f(
+		// positions[indices[i]*3 ] + normals[indices[i]*3 ]*0.05f,
+		// positions[indices[i]*3+1] + normals[indices[i]*3+1]*0.05f,
+		// positions[indices[i]*3+2] + normals[indices[i]*3+2]*0.05f
+		// );
+		// }
+		// }
+		// gl.glEnd();
+		// gl.glEnable(GL.GL_LIGHTING);
+	}
+
+	public BoundingBox getBoundingBox() {
+		return boundingBox;
+	}
+
+	public void updateBoundingBox() {
+		boundingBox.empty();
+		if (vertexStream == null) {
+			return;
+		}
+
+		FloatVertexAttrib posAttrib = (FloatVertexAttrib) vertexStream
+				.getAttribByName("pos");
+		if (posAttrib == null) {
+			return;
+		}
+
+		float[] posBuf = posAttrib.getBuffer();
+		if (posBuf == null) {
+			return;
+		}
+
+		for (int i = 0; i < posBuf.length; i += 3) {
+			boundingBox.add(posBuf[i], posBuf[i + 1], posBuf[i + 2]);
+		}
+	}
+}
Index: java/client_3D/src/pl/vorg/mowa/core/graphics/GeometryGroup.java
===================================================================
--- java/client_3D/src/pl/vorg/mowa/core/graphics/GeometryGroup.java	(revision 65)
+++ java/client_3D/src/pl/vorg/mowa/core/graphics/GeometryGroup.java	(revision 65)
@@ -0,0 +1,44 @@
+package pl.vorg.mowa.core.graphics;
+
+import java.util.ArrayList;
+
+import javax.media.opengl.GL;
+
+/**
+ * A 3d model, containing many 3d shapes.
+ * 
+ * @author vorg
+ */
+public class GeometryGroup {
+	private ArrayList<Geometry> children = new ArrayList<Geometry>();
+	private BoundingBox boundingBox = new BoundingBox();
+
+	public ArrayList<Geometry> getChildren() {
+		return children;
+	}
+
+	public void setChildren(ArrayList<Geometry> value) {
+		this.children = value;
+	}
+	
+	public BoundingBox getBoundingBox() {
+		return boundingBox;
+	}
+	
+	public void display(GL gl) {
+		for(Geometry geometry : children) {
+			geometry.display(gl);
+		}
+	}
+	
+	public void updateBoundingBox() {
+		boundingBox.empty();
+		for(Geometry geometry : children) {
+			if (geometry.getBoundingBox().isEmpty()) {
+				geometry.updateBoundingBox();
+			}
+			boundingBox.add(geometry.getBoundingBox());
+		}
+	}
+	
+}
Index: java/client_3D/src/pl/vorg/mowa/core/graphics/GeometryUtils.java
===================================================================
--- java/client_3D/src/pl/vorg/mowa/core/graphics/GeometryUtils.java	(revision 65)
+++ java/client_3D/src/pl/vorg/mowa/core/graphics/GeometryUtils.java	(revision 65)
@@ -0,0 +1,231 @@
+package pl.vorg.mowa.core.graphics;
+
+import com.framsticks.net.client3D.Log;
+
+/**
+ * A set of helper metods useful with 3d objects.
+ * 
+ * @author vorg
+ */
+public class GeometryUtils {
+	public static void generateNormals(Geometry geometry) {
+		VertexStream vertexStream = geometry.getVertexStream();
+		if (vertexStream == null) {
+			Log.getInstance()
+					.log("err",
+							"GeometryUtils.generateNormals err: vertex stream not found");
+			return;
+		}
+		if (vertexStream.getAttribByName("normal") == null) {
+			FloatVertexAttrib normalAttrib = new FloatVertexAttrib("normal");
+			vertexStream.addAttrib(normalAttrib);
+			float[] normals = new float[((FloatVertexAttrib) vertexStream
+					.getAttribByName("pos")).getBuffer().length];
+			normalAttrib.setBuffer(normals);
+		}
+		if (geometry.getPrimitiveType() == PrimitiveType.Triangles) {
+			genSmoothTriangleNormals(geometry.getVertexStream());
+		}
+		if (geometry.getPrimitiveType() == PrimitiveType.Quads) {
+			genSmoothQuadNormals(geometry.getVertexStream());
+		}
+	}
+
+	private static void genSmoothTriangleNormals(VertexStream vertexStream) {
+		IndexBuffer indexBuffer = vertexStream.getIndexBuffer();
+		int[] indices = indexBuffer.getBuffer();
+		FloatVertexAttrib posAttrib = (FloatVertexAttrib) vertexStream
+				.getAttribByName("pos");
+		float[] positions = posAttrib.getBuffer();
+		FloatVertexAttrib normalAttrib = (FloatVertexAttrib) vertexStream
+				.getAttribByName("normal");
+		float[] normals = normalAttrib.getBuffer();
+		normalAttrib.setBuffer(normals);
+		Vec3[] faceNormalList = new Vec3[indices.length / 3];
+
+		for (int i = 0; i < indices.length; i += 3) {
+			Vec3 a = new Vec3(positions[indices[i] * 3],
+					positions[indices[i] * 3 + 1],
+					positions[indices[i] * 3 + 2]);
+			Vec3 b = new Vec3(positions[indices[i + 1] * 3],
+					positions[indices[i + 1] * 3 + 1],
+					positions[indices[i + 1] * 3 + 2]);
+			Vec3 c = new Vec3(positions[indices[i + 2] * 3],
+					positions[indices[i + 2] * 3 + 1],
+					positions[indices[i + 2] * 3 + 2]);
+			Vec3 bsa = Vec3.sub(b, a);
+			Vec3 bsc = Vec3.sub(b, c);
+			Vec3 n = Vec3.cross(bsc, bsa);
+			n.normalize();
+			faceNormalList[i / 3] = n;
+		}
+
+		// For each vertice.
+		for (int i = 0; i < positions.length; i += 3) {
+			// Search faces containg the vertice and add a normal vector to it.
+			float x = positions[i];
+			float y = positions[i + 1];
+			float z = positions[i + 2];
+			Vec3 vertNormal = new Vec3();
+			for (int j = 0; j < indices.length; j++) {
+				if ((x == positions[indices[j] * 3])
+						&& (y == positions[indices[j] * 3 + 1])
+						&& (z == positions[indices[j] * 3 + 2])) {
+					vertNormal.add(faceNormalList[j / 3]);
+				}
+			}
+			vertNormal.normalize();
+			normals[i] = vertNormal.getX();
+			normals[i + 1] = vertNormal.getY();
+			normals[i + 2] = vertNormal.getZ();
+		}
+	}
+
+	private static void genSmoothQuadNormals(VertexStream vertexStream) {
+		IndexBuffer indexBuffer = vertexStream.getIndexBuffer();
+		int[] indices = indexBuffer.getBuffer();
+		FloatVertexAttrib posAttrib = (FloatVertexAttrib) vertexStream
+				.getAttribByName("pos");
+		float[] positions = posAttrib.getBuffer();
+		FloatVertexAttrib normalAttrib = (FloatVertexAttrib) vertexStream
+				.getAttribByName("normal");
+		float[] normals = new float[positions.length];
+		normalAttrib.setBuffer(normals);
+		Vec3[] faceNormalList = new Vec3[indices.length / 4];
+
+		for (int i = 0; i < indices.length; i += 4) {
+			Vec3 a = new Vec3(positions[indices[i] * 3],
+					positions[indices[i] * 3 + 1],
+					positions[indices[i] * 3 + 2]);
+			Vec3 b = new Vec3(positions[indices[i + 1] * 3],
+					positions[indices[i + 1] * 3 + 1],
+					positions[indices[i + 1] * 3 + 2]);
+			Vec3 c = new Vec3(positions[indices[i + 2] * 3],
+					positions[indices[i + 2] * 3 + 1],
+					positions[indices[i + 2] * 3 + 2]);
+			Vec3 bsa = Vec3.sub(b, a);
+			Vec3 bsc = Vec3.sub(b, c);
+			Vec3 n = Vec3.cross(bsc, bsa);
+			n.normalize();
+			faceNormalList[i / 4] = n;
+		}
+
+		// For each vertice.
+		for (int i = 0; i < positions.length; i += 3) {
+			// Search faces containg the vertice and add a normal vector to it.
+			float x = positions[i];
+			float y = positions[i + 1];
+			float z = positions[i + 2];
+			Vec3 vertNormal = new Vec3();
+			for (int j = 0; j < indices.length; j++) {
+				if ((x == positions[indices[j] * 3])
+						&& (y == positions[indices[j] * 3 + 1])
+						&& (z == positions[indices[j] * 3 + 2])) {
+					vertNormal.add(faceNormalList[j / 4]);
+				}
+			}
+			vertNormal.normalize();
+			normals[i] = vertNormal.getX();
+			normals[i + 1] = vertNormal.getY();
+			normals[i + 2] = vertNormal.getZ();
+		}
+	}
+
+	// Isn't tested well.
+	public static void generateTangents(Geometry geometry) {
+		VertexStream vertexStream = geometry.getVertexStream();
+		if (vertexStream == null) {
+			Log.getInstance()
+					.log("err",
+							"GeometryUtils.generateTangents err: vertex stream not found");
+			return;
+		}
+		if (vertexStream.getAttribByName("tangent") == null) {
+			FloatVertexAttrib tangentAttrib = new FloatVertexAttrib("tangent");
+			vertexStream.addAttrib(tangentAttrib);
+			float[] tangents = new float[((FloatVertexAttrib) vertexStream
+					.getAttribByName("pos")).getBuffer().length];
+			tangentAttrib.setBuffer(tangents);
+		}
+		if (vertexStream.getAttribByName("normal") == null) {
+			FloatVertexAttrib normalAttrib = new FloatVertexAttrib("normal");
+			vertexStream.addAttrib(normalAttrib);
+			float[] normals = new float[((FloatVertexAttrib) vertexStream
+					.getAttribByName("pos")).getBuffer().length];
+			normalAttrib.setBuffer(normals);
+		}
+		if (geometry.getPrimitiveType() == PrimitiveType.Triangles) {
+			genTriangleTangents(geometry.getVertexStream());
+		}
+	}
+
+	private static void genTriangleTangents(VertexStream vertexStream) {
+		IndexBuffer indexBuffer = vertexStream.getIndexBuffer();
+		int[] indices = indexBuffer.getBuffer();
+		FloatVertexAttrib posAttrib = (FloatVertexAttrib) vertexStream
+				.getAttribByName("pos");
+		float[] positions = posAttrib.getBuffer();
+		FloatVertexAttrib texCoord0Attrib = (FloatVertexAttrib) vertexStream
+				.getAttribByName("texCoord0");
+		float[] texCoords0 = texCoord0Attrib.getBuffer();
+		FloatVertexAttrib tangentAttrib = (FloatVertexAttrib) vertexStream
+				.getAttribByName("tangent");
+		float[] tangents = tangentAttrib.getBuffer();
+
+		for (int i = 0; i < indices.length; i += 3) {
+			int i1 = indices[i];
+			int i2 = indices[i + 1];
+			int i3 = indices[i + 2];
+			Vec3 pos1 = new Vec3(positions[i1] * 3, positions[i1 * 3 + 1],
+					positions[i1 * 3 + 2]);
+			Vec3 pos2 = new Vec3(positions[i2] * 3, positions[i2 * 3 + 1],
+					positions[i2 * 3 + 2]);
+			Vec3 pos3 = new Vec3(positions[i3] * 3, positions[i3 * 3 + 1],
+					positions[i3 * 3 + 2]);
+			Vec2 coord1 = new Vec2(texCoords0[i1 * 2], texCoords0[i1 * 2 + 1]);
+			Vec2 coord2 = new Vec2(texCoords0[i2 * 2], texCoords0[i2 * 2 + 1]);
+			Vec2 coord3 = new Vec2(texCoords0[i3 * 2], texCoords0[i3 * 2 + 1]);
+			Vec3 v0 = Vec3.sub(pos1, pos2);
+			Vec3 v1 = Vec3.sub(pos3, pos1);
+			Vec3 normal = Vec3.normalized(Vec3.cross(v0, v1));
+
+			float dt0 = coord1.getT() - coord2.getT();
+			float dt1 = coord3.getT() - coord1.getT();
+			Vec3 tangent = Vec3.normalized(Vec3.sub(Vec3.mul(dt1, v0),
+					Vec3.mul(dt0, v1)));
+
+			float ds0 = coord1.getS() - coord2.getS();
+			float ds1 = coord3.getS() - coord1.getS();
+			Vec3 binormal = Vec3.normalized(Vec3.sub(Vec3.mul(ds1, v0),
+					Vec3.mul(ds0, v1)));
+
+			Vec3 tangentCross = Vec3.cross(tangent, binormal);
+			if (Vec3.dot(tangentCross, normal) < 0.0f) {
+				tangent = Vec3.neg(tangent);
+				binormal = Vec3.neg(binormal);
+			}
+
+			tangents[i1 * 3] = tangents[i1 * 3] + tangent.getX();
+			tangents[i1 * 3 + 1] = tangents[i1 * 3 + 1] + tangent.getY();
+			tangents[i1 * 3 + 2] = tangents[i1 * 3 + 2] + tangent.getZ();
+
+			tangents[i2 * 3] = tangents[i2 * 3] + tangent.getX();
+			tangents[i2 * 3 + 1] = tangents[i2 * 3 + 1] + tangent.getY();
+			tangents[i2 * 3 + 2] = tangents[i2 * 3 + 2] + tangent.getZ();
+
+			tangents[i3 * 3] = tangents[i3 * 3] + tangent.getX();
+			tangents[i3 * 3 + 1] = tangents[i3 * 3 + 1] + tangent.getY();
+			tangents[i3 * 3 + 2] = tangents[i3 * 3 + 2] + tangent.getZ();
+		}
+
+		for (int j = 0; j < tangents.length; j += 3) {
+			Vec3 tangent = new Vec3(tangents[j], tangents[j + 1],
+					tangents[j + 2]);
+			tangent.normalize();
+			tangents[j] = tangent.getX();
+			tangents[j + 1] = tangent.getY();
+			tangents[j + 2] = tangent.getZ();
+		}
+
+	}
+}
Index: java/client_3D/src/pl/vorg/mowa/core/graphics/IndexBuffer.java
===================================================================
--- java/client_3D/src/pl/vorg/mowa/core/graphics/IndexBuffer.java	(revision 65)
+++ java/client_3D/src/pl/vorg/mowa/core/graphics/IndexBuffer.java	(revision 65)
@@ -0,0 +1,19 @@
+package pl.vorg.mowa.core.graphics;
+
+/**
+ * A buffer with a list of indexes of sequent points of a figure (a type of a
+ * figure depends on PrimitiveType).
+ * 
+ * @author vorg
+ */
+public class IndexBuffer {
+	private int[] buffer;
+
+	public int[] getBuffer() {
+		return buffer;
+	}
+
+	public void setBuffer(int[] buffer) {
+		this.buffer = buffer;
+	}
+}
Index: java/client_3D/src/pl/vorg/mowa/core/graphics/PrimitiveType.java
===================================================================
--- java/client_3D/src/pl/vorg/mowa/core/graphics/PrimitiveType.java	(revision 65)
+++ java/client_3D/src/pl/vorg/mowa/core/graphics/PrimitiveType.java	(revision 65)
@@ -0,0 +1,24 @@
+package pl.vorg.mowa.core.graphics;
+
+import javax.media.opengl.GL;
+
+/**
+ * A type of a figure which can be a part of a 3d object.
+ * 
+ * @author vorg
+ * 
+ */
+public enum PrimitiveType {
+	None(GL.GL_POINTS), Points(GL.GL_POINTS), Triangles(GL.GL_TRIANGLES), Quads(
+			GL.GL_QUADS);
+
+	private final int glId;
+
+	PrimitiveType(int glId) {
+		this.glId = glId;
+	}
+
+	public int toGL() {
+		return glId;
+	}
+}
Index: java/client_3D/src/pl/vorg/mowa/core/graphics/SkyBox.java
===================================================================
--- java/client_3D/src/pl/vorg/mowa/core/graphics/SkyBox.java	(revision 65)
+++ java/client_3D/src/pl/vorg/mowa/core/graphics/SkyBox.java	(revision 65)
@@ -0,0 +1,259 @@
+package pl.vorg.mowa.core.graphics;
+
+import java.io.File;
+import java.nio.Buffer;
+
+import javax.media.opengl.GL;
+
+import com.framsticks.net.client3D.Log;
+import com.sun.opengl.util.texture.Texture;
+import com.sun.opengl.util.texture.TextureData;
+import com.sun.opengl.util.texture.TextureIO;
+
+/**
+ * An environment texture. 
+ */
+public class SkyBox {
+
+	Buffer buf;
+
+	public boolean Init(GL gl) {
+
+		return Init(gl, new String("res/img/Front.PNG"), new String(
+				"res/img/Back.PNG"), new String("res/img/Left.PNG"),
+				new String("res/img/Right.PNG"), new String("res/img/Top.PNG"),
+				new String("res/img/Bottom.PNG"));
+
+	}
+
+	public boolean Init(GL gl, String front, String back, String left,
+			String right, String top, String bottom) {
+		try {
+			m_tFront = LoadTex(front);
+			m_tBack = LoadTex(back);
+			m_tLeft = LoadTex(left);
+			;
+			m_tRight = LoadTex(right);
+			m_tTop = LoadTex(top);
+			m_tBottom = LoadTex(bottom);
+			CreateCube(gl, front, back, left, right, top, bottom);
+		} catch (Exception e) {
+			Log.getInstance().log("err",
+					"SkyBox: Error while loading textures! " + e.getMessage());
+			return false;
+		}
+		return true;
+	}
+
+	Texture LoadTex(String fileName) throws Exception {
+		Texture result = TextureIO.newTexture(new File(fileName), false);
+
+		result.setTexParameteri(GL.GL_TEXTURE_WRAP_S, GL.GL_NONE);
+		result.setTexParameteri(GL.GL_TEXTURE_WRAP_T, GL.GL_NONE);
+		return result;
+
+	}
+
+	public void Draw(GL gl, float boxSize, Vec3 rot) {
+		boolean light = gl.glIsEnabled(GL.GL_LIGHTING);
+		gl.glDisable(GL.GL_LIGHTING);
+		float size = boxSize * (1.72f / 3);
+		// float pozx = 0 ,pozy = 0 ,pozz = 0;
+		// float clip = g_CCamera.m_fFarClip;
+		// g_CCamera.m_fFarClip = 1000;
+		// g_CCamera.Set();
+		gl.glPushMatrix();
+		gl.glLoadIdentity();
+		// glTranslatef(g_CCamera.GetPosition().x,g_CCamera.GetPosition().y,g_CCamera.GetPosition().z);
+		gl.glRotatef(rot.getX(), 1, 0, 0);
+		gl.glRotatef(rot.getY(), 0, 1, 0);
+		gl.glRotatef(rot.getZ(), 0, 0, 1);
+		gl.glDepthMask(false);
+		gl.glEnable(GL.GL_TEXTURE_2D);
+		m_tFront.bind();
+		gl.glColor4f(1.0f, 1.0f, 1.0f, 0.0f);
+		// gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_DST_COLOR);
+		// gl.glEnable(GL.GL_BLEND);
+		gl.glBegin(GL.GL_QUADS);
+		// front
+
+		gl.glTexCoord2d(1, 1);
+		gl.glVertex3f((float) -size, (float) -size, (float) -size);
+		gl.glTexCoord2d(0, 1);
+		gl.glVertex3f((float) size, (float) -size, (float) -size);
+		gl.glTexCoord2d(0, 0);
+		gl.glVertex3f((float) size, (float) size, (float) -size);
+		gl.glTexCoord2d(1, 0);
+		gl.glVertex3f((float) -size, (float) size, (float) -size);
+		gl.glEnd();
+		// back
+		m_tBack.bind();
+		gl.glBegin(GL.GL_QUADS);
+		gl.glTexCoord2d(0, 1);
+		gl.glVertex3f((float) -size, (float) -size, (float) size);
+		gl.glTexCoord2d(0, 0);
+		gl.glVertex3f((float) -size, (float) size, (float) size);
+		gl.glTexCoord2d(1, 0);
+		gl.glVertex3f((float) size, (float) size, (float) size);
+		gl.glTexCoord2d(1, 1);
+		gl.glVertex3f((float) size, (float) -size, (float) size);
+		gl.glEnd();
+		// left
+		m_tLeft.bind();
+		gl.glBegin(GL.GL_QUADS);
+		gl.glTexCoord2d(0, 1);
+		gl.glVertex3f((float) -size, (float) -size, (float) -size);
+		gl.glTexCoord2d(0, 0);
+		gl.glVertex3f((float) -size, (float) size, (float) -size);
+		gl.glTexCoord2d(1, 0);
+		gl.glVertex3f((float) -size, (float) size, (float) size);
+		gl.glTexCoord2d(1, 1);
+		gl.glVertex3f((float) -size, (float) -size, (float) size);
+		gl.glEnd();
+		// Right
+		m_tRight.bind();
+		gl.glBegin(GL.GL_QUADS);
+		gl.glTexCoord2d(1, 1);
+		gl.glVertex3f((float) size, (float) -size, (float) -size);
+		gl.glTexCoord2d(0, 1);
+		gl.glVertex3f((float) size, (float) -size, (float) size);
+		gl.glTexCoord2d(0, 0);
+		gl.glVertex3f((float) size, (float) size, (float) size);
+		gl.glTexCoord2d(1, 0);
+		gl.glVertex3f((float) size, (float) size, (float) -size);
+		gl.glEnd();
+		// Top
+		m_tTop.bind();
+
+		gl.glBegin(GL.GL_QUADS);
+		gl.glTexCoord2d(1, 0);
+		gl.glVertex3f((float) -size, (float) size, (float) size);
+		gl.glTexCoord2d(1, 1);
+		gl.glVertex3f((float) -size, (float) size, (float) -size);
+		gl.glTexCoord2d(0, 1);
+		gl.glVertex3f((float) size, (float) size, (float) -size);
+		gl.glTexCoord2d(0, 0);
+		gl.glVertex3f((float) size, (float) size, (float) size);
+		gl.glEnd();
+		// Bottom
+		m_tBottom.bind();
+
+		gl.glBegin(GL.GL_QUADS);
+		gl.glTexCoord2d(1, 0);
+		gl.glVertex3f((float) -size, (float) -size, (float) -size);
+		gl.glTexCoord2d(1, 1);
+		gl.glVertex3f((float) -size, (float) -size, (float) size);
+		gl.glTexCoord2d(0, 1);
+		gl.glVertex3f((float) size, (float) -size, (float) size);
+		gl.glTexCoord2d(0, 0);
+		gl.glVertex3f((float) size, (float) -size, (float) -size);
+		gl.glEnd();
+		gl.glBindTexture(GL.GL_TEXTURE_2D, 0);
+		// gl.glDisable(GL.GL_BLEND);
+		// glEnable(GL_FOG);
+		gl.glPopMatrix();
+		if (light)
+			gl.glEnable(GL.GL_LIGHTING);
+		gl.glDepthMask(true);
+	}
+
+	private int[] textureCubeMapId;
+
+	void CreateCube(GL gl, String front, String back, String left,
+			String right, String top, String bottom) {
+		textureCubeMapId = new int[1];
+		int internalFormat = 0, format = 0, face = 0;
+
+		gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1);
+		gl.glGenTextures(1, textureCubeMapId, 0);
+		gl.glBindTexture(GL.GL_TEXTURE_CUBE_MAP, textureCubeMapId[0]);
+
+		gl.glTexParameteri(GL.GL_TEXTURE_CUBE_MAP, GL.GL_TEXTURE_WRAP_S,
+				GL.GL_CLAMP_TO_EDGE);
+		gl.glTexParameteri(GL.GL_TEXTURE_CUBE_MAP, GL.GL_TEXTURE_WRAP_T,
+				GL.GL_CLAMP_TO_EDGE);
+		gl.glTexParameteri(GL.GL_TEXTURE_CUBE_MAP, GL.GL_TEXTURE_MAG_FILTER,
+				GL.GL_LINEAR);
+		gl.glTexParameteri(GL.GL_TEXTURE_CUBE_MAP, GL.GL_TEXTURE_MIN_FILTER,
+				GL.GL_LINEAR);
+		TextureData texDataFront = null;
+		TextureData texDataBack = null;
+		TextureData texDataLeft = null;
+		TextureData texDataRight = null;
+		TextureData texDataTop = null;
+		TextureData texDataBottom = null;
+		try {
+			texDataFront = TextureIO
+					.newTextureData(new File(front), true, null);
+			texDataBack = TextureIO.newTextureData(new File(back), true, null);
+			texDataLeft = TextureIO.newTextureData(new File(left), true, null);
+			texDataRight = TextureIO
+					.newTextureData(new File(right), true, null);
+			texDataTop = TextureIO.newTextureData(new File(top), true, null);
+			texDataBottom = TextureIO.newTextureData(new File(bottom), true,
+					null);
+		} catch (Exception e) {
+			Log.getInstance().log(
+					"err",
+					"SkyBox: Error while generating cube textures! "
+							+ e.getMessage());
+		}
+		TextureData active = texDataFront;
+		for (int i = 0; i < 6; i++) {
+
+			switch (i) {
+			case 0:
+				face = GL.GL_TEXTURE_CUBE_MAP_POSITIVE_X;
+				active = texDataRight;
+				break;
+			case 1:
+				face = GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_X;
+				active = texDataLeft;
+				break;
+			case 2:
+				face = GL.GL_TEXTURE_CUBE_MAP_POSITIVE_Y;
+				active = texDataTop;
+				break;
+			case 3:
+				face = GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y;
+				active = texDataBottom;
+				break;
+			case 4:
+				face = GL.GL_TEXTURE_CUBE_MAP_POSITIVE_Z;
+				active = texDataBack;
+				break;
+			case 5:
+				face = GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
+				active = texDataFront;
+				break;
+			}
+			internalFormat = active.getInternalFormat();
+			internalFormat = GL.GL_RGB8;
+			// format = active.getPixelFormat() ;
+			format = GL.GL_RGB;
+
+			// for (int j=0; j< active.getMipmapData().length; j++)
+			// gl.glTexImage2D(face, j, internalFormat, active.getWidth(),
+			// active.getHeight(),0, format, GL.GL_UNSIGNED_BYTE,
+			// active.getMipmapData()[j]);
+			gl.glTexImage2D(face, 0, internalFormat, active.getWidth(),
+					active.getHeight(), 0, format, GL.GL_UNSIGNED_BYTE,
+					active.getBuffer());
+		}
+
+	}
+
+	public int getCube() {
+		return textureCubeMapId[0];
+	}
+
+	// void Kill() {m_tFront(); m_tBack.Free(); m_tLeft.Free(); m_tRight.Free();
+	// m_tTop.Free(); m_tBottom.Free();}
+
+	Texture m_tFront;
+	Texture m_tBack;
+	Texture m_tLeft;
+	Texture m_tRight;
+	Texture m_tTop;
+	Texture m_tBottom;
+}
Index: java/client_3D/src/pl/vorg/mowa/core/graphics/Vec2.java
===================================================================
--- java/client_3D/src/pl/vorg/mowa/core/graphics/Vec2.java	(revision 65)
+++ java/client_3D/src/pl/vorg/mowa/core/graphics/Vec2.java	(revision 65)
@@ -0,0 +1,107 @@
+package pl.vorg.mowa.core.graphics;
+
+/**
+ * A vector in 2d space.
+ * 
+ * @author vorg
+ */
+public class Vec2 {
+	private float[] v = {0,0};
+	
+	public Vec2() {
+		
+	}
+	
+	public Vec2(float x, float y) {
+		v[0] = x;
+		v[1] = y;
+	}
+	
+	public Vec2(Vec2 vec) {
+		float[] w = vec.getVector();
+		v[0] = w[0];
+		v[1] = w[1];
+	}
+	
+	public Vec2(float[] vec) {
+		v[0] = vec[0];
+		v[1] = vec[1];
+	}
+	
+	@Override
+	public boolean equals(java.lang.Object obj) {
+		float[] w = ((Vec2)obj).getVector();
+		return (v[0] == w[0]) && (v[1] == w[1]); 
+	}
+	
+	public float getX() {
+		return v[0];
+	}
+	
+	public float getY() {
+		return v[1];
+	}
+	
+	public float getS() {
+		return v[0];
+	}
+	
+	public float getT() {
+		return v[1];
+	}
+	
+	public void setX(float x) {
+		v[0] = x;
+	}
+	
+	public void setY(float y) {
+		v[1] = y;
+	}
+	
+	public void setS(float s) {
+		v[0] = s;
+	}
+	
+	public void setT(float t) {
+		v[1] = t;
+	}
+	
+	public float[] getVector() {
+		return v;
+	}
+	
+	public void normalize() {
+		float length = length();
+		if (length > 0) {
+			v[0] /= length;
+			v[1] /= length;
+		}
+	}
+	
+	public float length() {
+		return (float)Math.sqrt(v[0]*v[0]+v[1]*v[1]);
+	}
+	
+	public float dot(Vec2 a) {
+		float[] va = a.getVector();
+		return v[0]*va[0] + v[1]*va[1];
+	}
+	
+	public float angle(Vec2 a) {
+		float len = length();
+		float alen = a.length();
+		if ((len == 0) || (alen == 0)) {
+			return 0;
+		}
+		return (float)Math.acos(this.dot(a)/(len*alen));
+	}
+	
+	public static float angle(Vec2 a, Vec2 b) {
+		return a.angle(b);
+	}
+	
+	public static float angleTan(Vec2 a, Vec2 b) {
+		double angle = Math.atan2(a.getY(),a.getX()) - Math.atan2(b.getY(),b.getX());
+		return (float)angle;
+	}
+}
Index: java/client_3D/src/pl/vorg/mowa/core/graphics/Vec3.java
===================================================================
--- java/client_3D/src/pl/vorg/mowa/core/graphics/Vec3.java	(revision 65)
+++ java/client_3D/src/pl/vorg/mowa/core/graphics/Vec3.java	(revision 65)
@@ -0,0 +1,163 @@
+package pl.vorg.mowa.core.graphics;
+
+/**
+ * A vector in 3d space.
+ * 
+ * @author vorg
+ */
+public class Vec3 {
+	private float[] v = {0,0,0};
+	
+	public Vec3() {
+		
+	}
+	
+	public Vec3(float x, float y, float z) {
+		v[0] = x;
+		v[1] = y;
+		v[2] = z;
+	}
+	
+	public Vec3(Vec3 vec) {
+		float[] w = vec.getVector();
+		v[0] = w[0];
+		v[1] = w[1];
+		v[2] = w[2];
+	}
+	
+	public Vec3(float[] vec) {
+		v[0] = vec[0];
+		v[1] = vec[1];
+		v[2] = vec[2];
+	}
+	
+	@Override
+	public boolean equals(java.lang.Object obj) {
+		float[] w = ((Vec3)obj).getVector();
+		return (v[0] == w[0]) && (v[1] == w[1]) && (v[2] == w[2]); 
+	}
+	
+	public float getX() {
+		return v[0];
+	}
+	
+	public float getY() {
+		return v[1];
+	}
+	
+	public float getZ() {
+		return v[2];
+	}
+	
+	public void setX(float x) {
+		v[0] = x;
+	}
+	
+	public void setY(float y) {
+		v[1] = y;
+	}
+	
+	public void setZ(float z) {
+		v[2] = z;
+	}
+	
+	
+	public float[] getVector() {
+		return v;
+	}
+	
+	public void normalize() {
+		float length = length();
+		if (length > 0) {
+			v[0] /= length;
+			v[1] /= length;
+			v[2] /= length;
+		}
+	}
+	
+	public float length() {
+		return (float)Math.sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]);
+	}
+	
+	public void add(Vec3 a) {
+		v[0] += a.getX();
+		v[1] += a.getY();
+		v[2] += a.getZ();
+	}
+	
+	public static Vec3 add(Vec3 a, Vec3 b) {
+		return new Vec3(
+			a.getX() + b.getX(),
+			a.getY() + b.getY(),
+			a.getZ() + b.getZ()
+		);
+	}
+	
+	public void sub(Vec3 a) {
+		v[0] -= a.getX();
+		v[1] -= a.getY();
+		v[2] -= a.getZ();
+	}
+	
+	public static Vec3 sub(Vec3 a, Vec3 b) {
+		return new Vec3(
+			a.getX() - b.getX(),
+			a.getY() - b.getY(),
+			a.getZ() - b.getZ()
+		);
+	}
+	
+	public void mul(float f) {
+		v[0] *= f;
+		v[1] *= f;
+		v[2] *= f;
+	}
+	
+	public static Vec3 mul(float f, Vec3 a) {
+		return new Vec3(
+			a.getX() * f,
+			a.getY() * f,
+			a.getZ() * f
+		);
+	}
+	
+	public static Vec3 cross(Vec3 a, Vec3 b) {
+		float[] va = a.getVector();
+		float[] vb = b.getVector();
+		return new Vec3(
+				va[1]*vb[2] - va[2]*vb[1],
+				va[2]*vb[0] - va[0]*vb[2],
+				va[0]*vb[1] - va[1]*vb[0]
+		); 
+	}
+	
+	public float dot(Vec3 a) {
+		float[] va = a.getVector();
+		return v[0]*va[0] + v[1]*va[1] + v[2]*va[2];
+	}
+	
+	public static float dot(Vec3 a, Vec3 b) {
+		float[] va = a.getVector();
+		float[] vb = b.getVector();
+		return va[0]*vb[0] + va[1]*vb[1] + va[2]*vb[2];
+	}
+	
+	public float angle(Vec3 a) {
+		float len = length();
+		float alen = a.length();
+		if ((len == 0) || (alen == 0)) {
+			return 0;
+		}
+		return (float)Math.acos(this.dot(a)/(len*alen));
+	}
+	
+	public static Vec3 normalized(Vec3 a) {
+		Vec3 na = new Vec3(a);
+		na.normalize();
+		return na;
+	}
+	
+	public static Vec3 neg(Vec3 a) {
+		return new Vec3(-a.getX(), -a.getY(), -a.getZ());
+	}
+}
Index: java/client_3D/src/pl/vorg/mowa/core/graphics/VertexAttrib.java
===================================================================
--- java/client_3D/src/pl/vorg/mowa/core/graphics/VertexAttrib.java	(revision 65)
+++ java/client_3D/src/pl/vorg/mowa/core/graphics/VertexAttrib.java	(revision 65)
@@ -0,0 +1,61 @@
+package pl.vorg.mowa.core.graphics;
+
+/**
+ * The base class of a vertex attribute used in buffers.
+ * 
+ * @author vorg
+ */
+public abstract class VertexAttrib {
+	/**
+	 * The attribute's name IS meaningful and it corresponds to the attribute's
+	 * semantics. Basic attributes are, e.g.: pos, normal, texCoord0.
+	 */
+	private String name = "";
+
+	/**
+	 * Number of elements that an attributes contains. E.g., a position contains
+	 * 3 floats x,y,z. It should be used in VertexArrays, however, since they
+	 * don't work in JOGL under OSX, those values are hard-coded in
+	 * Geometry.displayImmediately(), and the dataType field hasn't been used.
+	 */
+	private int dataType;
+
+	/**
+	 * Constructor.
+	 * 
+	 * @param name
+	 *            The attribute's name. It IS meaningful and it corresponds to
+	 *            the attribute's semantics. Basic attributes are, e.g.: pos,
+	 *            normal, texCoord0.
+	 */
+	public VertexAttrib(String name) {
+		this.name = name;
+	}
+
+	/**
+	 * @return Number of elements that an attributes contains.
+	 */
+	public int getDataType() {
+		return dataType;
+	}
+
+	public void setDataType(int dataType) {
+		this.dataType = dataType;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	/**
+	 * Sets the attribute's name. It IS meaningful and it corresponds to the
+	 * attribute's semantics. Basic attributes are, e.g.: pos, normal,
+	 * texCoord0.
+	 * 
+	 * @param name
+	 *            The attribute's name.
+	 */
+	public void setName(String name) {
+		this.name = name;
+	}
+}
Index: java/client_3D/src/pl/vorg/mowa/core/graphics/VertexStream.java
===================================================================
--- java/client_3D/src/pl/vorg/mowa/core/graphics/VertexStream.java	(revision 65)
+++ java/client_3D/src/pl/vorg/mowa/core/graphics/VertexStream.java	(revision 65)
@@ -0,0 +1,48 @@
+package pl.vorg.mowa.core.graphics;
+
+import java.util.ArrayList;
+
+/**
+ * A stream of vertex. Eeach vertex may be described by many different
+ * attributes (position, color, texture coordinates, etc.) contained in buffers.
+ * 
+ * Za to jak kolejno ukladaja sie wiercholki odpowiada indexBuffer.
+ * 
+ * Np trojkat o wierzcholkach (0,0,0) (1,0,0) (0,1,0) o kolorach czerwonym,
+ * zielonym i niebieskim ze wspolrzednymi tekstury (0,0) (1,0) (0,1)
+ * 
+ * wygladalby tak FloatVertexAttrib "pos" : [0, 0, 0, 1, 0, 0, 0, 1, 0]
+ * FloatVertexAttrib "color" : [1, 0, 0, 0, 1, 0, 0, 0, 1] FloatVertexAttrib
+ * "texCoord0" : [0, 0, 1, 0, 0, 1] IndexBuffer : [0, 1, 2]
+ * 
+ * @author vorg
+ */
+public class VertexStream {
+	private ArrayList<VertexAttrib> attribs = new ArrayList<VertexAttrib>();
+	private IndexBuffer indexBuffer;
+
+	public ArrayList<VertexAttrib> getAttribs() {
+		return attribs;
+	}
+
+	public VertexAttrib getAttribByName(String name) {
+		for (VertexAttrib attrib : attribs) {
+			if (attrib.getName().equals(name)) {
+				return attrib;
+			}
+		}
+		return null;
+	}
+
+	public void addAttrib(VertexAttrib attrib) {
+		attribs.add(attrib);
+	}
+
+	public IndexBuffer getIndexBuffer() {
+		return indexBuffer;
+	}
+
+	public void setIndexBuffer(IndexBuffer indexBuffer) {
+		this.indexBuffer = indexBuffer;
+	}
+}
Index: java/client_3D/static/osx/client3d.app/Contents/Info.plist
===================================================================
--- java/client_3D/static/osx/client3d.app/Contents/Info.plist	(revision 65)
+++ java/client_3D/static/osx/client3d.app/Contents/Info.plist	(revision 65)
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
+<plist version="0.9">
+<dict>
+	<key>CFBundleName</key>
+	<string>Client3d</string>
+	<key>CFBundleVersion</key>
+	<string>10.2</string>
+	<key>CFBundleAllowMixedLocalizations</key>
+	<string>true</string>
+	<key>CFBundleExecutable</key>
+	<string>JavaApplicationStub</string>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>English</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleIconFile</key>
+	<string>GenericJavaApp.icns</string>
+	<key>Java</key>
+	<dict>
+		<key>WorkingDirectory</key>
+		<string>$APP_PACKAGE/Contents/Resources/Java</string>
+		<key>MainClass</key>
+		<string>pl.put.idss.framsticks.App</string>
+		<key>JVMVersion</key>
+		<string>1.5+</string>
+		<key>ClassPath</key>
+			<array>
+			<string>$JAVAROOT/framsticks-jogl.jar</string>
+			<string>$JAVAROOT/jogl.jar</string>
+			<string>$JAVAROOT/foxtrot.jar</string>
+			</array>
+	</dict>
+</dict>
+</plist>
Index: java/client_3D/static/osx/client3d.app/Contents/PkgInfo
===================================================================
--- java/client_3D/static/osx/client3d.app/Contents/PkgInfo	(revision 65)
+++ java/client_3D/static/osx/client3d.app/Contents/PkgInfo	(revision 65)
@@ -0,0 +1,1 @@
+APPL????
Index: java/client_3D/static/shared/res/mock/creature_Oafuz_info.txt
===================================================================
--- java/client_3D/static/shared/res/mock/creature_Oafuz_info.txt	(revision 65)
+++ java/client_3D/static/shared/res/mock/creature_Oafuz_info.txt	(revision 65)
@@ -0,0 +1,34 @@
+file 
+Creature:
+name:Oafuz Okaetehe
+genotype:~
+lllfffX[0:3.314, 2:-2, 1:-1][-1:1, 0:1, 0:-1][-1:1](RRlllfffMMMX[|-1:-8.522]lllFFFMMMX[|-2:-1], fffIXlllfffMMMsX[|6:10, 3:-10](RRlllfffMMMIX[|-4:-10]lllFFFMMMIX[|-5:-1][S:1.995], , RRlllfffMMMIX[|-7:10]lllFFFMMMIX[|-8:-1][S:1]), RRlllfffMMMX[|-10:10]lllFFFMMMX[|-11:-1.784])~
+info:1.82% mutation of 'Aafup Okaetehe'
+gnum:3
+energ0:55000
+idleen:11
+energy:36718
+energy_p:0
+energy_m:11
+energy_b:-11
+perf:1
+nnenabled:1
+lifespan:1600
+distance:12.1467
+c_velocity:0.00957626
+c_vertvelocity:0.000272313
+c_vertpos:0.00355466
+velocity:0.00759171
+vertpos:0.00938471
+vertvel:0.000384282
+user1:null
+user2:null
+user3:null
+parts:12
+joints:11
+neurodefs:25
+mechparts:12
+mechjoints:11
+neurons:25
+
+eof
Index: java/client_3D/static/shared/res/mock/creature_Oafuz_joints.txt
===================================================================
--- java/client_3D/static/shared/res/mock/creature_Oafuz_joints.txt	(revision 65)
+++ java/client_3D/static/shared/res/mock/creature_Oafuz_joints.txt	(revision 65)
@@ -0,0 +1,156 @@
+file 
+Joint:
+p1:0
+p2:1
+rx:0
+ry:0
+rz:0
+dx:0.55981
+dy:0
+dz:0
+stif:1
+rotstif:1
+stam:0.25
+Vstyle:joint
+
+Joint:
+p1:1
+p2:2
+rx:1.5706
+ry:0
+rz:-1.5705
+dx:0.484317
+dy:0
+dz:0
+stif:1
+rotstif:1
+stam:0.0892857
+Vstyle:joint
+
+Joint:
+p1:2
+p2:3
+rx:0
+ry:0
+rz:0
+dx:0.47137
+dy:0
+dz:0
+stif:1
+rotstif:1
+stam:0.0647866
+Vstyle:joint
+
+Joint:
+p1:1
+p2:4
+rx:0
+ry:0
+rz:0
+dx:0.779905
+dy:0
+dz:0
+stif:1
+rotstif:1
+stam:0.15625
+Vstyle:joint
+
+Joint:
+p1:4
+p2:5
+rx:0
+ry:0
+rz:0
+dx:0.522064
+dy:0
+dz:0
+stif:1
+rotstif:1
+stam:0.0360825
+Vstyle:joint
+
+Joint:
+p1:5
+p2:6
+rx:1.5706
+ry:0
+rz:-1.5705
+dx:0.477844
+dy:0
+dz:0
+stif:1
+rotstif:1
+stam:0.0246441
+Vstyle:joint
+
+Joint:
+p1:6
+p2:7
+rx:0
+ry:0
+rz:0
+dx:0.47026
+dy:0
+dz:0
+stif:1
+rotstif:1
+stam:0.0176989
+Vstyle:joint
+
+Joint:
+p1:5
+p2:8
+rx:1.5706
+ry:0
+rz:1.5705
+dx:0.477844
+dy:0
+dz:0
+stif:1
+rotstif:1
+stam:0.0246441
+Vstyle:joint
+
+Joint:
+p1:8
+p2:9
+rx:0
+ry:0
+rz:0
+dx:0.47026
+dy:0
+dz:0
+stif:1
+rotstif:1
+stam:0.0176989
+Vstyle:joint
+
+Joint:
+p1:1
+p2:10
+rx:1.5706
+ry:0
+rz:1.5705
+dx:0.484317
+dy:0
+dz:0
+stif:1
+rotstif:1
+stam:0.0892857
+Vstyle:joint
+
+Joint:
+p1:10
+p2:11
+rx:0
+ry:0
+rz:0
+dx:0.47137
+dy:0
+dz:0
+stif:1
+rotstif:1
+stam:0.0647866
+Vstyle:joint
+
+eof
Index: java/client_3D/static/shared/res/mock/creature_Oafuz_neurons.txt
===================================================================
--- java/client_3D/static/shared/res/mock/creature_Oafuz_neurons.txt	(revision 65)
+++ java/client_3D/static/shared/res/mock/creature_Oafuz_neurons.txt	(revision 65)
@@ -0,0 +1,290 @@
+file 
+Neuro:
+getInputCount:3
+inputSum:0.267955
+weightedInputSum:3.50275
+state:0.998188
+channelCount:1
+hold:0
+currState:0.998188
+position_x:5.11963
+position_y:0.194211
+position_z:-0.0298492
+
+Neuro:
+getInputCount:3
+inputSum:0.251526
+weightedInputSum:-0.392288
+state:-0.373331
+channelCount:1
+hold:0
+currState:-0.373331
+position_x:5.11963
+position_y:0.194211
+position_z:-0.0298492
+
+Neuro:
+getInputCount:1
+inputSum:-0.373331
+weightedInputSum:-0.373331
+state:-0.356902
+channelCount:1
+hold:0
+currState:-0.356902
+position_x:5.11963
+position_y:0.194211
+position_z:-0.0298492
+
+Neuro:
+getInputCount:1
+inputSum:-0.356902
+weightedInputSum:3.56902
+state:0.998413
+channelCount:1
+hold:0
+currState:0.998413
+position_x:5.53617
+position_y:-0.0577703
+position_z:-0.0337443
+
+Neuro:
+getInputCount:1
+inputSum:0.998413
+weightedInputSum:0.998413
+state:0.998413
+channelCount:1
+hold:0
+currState:0.998413
+position_x:5.11963
+position_y:0.194211
+position_z:-0.0298492
+
+Neuro:
+getInputCount:1
+inputSum:-0.356902
+weightedInputSum:0.356902
+state:0.342482
+channelCount:1
+hold:0
+currState:0.342482
+position_x:5.73497
+position_y:-0.190173
+position_z:0.339278
+
+Neuro:
+getInputCount:1
+inputSum:0.342482
+weightedInputSum:0.342482
+state:0.342482
+channelCount:1
+hold:0
+currState:0.342482
+position_x:5.53617
+position_y:-0.0577703
+position_z:-0.0337443
+
+Neuro:
+getInputCount:2
+inputSum:0
+weightedInputSum:0
+state:0
+channelCount:1
+hold:0
+currState:0
+position_x:6.39474
+position_y:0.17859
+position_z:-0.028254
+
+Neuro:
+getInputCount:1
+inputSum:0
+weightedInputSum:0
+state:0
+channelCount:1
+hold:0
+currState:0
+position_x:5.88552
+position_y:0.182048
+position_z:-0.0237908
+
+Neuro:
+getInputCount:1
+inputSum:-0.356902
+weightedInputSum:3.56902
+state:0.998413
+channelCount:1
+hold:0
+currState:0.998413
+position_x:6.80046
+position_y:-0.0660786
+position_z:-0.0337176
+
+Neuro:
+getInputCount:1
+inputSum:0.998413
+weightedInputSum:0.998413
+state:0.998413
+channelCount:1
+hold:0
+currState:0.998413
+position_x:6.39474
+position_y:0.17859
+position_z:-0.028254
+
+Neuro:
+getInputCount:1
+inputSum:-0.356902
+weightedInputSum:0.356902
+state:0.342482
+channelCount:1
+hold:0
+currState:0.342482
+position_x:7.00128
+position_y:-0.19745
+position_z:0.337384
+
+Neuro:
+getInputCount:1
+inputSum:0.342482
+weightedInputSum:0.342482
+state:0.342482
+channelCount:1
+hold:0
+currState:0.342482
+position_x:6.80046
+position_y:-0.0660786
+position_z:-0.0337176
+
+Neuro:
+getInputCount:1
+inputSum:0
+weightedInputSum:0
+state:0
+channelCount:1
+hold:0
+currState:0
+position_x:7.00128
+position_y:-0.19745
+position_z:0.337384
+
+Neuro:
+getInputCount:0
+inputSum:0
+weightedInputSum:0
+state:0
+channelCount:1
+hold:0
+currState:0
+position_x:7.00128
+position_y:-0.19745
+position_z:0.337384
+
+Neuro:
+getInputCount:1
+inputSum:-0.356902
+weightedInputSum:-3.56902
+state:-0.998413
+channelCount:1
+hold:0
+currState:-0.998413
+position_x:6.80653
+position_y:0.415621
+position_z:-0.0291558
+
+Neuro:
+getInputCount:1
+inputSum:-0.998413
+weightedInputSum:-0.998413
+state:-0.998413
+channelCount:1
+hold:0
+currState:-0.998413
+position_x:6.39474
+position_y:0.17859
+position_z:-0.028254
+
+Neuro:
+getInputCount:1
+inputSum:-0.356902
+weightedInputSum:0.356902
+state:0.342482
+channelCount:1
+hold:0
+currState:0.342482
+position_x:7.01115
+position_y:0.510097
+position_z:0.350257
+
+Neuro:
+getInputCount:1
+inputSum:0.342482
+weightedInputSum:0.342482
+state:0.342482
+channelCount:1
+hold:0
+currState:0.342482
+position_x:6.80653
+position_y:0.415621
+position_z:-0.0291558
+
+Neuro:
+getInputCount:0
+inputSum:0
+weightedInputSum:0
+state:0
+channelCount:1
+hold:0
+currState:0
+position_x:7.01115
+position_y:0.510097
+position_z:0.350257
+
+Neuro:
+getInputCount:1
+inputSum:-0.356902
+weightedInputSum:-3.56902
+state:-0.998413
+channelCount:1
+hold:0
+currState:-0.998413
+position_x:5.53421
+position_y:0.440298
+position_z:-0.0288489
+
+Neuro:
+getInputCount:1
+inputSum:-0.998413
+weightedInputSum:-0.998413
+state:-0.998413
+channelCount:1
+hold:0
+currState:-0.998413
+position_x:5.11963
+position_y:0.194211
+position_z:-0.0298492
+
+Neuro:
+getInputCount:1
+inputSum:-0.356902
+weightedInputSum:0.636713
+state:0.562657
+channelCount:1
+hold:0
+currState:0.562657
+position_x:5.45969
+position_y:0.374409
+position_z:0.398577
+
+Neuro:
+getInputCount:1
+inputSum:0.562657
+weightedInputSum:0.562657
+state:0.562657
+channelCount:1
+hold:0
+currState:0.562657
+position_x:5.53421
+position_y:0.440298
+position_z:-0.0288489
+
+eof
Index: java/client_3D/static/shared/res/mock/creature_Oafuz_parts.txt
===================================================================
--- java/client_3D/static/shared/res/mock/creature_Oafuz_parts.txt	(revision 65)
+++ java/client_3D/static/shared/res/mock/creature_Oafuz_parts.txt	(revision 65)
@@ -0,0 +1,182 @@
+file 
+Part:
+x:0
+y:0
+z:0
+m:1
+s:1
+dn:1
+fr:0.2048
+ing:0.25
+as:0.25
+rx:0
+ry:0
+rz:0
+Vstyle:part
+
+Part:
+x:0.55981
+y:0
+z:0
+m:4
+s:1
+dn:1
+fr:0.2048
+ing:0.25
+as:0.25
+rx:0
+ry:0
+rz:0
+Vstyle:part
+
+Part:
+x:0.559954
+y:-0.484317
+z:0
+m:2
+s:1
+dn:1
+fr:0.124846
+ing:0.0892857
+as:0.0892857
+rx:0
+ry:0
+rz:0
+Vstyle:part
+
+Part:
+x:0.560093
+y:-0.955687
+z:0
+m:1
+s:1
+dn:1
+fr:2.0441
+ing:0.0647866
+as:0.0647866
+rx:0
+ry:0
+rz:0
+Vstyle:part
+
+Part:
+x:1.33971
+y:0
+z:0
+m:2
+s:1
+dn:1
+fr:0.124846
+ing:0.53125
+as:0.15625
+rx:0
+ry:0
+rz:0
+Vstyle:part
+
+Part:
+x:1.86178
+y:0
+z:0
+m:3
+s:1
+dn:1
+fr:0.092097
+ing:0.16323
+as:0.0601375
+rx:0
+ry:0
+rz:0
+Vstyle:part
+
+Part:
+x:1.86192
+y:-0.477844
+z:0
+m:2
+s:1
+dn:1
+fr:0.0786829
+ing:0.475794
+as:0.0306575
+rx:0
+ry:0
+rz:0
+Vstyle:part
+
+Part:
+x:1.86206
+y:-0.948104
+z:0
+m:1
+s:1
+dn:1
+fr:2.02519
+ing:0.505182
+as:0.0189202
+rx:0
+ry:0
+rz:0
+Vstyle:part
+
+Part:
+x:1.86192
+y:0.477844
+z:0
+m:2
+s:1
+dn:1
+fr:0.0786829
+ing:0.475794
+as:0.0306575
+rx:0
+ry:0
+rz:0
+Vstyle:part
+
+Part:
+x:1.86206
+y:0.948104
+z:0
+m:1
+s:1
+dn:1
+fr:2.02519
+ing:0.505182
+as:0.0189202
+rx:0
+ry:0
+rz:0
+Vstyle:part
+
+Part:
+x:0.559954
+y:0.484317
+z:0
+m:2
+s:1
+dn:1
+fr:0.124846
+ing:0.0892857
+as:0.0892857
+rx:0
+ry:0
+rz:0
+Vstyle:part
+
+Part:
+x:0.560093
+y:0.955687
+z:0
+m:1
+s:1
+dn:1
+fr:2.0441
+ing:0.0647866
+as:0.0647866
+rx:0
+ry:0
+rz:0
+Vstyle:part
+
+eof
Index: java/client_3D/static/shared/res/mock/creatures.txt
===================================================================
--- java/client_3D/static/shared/res/mock/creatures.txt	(revision 65)
+++ java/client_3D/static/shared/res/mock/creatures.txt	(revision 65)
@@ -0,0 +1,102 @@
+file /simulator/populations/groups/0/creatures/0/parts{x,y,z}
+Part:
+x:1
+y:1
+z:0
+
+Part:
+x:1
+y:1
+z:0
+
+Part:
+x:2
+y:1
+z:0
+
+eof
+file /simulator/populations/groups/1/creatures/0/parts{x,y,z}
+Part:
+x:0
+y:0
+z:0
+
+Part:
+x:1
+y:0
+z:0
+
+Part:
+x:2
+y:0
+z:0
+
+Part:
+x:3
+y:0
+z:0
+
+eof
+file /simulator/populations/groups/0/creatures/0/mechparts{x,y,z}
+MechPart:
+x:13.2728
+y:4.83487
+z:0.101
+
+MechPart:
+x:14.2728
+y:4.83487
+z:0.101
+
+MechPart:
+x:15.2728
+y:4.83487
+z:0.101
+
+eof
+file /simulator/populations/groups/1/creatures/0/mechparts{x,y,z}
+MechPart:
+x:-0.332969
+y:1.4589
+z:0.101
+
+MechPart:
+x:0.667031
+y:1.4589
+z:0.101
+
+MechPart:
+x:1.66703
+y:1.4589
+z:0.101
+
+MechPart:
+x:2.66703
+y:1.4589
+z:0.101
+
+eof
+file /simulator/populations/groups/0/creatures/0/joints{p1,p2}
+Joint:
+p1:0
+p2:1
+
+Joint:
+p1:1
+p2:2
+
+eof
+file /simulator/populations/groups/1/creatures/0/joints{p1,p2}
+Joint:
+p1:0
+p2:1
+
+Joint:
+p1:1
+p2:2
+
+Joint:
+p1:2
+p2:3
+
+eof
Index: java/client_3D/static/shared/res/mock/world.txt
===================================================================
--- java/client_3D/static/shared/res/mock/world.txt	(revision 65)
+++ java/client_3D/static/shared/res/mock/world.txt	(revision 65)
@@ -0,0 +1,15 @@
+file 
+World:
+wrldbnd:0
+wrldwat:-1
+faces:~
+p
+0 0 0
+20.0 0 0
+20.0 20.0 0
+0 20.0 0
+f
+0 1 2 3
+~
+
+eof
Index: java/client_3D/static/shared/res/shaders/amber.cgfx
===================================================================
--- java/client_3D/static/shared/res/shaders/amber.cgfx	(revision 65)
+++ java/client_3D/static/shared/res/shaders/amber.cgfx	(revision 65)
@@ -0,0 +1,76 @@
+float4x4 mvp : ModelViewProjection;
+float4x4 mv : ModelView;
+float4x4 mvit : ModelViewIT;
+
+samplerCUBE skybox : SKYBOX;
+
+sampler2D amberTex <string fileName = "amber.jpg";> = sampler_state {
+  minFilter = Linear;
+  magFilter = Linear;
+  wrapS = Repeat;
+  wrapT = Repeat;
+};
+
+float4 vertex(uniform float4x4 modelViewProj,
+              uniform float4x4 modelView,
+              uniform float4x4 modelViewIT,
+              float4 P : POSITION,
+              float4 N : NORMAL,
+	          float2 uvIn : TEXCOORD0,
+		   in float4 Cin : COLOR0,
+              out float4 CC : COLOR0,
+              out float3 Pcam : TEXCOORD1,
+              out float3 Ncam : TEXCOORD2,
+	      out float2 uv : TEXCOORD0) : POSITION
+{
+    CC.xyz =  normalize(N.xyz);
+    Pcam = mul(modelView, P).xyz;
+    float3x3 rotation = (float3x3)modelView;
+    Ncam.xyz = mul(rotation,N.xyz);
+    uv = uvIn;
+    return mul(modelViewProj, P);
+}
+
+
+float4 light(in float3 texcoord,
+                float3 Pcam : TEXCOORD1, 
+                float3 Ncam : TEXCOORD2) : COLOR 
+{
+   float3 Nn = normalize(Ncam);
+
+   // Reflection and refraction.
+   float3 refl = reflect(Pcam,Nn);
+   float3 refrRed = refract(Pcam,Nn, 1.3);
+   float3 refrYellow = refract(Pcam,Nn,1.5);
+    
+   float3 reflCol = texCUBE(skybox,refl).rgb;
+   reflCol.b = 0;
+   
+   float3 refrRedCol; 
+   refrRedCol.r = texCUBE(skybox,refrRed).r;
+   
+   float3 refrYellowCol;
+   refrYellowCol.r = texCUBE(skybox,refrYellow).r;
+   refrYellowCol.g = texCUBE(skybox,refrYellow).g;
+   
+   float3 refCol = (reflCol + refrRedCol + refrYellowCol) / 3.0;
+   
+   // Amber Texture:
+   float3 amberCol = tex2D(amberTex, texcoord);
+   
+   // Random factor:
+   float3 randomCol = normalize(Pcam);
+   randomCol.b *= .25;
+
+   float3 finalCol = (5.0 * refCol + 5.0 * amberCol + 0.0 * randomCol) / 10.0;
+   return float4(finalCol, 1);
+}
+
+technique red <string target = "both";>
+{
+	pass p0
+	{
+ 		VertexProgram = compile arbvp1 vertex(mvp, mv, mvit);
+ 		FragmentProgram = compile arbfp1 light();
+	}
+}
Index: java/client_3D/static/shared/res/shaders/basic.cgfx
===================================================================
--- java/client_3D/static/shared/res/shaders/basic.cgfx	(revision 65)
+++ java/client_3D/static/shared/res/shaders/basic.cgfx	(revision 65)
@@ -0,0 +1,26 @@
+#include "res/shaders/lights.cg"
+float4x4 mvp : ModelViewProjection;
+float4x4 mv : ModelView;
+float4x4 mvit : ModelViewIT;
+
+
+sampler2D greenTex <string fileName = "green2.png";> = sampler_state {
+  minFilter = Linear;
+  magFilter = Linear;
+  wrapS = Repeat;
+  wrapT = Repeat;
+};
+sampler2D green2Tex<string fileName = "green4.png";> = sampler_state {
+  generateMipMap = true;
+  minFilter = Linear;
+  magFilter = Linear;
+  wrapS = Repeat;
+  wrapT = Repeat;
+};
+
+technique parts <string target = "both";>
+{
+	pass p0
+	{	
+	}
+}
Index: java/client_3D/static/shared/res/shaders/bumpy.cgfx
===================================================================
--- java/client_3D/static/shared/res/shaders/bumpy.cgfx	(revision 65)
+++ java/client_3D/static/shared/res/shaders/bumpy.cgfx	(revision 65)
@@ -0,0 +1,80 @@
+#include "res/shaders/lights.cg"
+float4x4 mvp : ModelViewProjection;
+float4x4 mv : ModelView;
+float4x4 mvit : ModelViewIT;
+
+
+sampler2D diffuse <string fileName = "stone.jpg";> = sampler_state {
+  minFilter = Linear;
+  magFilter = Linear;
+  wrapS = Repeat;
+  wrapT = Repeat;
+};
+sampler2D normal<string fileName = "normal.tga";> = sampler_state {
+  generateMipMap = true;
+  minFilter = Linear;
+  magFilter = Linear;
+  wrapS = Repeat;
+  wrapT = Repeat;
+};
+
+float4 vertex(uniform float4x4 modelViewProj,
+              uniform float4x4 modelView,
+              uniform float4x4 modelViewIT,
+              float4 P : POSITION,
+              float4 N : NORMAL,
+	      float2 uvIn : TEXCOORD0,
+		 in float4 Cin : COLOR0,
+              out float4 CC : COLOR0,
+              out float3 Pcam : TEXCOORD1,
+              out float3 Ncam : TEXCOORD2,
+	      out float2 uv : TEXCOORD0) : POSITION
+{
+    CC.xyz =  normalize(N.xyz);
+    Pcam = mul(modelView, P).xyz;
+    Ncam = mul(modelViewIT, N).xyz;
+    //float3x3 rotation = (float3x3)modelView;
+    //Ncam.xyz = mul(rotation,N.xyz);
+    
+    uv = uvIn;
+    return mul(modelViewProj, P);
+}
+
+float3 expand(float3 v) { return (v-0.5)*2; }
+
+float4 light(	float4 col: COLOR0,
+                  in float3 texcoord,
+                  float3 Pcam : TEXCOORD1, 
+                  float3 Ncam : TEXCOORD2) : COLOR 
+{
+    float3 tan = normalize(col.xyz);
+    float3 wo = normalize(-Pcam);
+    float3 result;// = ambient;
+    float3 Nn = normalize(Ncam);
+   float3 biNorm = normalize(cross(tan,Nn));
+    float3x3 toTexture = transpose(float3x3(tan,biNorm,Nn));
+
+	Nn = mul(toTexture,expand(tex2D(normal,texcoord)));
+
+   
+    	 float3 scolor = float3(1.0,1.0,1.0);
+        float3 Ln = normalize(float3(0,0,-1)-Pcam);
+        float3 lcolor = float3(1,1,1);
+        float3 Hn = normalize(wo + Ln);
+        float ldn = dot(Ln,Nn);
+        float hdn = dot(Hn,Nn);
+        float4 litV = lit(ldn, hdn, 32);
+            
+    result = tex2D(diffuse ,texcoord)*litV.y;
+    return float4(result, 1.);
+}
+
+technique stone <string target = "both";>
+{
+	pass p0
+	{
+ 		VertexProgram = compile arbvp1 vertex(mvp, mv, mvit);
+ 		FragmentProgram = compile arbfp1 light();
+	
+	}
+}
Index: java/client_3D/static/shared/res/shaders/candy.cgfx
===================================================================
--- java/client_3D/static/shared/res/shaders/candy.cgfx	(revision 65)
+++ java/client_3D/static/shared/res/shaders/candy.cgfx	(revision 65)
@@ -0,0 +1,66 @@
+float4x4 mvp : ModelViewProjection;
+float4x4 mv : ModelView;
+float4x4 mvit : ModelViewIT;
+float3 refFactor = float3(1.1,1.3,1.5);
+float fresOffset = 0.1;
+float fresScale = 0.3;
+float fresPower = 0.2;
+
+samplerCUBE skybox : SKYBOX;
+string jointGeo = "cylinder.obj";
+
+float4 vertex(uniform float4x4 modelViewProj,
+              uniform float4x4 modelView,
+              uniform float4x4 modelViewIT,
+              float4 P : POSITION,
+              float4 N : NORMAL,
+	      float2 uvIn : TEXCOORD0,
+		 in float4 Cin : COLOR0,
+
+              out float3 CC : COLOR0,
+			  out float2 uv : TEXCOORD0,
+              out float3 Pcam : TEXCOORD1,
+              out float3 Ncam : TEXCOORD2) : POSITION
+{
+    CC.xyz =  normalize(N.xyz);
+    uv = uvIn;
+    Pcam = mul(modelView, P).xyz;
+    Ncam = mul(modelView, N).xyz;
+    return mul(modelViewProj, P);
+}
+
+float3 toRGB(float hue)
+{
+	float r = max(min(hue*3-2, 1), 0) + max(min(1-hue*3, 1), 0);
+	float g = max(min(hue*3-0, 1), 0) - max(min(hue*3-1, 1), 0);
+	float b = max(min(hue*3-1, 1), 0) - max(min(hue*3-2, 1), 0);
+	return float3(r,g,b);
+}
+
+float3 toRGB2(float hue)
+{
+	float b = max(min(hue*2, 1), 0) - max(min(hue*2-1, 1), 0);
+	float g = 1-b;
+	return float3(0,g,b);
+}
+
+float4 rainbow(	  float4 col : COLOR0,
+                  float3 texcoord : TEXCOORD0,
+                  float3 Pcam : TEXCOORD1, 
+                  float3 Ncam : TEXCOORD2) : COLOR 
+{
+	float dist = sqrt(Pcam.x*Pcam.x + Pcam.y*Pcam.y + Pcam.z*Pcam.z);
+	float angle = dot(-normalize(Pcam), normalize(Ncam));
+	float factor = (cos(dist*70)+1)/2;  //see stronger 'interference' effect: (1-angle)*(cos(dist*10000)+1)/2;
+	float3 c = toRGB(factor);
+	return float4(c, 1);
+}
+
+technique red <string target = "both";>
+{
+	pass p0
+	{
+		VertexProgram = compile arbvp1 vertex(mvp, mv, mvit);
+		FragmentProgram = compile arbfp1 rainbow();
+	}
+}
Index: java/client_3D/static/shared/res/shaders/cartoon.cgfx
===================================================================
--- java/client_3D/static/shared/res/shaders/cartoon.cgfx	(revision 65)
+++ java/client_3D/static/shared/res/shaders/cartoon.cgfx	(revision 65)
@@ -0,0 +1,112 @@
+#include "res/shaders/lights.cg"
+float4x4 mvp : ModelViewProjection;
+float4x4 mv : ModelView;
+float4x4 mvit : ModelViewIT;
+
+
+sampler2D greenTex <string fileName = "green2.png";> = sampler_state {
+  minFilter = Linear;
+  magFilter = Linear;
+  wrapS = Repeat;
+  wrapT = Repeat;
+};
+sampler2D green2Tex<string fileName = "green4.png";> = sampler_state {
+  generateMipMap = true;
+  minFilter = Linear;
+  magFilter = Linear;
+  wrapS = Repeat;
+  wrapT = Repeat;
+};
+
+
+float4 vertex(uniform float offset,
+			  float4 P : POSITION,
+              float4 N : NORMAL,
+	      float2 uvIn : TEXCOORD0,
+		 in float4 Cin : COLOR0,
+              out float4 CC : COLOR0,
+              out float3 Pcam : TEXCOORD1,
+              out float3 Ncam : TEXCOORD2,
+	      out float2 uv : TEXCOORD0) : POSITION
+{
+    CC.xyz =  normalize(N.xyz);
+    float4 vert = P + N*offset;
+    Pcam = mul(mv, vert).xyz;
+    Ncam = mul(mvit, N).xyz;
+        uv = uvIn;
+    return mul(mvp, vert);
+}
+
+float4 line(	float4 col: COLOR0,
+                  in float3 texcoord,
+                  float3 Pcam : TEXCOORD1, 
+                  float3 Ncam : TEXCOORD2) : COLOR 
+{
+    float3 tan = normalize(col.xyz);
+    float3 wo = normalize(-Pcam);
+    float3 Nn = normalize(Ncam);
+    float d = abs(dot(Nn,wo));
+float3 result;
+    if (d < 0.3)
+		return float4(0,0,0,1);
+    else
+		discard;
+}
+
+float4 light(	uniform float4 col,
+                  in float3 texcoord,
+                  float3 Pcam : TEXCOORD1, 
+                  float3 Ncam : TEXCOORD2) : COLOR 
+{
+    float3 wo = normalize(-Pcam);
+    float4 result;// = ambient;
+    float3 Nn = normalize(Ncam);
+   
+    	 float4 scolor = float4(1.0,1.0,1.0,1.0);
+        float3 Ln = normalize(float3(0,0,-1)-Pcam);
+        float3 lcolor = float3(1,1,1);
+        float3 Hn = normalize(wo + Ln);
+        float ldn = dot(Ln,Nn);
+        float hdn = dot(Hn,Nn);
+        float4 litV = lit(ldn, hdn, 32);
+     
+    float ds,ss;
+	if (litV.y > 0.5)
+		ds = 1.0;
+	else 
+		ds = 0.3;
+	
+	if (litV.z > 0.5)
+		ss = 1.0;
+	else
+		ss = 0.0;
+	float lineS = abs(dot(wo,Nn));	        
+    if (lineS < 0.3)
+		lineS = 0;
+	else
+		lineS = 1.0;
+    result = col*ds + scolor*ss;
+    result *= lineS;
+    return result;
+}
+
+technique parts <string target = "both";>
+{
+	pass p0
+	{	
+		VertexProgram = compile arbvp1 vertex(0.0f);
+ 		FragmentProgram = compile arbfp1 light(float4(1.0,0.0,0.0,1.0));
+
+	}
+	/*
+	pass p1
+	{	
+		LineWidth = 5.0;
+		CullFaceEnable = true;
+		PolygonMode = int2(FrontAndBack,Line);
+		VertexProgram = compile arbvp1 vertex(0.01f);
+ 		FragmentProgram = compile arbfp1 line();
+
+	}
+	*/
+}
Index: java/client_3D/static/shared/res/shaders/default.cgfx
===================================================================
--- java/client_3D/static/shared/res/shaders/default.cgfx	(revision 65)
+++ java/client_3D/static/shared/res/shaders/default.cgfx	(revision 65)
@@ -0,0 +1,75 @@
+#include "res/shaders/lights.cg"
+float4x4 mvp : ModelViewProjection;
+float4x4 mv : ModelView;
+float4x4 mvit : ModelViewIT;
+
+
+sampler2D stoneTex <string fileName = "stone.jpg";> = sampler_state {
+  minFilter = Linear;
+  magFilter = Linear;
+  wrapS = Repeat;
+  wrapT = Repeat;
+};
+sampler2D stone2Tex<string fileName = "stone2.jpg";> = sampler_state {
+  generateMipMap = true;
+  minFilter = Linear;
+  magFilter = Linear;
+  wrapS = Repeat;
+  wrapT = Repeat;
+};
+
+float4 vertex(uniform float4x4 modelViewProj,
+              uniform float4x4 modelView,
+              uniform float4x4 modelViewIT,
+              float4 P : POSITION,
+              float4 N : NORMAL,
+	      float2 uvIn : TEXCOORD0,
+		 in float4 Cin : COLOR0,
+              out float4 CC : COLOR0,
+              out float3 Pcam : TEXCOORD1,
+              out float3 Ncam : TEXCOORD2,
+	      out float2 uv : TEXCOORD0) : POSITION
+{
+    CC.xyz =  normalize(N.xyz);
+    Pcam = mul(modelView, P).xyz;
+    //Ncam = mul(modelViewIT, N).xyz;
+    float3x3 rotation = (float3x3)modelView;
+    Ncam.xyz = mul(rotation,N.xyz);
+    
+    uv = uvIn;
+    return mul(modelViewProj, P);
+}
+
+float3 expand(float3 v) { return (v-0.5)*2; }
+
+float4 light(	float4 col: COLOR0,
+                  in float3 texcoord,
+                  float3 Pcam : TEXCOORD1, 
+                  float3 Ncam : TEXCOORD2) : COLOR 
+{
+    float3 tan = normalize(col.xyz);
+    float3 wo = normalize(-Pcam);
+    float3 result;// = ambient;
+    float3 Nn = normalize(Ncam);
+   
+    	 float3 scolor = float3(1.0,1.0,1.0);
+        float3 Ln = normalize(float3(0,0,-1)-Pcam);
+        float3 lcolor = float3(1,1,1);
+        float3 Hn = normalize(wo + Ln);
+        float ldn = dot(Ln,Nn);
+        float hdn = dot(Hn,Nn);
+        float4 litV = lit(ldn, hdn, 32);
+            
+    result = tex2D(stone2Tex ,texcoord)*litV.y;
+    return float4(result, 1.);
+}
+
+technique stone <string target = "both";>
+{
+	pass p0
+	{
+ 		VertexProgram = compile arbvp1 vertex(mvp, mv, mvit);
+ 		FragmentProgram = compile arbfp1 light();
+	
+	}
+}
Index: java/client_3D/static/shared/res/shaders/fur.cgfx
===================================================================
--- java/client_3D/static/shared/res/shaders/fur.cgfx	(revision 65)
+++ java/client_3D/static/shared/res/shaders/fur.cgfx	(revision 65)
@@ -0,0 +1,149 @@
+sampler2D diffuse <string fileName = "fur_diffuse_256.jpg";> = sampler_state {
+  minFilter = Linear;
+  magFilter = Linear;
+  wrapS = Repeat;
+  wrapT = Repeat;
+};
+sampler2D bump<string fileName = "fur_bump.tga";> = sampler_state {
+  generateMipMap = true;
+  minFilter = Linear;
+  magFilter = Linear;
+  wrapS = Repeat;
+  wrapT = Repeat;
+};
+
+
+float4x4 mvp : ModelViewProjection;
+float4x4 mv : ModelView;
+float4x4 mvit : ModelViewIT;
+
+
+
+float4 vertexDiff(float4 P : POSITION,
+              float4 N : NORMAL,
+	      float2 uvIn : TEXCOORD0,
+		 in float4 Cin : COLOR0,
+              out float4 CC : COLOR0,
+              out float3 Pcam : TEXCOORD1,
+              out float3 Ncam : TEXCOORD2,
+	      out float2 uv : TEXCOORD0) : POSITION
+{
+    CC.xyz =  normalize(N.xyz);
+    Pcam = mul(mv, P).xyz;
+    Ncam = mul(mvit, N).xyz;
+    return mul(mvp, P);
+}
+
+
+float4 light(	float4 col: COLOR0,
+                  in float3 texcoord,
+                  float3 Pcam : TEXCOORD1, 
+                  float3 Ncam : TEXCOORD2) : COLOR 
+{
+    float3 tan = normalize(col.xyz);
+    float3 wo = normalize(-Pcam);
+    float3 result;// = ambient;
+    float3 Nn = normalize(Ncam);
+   
+    	 float3 scolor = float3(1.0,1.0,1.0);
+        float3 Ln = normalize(float3(0,0,-1)-Pcam);
+        float3 lcolor = float3(1,1,1);
+        float3 Hn = normalize(wo + Ln);
+        float ldn = dot(Ln,Nn);
+        float hdn = dot(Hn,Nn);
+        float4 litV = lit(ldn, hdn, 32);
+            
+    result = tex2D(diffuse ,texcoord)*litV.y;
+    return float4(result, 1.);
+}
+
+float4 vertex(uniform float scale,
+              float4 P : POSITION,
+              float4 N : NORMAL,
+	      float2 uvIn : TEXCOORD0,
+		 in float4 Cin : COLOR0,
+              out float4 CC : COLOR0,
+              out float3 Pcam : TEXCOORD1,
+              out float3 Ncam : TEXCOORD2,
+	      out float2 uv : TEXCOORD0) : POSITION
+{
+	float4 vert = P + float4(N.xyz*scale, 0);
+	uv = uvIn;
+	uv *= 0.25;
+	Ncam = mul(mvit, N).xyz;
+	Pcam = mul(mv, vert);
+	return mul(mvp, vert); 
+}
+
+
+float4 fragment(	in float3 uv,
+                  float3 Pcam : TEXCOORD1, 
+                  float3 Ncam : TEXCOORD2) : COLOR 
+{
+    float3 wo = normalize(-Pcam);
+    float3 result;// = ambient;
+    float3 alpha = tex2D(bump, uv*2).xyz;
+    if (alpha.r + alpha.g + alpha.b < 1.5) discard;
+    return tex2D(diffuse,uv);
+}
+
+technique red <string target = "both";>
+{
+	pass p110
+	{
+		VertexProgram = compile arbvp1 vertexDiff();
+ 		FragmentProgram = compile arbfp1 light();
+	}
+
+	pass p0
+	{
+		VertexProgram = compile arbvp1 vertex(0.012);
+ 		FragmentProgram = compile arbfp1 fragment();
+	}
+	pass p1
+	{
+		VertexProgram = compile arbvp1 vertex(0.024);
+ 		FragmentProgram = compile arbfp1 fragment();
+	}
+	pass p2
+	{
+ 		VertexProgram = compile arbvp1 vertex(0.036);
+ 		FragmentProgram = compile arbfp1 fragment();
+	}
+	pass p3
+	{
+ 		VertexProgram = compile arbvp1 vertex(0.048);
+ 		FragmentProgram = compile arbfp1 fragment();
+	}
+	pass p4
+	{
+		VertexProgram = compile arbvp1 vertex(0.06);
+ 		FragmentProgram = compile arbfp1 fragment();
+	}
+	pass p5
+	{
+		VertexProgram = compile arbvp1 vertex(0.072);
+ 		FragmentProgram = compile arbfp1 fragment();
+	}
+	pass p6
+	{
+		VertexProgram = compile arbvp1 vertex(0.084);
+ 		FragmentProgram = compile arbfp1 fragment();
+	}
+	pass p7
+	{
+		VertexProgram = compile arbvp1 vertex(0.096);
+ 		FragmentProgram = compile arbfp1 fragment();
+	}
+	pass p8
+	{
+		VertexProgram = compile arbvp1 vertex(0.108);
+ 		FragmentProgram = compile arbfp1 fragment();
+	}
+	pass p9
+	{
+ 		VertexProgram = compile arbvp1 vertex(0.12);
+ 		FragmentProgram = compile arbfp1 fragment();
+	}
+}
+
Index: java/client_3D/static/shared/res/shaders/lights.cg
===================================================================
--- java/client_3D/static/shared/res/shaders/lights.cg	(revision 65)
+++ java/client_3D/static/shared/res/shaders/lights.cg	(revision 65)
@@ -0,0 +1,29 @@
+#ifndef __LIGHTS
+#define __LIGHTS
+
+interface Light {
+    float3 illuminate(float3 p, out float3 L);
+};
+
+struct PointLight : Light {
+    float3 Plight, Clight;
+    float3 illuminate(float3 P, out float3 L) {
+        L = normalize(Plight - P);
+        return Clight;
+    }
+};
+
+struct SpotLight : Light {
+    float3 Plight, Clight, target;
+    float3 illuminate(float3 P, out float3 L) {
+        L = normalize(Plight - P);
+        float3 shine = normalize(Plight - target);
+        float costheta = dot(L, shine);
+        return Clight * smoothstep(0.98, 1., costheta);
+    }
+};
+
+
+
+
+#endif
Index: java/client_3D/static/shared/res/shaders/metal.cgfx
===================================================================
--- java/client_3D/static/shared/res/shaders/metal.cgfx	(revision 65)
+++ java/client_3D/static/shared/res/shaders/metal.cgfx	(revision 65)
@@ -0,0 +1,85 @@
+float4x4 mvp : ModelViewProjection;
+float4x4 mv : ModelView;
+float4x4 mvit : ModelViewIT;
+samplerCUBE skybox : SKYBOX;
+string jointGeo = "cylinder.obj";
+sampler2D normal<string fileName = "normal.tga";> = sampler_state {
+  generateMipMap = true;
+  minFilter = Linear;
+  magFilter = Linear;
+  wrapS = Repeat;
+  wrapT = Repeat;
+};
+
+float3 expand(float3 v) { return (v-0.5)*2; }
+float4 vertex(uniform float4x4 modelViewProj,
+              uniform float4x4 modelView,
+              uniform float4x4 modelViewIT,
+              float4 P : POSITION,
+              float4 N : NORMAL,
+	      float2 uvIn : TEXCOORD0,
+		 in float4 Cin : COLOR0,
+              out float4 CC : COLOR0,
+              out float3 Pcam : TEXCOORD1,
+              out float3 Ncam : TEXCOORD2,
+	      out float2 uv : TEXCOORD0) : POSITION
+{
+    CC.xyz =  normalize(N.xyz);
+    Pcam = mul(modelView, P).xyz;
+    Ncam = mul(modelViewIT, N).xyz;
+    //float3x3 rotation = (float3x3)modelView;
+    //Ncam.xyz = mul(rotation,N.xyz);
+    
+    uv = uvIn;
+    return mul(modelViewProj, P);
+}
+
+
+float4 light(	uniform float4 col,
+                  in float3 texcoord,
+                  float3 Pcam : TEXCOORD1, 
+                  float3 Ncam : TEXCOORD2) : COLOR 
+{
+	float3 tan = normalize(col.xyz);
+    float3 wo = normalize(-Pcam);
+    float3 result;// = ambient;
+    float3 Nn = normalize(Ncam);
+    float3 Nn2 = Nn; 
+float3 biNorm = normalize(cross(tan,Nn));
+    float3x3 toTexture = transpose(float3x3(tan,biNorm,Nn));
+
+	Nn = mul(toTexture,expand(tex2D(normal,texcoord)));
+	Nn = normalize(Nn+2*Nn2);
+   
+    	 float3 scolor = float3(1.0,1.0,1.0);
+        float3 Ln = normalize(float3(0,0,-1)-Pcam);
+        float3 lcolor = float3(1,1,1);
+        float3 Hn = normalize(wo + Ln);
+        float ldn = dot(Ln,Nn);
+        float hdn = dot(Hn,Nn);
+        float4 litV = lit(ldn, hdn, 32);
+     
+   col = texCUBE(skybox,Nn);       
+    result = col*litV.y + scolor*litV.z;
+    return float4(result, 1.);
+}
+
+technique red <string target = "joints";>
+{
+	pass p0
+	{
+ 		VertexProgram = compile arbvp1 vertex(mvp, mv, mvit);
+ 		FragmentProgram = compile arbfp1 light(float4(1.0, 0.0 ,0.0 ,1.0));
+	
+	}
+}
+
+technique red <string target = "parts";>
+{
+	pass p0
+	{
+ 		VertexProgram = compile arbvp1 vertex(mvp, mv, mvit);
+ 		FragmentProgram = compile arbfp1 light(float4(0.0, 1.0 ,0.0 ,1.0));
+	
+	}
+}
Index: java/client_3D/static/shared/res/shaders/old_diffuse_light.glsl
===================================================================
--- java/client_3D/static/shared/res/shaders/old_diffuse_light.glsl	(revision 65)
+++ java/client_3D/static/shared/res/shaders/old_diffuse_light.glsl	(revision 65)
@@ -0,0 +1,32 @@
+//name diffuse_light
+//vert
+varying vec3 ecPos;
+varying vec3 ecNormal;			
+void main()
+{	
+	gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;
+	gl_TexCoord[0].st = gl_MultiTexCoord0.ts;
+	ecNormal = gl_NormalMatrix * gl_Normal;
+	vec4 ecPos4 = gl_ModelViewMatrix * gl_Vertex;
+	ecPos = ecPos4.xyz/ecPos4.w;
+}
+
+//frag
+uniform sampler2D diffuseMap;
+varying vec3 ecPos;
+varying vec3 ecNormal;
+vec3 ecEye;
+vec3 ecLight;
+
+float diffuse(int lightId)
+{
+	ecLight = gl_LightSource[lightId].position.xyz - ecPos;
+	return max(0.2, dot(normalize(ecNormal), normalize(ecLight)));
+}
+
+void main()
+{					
+	ecEye = normalize(-ecPos);				
+	gl_FragColor = diffuse(0) * texture2D(diffuseMap, gl_TexCoord[0].st);
+}
+
Index: java/client_3D/static/shared/res/shaders/old_fur.glsl
===================================================================
--- java/client_3D/static/shared/res/shaders/old_fur.glsl	(revision 65)
+++ java/client_3D/static/shared/res/shaders/old_fur.glsl	(revision 65)
@@ -0,0 +1,38 @@
+//name fur
+//vert
+uniform float scale;
+varying vec3 ecPos;
+varying vec3 ecNormal;			
+void main()
+{	
+	vec4 vert = gl_Vertex + vec4(gl_Normal.x*scale, gl_Normal.y*scale, gl_Normal.z*scale, 0);
+	gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * vert;
+	gl_TexCoord[0].st = gl_MultiTexCoord0.ts;
+	gl_TexCoord[0].st *= 0.25;
+	ecNormal = gl_NormalMatrix * gl_Normal;
+	vec4 ecPos4 = gl_ModelViewMatrix * gl_Vertex;
+	ecPos = ecPos4.xyz/ecPos4.w;
+}
+
+//frag
+uniform sampler2D diffuseMap;
+uniform sampler2D furMap;
+varying vec3 ecPos;
+varying vec3 ecNormal;
+vec3 ecEye;
+vec3 ecLight;
+
+float diffuse(int lightId)
+{
+	ecLight = gl_LightSource[lightId].position.xyz - ecPos;
+	return max(0.2, dot(normalize(ecNormal), normalize(ecLight)));
+}
+
+void main()
+{					
+	ecEye = normalize(-ecPos);	
+	vec3 alpha = texture2D(furMap, vec2(gl_TexCoord[0].s * 2.0, gl_TexCoord[0].t * 2.0)).xyz;
+	if (alpha.r + alpha.g + alpha.b < 1.5) discard;			
+	gl_FragColor = diffuse(0) * texture2D(diffuseMap, gl_TexCoord[0].st);;
+}
+
Index: java/client_3D/static/shared/res/shaders/old_normal_mapping.glsl
===================================================================
--- java/client_3D/static/shared/res/shaders/old_normal_mapping.glsl	(revision 65)
+++ java/client_3D/static/shared/res/shaders/old_normal_mapping.glsl	(revision 65)
@@ -0,0 +1,67 @@
+//vert
+	
+varying vec3 lightVec; 
+varying vec3 eyeVec;
+varying vec2 texCoord;
+attribute vec3 tangent; 
+					 
+
+void main(void)
+{
+	gl_Position = ftransform();
+	texCoord = gl_MultiTexCoord0.xy * 0.4;
+	
+	vec3 n = normalize(gl_NormalMatrix * gl_Normal);
+	vec3 t = normalize(gl_NormalMatrix * tangent);
+	vec3 b = cross(n, t);
+	
+	vec3 vVertex = vec3(gl_ModelViewMatrix * gl_Vertex);
+	vec3 tmpVec = gl_LightSource[0].position.xyz - vVertex;
+
+	lightVec.x = dot(tmpVec, t);
+	lightVec.y = dot(tmpVec, b);
+	lightVec.z = dot(tmpVec, n);
+
+	tmpVec = -vVertex;
+	eyeVec.x = dot(tmpVec, t);
+	eyeVec.y = dot(tmpVec, b);
+	eyeVec.z = dot(tmpVec, n);
+}
+	
+//frag
+
+varying vec3 lightVec;
+varying vec3 eyeVec;
+varying vec2 texCoord;
+uniform sampler2D diffuseMap;
+uniform sampler2D normalMap;
+//uniform float invRadius;
+
+void main (void)
+{
+	float invRadius = 0.2;
+	
+	float distSqr = dot(lightVec, lightVec);
+	float att = clamp(1.0 - invRadius * sqrt(distSqr), 0.0, 1.0);
+	vec3 lVec = lightVec * inversesqrt(distSqr);
+
+	vec3 vVec = normalize(eyeVec);
+	
+	vec4 base = texture2D(diffuseMap, texCoord);
+	
+	vec3 bump = normalize( texture2D(normalMap, texCoord).xyz * 2.0 - 1.0);
+
+	vec4 vAmbient = vec4(0.8, 0.5, 0.2, 0.0);//gl_LightSource[0].ambient * gl_FrontMaterial.ambient;
+
+	float diffuse = max( dot(lVec, bump), 0.0 );
+	
+	vec4 vDiffuse = gl_LightSource[0].diffuse * gl_FrontMaterial.diffuse * diffuse;	
+
+	float specular = pow(clamp(dot(reflect(-lVec, bump), vVec), 0.0, 1.0), gl_FrontMaterial.shininess );
+	
+	vec4 vSpecular = gl_LightSource[0].specular * gl_FrontMaterial.specular * specular;	
+	
+	gl_FragColor = ( vAmbient*base + 
+					 vDiffuse*base + 
+					 vSpecular) * att;
+}
Index: java/client_3D/static/shared/res/shaders/old_std.cg
===================================================================
--- java/client_3D/static/shared/res/shaders/old_std.cg	(revision 65)
+++ java/client_3D/static/shared/res/shaders/old_std.cg	(revision 65)
@@ -0,0 +1,25 @@
+#ifndef __STD_H
+#define __STD_H
+
+float4 vertex(uniform float4x4 modelViewProj,
+              uniform float4x4 modelView,
+              uniform float4x4 modelViewIT,
+              float4 P : POSITION,
+              float4 N : NORMAL,
+	      float2 uvIn : TEXCOORD0,
+              out float4 CC : COLOR0,
+              out float3 Pcam : TEXCOORD1,
+              out float3 Ncam : TEXCOORD2,
+	      out float2 uv : TEXCOORD0) : POSITION
+{
+    CC.xyz =  normalize(N.xyz);
+    Pcam = mul(modelView, P).xyz;
+    //Ncam = mul(modelViewIT, N).xyz;
+    float3x3 rotation = (float3x3)modelView;
+    Ncam.xyz = mul(rotation,N.xyz);
+    
+    uv = uvIn;
+    return mul(modelViewProj, P);
+}
+
+#endif
Index: java/client_3D/static/shared/res/shaders/old_stone.glsl
===================================================================
--- java/client_3D/static/shared/res/shaders/old_stone.glsl	(revision 65)
+++ java/client_3D/static/shared/res/shaders/old_stone.glsl	(revision 65)
@@ -0,0 +1,27 @@
+//name stone
+//vert
+	
+varying vec2 texCoord;
+varying vec3 normalVec; 
+
+void main(void)
+{
+	gl_Position = ftransform();
+	texCoord = gl_MultiTexCoord0.xy * 0.4;
+	normalVec = normalize(gl_NormalMatrix * gl_Normal);
+}
+	
+//frag
+
+varying vec2 texCoord;
+uniform sampler2D diffuseMap;
+varying vec3 normalVec;
+
+void main (void)
+{
+	vec4 base = texture2D(diffuseMap, texCoord);
+	
+	float c = dot(normalVec, vec3(0,0,-1));
+	c = abs(c);
+	gl_FragColor = base * (1.0-c);
+}
Index: java/client_3D/static/shared/res/shaders/plastic.cgfx
===================================================================
--- java/client_3D/static/shared/res/shaders/plastic.cgfx	(revision 65)
+++ java/client_3D/static/shared/res/shaders/plastic.cgfx	(revision 65)
@@ -0,0 +1,69 @@
+float4x4 mvp : ModelViewProjection;
+float4x4 mv : ModelView;
+float4x4 mvit : ModelViewIT;
+
+
+
+float4 vertex(uniform float4x4 modelViewProj,
+              uniform float4x4 modelView,
+              uniform float4x4 modelViewIT,
+              float4 P : POSITION,
+              float4 N : NORMAL,
+	      float2 uvIn : TEXCOORD0,
+		 in float4 Cin : COLOR0,
+              out float4 CC : COLOR0,
+              out float3 Pcam : TEXCOORD1,
+              out float3 Ncam : TEXCOORD2,
+	      out float2 uv : TEXCOORD0) : POSITION
+{
+    CC.xyz =  normalize(N.xyz);
+    Pcam = mul(modelView, P).xyz;
+    //Ncam = mul(modelViewIT, N).xyz;
+    float3x3 rotation = (float3x3)modelView;
+    Ncam.xyz = mul(rotation,N.xyz);
+    
+    uv = uvIn;
+    return mul(modelViewProj, P);
+}
+
+
+float4 light(	uniform float4 col,
+                  in float3 texcoord,
+                  float3 Pcam : TEXCOORD1, 
+                  float3 Ncam : TEXCOORD2) : COLOR 
+{
+    float3 wo = normalize(-Pcam);
+    float3 result;// = ambient;
+    float3 Nn = normalize(Ncam);
+   
+    	 float3 scolor = float3(1.0,1.0,1.0);
+        float3 Ln = normalize(float3(0,0,-1)-Pcam);
+        float3 lcolor = float3(1,1,1);
+        float3 Hn = normalize(wo + Ln);
+        float ldn = dot(Ln,Nn);
+        float hdn = dot(Hn,Nn);
+        float4 litV = lit(ldn, hdn, 32);
+            
+    result = col*litV.y + scolor*litV.z;
+    return float4(result, 1.);
+}
+
+technique red <string target = "joints";>
+{
+	pass p0
+	{
+ 		VertexProgram = compile arbvp1 vertex(mvp, mv, mvit);
+ 		FragmentProgram = compile arbfp1 light(float4(1.0, 0.0 ,0.0 ,1.0));
+	
+	}
+}
+
+technique red <string target = "parts";>
+{
+	pass p0
+	{
+ 		VertexProgram = compile arbvp1 vertex(mvp, mv, mvit);
+ 		FragmentProgram = compile arbfp1 light(float4(0.0, 1.0 ,0.0 ,1.0));
+	
+	}
+}
Index: java/client_3D/static/shared/res/shaders/refraction.cgfx
===================================================================
--- java/client_3D/static/shared/res/shaders/refraction.cgfx	(revision 65)
+++ java/client_3D/static/shared/res/shaders/refraction.cgfx	(revision 65)
@@ -0,0 +1,92 @@
+float4x4 mvp : ModelViewProjection;
+float4x4 mv : ModelView;
+float4x4 mvit : ModelViewIT;
+float3 refFactor = float3(1.1,1.3,1.5);
+float fresOffset = 0.1;
+float fresScale = 0.3;
+float fresPower = 0.2;
+
+samplerCUBE skybox : SKYBOX;
+string jointGeo = "cylinder.obj";
+sampler2D normal<string fileName = "normal.tga";> = sampler_state {
+  generateMipMap = true;
+  minFilter = Linear;
+  magFilter = Linear;
+  wrapS = Repeat;
+  wrapT = Repeat;
+};
+
+float3 expand(float3 v) { return (v-0.5)*2; }
+float4 vertex(uniform float4x4 modelViewProj,
+              uniform float4x4 modelView,
+              uniform float4x4 modelViewIT,
+              float4 P : POSITION,
+              float4 N : NORMAL,
+	      float2 uvIn : TEXCOORD0,
+		 in float4 Cin : COLOR0,
+
+              out float3 CC : COLOR0,
+			  out float2 uv : TEXCOORD0,
+              out float3 Pcam : TEXCOORD1,
+              out float3 Ncam : TEXCOORD2) : POSITION
+{
+    CC.xyz =  normalize(N.xyz);
+    uv = uvIn;
+    Pcam = mul(modelView, P).xyz;
+    Ncam = mul(modelViewIT, N).xyz;
+    return mul(modelViewProj, P);
+}
+
+
+float4 light(	uniform float4 col,
+                  in float3 texcoord,
+                  float3 Pcam : TEXCOORD1, 
+                  float3 Ncam : TEXCOORD2) : COLOR 
+{
+	float3 tan = normalize(col.xyz);
+    float3 wo = normalize(-Pcam);
+    float3 result;// = ambient;
+    float3 Nn = normalize(Ncam);
+    float3 Nn2 = Nn; 
+float3 biNorm = normalize(cross(tan,Nn));
+    float3x3 toTexture = transpose(float3x3(tan,biNorm,Nn));
+
+	Nn = mul(toTexture,expand(tex2D(normal,texcoord)));
+	Nn = normalize(Nn+2*Nn2);
+
+   float3 r, tred, tblue, tgreen;   
+   r = reflect(Pcam,Nn);
+   tred = refract(Pcam,Nn,refFactor.r);
+   tgreen = refract(Pcam,Nn,refFactor.g);
+   tblue = refract(Pcam,Nn,refFactor.b);
+    
+   r = texCUBE(skybox,r.rgb).rgb; 
+   col.r = texCUBE(skybox,tred).r;
+   col.g = texCUBE(skybox,tred).g; 
+   col.b = texCUBE(skybox,tred).b; 
+   float fres = fresOffset + fresScale * pow(1 + dot(normalize(Pcam), Nn), 
+                                        fresPower );
+   //col.rgb = r.rgb;
+   return float4(lerp(col.rgb, r.rgb, fres).rgb,1);                                     
+   //return col;
+}
+
+technique red <string target = "joints";>
+{
+	pass p0
+	{
+ 		VertexProgram = compile arbvp1 vertex(mvp, mv, mvit);
+ 		FragmentProgram = compile arbfp1 light(float4(1.0, 0.0 ,0.0 ,1.0));
+	
+	}
+}
+
+technique red <string target = "parts";>
+{
+	pass p0
+	{
+ 		VertexProgram = compile arbvp1 vertex(mvp, mv, mvit);
+ 		FragmentProgram = compile arbfp1 light(float4(0.0, 1.0 ,0.0 ,1.0));
+	
+	}
+}
Index: java/client_3D/static/win/run.bat
===================================================================
--- java/client_3D/static/win/run.bat	(revision 65)
+++ java/client_3D/static/win/run.bat	(revision 65)
@@ -0,0 +1,1 @@
+java -classpath lib/jogl.jar;lib/foxtrot.jar;lib/framsticks-jogl.jar;lib/gluegen-rt.jar -Djava.library.path=lib  com.framsticks.net.client3D.App
