commit 9e53abbb7ac71afbccd9f58ec9a964cf84bd16f5
Author: nor..67
Date: Mon Mar 14 12:55:32 2011 +0000
move jme3 to trunk
git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@6971 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
diff --git a/engine/.classpath b/engine/.classpath
new file mode 100644
index 000000000..1fecfb45c
--- /dev/null
+++ b/engine/.classpath
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/engine/.project b/engine/.project
new file mode 100644
index 000000000..8d207ff7c
--- /dev/null
+++ b/engine/.project
@@ -0,0 +1,17 @@
+
+
+ jme3
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/engine/MANIFEST.MF b/engine/MANIFEST.MF
new file mode 100644
index 000000000..f6d6f5653
--- /dev/null
+++ b/engine/MANIFEST.MF
@@ -0,0 +1 @@
+X-Comment: jMonkeyEngine 3.0
\ No newline at end of file
diff --git a/engine/TestChooser.exe b/engine/TestChooser.exe
new file mode 100644
index 000000000..00f0161d1
Binary files /dev/null and b/engine/TestChooser.exe differ
diff --git a/engine/build-android.xml b/engine/build-android.xml
new file mode 100644
index 000000000..a5394ed4f
--- /dev/null
+++ b/engine/build-android.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/engine/build.xml b/engine/build.xml
new file mode 100644
index 000000000..5805ecba1
--- /dev/null
+++ b/engine/build.xml
@@ -0,0 +1,223 @@
+
+
+
+ Builds, tests, and runs the project jME3_ordered.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/engine/lib/CopyLibs/org-netbeans-modules-java-j2seproject-copylibstask.jar b/engine/lib/CopyLibs/org-netbeans-modules-java-j2seproject-copylibstask.jar
new file mode 100644
index 000000000..47dcc3b3a
Binary files /dev/null and b/engine/lib/CopyLibs/org-netbeans-modules-java-j2seproject-copylibstask.jar differ
diff --git a/engine/lib/JWSAntTasks/org-netbeans-modules-javawebstart-anttasks.jar b/engine/lib/JWSAntTasks/org-netbeans-modules-javawebstart-anttasks.jar
new file mode 100644
index 000000000..292c9ed28
Binary files /dev/null and b/engine/lib/JWSAntTasks/org-netbeans-modules-javawebstart-anttasks.jar differ
diff --git a/engine/lib/android/android.jar b/engine/lib/android/android.jar
new file mode 100644
index 000000000..24ae5637d
Binary files /dev/null and b/engine/lib/android/android.jar differ
diff --git a/engine/lib/antlibs/jsch-0.1.42.jar b/engine/lib/antlibs/jsch-0.1.42.jar
new file mode 100644
index 000000000..c65eff095
Binary files /dev/null and b/engine/lib/antlibs/jsch-0.1.42.jar differ
diff --git a/engine/lib/jbullet/asm-all-3.1.jar b/engine/lib/jbullet/asm-all-3.1.jar
new file mode 100644
index 000000000..5f37af522
Binary files /dev/null and b/engine/lib/jbullet/asm-all-3.1.jar differ
diff --git a/engine/lib/jbullet/jbullet.jar b/engine/lib/jbullet/jbullet.jar
new file mode 100644
index 000000000..43926d5df
Binary files /dev/null and b/engine/lib/jbullet/jbullet.jar differ
diff --git a/engine/lib/jbullet/stack-alloc.jar b/engine/lib/jbullet/stack-alloc.jar
new file mode 100644
index 000000000..ab1d988ce
Binary files /dev/null and b/engine/lib/jbullet/stack-alloc.jar differ
diff --git a/engine/lib/jbullet/vecmath.jar b/engine/lib/jbullet/vecmath.jar
new file mode 100644
index 000000000..ddfd73bdb
Binary files /dev/null and b/engine/lib/jbullet/vecmath.jar differ
diff --git a/engine/lib/jheora/LICENSE.jheora b/engine/lib/jheora/LICENSE.jheora
new file mode 100644
index 000000000..b40af560b
--- /dev/null
+++ b/engine/lib/jheora/LICENSE.jheora
@@ -0,0 +1,23 @@
+/* Jheora
+ * Copyright (C) 2004 Fluendo S.L.
+ *
+ * Written by: 2004 Wim Taymans
+ *
+ * Many thanks to
+ * The Xiph.Org Foundation http://www.xiph.org/
+ * Jheora was based on their Theora reference decoder.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
diff --git a/engine/lib/jheora/cortado-0.6.0.tar.gz b/engine/lib/jheora/cortado-0.6.0.tar.gz
new file mode 100644
index 000000000..a8b57300f
Binary files /dev/null and b/engine/lib/jheora/cortado-0.6.0.tar.gz differ
diff --git a/engine/lib/jheora/jheora-debug-0.6.0.jar b/engine/lib/jheora/jheora-debug-0.6.0.jar
new file mode 100644
index 000000000..f7d83e6fc
Binary files /dev/null and b/engine/lib/jheora/jheora-debug-0.6.0.jar differ
diff --git a/engine/lib/jheora/jheora-jst-debug-0.6.0.jar b/engine/lib/jheora/jheora-jst-debug-0.6.0.jar
new file mode 100644
index 000000000..fc1c0a34e
Binary files /dev/null and b/engine/lib/jheora/jheora-jst-debug-0.6.0.jar differ
diff --git a/engine/lib/jogg/j-ogg-oggd.jar b/engine/lib/jogg/j-ogg-oggd.jar
new file mode 100644
index 000000000..1939ee0e7
Binary files /dev/null and b/engine/lib/jogg/j-ogg-oggd.jar differ
diff --git a/engine/lib/jogg/j-ogg-vorbisd.jar b/engine/lib/jogg/j-ogg-vorbisd.jar
new file mode 100644
index 000000000..77e576931
Binary files /dev/null and b/engine/lib/jogg/j-ogg-vorbisd.jar differ
diff --git a/engine/lib/jogl/CHANGELOG.txt b/engine/lib/jogl/CHANGELOG.txt
new file mode 100644
index 000000000..1455bec55
--- /dev/null
+++ b/engine/lib/jogl/CHANGELOG.txt
@@ -0,0 +1,101 @@
+Changes between JOGL 1.1.0 and 1.1.1:
+
+ - Fixed a bug in the checking of incoming buffers' sizes to
+ glTexImage1D, glTexImage2D, and glTexImage3D.
+
+Changes between JOGL 1.0.0 and 1.1.0:
+
+ - The glext.h and associated header files JOGL uses have been updated
+ to OpenGL 2.1 with NVidia's GeForce 8 series extensions. The new
+ functions are available as methods in the GL interface.
+
+ - The developer build bundles have been changed to zip archives, so
+ instead of having to download multiple jars, you can now just
+ download the zip archive for your particular platform. The new zip
+ archives are versioned with the build date.
+
+ - The source distribution now contains the generated sources like
+ GL.java, GLU.java, etc. for more convenient use in IDEs.
+
+ - The chosen GLCapabilities are now exposed from the GLDrawable via
+ GLDrawable.getChosenGLCapabilities(); this functionality works on
+ all platforms even in cases where the GLCapabilitiesChooser is not
+ supported, and attempts to provide correct answers so programs can
+ make decisions based on the results.
+
+ - The native code for the "DRI hack" (to support the open-source DRI
+ drivers on Linux and other X11 platforms) has been removed; JOGL
+ now uses the GlueGen NativeLibrary class for this purpose.
+ Reliability improvements have been made to the implementation of
+ this class; it has been confirmed as working again with ATI's
+ proprietary drivers on Linux and should also work better with
+ NVidia's drivers.
+
+ - The GlueGen runtime classes have been removed from jogl.jar. These
+ have been factored out into gluegen-rt.jar and are referenced by
+ both the JOGL and JOAL projects.
+
+ - Thanks to John Burkey some optimizations have been made to the
+ buffer object-related validity checks in glVertexPointer, etc. as
+ well as a buffer size query that was being made in the glMapBuffer
+ implementation. This improves performance for applications
+ performing a lot of VBO- or vertex array-based rendering, in
+ particular with the multithreaded OpenGL implementation on Mac OS
+ X.
+
+ - The JOGL applet launcher now supports deployment of applets which
+ use both OpenGL for 3D graphics via JOGL as well as OpenAL for
+ spatialized audio via JOAL. It now prompts the user on Windows
+ platforms to allow it to enable the -Dsun.java2d.noddraw=true
+ system property for best robustness. It has been updated for the
+ changes in the GlueGen runtime classes and native library
+ structure. Some bugs have been fixed, some of which were preventing
+ different JOGL-based applets from being deployed from the same
+ codebase. The documentation and on-line examples have been updated
+ as well.
+
+ - The TextureIO implementation has been changed to no longer copy the
+ data associated with BufferedImage TextureData objects. Instead,
+ the necessary vertical flip is now implemented by flipping the
+ texture coordinates vertically.
+
+ - An API for updating a sub-image of a Texture object from a
+ sub-portion of a TextureData object has been added.
+
+ - A GLContext.copy() operation has been added based on community
+ feedback.
+
+ - Three helper classes have been added to the com.sun.opengl.util.j2d
+ package to improve interoperability between JOGL and Java 2D:
+ TextureRenderer, Overlay and TextRenderer. The TextureRenderer
+ supports drawing into an OpenGL texture using Java 2D. The Overlay
+ class provides a convenient Java 2D-based overlay on top of an
+ arbitrary GLDrawable. The TextRenderer class supports drawing of
+ Java 2D text into an OpenGL context. Thanks to Chris Campbell of
+ the Java 2D team for collaboration and to the JOGL community for
+ extensive feedback and testing assistance.
+
+ - Various bug fixes and robustness improvements were made to the
+ GlueGen runtime, JOGL and GLU implementations.
+
+ - Fixes to the DDSImage class were contributed by the community: a
+ bug fix to mipmap handling and support for cubemap textures. Thanks
+ to java.net user bandures.
+
+ - TextureIO.setTexRectEnabled() and isTexRectEnabled() were added
+ based on feedback from Chris Campbell, in order to simplify the
+ writing of pixel shaders which have different samplers for
+ GL_TEXTURE_2D and GL_TEXTURE_RECTANGLE_ARB targets.
+
+ - Thanks to Erik Tollerud, the links to the OpenGL documentation in
+ the JOGL javadoc were revised to point to the new on-line man pages
+ in the OpenGL SDK.
+
+ - Support for automatic mipmap generation via GL_GENERATE_MIPMAP was
+ added to the TextureIO, TextureRenderer and TextRenderer classes.
+
+ - Windows/AMD64 binaries, including the JOGL Cg binding, are now
+ supplied.
+
+ - Worked around breakage of JOGL with 5.0u10; see Sun bug IDs 6504460
+ and 6333613.
diff --git a/engine/lib/jogl/COPYRIGHT.txt b/engine/lib/jogl/COPYRIGHT.txt
new file mode 100644
index 000000000..360d3748b
--- /dev/null
+++ b/engine/lib/jogl/COPYRIGHT.txt
@@ -0,0 +1,31 @@
+
+Copyright 2007 Sun Microsystems, Inc., 4150 Network
+Circle, Santa Clara, California 95054, U.S.A. All rights
+reserved.
+
+U.S. Government Rights - Commercial software. Government
+users are subject to the Sun Microsystems, Inc.
+standard license agreement and applicable provisions of
+the FAR and its supplements.
+
+Use is subject to license terms.
+
+This distribution may include materials developed by third
+parties.
+
+Sun, Sun Microsystems, the Sun logo and Java are trademarks
+or registered trademarks of Sun Microsystems, Inc. in the
+U.S. and other countries.
+
+OpenGL is a registered trademark of Silicon Graphics, Inc.
+
+This product is covered and controlled by U.S. Export
+Control laws and may be subject to the export or import
+laws in other countries. Nuclear, missile, chemical
+biological weapons or nuclear maritime end uses or end
+users, whether direct or indirect, are strictly prohibited.
+Export or reexport to countries subject to U.S. embargo or
+to entities identified on U.S. export exclusion lists,
+including, but not limited to, the denied persons and
+specially designated nationals lists is strictly prohibited.
+
diff --git a/engine/lib/jogl/LICENSE-JOGL-1.1.1.txt b/engine/lib/jogl/LICENSE-JOGL-1.1.1.txt
new file mode 100644
index 000000000..cd35e8827
--- /dev/null
+++ b/engine/lib/jogl/LICENSE-JOGL-1.1.1.txt
@@ -0,0 +1,152 @@
+JOGL is released under the BSD license. The full license terms follow:
+
+ Copyright (c) 2003-2007 Sun Microsystems, Inc. All Rights Reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ - Redistribution of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ - Redistribution in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ Neither the name of Sun Microsystems, Inc. or the names of
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ This software is provided "AS IS," without a warranty of any kind. ALL
+ EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
+ MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
+ ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
+ ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
+ DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
+ DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
+ ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
+ SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+ You acknowledge that this software is not designed or intended for use
+ in the design, construction, operation or maintenance of any nuclear
+ facility.
+
+The JOGL source tree contains code ported from the OpenGL sample
+implementation by Silicon Graphics, Inc. This code is licensed under
+the SGI Free Software License B (Sun is redistributing the modified code
+under a slightly modified, alternative license, which is described two
+paragraphs below after "NOTE:"):
+
+ License Applicability. Except to the extent portions of this file are
+ made subject to an alternative license as permitted in the SGI Free
+ Software License B, Version 1.1 (the "License"), the contents of this
+ file are subject only to the provisions of the License. You may not use
+ this file except in compliance with the License. You may obtain a copy
+ of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
+ Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
+
+ http://oss.sgi.com/projects/FreeB
+
+ Note that, as provided in the License, the Software is distributed on an
+ "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
+ DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
+ CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
+ PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
+
+ NOTE: The Original Code (as defined below) has been licensed to Sun
+ Microsystems, Inc. ("Sun") under the SGI Free Software License B
+ (Version 1.1), shown above ("SGI License"). Pursuant to Section
+ 3.2(3) of the SGI License, Sun is distributing the Covered Code to
+ you under an alternative license ("Alternative License"). This
+ Alternative License includes all of the provisions of the SGI License
+ except that Section 2.2 and 11 are omitted. Any differences between
+ the Alternative License and the SGI License are offered solely by Sun
+ and not by SGI.
+
+ Original Code. The Original Code is: OpenGL Sample Implementation,
+ Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
+ Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
+ Copyright in any portions created by third parties is as indicated
+ elsewhere herein. All Rights Reserved.
+
+ Additional Notice Provisions: The application programming interfaces
+ established by SGI in conjunction with the Original Code are The
+ OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
+ April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
+ 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
+ Window System(R) (Version 1.3), released October 19, 1998. This software
+ was created using the OpenGL(R) version 1.2.1 Sample Implementation
+ published by SGI, but has not been independently verified as being
+ compliant with the OpenGL(R) version 1.2.1 Specification.
+
+
+The JOGL source tree contains code from the LWJGL project which is
+similarly covered by the BSD license:
+
+ Copyright (c) 2002-2004 LWJGL Project
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of 'LWJGL' nor the names of
+ its contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The JOGL source tree also contains a Java port of Brian Paul's Tile
+Rendering library, used with permission of the author under the BSD
+license instead of the original LGPL:
+
+ Copyright (c) 1997-2005 Brian Paul. All Rights Reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ - Redistribution of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ - Redistribution in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ Neither the name of Brian Paul or the names of contributors may be
+ used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ This software is provided "AS IS," without a warranty of any
+ kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ EXCLUDED. THE COPYRIGHT HOLDERS AND CONTRIBUTORS SHALL NOT BE
+ LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING,
+ MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO
+ EVENT WILL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY
+ LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ INABILITY TO USE THIS SOFTWARE, EVEN IF THE COPYRIGHT HOLDERS OR
+ CONTRIBUTORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
diff --git a/engine/lib/jogl/README.txt b/engine/lib/jogl/README.txt
new file mode 100644
index 000000000..98a1ee32b
--- /dev/null
+++ b/engine/lib/jogl/README.txt
@@ -0,0 +1,45 @@
+
+Java (TM) Binding for the OpenGL (r) API, version 1.1.1
+-------------------------------------------------------
+
+This software is licensed by Sun Microsystems, as specified
+in the LICENSE-JOGL-1.1.1.txt file. You must use this software
+in accordance with the terms under which the code is licensed.
+
+
+
+Instructions for unzipping Java Binding for the OpenGL API, version 1.1.1
+--------------------------------------------------------------------
+
+After downloading and unzipping the zip file containing the
+JOGL release for your target platform, you will see the
+following files in the top directory:
+
+ COPYRIGHT.txt
+ LICENSE-JOGL-1.1.1.txt
+ Userguide.html
+ README.txt README file (you are reading it now)
+
+and the following subdirectory:
+
+ lib contains JOGL implementation
+
+All of the JOGL implementation files (jar files and native
+libraries) are in the lib subdirectory. For instructions on
+how to use these implementation files to build or run a JOGL
+program see the enclosed JOGL user guide (Userguide.html).
+
+
+
+Project source code and getting assistance
+------------------------------------------
+
+JOGL source code and project information can be found at:
+
+ https://jogl.dev.java.net/
+
+
+Numerous answers to common questions can be found on the JOGL
+forum:
+
+ http://www.javagaming.org/forums/index.php?board=25.0
diff --git a/engine/lib/jogl/Userguide.html b/engine/lib/jogl/Userguide.html
new file mode 100644
index 000000000..3dfd57568
--- /dev/null
+++ b/engine/lib/jogl/Userguide.html
@@ -0,0 +1,999 @@
+
+
+
+Jogl - User's Guide
+
+
+
+Jogl - User's Guide
+
+
+
+
+
+ Overview
+ Developing with JOGL
+
+ Building the source tree
+ Local installation for development
+ Java Web Start integration
+ Applet support
+
+ GLDrawable and GLContext
+ Creating a GLAutoDrawable
+ Writing a GLEventListener
+ Using the Composable Pipeline
+ Heavyweight and Lightweight Issues
+ Multithreading Issues
+ Pbuffers
+ GLU
+ More Resources
+ Platform notes
+
+ All Platforms
+ Windows
+ Linux
+ Solaris, Linux (X11 platforms)
+ Macintosh OS X
+
+ Version History
+
+
+
+ Overview
+
+
+
+Jogl is a Java programming language binding for the OpenGL 3D graphics
+API. It supports integration with the Java platform's AWT and Swing
+widget sets while providing a minimal and easy-to-use API that handles
+many of the issues associated with building multithreaded OpenGL
+applications. Jogl provides access to the latest OpenGL routines
+(OpenGL 2.0 with vendor extensions) as well as platform-independent
+access to hardware-accelerated offscreen rendering ("pbuffers"). Jogl
+also provides some of the most popular features introduced by other
+Java bindings for OpenGL like GL4Java, LWJGL and Magician, including a
+composable pipeline model which can provide faster debugging for
+Java-based OpenGL applications than the analogous C program.
+
+
+
+
+Jogl was designed for the most recent versions of the Java platform
+and for this reason supports only J2SE 1.4 and later. It also only
+supports truecolor (15 bits per pixel and higher) rendering; it does
+not support color-indexed modes. It was designed with New I/O (NIO) in
+mind and uses NIO internally in the implementation. The Jogl binding
+is itself written almost completely in the Java programming language.
+There are roughly 150 lines of handwritten C code in the entire Jogl
+source base (100 of which work around bugs in older OpenGL drivers on
+Windows); the rest of the native code is autogenerated during the
+build process by a new tool called GlueGen , the source code of
+which is available from its own java.net project.
+
+
+
+
+The JOGL source tree in its current form is an experimental workspace
+for the JSR-231 Java
+Bindings for OpenGL JSR. JOGL is not the official reference
+implementation, but an evolving workspace. Snapshots of the JOGL
+source tree are run through the JSR-231 Technology Compatibility Kit
+(TCK) to become the official reference implementation (RI). As of this
+writing the JSR has not been finalized, so the first official RI of
+the JSR has not yet been produced.
+
+
+
+ Developing with JOGL
+
+ Building the source tree
+
+
+
+Most developers using JOGL will download the most current release
+build . Separate instructions are available on how to build
+the source tree .
+
+
+
+ Local installation for development
+
+
+
+The JOGL distribution for developers comes in the form of a zip
+archive which contains the Java classes to call OpenGL from Java, as
+well as the associated JNI native libraries. JOGL depends on some
+run-time support classes and native code provided by the GlueGen
+project; these classes and native code are also provided in the zip
+bundles.
+
+
+
+
+If you are developing a new application which uses JOGL, download the
+zip archive for your platform (for example.,
+jogl-[version]-windows-i586.zip) and unzip it. Modify your CLASSPATH
+environment variable to include the full paths to jogl.jar and
+gluegen-rt.jar; for example,
+".;C:\Some\Other\Package\foo.jar;C:\Users\myhome\jogl-[version]-windows-i586\lib\jogl.jar;C:\Users\myhome\jogl-[version]-windows-i586\lib\gluegen-rt.jar".
+(If you did not previously set the CLASSPATH environment variable, you
+may want to make sure that ".", the current directory, is on your new
+CLASSPATH.) Modify your PATH environment variable (Windows),
+LD_LIBRARY_PATH environment variable (Solaris and Linux), or
+DYLD_LIBRARY_PATH environment variable (Mac OS X) to contain the full
+path to the "lib" directory; for example, on Windows, add
+"C:\Users\myhome\jogl-[version]-windows-i586\lib" to your PATH using
+the System control panel, Advanced tab, Environment Variables
+button. At this point your Java installation should be able to see the
+JOGL class files. Users of IDEs such as NetBeans and Eclipse should
+consult the IDE's documentation to see how to add jar files and native
+libraries to their current project.
+
+
+
+
+Dropping the JOGL jar and native library into the extension directory
+of the JRE is strongly discouraged . Doing so can cause
+conflicts with third-party applications launched via Java Web Start,
+and causes confusion later when upgrading the distribution.
+
+
+
+
+If you are on the Linux platform, please see the Linux-specific
+platform notes, below, with information on incompatibility between the
+JPackage Java RPMs and JOGL.
+
+
+
+ Java Web Start integration
+
+
+
+The recommended distribution vehicle for applications using JOGL is
+Java Web Start. JOGL-based applications do not even need to be signed;
+all that is necessary is to reference the JOGL extension JNLP file.
+Because the JOGL jar files are signed, an unsigned application can
+reference the signed JOGL library and continue to run inside the
+sandbox.
+
+
+
+
+To reference JOGL within your application's JNLP file, simply place
+the following line in the <resources>
section:
+
+
+ <extension name="jogl" href="http://download.java.net/media/jogl/builds/archive/jsr-231-webstart-current/jogl.jnlp" />
+
+
+This JNLP file points to the current JSR-231 unofficial development
+build. For reference, the extension JNLP file for the most recent
+official JSR-231 build is available at
+
+
+ <extension name="jogl" href="http://download.java.net/media/jogl/builds/archive/jsr-231-1.1.0/webstart/jogl.jnlp" />
+
+
+Note that before JOGL transitioned to the JSR-231 APIs, there were
+releases of the library in the net.java.games.jogl
+namespace under version numbers "1.0", "1.1", and "1.1.1". All of
+these releases have been superseded by JSR-231. Please update your
+applications.
+
+
+
+ Applet support
+
+
+
+Lilian Chamontin, in conjunction with several other members of the
+JOGL community, has contributed a JOGL applet installer. This
+installer uses some clever tricks to allow deployment of unsigned
+applets which use JOGL into existing web browsers and JREs as far back
+as 1.4.2, which is the earliest version of Java supported by JOGL.
+
+
+
+
+The JOGLAppletInstaller is distributed inside jogl.jar as a utility
+class in com.sun.opengl.util. It requires that the developer host a
+local, signed copy of jogl.jar and all of the jogl-natives jars; the
+certificates must be the same on all of these jars. Note that in the
+release builds of JOGL all of these jars are signed by Sun
+Microsystems, so the developer can deploy applets without needing any
+certificates.
+
+
+
+
+The JOGLAppletInstaller javadoc describes the basic steps for
+deployment of an applet utilizing JOGL. Please refer to this
+documentation for more information. A live example of deploying an
+unsigned JOGL applet will be added to this documentation shortly once
+the first signed build of the JOGLAppletInstaller has been shipped.
+
+
+
+ GLDrawable and GLContext
+
+
+
+The JSR-231 APIs specify interfaces two low-level OpenGL abstractions:
+drawables and contexts. An OpenGL drawable is effectively a surface
+upon which OpenGL rendering will be performed. In order to perform
+rendering, an OpenGL rendering context is needed. Contexts and
+drawables typically go hand-in-hand. More than one context may be
+created for a particular drawable. In the JSR-231 abstractions, a
+context is always associated with exactly one drawable.
+
+
+
+
+Most end users will not need to use these abstractions directly.
+However, when sharing textures, display lists and other OpenGL objects
+between widgets, the concrete identifier for the "namespace" for these
+objects is the GLContext.
+
+
+
+ Creating a GLAutoDrawable
+
+
+
+Jogl provides two basic widgets into which OpenGL rendering can be
+performed. The GLCanvas is a heavyweight AWT widget which supports
+hardware acceleration and which is intended to be the primary widget
+used by applications. The GLJPanel is a fully Swing-compatible
+lightweight widget which supports hardware acceleration but which is
+not as fast as the GLCanvas because it typically reads back the frame
+buffer in order to draw it using Java2D. The GLJPanel is intended to
+provide 100% correct Swing integration in the circumstances where a
+GLCanvas can not be used. See this
+article on The
+Swing Connection for more information about mixing lightweight and
+heavyweight widgets. See also the section on "Heavyweight and
+Lightweight Issues" below. Recent work in the Mustang release of the
+JDK has sped up the GLJPanel significantly when the Java2D OpenGL
+pipeline is enabled; see this
+forum discussion for more details.
+
+
+
+
+Both the GLCanvas and GLJPanel implement a common interface called
+GLAutoDrawable so applications can switch between them with minimal
+code changes. The GLAutoDrawable interface provides
+
+
+
+ access to the GL object for calling OpenGL routines
+
+ a callback mechanism (GLEventListener) for performing OpenGL
+ rendering
+
+ a display()
method for forcing OpenGL rendering to
+ be performed synchronously
+
+ AWT- and Swing-independent abstractions for getting and setting
+ the size of the widget and adding and removing event listeners
+
+
+
+
+
+
+When creating GLCanvas and GLJPanel instances, the user may request a
+certain set of OpenGL parameters in the form of a GLCapabilities
+object, customize the format selection algorithm by specifying a
+GLCapabilitiesChooser, share textures and display lists with other
+GLDrawables, and specify the display device on which the
+GLAutoDrawable will be created (GLCanvas only).
+
+
+
+
+A GLCapabilities object specifies the OpenGL parameters for a
+newly-created widget, such as the color, alpha,, z-buffer and
+accumulation buffer bit depths and whether the widget is
+double-buffered. The default capabilities are loosely specified but
+provide for truecolor RGB, a reasonably large depth buffer,
+double-buffered, with no alpha, stencil, or accumulation buffers.
+
+
+
+
+An application can override the default pixel format selection
+algorithm by providing a GLCapabilitiesChooser to the GLCanvas or
+GLJPanel constructor. (Not all platforms support the
+GLCapabilitiesChooser mechanism, however; it may be ignored, in
+particular on Mac OS X where pixel format selection is very different
+than on other platforms.) The chooseCapabilities method will be called
+with all of the available pixel formats as an array of GLCapabilities
+objects, as well as the index indicating the window system's
+recommended choice; it should return an integer index into this
+array. The DefaultGLCapabilitiesChooser uses the window system's
+recommendation when it is available, and otherwise attempts to use a
+platform-independent selection algorithm.
+
+
+
+
+The GLJPanel can be made non-opaque according to Swing's rendering
+model, so it can act as an overlay to other Swing or Java2D drawing.
+In order to enable this, set up your GLCapabilities object with a
+non-zero alpha depth (a common value is 8 bits) and call
+setOpaque(false) on the GLJPanel once it has been created. Java2D
+rendering underneath it will then show through areas where OpenGL has
+produced an alpha value less than 1.0. See the JGears and JRefract
+demos for examples of how to use this functionality.
+
+
+
+ Writing a GLEventListener
+
+
+
+Applications implement the GLEventListener interface to perform OpenGL
+drawing via callbacks. When the methods of the GLEventListener are
+called, the underlying OpenGL context associated with the drawable is
+already current. The listener fetches the GL object out of the
+GLAutoDrawable and begins to perform rendering.
+
+
+
+
+The init()
method is called when a new OpenGL context is
+created for the given GLAutoDrawable. Any display lists or textures
+used during the application's normal rendering loop can be safely
+initialized in init()
. It is important to note that
+because the underlying AWT window may be destroyed and recreated while
+using the same GLCanvas and GLEventListener, the GLEventListener's
+init()
method may be called more than once during the
+lifetime of the application. The init() method should therefore be
+kept as short as possible and only contain the OpenGL initialization
+required for the display()
method to run properly. It is
+the responsibility of the application to keep track of how its various
+OpenGL contexts share display lists, textures and other OpenGL objects
+so they can be either be reinitialized or so that reinitialization can
+be skipped when the init()
callback is called.
+
+
+
+
+Note also that the GLEventListener should be added to the
+GLAutoDrawable before the GLAutoDrawable is shown or rendered to for
+the first time. If this is not done, it is possible that the init()
+method will not be called on the GLEventListener. JOGL does not
+maintain internal state to keep track of whether init() has been
+called on a particular GLEventListener since the last time an OpenGL
+context was created for that GLAutoDrawable.
+
+
+
+
+The display()
method is called to perform per-frame
+rendering. The reshape()
method is called when the
+drawable has been resized; the default implementation automatically
+resizes the OpenGL viewport so often it is not necessary to do any
+work in this method. The displayChanged()
method is
+designed to allow applications to support on-the-fly screen mode
+switching, but support for this is not yet implemented so the body of
+this method should remain empty.
+
+
+
+
+It is strongly recommended that applications always refetch the GL
+object out of the GLAutoDrawable upon each call to the
+init()
, display()
and reshape()
+methods and pass the GL object down on the stack to any drawing
+routines, as opposed to storing the GL in a field and referencing it
+from there. The reason is that multithreading issues inherent to the
+AWT toolkit make it difficult to reason about which threads certain
+operations are occurring on, and if the GL object is stored in a field
+it is unfortunately too easy to accidentally make OpenGL calls from a
+thread that does not have a current context. This will usually cause
+the application to crash. For more information please see the section
+on multithreading.
+
+
+
+ Using the Composable Pipeline
+
+
+
+Jogl supports the "composable pipeline" paradigm introduced by the
+Magician Java binding for OpenGL. The DebugGL pipeline calls
+glGetError
after each OpenGL call, reporting any errors
+found. It can greatly speed up development time because of its
+fine-grained error checking as opposed to the manual error checking
+usually required in OpenGL programs written in C. The TraceGL prints
+logging information upon each OpenGL call and is helpful when an
+application crash makes it difficult to see where the error occurred.
+
+
+
+
+To use these pipelines, call GLAutoDrawable.setGL
at the
+beginning of the init
method in your GLEventListener. For
+example,
+
+
+class MyListener implements GLEventListener {
+ public void init(GLDrawable drawable) {
+ drawable.setGL(new DebugGL(drawable.getGL()));
+ // ...
+ }
+
+ // ...
+}
+
+
+
+
+
+Note that the GLAutoDrawable.setGL() method simply calls setGL() on
+the default OpenGL context created by the GLAutoDrawable, so
+sophisticated applications creating their own OpenGL contexts can use
+the composable pipeline with these contexts by setting the GL object
+in the context object itself. The composable pipeline needs to be
+re-installed every time GLContext.makeCurrent() returns
+CONTEXT_CURRENT_NEW.
+
+
+
+ Heavyweight and Lightweight Issues
+
+
+
+As mentioned above, JOGL supplies both a heavyweight (GLCanvas) and a
+lightweight (GLJPanel) widget to be able to provide the fastest
+possible performance for applications which need it as well as 100%
+correct Swing integration, again for applications which need it. The
+GLCanvas usually provides higher performance than the GLJPanel, though
+in recent releases the GLJPanel's speed has been improved when the
+Java2D/OpenGL pipeline is active as described in this
+forum discussion . Nonetheless, the GLCanvas can be used in almost
+every kind of application except those using JInternalFrames. Please
+see the Swing Connection article mentioned above for details on mixing
+heavyweight and lightweight widgets. A couple of common pitfalls are
+described here.
+
+
+
+
+When using JPopupMenus or Swing tool tips in conjunction with the
+GLCanvas, it is necessary to disable the use of lightweight widgets
+for the popups. See the methods
+ToolTipManager.setLightWeightPopupEnabled
,
+JPopupMenu.setLightWeightPopupEnabled
, and
+JPopupMenu.setDefaultLightWeightPopupEnabled
.
+
+
+
+
+There are occasionally problems with certain LayoutManagers and
+component configurations where if a GLCanvas is placed in the middle
+of a set of lightweight widgets then it may only grow and never
+shrink. These issues are documented somewhat in JOGL Issue
+135 and most recently in the thread "Resize
+behaviour" in the JOGL forum. The root cause is behavior of the
+Canvas, and in particular its ComponentPeer. The implementation of
+getPreferredSize() calls getMinimumSize() and getMinimumSize() turns
+around and calls Component.getSize(). This effectively means that the
+Canvas will report its preferred size as being as large as the
+component has ever been. For some layout managers this doesn't seem to
+matter, but for others like the BoxLayout it does. See the test case
+attached to Issue 135 for an example. Replacing the GLCanvas with an
+ordinary Canvas yields the same behavior.
+
+
+
+
+One suggestion was to override getPreferredSize() so that if a
+preferred size has not been set by the user, to default to (0,
+0). This works fine for some test cases but breaks all of the other
+JOGL demos because they use a different LayoutManager. There appear to
+be a lot of interactions between heavyweight vs. lightweight widgets
+and layout managers. One experiment which was done was to override
+setSize() in GLCanvas to update the preferred size. This works down
+to the size specified by the user; if the window is resized any
+smeller the same problem appears. If reshape() (the base routine of
+setSize(), setBounds(), etc.) is changed to do the same thing, the
+demo breaks in the same way it originally did. Therefore this solution
+is fragile because it isn't clear which of these methods are used
+internally by the AWT and for what purposes.
+
+
+
+
+There are two possible solutions, both application-specific. The best
+and most portable appears to be to put the GLCanvas into a JPanel and
+set the JPanel's preferred size to (0, 0). The JPanel will cause this
+constraint to be enforced on its contained GLCanvas. The other
+workaround is to call setPreferredSize(new Dimension(0,
+0))
on a newly-created GLCanvas; this method is new in 1.5.
+
+
+
+
+Another issue that occasionally arises on Windows is flickering during
+live resizing of a GLCanvas. This is caused by the AWT's repainting
+the background of the Canvas and can not be overridden on a per-Canvas
+basis, for example when subclassing Canvas into GLCanvas. The
+repainting of the background of Canvases on Windows can be disabled by
+specifying the system property
+-Dsun.awt.noerasebackground=true
. Whether to specify this
+flag depends on the application and should not be done universally,
+but instead on a case-by-case basis. Some more detail is in the thread
+"TIP:
+JOGL + Swing flicker" in the JOGL forum.
+
+
+
+ Multithreading Issues
+
+
+
+Jogl was designed to interoperate with the AWT, an inherently
+multithreaded GUI toolkit. OpenGL, in contrast, was originally
+designed in single-threaded C programming environments. For this
+reason Jogl provides a framework in which it is possible to write
+correct multithreaded OpenGL applications using the GLEventListener
+paradigm.
+
+
+
+
+If an application written using Jogl interacts in any way with the
+mouse or keyboard, the AWT is processing these events and the
+multithreaded aspects of the program must be considered.
+
+
+
+
+OpenGL applications usually behave in one of two ways: either they
+repaint only on demand, for example when mouse input comes in, or they
+repaint continually, regardless of whether user input is coming in. In
+the repaint-on-demand model, the application can merely call
+GLAutoDrawable.display()
manually at the end of the mouse
+or keyboard listener to cause repainting to be done. Alternatively if
+the application knows the concrete type of the GLDrawable it can call
+repaint() to have the painting scheduled for a later time.
+
+
+
+
+In the continuous repaint model, the application typically has a main
+loop which is calling GLAutoDrawable.display()
+repeatedly, or is using the Animator class, which does this
+internally. In both of these cases the OpenGL rendering will be done
+on this thread rather than the internal AWT event queue thread which
+dispatches mouse and keyboard events.
+
+
+
+
+Both of these models (repaint-on-demand and repaint continually) still
+require the user to think about which thread keyboard and mouse events
+are coming in on, and which thread is performing the OpenGL rendering.
+OpenGL rendering may not occur directly inside the mouse or
+keyboard handlers, because the OpenGL context for the drawable is not
+current at this point (hence the warning about storing a GL object in
+a field, where it can be fetched and accidentally used by another
+thread). However, a mouse or keyboard listener may invoke
+GLAutoDrawable.display()
.
+
+
+
+
+It is generally recommended that applications perform as little work
+as possible inside their mouse and keyboard handlers to keep the GUI
+responsive. However, since OpenGL commands can not be run from
+directly within the mouse or keyboard event listener, the best
+practice is to store off state when the listener is entered and
+retrieve this state during the next call to
+GLEventListener.display()
.
+
+
+
+
+Furthermore, it is recommended that if there are long computational
+sequences in the GLEventListener's display
method which
+reference variables which may be being simultaneously modified by the
+AWT thread (mouse and keyboard listeners) that copies of these
+variables be made upon entry to display
and these copies
+be referenced throughout display() and the methods it calls. This will
+prevent the values from changing while the OpenGL rendering is being
+performed. Errors of this kind show up in many ways, including certain
+kinds of flickering of the rendered image as certain pieces of objects
+are rendered in one place and other pieces are rendered elsewhere in
+the scene. Restructuring the display() method as described has solved
+all instances of this kind of error that have been seen with Jogl to
+date.
+
+
+
+
+Prior to Jogl 1.1 b10, the Jogl library attempted to give applications
+strict control over which thread or threads performed OpenGL
+rendering. The setRenderingThread()
,
+setNoAutoRedrawMode()
and display()
APIs
+were originally designed to allow the application to create its own
+animation thread and avoid OpenGL context switching on platforms that
+supported it. Unfortunately, serious stability issues caused by
+multithreading bugs in either vendors' OpenGL drivers or in the Java
+platform implementation have arisen on three of Jogl's major supported
+platforms: Windows, Linux and Mac OS X. In order to address these
+bugs, the threading model in Jogl 1.1 b10 and later has changed.
+
+
+
+
+All GLEventListener callbacks and other internal OpenGL context
+management are now performed on one thread. (In the current
+implementation, this thread is the AWT event queue thread, which is a
+thread internal to the implementation of the AWT and which is always
+present when the AWT is being used. Future versions of Jogl may change
+the thread on which the OpenGL work is performed.) When the
+GLAutoDrawable.display()
method is called from user code,
+it now performs the work synchronously on the AWT event queue thread,
+even if the calling thread is a different thread. The
+setRenderingThread()
optimization is now a no-op. The
+setNoAutoRedrawMode()
API still works as previously
+advertised, though now that all work is done on the AWT event queue
+thread it no longer needs to be used in most cases. (It was previously
+useful for working around certain kinds of OpenGL driver bugs.)
+
+
+
+
+Most applications will not see a change in behavior from this change
+in the Jogl implementation. Applications which use thread-local
+storage or complex multithreading and synchronization may see a change
+in their control flow requiring code changes. While it is strongly
+recommended to change such applications to work under the new
+threading model, the old threading model can be used by specifying the
+system property -Djogl.1thread=auto
or
+-Djogl.1thread=false
. The "auto" setting is equivalent to
+the behavior in 1.1 b09 and before, where on ATI cards the
+single-threaded mode would be used. The "false' setting is equivalent
+to disabling the single-threaded mode. "true" is now the default
+setting.
+
+
+
+
+In the JSR-231 APIs the single-threaded behavior continues to be the
+default and the setRenderingThread()
and
+setNoAutoRedrawMode()
APIs have been removed. The public
+Threading
class still provides some control over the
+internal use of threads in the library as well as external access to
+these mechanisms.
+
+
+
+ Pbuffers
+
+
+
+Jogl exposes hardware-accelerated offscreen rendering (pbuffers) with
+a minimal and platform-agnostic API. Several recent demos have been
+successfully ported from C/C++ to Java using Jogl's pbuffer APIs.
+However, the pbuffer support in Jogl remains one of the more
+experimental aspects of the package and the APIs may need to change in
+the future.
+
+
+
+
+To create a pbuffer, call
+GLDrawableFactory.createGLPbuffer()
. It is wise to call
+GLDrawableFactory.canCreateGLPbuffer()
first to ensure
+the graphics card has pbuffer support first. The pbuffer is created
+immediately and is available for rendering as soon as
+createGLPbuffer
returns.
+
+
+
+
+A pbuffer is used in conjunction with the GLEventListener mechanism by
+calling its display() method. Rendering, as always, occurs while the
+pbuffer's OpenGL context is current. There are render-to-texture
+options that can be specified in the GLCapabilities for the pbuffer
+which can make it easier to operate upon the resulting pixels. These
+APIs are however highly experimental and not yet implemented on all
+platforms.
+
+
+
+ GLU
+
+
+
+Jogl contains support for the GLU (OpenGL Utility Library) version
+1.3. Jogl originally supported GLU by wrapping the C version of the
+APIs, but over time, and thanks to the contributions of several
+individuals, it now uses a pure-Java version of SGI's GLU library. The
+pure Java port is enabled by default, and addresses stability issues
+on certain Linux distributions as well as the lack of native GLU 1.3
+support on the Windows platform. In case of problems with the Java
+port, the C version of the GLU library may be used by specifying the
+system property -Djogl.glu.nojava
on the command
+line. All of the same functionality is exposed with both the Java and
+C versions of the GLU library; currently NURBS support is the only
+missing feature on both sides. If you run into problems with the Java
+port of the GLU library please file a bug using the Issue Tracker on
+the Jogl home page.
+
+
+
+
+To use the GLU, simply instantiate a GLU object via new
+GLU()
at the beginning of your program. The methods on the GLU
+object may be called at any point when an OpenGL context is current.
+Because the GLU implementation is not thread-safe, one GLU object
+should be created for each GLEventListener or other entity performing
+OpenGL rendering in a given thread.
+
+
+
+ More Resources
+
+
+
+The JOGL
+forum on javagaming.org is
+the best place to ask questions about the library. Many users, as well
+as the Jogl developers, read this forum frequently, and the archived
+threads contain a lot of useful information (which still needs to be
+distilled into documentation).
+
+
+
+
+The JOGL demos provide
+several examples of usage of the library.
+
+
+
+
+Pepijn Van Eeckhoudt, Kevin Duling and Abdul Bezrati have done JOGL ports of
+many of the the NeHe demos . These are small examples of various
+pieces of OpenGL functionality. See also the NeHe web site .
+
+
+
+
+Pepijn also did a JOGL port of
+Paolo Martella's GLExcess
+demo. To see the news update about this port, go to the main GLExcess
+site and scroll down.
+
+
+
+
+Gregory Pierce's introduction
+to JOGL is a useful tutorial on starting to use the JOGL library.
+
+
+
+
+For release information about the JOGL library, please see the JOGL Release
+Information thread on the JOGL forum on javagaming.org.
+
+
+
+
+Please post on the JOGL forum if you have a resource you'd like to add
+to this documentation.
+
+
+
+ Platform Notes
+
+ All Platforms
+
+
+
+The following issues, among others, are outstanding on all platforms:
+
+
+
+
+
+ A few remaining stability issues, mostly on older graphics cards.
+
+ JOGL now supports experimental integration and interoperability
+with the Java2D/OpenGL pipeline in Java SE 6 (Mustang), enabling a
+much faster GLJPanel as well as other features. Please see this
+forum discussion for more details.
+
+
+
+ Windows
+
+
+
+For correct operation, it is necessary to specify the system property
+-Dsun.java2d.noddraw=true
when running JOGL applications
+on Windows; this system property disables the use of DirectDraw by
+Java2D. There are driver-level incompatibilities between DirectDraw
+and OpenGL which manifest themselves as application crashes, poor
+performance, bad flickering, and other artifacts. This poor behavior
+may exhibit itself when OpenGL and DirectDraw are simply used in the
+same application, not even just in the same window, so disabling
+Java2D's DirectDraw pipeline and forcing it to use its GDI pipeline is
+the only way to work around these issues. Java Web Start applications
+may set this system property by adding the following line to the
+<resources>
section of the JNLP file:
+<property name="sun.java2d.noddraw" value="true"/>
+
+
+
+
+There is a serious memory leak in ATI's OpenGL drivers which is
+exhibited on Windows XP on Mobility Radeon 9700 hardware. It's
+possible it will be present on other hardware as well though it was
+not reproducible at the time of this writing on desktop Radeon
+hardware or older ATI mobile chips. The bug is documented in JOGL Issue
+166 and a bug has been filed with ATI. You can confirm the
+presence of the bug either with the test case in that bug report or by
+simply running the Gears demo; if the process size grows over time in
+the Task Manager, the memory leak is present on your hardware. For the
+time being, you can work around this memory leak by specifying the
+system property -Djogl.GLContext.nofree
on the command
+line when launching your JOGL applications. There is no good
+general-purpose workaround for this bug which behaves well on all
+hardware.
+
+
+
+ Linux
+
+
+
+The Sun JDK "compatibility" RPMs (java-1.5.0-sun-compat,
+java-1.6.0-sun-compat) provided by jpackage.org are incompatible with
+JOGL. These RPMs symlink an internal JDK directory to /usr/lib, which
+overrides how both NVidia and ATI currently provide their drivers to
+some Linux distributions, which is through an override in
+/etc/ld.so.conf (actually, in /etc/ld.so.conf.d). The implicit
+presence of /usr/lib on LD_LIBRARY_PATH forces the /usr/lib/libGL.so.1
+version of OpenGL to be used, which is typically Mesa and which will
+provide only software rendering.
+
+
+
+
+Unfortunately the JPackage maintainers have so far been unreceptive to
+changing their installation mechanism; see this
+mailing list posting . Until this is resolved, we strongly
+discourage the use of the JPackage installers for the Sun JDK.
+Instead, download the JRE or JDK installers directly from Sun's
+website.
+
+
+
+
+Archived forum postings illustrating this problem are here
+and here .
+
+
+
+ Solaris, Linux (X11 platforms)
+
+
+
+Support has been added to the JOGL library for allowing multiple
+threads to each have an OpenGL context current simultaneously, for
+example to implement multi-head CAVE-like environments. Normally a
+global AWT lock is held between calls to GLContext.makeCurrent() /
+release() for on-screen heavyweight contexts (for example, those
+associated with a Canvas or GLCanvas). We have found this to be
+necessary for stability purposes on all supported X11 platforms, even
+with relatively robust drivers such as those from NVidia.
+
+
+
+
+To enable multiple GLContexts to be made current simultaneously on X11
+platforms, specify the command line argument
+-Djogl.GLContext.optimize
when starting the JVM. Note
+that this may incur robustness problems, in particular when resizing
+or moving windows. We have also found that ATI's proprietary drivers
+do not work at all with this flag, apparently because they cause GLX
+tokens to be sent to the X server for various GL calls even for direct
+contexts. For this reason if the GLX vendor is ATI then this flag
+currently has no effect.
+
+
+
+ Mac OS X
+
+
+
+There are some problems with visual artifacts and stability problems
+with some of the Jogl demos on Mac OS X. It appears that at least some
+of these problems are due to bugs in Apple's OpenGL support. Bugs have
+been filed about these problems and it is hoped they will be addressed
+in the near future.
+
+
+
+
+The Mac OS X port of Jogl, in particular the GL interface and its
+implementation, can be used either with the provided GLCanvas widget
+or with the Cocoa NSOpenGLView. In order to use it with Cocoa the
+following steps should be taken:
+
+
+
+ Create an "external" OpenGL context using the
+GLDrawableFactory.createExternalGLContext()
API. The
+context object must be created while a real underlying OpenGL context
+is current.
+
+ Fetch the GL instance out of the context using getGL() as usual.
+Only use the GL instance when the OpenGL context from the NSOpenGLView
+is current.
+
+
+
+NOTE: the Cocoa interoperability has not been retested
+recently, though similar interoperability has been tested on other
+platforms. Please report any problems found with using Jogl with an
+NSOpenGLView.
+
+
+
+
+The following issues remain with the Mac OS X port:
+
+
+
+ Due to the mechanism by which the Cocoa graphics system selects
+OpenGL pixel formats, the GLCapabilitiesChooser mechanism can not be
+implemented on Mac OS X as on other platforms. Currently the
+underlying Cocoa pixel format selection is used on an
+NSOpenGLPixelFormat derived from the settings in the GLCapabilities,
+and the GLCapabilitiesChooser is ignored.
+
+
+
+
+
+ Version History
+
+
+
+JOGL's version history can be found online in the "JOGL Release
+Information" thread in the JOGL forum. Comments about the 1.1
+release train are in the thread "JOGL 1.1
+released" .
+
+
+
+
diff --git a/engine/lib/jogl/gluegen-rt.jar b/engine/lib/jogl/gluegen-rt.jar
new file mode 100644
index 000000000..7ac10a354
Binary files /dev/null and b/engine/lib/jogl/gluegen-rt.jar differ
diff --git a/engine/lib/jogl/jME3-jogl-natives.jar b/engine/lib/jogl/jME3-jogl-natives.jar
new file mode 100644
index 000000000..5b8c877d9
Binary files /dev/null and b/engine/lib/jogl/jME3-jogl-natives.jar differ
diff --git a/engine/lib/jogl/jogl.jar b/engine/lib/jogl/jogl.jar
new file mode 100644
index 000000000..305e99873
Binary files /dev/null and b/engine/lib/jogl/jogl.jar differ
diff --git a/engine/lib/jogl/macosx_ppc/libgluegen-rt.jnilib b/engine/lib/jogl/macosx_ppc/libgluegen-rt.jnilib
new file mode 100644
index 000000000..0ed9ba1b2
Binary files /dev/null and b/engine/lib/jogl/macosx_ppc/libgluegen-rt.jnilib differ
diff --git a/engine/lib/jogl/macosx_ppc/libjogl.jnilib b/engine/lib/jogl/macosx_ppc/libjogl.jnilib
new file mode 100644
index 000000000..66ba6d12c
Binary files /dev/null and b/engine/lib/jogl/macosx_ppc/libjogl.jnilib differ
diff --git a/engine/lib/jogl/macosx_ppc/libjogl_awt.jnilib b/engine/lib/jogl/macosx_ppc/libjogl_awt.jnilib
new file mode 100644
index 000000000..55d38655e
Binary files /dev/null and b/engine/lib/jogl/macosx_ppc/libjogl_awt.jnilib differ
diff --git a/engine/lib/jogl/macosx_ppc/libjogl_cg.jnilib b/engine/lib/jogl/macosx_ppc/libjogl_cg.jnilib
new file mode 100644
index 000000000..d9d06dced
Binary files /dev/null and b/engine/lib/jogl/macosx_ppc/libjogl_cg.jnilib differ
diff --git a/engine/lib/jogl/macosx_universal/libgluegen-rt.jnilib b/engine/lib/jogl/macosx_universal/libgluegen-rt.jnilib
new file mode 100644
index 000000000..195628012
Binary files /dev/null and b/engine/lib/jogl/macosx_universal/libgluegen-rt.jnilib differ
diff --git a/engine/lib/jogl/macosx_universal/libjogl.jnilib b/engine/lib/jogl/macosx_universal/libjogl.jnilib
new file mode 100644
index 000000000..eb8719aa9
Binary files /dev/null and b/engine/lib/jogl/macosx_universal/libjogl.jnilib differ
diff --git a/engine/lib/jogl/macosx_universal/libjogl_awt.jnilib b/engine/lib/jogl/macosx_universal/libjogl_awt.jnilib
new file mode 100644
index 000000000..2f16fbfb6
Binary files /dev/null and b/engine/lib/jogl/macosx_universal/libjogl_awt.jnilib differ
diff --git a/engine/lib/jogl/macosx_universal/libjogl_cg.jnilib b/engine/lib/jogl/macosx_universal/libjogl_cg.jnilib
new file mode 100644
index 000000000..562712108
Binary files /dev/null and b/engine/lib/jogl/macosx_universal/libjogl_cg.jnilib differ
diff --git a/engine/lib/jogl/win32/gluegen-rt.dll b/engine/lib/jogl/win32/gluegen-rt.dll
new file mode 100644
index 000000000..281d389d1
Binary files /dev/null and b/engine/lib/jogl/win32/gluegen-rt.dll differ
diff --git a/engine/lib/jogl/win32/jogl.dll b/engine/lib/jogl/win32/jogl.dll
new file mode 100644
index 000000000..ee7a6a590
Binary files /dev/null and b/engine/lib/jogl/win32/jogl.dll differ
diff --git a/engine/lib/jogl/win32/jogl_awt.dll b/engine/lib/jogl/win32/jogl_awt.dll
new file mode 100644
index 000000000..2b47eee6e
Binary files /dev/null and b/engine/lib/jogl/win32/jogl_awt.dll differ
diff --git a/engine/lib/jogl/win32/jogl_cg.dll b/engine/lib/jogl/win32/jogl_cg.dll
new file mode 100644
index 000000000..c64e2a2ea
Binary files /dev/null and b/engine/lib/jogl/win32/jogl_cg.dll differ
diff --git a/engine/lib/jogl/win64/gluegen-rt.dll b/engine/lib/jogl/win64/gluegen-rt.dll
new file mode 100644
index 000000000..a7eb6f5b9
Binary files /dev/null and b/engine/lib/jogl/win64/gluegen-rt.dll differ
diff --git a/engine/lib/jogl/win64/jogl.dll b/engine/lib/jogl/win64/jogl.dll
new file mode 100644
index 000000000..990986f19
Binary files /dev/null and b/engine/lib/jogl/win64/jogl.dll differ
diff --git a/engine/lib/jogl/win64/jogl_awt.dll b/engine/lib/jogl/win64/jogl_awt.dll
new file mode 100644
index 000000000..d247b4a0b
Binary files /dev/null and b/engine/lib/jogl/win64/jogl_awt.dll differ
diff --git a/engine/lib/jogl/win64/jogl_cg.dll b/engine/lib/jogl/win64/jogl_cg.dll
new file mode 100644
index 000000000..9e2f371b0
Binary files /dev/null and b/engine/lib/jogl/win64/jogl_cg.dll differ
diff --git a/engine/lib/jogl2/gluegen-rt.jar b/engine/lib/jogl2/gluegen-rt.jar
new file mode 100644
index 000000000..60e871f72
Binary files /dev/null and b/engine/lib/jogl2/gluegen-rt.jar differ
diff --git a/engine/lib/jogl2/jogl.all.jar b/engine/lib/jogl2/jogl.all.jar
new file mode 100644
index 000000000..d8b6da4ee
Binary files /dev/null and b/engine/lib/jogl2/jogl.all.jar differ
diff --git a/engine/lib/jogl2/linux32/libgluegen-rt.so b/engine/lib/jogl2/linux32/libgluegen-rt.so
new file mode 100644
index 000000000..e6730cec5
Binary files /dev/null and b/engine/lib/jogl2/linux32/libgluegen-rt.so differ
diff --git a/engine/lib/jogl2/linux32/libjogl_desktop.so b/engine/lib/jogl2/linux32/libjogl_desktop.so
new file mode 100644
index 000000000..a88ae3f1d
Binary files /dev/null and b/engine/lib/jogl2/linux32/libjogl_desktop.so differ
diff --git a/engine/lib/jogl2/linux32/libnativewindow_awt.so b/engine/lib/jogl2/linux32/libnativewindow_awt.so
new file mode 100644
index 000000000..77a189a8f
Binary files /dev/null and b/engine/lib/jogl2/linux32/libnativewindow_awt.so differ
diff --git a/engine/lib/jogl2/linux32/libnativewindow_x11.so b/engine/lib/jogl2/linux32/libnativewindow_x11.so
new file mode 100644
index 000000000..1b9d885fd
Binary files /dev/null and b/engine/lib/jogl2/linux32/libnativewindow_x11.so differ
diff --git a/engine/lib/jogl2/linux32/libnewt.so b/engine/lib/jogl2/linux32/libnewt.so
new file mode 100644
index 000000000..6159024a3
Binary files /dev/null and b/engine/lib/jogl2/linux32/libnewt.so differ
diff --git a/engine/lib/jogl2/nativewindow.all.jar b/engine/lib/jogl2/nativewindow.all.jar
new file mode 100644
index 000000000..8e9fa19e9
Binary files /dev/null and b/engine/lib/jogl2/nativewindow.all.jar differ
diff --git a/engine/lib/jogl2/nativewindow.os.x11.jar b/engine/lib/jogl2/nativewindow.os.x11.jar
new file mode 100644
index 000000000..6a8303be6
Binary files /dev/null and b/engine/lib/jogl2/nativewindow.os.x11.jar differ
diff --git a/engine/lib/jogl2/newt.all.jar b/engine/lib/jogl2/newt.all.jar
new file mode 100644
index 000000000..de21435d7
Binary files /dev/null and b/engine/lib/jogl2/newt.all.jar differ
diff --git a/engine/lib/jogl2/newt.os.x11.jar b/engine/lib/jogl2/newt.os.x11.jar
new file mode 100644
index 000000000..da8d04be7
Binary files /dev/null and b/engine/lib/jogl2/newt.os.x11.jar differ
diff --git a/engine/lib/junit_4/junit-4.5-api.zip b/engine/lib/junit_4/junit-4.5-api.zip
new file mode 100644
index 000000000..5748c444d
Binary files /dev/null and b/engine/lib/junit_4/junit-4.5-api.zip differ
diff --git a/engine/lib/junit_4/junit-4.5-src.jar b/engine/lib/junit_4/junit-4.5-src.jar
new file mode 100644
index 000000000..18774a573
Binary files /dev/null and b/engine/lib/junit_4/junit-4.5-src.jar differ
diff --git a/engine/lib/junit_4/junit-4.5.jar b/engine/lib/junit_4/junit-4.5.jar
new file mode 100644
index 000000000..83f8bc793
Binary files /dev/null and b/engine/lib/junit_4/junit-4.5.jar differ
diff --git a/engine/lib/lwjgl/3rdparty/jinput_license.txt b/engine/lib/lwjgl/3rdparty/jinput_license.txt
new file mode 100644
index 000000000..cee4669ba
--- /dev/null
+++ b/engine/lib/lwjgl/3rdparty/jinput_license.txt
@@ -0,0 +1,32 @@
+/*****************************************************************************
+ * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistribution of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materails provided with the distribution.
+ *
+ * Neither the name Sun Microsystems, Inc. or the names of the contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any kind.
+ * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
+ * ANY IMPLIED WARRANT OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
+ * NON-INFRINGEMEN, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND
+ * ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS
+ * A RESULT OF USING, MODIFYING OR DESTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
+ * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
+ * INCIDENTAL OR PUNITIVE DAMAGES. HOWEVER CAUSED AND REGARDLESS OF THE THEORY
+ * OF LIABILITY, ARISING OUT OF THE USE OF OUR INABILITY TO USE THIS SOFTWARE,
+ * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed or intended for us in
+ * the design, construction, operation or maintenance of any nuclear facility
+ *
+ *****************************************************************************/
\ No newline at end of file
diff --git a/engine/lib/lwjgl/3rdparty/jogl_license.txt b/engine/lib/lwjgl/3rdparty/jogl_license.txt
new file mode 100644
index 000000000..db9b93374
--- /dev/null
+++ b/engine/lib/lwjgl/3rdparty/jogl_license.txt
@@ -0,0 +1,152 @@
+JOGL is released under the BSD license. The full license terms follow:
+
+ Copyright (c) 2003-2009 Sun Microsystems, Inc. All Rights Reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ - Redistribution of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ - Redistribution in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ Neither the name of Sun Microsystems, Inc. or the names of
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ This software is provided "AS IS," without a warranty of any kind. ALL
+ EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
+ MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
+ ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
+ ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
+ DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
+ DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
+ ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
+ SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+ You acknowledge that this software is not designed or intended for use
+ in the design, construction, operation or maintenance of any nuclear
+ facility.
+
+The JOGL source tree contains code ported from the OpenGL sample
+implementation by Silicon Graphics, Inc. This code is licensed under
+the SGI Free Software License B (Sun is redistributing the modified code
+under a slightly modified, alternative license, which is described two
+paragraphs below after "NOTE:"):
+
+ License Applicability. Except to the extent portions of this file are
+ made subject to an alternative license as permitted in the SGI Free
+ Software License B, Version 1.1 (the "License"), the contents of this
+ file are subject only to the provisions of the License. You may not use
+ this file except in compliance with the License. You may obtain a copy
+ of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
+ Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
+
+ http://oss.sgi.com/projects/FreeB
+
+ Note that, as provided in the License, the Software is distributed on an
+ "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
+ DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
+ CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
+ PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
+
+ NOTE: The Original Code (as defined below) has been licensed to Sun
+ Microsystems, Inc. ("Sun") under the SGI Free Software License B
+ (Version 1.1), shown above ("SGI License"). Pursuant to Section
+ 3.2(3) of the SGI License, Sun is distributing the Covered Code to
+ you under an alternative license ("Alternative License"). This
+ Alternative License includes all of the provisions of the SGI License
+ except that Section 2.2 and 11 are omitted. Any differences between
+ the Alternative License and the SGI License are offered solely by Sun
+ and not by SGI.
+
+ Original Code. The Original Code is: OpenGL Sample Implementation,
+ Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
+ Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
+ Copyright in any portions created by third parties is as indicated
+ elsewhere herein. All Rights Reserved.
+
+ Additional Notice Provisions: The application programming interfaces
+ established by SGI in conjunction with the Original Code are The
+ OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
+ April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
+ 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
+ Window System(R) (Version 1.3), released October 19, 1998. This software
+ was created using the OpenGL(R) version 1.2.1 Sample Implementation
+ published by SGI, but has not been independently verified as being
+ compliant with the OpenGL(R) version 1.2.1 Specification.
+
+
+The JOGL source tree contains code from the LWJGL project which is
+similarly covered by the BSD license:
+
+ Copyright (c) 2002-2004 LWJGL Project
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of 'LWJGL' nor the names of
+ its contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The JOGL source tree also contains a Java port of Brian Paul's Tile
+Rendering library, used with permission of the author under the BSD
+license instead of the original LGPL:
+
+ Copyright (c) 1997-2005 Brian Paul. All Rights Reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ - Redistribution of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ - Redistribution in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ Neither the name of Brian Paul or the names of contributors may be
+ used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ This software is provided "AS IS," without a warranty of any
+ kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ EXCLUDED. THE COPYRIGHT HOLDERS AND CONTRIBUTORS SHALL NOT BE
+ LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING,
+ MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO
+ EVENT WILL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY
+ LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ INABILITY TO USE THIS SOFTWARE, EVEN IF THE COPYRIGHT HOLDERS OR
+ CONTRIBUTORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
diff --git a/engine/lib/lwjgl/3rdparty/lzma_license.txt b/engine/lib/lwjgl/3rdparty/lzma_license.txt
new file mode 100644
index 000000000..d82521961
--- /dev/null
+++ b/engine/lib/lwjgl/3rdparty/lzma_license.txt
@@ -0,0 +1,15 @@
+LZMA# SDK is licensed under two licenses:
+
+1) GNU Lesser General Public License (GNU LGPL)
+2) Common Public License (CPL)
+
+It means that you can select one of these two licenses and
+follow rules of that license.
+
+SPECIAL EXCEPTION
+Igor Pavlov, as the author of this code, expressly permits you
+to statically or dynamically link your code (or bind by name)
+to the files from LZMA# SDK without subjecting your linked
+code to the terms of the CPL or GNU LGPL.
+Any modifications or additions to files from LZMA# SDK, however,
+are subject to the GNU LGPL or CPL terms.
\ No newline at end of file
diff --git a/engine/lib/lwjgl/3rdparty/openal_license.txt b/engine/lib/lwjgl/3rdparty/openal_license.txt
new file mode 100644
index 000000000..339560d0d
--- /dev/null
+++ b/engine/lib/lwjgl/3rdparty/openal_license.txt
@@ -0,0 +1,437 @@
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License"). Each licensee is
+addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ c) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ d) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
diff --git a/engine/lib/lwjgl/CREDITS b/engine/lib/lwjgl/CREDITS
new file mode 100644
index 000000000..8f1a038fb
--- /dev/null
+++ b/engine/lib/lwjgl/CREDITS
@@ -0,0 +1,36 @@
+The following people have helped to make this project what it is today:
+ - Caspian Rychlik-Prince
+ - Brian Matzon
+ - Elias Naur
+ - Ioannis Tsakpinis
+ - Niels Jørgensen
+ - Tristan Campbell
+ - Gregory Pierce
+ - Luke Holden
+ - Mark Bernard
+ - Erik Duijs
+ - Jos Hirth
+ - Kevin Glass
+ - Atsuya Takagi
+ - kappaOne
+ - Simon Felix
+ - Ryan McNally
+ - Ciardhubh
+ - Jens von Pilgrim
+ - Ruben Garat
+
+additional credits goes to:
+ - Joseph I. Valenzuela [OpenAL stuff]
+ - Lev Povalahev [OpenGL Extensions]
+ - Endolf [Nightly builds and JInput]
+
+The LWJGL project includes files from or depends on the following projects:
+ - OpenGL, SGI - http://opengl.org/
+ - OpenAL, Creative Labs - http://openal.org/
+ - jinput, Sun - https://jinput.dev.java.net/
+ - lzma, p7zip - http://p7zip.sourceforge.net/
+ - JOGL, Sun - http://kenai.com/projects/jogl/pages/Home
+
+Please see the /doc/3rdparty/ directory for licenses.
+
+All trademarks and registered trademarks are the property of their respective owners.
diff --git a/engine/lib/lwjgl/LICENSE b/engine/lib/lwjgl/LICENSE
new file mode 100644
index 000000000..d277220e1
--- /dev/null
+++ b/engine/lib/lwjgl/LICENSE
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2002-2008 Lightweight Java Game Library Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'Light Weight Java Game Library' nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
\ No newline at end of file
diff --git a/engine/lib/lwjgl/README b/engine/lib/lwjgl/README
new file mode 100644
index 000000000..977ae0214
--- /dev/null
+++ b/engine/lib/lwjgl/README
@@ -0,0 +1,50 @@
+This is the official readme file for lwjgl.
+
+Unless otherwise stated, all files distributed or in SVN are covered by
+the license as stated in the LICENSE file. If you have not received this
+file, please download it from the cvs server.
+
+To run some of the included tests:
+ Extract the archive, and cd into directory
+ (please substitute ; and \ according to platform)
+
+ java -cp .;res;jar\lwjgl.jar;jar\lwjgl_test.jar;jar\lwjgl_util.jar;jar\jinput.jar; -Djava.library.path=native\ TEST
+ (this specifies that the jvm should locate the lwjgl native libs in 'native' directory)
+
+ where TEST is some of the following:
+
+ org.lwjgl.test.WindowCreationTest
+ org.lwjgl.test.SysTest
+ org.lwjgl.test.DisplayTest
+
+ org.lwjgl.test.input.MouseCreationTest
+ org.lwjgl.test.input.MouseTest
+ org.lwjgl.test.input.HWCursorTest
+ org.lwjgl.test.input.KeyboardTest
+ org.lwjgl.test.input.TestControllers
+
+ org.lwjgl.test.openal.ALCTest
+ org.lwjgl.test.openal.OpenALCreationTest
+ org.lwjgl.test.openal.MovingSoundTest
+ org.lwjgl.test.openal.PlayTest
+ org.lwjgl.test.openal.PlayTestMemory
+ org.lwjgl.test.openal.SourceLimitTest
+ org.lwjgl.test.openal.PositionTest
+ org.lwjgl.test.openal.StressTest
+ org.lwjgl.test.openal.SourceLimitTest
+
+ org.lwjgl.test.opengl.FullScreenWindowedTest
+ org.lwjgl.test.opengl.PbufferTest
+ org.lwjgl.test.opengl.VBOIndexTest
+ org.lwjgl.test.opengl.VBOTest
+
+ org.lwjgl.test.opengl.pbuffers.PbufferTest
+
+ org.lwjgl.test.opengl.shaders.ShadersTest
+
+You may also run the Space invaders demo by executing:
+ java -cp .;res;jar\lwjgl.jar;jar\lwjgl_test.jar;jar\lwjgl_util.jar; -Djava.library.path=native\ org.lwjgl.examples.spaceinvaders.Game
+
+Project Webpage: www.lwjgl.org
+Project Forum: forum.lwjgl.org
+Project SVN: https://java-game-lib.svn.sourceforge.net/svnroot/java-game-lib
diff --git a/engine/lib/lwjgl/jME3-lwjgl-natives.jar b/engine/lib/lwjgl/jME3-lwjgl-natives.jar
new file mode 100644
index 000000000..63564ce61
Binary files /dev/null and b/engine/lib/lwjgl/jME3-lwjgl-natives.jar differ
diff --git a/engine/lib/lwjgl/jinput.jar b/engine/lib/lwjgl/jinput.jar
new file mode 100644
index 000000000..7c2b6b06f
Binary files /dev/null and b/engine/lib/lwjgl/jinput.jar differ
diff --git a/engine/lib/lwjgl/lwjgl-debug.jar b/engine/lib/lwjgl/lwjgl-debug.jar
new file mode 100644
index 000000000..b1b424fa2
Binary files /dev/null and b/engine/lib/lwjgl/lwjgl-debug.jar differ
diff --git a/engine/lib/lwjgl/lwjgl.jar b/engine/lib/lwjgl/lwjgl.jar
new file mode 100644
index 000000000..0f5681549
Binary files /dev/null and b/engine/lib/lwjgl/lwjgl.jar differ
diff --git a/engine/lib/lwjgl/lwjgl_hidden_switches.text b/engine/lib/lwjgl/lwjgl_hidden_switches.text
new file mode 100644
index 000000000..6efed5e7b
--- /dev/null
+++ b/engine/lib/lwjgl/lwjgl_hidden_switches.text
@@ -0,0 +1,25 @@
+LWJGL "Hidden" switches:
+
+org.lwjgl.opengl.Display.noinput
+Do not initialize any controls when creating the display
+
+org.lwjgl.opengl.Display.nomouse
+Do not create the mouse when creating the display
+
+org.lwjgl.opengl.Display.nokeyboard
+Do not create the keyboard when creating the display
+
+org.lwjgl.util.Debug
+Whether to output debug info
+
+org.lwjgl.util.NoChecks
+Whether to disable runtime function/buffer checks and state tracking.
+
+org.lwjgl.opengl.Display.allowSoftwareOpenGL
+Whether to allow creation of a software only opengl context
+
+org.lwjgl.opengl.Window.undecorated
+Whether to create an undecorated window (no title bar)
+
+org.lwjgl.input.Mouse.allowNegativeMouseCoords
+Usually mouse is clamped to 0,0 - setting this to true will cause you to get negative values if dragging outside and below or left of window
\ No newline at end of file
diff --git a/engine/lib/nblibraries.properties b/engine/lib/nblibraries.properties
new file mode 100644
index 000000000..1a39e98ab
--- /dev/null
+++ b/engine/lib/nblibraries.properties
@@ -0,0 +1,55 @@
+libs.CopyLibs.classpath=\
+ ${base}/CopyLibs/org-netbeans-modules-java-j2seproject-copylibstask.jar
+libs.jbullet.classpath=\
+ ${base}/jbullet/jbullet.jar:\
+ ${base}/jbullet/stack-alloc.jar:\
+ ${base}/jbullet/vecmath.jar
+libs.jheora.classpath=\
+ ${base}/jheora/jheora-jst-debug-0.6.0.jar
+libs.jme3-test-data.classpath=\
+ ${base}/../src/test-data/
+libs.jogg.classpath=\
+ ${base}/jogg/j-ogg-oggd.jar:\
+ ${base}/jogg/j-ogg-vorbisd.jar
+libs.jogl.classpath=\
+ ${base}/jogl/gluegen-rt.jar:\
+ ${base}/jogl/jME3-jogl-natives.jar:\
+ ${base}/jogl/jogl.jar
+libs.jogl2.classpath=\
+ ${base}/jogl2/jogl.all.jar;\
+ ${base}/jogl2/nativewindow.all.jar;\
+ ${base}/jogl2/newt.all.jar;\
+ ${base}/jogl2/gluegen-rt.jar
+libs.junit_4.classpath=\
+ ${base}/junit_4/junit-4.5.jar
+libs.junit_4.javadoc=\
+ ${base}/junit_4/junit-4.5-api.zip
+libs.junit_4.src=\
+ ${base}/junit_4/junit-4.5-src.jar
+libs.JWSAntTasks.classpath=\
+ ${base}/JWSAntTasks/org-netbeans-modules-javawebstart-anttasks.jar
+libs.lwjgl.classpath=\
+ ${base}/lwjgl/jME3-lwjgl-natives.jar;\
+ ${base}/lwjgl/jinput.jar;\
+ ${base}/lwjgl/lwjgl.jar
+libs.niftygui1.2.classpath=\
+ ${base}/niftygui/nifty-1.2-SNAPSHOT.jar;\
+ ${base}/niftygui/nifty-default-controls-1.2-SNAPSHOT.jar;\
+ ${base}/niftygui/nifty-style-black-1.2-SNAPSHOT.jar;\
+ ${base}/niftygui/xmlpull-xpp3-1.1.4c.jar;\
+ ${base}/niftygui/nifty-examples-1.2_small.jar
+libs.niftygui1.3.classpath=\
+ ${base}/niftygui/nifty-1.3-SNAPSHOT.jar;\
+ ${base}/niftygui/nifty-default-controls-1.3-SNAPSHOT.jar;\
+ ${base}/niftygui/nifty-style-black-1.3-SNAPSHOT.jar;\
+ ${base}/niftygui/nifty-style-grey-1.0.jar;\
+ ${base}/niftygui/nifty-examples-1.3-SNAPSHOT.jar;\
+ ${base}/niftygui/xmlpull-xpp3-1.1.4c.jar;\
+ ${base}/niftygui/eventbus-1.4.jar
+libs.swing-layout.classpath=\
+ ${base}/swing-layout/swing-layout-1.0.4.jar
+libs.swing-layout.javadoc=\
+ ${base}/swing-layout/swing-layout-1.0.4-doc.zip
+libs.swing-layout.src=\
+ ${base}/swing-layout/swing-layout-1.0.4-src.zip
+
diff --git a/engine/lib/niftygui/eventbus-1.4.jar b/engine/lib/niftygui/eventbus-1.4.jar
new file mode 100644
index 000000000..a02006f95
Binary files /dev/null and b/engine/lib/niftygui/eventbus-1.4.jar differ
diff --git a/engine/lib/niftygui/nifty-1.3-SNAPSHOT.jar b/engine/lib/niftygui/nifty-1.3-SNAPSHOT.jar
new file mode 100644
index 000000000..5b107697c
Binary files /dev/null and b/engine/lib/niftygui/nifty-1.3-SNAPSHOT.jar differ
diff --git a/engine/lib/niftygui/nifty-default-controls-1.3-SNAPSHOT.jar b/engine/lib/niftygui/nifty-default-controls-1.3-SNAPSHOT.jar
new file mode 100644
index 000000000..62e9c1ce8
Binary files /dev/null and b/engine/lib/niftygui/nifty-default-controls-1.3-SNAPSHOT.jar differ
diff --git a/engine/lib/niftygui/nifty-examples-1.3-SNAPSHOT.jar b/engine/lib/niftygui/nifty-examples-1.3-SNAPSHOT.jar
new file mode 100644
index 000000000..ac27eba16
Binary files /dev/null and b/engine/lib/niftygui/nifty-examples-1.3-SNAPSHOT.jar differ
diff --git a/engine/lib/niftygui/nifty-style-black-1.3-SNAPSHOT.jar b/engine/lib/niftygui/nifty-style-black-1.3-SNAPSHOT.jar
new file mode 100644
index 000000000..69a443402
Binary files /dev/null and b/engine/lib/niftygui/nifty-style-black-1.3-SNAPSHOT.jar differ
diff --git a/engine/lib/niftygui/nifty-style-grey-1.0.jar b/engine/lib/niftygui/nifty-style-grey-1.0.jar
new file mode 100644
index 000000000..e79967eaa
Binary files /dev/null and b/engine/lib/niftygui/nifty-style-grey-1.0.jar differ
diff --git a/engine/lib/niftygui/xmlpull-xpp3-1.1.4c.jar b/engine/lib/niftygui/xmlpull-xpp3-1.1.4c.jar
new file mode 100644
index 000000000..99b7aeabb
Binary files /dev/null and b/engine/lib/niftygui/xmlpull-xpp3-1.1.4c.jar differ
diff --git a/engine/lib/swing-layout/swing-layout-1.0.4-doc.zip b/engine/lib/swing-layout/swing-layout-1.0.4-doc.zip
new file mode 100644
index 000000000..6ea4f2b37
Binary files /dev/null and b/engine/lib/swing-layout/swing-layout-1.0.4-doc.zip differ
diff --git a/engine/lib/swing-layout/swing-layout-1.0.4-src.zip b/engine/lib/swing-layout/swing-layout-1.0.4-src.zip
new file mode 100644
index 000000000..ec9836ee0
Binary files /dev/null and b/engine/lib/swing-layout/swing-layout-1.0.4-src.zip differ
diff --git a/engine/lib/swing-layout/swing-layout-1.0.4.jar b/engine/lib/swing-layout/swing-layout-1.0.4.jar
new file mode 100644
index 000000000..46fe3a2ee
Binary files /dev/null and b/engine/lib/swing-layout/swing-layout-1.0.4.jar differ
diff --git a/engine/lib/swingext/swing-layout-1.0.3.jar b/engine/lib/swingext/swing-layout-1.0.3.jar
new file mode 100644
index 000000000..6e1b43b36
Binary files /dev/null and b/engine/lib/swingext/swing-layout-1.0.3.jar differ
diff --git a/engine/master-application.jnlp b/engine/master-application.jnlp
new file mode 100644
index 000000000..04731feda
--- /dev/null
+++ b/engine/master-application.jnlp
@@ -0,0 +1,26 @@
+
+
+ ${APPLICATION.TITLE}
+ ${APPLICATION.VENDOR}
+
+ ${APPLICATION.DESC}
+ ${APPLICATION.DESC.SHORT}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/engine/nbproject/build-impl.xml b/engine/nbproject/build-impl.xml
new file mode 100644
index 000000000..670444fc4
--- /dev/null
+++ b/engine/nbproject/build-impl.xml
@@ -0,0 +1,1082 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must set src.core.dir
+ Must set src.core-data.dir
+ Must set src.core-plugins.dir
+ Must set src.terrain.dir
+ Must set src.networking.dir
+ Must set src.desktop.dir
+ Must set src.desktop-fx.dir
+ Must set src.games.dir
+ Must set src.jbullet.dir
+ Must set src.niftygui.dir
+ Must set src.jogg.dir
+ Must set src.lwjgl-oal.dir
+ Must set src.lwjgl-ogl.dir
+ Must set src.ogre.dir
+ Must set src.pack.dir
+ Must set src.jheora.dir
+ Must set src.test.dir
+ Must set src.tools.dir
+ Must set src.xml.dir
+ Must set test.test.dir
+ Must set build.dir
+ Must set dist.dir
+ Must set build.classes.dir
+ Must set dist.javadoc.dir
+ Must set build.test.classes.dir
+ Must set build.test.results.dir
+ Must set build.classes.excludes
+ Must set dist.jar
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must set javac.includes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select some files in the IDE or set javac.includes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ To run this application from the command line without Ant, try:
+
+
+
+
+
+
+ java -cp "${run.classpath.with.dist.jar}" ${main.class}
+
+
+
+
+
+
+
+
+
+
+
+ To run this application from the command line without Ant, try:
+
+ java -jar "${dist.jar.resolved}"
+
+
+
+
+
+
+
+ To run this application from the command line without Ant, try:
+
+ java -jar "${dist.jar.resolved}"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set run.class
+
+
+
+ Must select one file in the IDE or set run.class
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set debug.class
+
+
+
+
+ Must select one file in the IDE or set debug.class
+
+
+
+
+ Must set fix.includes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select some files in the IDE or set javac.includes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Some tests failed; see details above.
+
+
+
+
+
+
+
+
+ Must select some files in the IDE or set test.includes
+
+
+
+ Some tests failed; see details above.
+
+
+
+
+ Must select one file in the IDE or set test.class
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set applet.url
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set applet.url
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/engine/nbproject/configs/JWS_generated.properties b/engine/nbproject/configs/JWS_generated.properties
new file mode 100644
index 000000000..7a873bf68
--- /dev/null
+++ b/engine/nbproject/configs/JWS_generated.properties
@@ -0,0 +1,4 @@
+$label=Web Start
+$target.debug=jws-debug
+$target.run=jws-run
+compile.on.save.unsupported.javawebstart=true
diff --git a/engine/nbproject/genfiles.properties b/engine/nbproject/genfiles.properties
new file mode 100644
index 000000000..743b9a955
--- /dev/null
+++ b/engine/nbproject/genfiles.properties
@@ -0,0 +1,11 @@
+build.xml.data.CRC32=52f8cb9e
+build.xml.script.CRC32=34d4c2f2
+build.xml.stylesheet.CRC32=958a1d3e
+# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
+# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
+nbproject/build-impl.xml.data.CRC32=759acdca
+nbproject/build-impl.xml.script.CRC32=10762282
+nbproject/build-impl.xml.stylesheet.CRC32=229523de@1.38.3.45
+nbproject/profiler-build-impl.xml.data.CRC32=aff514c1
+nbproject/profiler-build-impl.xml.script.CRC32=abda56ed
+nbproject/profiler-build-impl.xml.stylesheet.CRC32=f10cf54c@1.11.1
diff --git a/engine/nbproject/jnlp-impl.xml b/engine/nbproject/jnlp-impl.xml
new file mode 100644
index 000000000..f180fdd71
--- /dev/null
+++ b/engine/nbproject/jnlp-impl.xml
@@ -0,0 +1,414 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ To run this application from the command line without Ant, try:
+
+ javaws "${jnlp.file.resolved}"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/engine/nbproject/profiler-build-impl.xml b/engine/nbproject/profiler-build-impl.xml
new file mode 100644
index 000000000..300ea4075
--- /dev/null
+++ b/engine/nbproject/profiler-build-impl.xml
@@ -0,0 +1,131 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must set JVM to use for profiling in profiler.info.jvm
+ Must set profiler agent JVM arguments in profiler.info.jvmargs.agent
+
+
+
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set profile.class
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/engine/nbproject/project.properties b/engine/nbproject/project.properties
new file mode 100644
index 000000000..25bd84cc8
--- /dev/null
+++ b/engine/nbproject/project.properties
@@ -0,0 +1,107 @@
+annotation.processing.enabled=false
+annotation.processing.enabled.in.editor=false
+annotation.processing.run.all.processors=true
+ant.customtasks.libs=JWSAntTasks
+application.homepage=http://www.jmonkeyengine.com/
+application.title=jMonkeyEngine 3.0
+application.vendor=jMonkeyEngine
+build.classes.dir=${build.dir}/classes
+build.classes.excludes=**/*.java,**/*.form
+# This directory is removed when the project is cleaned:
+build.dir=build
+build.generated.dir=${build.dir}/generated
+build.generated.sources.dir=${build.dir}/generated-sources
+# Only compile against the classpath explicitly listed here:
+build.sysclasspath=ignore
+build.test.classes.dir=${build.dir}/test/classes
+build.test.results.dir=${build.dir}/test/results
+# Uncomment to specify the preferred debugger connection transport:
+#debug.transport=dt_socket
+debug.classpath=\
+ ${run.classpath}
+debug.test.classpath=\
+ ${run.test.classpath}
+# This directory is removed when the project is cleaned:
+dist.dir=dist
+dist.jar=${dist.dir}/jMonkeyEngine3.jar
+dist.javadoc.dir=${dist.dir}/javadoc
+endorsed.classpath=
+excludes=
+file.reference.src-test-data=src/test-data
+includes=**
+jar.archive.disabled=${jnlp.enabled}
+jar.compress=true
+jar.index=${jnlp.enabled}
+javac.classpath=\
+ ${libs.jogg.classpath}:\
+ ${libs.jbullet.classpath}:\
+ ${libs.lwjgl.classpath}:\
+ ${libs.jheora.classpath}:\
+ ${libs.niftygui1.3.classpath}:\
+ ${libs.jme3-test-data.classpath}
+# Space-separated list of extra javac options
+javac.compilerargs=
+javac.deprecation=false
+javac.processorpath=\
+ ${javac.classpath}
+javac.source=1.5
+javac.target=1.5
+javac.test.classpath=\
+ ${javac.classpath}:\
+ ${build.classes.dir}:\
+ ${libs.junit_4.classpath}
+javadoc.additionalparam=
+javadoc.author=false
+javadoc.encoding=${source.encoding}
+javadoc.noindex=false
+javadoc.nonavbar=false
+javadoc.notree=false
+javadoc.private=false
+javadoc.splitindex=true
+javadoc.use=true
+javadoc.version=false
+javadoc.windowtitle=jMonkeyEngine3
+jaxbwiz.endorsed.dirs="${netbeans.home}/../ide12/modules/ext/jaxb/api"
+jnlp.applet.class=jme3test.awt.AppHarness
+jnlp.applet.height=300
+jnlp.applet.width=300
+jnlp.codebase.type=user
+jnlp.codebase.user=http://jmonkeyengine.com/javawebstart/
+jnlp.descriptor=application
+jnlp.enabled=false
+jnlp.icon=/Users/normenhansen/Pictures/jme/icons/jme-logo48.png
+jnlp.mixed.code=defaut
+jnlp.offline-allowed=true
+jnlp.signed=true
+main.class=jme3test.TestChooser
+manifest.file=MANIFEST.MF
+meta.inf.dir=${src.dir}/META-INF
+platform.active=default_platform
+run.classpath=\
+ ${javac.classpath}:\
+ ${build.classes.dir}
+run.jvmargs=-Xms40m -Xmx40m -XX:MaxDirectMemorySize=256M
+run.test.classpath=\
+ ${javac.test.classpath}:\
+ ${build.test.classes.dir}
+source.encoding=UTF-8
+src.core-data.dir=src/core-data
+src.core-plugins.dir=src/core-plugins
+src.core.dir=src/core
+src.desktop-fx.dir=src/desktop-fx
+src.desktop.dir=src/desktop
+src.games.dir=src/games
+src.jbullet.dir=src/jbullet
+src.jheora.dir=src/jheora
+src.jogg.dir=src/jogg
+src.lwjgl-oal.dir=src/lwjgl-oal
+src.lwjgl-ogl.dir=src/lwjgl-ogl
+src.networking.dir=src\\networking
+src.niftygui.dir=src/niftygui
+src.ogre.dir=src/ogre
+src.pack.dir=src/pack
+src.terrain.dir=src/terrain
+src.test.dir=src/test
+src.tools.dir=src/tools
+src.xml.dir=src/xml
+test.test.dir=test
diff --git a/engine/nbproject/project.xml b/engine/nbproject/project.xml
new file mode 100644
index 000000000..8aea04430
--- /dev/null
+++ b/engine/nbproject/project.xml
@@ -0,0 +1,42 @@
+
+
+ org.netbeans.modules.java.j2seproject
+
+
+
+
+
+
+
+ jMonkeyEngine3
+ 1.6.5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ./lib/nblibraries.properties
+
+
+
diff --git a/engine/preview-application.html b/engine/preview-application.html
new file mode 100644
index 000000000..4834dd87b
--- /dev/null
+++ b/engine/preview-application.html
@@ -0,0 +1,17 @@
+
+
+
+ Test page for launching the application via JNLP
+
+
+ Test page for launching the application via JNLP
+
+
+
+
+
+
diff --git a/engine/quake3level.zip b/engine/quake3level.zip
new file mode 100644
index 000000000..291ff1182
Binary files /dev/null and b/engine/quake3level.zip differ
diff --git a/engine/src/android/com/jme3/R.java b/engine/src/android/com/jme3/R.java
new file mode 100644
index 000000000..af9687431
--- /dev/null
+++ b/engine/src/android/com/jme3/R.java
@@ -0,0 +1,20 @@
+/* AUTO-GENERATED FILE. DO NOT MODIFY.
+ *
+ * This class was automatically generated by the
+ * aapt tool from the resource data it found. It
+ * should not be modified by hand.
+ */
+
+package com.jme3;
+
+public final class R {
+ public static final class attr {
+ }
+ public static final class layout {
+ public static final int main=0x7f020000;
+ }
+ public static final class string {
+ public static final int app_name=0x7f030000;
+ public static final int jme3_appclass=0x7f030001;
+ }
+}
diff --git a/engine/src/android/com/jme3/app/AndroidHarness.java b/engine/src/android/com/jme3/app/AndroidHarness.java
new file mode 100644
index 000000000..d8b0c9a03
--- /dev/null
+++ b/engine/src/android/com/jme3/app/AndroidHarness.java
@@ -0,0 +1,72 @@
+package com.jme3.app;
+
+import android.app.Activity;
+import android.content.res.Resources;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+import android.view.Window;
+import android.view.WindowManager;
+import com.jme3.R;
+import com.jme3.system.AppSettings;
+import com.jme3.system.JmeSystem;
+import com.jme3.system.android.OGLESContext;
+
+/**
+ *
+ * @author Kirill
+ */
+public class AndroidHarness extends Activity {
+
+ private OGLESContext ctx;
+ private GLSurfaceView view;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ JmeSystem.setResources(getResources());
+
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
+ WindowManager.LayoutParams.FLAG_FULLSCREEN);
+
+ AppSettings settings = new AppSettings(true);
+
+// String appClass = getResources().getString(R.string.jme3_appclass);
+ String appClass = "jme3test.android.Test";
+ Application app = null;
+ try{
+ Class extends Application> clazz = (Class extends Application>) Class.forName(appClass);
+ app = clazz.newInstance();
+ }catch (Exception ex){
+ ex.printStackTrace();
+ }
+
+ app.setSettings(settings);
+ app.start();
+
+ ctx = (OGLESContext) app.getContext();
+ view = ctx.createView(this);
+ setContentView(view);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ view.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ view.onPause();
+ }
+
+// @Override
+// protected void onDestroy(){
+// super.onDestroy();
+
+// Debug.stopMethodTracing();
+// }
+
+}
diff --git a/engine/src/android/com/jme3/app/R.java b/engine/src/android/com/jme3/app/R.java
new file mode 100644
index 000000000..4db44e3dc
--- /dev/null
+++ b/engine/src/android/com/jme3/app/R.java
@@ -0,0 +1,20 @@
+/* AUTO-GENERATED FILE. DO NOT MODIFY.
+ *
+ * This class was automatically generated by the
+ * aapt tool from the resource data it found. It
+ * should not be modified by hand.
+ */
+
+package com.jme3.app;
+
+public final class R {
+ public static final class attr {
+ }
+ public static final class layout {
+ public static final int main=0x7f020000;
+ }
+ public static final class string {
+ public static final int app_name=0x7f030000;
+ public static final int jme3_appclass=0x7f030001;
+ }
+}
diff --git a/engine/src/android/com/jme3/asset/AndroidAssetManager.java b/engine/src/android/com/jme3/asset/AndroidAssetManager.java
new file mode 100644
index 000000000..ee2908e87
--- /dev/null
+++ b/engine/src/android/com/jme3/asset/AndroidAssetManager.java
@@ -0,0 +1,269 @@
+package com.jme3.asset;
+
+import com.jme3.asset.plugins.AndroidLocator;
+import com.jme3.audio.AudioData;
+import com.jme3.audio.AudioKey;
+import com.jme3.export.binary.BinaryExporter;
+import com.jme3.export.binary.BinaryImporter;
+import com.jme3.font.BitmapFont;
+import com.jme3.font.plugins.BitmapFontLoader;
+import com.jme3.material.Material;
+import com.jme3.material.plugins.J3MLoader;
+import com.jme3.scene.Spatial;
+import com.jme3.shader.Shader;
+import com.jme3.shader.ShaderKey;
+import com.jme3.shader.plugins.GLSLLoader;
+import com.jme3.texture.Image;
+import com.jme3.texture.Texture;
+import com.jme3.texture.plugins.AndroidImageLoader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * AssetManager for Android
+ *
+ * @author Kirill Vainer
+ */
+public final class AndroidAssetManager implements AssetManager {
+
+ private static final Logger logger = Logger.getLogger(AndroidAssetManager.class.getName());
+
+ private final AndroidLocator locator = new AndroidLocator();
+ private final AndroidImageLoader imageLoader = new AndroidImageLoader();
+ private final BinaryImporter modelLoader = new BinaryImporter();
+ private final BitmapFontLoader fontLoader = new BitmapFontLoader();
+ private final J3MLoader j3mLoader = new J3MLoader();
+ private final J3MLoader j3mdLoader = new J3MLoader();
+ private final GLSLLoader glslLoader = new GLSLLoader();
+
+ private final BinaryExporter exporter = new BinaryExporter();
+ private final HashMap cache = new HashMap();
+
+ public AndroidAssetManager(){
+ this(false);
+ }
+
+ public AndroidAssetManager(boolean loadDefaults){
+ if (loadDefaults){
+// AssetConfig cfg = new AssetConfig(this);
+// InputStream stream = AssetManager.class.getResourceAsStream("Desktop.cfg");
+// try{
+// cfg.loadText(stream);
+// }catch (IOException ex){
+// logger.log(Level.SEVERE, "Failed to load asset config", ex);
+// }finally{
+// if (stream != null)
+// try{
+// stream.close();
+// }catch (IOException ex){
+// }
+// }
+ }
+ logger.info("AndroidAssetManager created.");
+ }
+
+ public void registerLoader(String loaderClass, String ... extensions){
+ }
+
+ public void registerLocator(String rootPath, String locatorClass, String ... extensions){
+ }
+
+ private Object tryLoadFromHD(AssetKey key){
+ if (!key.getExtension().equals("fnt"))
+ return null;
+
+ File f = new File("/sdcard/" + key.getName() + ".opt");
+ if (!f.exists())
+ return null;
+
+ try {
+ InputStream stream = new FileInputStream(f);
+ BitmapFont font = (BitmapFont) modelLoader.load(stream, null, null);
+ stream.close();
+ return font;
+ } catch (IOException ex){
+ }
+
+ return null;
+ }
+
+ private void tryPutToHD(AssetKey key, Object data){
+ if (!key.getExtension().equals("fnt"))
+ return;
+
+ File f = new File("/sdcard/" + key.getName() + ".opt");
+
+ try {
+ BitmapFont font = (BitmapFont) data;
+ OutputStream stream = new FileOutputStream(f);
+ exporter.save(font, stream);
+ stream.close();
+ } catch (IOException ex){
+ }
+ }
+
+ public Object loadAsset(AssetKey key){
+ logger.info("loadAsset(" + key + ")");
+ Object asset;
+// Object asset = tryLoadFromHD(key);
+// if (asset != null)
+// return asset;
+
+ if (key.shouldCache()){
+ asset = cache.get(key);
+ if (asset != null)
+ return key.createClonedInstance(asset);
+ }
+ // find resource
+ AssetInfo info = locator.locate(this, key);
+ if (info == null){
+ logger.log(Level.WARNING, "Cannot locate resource: "+key.getName());
+ return null;
+ }
+
+ String ex = key.getExtension();
+ logger.log(Level.INFO, "Loading asset: "+key.getName());
+ try{
+ if (ex.equals("png") || ex.equals("jpg")
+ || ex.equals("jpeg") || ex.equals("j3i")){
+ Image image;
+ if (ex.equals("j3i")){
+ image = (Image) modelLoader.load(info);
+ }else{
+ image = (Image) imageLoader.load(info);
+ }
+ TextureKey tkey = (TextureKey) key;
+ asset = image;
+ Texture tex = (Texture) tkey.postProcess(asset);
+ tex.setMagFilter(Texture.MagFilter.Nearest);
+ tex.setAnisotropicFilter(0);
+ if (tex.getMinFilter().usesMipMapLevels()){
+ tex.setMinFilter(Texture.MinFilter.NearestNearestMipMap);
+ }else{
+ tex.setMinFilter(Texture.MinFilter.NearestNoMipMaps);
+ }
+ asset = tex;
+ }else if (ex.equals("j3o")){
+ asset = modelLoader.load(info);
+ }else if (ex.equals("fnt")){
+ asset = fontLoader.load(info);
+ }else if (ex.equals("j3md")){
+ asset = j3mdLoader.load(info);
+ }else if (ex.equals("j3m")){
+ asset = j3mLoader.load(info);
+ }else{
+ logger.info("loading asset as glsl shader ...");
+ asset = glslLoader.load(info);
+ // logger.log(Level.WARNING, "No loader registered for type: "+ex);
+ // return null;
+ }
+
+ if (key.shouldCache())
+ cache.put(key, asset);
+
+// tryPutToHD(key, asset);
+
+ return key.createClonedInstance(asset);
+ } catch (Exception e){
+ logger.log(Level.WARNING, "Failed to load resource: "+key.getName(), e);
+ }
+ return null;
+ }
+
+ public AssetInfo locateAsset(AssetKey> key){
+ AssetInfo info = locator.locate(this, key);
+ if (info == null){
+ logger.log(Level.WARNING, "Cannot locate resource: "+key.getName());
+ return null;
+ }
+ return info;
+ }
+
+
+
+
+ public Object loadAsset(String name) {
+ return loadAsset(new AssetKey(name));
+ }
+
+ public Spatial loadModel(String name) {
+ return (Spatial) loadAsset(name);
+ }
+
+ public Material loadMaterial(String name) {
+ return (Material) loadAsset(name);
+ }
+
+ public BitmapFont loadFont(String name){
+ return (BitmapFont) loadAsset(name);
+ }
+
+ public Texture loadTexture(TextureKey key){
+ return (Texture) loadAsset(key);
+ }
+
+ public Texture loadTexture(String name){
+ return loadTexture(new TextureKey(name, false));
+ }
+
+ public Shader loadShader(ShaderKey key){
+ logger.info("loadShader(" + key + ")");
+
+ String vertName = key.getVertName();
+ String fragName = key.getFragName();
+
+ String vertSource = (String) loadAsset(new AssetKey(vertName));
+ String fragSource = (String) loadAsset(new AssetKey(fragName));
+
+ Shader s = new Shader(key.getLanguage());
+ s.addSource(Shader.ShaderType.Vertex, vertName, vertSource, key.getDefines().getCompiled());
+ s.addSource(Shader.ShaderType.Fragment, fragName, fragSource, key.getDefines().getCompiled());
+
+ logger.info("returing shader: [" + s + "]");
+ return s;
+ }
+
+
+ public void registerLocator(String rootPath, String locatorClassName) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public AudioData loadAudio(AudioKey key) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public AudioData loadAudio(String name) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public Spatial loadModel(ModelKey key) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ /* new */
+
+ private AssetEventListener eventListener = null;
+
+ public void setAssetEventListener(AssetEventListener listener){
+ eventListener = listener;
+ }
+
+ public void registerLocator(String rootPath, Class extends AssetLocator> locatorClass){
+ logger.warning("not implemented.");
+ }
+
+ public void registerLoader(Class extends AssetLoader> loader, String ... extensions){
+ logger.warning("not implemented.");
+ }
+
+
+
+
+}
diff --git a/engine/src/android/com/jme3/asset/plugins/AndroidLocator.java b/engine/src/android/com/jme3/asset/plugins/AndroidLocator.java
new file mode 100644
index 000000000..adbe7603d
--- /dev/null
+++ b/engine/src/android/com/jme3/asset/plugins/AndroidLocator.java
@@ -0,0 +1,62 @@
+package com.jme3.asset.plugins;
+
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetKey;
+import com.jme3.asset.AssetLocator;
+import com.jme3.system.JmeSystem;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.logging.Logger;
+
+public class AndroidLocator implements AssetLocator {
+
+ private static final Logger logger = Logger.getLogger(AndroidLocator.class.getName());
+ private Resources resources;
+ private AssetManager androidManager;
+
+ private class AndroidAssetInfo extends AssetInfo {
+
+ private final InputStream in;
+
+ public AndroidAssetInfo(com.jme3.asset.AssetManager manager, AssetKey key,
+ InputStream in){
+ super(manager, key);
+ this.in = in;
+ }
+
+ @Override
+ public InputStream openStream() {
+ return in;
+ }
+ }
+
+
+ public AndroidLocator(){
+ resources = JmeSystem.getResources();
+ androidManager = resources.getAssets();
+ }
+
+ public void setRootPath(String rootPath) {
+ }
+
+ public AssetInfo locate(com.jme3.asset.AssetManager manager, AssetKey key) {
+ InputStream in = null;
+ try {
+ in = androidManager.open(key.getName());
+ if (in == null)
+ return null;
+
+ return new AndroidAssetInfo(manager, key, in);
+ } catch (IOException ex) {
+ if (in != null)
+ try {
+ in.close();
+ } catch (IOException ex1) {
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/engine/src/android/com/jme3/input/android/AndroidInput.java b/engine/src/android/com/jme3/input/android/AndroidInput.java
new file mode 100644
index 000000000..3be23f644
--- /dev/null
+++ b/engine/src/android/com/jme3/input/android/AndroidInput.java
@@ -0,0 +1,448 @@
+package com.jme3.input.android;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import com.jme3.input.KeyInput;
+import com.jme3.input.MouseInput;
+import com.jme3.input.RawInputListener;
+import com.jme3.input.event.KeyInputEvent;
+import com.jme3.input.event.MouseButtonEvent;
+import com.jme3.input.event.MouseMotionEvent;
+
+public class AndroidInput extends GLSurfaceView implements KeyInput, MouseInput {
+
+ private RawInputListener listener;
+ private int lastX = -1, lastY = -1;
+
+ private static final char[] ANDROID_TO_JME_CHR = {
+ 0x0,// unknown
+ 0x0,// soft left
+ 0x0,// soft right
+ 0x0,// home
+ 0x0,// back
+ 0x0,// call
+ 0x0,// endcall
+ '0',
+ '1',
+ '2',
+ '3',
+ '4',
+ '5',
+ '6',
+ '7',
+ '8',
+ '9',
+ '*',
+ '#',
+ 0x0,//dpad_up
+ 0x0,//dpad_down
+ 0x0,//dpad_left
+ 0x0,//dpad_right
+ 0x0,//dpad_center
+ 0x0,//volume up
+ 0x0,//volume down
+ 0x0,//power
+ 0x0,//camera
+ 0x0,//clear
+ 'a',
+ 'b',
+ 'c',
+ 'd',
+ 'e',
+ 'f',
+ 'g',
+ 'h',
+ 'i',
+ 'j',
+ 'k',
+ 'l',
+ 'm',
+ 'n',
+ 'o',
+ 'p',
+ 'q',
+ 'r',
+ 's',
+ 't',
+ 'u',
+ 'v',
+ 'w',
+ 'x',
+ 'y',
+ 'z',
+ ',',
+ '.',
+
+ 0x0,//left alt
+ 0x0,//right alt
+ 0x0,//left ctrl
+ 0x0,//right ctrl
+
+// 0x0,//fn
+// 0x0,//cap
+
+ '\t',
+ ' ',
+ 0x0,//sym(bol)
+ 0x0,//explorer
+ 0x0,//envelope
+ '\n',//newline
+ 0x0,//delete
+ '`',
+ '-',
+ '=',
+ '[',
+ ']',
+ '\\',//backslash
+ ';',
+ '\'',//apostrophe
+ '/',//slash
+ '@',//at
+ 0x0,//num
+ 0x0,//headset hook
+ 0x0,//focus
+ 0x0,
+ 0x0,//menu
+ 0x0,//notification
+ 0x0,//search
+ 0x0,//media play/pause
+ 0x0,//media stop
+ 0x0,//media next
+ 0x0,//media previous
+ 0x0,//media rewind
+ 0x0,//media fastforward
+ 0x0,//mute
+ };
+
+ private static final int[] ANDROID_TO_JME = {
+ 0x0, // unknown
+ 0x0, // key code soft left
+ 0x0, // key code soft right
+ KeyInput.KEY_HOME,
+ KeyInput.KEY_ESCAPE, // key back
+ 0x0, // key call
+ 0x0, // key endcall
+ KeyInput.KEY_0,
+ KeyInput.KEY_1,
+ KeyInput.KEY_2,
+ KeyInput.KEY_3,
+ KeyInput.KEY_4,
+ KeyInput.KEY_5,
+ KeyInput.KEY_6,
+ KeyInput.KEY_7,
+ KeyInput.KEY_8,
+ KeyInput.KEY_9,
+ KeyInput.KEY_MULTIPLY,
+ 0x0, // key pound
+ KeyInput.KEY_UP,
+ KeyInput.KEY_DOWN,
+ KeyInput.KEY_LEFT,
+ KeyInput.KEY_RIGHT,
+ KeyInput.KEY_RETURN, // dpad center
+ 0x0, // volume up
+ 0x0, // volume down
+ KeyInput.KEY_POWER, // power (?)
+ 0x0, // camera
+ 0x0, // clear
+ KeyInput.KEY_A,
+ KeyInput.KEY_B,
+ KeyInput.KEY_C,
+ KeyInput.KEY_D,
+ KeyInput.KEY_E,
+ KeyInput.KEY_F,
+ KeyInput.KEY_G,
+ KeyInput.KEY_H,
+ KeyInput.KEY_I,
+ KeyInput.KEY_J,
+ KeyInput.KEY_K,
+ KeyInput.KEY_L,
+ KeyInput.KEY_M,
+ KeyInput.KEY_N,
+ KeyInput.KEY_O,
+ KeyInput.KEY_P,
+ KeyInput.KEY_Q,
+ KeyInput.KEY_R,
+ KeyInput.KEY_S,
+ KeyInput.KEY_T,
+ KeyInput.KEY_U,
+ KeyInput.KEY_V,
+ KeyInput.KEY_W,
+ KeyInput.KEY_X,
+ KeyInput.KEY_Y,
+ KeyInput.KEY_Z,
+ KeyInput.KEY_COMMA,
+ KeyInput.KEY_PERIOD,
+
+ KeyInput.KEY_LMENU,
+ KeyInput.KEY_RMENU,
+
+ KeyInput.KEY_LSHIFT,
+ KeyInput.KEY_RSHIFT,
+
+// 0x0, // fn
+// 0x0, // cap (?)
+
+ KeyInput.KEY_TAB,
+ KeyInput.KEY_SPACE,
+ 0x0, // sym (?) symbol
+ 0x0, // explorer
+ 0x0, // envelope
+ KeyInput.KEY_RETURN, // newline/enter
+ KeyInput.KEY_DELETE,
+ KeyInput.KEY_GRAVE,
+ KeyInput.KEY_MINUS,
+ KeyInput.KEY_EQUALS,
+
+ KeyInput.KEY_LBRACKET,
+ KeyInput.KEY_RBRACKET,
+
+ KeyInput.KEY_BACKSLASH,
+ KeyInput.KEY_SEMICOLON,
+ KeyInput.KEY_APOSTROPHE,
+ KeyInput.KEY_SLASH,
+ KeyInput.KEY_AT, // at (@)
+ KeyInput.KEY_NUMLOCK, //0x0, // num
+ 0x0, //headset hook
+ 0x0, //focus
+ KeyInput.KEY_ADD,
+ KeyInput.KEY_LMETA, //menu
+ 0x0,//notification
+ 0x0,//search
+ 0x0,//media play/pause
+ 0x0,//media stop
+ 0x0,//media next
+ 0x0,//media previous
+ 0x0,//media rewind
+ 0x0,//media fastforward
+ 0x0,//mute
+ };
+
+// private int[] keyMap = {
+// 0x0,
+// KeyEvent.KEYCODE_BACK, // ESC key
+//
+// KeyEvent.KEYCODE_1,
+// KeyEvent.KEYCODE_2,
+// KeyEvent.KEYCODE_3,
+// KeyEvent.KEYCODE_4,
+// KeyEvent.KEYCODE_5,
+// KeyEvent.KEYCODE_6,
+// KeyEvent.KEYCODE_7,
+// KeyEvent.KEYCODE_8,
+// KeyEvent.KEYCODE_9,
+// KeyEvent.KEYCODE_0,
+// KeyEvent.KEYCODE_MINUS,
+// KeyEvent.KEYCODE_EQUALS,
+// KeyEvent.KEYCODE_BACK,
+// KeyEvent.KEYCODE_TAB,
+// KeyEvent.KEYCODE_Q,
+// KeyEvent.KEYCODE_W,
+// KeyEvent.KEYCODE_E,
+// KeyEvent.KEYCODE_R,
+// KeyEvent.KEYCODE_T,
+// KeyEvent.KEYCODE_Y,
+// KeyEvent.KEYCODE_U,
+// KeyEvent.KEYCODE_I,
+// KeyEvent.KEYCODE_O,
+// KeyEvent.KEYCODE_P,
+// KeyEvent.KEYCODE_LEFT_BRACKET,
+// KeyEvent.KEYCODE_RIGHT_BRACKET,
+// KeyEvent.KEYCODE_ENTER,
+// KeyEvent.KEYCODE_SOFT_LEFT, // Left Ctrl
+// KeyEvent.KEYCODE_A,
+// KeyEvent.KEYCODE_S,
+// KeyEvent.KEYCODE_D,
+// KeyEvent.KEYCODE_F,
+// KeyEvent.KEYCODE_G,
+// KeyEvent.KEYCODE_H,
+// KeyEvent.KEYCODE_J,
+// KeyEvent.KEYCODE_K,
+// KeyEvent.KEYCODE_L,
+// KeyEvent.KEYCODE_SEMICOLON,
+// KeyEvent.KEYCODE_APOSTROPHE,
+// KeyEvent.KEYCODE_GRAVE,
+// KeyEvent.KEYCODE_SHIFT_LEFT,
+// KeyEvent.KEYCODE_BACKSLASH,
+// KeyEvent.KEYCODE_Z,
+// KeyEvent.KEYCODE_X,
+// KeyEvent.KEYCODE_C,
+// KeyEvent.KEYCODE_V,
+// KeyEvent.KEYCODE_B,
+// KeyEvent.KEYCODE_N,
+// KeyEvent.KEYCODE_M,
+//
+// KeyEvent.KEYCODE_COMMA,
+// KeyEvent.KEYCODE_PERIOD,
+// KeyEvent.KEYCODE_SLASH,
+// KeyEvent.KEYCODE_SHIFT_RIGHT,
+// KeyEvent.KEYCODE_STAR,
+//
+// KeyEvent.KEYCODE_ALT_LEFT,
+// KeyEvent.KEYCODE_SPACE,
+//
+// 0x0, // no caps lock
+//
+// 0x0, // F1
+// 0x0, // F2
+// 0x0, // F3
+// 0x0, // F4
+// 0x0, // F5
+// 0x0, // F6
+// 0x0, // F7
+// 0x0, // F8
+// 0x0, // F9
+// 0x0, // F10
+//
+// KeyEvent.KEYCODE_NUM,
+// 0x0, // scroll lock
+//
+// 0x0, // numpad7
+// 0x0, // numpad8
+// 0x0, // numpad9
+//
+// KeyEvent.
+// }
+
+ public AndroidInput(Context ctx, AttributeSet attribs){
+ super(ctx, attribs);
+ }
+
+ public AndroidInput(Context ctx){
+ super(ctx);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent motionEvent){
+ int newX = getWidth() - (int) motionEvent.getX();
+ int newY = (int) motionEvent.getY();
+
+
+ switch (motionEvent.getAction()){
+ case MotionEvent.ACTION_DOWN:
+ MouseButtonEvent btn = new MouseButtonEvent(0, true, newX, newY);
+ btn.setTime(motionEvent.getEventTime());
+ processEvent(btn);
+ // listener.onMouseButtonEvent(btn);
+ lastX = -1;
+ lastY = -1;
+ return true;
+ case MotionEvent.ACTION_UP:
+ MouseButtonEvent btn2 = new MouseButtonEvent(0, false, newX, newY);
+ btn2.setTime(motionEvent.getEventTime());
+ processEvent(btn2);
+ // listener.onMouseButtonEvent(btn2);
+ lastX = -1;
+ lastY = -1;
+ return true;
+ case MotionEvent.ACTION_MOVE:
+ // int newX = getWidth() - (int) motionEvent.getX();
+ // int newY = (int) motionEvent.getY();
+ int dx;
+ int dy;
+ if (lastX != -1){
+ dx = newX - lastX;
+ dy = newY - lastY;
+ }else{
+ dx = 0;
+ dy = 0;
+ }
+ lastX = newX;
+ lastY = newY;
+ MouseMotionEvent mot = new MouseMotionEvent(newX, newY, dx, dy, 0, 0);
+ mot.setTime(motionEvent.getEventTime());
+ processEvent(mot);
+ //listener.onMouseMotionEvent(mot);
+ try{
+ Thread.sleep(15);
+ } catch (InterruptedException ex) {
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onKeyDown (int keyCode, KeyEvent event) {
+ int jmeCode = ANDROID_TO_JME[keyCode];
+ String str = event.getCharacters();
+ char c = str != null && str.length() > 0 ? str.charAt(0) : 0x0;
+ KeyInputEvent evt = new KeyInputEvent(jmeCode, c, true, false);
+ processEvent(evt);
+ // listener.onKeyEvent(evt);
+ return false;
+ }
+
+ @Override
+ public boolean onKeyUp (int keyCode, KeyEvent event) {
+ int jmeCode = ANDROID_TO_JME[keyCode];
+ String str = event.getCharacters();
+ char c = str != null && str.length() > 0 ? str.charAt(0) : 0x0;
+ KeyInputEvent evt = new KeyInputEvent(jmeCode, c, false, false);
+ processEvent(evt);
+ //listener.onKeyEvent(evt);
+ return false;
+ }
+
+ public void setCursorVisible(boolean visible){
+ }
+
+ public int getButtonCount(){
+ return 255;
+ }
+
+ public void initialize() {
+ }
+
+ public void update() {
+ generateEvents();
+ }
+
+ public void destroy() {
+ }
+
+ public boolean isInitialized() {
+ return true;
+ }
+
+ // XXX: android does not have an Event interface?
+ private List currentEvents = new ArrayList();
+
+ private final static int MAX_EVENTS = 1024;
+
+ private void processEvent(Object event) {
+ synchronized (currentEvents) {
+ if (currentEvents.size() < MAX_EVENTS)
+ currentEvents.add(event);
+ }
+ }
+
+ private void generateEvents() {
+ synchronized (currentEvents) {
+ for (Object event: currentEvents) {
+ if (event instanceof MouseButtonEvent) {
+ listener.onMouseButtonEvent((MouseButtonEvent) event);
+ } else if (event instanceof MouseMotionEvent) {
+ listener.onMouseMotionEvent((MouseMotionEvent) event);
+ } else if (event instanceof KeyInputEvent) {
+ listener.onKeyEvent((KeyInputEvent) event);
+ }
+ }
+ currentEvents.clear();
+ }
+ }
+
+ public void setInputListener(RawInputListener listener) {
+ this.listener = listener;
+ }
+
+ public long getInputTimeNanos() {
+ return System.nanoTime();
+ }
+
+}
diff --git a/engine/src/android/com/jme3/renderer/android/OGLESRenderer.java b/engine/src/android/com/jme3/renderer/android/OGLESRenderer.java
new file mode 100644
index 000000000..7d7244a8b
--- /dev/null
+++ b/engine/src/android/com/jme3/renderer/android/OGLESRenderer.java
@@ -0,0 +1,991 @@
+package com.jme3.renderer.android;
+
+import com.jme3.light.DirectionalLight;
+import com.jme3.light.Light;
+import com.jme3.light.LightList;
+import com.jme3.light.PointLight;
+import com.jme3.material.RenderState;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Matrix4f;
+import com.jme3.renderer.Caps;
+import com.jme3.renderer.GLObjectManager;
+import com.jme3.renderer.IDList;
+import com.jme3.renderer.RenderContext;
+import com.jme3.renderer.Renderer;
+import com.jme3.renderer.Statistics;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Mesh.Mode;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.VertexBuffer.Usage;
+import com.jme3.shader.Shader;
+import com.jme3.shader.Shader.ShaderSource;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Image;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapAxis;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.IntMap;
+import com.jme3.util.IntMap.Entry;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.logging.Logger;
+import javax.microedition.khronos.opengles.GL10;
+import javax.microedition.khronos.opengles.GL11;
+import javax.microedition.khronos.opengles.GL11Ext;
+
+public final class OGLESRenderer implements Renderer {
+
+ private static final Logger logger = Logger.getLogger(OGLESRenderer.class.getName());
+
+ private Matrix4f worldMatrix = new Matrix4f();
+ private Matrix4f viewMatrix = new Matrix4f();
+ private Matrix4f projMatrix = new Matrix4f();
+ private FloatBuffer fb16 = BufferUtils.createFloatBuffer(16);
+ private float[] fa16 = new float[16];
+ private IntBuffer intBuf1 = BufferUtils.createIntBuffer(1);
+ private GL10 gl;
+ private GL11 gl11;
+ private GL11Ext glExt;
+
+ private RenderContext context = new RenderContext();
+ private GLObjectManager objManager = new GLObjectManager();
+
+ private EnumSet caps = EnumSet.noneOf(Caps.class);
+ private boolean powerOf2 = false;
+
+ private final Statistics statistics = new Statistics();
+ private int vpX, vpY, vpW, vpH;
+
+ public OGLESRenderer(GL10 gl){
+ setGL(gl);
+ }
+
+ public void setGL(GL10 gl){
+ this.gl = gl;
+ if (gl instanceof GL11){
+ gl11 = (GL11) gl;
+ if (gl instanceof GL11Ext){
+ glExt = (GL11Ext) gl;
+ }
+ }
+ }
+
+ public void initialize(){
+ logger.info("Vendor: "+gl.glGetString(gl.GL_VENDOR));
+ logger.info("Renderer: "+gl.glGetString(gl.GL_RENDERER));
+ logger.info("Version: "+gl.glGetString(gl.GL_VERSION));
+
+ String extensions = gl.glGetString(gl.GL_EXTENSIONS);
+ if (extensions.contains("GL_OES_texture_npot"))
+ powerOf2 = true;
+
+ applyRenderState(RenderState.DEFAULT);
+// gl.glClearDepthf(1.0f);
+ gl.glDisable(GL10.GL_DITHER);
+ gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
+ }
+
+ public Statistics getStatistics() {
+ return null;
+ }
+
+ public void setClipRect(int x, int y, int width, int height) {
+ }
+
+ public void clearClipRect() {
+ }
+
+ public void resetGLObjects() {
+ }
+
+ public EnumSet getCaps() {
+ return caps;
+ }
+
+ public void setBackgroundColor(ColorRGBA color) {
+ gl.glClearColor(color.r, color.g, color.b, color.a);
+ }
+
+ public void cleanup(){
+ objManager.deleteAllObjects(this);
+ }
+
+ public void clearBuffers(boolean color, boolean depth, boolean stencil) {
+ int bits = 0;
+ if (color) bits = gl.GL_COLOR_BUFFER_BIT;
+ if (depth) bits |= gl.GL_DEPTH_BUFFER_BIT;
+ if (stencil) bits |= gl.GL_STENCIL_BUFFER_BIT;
+ if (bits != 0) gl.glClear(bits);
+ }
+
+ public void applyRenderState(RenderState state){
+ // TODO: is wireframe supported under OGL ES?
+
+// if (state.isWireframe() && !context.wireframe){
+// gl.glPolygonMode(gl.GL_FRONT_AND_BACK, gl.GL_LINE);
+// context.wireframe = true;
+// }else if (!state.isWireframe() && context.wireframe){
+// gl.glPolygonMode(gl.GL_FRONT_AND_BACK, gl.GL_FILL);
+// context.wireframe = false;
+// }
+
+ if (state.isDepthTest() && !context.depthTestEnabled){
+ gl.glEnable(gl.GL_DEPTH_TEST);
+ gl.glDepthFunc(gl.GL_LEQUAL);
+ context.depthTestEnabled = true;
+ }else if (!state.isDepthTest() && context.depthTestEnabled){
+ gl.glDisable(gl.GL_DEPTH_TEST);
+ context.depthTestEnabled = false;
+ }
+ if (state.isAlphaTest() && !context.alphaTestEnabled){
+ gl.glEnable(gl.GL_ALPHA_TEST);
+ gl.glAlphaFunc(gl.GL_GREATER, state.getAlphaFallOff());
+ context.alphaTestEnabled = true;
+ }else if (!state.isAlphaTest() && context.alphaTestEnabled){
+ gl.glDisable(gl.GL_ALPHA_TEST);
+ context.alphaTestEnabled = false;
+ }
+ if (state.isDepthWrite() && !context.depthWriteEnabled){
+ gl.glDepthMask(true);
+ context.depthWriteEnabled = true;
+ }else if (!state.isDepthWrite() && context.depthWriteEnabled){
+ gl.glDepthMask(false);
+ context.depthWriteEnabled = false;
+ }
+ if (state.isColorWrite() && !context.colorWriteEnabled){
+ gl.glColorMask(true,true,true,true);
+ context.colorWriteEnabled = true;
+ }else if (!state.isColorWrite() && context.colorWriteEnabled){
+ gl.glColorMask(false,false,false,false);
+ context.colorWriteEnabled = false;
+ }
+ if (state.isPolyOffset()){
+ if (!context.polyOffsetEnabled){
+ gl.glEnable(gl.GL_POLYGON_OFFSET_FILL);
+ gl.glPolygonOffset(state.getPolyOffsetFactor(),
+ state.getPolyOffsetUnits());
+ context.polyOffsetEnabled = true;
+ context.polyOffsetFactor = state.getPolyOffsetFactor();
+ context.polyOffsetUnits = state.getPolyOffsetUnits();
+ }else{
+ if (state.getPolyOffsetFactor() != context.polyOffsetFactor
+ || state.getPolyOffsetUnits() != context.polyOffsetUnits){
+ gl.glPolygonOffset(state.getPolyOffsetFactor(),
+ state.getPolyOffsetUnits());
+ context.polyOffsetFactor = state.getPolyOffsetFactor();
+ context.polyOffsetUnits = state.getPolyOffsetUnits();
+ }
+ }
+ }else{
+ if (context.polyOffsetEnabled){
+ gl.glDisable(gl.GL_POLYGON_OFFSET_FILL);
+ context.polyOffsetEnabled = false;
+ context.polyOffsetFactor = 0;
+ context.polyOffsetUnits = 0;
+ }
+ }
+ if (state.getFaceCullMode() != context.cullMode){
+ if (state.getFaceCullMode() == RenderState.FaceCullMode.Off)
+ gl.glDisable(gl.GL_CULL_FACE);
+ else
+ gl.glEnable(gl.GL_CULL_FACE);
+
+ switch (state.getFaceCullMode()){
+ case Off:
+ break;
+ case Back:
+ gl.glCullFace(gl.GL_BACK);
+ break;
+ case Front:
+ gl.glCullFace(gl.GL_FRONT);
+ break;
+ case FrontAndBack:
+ gl.glCullFace(gl.GL_FRONT_AND_BACK);
+ break;
+ default:
+ throw new UnsupportedOperationException("Unrecognized face cull mode: "+
+ state.getFaceCullMode());
+ }
+
+ context.cullMode = state.getFaceCullMode();
+ }
+
+ if (state.getBlendMode() != context.blendMode){
+ if (state.getBlendMode() == RenderState.BlendMode.Off)
+ gl.glDisable(gl.GL_BLEND);
+ else
+ gl.glEnable(gl.GL_BLEND);
+
+ switch (state.getBlendMode()){
+ case Off:
+ break;
+ case Additive:
+ gl.glBlendFunc(gl.GL_ONE, gl.GL_ONE);
+ break;
+ case Alpha:
+ gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA);
+ break;
+ case PremultAlpha:
+ gl.glBlendFunc(gl.GL_ONE, gl.GL_ONE_MINUS_SRC_ALPHA);
+ break;
+ case Modulate:
+ gl.glBlendFunc(gl.GL_DST_COLOR, gl.GL_ZERO);
+ break;
+ case ModulateX2:
+ gl.glBlendFunc(gl.GL_DST_COLOR, gl.GL_SRC_COLOR);
+ break;
+ default:
+ throw new UnsupportedOperationException("Unrecognized blend mode: "+
+ state.getBlendMode());
+ }
+
+ context.blendMode = state.getBlendMode();
+ }
+ }
+
+ public void onFrame() {
+ objManager.deleteUnused(this);
+ }
+
+ public void setDepthRange(float start, float end) {
+ gl.glDepthRangef(start, end);
+ }
+
+ public void setViewPort(int x, int y, int width, int height){
+ gl.glViewport(x, y, width, height);
+ vpX = x;
+ vpY = y;
+ vpW = width;
+ vpH = height;
+ }
+
+ public void setViewProjectionMatrices(Matrix4f viewMatrix, Matrix4f projMatrix){
+ this.viewMatrix.set(viewMatrix);
+ this.projMatrix.set(projMatrix);
+
+ if (context.matrixMode != gl.GL_PROJECTION){
+ gl.glMatrixMode(gl.GL_PROJECTION);
+ context.matrixMode = gl.GL_PROJECTION;
+ }
+
+ projMatrix.fillFloatArray(fa16, true);
+ gl.glLoadMatrixf(fa16, 0);
+
+// gl.glMatrixMode(gl.GL_MODELVIEW);
+// gl.glLoadIdentity();
+// gl.glLoadMatrixf(storeMatrix(viewMatrix, fb16));
+ }
+
+ public void setWorldMatrix(Matrix4f worldMatrix) {
+ this.worldMatrix.set(worldMatrix);
+
+ if (context.matrixMode != gl.GL_MODELVIEW){
+ gl.glMatrixMode(gl.GL_MODELVIEW);
+ context.matrixMode = gl.GL_MODELVIEW;
+ }
+
+ viewMatrix.fillFloatArray(fa16, true);
+ gl.glLoadMatrixf(fa16, 0);
+ worldMatrix.fillFloatArray(fa16, true);
+ gl.glMultMatrixf(fa16, 0);
+ }
+
+ public void setMatrixPalette(Matrix4f[] offsetMatrices){
+ if (glExt == null)
+ throw new UnsupportedOperationException("Requires GL_OES_compressed_paletted_texture");
+
+ if (context.matrixMode != glExt.GL_MATRIX_PALETTE_OES){
+ gl.glMatrixMode(glExt.GL_MATRIX_PALETTE_OES);
+ context.matrixMode = glExt.GL_MATRIX_PALETTE_OES;
+ }
+
+ for (int i = 0; i < offsetMatrices.length; i++){
+ Matrix4f offsetMat = offsetMatrices[i];
+ glExt.glCurrentPaletteMatrixOES(i);
+
+ offsetMat.fillFloatArray(fa16, true);
+ gl.glLoadMatrixf(fa16, 0);
+ }
+ }
+
+ public void setLighting(LightList list) {
+ if (list.size() == 0) {
+ // turn off lighting
+ gl.glDisable(gl.GL_LIGHTING);
+ return;
+ }
+
+ gl.glEnable(gl.GL_LIGHTING);
+ gl.glShadeModel(gl.GL_SMOOTH);
+
+ float[] temp = new float[4];
+
+ // reset model view to specify
+ // light positions in world space
+ // instead of model space
+ gl.glPushMatrix();
+ gl.glLoadIdentity();
+
+ for (int i = 0; i < list.size()+1; i++){
+ if (list.size() <= i){
+ // goes beyond the num lights we need
+ // disable it
+ gl.glDisable(gl.GL_LIGHT0 + i);
+ break;
+ }
+
+ Light l = list.get(i);
+ int lightId = gl.GL_LIGHT0 + i;
+
+ ColorRGBA color = l.getColor();
+ color.toArray(temp);
+
+ gl.glEnable(lightId);
+ gl.glLightfv(lightId, gl.GL_DIFFUSE, temp, 0);
+ gl.glLightfv(lightId, gl.GL_SPECULAR, temp, 0);
+
+ ColorRGBA.Black.toArray(temp);
+ gl.glLightfv(lightId, gl.GL_AMBIENT, temp, 0);
+
+ switch (l.getType()){
+ case Directional:
+ DirectionalLight dl = (DirectionalLight) l;
+ dl.getDirection().toArray(temp);
+ temp[3] = 0f; // marks to GL its a directional light
+ gl.glLightfv(lightId, gl.GL_POSITION, temp, 0);
+ break;
+ case Point:
+ PointLight pl = (PointLight) l;
+ pl.getPosition().toArray(temp);
+ temp[3] = 1f; // marks to GL its a point light
+ gl.glLightfv(lightId, gl.GL_POSITION, temp, 0);
+ break;
+ }
+
+ }
+
+ // restore modelview to original value
+ gl.glPopMatrix();
+ }
+
+ public void updateShaderSourceData(ShaderSource source) {
+ }
+
+ public void deleteShaderSource(ShaderSource source) {
+ }
+
+ public void updateShaderData(Shader shader) {
+ }
+
+ public void setShader(Shader shader) {
+ }
+
+ public void deleteShader(Shader shader) {
+ }
+
+ public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst) {
+ }
+
+ public void setFrameBuffer(FrameBuffer fb) {
+ }
+
+ public void updateFrameBuffer(FrameBuffer fb) {
+ }
+
+ public void deleteFrameBuffer(FrameBuffer fb) {
+ }
+
+ /**
+ * Warning: documentation states that this method returns data in BGRA format,
+ * it actually returns data in RGBA format.
+ * @param fb
+ * @param byteBuf
+ */
+ public void readFrameBuffer(FrameBuffer fb, ByteBuffer byteBuf) {
+ if (fb == null){
+ gl.glReadPixels(vpX, vpY, vpW, vpH, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, byteBuf);
+ }else{
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ private int convertTextureType(Texture.Type type){
+ switch (type){
+ case TwoDimensional:
+ return gl.GL_TEXTURE_2D;
+ default:
+ throw new UnsupportedOperationException("Unknown texture type: "+type);
+ }
+ }
+
+ private int convertMagFilter(Texture.MagFilter filter){
+ switch (filter){
+ case Bilinear:
+ return gl.GL_LINEAR;
+ case Nearest:
+ return gl.GL_NEAREST;
+ default:
+ throw new UnsupportedOperationException("Unknown mag filter: "+filter);
+ }
+ }
+
+ private int convertMinFilter(Texture.MinFilter filter){
+ switch (filter){
+ case Trilinear:
+ return gl.GL_LINEAR_MIPMAP_LINEAR;
+ case BilinearNearestMipMap:
+ return gl.GL_LINEAR_MIPMAP_NEAREST;
+ case NearestLinearMipMap:
+ return gl.GL_NEAREST_MIPMAP_LINEAR;
+ case NearestNearestMipMap:
+ return gl.GL_NEAREST_MIPMAP_NEAREST;
+ case BilinearNoMipMaps:
+ return gl.GL_LINEAR;
+ case NearestNoMipMaps:
+ return gl.GL_NEAREST;
+ default:
+ throw new UnsupportedOperationException("Unknown min filter: "+filter);
+ }
+ }
+
+ private int convertWrapMode(Texture.WrapMode mode){
+ switch (mode){
+// case BorderClamp:
+// return gl.GL_CLAMP_TO_BORDER;
+// case Clamp:
+// return gl.GL_CLAMP;
+ case EdgeClamp:
+ return gl.GL_CLAMP_TO_EDGE;
+ case Repeat:
+ return gl.GL_REPEAT;
+// case MirroredRepeat:
+// return gl.GL_MIRRORED_REPEAT;
+ default:
+ throw new UnsupportedOperationException("Unknown wrap mode: "+mode);
+ }
+ }
+
+ public void updateTextureData(Texture tex){
+ int texId = tex.getId();
+ if (texId == -1){
+ // create texture
+ gl.glGenTextures(1, intBuf1);
+ texId = intBuf1.get(0);
+ tex.setId(texId);
+ objManager.registerForCleanup(tex);
+ }
+
+ // bind texture
+ int target = convertTextureType(tex.getType());
+ if (context.boundTextures[0] != tex){
+ if (context.boundTextureUnit != 0){
+ gl.glActiveTexture(gl.GL_TEXTURE0);
+ context.boundTextureUnit = 0;
+ }
+
+ gl.glBindTexture(target, texId);
+ context.boundTextures[0] = tex;
+ }
+
+ // filter things
+ int minFilter = convertMinFilter(tex.getMinFilter());
+ int magFilter = convertMagFilter(tex.getMagFilter());
+ gl.glTexParameterx(target, gl.GL_TEXTURE_MIN_FILTER, minFilter);
+ gl.glTexParameterx(target, gl.GL_TEXTURE_MAG_FILTER, magFilter);
+
+ // repeat modes
+ switch (tex.getType()){
+ case TwoDimensional:
+ gl.glTexParameterx(target, gl.GL_TEXTURE_WRAP_T, convertWrapMode(tex.getWrap(WrapAxis.T)));
+ // fall down here is intentional..
+// case OneDimensional:
+ gl.glTexParameterx(target, gl.GL_TEXTURE_WRAP_S, convertWrapMode(tex.getWrap(WrapAxis.S)));
+ break;
+ default:
+ throw new UnsupportedOperationException("Unknown texture type: "+tex.getType());
+ }
+
+ Image img = tex.getImage();
+ if (img != null){
+ boolean generateMips = false;
+ if (!img.hasMipmaps() && tex.getMinFilter().usesMipMapLevels()){
+ // No pregenerated mips available,
+ // generate from base level if required
+ if (gl11 != null){
+ gl.glTexParameterx(target, GL11.GL_GENERATE_MIPMAP, gl.GL_TRUE);
+ }else{
+ generateMips = true;
+ }
+ }
+
+ TextureUtil.uploadTexture(gl, img, tex.getImageDataIndex(), generateMips, powerOf2);
+ }
+
+ tex.clearUpdateNeeded();
+ }
+
+ private void checkTexturingUsed(){
+ IDList textureList = context.textureIndexList;
+ // old mesh used texturing, new mesh doesn't use it
+ // should actually go through entire oldLen and
+ // disable texturing for each unit.. but that's for later.
+ if (textureList.oldLen > 0 && textureList.newLen == 0){
+ gl.glDisable(gl.GL_TEXTURE_2D);
+ }
+ }
+
+ public void setTexture(int unit, Texture tex){
+ if (tex.isUpdateNeeded())
+ updateTextureData(tex);
+
+ int texId = tex.getId();
+ assert texId != -1;
+
+ Texture[] textures = context.boundTextures;
+
+ int type = convertTextureType(tex.getType());
+ if (!context.textureIndexList.moveToNew(unit)){
+ if (context.boundTextureUnit != unit){
+ gl.glActiveTexture(gl.GL_TEXTURE0 + unit);
+ context.boundTextureUnit = unit;
+ }
+
+ gl.glEnable(type);
+ }
+
+ if (textures[unit] != tex){
+ if (context.boundTextureUnit != unit){
+ gl.glActiveTexture(gl.GL_TEXTURE0 + unit);
+ context.boundTextureUnit = unit;
+ }
+
+ gl.glBindTexture(type, texId);
+ textures[unit] = tex;
+ }
+ }
+
+ public void clearTextureUnits(){
+ IDList textureList = context.textureIndexList;
+ Texture[] textures = context.boundTextures;
+ for (int i = 0; i < textureList.oldLen; i++){
+ int idx = textureList.oldList[i];
+
+ if (context.boundTextureUnit != idx){
+ gl.glActiveTexture(gl.GL_TEXTURE0 + idx);
+ context.boundTextureUnit = idx;
+ }
+ gl.glDisable(convertTextureType(textures[idx].getType()));
+ textures[idx] = null;
+ }
+ context.textureIndexList.copyNewToOld();
+ }
+
+ public void deleteTexture(Texture tex){
+ int texId = tex.getId();
+ if (texId != -1){
+ intBuf1.put(0, texId);
+ intBuf1.position(0).limit(1);
+ gl.glDeleteTextures(1, intBuf1);
+ tex.resetObject();
+ }
+ }
+
+ private int convertUsage(Usage usage){
+ switch (usage){
+ case Static:
+ return gl11.GL_STATIC_DRAW;
+ case Dynamic:
+ case Stream:
+ return gl11.GL_DYNAMIC_DRAW;
+ default:
+ throw new RuntimeException("Unknown usage type: "+usage);
+ }
+ }
+
+ public void updateBufferData(VertexBuffer vb) {
+ int bufId = vb.getId();
+ if (bufId == -1){
+ // create buffer
+ gl11.glGenBuffers(1, intBuf1);
+ bufId = intBuf1.get(0);
+ vb.setId(bufId);
+ objManager.registerForCleanup(vb);
+ }
+
+ int target;
+ if (vb.getBufferType() == VertexBuffer.Type.Index){
+ target = gl11.GL_ELEMENT_ARRAY_BUFFER;
+ if (context.boundElementArrayVBO != bufId){
+ gl11.glBindBuffer(target, bufId);
+ context.boundElementArrayVBO = bufId;
+ }
+ }else{
+ target = gl11.GL_ARRAY_BUFFER;
+ if (context.boundArrayVBO != bufId){
+ gl11.glBindBuffer(target, bufId);
+ context.boundArrayVBO = bufId;
+ }
+ }
+
+ int usage = convertUsage(vb.getUsage());
+ Buffer data = vb.getData();
+ data.rewind();
+
+ gl11.glBufferData(target,
+ data.capacity() * vb.getFormat().getComponentSize(),
+ data,
+ usage);
+
+ vb.clearUpdateNeeded();
+ }
+
+ public void deleteBuffer(VertexBuffer vb) {
+ int bufId = vb.getId();
+ if (bufId != -1){
+ // delete buffer
+ intBuf1.put(0, bufId);
+ intBuf1.position(0).limit(1);
+ gl11.glDeleteBuffers(1, intBuf1);
+ vb.resetObject();
+ }
+ }
+
+ private int convertArrayType(VertexBuffer.Type type){
+ switch (type){
+ case Position:
+ return gl.GL_VERTEX_ARRAY;
+ case Normal:
+ return gl.GL_NORMAL_ARRAY;
+ case TexCoord:
+ return gl.GL_TEXTURE_COORD_ARRAY;
+ case Color:
+ return gl.GL_COLOR_ARRAY;
+ default:
+ return -1; // unsupported
+ }
+ }
+
+ private int convertVertexFormat(VertexBuffer.Format fmt){
+ switch (fmt){
+ case Byte:
+ return gl.GL_BYTE;
+ case Float:
+ return gl.GL_FLOAT;
+ case Short:
+ return gl.GL_SHORT;
+ case UnsignedByte:
+ return gl.GL_UNSIGNED_BYTE;
+ case UnsignedShort:
+ return gl.GL_UNSIGNED_SHORT;
+ case Int:
+ return gl.GL_FIXED;
+ default:
+ throw new UnsupportedOperationException("Unrecognized vertex format: "+fmt);
+ }
+ }
+
+ private int convertElementMode(Mesh.Mode mode){
+ switch (mode){
+ case Points:
+ return gl.GL_POINTS;
+ case Lines:
+ return gl.GL_LINES;
+ case LineLoop:
+ return gl.GL_LINE_LOOP;
+ case LineStrip:
+ return gl.GL_LINE_STRIP;
+ case Triangles:
+ return gl.GL_TRIANGLES;
+ case TriangleFan:
+ return gl.GL_TRIANGLE_FAN;
+ case TriangleStrip:
+ return gl.GL_TRIANGLE_STRIP;
+ default:
+ throw new UnsupportedOperationException("Unrecognized mesh mode: "+mode);
+ }
+ }
+
+ private void setVertexAttribVBO(VertexBuffer vb, VertexBuffer idb){
+ int arrayType = convertArrayType(vb.getBufferType());
+ if (arrayType == -1)
+ return; // unsupported
+
+ if (vb.isUpdateNeeded() && idb == null)
+ updateBufferData(vb);
+
+ int bufId = idb != null ? idb.getId() : vb.getId();
+ if (context.boundArrayVBO != bufId){
+ gl11.glBindBuffer(gl11.GL_ARRAY_BUFFER, bufId);
+ context.boundArrayVBO = bufId;
+ }
+
+ gl.glEnableClientState(arrayType);
+ context.boundAttribs[vb.getBufferType().ordinal()] = vb;
+
+ if (vb.getBufferType() == Type.Normal){
+ // normalize if requested
+ if (vb.isNormalized() && !context.normalizeEnabled){
+ gl.glEnable(gl.GL_NORMALIZE);
+ context.normalizeEnabled = true;
+ }else if (!vb.isNormalized() && context.normalizeEnabled){
+ gl.glDisable(gl.GL_NORMALIZE);
+ context.normalizeEnabled = false;
+ }
+ }
+
+ int comps = vb.getNumComponents();
+ int type = convertVertexFormat(vb.getFormat());
+
+ switch (vb.getBufferType()){
+ case Position:
+ gl11.glVertexPointer(comps, type, vb.getStride(), vb.getOffset());
+ break;
+ case Normal:
+ gl11.glNormalPointer(type, vb.getStride(), vb.getOffset());
+ break;
+ case Color:
+ gl11.glColorPointer(comps, type, vb.getStride(), vb.getOffset());
+ break;
+ case TexCoord:
+ gl11.glTexCoordPointer(comps, type, vb.getStride(), vb.getOffset());
+ break;
+ }
+ }
+
+ private void drawTriangleListVBO(VertexBuffer indexBuf, Mesh mesh, int count){
+ if (indexBuf.getBufferType() != VertexBuffer.Type.Index)
+ throw new IllegalArgumentException("Only index buffers are allowed as triangle lists.");
+
+ if (indexBuf.isUpdateNeeded())
+ updateBufferData(indexBuf);
+
+ int bufId = indexBuf.getId();
+ assert bufId != -1;
+
+ if (context.boundElementArrayVBO != bufId){
+ gl11.glBindBuffer(gl11.GL_ELEMENT_ARRAY_BUFFER, bufId);
+ context.boundElementArrayVBO = bufId;
+ }
+
+ if (mesh.getMode() == Mode.Hybrid){
+ int[] modeStart = mesh.getModeStart();
+ int[] elementLengths = mesh.getElementLengths();
+
+ int elMode = convertElementMode(Mode.Triangles);
+ int fmt = convertVertexFormat(indexBuf.getFormat());
+ int elSize = indexBuf.getFormat().getComponentSize();
+// int listStart = modeStart[0];
+ int stripStart = modeStart[1];
+ int fanStart = modeStart[2];
+ int curOffset = 0;
+ for (int i = 0; i < elementLengths.length; i++){
+ if (i == stripStart){
+ elMode = convertElementMode(Mode.TriangleStrip);
+ }else if (i == fanStart){
+ elMode = convertElementMode(Mode.TriangleStrip);
+ }
+ int elementLength = elementLengths[i];
+ gl11.glDrawElements(elMode,
+ elementLength,
+ fmt,
+ curOffset);
+ curOffset += elementLength * elSize;
+ }
+ }else{
+ gl11.glDrawElements(convertElementMode(mesh.getMode()),
+ indexBuf.getData().capacity(),
+ convertVertexFormat(indexBuf.getFormat()),
+ 0);
+ }
+ }
+
+ public void setVertexAttrib(VertexBuffer vb, VertexBuffer idb) {
+ int arrayType = convertArrayType(vb.getBufferType());
+ if (arrayType == -1)
+ return; // unsupported
+
+ gl.glEnableClientState(arrayType);
+ context.boundAttribs[vb.getBufferType().ordinal()] = vb;
+
+ if (vb.getBufferType() == Type.Normal){
+ // normalize if requested
+ if (vb.isNormalized() && !context.normalizeEnabled){
+ gl.glEnable(gl.GL_NORMALIZE);
+ context.normalizeEnabled = true;
+ }else if (!vb.isNormalized() && context.normalizeEnabled){
+ gl.glDisable(gl.GL_NORMALIZE);
+ context.normalizeEnabled = false;
+ }
+ }
+
+ // NOTE: Use data from interleaved buffer if specified
+ Buffer data = idb != null ? idb.getData() : vb.getData();
+ int comps = vb.getNumComponents();
+ int type = convertVertexFormat(vb.getFormat());
+ data.clear();
+ data.position(vb.getOffset());
+
+ switch (vb.getBufferType()){
+ case Position:
+ gl.glVertexPointer(comps, type, vb.getStride(), data);
+ break;
+ case Normal:
+ gl.glNormalPointer(type, vb.getStride(), data);
+ break;
+ case Color:
+ gl.glColorPointer(comps, type, vb.getStride(), data);
+ break;
+ case TexCoord:
+ gl.glTexCoordPointer(comps, type, vb.getStride(), data);
+ break;
+ }
+ }
+
+ public void setVertexAttrib(VertexBuffer vb){
+ setVertexAttrib(vb, null);
+ }
+
+ public void clearVertexAttribs() {
+ for (int i = 0; i < 16; i++){
+ VertexBuffer vb = context.boundAttribs[i];
+ if (vb != null){
+ int arrayType = convertArrayType(vb.getBufferType());
+ gl.glDisableClientState(arrayType);
+ context.boundAttribs[vb.getBufferType().ordinal()] = null;
+ }
+ }
+ }
+
+ public void drawTriangleList(VertexBuffer indexBuf, Mesh mesh, int count) {
+ Mesh.Mode mode = mesh.getMode();
+
+ Buffer indexData = indexBuf.getData();
+ indexData.clear();
+ if (mesh.getMode() == Mode.Hybrid){
+ int[] modeStart = mesh.getModeStart();
+ int[] elementLengths = mesh.getElementLengths();
+
+ int elMode = convertElementMode(Mode.Triangles);
+ int fmt = convertVertexFormat(indexBuf.getFormat());
+// int elSize = indexBuf.getFormat().getComponentSize();
+// int listStart = modeStart[0];
+ int stripStart = modeStart[1];
+ int fanStart = modeStart[2];
+ int curOffset = 0;
+ for (int i = 0; i < elementLengths.length; i++){
+ if (i == stripStart){
+ elMode = convertElementMode(Mode.TriangleStrip);
+ }else if (i == fanStart){
+ elMode = convertElementMode(Mode.TriangleStrip);
+ }
+ int elementLength = elementLengths[i];
+ indexData.position(curOffset);
+ gl.glDrawElements(elMode,
+ elementLength,
+ fmt,
+ indexData);
+ curOffset += elementLength;
+ }
+ }else{
+ gl.glDrawElements(convertElementMode(mode),
+ indexData.capacity(),
+ convertVertexFormat(indexBuf.getFormat()),
+ indexData);
+ }
+ }
+
+ private void renderMeshDefault(Mesh mesh, int lod, int count) {
+ VertexBuffer indices = null;
+ VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData);
+ IntMap buffers = mesh.getBuffers();
+ if (mesh.getNumLodLevels() > 0){
+ indices = mesh.getLodLevel(lod);
+ }else{
+ indices = buffers.get(Type.Index.ordinal());
+ }
+ for (Entry entry : buffers){
+ VertexBuffer vb = entry.getValue();
+
+ if (vb.getBufferType() == Type.InterleavedData
+ || vb.getUsage() == Usage.CpuOnly) // ignore cpu-only buffers
+ continue;
+
+ if (vb.getBufferType() == Type.Index){
+ indices = vb;
+ }else{
+ if (vb.getStride() == 0){
+ // not interleaved
+ setVertexAttrib(vb);
+ }else{
+ // interleaved
+ setVertexAttrib(vb, interleavedData);
+ }
+ }
+ }
+
+ if (indices != null){
+ drawTriangleList(indices, mesh, count);
+ }else{
+ gl.glDrawArrays(convertElementMode(mesh.getMode()), 0, mesh.getVertexCount());
+ }
+ clearVertexAttribs();
+ clearTextureUnits();
+ }
+
+ private void renderMeshVBO(Mesh mesh, int lod, int count){
+ VertexBuffer indices = null;
+ VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData);
+ if (interleavedData != null && interleavedData.isUpdateNeeded()){
+ updateBufferData(interleavedData);
+ }
+ IntMap buffers = mesh.getBuffers();
+ if (mesh.getNumLodLevels() > 0){
+ indices = mesh.getLodLevel(lod);
+ }else{
+ indices = buffers.get(Type.Index.ordinal());
+ }
+ for (Entry entry : buffers){
+ VertexBuffer vb = entry.getValue();
+
+ if (vb.getBufferType() == Type.InterleavedData
+ || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers
+ || vb.getBufferType() == Type.Index)
+ continue;
+
+ if (vb.getStride() == 0){
+ // not interleaved
+ setVertexAttribVBO(vb, null);
+ }else{
+ // interleaved
+ setVertexAttribVBO(vb, interleavedData);
+ }
+ }
+
+ if (indices != null){
+ drawTriangleListVBO(indices, mesh, count);
+ }else{
+ gl.glDrawArrays(convertElementMode(mesh.getMode()), 0, mesh.getVertexCount());
+ }
+ clearVertexAttribs();
+ clearTextureUnits();
+ }
+
+ public void renderMesh(Mesh mesh, int lod, int count){
+ // check if texturing is used for new model, if not
+ // disable texturing entirely.
+ checkTexturingUsed();
+ if (gl11 != null){
+ // use vbo
+ renderMeshVBO(mesh, lod, count);
+ }else{
+ // use vertex arrays
+ renderMeshDefault(mesh, lod, count);
+ }
+ }
+
+}
diff --git a/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java b/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java
new file mode 100644
index 000000000..6846166b1
--- /dev/null
+++ b/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java
@@ -0,0 +1,2848 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.renderer.android;
+
+import com.jme3.light.LightList;
+import com.jme3.material.RenderState;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Caps;
+import com.jme3.renderer.GLObjectManager;
+import com.jme3.renderer.IDList;
+import com.jme3.renderer.Renderer;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Format;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.VertexBuffer.Usage;
+import com.jme3.renderer.RenderContext;
+import com.jme3.renderer.RendererException;
+import com.jme3.renderer.Statistics;
+import com.jme3.scene.Mesh.Mode;
+import com.jme3.shader.Attribute;
+import com.jme3.shader.Shader;
+import com.jme3.shader.Shader.ShaderSource;
+import com.jme3.shader.Shader.ShaderType;
+import com.jme3.shader.Uniform;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.FrameBuffer.RenderBuffer;
+import com.jme3.texture.Image;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapAxis;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.IntMap;
+import com.jme3.util.IntMap.Entry;
+import com.jme3.util.ListMap;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.DoubleBuffer;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.nio.ShortBuffer;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/*
+//import org.lwjgl.opengl.ARBGeometryShader4;
+//import org.lwjgl.opengl.ARBHalfFloatVertex;
+//import org.lwjgl.opengl.ARBVertexArrayObject;
+//import org.lwjgl.opengl.ARBHalfFloatVertex;
+//import org.lwjgl.opengl.ARBVertexArrayObject;
+import org.lwjgl.opengl.ARBDrawBuffers;
+//import org.lwjgl.opengl.ARBDrawInstanced;
+import org.lwjgl.opengl.ARBDrawInstanced;
+import org.lwjgl.opengl.ARBMultisample;
+import org.lwjgl.opengl.ContextCapabilities;
+import org.lwjgl.opengl.EXTTextureArray;
+import org.lwjgl.opengl.EXTTextureFilterAnisotropic;
+import org.lwjgl.opengl.GLContext;
+import org.lwjgl.opengl.NVHalfFloat;
+
+import static org.lwjgl.opengl.GL11.*;
+import static org.lwjgl.opengl.GL12.*;
+import static org.lwjgl.opengl.GL13.*;
+import static org.lwjgl.opengl.GL14.*;
+import static org.lwjgl.opengl.GL15.*;
+import static org.lwjgl.opengl.GL20.*;
+
+import static org.lwjgl.opengl.EXTFramebufferObject.*;
+import static org.lwjgl.opengl.EXTFramebufferMultisample.*;
+import static org.lwjgl.opengl.EXTFramebufferBlit.*;
+import org.lwjgl.opengl.ARBShaderObjects.*;
+import org.lwjgl.opengl.ARBVertexArrayObject;
+//import static org.lwjgl.opengl.ARBDrawInstanced.*;
+*/
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.opengl.GLES10;
+import android.opengl.GLES11;
+import android.opengl.GLES20;
+import android.opengl.GLSurfaceView;
+import android.opengl.GLUtils;
+import android.opengl.Matrix;
+import android.os.SystemClock;
+import android.util.Log;
+
+
+
+public class OGLESShaderRenderer implements Renderer {
+
+ private static final Logger logger = Logger.getLogger(OGLESShaderRenderer.class.getName());
+ private static final boolean VALIDATE_SHADER = false;
+
+ private final ByteBuffer nameBuf = BufferUtils.createByteBuffer(250);
+ private final StringBuilder stringBuf = new StringBuilder(250);
+
+ private final IntBuffer intBuf1 = BufferUtils.createIntBuffer(1);
+ private final IntBuffer intBuf16 = BufferUtils.createIntBuffer(16);
+
+ private final RenderContext context = new RenderContext();
+ private final GLObjectManager objManager = new GLObjectManager();
+ private final EnumSet caps = EnumSet.noneOf(Caps.class);
+
+ // current state
+ private Shader boundShader;
+ private int initialDrawBuf, initialReadBuf;
+
+ private int glslVer;
+ private int vertexTextureUnits;
+ private int fragTextureUnits;
+ private int vertexUniforms;
+ private int fragUniforms;
+ private int vertexAttribs;
+ private int maxFBOSamples;
+ private int maxFBOAttachs;
+ private int maxMRTFBOAttachs;
+ private int maxRBSize;
+ private int maxTexSize;
+ private int maxCubeTexSize;
+ private int maxVertCount;
+ private int maxTriCount;
+ private boolean tdc;
+ private FrameBuffer lastFb = null;
+
+ private final Statistics statistics = new Statistics();
+
+ private int vpX, vpY, vpW, vpH;
+ private int clipX, clipY, clipW, clipH;
+
+
+ private final GL10 gl;
+ private boolean powerOf2 = false;
+ private boolean verboseLogging = false;
+ private boolean useVBO = true;
+
+
+ public OGLESShaderRenderer(GL10 gl) {
+ this.gl = gl;
+ }
+
+ public void setUseVA(boolean value) {
+ logger.info("use_VBO [" + useVBO + "] -> [" + (!value) + "]");
+ useVBO = !value;
+ }
+
+ public void setVerboseLogging(boolean value) {
+ logger.info("verboseLogging [" + verboseLogging + "] -> [" + value + "]");
+ verboseLogging = value;
+ }
+
+ protected void updateNameBuffer(){
+ int len = stringBuf.length();
+
+ nameBuf.position(0);
+ nameBuf.limit(len);
+ for (int i = 0; i < len; i++)
+ nameBuf.put((byte)stringBuf.charAt(i));
+
+ nameBuf.rewind();
+ }
+
+ public Statistics getStatistics(){
+ return statistics;
+ }
+
+ public EnumSet getCaps(){
+ return caps;
+ }
+
+ public void initialize(){
+
+ logger.info("Vendor: " + GLES20.glGetString(GLES20.GL_VENDOR));
+ logger.info("Renderer: " + GLES20.glGetString(GLES20.GL_RENDERER));
+ logger.info("Version: " + GLES20.glGetString(GLES20.GL_VERSION));
+
+ String shadingLanguageVersion = GLES20.glGetString(GLES20.GL_SHADING_LANGUAGE_VERSION);
+ logger.info("GLES20.Shading Language Version: " + shadingLanguageVersion);
+
+/*
+ ContextCapabilities ctxCaps = GLContext.getCapabilities();
+ if (ctxCaps.OpenGL20){
+ caps.add(Caps.OpenGL20);
+ }
+ if (ctxCaps.OpenGL21){
+ caps.add(Caps.OpenGL21);
+ }
+ if (ctxCaps.OpenGL30){
+ caps.add(Caps.OpenGL30);
+ }
+*/
+ String versionStr = GLES20.glGetString(GLES20.GL_SHADING_LANGUAGE_VERSION);
+ if (versionStr == null || versionStr.equals("")){
+ glslVer = -1;
+ throw new UnsupportedOperationException("GLSL and OpenGL2 is " +
+ "required for the LWJGL " +
+ "renderer!");
+ }
+
+ // Fix issue in TestRenderToMemory when GL_FRONT is the main
+ // buffer being used.
+
+// initialDrawBuf = GLES20.glGetIntegeri(GLES20.GL_DRAW_BUFFER);
+// initialReadBuf = GLES20.glGetIntegeri(GLES20.GL_READ_BUFFER);
+
+ int spaceIdx = versionStr.lastIndexOf(" ");
+ if (spaceIdx >= 1)
+ versionStr = versionStr.substring(spaceIdx, versionStr.length());
+
+ float version = Float.parseFloat(versionStr);
+ glslVer = (int) (version * 100);
+
+ switch (glslVer){
+ default:
+ if (glslVer < 400)
+ break;
+
+ // so that future OpenGL revisions wont break jme3
+
+ // fall through intentional
+ case 400:
+ case 330:
+ case 150:
+ caps.add(Caps.GLSL150);
+ case 140:
+ caps.add(Caps.GLSL140);
+ case 130:
+ caps.add(Caps.GLSL130);
+ case 120:
+ caps.add(Caps.GLSL120);
+ case 110:
+ caps.add(Caps.GLSL110);
+ case 100:
+ caps.add(Caps.GLSL100);
+ break;
+ }
+
+ if (!caps.contains(Caps.GLSL100)){
+ logger.info("Force-adding GLSL100 support, since OpenGL is supported.");
+ caps.add(Caps.GLSL100);
+ }
+
+ GLES20.glGetIntegerv(GLES20.GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, intBuf16);
+ vertexTextureUnits = intBuf16.get(0);
+ logger.info("VTF Units: " + vertexTextureUnits);
+ if (vertexTextureUnits > 0)
+ caps.add(Caps.VertexTextureFetch);
+
+ GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_IMAGE_UNITS, intBuf16);
+ fragTextureUnits = intBuf16.get(0);
+ logger.info("Texture Units: " + fragTextureUnits);
+/*
+ GLES20.glGetIntegerv(GLES20.GL_MAX_VERTEX_UNIFORM_COMPONENTS, intBuf16);
+ vertexUniforms = intBuf16.get(0);
+ logger.log(Level.FINER, "Vertex Uniforms: {0}", vertexUniforms);
+
+ GLES20.glGetIntegerv(GLES20.GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, intBuf16);
+ fragUniforms = intBuf16.get(0);
+ logger.log(Level.FINER, "Fragment Uniforms: {0}", fragUniforms);
+*/
+
+ GLES20.glGetIntegerv(GLES20.GL_MAX_VERTEX_ATTRIBS, intBuf16);
+ vertexAttribs = intBuf16.get(0);
+ logger.info("Vertex Attributes: " + vertexAttribs);
+
+/*
+ GLES20.glGetIntegerv(GLES20.GL_MAX_VARYING_FLOATS, intBuf16);
+ int varyingFloats = intBuf16.get(0);
+ logger.log(Level.FINER, "Varying Floats: {0}", varyingFloats);
+*/
+
+ GLES20.glGetIntegerv(GLES20.GL_SUBPIXEL_BITS, intBuf16);
+ int subpixelBits = intBuf16.get(0);
+ logger.info("Subpixel Bits: " + subpixelBits);
+/*
+ GLES20.glGetIntegerv(GLES20.GL_MAX_ELEMENTS_VERTICES, intBuf16);
+ maxVertCount = intBuf16.get(0);
+ logger.log(Level.FINER, "Preferred Batch Vertex Count: {0}", maxVertCount);
+
+ GLES20.glGetIntegerv(GLES20.GL_MAX_ELEMENTS_INDICES, intBuf16);
+ maxTriCount = intBuf16.get(0);
+ logger.log(Level.FINER, "Preferred Batch Index Count: {0}", maxTriCount);
+*/
+ GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, intBuf16);
+ maxTexSize = intBuf16.get(0);
+ logger.info("Maximum Texture Resolution: " + maxTexSize);
+
+ GLES20.glGetIntegerv(GLES20.GL_MAX_CUBE_MAP_TEXTURE_SIZE, intBuf16);
+ maxCubeTexSize = intBuf16.get(0);
+ logger.info("Maximum CubeMap Resolution: " + maxCubeTexSize);
+
+
+/*
+ if (ctxCaps.GL_ARB_color_buffer_float){
+ // XXX: Require both 16 and 32 bit float support for FloatColorBuffer.
+ if (ctxCaps.GL_ARB_half_float_pixel){
+ caps.add(Caps.FloatColorBuffer);
+ }
+ }
+
+ if (ctxCaps.GL_ARB_depth_buffer_float){
+ caps.add(Caps.FloatDepthBuffer);
+ }
+
+ if (ctxCaps.GL_ARB_draw_instanced)
+ caps.add(Caps.MeshInstancing);
+
+ if (ctxCaps.GL_ARB_fragment_program)
+ caps.add(Caps.ARBprogram);
+
+ if (ctxCaps.GL_ARB_texture_buffer_object)
+ caps.add(Caps.TextureBuffer);
+
+ if (ctxCaps.GL_ARB_texture_float){
+ if (ctxCaps.GL_ARB_half_float_pixel){
+ caps.add(Caps.FloatTexture);
+ }
+ }
+
+ if (ctxCaps.GL_ARB_vertex_array_object)
+ caps.add(Caps.VertexBufferArray);
+
+ boolean latc = ctxCaps.GL_EXT_texture_compression_latc;
+ boolean atdc = ctxCaps.GL_ATI_texture_compression_3dc;
+ if (latc || atdc){
+ caps.add(Caps.TextureCompressionLATC);
+ if (atdc && !latc){
+ tdc = true;
+ }
+ }
+
+ if (ctxCaps.GL_EXT_packed_float){
+ caps.add(Caps.PackedFloatColorBuffer);
+ if (ctxCaps.GL_ARB_half_float_pixel){
+ // because textures are usually uploaded as RGB16F
+ // need half-float pixel
+ caps.add(Caps.PackedFloatTexture);
+ }
+ }
+
+ if (ctxCaps.GL_EXT_texture_array)
+ caps.add(Caps.TextureArray);
+
+ if (ctxCaps.GL_EXT_texture_shared_exponent)
+ caps.add(Caps.SharedExponentTexture);
+
+ if (ctxCaps.GL_EXT_framebuffer_object){
+ caps.add(Caps.FrameBuffer);
+
+ glGetInteger(GL_MAX_RENDERBUFFER_SIZE_EXT, intBuf16);
+ maxRBSize = intBuf16.get(0);
+ logger.log(Level.FINER, "FBO RB Max Size: {0}", maxRBSize);
+
+ glGetInteger(GL_MAX_COLOR_ATTACHMENTS_EXT, intBuf16);
+ maxFBOAttachs = intBuf16.get(0);
+ logger.log(Level.FINER, "FBO Max renderbuffers: {0}", maxFBOAttachs);
+
+ if (ctxCaps.GL_EXT_framebuffer_multisample){
+ caps.add(Caps.FrameBufferMultisample);
+
+ glGetInteger(GL_MAX_SAMPLES_EXT, intBuf16);
+ maxFBOSamples = intBuf16.get(0);
+ logger.log(Level.FINER, "FBO Max Samples: {0}", maxFBOSamples);
+ }
+
+ if (ctxCaps.GL_ARB_draw_buffers){
+ caps.add(Caps.FrameBufferMRT);
+ glGetInteger(ARBDrawBuffers.GL_MAX_DRAW_BUFFERS_ARB, intBuf16);
+ maxMRTFBOAttachs = intBuf16.get(0);
+ logger.log(Level.FINER, "FBO Max MRT renderbuffers: {0}", maxMRTFBOAttachs);
+ }
+ }
+
+ if (ctxCaps.GL_ARB_multisample){
+ glGetInteger(ARBMultisample.GL_SAMPLE_BUFFERS_ARB, intBuf16);
+ boolean available = intBuf16.get(0) != 0;
+ glGetInteger(ARBMultisample.GL_SAMPLES_ARB, intBuf16);
+ int samples = intBuf16.get(0);
+ logger.log(Level.FINER, "Samples: {0}", samples);
+ boolean enabled = glIsEnabled(ARBMultisample.GL_MULTISAMPLE_ARB);
+ if (samples > 0 && available && !enabled){
+ glEnable(ARBMultisample.GL_MULTISAMPLE_ARB);
+ }
+ }
+*/
+
+ String extensions = GLES20.glGetString(GLES20.GL_EXTENSIONS);
+
+ logger.info("GL_EXTENSIONS: " + extensions);
+
+ if (extensions.contains("GL_OES_texture_npot"))
+ powerOf2 = true;
+
+ applyRenderState(RenderState.DEFAULT);
+// GLES20.glClearDepthf(1.0f);
+
+ if (verboseLogging)
+ logger.info("GLES20.glDisable(GL10.GL_DITHER)");
+
+ GLES20.glDisable(GL10.GL_DITHER);
+
+ checkGLError();
+
+ if (verboseLogging)
+ logger.info("GLES20.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST)");
+
+ GLES20.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
+
+// checkGLError();
+
+ logger.log(Level.INFO, "Caps: " + caps);
+ }
+
+ public void resetGLObjects(){
+ objManager.resetObjects();
+ statistics.clearMemory();
+ boundShader = null;
+ lastFb = null;
+ context.reset();
+ }
+
+ public void cleanup(){
+ objManager.deleteAllObjects(this);
+ statistics.clearMemory();
+ }
+
+ private void checkCap(Caps cap){
+ if (!caps.contains(cap))
+ throw new UnsupportedOperationException("Required capability missing: "+cap.name());
+ }
+
+ /*********************************************************************\
+ |* Render State *|
+ \*********************************************************************/
+ public void setDepthRange(float start, float end){
+
+ if (verboseLogging)
+ logger.info("GLES20.glDepthRangef(" + start + ", " + end + ")");
+ GLES20.glDepthRangef(start, end);
+ checkGLError();
+ }
+
+ public void clearBuffers(boolean color, boolean depth, boolean stencil){
+ int bits = 0;
+ if (color) bits = GLES20.GL_COLOR_BUFFER_BIT;
+ if (depth) bits |= GLES20.GL_DEPTH_BUFFER_BIT;
+ if (stencil) bits |= GLES20.GL_STENCIL_BUFFER_BIT;
+ if (bits != 0) {
+ if (verboseLogging)
+ logger.info("GLES20.glClear(color=" + color + ", depth=" + depth + ", stencil=" + stencil + ")");
+ GLES20.glClear(bits); checkGLError();
+ }
+ }
+
+ public void setBackgroundColor(ColorRGBA color){
+ if (verboseLogging)
+ logger.info("GLES20.glClearColor(" + color.r + ", " + color.g + ", " + color.b + ", " + color.a + ")");
+ GLES20.glClearColor(color.r, color.g, color.b, color.a);
+ checkGLError();
+ }
+
+ public void applyRenderState(RenderState state){
+/*
+ if (state.isWireframe() && !context.wireframe){
+ GLES20.glPolygonMode(GLES20.GL_FRONT_AND_BACK, GLES20.GL_LINE);
+ context.wireframe = true;
+ }else if (!state.isWireframe() && context.wireframe){
+ GLES20.glPolygonMode(GLES20.GL_FRONT_AND_BACK, GLES20.GL_FILL);
+ context.wireframe = false;
+ }
+*/
+ if (state.isDepthTest() && !context.depthTestEnabled){
+ if (verboseLogging)
+ logger.info("GLES20.glEnable(GLES20.GL_DEPTH_TEST)");
+ GLES20.glEnable(GLES20.GL_DEPTH_TEST);
+ checkGLError();
+ if (verboseLogging)
+ logger.info("GLES20.glDepthFunc(GLES20.GL_LEQUAL)");
+ GLES20.glDepthFunc(GLES20.GL_LEQUAL);
+ checkGLError();
+ context.depthTestEnabled = true;
+ }else if (!state.isDepthTest() && context.depthTestEnabled){
+ if (verboseLogging)
+ logger.info("GLES20.glDisable(GLES20.GL_DEPTH_TEST)");
+ GLES20.glDisable(GLES20.GL_DEPTH_TEST);
+ checkGLError();
+ context.depthTestEnabled = false;
+ }
+ if (state.isAlphaTest() && !context.alphaTestEnabled){
+// GLES20.glEnable(GLES20.GL_ALPHA_TEST);
+// GLES20.glAlphaFunc(GLES20.GL_GREATER, state.getAlphaFallOff());
+ context.alphaTestEnabled = true;
+ }else if (!state.isAlphaTest() && context.alphaTestEnabled){
+// GLES20.glDisable(GLES20.GL_ALPHA_TEST);
+ context.alphaTestEnabled = false;
+ }
+ if (state.isDepthWrite() && !context.depthWriteEnabled){
+ if (verboseLogging)
+ logger.info("GLES20.glDepthMask(true)");
+ GLES20.glDepthMask(true);
+ checkGLError();
+ context.depthWriteEnabled = true;
+ }else if (!state.isDepthWrite() && context.depthWriteEnabled){
+ if (verboseLogging)
+ logger.info("GLES20.glDepthMask(false)");
+ GLES20.glDepthMask(false);
+ checkGLError();
+ context.depthWriteEnabled = false;
+ }
+ if (state.isColorWrite() && !context.colorWriteEnabled){
+ if (verboseLogging)
+ logger.info("GLES20.glColorMask(true, true, true, true)");
+ GLES20.glColorMask(true,true,true,true);
+ checkGLError();
+ context.colorWriteEnabled = true;
+ }else if (!state.isColorWrite() && context.colorWriteEnabled){
+ if (verboseLogging)
+ logger.info("GLES20.glColorMask(false, false, false, false)");
+ GLES20.glColorMask(false,false,false,false);
+ checkGLError();
+ context.colorWriteEnabled = false;
+ }
+ if (state.isPointSprite() && !context.pointSprite){
+// GLES20.glEnable(GLES20.GL_POINT_SPRITE);
+// GLES20.glTexEnvi(GLES20.GL_POINT_SPRITE, GLES20.GL_COORD_REPLACE, GLES20.GL_TRUE);
+// GLES20.glEnable(GLES20.GL_VERTEX_PROGRAM_POINT_SIZE);
+// GLES20.glPointParameterf(GLES20.GL_POINT_SIZE_MIN, 1.0f);
+ }else if (!state.isPointSprite() && context.pointSprite){
+// GLES20.glDisable(GLES20.GL_POINT_SPRITE);
+ }
+
+ if (state.isPolyOffset()){
+ if (!context.polyOffsetEnabled){
+ if (verboseLogging)
+ logger.info("GLES20.glEnable(GLES20.GL_POLYGON_OFFSET_FILL)");
+ GLES20.glEnable(GLES20.GL_POLYGON_OFFSET_FILL);
+ checkGLError();
+ if (verboseLogging)
+ logger.info("GLES20.glPolygonOffset(" + state.getPolyOffsetFactor() + ", " + state.getPolyOffsetUnits() + ")");
+ GLES20.glPolygonOffset(state.getPolyOffsetFactor(),
+ state.getPolyOffsetUnits());
+ checkGLError();
+ context.polyOffsetEnabled = true;
+ context.polyOffsetFactor = state.getPolyOffsetFactor();
+ context.polyOffsetUnits = state.getPolyOffsetUnits();
+ }else{
+ if (state.getPolyOffsetFactor() != context.polyOffsetFactor
+ || state.getPolyOffsetUnits() != context.polyOffsetUnits){
+ if (verboseLogging)
+ logger.info("GLES20.glPolygonOffset(" + state.getPolyOffsetFactor() + ", " + state.getPolyOffsetUnits() + ")");
+ GLES20.glPolygonOffset(state.getPolyOffsetFactor(),
+ state.getPolyOffsetUnits());
+ checkGLError();
+ context.polyOffsetFactor = state.getPolyOffsetFactor();
+ context.polyOffsetUnits = state.getPolyOffsetUnits();
+ }
+ }
+ }else{
+ if (context.polyOffsetEnabled){
+ if (verboseLogging)
+ logger.info("GLES20.glDisable(GLES20.GL_POLYGON_OFFSET_FILL)");
+ GLES20.glDisable(GLES20.GL_POLYGON_OFFSET_FILL);
+ checkGLError();
+ context.polyOffsetEnabled = false;
+ context.polyOffsetFactor = 0;
+ context.polyOffsetUnits = 0;
+ }
+ }
+ if (state.getFaceCullMode() != context.cullMode){
+ if (state.getFaceCullMode() == RenderState.FaceCullMode.Off) {
+ if (verboseLogging)
+ logger.info("GLES20.glDisable(GLES20.GL_CULL_FACE)");
+ GLES20.glDisable(GLES20.GL_CULL_FACE);
+ } else {
+ if (verboseLogging)
+ logger.info("GLES20.glEnable(GLES20.GL_CULL_FACE)");
+ GLES20.glEnable(GLES20.GL_CULL_FACE);
+ }
+
+ checkGLError();
+
+ switch (state.getFaceCullMode()){
+ case Off:
+ break;
+ case Back:
+ if (verboseLogging)
+ logger.info("GLES20.glCullFace(GLES20.GL_BACK)");
+ GLES20.glCullFace(GLES20.GL_BACK);
+ break;
+ case Front:
+ if (verboseLogging)
+ logger.info("GLES20.glCullFace(GLES20.GL_FRONT)");
+ GLES20.glCullFace(GLES20.GL_FRONT);
+ break;
+ case FrontAndBack:
+ if (verboseLogging)
+ logger.info("GLES20.glCullFace(GLES20.GL_FRONT_AND_BACK)");
+ GLES20.glCullFace(GLES20.GL_FRONT_AND_BACK);
+ break;
+ default:
+ throw new UnsupportedOperationException("Unrecognized face cull mode: "+
+ state.getFaceCullMode());
+ }
+
+ checkGLError();
+
+ context.cullMode = state.getFaceCullMode();
+ }
+
+ if (state.getBlendMode() != context.blendMode){
+ if (state.getBlendMode() == RenderState.BlendMode.Off){
+ if (verboseLogging)
+ logger.info("GLES20.glDisable(GLES20.GL_BLEND)");
+ GLES20.glDisable(GLES20.GL_BLEND);
+ } else {
+ if (verboseLogging)
+ logger.info("GLES20.glEnable(GLES20.GL_BLEND)");
+ GLES20.glEnable(GLES20.GL_BLEND);
+ switch (state.getBlendMode()){
+ case Off:
+ break;
+ case Additive:
+ if (verboseLogging)
+ logger.info("GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE)");
+ GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE);
+ break;
+ case AlphaAdditive:
+ if (verboseLogging)
+ logger.info("GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE)");
+ GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE);
+ break;
+ case Color:
+ if (verboseLogging)
+ logger.info("GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_COLOR)");
+ GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_COLOR);
+ break;
+ case Alpha:
+ if (verboseLogging)
+ logger.info("GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA)");
+ GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
+ break;
+ case PremultAlpha:
+ if (verboseLogging)
+ logger.info("GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA)");
+ GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);
+ break;
+ case Modulate:
+ if (verboseLogging)
+ logger.info("GLES20.glBlendFunc(GLES20.GL_DST_COLOR, GLES20.GL_ZERO)");
+ GLES20.glBlendFunc(GLES20.GL_DST_COLOR, GLES20.GL_ZERO);
+ break;
+ case ModulateX2:
+ if (verboseLogging)
+ logger.info("GLES20.glBlendFunc(GLES20.GL_DST_COLOR, GLES20.GL_SRC_COLOR)");
+ GLES20.glBlendFunc(GLES20.GL_DST_COLOR, GLES20.GL_SRC_COLOR);
+ break;
+ default:
+ throw new UnsupportedOperationException("Unrecognized blend mode: "+
+ state.getBlendMode());
+ }
+ }
+
+ checkGLError();
+
+ context.blendMode = state.getBlendMode();
+ }
+ }
+
+ /*********************************************************************\
+ |* Camera and World transforms *|
+ \*********************************************************************/
+ public void setViewPort(int x, int y, int w, int h){
+ if (x != vpX || vpY != y || vpW != w || vpH != h){
+ if (verboseLogging)
+ logger.info("GLES20.glViewport(" + x + ", " + y + ", " + w + ", " + h + ")");
+ GLES20.glViewport(x, y, w, h);
+ checkGLError();
+ vpX = x;
+ vpY = y;
+ vpW = w;
+ vpH = h;
+ }
+ }
+
+ public void setClipRect(int x, int y, int width, int height){
+ if (!context.clipRectEnabled){
+ if (verboseLogging)
+ logger.info("GLES20.glEnable(GLES20.GL_SCISSOR_TEST)");
+ GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
+ checkGLError();
+ context.clipRectEnabled = true;
+ }
+ if (clipX != x || clipY != y || clipW != width || clipH != height){
+ if (verboseLogging)
+ logger.info("GLES20.glScissor(" + x + ", " + y + ", " + width + ", " + height + ")");
+ GLES20.glScissor(x, y, width, height);
+ clipX = x;
+ clipY = y;
+ clipW = width;
+ clipH = height;
+ checkGLError();
+ }
+ }
+
+ public void clearClipRect(){
+ if (context.clipRectEnabled){
+ if (verboseLogging)
+ logger.info("GLES20.glDisable(GLES20.GL_SCISSOR_TEST)");
+ GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
+ checkGLError();
+ context.clipRectEnabled = false;
+
+ clipX = 0;
+ clipY = 0;
+ clipW = 0;
+ clipH = 0;
+ }
+ }
+
+ public void onFrame(){
+ objManager.deleteUnused(this);
+// statistics.clearFrame();
+ }
+
+ public void setWorldMatrix(Matrix4f worldMatrix){
+ }
+
+ public void setViewProjectionMatrices(Matrix4f viewMatrix, Matrix4f projMatrix){
+ }
+
+ /*********************************************************************\
+ |* Shaders *|
+ \*********************************************************************/
+
+ protected void updateUniformLocation(Shader shader, Uniform uniform){
+ stringBuf.setLength(0);
+ stringBuf.append(uniform.getName()).append('\0');
+ updateNameBuffer();
+ if (verboseLogging)
+ logger.info("GLES20.glGetUniformLocation(" + shader.getId() + ", " + uniform.getName() + ")");
+ int loc = GLES20.glGetUniformLocation(shader.getId(), uniform.getName());
+ checkGLError();
+ if (loc < 0){
+ uniform.setLocation(-1);
+ // uniform is not declared in shader
+ if (verboseLogging)
+ logger.warning("Uniform [" + uniform.getName() + "] is not declared in shader.");
+ }else{
+ uniform.setLocation(loc);
+ }
+ }
+
+ protected void updateUniform(Shader shader, Uniform uniform){
+ int shaderId = shader.getId();
+
+ assert uniform.getName() != null;
+ assert shader.getId() > 0;
+
+ if (context.boundShaderProgram != shaderId){
+ if (verboseLogging)
+ logger.info("GLES20.glUseProgram(" + shaderId + ")");
+ GLES20.glUseProgram(shaderId);
+ checkGLError();
+ statistics.onShaderUse(shader, true);
+ boundShader = shader;
+ context.boundShaderProgram = shaderId;
+ }else{
+ statistics.onShaderUse(shader, false);
+ }
+
+ int loc = uniform.getLocation();
+ if (loc == -1) {
+ if (verboseLogging)
+ logger.warning("no location for uniform [" + uniform.getName() + "]");
+ return;
+ }
+
+ if (loc == -2){
+ // get uniform location
+ updateUniformLocation(shader, uniform);
+ if (uniform.getLocation() == -1){
+ // not declared, ignore
+
+ if (verboseLogging)
+ logger.warning("not declared uniform: [" + uniform.getName() + "]");
+
+ uniform.clearUpdateNeeded();
+ return;
+ }
+ loc = uniform.getLocation();
+ }
+
+ if (uniform.getVarType() == null) {
+ logger.warning("value is not set yet.");
+ return; // value not set yet..
+ }
+
+ statistics.onUniformSet();
+
+ uniform.clearUpdateNeeded();
+ FloatBuffer fb;
+ switch (uniform.getVarType()){
+ case Float:
+ if (verboseLogging)
+ logger.info("GLES20.glUniform1f set Float. " + uniform.getName());
+ Float f = (Float)uniform.getValue();
+ GLES20.glUniform1f(loc, f.floatValue());
+ break;
+ case Vector2:
+ if (verboseLogging)
+ logger.info("GLES20.glUniform2f set Vector2. " + uniform.getName());
+ Vector2f v2 = (Vector2f)uniform.getValue();
+ GLES20.glUniform2f(loc, v2.getX(), v2.getY());
+ break;
+ case Vector3:
+ if (verboseLogging)
+ logger.info("GLES20.glUniform3f set Vector3. " + uniform.getName());
+ Vector3f v3 = (Vector3f)uniform.getValue();
+ GLES20.glUniform3f(loc, v3.getX(), v3.getY(), v3.getZ());
+ break;
+ case Vector4:
+ if (verboseLogging)
+ logger.info("GLES20.glUniform4f set Vector4." + uniform.getName());
+ Object val = uniform.getValue();
+ if (val instanceof ColorRGBA){
+ ColorRGBA c = (ColorRGBA) val;
+ GLES20.glUniform4f(loc, c.r, c.g, c.b, c.a);
+ }else{
+ Quaternion c = (Quaternion)uniform.getValue();
+ GLES20.glUniform4f(loc, c.getX(), c.getY(), c.getZ(), c.getW());
+ }
+ break;
+ case Boolean:
+ if (verboseLogging)
+ logger.info("GLES20.glUniform1i set Boolean." + uniform.getName());
+ Boolean b = (Boolean)uniform.getValue();
+ GLES20.glUniform1i(loc, b.booleanValue() ? GLES20.GL_TRUE : GLES20.GL_FALSE);
+ break;
+ case Matrix3:
+ if (verboseLogging)
+ logger.info("GLES20.glUniformMatrix3fv set Matrix3." + uniform.getName());
+ fb = (FloatBuffer)uniform.getValue();
+ assert fb.remaining() == 9;
+ GLES20.glUniformMatrix3fv(loc, 1, false, fb);
+ break;
+ case Matrix4:
+ if (verboseLogging)
+ logger.info("GLES20.glUniformMatrix4fv set Matrix4." + uniform.getName());
+ fb = (FloatBuffer)uniform.getValue();
+ assert fb.remaining() == 16;
+ GLES20.glUniformMatrix4fv(loc, 1, false, fb);
+ break;
+ case FloatArray:
+ if (verboseLogging)
+ logger.info("GLES20.glUniform1fv set FloatArray." + uniform.getName());
+ fb = (FloatBuffer)uniform.getValue();
+ GLES20.glUniform1fv(loc, 1, fb);
+ break;
+ case Vector2Array:
+ if (verboseLogging)
+ logger.info("GLES20.glUniform2fv set Vector2Array." + uniform.getName());
+ fb = (FloatBuffer)uniform.getValue();
+ GLES20.glUniform2fv(loc, 1, fb);
+ break;
+ case Vector3Array:
+ if (verboseLogging)
+ logger.info("GLES20.glUniform3fv set Vector3Array." + uniform.getName());
+ fb = (FloatBuffer)uniform.getValue();
+ GLES20.glUniform3fv(loc, 1, fb);
+ break;
+ case Vector4Array:
+ if (verboseLogging)
+ logger.info("GLES20.glUniform4fv set Vector4Array." + uniform.getName());
+ fb = (FloatBuffer)uniform.getValue();
+ GLES20.glUniform4fv(loc, 1, fb);
+ break;
+ case Matrix4Array:
+ if (verboseLogging)
+ logger.info("GLES20.glUniform4fv set Matrix4Array." + uniform.getName());
+ fb = (FloatBuffer)uniform.getValue();
+ GLES20.glUniformMatrix4fv(loc, 1, false, fb);
+ break;
+ case Int:
+ if (verboseLogging)
+ logger.info("GLES20.glUniform1i set Int." + uniform.getName());
+ Integer i = (Integer)uniform.getValue();
+ GLES20.glUniform1i(loc, i.intValue());
+ break;
+ default:
+ throw new UnsupportedOperationException("Unsupported uniform type: "+uniform.getVarType());
+ }
+ checkGLError();
+ }
+
+ protected void updateShaderUniforms(Shader shader){
+ ListMap uniforms = shader.getUniformMap();
+// for (Uniform uniform : shader.getUniforms()){
+ for (int i = 0; i < uniforms.size(); i++){
+ Uniform uniform = uniforms.getValue(i);
+ if (uniform.isUpdateNeeded())
+ updateUniform(shader, uniform);
+ }
+ }
+
+
+ protected void resetUniformLocations(Shader shader){
+ ListMap uniforms = shader.getUniformMap();
+// for (Uniform uniform : shader.getUniforms()){
+ for (int i = 0; i < uniforms.size(); i++){
+ Uniform uniform = uniforms.getValue(i);
+ uniform.reset(); // e.g check location again
+ }
+ }
+
+ /*
+ * (Non-javadoc)
+ * Only used for fixed-function. Ignored.
+ */
+ public void setLighting(LightList list){
+ }
+
+ public int convertShaderType(ShaderType type){
+ switch (type){
+ case Fragment:
+ return GLES20.GL_FRAGMENT_SHADER;
+ case Vertex:
+ return GLES20.GL_VERTEX_SHADER;
+// case Geometry:
+// return ARBGeometryShader4.GL_GEOMETRY_SHADER_ARB;
+ default:
+ throw new RuntimeException("Unrecognized shader type.");
+ }
+ }
+
+ public void updateShaderSourceData(ShaderSource source, String language){
+ int id = source.getId();
+ if (id == -1){
+ // create id
+ if (verboseLogging)
+ logger.info("GLES20.glCreateShader(" + source.getType() + ")");
+ id = GLES20.glCreateShader(convertShaderType(source.getType()));
+ checkGLError();
+ if (id <= 0)
+ throw new RendererException("Invalid ID received when trying to create shader.");
+
+ source.setId(id);
+ }
+
+ // upload shader source
+ // merge the defines and source code
+ byte[] versionData = new byte[]{};//"#version 140\n".getBytes();
+// versionData = "#define INSTANCING 1\n".getBytes();
+ byte[] definesCodeData = source.getDefines().getBytes();
+ byte[] sourceCodeData = source.getSource().getBytes();
+ ByteBuffer codeBuf = BufferUtils.createByteBuffer(versionData.length
+ + definesCodeData.length
+ + sourceCodeData.length);
+ codeBuf.put(versionData);
+ codeBuf.put(definesCodeData);
+ codeBuf.put(sourceCodeData);
+ codeBuf.flip();
+
+ if (verboseLogging)
+ logger.info("GLES20.glShaderSource("+ id + ")");
+
+ GLES20.glShaderSource(
+ id,
+ "precision mediump float;\n" +
+ source.getDefines() +
+ source.getSource()
+ );
+
+ checkGLError();
+
+ if (verboseLogging)
+ logger.info("GLES20.glCompileShader(" + id + ")");
+
+ GLES20.glCompileShader(id);
+
+ checkGLError();
+
+ if (verboseLogging)
+ logger.info("GLES20.glGetShaderiv(" + id + ", GLES20.GL_COMPILE_STATUS)");
+
+ GLES20.glGetShaderiv(id, GLES20.GL_COMPILE_STATUS, intBuf1);
+
+ checkGLError();
+
+ boolean compiledOK = intBuf1.get(0) == GLES20.GL_TRUE;
+ String infoLog = null;
+
+ if (VALIDATE_SHADER || !compiledOK){
+ // even if compile succeeded, check
+ // log for warnings
+ if (verboseLogging)
+ logger.info("GLES20.glGetShaderiv()");
+ GLES20.glGetShaderiv(id, GLES20.GL_INFO_LOG_LENGTH, intBuf1);
+ checkGLError();
+ int length = intBuf1.get(0);
+ if (length > 3){
+ // get infos
+ ByteBuffer logBuf = BufferUtils.createByteBuffer(length);
+ if (verboseLogging)
+ logger.info("GLES20.glGetShaderInfoLog(" + id + ")");
+ infoLog = GLES20.glGetShaderInfoLog(id);
+ }
+ }
+
+ if (compiledOK){
+ if (infoLog != null){
+ logger.log(Level.INFO, "compile success: " + source.getName() + ", " + infoLog);
+ }else{
+ logger.log(Level.FINE, "compile success: " + source.getName());
+ }
+ }else{
+ if (infoLog != null){
+ logger.log(Level.WARNING, "compile error: " + source.getName() + ", " + infoLog);
+ }else{
+ logger.log(Level.WARNING, "compile error: " + source.getName());
+ }
+ logger.log(Level.WARNING, source.getDefines() + "\n" + source.getSource());
+ }
+
+ source.clearUpdateNeeded();
+ // only usable if compiled
+ source.setUsable(compiledOK);
+ if (!compiledOK){
+ // make sure to dispose id cause all program's
+ // shaders will be cleared later.
+ if (verboseLogging)
+ logger.info("GLES20.glDeleteShader(" + id + ")");
+ GLES20.glDeleteShader(id);
+ checkGLError();
+ }else{
+ // register for cleanup since the ID is usable
+ objManager.registerForCleanup(source);
+ }
+ }
+
+ public void updateShaderData(Shader shader){
+ int id = shader.getId();
+ boolean needRegister = false;
+ if (id == -1){
+ // create program
+
+ if (verboseLogging)
+ logger.info("GLES20.glCreateProgram()");
+
+ id = GLES20.glCreateProgram();
+
+ if (id <= 0)
+ throw new RendererException("Invalid ID received when trying to create shader program.");
+
+ shader.setId(id);
+ needRegister = true;
+ }
+
+ for (ShaderSource source : shader.getSources()){
+ if (source.isUpdateNeeded()){
+ updateShaderSourceData(source, shader.getLanguage());
+ // shader has been compiled here
+ }
+
+ if (!source.isUsable()){
+ // it's useless.. just forget about everything..
+ shader.setUsable(false);
+ shader.clearUpdateNeeded();
+ return;
+ }
+ if (verboseLogging)
+ logger.info("GLES20.glAttachShader(" + id + ", " + source.getId() + ")");
+
+ GLES20.glAttachShader(id, source.getId());
+ }
+
+ // link shaders to program
+ if (verboseLogging)
+ logger.info("GLES20.glLinkProgram(" + id + ")");
+
+ GLES20.glLinkProgram(id);
+
+
+ if (verboseLogging)
+ logger.info("GLES20.glGetProgramiv(" + id + ")");
+
+ GLES20.glGetProgramiv(id, GLES20.GL_LINK_STATUS, intBuf1);
+
+ boolean linkOK = intBuf1.get(0) == GLES20.GL_TRUE;
+ String infoLog = null;
+
+ if (VALIDATE_SHADER || !linkOK){
+ if (verboseLogging)
+ logger.info("GLES20.glGetProgramiv(" + id + ", GLES20.GL_INFO_LOG_LENGTH, buffer)");
+
+ GLES20.glGetProgramiv(id, GLES20.GL_INFO_LOG_LENGTH, intBuf1);
+
+ int length = intBuf1.get(0);
+ if (length > 3){
+ // get infos
+
+ if (verboseLogging)
+ logger.info("GLES20.glGetProgramInfoLog(" + id + ")");
+
+ infoLog = GLES20.glGetProgramInfoLog(id);
+ }
+ }
+
+ if (linkOK){
+ if (infoLog != null){
+ logger.log(Level.INFO, "shader link success. \n{0}", infoLog);
+ }else{
+ logger.fine("shader link success");
+ }
+ }else{
+ if (infoLog != null){
+ logger.log(Level.WARNING, "shader link failure. \n{0}", infoLog);
+ }else{
+ logger.warning("shader link failure");
+ }
+ }
+
+ shader.clearUpdateNeeded();
+ if (!linkOK){
+ // failure.. forget about everything
+ shader.resetSources();
+ shader.setUsable(false);
+ deleteShader(shader);
+ }else{
+ shader.setUsable(true);
+ if (needRegister){
+ objManager.registerForCleanup(shader);
+ statistics.onNewShader();
+ }else{
+ // OpenGL spec: uniform locations may change after re-link
+ resetUniformLocations(shader);
+ }
+ }
+ }
+
+ public void setShader(Shader shader){
+ if (verboseLogging)
+ logger.info("setShader(" + shader + ")");
+
+ if (shader == null){
+ if (context.boundShaderProgram > 0){
+
+ if (verboseLogging)
+ logger.info("GLES20.glUseProgram(0)");
+
+ GLES20.glUseProgram(0);
+
+ statistics.onShaderUse(null, true);
+ context.boundShaderProgram = 0;
+ boundShader = null;
+ }
+ } else {
+ if (shader.isUpdateNeeded())
+ updateShaderData(shader);
+
+ // NOTE: might want to check if any of the
+ // sources need an update?
+
+ if (!shader.isUsable()) {
+ logger.warning("shader is not usable.");
+ return;
+ }
+
+ assert shader.getId() > 0;
+
+ updateShaderUniforms(shader);
+ if (context.boundShaderProgram != shader.getId()){
+ if (VALIDATE_SHADER){
+ // check if shader can be used
+ // with current state
+ if (verboseLogging)
+ logger.info("GLES20.glValidateProgram(" + shader.getId() + ")");
+
+ GLES20.glValidateProgram(shader.getId());
+
+ if (verboseLogging)
+ logger.info("GLES20.glGetProgramiv(" + shader.getId() + ", GLES20.GL_VALIDATE_STATUS, buffer)");
+
+ GLES20.glGetProgramiv(shader.getId(), GLES20.GL_VALIDATE_STATUS, intBuf1);
+
+ boolean validateOK = intBuf1.get(0) == GLES20.GL_TRUE;
+
+ if (validateOK){
+ logger.fine("shader validate success");
+ }else{
+ logger.warning("shader validate failure");
+ }
+ }
+
+ if (verboseLogging)
+ logger.info("GLES20.glUseProgram(" + shader.getId() + ")");
+
+ GLES20.glUseProgram(shader.getId());
+
+ statistics.onShaderUse(shader, true);
+ context.boundShaderProgram = shader.getId();
+ boundShader = shader;
+ }else{
+ statistics.onShaderUse(shader, false);
+ }
+ }
+ }
+
+ public void deleteShaderSource(ShaderSource source){
+ if (source.getId() < 0){
+ logger.warning("Shader source is not uploaded to GPU, cannot delete.");
+ return;
+ }
+ source.setUsable(false);
+ source.clearUpdateNeeded();
+
+ if (verboseLogging)
+ logger.info("GLES20.glDeleteShader(" + source.getId() + ")");
+
+ GLES20.glDeleteShader(source.getId());
+ source.resetObject();
+ }
+
+ public void deleteShader(Shader shader){
+ if (shader.getId() == -1){
+ logger.warning("Shader is not uploaded to GPU, cannot delete.");
+ return;
+ }
+ for (ShaderSource source : shader.getSources()){
+ if (source.getId() != -1){
+
+ if (verboseLogging)
+ logger.info("GLES20.glDetachShader(" + shader.getId() + ", " + source.getId() + ")");
+
+ GLES20.glDetachShader(shader.getId(), source.getId());
+ // the next part is done by the GLObjectManager automatically
+// glDeleteShader(source.getId());
+ }
+ }
+ // kill all references so sources can be collected
+ // if needed.
+ shader.resetSources();
+
+ if (verboseLogging)
+ logger.info("GLES20.glDeleteProgram(" + shader.getId() + ")");
+
+ GLES20.glDeleteProgram(shader.getId());
+
+ statistics.onDeleteShader();
+ }
+
+ /*********************************************************************\
+ |* Framebuffers *|
+ \*********************************************************************/
+
+ public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst){
+ logger.warning("copyFrameBuffer is not supported.");
+ }
+/*
+ public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst){
+ if (GLContext.getCapabilities().GL_EXT_framebuffer_blit){
+ int srcW = 0;
+ int srcH = 0;
+ int dstW = 0;
+ int dstH = 0;
+ int prevFBO = context.boundFBO;
+
+ if (src != null && src.isUpdateNeeded())
+ updateFrameBuffer(src);
+
+ if (dst != null && dst.isUpdateNeeded())
+ updateFrameBuffer(dst);
+
+ if (src == null){
+ glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0);
+// srcW = viewWidth;
+// srcH = viewHeight;
+ }else{
+ glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, src.getId());
+ srcW = src.getWidth();
+ srcH = src.getHeight();
+ }
+ if (dst == null){
+ glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0);
+// dstW = viewWidth;
+// dstH = viewHeight;
+ }else{
+ glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, dst.getId());
+ dstW = dst.getWidth();
+ dstH = dst.getHeight();
+ }
+ glBlitFramebufferEXT(0, 0, srcW, srcH,
+ 0, 0, dstW, dstH,
+ GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT,
+ GL_NEAREST);
+
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, prevFBO);
+ try {
+ checkFrameBufferError();
+ } catch (IllegalStateException ex){
+ logger.log(Level.SEVERE, "Source FBO:\n{0}", src);
+ logger.log(Level.SEVERE, "Dest FBO:\n{0}", dst);
+ throw ex;
+ }
+ }else{
+ throw new UnsupportedOperationException("EXT_framebuffer_blit required.");
+ // TODO: support non-blit copies?
+ }
+ }
+*/
+ private void checkFrameBufferError() {
+ logger.warning("checkFrameBufferError is not supported.");
+ }
+/*
+ private void checkFrameBufferError() {
+ int status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
+ switch (status) {
+ case GL_FRAMEBUFFER_COMPLETE_EXT:
+ break;
+ case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
+ //Choose different formats
+ throw new IllegalStateException("Framebuffer object format is " +
+ "unsupported by the video hardware.");
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
+ throw new IllegalStateException("Framebuffer has erronous attachment.");
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
+ throw new IllegalStateException("Framebuffer is missing required attachment.");
+ case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
+ throw new IllegalStateException("Framebuffer attachments must have same dimensions.");
+ case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
+ throw new IllegalStateException("Framebuffer attachments must have same formats.");
+ case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
+ throw new IllegalStateException("Incomplete draw buffer.");
+ case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
+ throw new IllegalStateException("Incomplete read buffer.");
+ case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT:
+ throw new IllegalStateException("Incomplete multisample buffer.");
+ default:
+ //Programming error; will fail on all hardware
+ throw new IllegalStateException("Some video driver error " +
+ "or programming error occured. " +
+ "Framebuffer object status is invalid. ");
+ }
+ }
+*/
+ private void updateRenderBuffer(FrameBuffer fb, RenderBuffer rb){
+ logger.warning("updateRenderBuffer is not supported.");
+ }
+/*
+ private void updateRenderBuffer(FrameBuffer fb, RenderBuffer rb){
+ int id = rb.getId();
+ if (id == -1){
+ glGenRenderbuffersEXT(intBuf1);
+ id = intBuf1.get(0);
+ rb.setId(id);
+ }
+
+ if (context.boundRB != id){
+ glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, id);
+ context.boundRB = id;
+ }
+
+ if (fb.getWidth() > maxRBSize || fb.getHeight() > maxRBSize)
+ throw new UnsupportedOperationException("Resolution "+fb.getWidth()+
+ ":"+fb.getHeight()+" is not supported.");
+
+ if (fb.getSamples() > 0 && GLContext.getCapabilities().GL_EXT_framebuffer_multisample){
+ int samples = fb.getSamples();
+ if (maxFBOSamples < samples){
+ samples = maxFBOSamples;
+ }
+ glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT,
+ samples,
+ TextureUtil.convertTextureFormat(rb.getFormat()),
+ fb.getWidth(),
+ fb.getHeight());
+ }else{
+ glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT,
+ TextureUtil.convertTextureFormat(rb.getFormat()),
+ fb.getWidth(),
+ fb.getHeight());
+ }
+ }
+*/
+ private int convertAttachmentSlot(int attachmentSlot){
+ logger.warning("convertAttachmentSlot is not supported.");
+ return -1;
+ }
+/*
+ private int convertAttachmentSlot(int attachmentSlot){
+ // can also add support for stencil here
+ if (attachmentSlot == -100){
+ return GL_DEPTH_ATTACHMENT_EXT;
+ }else if (attachmentSlot < 0 || attachmentSlot >= 16){
+ throw new UnsupportedOperationException("Invalid FBO attachment slot: "+attachmentSlot);
+ }
+
+ return GL_COLOR_ATTACHMENT0_EXT + attachmentSlot;
+ }
+*/
+
+ public void updateRenderTexture(FrameBuffer fb, RenderBuffer rb){
+ logger.warning("updateRenderTexture is not supported.");
+ }
+/*
+ public void updateRenderTexture(FrameBuffer fb, RenderBuffer rb){
+ Texture tex = rb.getTexture();
+ Image image = tex.getImage();
+ if (image.isUpdateNeeded())
+ updateTexImageData(image, tex.getType(), tex.getMinFilter().usesMipMapLevels());
+
+ glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
+ convertAttachmentSlot(rb.getSlot()),
+ convertTextureType(tex.getType()),
+ image.getId(),
+ 0);
+ }
+*/
+ public void updateFrameBufferAttachment(FrameBuffer fb, RenderBuffer rb){
+ logger.warning("updateFrameBufferAttachment is not supported.");
+ }
+/*
+ public void updateFrameBufferAttachment(FrameBuffer fb, RenderBuffer rb){
+ boolean needAttach;
+ if (rb.getTexture() == null){
+ // if it hasn't been created yet, then attach is required.
+ needAttach = rb.getId() == -1;
+ updateRenderBuffer(fb, rb);
+ }else{
+ needAttach = false;
+ updateRenderTexture(fb, rb);
+ }
+ if (needAttach){
+ glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
+ convertAttachmentSlot(rb.getSlot()),
+ GL_RENDERBUFFER_EXT,
+ rb.getId());
+ }
+ }
+*/
+ public void updateFrameBuffer(FrameBuffer fb) {
+ logger.warning("updateFrameBuffer is not supported.");
+ }
+/*
+ public void updateFrameBuffer(FrameBuffer fb) {
+ int id = fb.getId();
+ if (id == -1){
+ // create FBO
+ glGenFramebuffersEXT(intBuf1);
+ id = intBuf1.get(0);
+ fb.setId(id);
+ objManager.registerForCleanup(fb);
+
+ statistics.onNewFrameBuffer();
+ }
+
+ if (context.boundFBO != id){
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, id);
+ // binding an FBO automatically sets draw buf to GL_COLOR_ATTACHMENT0
+ context.boundDrawBuf = 0;
+ context.boundFBO = id;
+ }
+
+ FrameBuffer.RenderBuffer depthBuf = fb.getDepthBuffer();
+ if (depthBuf != null){
+ updateFrameBufferAttachment(fb, depthBuf);
+ }
+
+ for (int i = 0; i < fb.getNumColorBuffers(); i++){
+ FrameBuffer.RenderBuffer colorBuf = fb.getColorBuffer(i);
+ updateFrameBufferAttachment(fb, colorBuf);
+ }
+
+ fb.clearUpdateNeeded();
+ }
+*/
+ public void setFrameBuffer(FrameBuffer fb) {
+ if (verboseLogging)
+ logger.warning("setFrameBuffer is not supported.");
+ }
+/*
+ public void setFrameBuffer(FrameBuffer fb) {
+ if (lastFb == fb)
+ return;
+
+ // generate mipmaps for last FB if needed
+ if (lastFb != null){
+ for (int i = 0; i < lastFb.getNumColorBuffers(); i++){
+ RenderBuffer rb = lastFb.getColorBuffer(i);
+ Texture tex = rb.getTexture();
+ if (tex != null
+ && tex.getMinFilter().usesMipMapLevels()){
+ setTexture(0, rb.getTexture());
+ glGenerateMipmapEXT(convertTextureType(tex.getType()));
+ }
+ }
+ }
+
+
+ if (fb == null){
+ // unbind any fbos
+ if (context.boundFBO != 0){
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+ statistics.onFrameBufferUse(null, true);
+
+ context.boundFBO = 0;
+ }
+ // select back buffer
+ if (context.boundDrawBuf != -1){
+ glDrawBuffer(initialDrawBuf);
+ context.boundDrawBuf = -1;
+ }
+ if (context.boundReadBuf != -1){
+ glReadBuffer(initialReadBuf);
+ context.boundReadBuf = -1;
+ }
+
+ lastFb = null;
+ }else{
+ if (fb.isUpdateNeeded())
+ updateFrameBuffer(fb);
+
+ if (context.boundFBO != fb.getId()){
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb.getId());
+ statistics.onFrameBufferUse(fb, true);
+
+ // update viewport to reflect framebuffer's resolution
+ setViewPort(0, 0, fb.getWidth(), fb.getHeight());
+
+ context.boundFBO = fb.getId();
+ }else{
+ statistics.onFrameBufferUse(fb, false);
+ }
+ if (fb.getNumColorBuffers() == 0){
+ // make sure to select NONE as draw buf
+ // no color buffer attached. select NONE
+ if (context.boundDrawBuf != -2){
+ glDrawBuffer(GL_NONE);
+ context.boundDrawBuf = -2;
+ }
+ if (context.boundReadBuf != -2){
+ glReadBuffer(GL_NONE);
+ context.boundReadBuf = -2;
+ }
+ }else{
+ if (fb.isMultiTarget()){
+ if (fb.getNumColorBuffers() > maxMRTFBOAttachs)
+ throw new UnsupportedOperationException("Framebuffer has more"
+ + " targets than are supported"
+ + " on the system!");
+
+ if (context.boundDrawBuf != 100 + fb.getNumColorBuffers()){
+ intBuf16.clear();
+ for (int i = 0; i < fb.getNumColorBuffers(); i++)
+ intBuf16.put( GL_COLOR_ATTACHMENT0_EXT + i );
+
+ intBuf16.flip();
+ glDrawBuffers(intBuf16);
+ context.boundDrawBuf = 100 + fb.getNumColorBuffers();
+ }
+ }else{
+ RenderBuffer rb = fb.getColorBuffer(fb.getTargetIndex());
+ // select this draw buffer
+ if (context.boundDrawBuf != rb.getSlot()){
+ glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT + rb.getSlot());
+ context.boundDrawBuf = rb.getSlot();
+ }
+ }
+ }
+
+ assert fb.getId() >= 0;
+ assert context.boundFBO == fb.getId();
+ lastFb = fb;
+ }
+
+ try {
+ checkFrameBufferError();
+ } catch (IllegalStateException ex){
+ logger.log(Level.SEVERE, "Problem FBO:\n{0}", fb);
+ throw ex;
+ }
+ }
+*/
+ public void readFrameBuffer(FrameBuffer fb, ByteBuffer byteBuf){
+ logger.warning("readFrameBuffer is not supported.");
+ }
+/*
+ public void readFrameBuffer(FrameBuffer fb, ByteBuffer byteBuf){
+ if (fb != null){
+ RenderBuffer rb = fb.getColorBuffer();
+ if (rb == null)
+ throw new IllegalArgumentException("Specified framebuffer" +
+ " does not have a colorbuffer");
+
+ setFrameBuffer(fb);
+ if (context.boundReadBuf != rb.getSlot()){
+ glReadBuffer(GL_COLOR_ATTACHMENT0_EXT + rb.getSlot());
+ context.boundReadBuf = rb.getSlot();
+ }
+ }else{
+ setFrameBuffer(null);
+ }
+
+ glReadPixels(vpX, vpY, vpW, vpH, GL_RGBA GL_BGRA, GL_UNSIGNED_BYTE, byteBuf);
+ }
+*/
+ private void deleteRenderBuffer(FrameBuffer fb, RenderBuffer rb){
+ logger.warning("deleteRenderBuffer is not supported.");
+ }
+/*
+ private void deleteRenderBuffer(FrameBuffer fb, RenderBuffer rb){
+ intBuf1.put(0, rb.getId());
+ glDeleteRenderbuffersEXT(intBuf1);
+ }
+*/
+ public void deleteFrameBuffer(FrameBuffer fb) {
+ logger.warning("deleteFrameBuffer is not supported.");
+ }
+/*
+ public void deleteFrameBuffer(FrameBuffer fb) {
+ if (fb.getId() != -1){
+ if (context.boundFBO == fb.getId()){
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+ context.boundFBO = 0;
+ }
+
+ if (fb.getDepthBuffer() != null){
+ deleteRenderBuffer(fb, fb.getDepthBuffer());
+ }
+ if (fb.getColorBuffer() != null){
+ deleteRenderBuffer(fb, fb.getColorBuffer());
+ }
+
+ intBuf1.put(0, fb.getId());
+ glDeleteFramebuffersEXT(intBuf1);
+ fb.resetObject();
+
+ statistics.onDeleteFrameBuffer();
+ }
+ }
+*/
+
+ /*********************************************************************\
+ |* Textures *|
+ \*********************************************************************/
+ private int convertTextureType(Texture.Type type){
+ switch (type){
+ case TwoDimensional:
+ return GLES20.GL_TEXTURE_2D;
+ // case TwoDimensionalArray:
+ // return EXTTextureArray.GL_TEXTURE_2D_ARRAY_EXT;
+// case ThreeDimensional:
+ // return GLES20.GL_TEXTURE_3D;
+ case CubeMap:
+ return GLES20.GL_TEXTURE_CUBE_MAP;
+ default:
+ throw new UnsupportedOperationException("Unknown texture type: "+type);
+ }
+ }
+
+ private int convertMagFilter(Texture.MagFilter filter){
+ switch (filter){
+ case Bilinear:
+ return GLES20.GL_LINEAR;
+ case Nearest:
+ return GLES20.GL_NEAREST;
+ default:
+ throw new UnsupportedOperationException("Unknown mag filter: "+filter);
+ }
+ }
+
+ private int convertMinFilter(Texture.MinFilter filter){
+ switch (filter){
+ case Trilinear:
+ return GLES20.GL_LINEAR_MIPMAP_LINEAR;
+ case BilinearNearestMipMap:
+ return GLES20.GL_LINEAR_MIPMAP_NEAREST;
+ case NearestLinearMipMap:
+ return GLES20.GL_NEAREST_MIPMAP_LINEAR;
+ case NearestNearestMipMap:
+ return GLES20.GL_NEAREST_MIPMAP_NEAREST;
+ case BilinearNoMipMaps:
+ return GLES20.GL_LINEAR;
+ case NearestNoMipMaps:
+ return GLES20.GL_NEAREST;
+ default:
+ throw new UnsupportedOperationException("Unknown min filter: "+filter);
+ }
+ }
+
+ private int convertWrapMode(Texture.WrapMode mode){
+ switch (mode){
+// case BorderClamp:
+// return GLES20.GL_CLAMP_TO_BORDER;
+// case Clamp:
+// return GLES20.GL_CLAMP;
+ case EdgeClamp:
+ return GLES20.GL_CLAMP_TO_EDGE;
+ case Repeat:
+ return GLES20.GL_REPEAT;
+ case MirroredRepeat:
+ return GLES20.GL_MIRRORED_REPEAT;
+ default:
+ throw new UnsupportedOperationException("Unknown wrap mode: "+mode);
+ }
+ }
+
+ private void setupTextureParams(Texture tex){
+ int target = convertTextureType(tex.getType());
+
+ // filter things
+ int minFilter = convertMinFilter(tex.getMinFilter());
+ int magFilter = convertMagFilter(tex.getMagFilter());
+
+ if (verboseLogging)
+ logger.info("GLES20.glTexParameteri(" + target + ", GLES20.GL_TEXTURE_MIN_FILTER, " + minFilter + ")");
+
+ GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_MIN_FILTER, minFilter);
+
+ if (verboseLogging)
+ logger.info("GLES20.glTexParameteri(" + target + ", GLES20.GL_TEXTURE_MAG_FILTER, " + magFilter + ")");
+
+ GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_MAG_FILTER, magFilter);
+
+ if (tex.getAnisotropicFilter() > 1){
+/*
+ if (GLContext.getCapabilities().GL_EXT_texture_filter_anisotropic){
+ glTexParameterf(target,
+ EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT,
+ tex.getAnisotropicFilter());
+ }
+*/
+ }
+ // repeat modes
+
+ switch (tex.getType()){
+ case ThreeDimensional:
+ case CubeMap: // cubemaps use 3D coords
+// GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_R, convertWrapMode(tex.getWrap(WrapAxis.R)));
+ case TwoDimensional:
+ case TwoDimensionalArray:
+
+ if (verboseLogging)
+ logger.info("GLES20.glTexParameteri(" + target + ", GLES20.GL_TEXTURE_WRAP_T, " + convertWrapMode(tex.getWrap(WrapAxis.T)));
+
+ GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_T, convertWrapMode(tex.getWrap(WrapAxis.T)));
+ // fall down here is intentional..
+// case OneDimensional:
+
+ if (verboseLogging)
+ logger.info("GLES20.glTexParameteri(" + target + ", GLES20.GL_TEXTURE_WRAP_S, " + convertWrapMode(tex.getWrap(WrapAxis.S)));
+
+ GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_S, convertWrapMode(tex.getWrap(WrapAxis.S)));
+ break;
+ default:
+ throw new UnsupportedOperationException("Unknown texture type: "+tex.getType());
+ }
+
+ // R to Texture compare mode
+/*
+ if (tex.getShadowCompareMode() != Texture.ShadowCompareMode.Off){
+ GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_COMPARE_MODE, GLES20.GL_COMPARE_R_TO_TEXTURE);
+ GLES20.glTexParameteri(target, GLES20.GL_DEPTH_TEXTURE_MODE, GLES20.GL_INTENSITY);
+ if (tex.getShadowCompareMode() == Texture.ShadowCompareMode.GreaterOrEqual){
+ GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_COMPARE_FUNC, GLES20.GL_GEQUAL);
+ }else{
+ GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_COMPARE_FUNC, GLES20.GL_LEQUAL);
+ }
+ }
+*/
+ }
+
+ public void updateTexImageData(Image img, Texture.Type type, boolean mips){
+ int texId = img.getId();
+ if (texId == -1){
+ // create texture
+
+ if (verboseLogging)
+ logger.info("GLES20.glGenTexture(1, buffer)");
+
+ GLES20.glGenTextures(1, intBuf1);
+ texId = intBuf1.get(0);
+ img.setId(texId);
+ objManager.registerForCleanup(img);
+
+ statistics.onNewTexture();
+ }
+
+ // bind texture
+ int target = convertTextureType(type);
+ if (context.boundTextures[0] != img){
+ if (context.boundTextureUnit != 0){
+
+ if (verboseLogging)
+ logger.info("GLES20.glActiveTexture(GLES20.GL_TEXTURE0)");
+
+ GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
+ context.boundTextureUnit = 0;
+ }
+
+ if (verboseLogging)
+ logger.info("GLES20.glBindTexture(" + target + ", " + texId + ")");
+
+ GLES20.glBindTexture(target, texId);
+ context.boundTextures[0] = img;
+ }
+
+ if (!img.hasMipmaps() && mips){
+ // No pregenerated mips available,
+ // generate from base level if required
+// if (!GLContext.getCapabilities().GL_EXT_framebuffer_multisample){
+
+ if (verboseLogging)
+ logger.info("GLES20.glTexParameteri(" + target + "GLES11.GL_GENERATE_MIMAP, GLES20.GL_TRUE)");
+
+ GLES20.glTexParameteri(target, GLES11.GL_GENERATE_MIPMAP, GLES20.GL_TRUE);
+// }
+ }else{
+// glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, 0 );
+ if (img.getMipMapSizes() != null){
+// GLES20.glTexParameteri(target, GLES11.GL_TEXTURE_MAX_LEVEL, img.getMipMapSizes().length );
+ }
+ }
+
+
+ if (target == GLES20.GL_TEXTURE_CUBE_MAP){
+ List data = img.getData();
+ if (data.size() != 6){
+ logger.log(Level.WARNING, "Invalid texture: {0}\n"
+ + "Cubemap textures must contain 6 data units.", img);
+ return;
+ }
+ for (int i = 0; i < 6; i++){
+ TextureUtil.uploadTexture(gl, img, GLES20.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, i, 0, tdc, true, powerOf2);
+ }
+ }/*else if (target == EXTTextureArray.GL_TEXTURE_2D_ARRAY_EXT){
+ List data = img.getData();
+ // -1 index specifies prepare data for 2D Array
+ TextureUtil.uploadTexture(img, target, -1, 0, tdc);
+ for (int i = 0; i < data.size(); i++){
+ // upload each slice of 2D array in turn
+ // this time with the appropriate index
+ TextureUtil.uploadTexture(img, target, i, 0, tdc);
+ }
+ }*/else{
+ TextureUtil.uploadTexture(gl, img, target, 0, 0, tdc, true, powerOf2);
+
+ if (verboseLogging)
+ logger.info("GLES20.glTexParameteri(" + target + "GLES11.GL_GENERATE_MIMAP, GLES20.GL_TRUE)");
+
+ GLES20.glTexParameteri(target, GLES11.GL_GENERATE_MIPMAP, GLES20.GL_TRUE);
+ }
+
+// if (GLContext.getCapabilities().GL_EXT_framebuffer_multisample){
+// glGenerateMipmapEXT(target);
+// }
+
+ img.clearUpdateNeeded();
+ }
+
+ public void setTexture(int unit, Texture tex){
+ Image image = tex.getImage();
+ if (image.isUpdateNeeded()){
+ updateTexImageData(image, tex.getType(), tex.getMinFilter().usesMipMapLevels());
+ }
+
+ int texId = image.getId();
+ assert texId != -1;
+
+ if (texId == -1) {
+ logger.warning("error: texture image has -1 id");
+ }
+
+ Image[] textures = context.boundTextures;
+
+ int type = convertTextureType(tex.getType());
+ if (!context.textureIndexList.moveToNew(unit)){
+// if (context.boundTextureUnit != unit){
+// glActiveTexture(GL_TEXTURE0 + unit);
+// context.boundTextureUnit = unit;
+// }
+// glEnable(type);
+ }
+
+ if (textures[unit] != image){
+ if (context.boundTextureUnit != unit){
+ if (verboseLogging)
+ logger.info("GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + " + unit + ")");
+ GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + unit);
+ context.boundTextureUnit = unit;
+ }
+
+ if (verboseLogging)
+ logger.info("GLES20.glBindTexture(" + type + ", " + texId + ")");
+
+ GLES20.glBindTexture(type, texId);
+ textures[unit] = image;
+
+ statistics.onTextureUse(tex.getImage(), true);
+ }else{
+ statistics.onTextureUse(tex.getImage(), false);
+ }
+
+ setupTextureParams(tex);
+ }
+
+ public void clearTextureUnits(){
+ IDList textureList = context.textureIndexList;
+ Image[] textures = context.boundTextures;
+ for (int i = 0; i < textureList.oldLen; i++){
+ int idx = textureList.oldList[i];
+// if (context.boundTextureUnit != idx){
+// glActiveTexture(GL_TEXTURE0 + idx);
+// context.boundTextureUnit = idx;
+// }
+// glDisable(convertTextureType(textures[idx].getType()));
+ textures[idx] = null;
+ }
+ context.textureIndexList.copyNewToOld();
+ }
+
+ public void deleteImage(Image image){
+ int texId = image.getId();
+ if (texId != -1){
+ intBuf1.put(0, texId);
+ intBuf1.position(0).limit(1);
+
+ if (verboseLogging)
+ logger.info("GLES20.glDeleteTexture(1, buffer)");
+
+ GLES20.glDeleteTextures(1, intBuf1);
+ image.resetObject();
+
+ statistics.onDeleteTexture();
+ }
+ }
+
+ /*********************************************************************\
+ |* Vertex Buffers and Attributes *|
+ \*********************************************************************/
+ private int convertUsage(Usage usage){
+ switch (usage){
+ case Static:
+ return GLES20.GL_STATIC_DRAW;
+ case Dynamic:
+ return GLES20.GL_DYNAMIC_DRAW;
+ case Stream:
+ return GLES20.GL_STREAM_DRAW;
+ default:
+ throw new RuntimeException("Unknown usage type.");
+ }
+ }
+
+ private int convertFormat(Format format){
+ switch (format){
+ case Byte:
+ return GLES20.GL_BYTE;
+ case UnsignedByte:
+ return GLES20.GL_UNSIGNED_BYTE;
+ case Short:
+ return GLES20.GL_SHORT;
+ case UnsignedShort:
+ return GLES20.GL_UNSIGNED_SHORT;
+ case Int:
+ return GLES20.GL_INT;
+ case UnsignedInt:
+ return GLES20.GL_UNSIGNED_INT;
+/*
+ case Half:
+ return NVHalfFloat.GL_HALF_FLOAT_NV;
+// return ARBHalfFloatVertex.GL_HALF_FLOAT;
+*/
+ case Float:
+ return GLES20.GL_FLOAT;
+// case Double:
+// return GLES20.GL_DOUBLE;
+ default:
+ throw new RuntimeException("Unknown buffer format.");
+
+ }
+ }
+
+ public void updateBufferData(VertexBuffer vb){
+
+ if (verboseLogging)
+ logger.info("updateBufferData(" + vb + ")");
+
+ int bufId = vb.getId();
+ boolean created = false;
+ if (bufId == -1){
+ // create buffer
+
+ if (verboseLogging)
+ logger.info("GLES20.glGenBuffers(" + 1 + ", buffer)");
+
+ GLES20.glGenBuffers(1, intBuf1);
+ bufId = intBuf1.get(0);
+ vb.setId(bufId);
+ objManager.registerForCleanup(vb);
+
+ created = true;
+ }
+
+ // bind buffer
+ int target;
+ if (vb.getBufferType() == VertexBuffer.Type.Index){
+ target = GLES20.GL_ELEMENT_ARRAY_BUFFER;
+
+ if (verboseLogging)
+ logger.info("vb.getBufferType() == VertexBuffer.Type.Index");
+
+ if (context.boundElementArrayVBO != bufId){
+
+ if (verboseLogging)
+ logger.info("GLES20.glBindBuffer(" + target + ", " + bufId + ")");
+
+ GLES20.glBindBuffer(target, bufId);
+ context.boundElementArrayVBO = bufId;
+ }
+ }else{
+ if (verboseLogging)
+ logger.info("vb.getBufferType() != VertexBuffer.Type.Index");
+
+ target = GLES20.GL_ARRAY_BUFFER;
+
+ if (context.boundArrayVBO != bufId){
+
+ if (verboseLogging)
+ logger.info("GLES20.glBindBuffer(" + target + ", " + bufId + ")");
+
+ GLES20.glBindBuffer(target, bufId);
+ context.boundArrayVBO = bufId;
+ }
+ }
+
+ int usage = convertUsage(vb.getUsage());
+ vb.getData().clear();
+
+ if (created || vb.hasDataSizeChanged()){
+ // upload data based on format
+ int size = vb.getData().capacity() * vb.getFormat().getComponentSize();
+
+ switch (vb.getFormat()){
+ case Byte:
+ case UnsignedByte:
+
+ if (verboseLogging)
+ logger.info("GLES20.glBufferData(" + target + ", " + size + ", (data), " + usage + ")");
+
+ GLES20.glBufferData(target, size, (ByteBuffer) vb.getData(), usage);
+ break;
+ // case Half:
+ case Short:
+ case UnsignedShort:
+
+ if (verboseLogging)
+ logger.info("GLES20.glBufferData(" + target + ", " + size + ", (data), " + usage + ")");
+
+ GLES20.glBufferData(target, size, (ShortBuffer) vb.getData(), usage);
+ break;
+ case Int:
+ case UnsignedInt:
+
+ if (verboseLogging)
+ logger.info("GLES20.glBufferData(" + target + ", " + size + ", (data), " + usage + ")");
+
+ GLES20.glBufferData(target, size, (IntBuffer) vb.getData(), usage);
+ break;
+ case Float:
+ if (verboseLogging)
+ logger.info("GLES20.glBufferData(" + target + ", " + size + ", (data), " + usage + ")");
+
+ GLES20.glBufferData(target, size, (FloatBuffer) vb.getData(), usage);
+ break;
+ case Double:
+ if (verboseLogging)
+ logger.info("GLES20.glBufferData(" + target + ", " + size + ", (data), " + usage + ")");
+
+ GLES20.glBufferData(target, size, (DoubleBuffer) vb.getData(), usage);
+ break;
+ default:
+ throw new RuntimeException("Unknown buffer format.");
+ }
+ }else{
+ int size = vb.getData().capacity() * vb.getFormat().getComponentSize();
+
+ switch (vb.getFormat()){
+ case Byte:
+ case UnsignedByte:
+ if (verboseLogging)
+ logger.info("GLES20.glBufferSubData(" + target + ", 0, " + size + ", (data))");
+
+ GLES20.glBufferSubData(target, 0, size, (ByteBuffer) vb.getData());
+ break;
+ case Short:
+ case UnsignedShort:
+ if (verboseLogging)
+ logger.info("GLES20.glBufferSubData(" + target + ", 0, " + size + ", (data))");
+
+ GLES20.glBufferSubData(target, 0, size, (ShortBuffer) vb.getData());
+ break;
+ case Int:
+ case UnsignedInt:
+ if (verboseLogging)
+ logger.info("GLES20.glBufferSubData(" + target + ", 0, " + size + ", (data))");
+
+ GLES20.glBufferSubData(target, 0, size, (IntBuffer) vb.getData());
+ break;
+ case Float:
+ if (verboseLogging)
+ logger.info("GLES20.glBufferSubData(" + target + ", 0, " + size + ", (data))");
+
+ GLES20.glBufferSubData(target, 0, size, (FloatBuffer) vb.getData());
+ break;
+ case Double:
+ if (verboseLogging)
+ logger.info("GLES20.glBufferSubData(" + target + ", 0, " + size + ", (data))");
+
+ GLES20.glBufferSubData(target, 0, size, (DoubleBuffer) vb.getData());
+ break;
+ default:
+ throw new RuntimeException("Unknown buffer format.");
+ }
+ }
+// }else{
+// if (created || vb.hasDataSizeChanged()){
+// glBufferData(target, vb.getData().capacity() * vb.getFormat().getComponentSize(), usage);
+// }
+//
+// ByteBuffer buf = glMapBuffer(target,
+// GL_WRITE_ONLY,
+// vb.getMappedData());
+//
+// if (buf != vb.getMappedData()){
+// buf = buf.order(ByteOrder.nativeOrder());
+// vb.setMappedData(buf);
+// }
+//
+// buf.clear();
+//
+// switch (vb.getFormat()){
+// case Byte:
+// case UnsignedByte:
+// buf.put( (ByteBuffer) vb.getData() );
+// break;
+// case Short:
+// case UnsignedShort:
+// buf.asShortBuffer().put( (ShortBuffer) vb.getData() );
+// break;
+// case Int:
+// case UnsignedInt:
+// buf.asIntBuffer().put( (IntBuffer) vb.getData() );
+// break;
+// case Float:
+// buf.asFloatBuffer().put( (FloatBuffer) vb.getData() );
+// break;
+// case Double:
+// break;
+// default:
+// throw new RuntimeException("Unknown buffer format.");
+// }
+//
+// glUnmapBuffer(target);
+// }
+
+ vb.clearUpdateNeeded();
+ }
+
+ public void deleteBuffer(VertexBuffer vb) {
+ int bufId = vb.getId();
+ if (bufId != -1){
+ // delete buffer
+ intBuf1.put(0, bufId);
+ intBuf1.position(0).limit(1);
+ if (verboseLogging)
+ logger.info("GLES20.glDeleteBuffers(1, buffer)");
+
+ GLES20.glDeleteBuffers(1, intBuf1);
+ vb.resetObject();
+ }
+ }
+
+ public void clearVertexAttribs(){
+ IDList attribList = context.attribIndexList;
+ for (int i = 0; i < attribList.oldLen; i++){
+ int idx = attribList.oldList[i];
+
+ if (verboseLogging)
+ logger.info("GLES20.glDisableVertexAttribArray(" + idx + ")");
+
+ GLES20.glDisableVertexAttribArray(idx);
+ context.boundAttribs[idx] = null;
+ }
+ context.attribIndexList.copyNewToOld();
+ }
+
+ public void setVertexAttrib(VertexBuffer vb, VertexBuffer idb){
+ if (verboseLogging)
+ logger.info("setVertexAttrib(" + vb + ", " + idb + ")");
+
+ if (vb.getBufferType() == VertexBuffer.Type.Index)
+ throw new IllegalArgumentException("Index buffers not allowed to be set to vertex attrib");
+
+ if (vb.isUpdateNeeded() && idb == null)
+ updateBufferData(vb);
+
+ int programId = context.boundShaderProgram;
+ if (programId > 0){
+ Attribute attrib = boundShader.getAttribute(vb.getBufferType().name());
+ int loc = attrib.getLocation();
+ if (loc == -1) {
+
+ if (verboseLogging)
+ logger.warning("location is invalid for attrib: [" + vb.getBufferType().name() + "]");
+
+ return; // not defined
+ }
+
+ if (loc == -2){
+// stringBuf.setLength(0);
+// stringBuf.append("in").append(vb.getBufferType().name()).append('\0');
+// updateNameBuffer();
+
+ String attributeName = "in" + vb.getBufferType().name();
+
+ if (verboseLogging)
+ logger.info("GLES20.glGetAttribLocation(" + programId + ", " + attributeName + ")");
+
+ loc = GLES20.glGetAttribLocation(programId, attributeName);
+
+ // not really the name of it in the shader (inPosition\0) but
+ // the internal name of the enum (Position).
+ if (loc < 0){
+ attrib.setLocation(-1);
+
+ if (verboseLogging)
+ logger.warning("attribute is invalid in shader: [" + vb.getBufferType().name() + "]");
+
+ return; // not available in shader.
+ }else{
+ attrib.setLocation(loc);
+ }
+ }
+
+ VertexBuffer[] attribs = context.boundAttribs;
+ if (!context.attribIndexList.moveToNew(loc)){
+ if (verboseLogging)
+ logger.info("GLES20.glEnableVertexAttribArray(" + loc + ")");
+
+ GLES20.glEnableVertexAttribArray(loc);
+ //System.out.println("Enabled ATTRIB IDX: "+loc);
+ }
+ if (attribs[loc] != vb){
+ // NOTE: Use id from interleaved buffer if specified
+ int bufId = idb != null ? idb.getId() : vb.getId();
+ assert bufId != -1;
+
+ if (bufId == -1) {
+ logger.warning("invalid buffer id");
+ }
+
+ if (context.boundArrayVBO != bufId){
+ if (verboseLogging)
+ logger.info("GLES20.glBindBuffer(" + GLES20.GL_ARRAY_BUFFER + ", " + bufId+ ")");
+ GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufId);
+ context.boundArrayVBO = bufId;
+ }
+
+ vb.getData().clear();
+
+ if (verboseLogging)
+ logger.info("GLES20.glVertexAttribPointer(" +
+ "location=" + loc + ", " +
+ "numComponents=" + vb.getNumComponents() + ", " +
+ "format=" + vb.getFormat() + ", " +
+ "isNormalized=" + vb.isNormalized() + ", " +
+ "stride=" + vb.getStride() + ", " +
+ "data.capacity=" + vb.getData().capacity() + ")"
+ );
+
+ GLES20.glVertexAttribPointer(loc,
+ vb.getNumComponents(),
+ convertFormat(vb.getFormat()),
+ vb.isNormalized(),
+ vb.getStride(),
+ vb.getData());
+
+ attribs[loc] = vb;
+ }
+ }else{
+ throw new IllegalStateException("Cannot render mesh without shader bound");
+ }
+ }
+
+ public void setVertexAttrib(VertexBuffer vb){
+ setVertexAttrib(vb, null);
+ }
+
+ public void drawTriangleArray(Mesh.Mode mode, int count, int vertCount){
+/* if (count > 1){
+ ARBDrawInstanced.glDrawArraysInstancedARB(convertElementMode(mode), 0,
+ vertCount, count);
+ }else{*/
+ if (verboseLogging)
+ logger.info("GLES20.glDrawArrays(" + vertCount + ")");
+
+ GLES20.glDrawArrays(convertElementMode(mode), 0, vertCount);
+/*
+ }*/
+ }
+
+ public void drawTriangleList(VertexBuffer indexBuf, Mesh mesh, int count){
+
+ if (verboseLogging)
+ logger.info("drawTriangleList(" + count + ")");
+
+ if (indexBuf.getBufferType() != VertexBuffer.Type.Index)
+ throw new IllegalArgumentException("Only index buffers are allowed as triangle lists.");
+
+ if (indexBuf.isUpdateNeeded()) {
+ if (verboseLogging)
+ logger.info("updateBufferData for indexBuf.");
+ updateBufferData(indexBuf);
+ }
+
+ int bufId = indexBuf.getId();
+ assert bufId != -1;
+
+ if (bufId == -1) {
+ logger.info("invalid buffer id!");
+ }
+
+ if (context.boundElementArrayVBO != bufId){
+ if (verboseLogging)
+ logger.info("GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, " + bufId + ")");
+
+ GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, bufId);
+ context.boundElementArrayVBO = bufId;
+ }
+
+ int vertCount = mesh.getVertexCount();
+ boolean useInstancing = count > 1 && caps.contains(Caps.MeshInstancing);
+
+ Buffer indexData = indexBuf.getData();
+
+ if (mesh.getMode() == Mode.Hybrid){
+ int[] modeStart = mesh.getModeStart();
+ int[] elementLengths = mesh.getElementLengths();
+
+ int elMode = convertElementMode(Mode.Triangles);
+ int fmt = convertFormat(indexBuf.getFormat());
+ int elSize = indexBuf.getFormat().getComponentSize();
+ int listStart = modeStart[0];
+ int stripStart = modeStart[1];
+ int fanStart = modeStart[2];
+ int curOffset = 0;
+ for (int i = 0; i < elementLengths.length; i++){
+ if (i == stripStart){
+ elMode = convertElementMode(Mode.TriangleStrip);
+ }else if (i == fanStart){
+ elMode = convertElementMode(Mode.TriangleStrip);
+ }
+ int elementLength = elementLengths[i];
+
+ if (useInstancing){
+ //ARBDrawInstanced.
+ throw new IllegalArgumentException("instancing is not supported.");
+/*
+ GLES20.glDrawElementsInstancedARB(elMode,
+ elementLength,
+ fmt,
+ curOffset,
+ count);
+*/
+ }else{
+ indexBuf.getData().position(curOffset);
+ if (verboseLogging)
+ logger.info("glDrawElements(): " + elementLength + ", " + curOffset);
+
+ GLES20.glDrawElements(elMode, elementLength, fmt, indexBuf.getData());
+/*
+ glDrawRangeElements(elMode,
+ 0,
+ vertCount,
+ elementLength,
+ fmt,
+ curOffset);
+*/
+ }
+
+ curOffset += elementLength * elSize;
+ }
+ }else{
+ if (useInstancing){
+ throw new IllegalArgumentException("instancing is not supported.");
+ //ARBDrawInstanced.
+/*
+ GLES20.glDrawElementsInstancedARB(convertElementMode(mesh.getMode()),
+ indexBuf.getData().capacity(),
+ convertFormat(indexBuf.getFormat()),
+ 0,
+ count);
+*/
+ }else{
+ indexData.clear();
+
+ if (verboseLogging)
+ logger.info("glDrawElements(), indexBuf.capacity (" + indexBuf.getData().capacity() + "), vertCount (" + vertCount + ")");
+
+ GLES11.glDrawElements(
+ convertElementMode(mesh.getMode()),
+ indexBuf.getData().capacity(),
+ convertFormat(indexBuf.getFormat()),
+ 0
+ );
+ }
+ }
+ }
+
+ /*********************************************************************\
+ |* Render Calls *|
+ \*********************************************************************/
+ public int convertElementMode(Mesh.Mode mode){
+ switch (mode){
+ case Points:
+ return GLES20.GL_POINTS;
+ case Lines:
+ return GLES20.GL_LINES;
+ case LineLoop:
+ return GLES20.GL_LINE_LOOP;
+ case LineStrip:
+ return GLES20.GL_LINE_STRIP;
+ case Triangles:
+ return GLES20.GL_TRIANGLES;
+ case TriangleFan:
+ return GLES20.GL_TRIANGLE_FAN;
+ case TriangleStrip:
+ return GLES20.GL_TRIANGLE_STRIP;
+ default:
+ throw new UnsupportedOperationException("Unrecognized mesh mode: "+mode);
+ }
+ }
+
+ public void updateVertexArray(Mesh mesh){
+ logger.info("updateVertexArray(" + mesh + ")");
+ int id = mesh.getId();
+/*
+ if (id == -1){
+ IntBuffer temp = intBuf1;
+ // ARBVertexArrayObject.glGenVertexArrays(temp);
+ GLES20.glGenVertexArrays(temp);
+ id = temp.get(0);
+ mesh.setId(id);
+ }
+
+ if (context.boundVertexArray != id){
+ // ARBVertexArrayObject.glBindVertexArray(id);
+ GLES20.glBindVertexArray(id);
+ context.boundVertexArray = id;
+ }
+*/
+ VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData);
+ if (interleavedData != null && interleavedData.isUpdateNeeded()){
+ updateBufferData(interleavedData);
+ }
+
+ IntMap buffers = mesh.getBuffers();
+ for (Entry entry : buffers){
+ VertexBuffer vb = entry.getValue();
+
+ if (vb.getBufferType() == Type.InterleavedData
+ || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers
+ || vb.getBufferType() == Type.Index)
+ continue;
+
+ if (vb.getStride() == 0){
+ // not interleaved
+ setVertexAttrib(vb);
+ }else{
+ // interleaved
+ setVertexAttrib(vb, interleavedData);
+ }
+ }
+ }
+
+
+ /**
+ * renderMeshVertexArray renders a mesh using vertex arrays
+ * @param mesh
+ * @param lod
+ * @param count
+ */
+ private void renderMeshVertexArray(Mesh mesh, int lod, int count)
+ {
+ if (verboseLogging)
+ logger.info("renderMeshVertexArray");
+
+ IntMap buffers = mesh.getBuffers();
+ for (Entry entry : buffers){
+ VertexBuffer vb = entry.getValue();
+
+ if (vb.getBufferType() == Type.InterleavedData
+ || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers
+ || vb.getBufferType() == Type.Index)
+ continue;
+
+ if (vb.getStride() == 0){
+ // not interleaved
+ setVertexAttrib_Array(vb);
+ }else{
+ // interleaved
+ VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData);
+ setVertexAttrib_Array(vb, interleavedData);
+ }
+ }
+
+ VertexBuffer indices = null;
+ if (mesh.getNumLodLevels() > 0)
+ {
+ indices = mesh.getLodLevel(lod);
+ }
+ else
+ {
+ indices = buffers.get(Type.Index.ordinal());
+ }
+ if (indices != null)
+ {
+ drawTriangleList_Array(indices, mesh, count);
+ }
+ else
+ {
+ if (verboseLogging)
+ logger.info("GLES20.glDrawArrays(" + mesh.getMode() + ", " + 0 + ", " + mesh.getVertexCount() + ")");
+
+ GLES20.glDrawArrays(convertElementMode(mesh.getMode()), 0, mesh.getVertexCount());
+ }
+ clearVertexAttribs();
+ clearTextureUnits();
+ }
+
+
+ private void renderMeshDefault(Mesh mesh, int lod, int count){
+ if (verboseLogging)
+ logger.info("renderMeshDefault(" + mesh + ", " + lod + ", " + count + ")");
+ VertexBuffer indices = null;
+
+ VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData);
+ if (interleavedData != null && interleavedData.isUpdateNeeded()){
+ updateBufferData(interleavedData);
+ }
+
+ IntMap buffers = mesh.getBuffers();
+ if (mesh.getNumLodLevels() > 0){
+ indices = mesh.getLodLevel(lod);
+ }else{
+ indices = buffers.get(Type.Index.ordinal());
+ }
+ for (Entry entry : buffers){
+ VertexBuffer vb = entry.getValue();
+
+ if (vb.getBufferType() == Type.InterleavedData
+ || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers
+ || vb.getBufferType() == Type.Index)
+ continue;
+
+ if (vb.getStride() == 0){
+ // not interleaved
+ setVertexAttrib(vb);
+ }else{
+ // interleaved
+ setVertexAttrib(vb, interleavedData);
+ }
+ }
+ if (indices != null){
+ drawTriangleList(indices, mesh, count);
+ }else{
+// throw new UnsupportedOperationException("Cannot render without index buffer");
+ if (verboseLogging)
+ logger.info("GLES20.glDrawArrays(" + convertElementMode(mesh.getMode()) + ", 0, " + mesh.getVertexCount() + ")");
+
+ GLES20.glDrawArrays(convertElementMode(mesh.getMode()), 0, mesh.getVertexCount());
+ }
+ clearVertexAttribs();
+ clearTextureUnits();
+ }
+
+ public void renderMesh(Mesh mesh, int lod, int count) {
+ if (context.pointSize != mesh.getPointSize()){
+
+ if (verboseLogging)
+ logger.info("GLES10.glPointSize(" + mesh.getPointSize() + ")");
+
+ GLES10.glPointSize(mesh.getPointSize());
+ context.pointSize = mesh.getPointSize();
+ }
+ if (context.lineWidth != mesh.getLineWidth()){
+
+ if (verboseLogging)
+ logger.info("GLES20.glLineWidth(" + mesh.getLineWidth() + ")");
+
+ GLES20.glLineWidth(mesh.getLineWidth());
+ context.lineWidth = mesh.getLineWidth();
+ }
+
+ statistics.onMeshDrawn(mesh, lod);
+// if (GLContext.getCapabilities().GL_ARB_vertex_array_object){
+// renderMeshVertexArray(mesh, lod, count);
+// }else{
+
+ if (useVBO) {
+ if (verboseLogging)
+ logger.info("RENDERING A MESH USING VertexBufferObject");
+
+ renderMeshDefault(mesh, lod, count);
+ } else {
+ if (verboseLogging)
+ logger.info("RENDERING A MESH USING VertexArray");
+
+ renderMeshVertexArray(mesh, lod, count);
+ }
+
+// }
+ }
+
+ private void checkGLError() {
+ int error;
+ while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
+ logger.warning("glError " + error);
+ // throw new RuntimeException("glError " + error);
+ }
+ }
+
+ private boolean log(String message) {
+ logger.info(message);
+ return true;
+ }
+
+ /**
+ * drawTriangleList_Array uses Vertex Array
+ * @param indexBuf
+ * @param mesh
+ * @param count
+ */
+ public void drawTriangleList_Array(VertexBuffer indexBuf, Mesh mesh, int count)
+ {
+ if (verboseLogging)
+ logger.info("drawTriangleList_Array(Count = " + count + ")");
+
+ if (indexBuf.getBufferType() != VertexBuffer.Type.Index)
+ throw new IllegalArgumentException("Only index buffers are allowed as triangle lists.");
+
+ boolean useInstancing = count > 1 && caps.contains(Caps.MeshInstancing);
+ if (useInstancing)
+ {
+ throw new IllegalArgumentException("Caps.MeshInstancing is not supported.");
+ }
+
+ int vertCount = mesh.getVertexCount();
+ Buffer indexData = indexBuf.getData();
+ indexData.clear();
+
+ if (mesh.getMode() == Mode.Hybrid)
+ {
+ int[] modeStart = mesh.getModeStart();
+ int[] elementLengths = mesh.getElementLengths();
+
+ int elMode = convertElementMode(Mode.Triangles);
+ int fmt = convertFormat(indexBuf.getFormat());
+ int elSize = indexBuf.getFormat().getComponentSize();
+ int listStart = modeStart[0];
+ int stripStart = modeStart[1];
+ int fanStart = modeStart[2];
+ int curOffset = 0;
+ for (int i = 0; i < elementLengths.length; i++)
+ {
+ if (i == stripStart)
+ {
+ elMode = convertElementMode(Mode.TriangleStrip);
+ }
+ else if (i == fanStart)
+ {
+ elMode = convertElementMode(Mode.TriangleStrip);
+ }
+ int elementLength = elementLengths[i];
+
+ indexBuf.getData().position(curOffset);
+ if (verboseLogging)
+ logger.info("glDrawElements(): " + elementLength + ", " + curOffset);
+
+ GLES20.glDrawElements(elMode, elementLength, fmt, indexBuf.getData());
+
+ curOffset += elementLength * elSize;
+ }
+ }
+ else //if (mesh.getMode() == Mode.Hybrid)
+ {
+ if (verboseLogging)
+ logger.info("glDrawElements(), indexBuf.capacity (" + indexBuf.getData().capacity() + "), vertCount (" + vertCount + ")");
+
+ GLES20.glDrawElements(
+ convertElementMode(mesh.getMode()),
+ indexBuf.getData().capacity(),
+ convertFormat(indexBuf.getFormat()),
+ indexBuf.getData()
+ );
+ }
+ }
+
+ /**
+ * setVertexAttrib_Array uses Vertex Array
+ * @param vb
+ * @param idb
+ */
+ public void setVertexAttrib_Array(VertexBuffer vb, VertexBuffer idb)
+ {
+ if (verboseLogging)
+ logger.info("setVertexAttrib_Array(" + vb + ", " + idb + ")");
+
+ if (vb.getBufferType() == VertexBuffer.Type.Index)
+ throw new IllegalArgumentException("Index buffers not allowed to be set to vertex attrib");
+
+ // Get shader
+ int programId = context.boundShaderProgram;
+ if (programId > 0)
+ {
+ VertexBuffer[] attribs = context.boundAttribs;
+
+ Attribute attrib = boundShader.getAttribute(vb.getBufferType().name());
+ int loc = attrib.getLocation();
+ if (loc == -1)
+ {
+ //throw new IllegalArgumentException("Location is invalid for attrib: [" + vb.getBufferType().name() + "]");
+ if (verboseLogging)
+ logger.warning("attribute is invalid in shader: [" + vb.getBufferType().name() + "]");
+ return;
+ }
+ else if (loc == -2)
+ {
+ String attributeName = "in" + vb.getBufferType().name();
+
+ if (verboseLogging)
+ logger.info("GLES20.glGetAttribLocation(" + programId + ", " + attributeName + ")");
+
+ loc = GLES20.glGetAttribLocation(programId, attributeName);
+ if (loc < 0)
+ {
+ attrib.setLocation(-1);
+ if (verboseLogging)
+ logger.warning("attribute is invalid in shader: [" + vb.getBufferType().name() + "]");
+ return; // not available in shader.
+ }
+ else
+ {
+ attrib.setLocation(loc);
+ }
+
+ } // if (loc == -2)
+
+ if ((attribs[loc] != vb) || vb.isUpdateNeeded())
+ {
+ // NOTE: Use data from interleaved buffer if specified
+ VertexBuffer avb = idb != null ? idb : vb;
+ avb.getData().clear();
+ avb.getData().position(vb.getOffset());
+
+ if (verboseLogging)
+ logger.info("GLES20.glVertexAttribPointer(" +
+ "location=" + loc + ", " +
+ "numComponents=" + vb.getNumComponents() + ", " +
+ "format=" + vb.getFormat() + ", " +
+ "isNormalized=" + vb.isNormalized() + ", " +
+ "stride=" + vb.getStride() + ", " +
+ "data.capacity=" + avb.getData().capacity() + ")"
+ );
+
+
+ // Upload attribute data
+ GLES20.glVertexAttribPointer(loc,
+ vb.getNumComponents(),
+ convertFormat(vb.getFormat()),
+ vb.isNormalized(),
+ vb.getStride(),
+ avb.getData());
+ checkGLError();
+
+ GLES20.glEnableVertexAttribArray(loc);
+
+ attribs[loc] = vb;
+ } // if (attribs[loc] != vb)
+ }
+ else
+ {
+ throw new IllegalStateException("Cannot render mesh without shader bound");
+ }
+ }
+
+ /**
+ * setVertexAttrib_Array uses Vertex Array
+ * @param vb
+ */
+ public void setVertexAttrib_Array(VertexBuffer vb)
+ {
+ setVertexAttrib_Array(vb, null);
+ }
+
+ public void setAlphaToCoverage(boolean value)
+ {
+ // TODO Auto-generated method stub
+ }
+}
diff --git a/engine/src/android/com/jme3/renderer/android/TextureUtil.java b/engine/src/android/com/jme3/renderer/android/TextureUtil.java
new file mode 100644
index 000000000..08ca07051
--- /dev/null
+++ b/engine/src/android/com/jme3/renderer/android/TextureUtil.java
@@ -0,0 +1,246 @@
+package com.jme3.renderer.android;
+
+import android.graphics.Bitmap;
+import android.opengl.GLUtils;
+import com.jme3.math.FastMath;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import java.nio.ByteBuffer;
+import javax.microedition.khronos.opengles.GL10;
+
+public class TextureUtil {
+
+ public static int convertTextureFormat(Format fmt){
+ switch (fmt){
+ case Alpha16:
+ case Alpha8:
+ return GL10.GL_ALPHA;
+ case Luminance8Alpha8:
+ case Luminance16Alpha16:
+ return GL10.GL_LUMINANCE_ALPHA;
+ case Luminance8:
+ case Luminance16:
+ return GL10.GL_LUMINANCE;
+ case RGB10:
+ case RGB16:
+ case BGR8:
+ case RGB8:
+ case RGB565:
+ return GL10.GL_RGB;
+ case RGB5A1:
+ case RGBA16:
+ case RGBA8:
+ return GL10.GL_RGBA;
+ default:
+ throw new UnsupportedOperationException("Unrecognized format: "+fmt);
+ }
+ }
+
+ private static void buildMipmap(GL10 gl, Bitmap bitmap) {
+ int level = 0;
+ int height = bitmap.getHeight();
+ int width = bitmap.getWidth();
+
+ while (height >= 1 || width >= 1) {
+ //First of all, generate the texture from our bitmap and set it to the according level
+ GLUtils.texImage2D(GL10.GL_TEXTURE_2D, level, bitmap, 0);
+
+ if (height == 1 || width == 1) {
+ break;
+ }
+
+ //Increase the mipmap level
+ level++;
+
+ height /= 2;
+ width /= 2;
+ Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, width, height, true);
+
+ bitmap.recycle();
+ bitmap = bitmap2;
+ }
+ }
+
+ private static void uploadTextureBitmap(GL10 gl, Bitmap bitmap, boolean generateMips, boolean powerOf2){
+ if (!powerOf2){
+ int width = bitmap.getWidth();
+ int height = bitmap.getHeight();
+ if (!FastMath.isPowerOfTwo(width) || !FastMath.isPowerOfTwo(height)){
+ // scale to power of two
+ width = FastMath.nearestPowerOfTwo(width);
+ height = FastMath.nearestPowerOfTwo(height);
+ Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, width, height, true);
+ bitmap.recycle();
+ bitmap = bitmap2;
+ }
+ }
+
+ if (generateMips){
+ buildMipmap(gl, bitmap);
+ }else{
+ GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
+ bitmap.recycle();
+ }
+ }
+
+ public static void uploadTexture(
+ GL10 gl,
+ Image img,
+ int target,
+ int index,
+ int border,
+ boolean tdc,
+ boolean generateMips,
+ boolean powerOf2){
+
+ if (img.getEfficentData() instanceof Bitmap){
+ Bitmap bitmap = (Bitmap) img.getEfficentData();
+ uploadTextureBitmap(gl, bitmap, generateMips, powerOf2);
+// img.setEfficentData(null);
+ return;
+ }
+
+ Image.Format fmt = img.getFormat();
+ ByteBuffer data;
+ if (index >= 0 || img.getData() != null && img.getData().size() > 0){
+ data = img.getData(index);
+ }else{
+ data = null;
+ }
+
+ int width = img.getWidth();
+ int height = img.getHeight();
+// int depth = img.getDepth();
+
+ boolean compress = false;
+ int format = -1;
+ int dataType = -1;
+
+ switch (fmt){
+ case Alpha16:
+ format = gl.GL_ALPHA;
+ dataType = gl.GL_UNSIGNED_BYTE;
+ break;
+ case Alpha8:
+ format = gl.GL_ALPHA;
+ dataType = gl.GL_UNSIGNED_BYTE;
+ break;
+ case Luminance8:
+ format = gl.GL_LUMINANCE;
+ dataType = gl.GL_UNSIGNED_BYTE;
+ break;
+ case Luminance8Alpha8:
+ format = gl.GL_LUMINANCE_ALPHA;
+ dataType = gl.GL_UNSIGNED_BYTE;
+ break;
+ case Luminance16Alpha16:
+ format = gl.GL_LUMINANCE_ALPHA;
+ dataType = gl.GL_UNSIGNED_BYTE;
+ break;
+ case Luminance16:
+ format = gl.GL_LUMINANCE;
+ dataType = gl.GL_UNSIGNED_BYTE;
+ break;
+ case RGB565:
+ format = gl.GL_RGB;
+ dataType = gl.GL_UNSIGNED_SHORT_5_6_5;
+ break;
+ case ARGB4444:
+ format = gl.GL_RGBA;
+ dataType = gl.GL_UNSIGNED_SHORT_4_4_4_4;
+ break;
+ case RGB10:
+ format = gl.GL_RGB;
+ dataType = gl.GL_UNSIGNED_BYTE;
+ break;
+ case RGB16:
+ format = gl.GL_RGB;
+ dataType = gl.GL_UNSIGNED_BYTE;
+ break;
+ case RGB5A1:
+ format = gl.GL_RGBA;
+ dataType = gl.GL_UNSIGNED_SHORT_5_5_5_1;
+ break;
+ case RGB8:
+ format = gl.GL_RGB;
+ dataType = gl.GL_UNSIGNED_BYTE;
+ break;
+ case BGR8:
+ format = gl.GL_RGB;
+ dataType = gl.GL_UNSIGNED_BYTE;
+ break;
+ case RGBA16:
+ format = gl.GL_RGBA;
+ dataType = gl.GL_UNSIGNED_BYTE;
+ break;
+ case RGBA8:
+ format = gl.GL_RGBA;
+ dataType = gl.GL_UNSIGNED_BYTE;
+ break;
+ default:
+ throw new UnsupportedOperationException("Unrecognized format: "+fmt);
+ }
+
+ if (data != null)
+ gl.glPixelStorei(gl.GL_UNPACK_ALIGNMENT, 1);
+
+ int[] mipSizes = img.getMipMapSizes();
+ int pos = 0;
+ if (mipSizes == null){
+ if (data != null)
+ mipSizes = new int[]{ data.capacity() };
+ else
+ mipSizes = new int[]{ width * height * fmt.getBitsPerPixel() / 8 };
+ }
+
+ // XXX: might want to change that when support
+ // of more than paletted compressions is added..
+ if (compress){
+ data.clear();
+ gl.glCompressedTexImage2D(gl.GL_TEXTURE_2D,
+ 1 - mipSizes.length,
+ format,
+ width,
+ height,
+ 0,
+ data.capacity(),
+ data);
+ return;
+ }
+
+ for (int i = 0; i < mipSizes.length; i++){
+ int mipWidth = Math.max(1, width >> i);
+ int mipHeight = Math.max(1, height >> i);
+// int mipDepth = Math.max(1, depth >> i);
+
+ if (data != null){
+ data.position(pos);
+ data.limit(pos + mipSizes[i]);
+ }
+
+ if (compress && data != null){
+ gl.glCompressedTexImage2D(gl.GL_TEXTURE_2D,
+ i,
+ format,
+ mipWidth,
+ mipHeight,
+ 0,
+ data.remaining(),
+ data);
+ }else{
+ gl.glTexImage2D(gl.GL_TEXTURE_2D,
+ i,
+ format,
+ mipWidth,
+ mipHeight,
+ 0,
+ format,
+ dataType,
+ data);
+ }
+
+ pos += mipSizes[i];
+ }
+ }
+
+}
diff --git a/engine/src/android/com/jme3/system/JmeSystem.java b/engine/src/android/com/jme3/system/JmeSystem.java
new file mode 100644
index 000000000..f7e07d6cb
--- /dev/null
+++ b/engine/src/android/com/jme3/system/JmeSystem.java
@@ -0,0 +1,101 @@
+package com.jme3.system;
+
+import android.content.res.Resources;
+import com.jme3.util.AndroidLogHandler;
+import com.jme3.asset.AndroidAssetManager;
+import com.jme3.asset.AssetManager;
+import com.jme3.audio.AudioNode;
+import com.jme3.audio.AudioData;
+import com.jme3.audio.AudioRenderer;
+import com.jme3.audio.AudioParam;
+import com.jme3.audio.Environment;
+import com.jme3.audio.Listener;
+import com.jme3.audio.ListenerParam;
+//import com.jme3.audio.DummyAudioRenderer;
+import com.jme3.system.JmeContext.Type;
+import com.jme3.system.android.OGLESContext;
+import com.jme3.util.JmeFormatter;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import java.net.URL;
+
+
+
+public class JmeSystem {
+
+ private static final Logger logger = Logger.getLogger(JmeSystem.class.getName());
+
+ private static boolean initialized = false;
+ private static Resources res;
+
+ public static void initialize(AppSettings settings){
+ if (initialized)
+ return;
+
+ initialized = true;
+ try {
+ JmeFormatter formatter = new JmeFormatter();
+
+ Handler consoleHandler = new AndroidLogHandler();
+ consoleHandler.setFormatter(formatter);
+// Logger.getLogger("").removeHandler(Logger.getLogger("").getHandlers()[0]);
+// Logger.getLogger("").addHandler(consoleHandler);
+
+// Logger.getLogger("com.g3d").setLevel(Level.FINEST);
+ } catch (SecurityException ex){
+ logger.log(Level.SEVERE, "Security error in creating log file", ex);
+ }
+ logger.info("Running on "+getFullName());
+ }
+
+ public static String getFullName(){
+ return "jMonkey Engine 3 ALPHA 0.50";
+ }
+
+ public static JmeContext newContext(AppSettings settings, Type contextType) {
+ initialize(settings);
+ return new OGLESContext();
+ }
+
+ public static AudioRenderer newAudioRenderer(AppSettings settings) {
+ return new AudioRenderer() {
+ public void setListener(Listener listener) {}
+ public void setEnvironment(Environment env) {}
+ public void playSourceInstance(AudioNode src) {}
+ public void playSource(AudioNode src) {}
+ public void pauseSource(AudioNode src) {}
+ public void stopSource(AudioNode src) {}
+ public void deleteAudioData(AudioData ad) {}
+ public void initialize() {}
+ public void update(float tpf) {}
+ public void cleanup() {}
+ public void updateListenerParam(Listener listener, ListenerParam parameter) {}
+ public void updateSourceParam(AudioNode node, AudioParam parameter) {}
+ };
+ }
+
+ public static void setResources(Resources res){
+ JmeSystem.res = res;
+ }
+
+ public static Resources getResources(){
+ return res;
+ }
+
+ public static AssetManager newAssetManager(){
+ logger.info("newAssetManager()");
+ return new AndroidAssetManager(true);
+ }
+
+ public static AssetManager newAssetManager(URL url){
+ logger.info("newAssetManager(" + url + ")");
+ return new AndroidAssetManager(true);
+ }
+
+ public static boolean showSettingsDialog(AppSettings settings) {
+ return true;
+ }
+
+}
diff --git a/engine/src/android/com/jme3/system/android/AndroidTimer.java b/engine/src/android/com/jme3/system/android/AndroidTimer.java
new file mode 100644
index 000000000..ef0b76898
--- /dev/null
+++ b/engine/src/android/com/jme3/system/android/AndroidTimer.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2003-2009 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.system.android;
+
+import com.jme3.system.Timer;
+
+/**
+ * NanoTimer
is a System.nanoTime implementation of Timer
.
+ * This is primarily useful for headless applications running on a server.
+ *
+ * @author Matthew D. Hicks
+ */
+public class AndroidTimer extends Timer {
+
+ private static final long TIMER_RESOLUTION = 1000L;
+ private static final float INVERSE_TIMER_RESOLUTION = 1f/1000L;
+
+ private long startTime;
+ private long previousTime;
+ private float tpf;
+ private float fps;
+
+ public AndroidTimer() {
+ startTime = System.currentTimeMillis();
+ }
+
+ /**
+ * Returns the time in seconds. The timer starts
+ * at 0.0 seconds.
+ *
+ * @return the current time in seconds
+ */
+ @Override
+ public float getTimeInSeconds() {
+ return getTime() * INVERSE_TIMER_RESOLUTION;
+ }
+
+ public long getTime() {
+ return System.currentTimeMillis() - startTime;
+ }
+
+ public long getResolution() {
+ return TIMER_RESOLUTION;
+ }
+
+ public float getFrameRate() {
+ return fps;
+ }
+
+ public float getTimePerFrame() {
+ return tpf;
+ }
+
+ public void update() {
+ tpf = (getTime() - previousTime) * (1.0f / TIMER_RESOLUTION);
+ fps = 1.0f / tpf;
+ previousTime = getTime();
+ }
+
+ public void reset() {
+ startTime = System.currentTimeMillis();
+ previousTime = getTime();
+ }
+}
diff --git a/engine/src/android/com/jme3/system/android/OGLESContext.java b/engine/src/android/com/jme3/system/android/OGLESContext.java
new file mode 100644
index 000000000..be01b1a85
--- /dev/null
+++ b/engine/src/android/com/jme3/system/android/OGLESContext.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright (c) 2003-2009 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.system.android;
+
+import android.app.Activity;
+import android.opengl.GLSurfaceView;
+import android.view.SurfaceHolder;
+import com.jme3.input.JoyInput;
+import com.jme3.input.KeyInput;
+import com.jme3.input.MouseInput;
+import com.jme3.input.android.AndroidInput;
+//import com.jme3.renderer.android.OGLESRenderer;
+import com.jme3.renderer.android.OGLESShaderRenderer;
+import com.jme3.system.AppSettings;
+import com.jme3.system.JmeContext;
+import com.jme3.system.SystemListener;
+import com.jme3.system.Timer;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Logger;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+
+public class OGLESContext implements JmeContext, GLSurfaceView.Renderer {
+
+ private static final Logger logger = Logger.getLogger(OGLESContext.class.getName());
+
+ protected AtomicBoolean created = new AtomicBoolean(false);
+ protected AppSettings settings = new AppSettings(true);
+
+ /* < OpenGL ES 2.0 * */
+ //protected OGLESRenderer renderer;
+ /* >= OpenGL ES 2.0 (Android 2.2+) */
+ protected OGLESShaderRenderer renderer;
+
+ protected Timer timer;
+ protected SystemListener listener;
+
+ protected AtomicBoolean needClose = new AtomicBoolean(false);
+ protected boolean wasActive = false;
+ protected int frameRate = 0;
+ protected boolean autoFlush = true;
+
+ protected AndroidInput view;
+
+ public OGLESContext(){
+ }
+
+ public Type getType() {
+ return Type.Display;
+ }
+
+ public GLSurfaceView createView(Activity activity){
+ view = new AndroidInput(activity);
+
+ /*
+ * Requesting client version from GLSurfaceView which is extended by
+ * AndroidInput.
+ * This is required to get OpenGL ES 2.0
+ */
+
+ logger.info("setEGLContextClientVersion(2)");
+ view.setEGLContextClientVersion(2);
+ logger.info("setEGLContextClientVersion(2) ... done.");
+
+ //RGB565, Depth16
+ view.setEGLConfigChooser(5, 6, 5, 0, 16, 0);
+ view.setFocusableInTouchMode(true);
+ view.setFocusable(true);
+ view.getHolder().setType(SurfaceHolder.SURFACE_TYPE_GPU);
+// view.setDebugFlags(GLSurfaceView.DEBUG_CHECK_GL_ERROR);
+// | GLSurfaceView.DEBUG_LOG_GL_CALLS);
+ view.setRenderer(this);
+ return view;
+
+ }
+
+ protected void applySettings(AppSettings setting){
+ }
+
+ protected void initInThread(GL10 gl){
+ logger.info("Display created.");
+ logger.fine("Running on thread: "+Thread.currentThread().getName());
+
+ Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
+ public void uncaughtException(Thread thread, Throwable thrown) {
+ listener.handleError("Uncaught exception thrown in "+thread.toString(), thrown);
+ }
+ });
+
+ created.set(true);
+
+ timer = new AndroidTimer();
+
+ renderer = new OGLESShaderRenderer(gl);
+ applySettingsToRenderer(renderer, settings);
+
+ renderer.initialize();
+ listener.initialize();
+
+ // OGLESShaderRenderer does not support guiView yet
+ // forcefully remove all gui nodes
+
+ if (listener instanceof com.jme3.app.SimpleApplication) {
+ ((com.jme3.app.SimpleApplication) listener).getGuiNode().detachAllChildren();
+ }
+ }
+
+ /**
+ * De-initialize in the OpenGL thread.
+ */
+ protected void deinitInThread(){
+ listener.destroy();
+ if (renderer != null) {
+ renderer.cleanup();
+ // do android specific cleaning here
+
+ logger.info("Display destroyed.");
+ created.set(false);
+ renderer = null;
+ timer = null;
+ }
+ }
+
+
+ protected void applySettingsToRenderer(OGLESShaderRenderer renderer, AppSettings settings) {
+ logger.warning("setSettings.USE_VA: [" + settings.getBoolean("USE_VA") + "]");
+ logger.warning("setSettings.VERBOSE_LOGGING: [" + settings.getBoolean("VERBOSE_LOGGING") + "]");
+ renderer.setUseVA(settings.getBoolean("USE_VA"));
+ renderer.setVerboseLogging(settings.getBoolean("VERBOSE_LOGGING"));
+ }
+
+ @Override
+ public void setSettings(AppSettings settings) {
+ this.settings.copyFrom(settings);
+
+ // XXX This code should be somewhere else
+ if (renderer != null)
+ applySettingsToRenderer(renderer, this.settings);
+ }
+
+ public void setSystemListener(SystemListener listener){
+ this.listener = listener;
+ }
+
+ public AppSettings getSettings() {
+ return settings;
+ }
+
+ public com.jme3.renderer.Renderer getRenderer() {
+ return renderer;
+ }
+
+ public MouseInput getMouseInput() {
+ return view;
+ }
+
+ public KeyInput getKeyInput() {
+ return view;
+ }
+
+ public JoyInput getJoyInput() {
+ return null;
+ }
+
+ public Timer getTimer() {
+ return timer;
+ }
+
+ public void setTitle(String title) {
+ }
+
+ public boolean isCreated(){
+ return created.get();
+ }
+
+ public void setAutoFlushFrames(boolean enabled){
+ this.autoFlush = enabled;
+ }
+
+ // renderer:initialize
+ public void onSurfaceCreated(GL10 gl, EGLConfig cfg) {
+ logger.info("Using Android");
+ initInThread(gl);
+ }
+
+ // SystemListener:reshape
+ public void onSurfaceChanged(GL10 gl, int width, int height) {
+ settings.setResolution(width, height);
+ listener.reshape(width, height);
+ }
+
+ // SystemListener:update
+ public void onDrawFrame(GL10 gl) {
+ if (needClose.get()){
+ deinitInThread(); // ???
+ return;
+ }
+
+// if (wasActive != Display.isActive()){
+// if (!wasActive){
+// listener.gainFocus();
+// wasActive = true;
+// }else{
+// listener.loseFocus();
+// wasActive = false;
+// }
+// }
+
+ if (!created.get())
+ throw new IllegalStateException();
+
+ listener.update();
+
+ // swap buffers
+
+ if (frameRate > 0){
+// Display.sync(frameRate);
+ // synchronzie to framerate
+ }
+
+ if (autoFlush)
+ renderer.onFrame();
+ }
+
+ /**
+ * TODO: get these methods to follow the spec
+ * @param waitFor
+ */
+ public void create(boolean waitFor) {
+ if (created.get()){
+ logger.warning("create() called when display is already created!");
+ return;
+ }
+ }
+
+ public void create(){
+ create(false);
+ }
+
+ public void restart() {
+ }
+
+ /**
+ * TODO: get these methods to follow the spec
+ * @param waitFor
+ */
+ public void destroy(boolean waitFor) {
+ needClose.set(true);
+ }
+
+ public void destroy(){
+ destroy(false);
+ }
+
+}
diff --git a/engine/src/android/com/jme3/texture/plugins/AndroidImageLoader.java b/engine/src/android/com/jme3/texture/plugins/AndroidImageLoader.java
new file mode 100644
index 000000000..e28ad5d41
--- /dev/null
+++ b/engine/src/android/com/jme3/texture/plugins/AndroidImageLoader.java
@@ -0,0 +1,95 @@
+package com.jme3.texture.plugins;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetLoader;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.util.BufferUtils;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+
+public class AndroidImageLoader implements AssetLoader {
+
+ public Object load2(AssetInfo info) throws IOException {
+ ByteBuffer bb = BufferUtils.createByteBuffer(1 * 1 * 2);
+ bb.put( (byte) 0xff ).put( (byte) 0xff );
+ bb.clear();
+ return new Image(Format.RGB5A1, 1, 1, bb);
+ }
+
+ public Object load(AssetInfo info) throws IOException {
+ InputStream in = null;
+ Bitmap bitmap = null;
+ try {
+ in = info.openStream();
+ bitmap = BitmapFactory.decodeStream(in);
+ if (bitmap == null){
+ throw new IOException("Failed to load image: "+info.getKey().getName());
+ }
+ } finally {
+ if (in != null)
+ in.close();
+ }
+
+ int width = bitmap.getWidth();
+ int height = bitmap.getHeight();
+ int bytesPerPixel = -1;
+ Format fmt;
+
+ switch (bitmap.getConfig()){
+ case ALPHA_8:
+ bytesPerPixel = 1;
+ fmt = Format.Alpha8;
+ break;
+ case ARGB_4444:
+ bytesPerPixel = 2;
+ fmt = Format.ARGB4444;
+ break;
+ case ARGB_8888:
+ bytesPerPixel = 4;
+ fmt = Format.RGBA8;
+ break;
+ case RGB_565:
+ bytesPerPixel = 2;
+ fmt = Format.RGB565;
+ break;
+ default:
+ return null;
+ }
+
+// if (width > 128 || height > 128){
+// if (width > height){
+// float aspect = (float) height / width;
+// width = 128;
+// height = (int) (128 * aspect);
+//
+// }else{
+// float aspect = (float) width / height;
+// width = (int) (128 * aspect);
+// height = 128;
+// }
+// bitmap = Bitmap.createScaledBitmap(bitmap, width, height, true);
+// }
+
+// if ( ((TextureKey)info.getKey()).isFlipY() ){
+// Bitmap newBitmap = null;
+// Matrix flipMat = new Matrix();
+// flipMat.preScale(1.0f, -1.0f);
+// newBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), flipMat, false);
+// bitmap.recycle();
+// bitmap = newBitmap;
+//
+// if (bitmap == null){
+// throw new IOException("Failed to load image2: "+info.getKey().getName());
+// }
+// }
+
+ Image image = new Image(fmt, width, height, null);
+ image.setEfficentData(bitmap);
+ return image;
+ }
+
+}
diff --git a/engine/src/android/com/jme3/util/AndroidLogHandler.java b/engine/src/android/com/jme3/util/AndroidLogHandler.java
new file mode 100644
index 000000000..8fb21c22c
--- /dev/null
+++ b/engine/src/android/com/jme3/util/AndroidLogHandler.java
@@ -0,0 +1,37 @@
+package com.jme3.util;
+
+import android.util.Log;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+
+public class AndroidLogHandler extends Handler {
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public void flush() {
+ }
+
+ @Override
+ public void publish(LogRecord record) {
+ Level level = record.getLevel();
+ String clsName = record.getSourceClassName();
+ String msg = record.getMessage();
+ Throwable t = record.getThrown();
+ if (level == Level.INFO){
+ Log.i(clsName, msg, t);
+ }else if (level == Level.SEVERE){
+ Log.e(clsName, msg, t);
+ }else if (level == Level.WARNING){
+ Log.w(clsName, msg, t);
+ }else if (level == Level.CONFIG){
+ Log.d(clsName, msg, t);
+ }else if (level == Level.FINE || level == Level.FINER || level == Level.FINEST){
+ Log.v(clsName, msg, t);
+ }
+ }
+
+}
diff --git a/engine/src/android/jme3test/android/AboutActivity.java b/engine/src/android/jme3test/android/AboutActivity.java
new file mode 100644
index 000000000..44797d1f2
--- /dev/null
+++ b/engine/src/android/jme3test/android/AboutActivity.java
@@ -0,0 +1,72 @@
+package jme3test.android;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import android.content.Intent;
+
+import android.view.View;
+import android.view.MenuInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+
+import android.widget.TextView;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.CheckBox;
+import android.widget.TableLayout;
+import android.widget.LinearLayout;
+import android.widget.TableRow;
+
+import android.hardware.SensorManager;
+//import android.hardware.SensorListener;
+
+import jme3test.android.AndroidActivity;
+
+import java.net.URI;
+
+
+public class AboutActivity extends Activity {
+
+ private static final java.util.logging.Logger logger = java.util.logging.Logger.getLogger(AboutActivity.class.getName());
+
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ logger.info("onCreate(" + savedInstanceState + ")");
+
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.about);
+ }
+
+ @Override
+ public void onDestroy() {
+ logger.info("onDestroy()");
+ super.onDestroy();
+ }
+
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ }
+
+
+}
+
diff --git a/engine/src/android/jme3test/android/AndroidActivity.java b/engine/src/android/jme3test/android/AndroidActivity.java
new file mode 100644
index 000000000..350d3c202
--- /dev/null
+++ b/engine/src/android/jme3test/android/AndroidActivity.java
@@ -0,0 +1,158 @@
+
+/*
+ *
+ * Android Activity for OpenGL ES2 based tests
+ * requires Android 2.2+
+ *
+ * created: Mon Nov 8 00:08:07 EST 2010
+ */
+
+package jme3test.android;
+
+import android.app.Activity;
+import android.content.res.Resources;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+import android.view.Window;
+import android.view.WindowManager;
+import com.jme3.R;
+import com.jme3.system.AppSettings;
+import com.jme3.system.JmeSystem;
+import com.jme3.system.android.OGLESContext;
+
+import com.jme3.app.Application;
+import com.jme3.app.SimpleApplication;
+
+
+public class AndroidActivity extends Activity {
+
+ private final static java.util.logging.Logger logger = java.util.logging.Logger.getLogger(AndroidActivity.class.getName());
+
+
+ private OGLESContext ctx;
+ private GLSurfaceView view;
+
+ private boolean useVA = false;
+ private boolean verboseLogging = false;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+
+ super.onCreate(savedInstanceState);
+
+ JmeSystem.setResources(getResources());
+
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
+ WindowManager.LayoutParams.FLAG_FULLSCREEN);
+
+ AppSettings settings = new AppSettings(true);
+
+ String testClassName = getIntent().getStringExtra(AndroidActivity.class.getName() + ".TEST_CLASS_NAME");
+
+ logger.info("test class name: [" + testClassName + "]");
+
+ String appClass = (testClassName != null? testClassName: "jme3test.android.SimpleTexturedTest");
+
+ useVA = getIntent().getBooleanExtra(AndroidActivity.class.getName() + ".USE_VA", false);
+
+ logger.info("USE_VA -> [" + useVA + "]");
+
+ settings.putBoolean("USE_VA", useVA);
+
+ verboseLogging = getIntent().getBooleanExtra(AndroidActivity.class.getName() + ".VERBOSE_LOGGING", false);
+
+ settings.putBoolean("VERBOSE_LOGGING", verboseLogging);
+
+ Application app = null;
+
+ try {
+ Class extends Application> clazz = (Class extends Application>) Class.forName(
+ appClass
+ );
+
+ app = clazz.newInstance();
+/*
+ app = (Application) java.lang.reflect.Proxy.newProxyInstance(
+ this.getClass().getClassLoader(),
+ new Class[] {Class.forName(appClass)},
+
+ new java.lang.reflect.InvocationHandler() {
+ public Object invoke(Object proxy, java.lang.reflect.Method method, Object[] args) throws Throwable {
+ if (
+ method.getName().equals("loadFPSText") ||
+ method.getName().equals("loadStatsView")
+ ) {
+ logger.info("ignoring method: [" + method + "]");
+ return null;
+ }
+
+ return method.invoke(proxy, args);
+ }
+ }
+ );
+*/
+
+
+ if (app instanceof SimpleApplication) {
+ ((SimpleApplication) app).setShowSettings(false);
+ }
+
+ logger.info("setting settings ...");
+ app.setSettings(settings);
+ logger.info("setting settings ... done.");
+
+ logger.info("starting app ...");
+ app.start();
+ logger.info("starting app ... done.");
+
+ if (app instanceof SimpleApplication)
+ ((SimpleApplication) app).getGuiNode().detachAllChildren();
+
+ logger.info("creating context ...");
+ ctx = (OGLESContext) app.getContext();
+ logger.info("creating context ... done.");
+
+ ctx.setSettings(settings);
+
+ logger.info("creating view ...");
+ view = ctx.createView(this);
+ logger.info("creating view ... done.");
+
+ logger.info("setting content view ...");
+ setContentView(view);
+ logger.info("setting content done ...");
+
+ } catch (Throwable exception) {
+ logger.warning("exception: " + exception);
+ exception.printStackTrace(System.err);
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ logger.info("onResume ...");
+ super.onResume();
+ logger.info("view.onResume ...");
+
+ view.onResume();
+
+ logger.info("view.onResume ... done.");
+ logger.info("onResume ... done.");
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ view.onPause();
+ }
+
+// @Override
+// protected void onDestroy(){
+// super.onDestroy();
+
+// Debug.stopMethodTracing();
+// }
+
+}
+
diff --git a/engine/src/android/jme3test/android/SimpleTexturedTest.java b/engine/src/android/jme3test/android/SimpleTexturedTest.java
new file mode 100644
index 000000000..635d8e188
--- /dev/null
+++ b/engine/src/android/jme3test/android/SimpleTexturedTest.java
@@ -0,0 +1,135 @@
+
+/*
+ * Android 2.2+ SimpleTextured test.
+ *
+ * created: Mon Nov 8 00:08:22 EST 2010
+ */
+
+package jme3test.android;
+
+
+import java.util.List;
+import java.util.ArrayList;
+
+import com.jme3.app.SimpleApplication;
+
+import com.jme3.asset.TextureKey;
+
+import com.jme3.material.Material;
+
+import com.jme3.math.Transform;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Node;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.scene.shape.Box;
+
+import com.jme3.texture.Texture;
+
+import com.jme3.light.PointLight;
+
+import com.jme3.util.TangentBinormalGenerator;
+
+import jme3tools.converters.model.ModelConverter;
+
+
+public class SimpleTexturedTest extends SimpleApplication {
+
+ private static final java.util.logging.Logger logger = java.util.logging.Logger.getLogger(SimpleTexturedTest.class.getName());
+
+
+ private Node spheresContainer = new Node("spheres-container");
+
+
+ private boolean lightingEnabled = true;
+ private boolean texturedEnabled = true;
+ private boolean spheres = true;
+
+ @Override
+ public void simpleInitApp() {
+
+ /*
+ * GUI rendering is broken on Android right now and prevents the main view from rendering.
+ * Detaching all children lets the main view to be rendered.
+ */
+
+ guiNode.detachAllChildren();
+
+ Mesh shape = null;
+
+ if (spheres) {
+ shape = new Sphere(16, 16, .5f);
+ } else {
+ shape = new Box(Vector3f.ZERO, 0.3f, 0.3f, 0.3f);
+ }
+
+ // ModelConverter.optimize(geom);
+
+ Texture texture = assetManager.loadTexture(new TextureKey("icons/textured.png"));
+
+ Material material = null;
+
+ if (texturedEnabled) {
+ if (lightingEnabled) {
+ material = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
+ material.setBoolean("VertexLighting", true);
+ material.setFloat("Shininess", 127);
+ material.setBoolean("LowQuality", true);
+ material.setTexture("DiffuseMap", texture);
+ } else {
+ material = new Material(assetManager, "Common/MatDefs/Misc/SimpleTextured.j3md");
+ material.setTexture("ColorMap", texture);
+ }
+ } else {
+ material = new Material(assetManager, "Common/MatDefs/Misc/SolidColor.j3md");
+ material.setColor("Color", ColorRGBA.Red);
+ }
+
+ TangentBinormalGenerator.generate(shape);
+
+ for (int y = -1; y < 2; y++) {
+ for (int x = -1; x < 2; x++){
+ // int x = 0;
+ // int y = 0;
+ Geometry geomClone = new Geometry("geometry-" + y + "-" + x, shape);
+ geomClone.setMaterial(material);
+ geomClone.setLocalTranslation(x, y, 0);
+
+// Transform t = geom.getLocalTransform().clone();
+// Transform t2 = geomClone.getLocalTransform().clone();
+// t.combineWithParent(t2);
+// geomClone.setLocalTransform(t);
+
+ spheresContainer.attachChild(geomClone);
+ }
+ }
+
+ spheresContainer.setLocalTranslation(new Vector3f(0, 0, -4f));
+ spheresContainer.setLocalScale(2.0f);
+
+ rootNode.attachChild(spheresContainer);
+
+ PointLight pointLight = new PointLight();
+
+ pointLight.setColor(new ColorRGBA(0.7f, 0.7f, 1.0f, 1.0f));
+
+ pointLight.setPosition(new Vector3f(0f, 0f, 0f));
+ pointLight.setRadius(8);
+
+ rootNode.addLight(pointLight);
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+
+ if (secondCounter == 0)
+ logger.info("Frames per second: " + timer.getFrameRate());
+
+ spheresContainer.rotate(0.2f * tpf, 0.4f * tpf, 0.8f * tpf);
+ }
+
+}
+
diff --git a/engine/src/android/jme3test/android/Test.java b/engine/src/android/jme3test/android/Test.java
new file mode 100644
index 000000000..f2c529682
--- /dev/null
+++ b/engine/src/android/jme3test/android/Test.java
@@ -0,0 +1,41 @@
+package jme3test.android;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.asset.TextureKey;
+import com.jme3.material.Material;
+import com.jme3.math.Transform;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.texture.Texture;
+import jme3tools.converters.model.ModelConverter;
+
+public class Test extends SimpleApplication {
+
+ @Override
+ public void simpleInitApp() {
+ Sphere s = new Sphere(8, 8, .5f);
+ Geometry geom = new Geometry("sphere", s);
+ // ModelConverter.optimize(geom);
+
+ Material mat = new Material(assetManager, "plain_texture.j3md");
+ Texture tex = assetManager.loadTexture(new TextureKey("monkey.j3i"));
+ mat.setTexture("ColorMap", tex);
+// geom.setMaterial(mat);
+
+ for (int y = -1; y < 2; y++){
+ for (int x = -1; x < 2; x++){
+ Geometry geomClone = new Geometry("geom", s);
+ geomClone.setMaterial(mat);
+ geomClone.setLocalTranslation(x, y, 0);
+
+ Transform t = geom.getLocalTransform().clone();
+ Transform t2 = geomClone.getLocalTransform().clone();
+ t.combineWithParent(t2);
+ geomClone.setLocalTransform(t);
+
+ rootNode.attachChild(geomClone);
+ }
+ }
+ }
+
+}
diff --git a/engine/src/android/jme3test/android/TestSceneLoading.java b/engine/src/android/jme3test/android/TestSceneLoading.java
new file mode 100644
index 000000000..f97aaf091
--- /dev/null
+++ b/engine/src/android/jme3test/android/TestSceneLoading.java
@@ -0,0 +1,37 @@
+package jme3test.android;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.Spatial.CullHint;
+
+
+public class TestSceneLoading extends SimpleApplication {
+
+ private void setState(Spatial s){
+ s.setCullHint(CullHint.Never);
+ if (s instanceof Node){
+ Node n = (Node) s;
+ for (int i = 0; i < n.getQuantity(); i++){
+ Spatial s2 = n.getChild(i);
+ setState(s2);
+ }
+ }
+ }
+
+ public void simpleInitApp() {
+ /* XXX: does not compile */
+
+/* Spatial scene = inputManager.loadModel("FINAL_LEVEL2.j3o");
+// setState(scene);
+ rootNode.attachChild(scene);
+
+ cam.setLocation(new Vector3f(-18.059685f, 34.64228f, 4.5048084f));
+ cam.setRotation(new Quaternion(0.22396432f, 0.5235024f, -0.1448922f, 0.8091919f));
+ cam.update();
+*/
+ }
+
+}
diff --git a/engine/src/android/jme3test/android/TestsActivity.java b/engine/src/android/jme3test/android/TestsActivity.java
new file mode 100644
index 000000000..dcf34a43e
--- /dev/null
+++ b/engine/src/android/jme3test/android/TestsActivity.java
@@ -0,0 +1,178 @@
+package jme3test.android;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import android.content.Intent;
+
+import android.view.View;
+import android.view.MenuInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+
+import android.widget.TextView;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.CheckBox;
+import android.widget.TableLayout;
+import android.widget.LinearLayout;
+import android.widget.TableRow;
+
+import android.hardware.SensorManager;
+//import android.hardware.SensorListener;
+
+import jme3test.android.AndroidActivity;
+
+import java.net.URI;
+
+
+public class TestsActivity extends Activity {
+
+ private static final java.util.logging.Logger logger = java.util.logging.Logger.getLogger(TestsActivity.class.getName());
+
+
+ public static class Test {
+
+ private String name = null;
+ private String className = null;
+
+ public Test(String name, String className) {
+ this.name = name;
+ this.className = className;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getClassName() {
+ return className;
+ }
+ }
+
+ private final static Test[] tests = {
+ new Test("SimpleTextured", "jme3test.android.SimpleTexturedTest"),
+ new Test("light.TestLightRadius", "jme3test.light.TestLightRadius"),
+ new Test("bullet.TestSimplePhysics", "jme3test.bullet.TestSimplePhysics"),
+ new Test("helloworld.HelloJME3", "jme3test.helloworld.HelloJME3"),
+ new Test("helloworld.HelloLoop", "jme3test.helloworld.HelloLoop"),
+ new Test("helloworld.HelloNode", "jme3test.helloworld.HelloNode"),
+ new Test("helloworld.HelloEffects", "jme3test.helloworld.HelloEffects"),
+ new Test("helloworld.HelloTerrain", "jme3test.helloworld.HelloTerrain")
+ };
+
+ private CheckBox useVA = null;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ logger.info("onCreate(" + savedInstanceState + ")");
+
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.tests);
+
+ try {
+
+ useVA = (CheckBox) findViewById(R.id.useVA);
+
+ LinearLayout buttonsContainer = (LinearLayout) findViewById(R.id.buttonsContainer);
+
+
+ for (Test test: tests) {
+ final Button button = new Button(this);
+ final String finalName = test.getName();
+ final String finalClassName = test.getClassName();
+
+ button.setText(test.getName());
+// button.setTextSize(10.0f);
+// button.setTextColor(Color.rgb(100, 200, 200));
+ buttonsContainer.addView(button);
+
+ button.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Intent intent = new Intent(view.getContext(), AndroidActivity.class);
+ intent.putExtra(AndroidActivity.class.getName() + ".TEST_CLASS_NAME", finalClassName);
+ intent.putExtra(AndroidActivity.class.getName() + ".USE_VA", useVA.isChecked());
+ startActivityForResult(intent, 0);
+ }
+ }
+ );
+ }
+ } catch (Exception exception) {
+ logger.warning("exception: " + exception);
+ exception.printStackTrace(System.err);
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ logger.info("onDestroy()");
+ super.onDestroy();
+ }
+
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.options, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.about_button:
+ about();
+ return true;
+ case R.id.quit_button:
+ quit();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private void quit() {
+ finish();
+ }
+
+ private void about() {
+ // Intent intent = new Intent(getView().getContext(), AboutActivity.class);
+ try {
+ Intent intent = new Intent();
+ intent.setClassName(
+ "jme3test.android",
+ "jme3test.android.AboutActivity"
+ );
+ startActivity(intent);
+ } catch (Exception exception) {
+ logger.warning("exception: " + exception);
+ exception.printStackTrace(System.err);
+ }
+ }
+
+
+}
+
diff --git a/engine/src/android/jme3tools/android/Fixed.java b/engine/src/android/jme3tools/android/Fixed.java
new file mode 100644
index 000000000..db7090400
--- /dev/null
+++ b/engine/src/android/jme3tools/android/Fixed.java
@@ -0,0 +1,437 @@
+package jme3tools.android;
+
+import java.util.Random;
+
+/**
+ * Fixed point maths class. This can be tailored for specific needs by
+ * changing the bits allocated to the 'fraction' part (see FIXED_POINT
+ *
, which would also require SIN_PRECALC
and
+ * COS_PRECALC
updating).
+ *
+ * http://blog.numfum.com/2007/09/java-fixed-point-maths.html
+ *
+ * @version 1.0
+ * @author CW
+ */
+public final class Fixed {
+ /**
+ * Number of bits used for 'fraction'.
+ */
+ public static final int FIXED_POINT = 16;
+
+ /**
+ * Decimal one as represented by the Fixed class.
+ */
+ public static final int ONE = 1 << FIXED_POINT;
+
+ /**
+ * Half in fixed point.
+ */
+ public static final int HALF = ONE >> 1;
+
+ /**
+ * Quarter circle resolution for trig functions (should be a power of
+ * two). This is the number of discrete steps in 90 degrees.
+ */
+ public static final int QUARTER_CIRCLE = 64;
+
+ /**
+ * Mask used to limit angles to one revolution. If a quarter circle is 64
+ * (i.e. 90 degrees is broken into 64 steps) then the mask is 255.
+ */
+ public static final int FULL_CIRCLE_MASK = QUARTER_CIRCLE * 4 - 1;
+
+ /**
+ * The trig table is generated at a higher precision than the typical
+ * 16.16 format used for the rest of the fixed point maths. The table
+ * values are then shifted to match the actual fixed point used.
+ */
+ private static final int TABLE_SHIFT = 30;
+
+ /**
+ * Equivalent to: sin((2 * PI) / (QUARTER_CIRCLE * 4))
+ *
+ * Note: if either QUARTER_CIRCLE or TABLE_SHIFT is changed this value
+ * will need recalculating (put the above formular into a calculator set
+ * radians, then shift the result by TABLE_SHIFT
).
+ */
+ private static final int SIN_PRECALC = 26350943;
+
+ /**
+ * Equivalent to: cos((2 * PI) / (QUARTER_CIRCLE * 4)) * 2
+ *
+ * Note: if either QUARTER_CIRCLE or TABLE_SHIFT is changed this value
+ * will need recalculating ((put the above formular into a calculator set
+ * radians, then shift the result by TABLE_SHIFT
).
+ */
+ private static final int COS_PRECALC = 2146836866;
+
+ /**
+ * One quarter sine wave as fixed point values.
+ */
+ private static final int[] SINE_TABLE = new int[QUARTER_CIRCLE + 1];
+
+ /**
+ * Scale value for indexing ATAN_TABLE[].
+ */
+ private static final int ATAN_SHIFT;
+
+ /**
+ * Reverse atan lookup table.
+ */
+ private static final byte[] ATAN_TABLE;
+
+ /**
+ * ATAN_TABLE.length
+ */
+ private static final int ATAN_TABLE_LEN;
+
+ /*
+ * Generates the tables and fills in any remaining static ints.
+ */
+ static {
+ // Generate the sine table using recursive synthesis.
+ SINE_TABLE[0] = 0;
+ SINE_TABLE[1] = SIN_PRECALC;
+ for (int n = 2; n < QUARTER_CIRCLE + 1; n++) {
+ SINE_TABLE[n] = (int) (((long) SINE_TABLE[n - 1] * COS_PRECALC) >> TABLE_SHIFT) - SINE_TABLE[n - 2];
+ }
+ // Scale the values to the fixed point format used.
+ for (int n = 0; n < QUARTER_CIRCLE + 1; n++) {
+ SINE_TABLE[n] = SINE_TABLE[n] + (1 << (TABLE_SHIFT - FIXED_POINT - 1)) >> TABLE_SHIFT - FIXED_POINT;
+ }
+
+ // Calculate a shift used to scale atan lookups
+ int rotl = 0;
+ int tan0 = tan(0);
+ int tan1 = tan(1);
+ while (rotl < 32) {
+ if ((tan1 >>= 1) > (tan0 >>= 1)) {
+ rotl++;
+ } else {
+ break;
+ }
+ }
+ ATAN_SHIFT = rotl;
+ // Create the a table of tan values
+ int[] lut = new int[QUARTER_CIRCLE];
+ for (int n = 0; n < QUARTER_CIRCLE; n++) {
+ lut[n] = tan(n) >> rotl;
+ }
+ ATAN_TABLE_LEN = lut[QUARTER_CIRCLE - 1];
+ // Then from the tan values create a reverse lookup
+ ATAN_TABLE = new byte[ATAN_TABLE_LEN];
+ for (byte n = 0; n < QUARTER_CIRCLE - 1; n++) {
+ int min = lut[n ];
+ int max = lut[n + 1];
+ for (int i = min; i < max; i++) {
+ ATAN_TABLE[i] = n;
+ }
+ }
+ }
+
+ /**
+ * How many decimal places to use when converting a fixed point value to
+ * a decimal string.
+ *
+ * @see #toString
+ */
+ private static final int STRING_DECIMAL_PLACES = 2;
+
+ /**
+ * Value to add in order to round down a fixed point number when
+ * converting to a string.
+ */
+ private static final int STRING_DECIMAL_PLACES_ROUND;
+ static {
+ int i = 10;
+ for (int n = 1; n < STRING_DECIMAL_PLACES; n++) {
+ i *= i;
+ }
+ if (STRING_DECIMAL_PLACES == 0) {
+ STRING_DECIMAL_PLACES_ROUND = ONE / 2;
+ } else {
+ STRING_DECIMAL_PLACES_ROUND = ONE / (2 * i);
+ }
+ }
+
+ /**
+ * Random number generator. The standard java.utll.Random
is
+ * used since it is available to both J2ME and J2SE. If a guaranteed
+ * sequence is required this would not be adequate.
+ */
+ private static Random rng = null;
+
+ /**
+ * Fixed can't be instantiated.
+ */
+ private Fixed() {}
+
+ /**
+ * Returns an integer as a fixed point value.
+ */
+ public static int intToFixed(int n) {
+ return n << FIXED_POINT;
+ }
+
+ /**
+ * Returns a fixed point value as a float.
+ */
+ public static float fixedToFloat(int i) {
+ float fp = i;
+ fp = fp / ((float)ONE);
+ return fp;
+ }
+
+ /**
+ * Returns a float as a fixed point value.
+ */
+ public static int floatToFixed(float fp){
+ return (int) (fp * ((float) ONE));
+ }
+
+ /**
+ * Converts a fixed point value into a decimal string.
+ */
+ public static String toString(int n) {
+ StringBuffer sb = new StringBuffer(16);
+ sb.append((n += STRING_DECIMAL_PLACES_ROUND) >> FIXED_POINT);
+ sb.append('.');
+ n &= ONE - 1;
+ for (int i = 0; i < STRING_DECIMAL_PLACES; i++) {
+ n *= 10;
+ sb.append((n / ONE) % 10);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Multiplies two fixed point values and returns the result.
+ */
+ public static int mul(int a, int b) {
+ return (int) ((long) a * (long) b >> FIXED_POINT);
+ }
+
+ /**
+ * Divides two fixed point values and returns the result.
+ */
+ public static int div(int a, int b) {
+ return (int) (((long) a << FIXED_POINT * 2) / (long) b >> FIXED_POINT);
+ }
+
+ /**
+ * Sine of an angle.
+ *
+ * @see #QUARTER_CIRCLE
+ */
+ public static int sin(int n) {
+ n &= FULL_CIRCLE_MASK;
+ if (n < QUARTER_CIRCLE * 2) {
+ if (n < QUARTER_CIRCLE) {
+ return SINE_TABLE[n];
+ } else {
+ return SINE_TABLE[QUARTER_CIRCLE * 2 - n];
+ }
+ } else {
+ if (n < QUARTER_CIRCLE * 3) {
+ return -SINE_TABLE[n - QUARTER_CIRCLE * 2];
+ } else {
+ return -SINE_TABLE[QUARTER_CIRCLE * 4 - n];
+ }
+ }
+ }
+
+ /**
+ * Cosine of an angle.
+ *
+ * @see #QUARTER_CIRCLE
+ */
+ public static int cos(int n) {
+ n &= FULL_CIRCLE_MASK;
+ if (n < QUARTER_CIRCLE * 2) {
+ if (n < QUARTER_CIRCLE) {
+ return SINE_TABLE[QUARTER_CIRCLE - n];
+ } else {
+ return -SINE_TABLE[n - QUARTER_CIRCLE];
+ }
+ } else {
+ if (n < QUARTER_CIRCLE * 3) {
+ return -SINE_TABLE[QUARTER_CIRCLE * 3 - n];
+ } else {
+ return SINE_TABLE[n - QUARTER_CIRCLE * 3];
+ }
+ }
+ }
+
+ /**
+ * Tangent of an angle.
+ *
+ * @see #QUARTER_CIRCLE
+ */
+ public static int tan(int n) {
+ return div(sin(n), cos(n));
+ }
+
+ /**
+ * Returns the arc tangent of an angle.
+ */
+ public static int atan(int n) {
+ n = n + (1 << (ATAN_SHIFT - 1)) >> ATAN_SHIFT;
+ if (n < 0) {
+ if (n <= -ATAN_TABLE_LEN) {
+ return -(QUARTER_CIRCLE - 1);
+ }
+ return -ATAN_TABLE[-n];
+ } else {
+ if (n >= ATAN_TABLE_LEN) {
+ return QUARTER_CIRCLE - 1;
+ }
+ return ATAN_TABLE[n];
+ }
+ }
+
+ /**
+ * Returns the polar angle of a rectangular coordinate.
+ */
+ public static int atan(int x, int y) {
+ int n = atan(div(x, abs(y) + 1)); // kludge to prevent ArithmeticException
+ if (y > 0) {
+ return n;
+ }
+ if (y < 0) {
+ if (x < 0) {
+ return -QUARTER_CIRCLE * 2 - n;
+ }
+ if (x > 0) {
+ return QUARTER_CIRCLE * 2 - n;
+ }
+ return QUARTER_CIRCLE * 2;
+ }
+ if (x > 0) {
+ return QUARTER_CIRCLE;
+ }
+ return -QUARTER_CIRCLE;
+ }
+
+ /**
+ * Rough calculation of the hypotenuse. Whilst not accurate it is very fast.
+ *
+ * Derived from a piece in Graphics Gems.
+ */
+ public static int hyp(int x1, int y1, int x2, int y2) {
+ if ((x2 -= x1) < 0) {
+ x2 = -x2;
+ }
+ if ((y2 -= y1) < 0) {
+ y2 = -y2;
+ }
+ return x2 + y2 - (((x2 > y2) ? y2 : x2) >> 1);
+ }
+
+ /**
+ * Fixed point square root.
+ *
+ * Derived from a 1993 Usenet algorithm posted by Christophe Meessen.
+ */
+ public static int sqrt(int n) {
+ if (n <= 0) {
+ return 0;
+ }
+ long sum = 0;
+ int bit = 0x40000000;
+ while (bit >= 0x100) { // lower values give more accurate results
+ long tmp = sum | bit;
+ if (n >= tmp) {
+ n -= tmp;
+ sum = tmp + bit;
+ }
+ bit >>= 1;
+ n <<= 1;
+ }
+ return (int) (sum >> 16 - (FIXED_POINT / 2));
+ }
+
+ /**
+ * Returns the absolute value.
+ */
+ public static int abs(int n) {
+ return (n < 0) ? -n : n;
+ }
+
+ /**
+ * Returns the sign of a value, -1 for negative numbers, otherwise 1.
+ */
+ public static int sgn(int n) {
+ return (n < 0) ? -1 : 1;
+ }
+
+ /**
+ * Returns the minimum of two values.
+ */
+ public static int min(int a, int b) {
+ return (a < b) ? a : b;
+ }
+
+ /**
+ * Returns the maximum of two values.
+ */
+ public static int max(int a, int b) {
+ return (a > b) ? a : b;
+ }
+
+ /**
+ * Clamps the value n between min and max.
+ */
+ public static int clamp(int n, int min, int max) {
+ return (n < min) ? min : (n > max) ? max : n;
+ }
+
+ /**
+ * Wraps the value n between 0 and the required limit.
+ */
+ public static int wrap(int n, int limit) {
+ return ((n %= limit) < 0) ? limit + n : n;
+ }
+
+ /**
+ * Returns the nearest int to a fixed point value. Equivalent to
+ * Math.round()
in the standard library.
+ */
+ public static int round(int n) {
+ return n + HALF >> FIXED_POINT;
+ }
+
+ /**
+ * Returns the nearest int rounded down from a fixed point value.
+ * Equivalent to Math.floor()
in the standard library.
+ */
+ public static int floor(int n) {
+ return n >> FIXED_POINT;
+ }
+
+ /**
+ * Returns the nearest int rounded up from a fixed point value.
+ * Equivalent to Math.ceil()
in the standard library.
+ */
+ public static int ceil(int n) {
+ return n + (ONE - 1) >> FIXED_POINT;
+ }
+
+ /**
+ * Returns a fixed point value greater than or equal to decimal 0.0 and
+ * less than 1.0 (in 16.16 format this would be 0 to 65535 inclusive).
+ */
+ public static int rand() {
+ if (rng == null) {
+ rng = new Random();
+ }
+ return rng.nextInt() >>> (32 - FIXED_POINT);
+ }
+
+ /**
+ * Returns a random number between 0 and n
(exclusive).
+ */
+ public static int rand(int n) {
+ return (rand() * n) >> FIXED_POINT;
+ }
+}
\ No newline at end of file
diff --git a/engine/src/android/res/layout/about.xml b/engine/src/android/res/layout/about.xml
new file mode 100644
index 000000000..d95af3722
--- /dev/null
+++ b/engine/src/android/res/layout/about.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/engine/src/android/res/layout/tests.xml b/engine/src/android/res/layout/tests.xml
new file mode 100644
index 000000000..afd405b87
--- /dev/null
+++ b/engine/src/android/res/layout/tests.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
diff --git a/engine/src/android/res/menu/options.xml b/engine/src/android/res/menu/options.xml
new file mode 100644
index 000000000..7f7d527fa
--- /dev/null
+++ b/engine/src/android/res/menu/options.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
diff --git a/engine/src/android/res/values/strings.xml b/engine/src/android/res/values/strings.xml
new file mode 100644
index 000000000..2705a3ef6
--- /dev/null
+++ b/engine/src/android/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+
+ JMEAndroidTest
+ About
+ Quit
+
diff --git a/engine/src/core-data/Common/MatDefs/Blur/HGaussianBlur.frag b/engine/src/core-data/Common/MatDefs/Blur/HGaussianBlur.frag
new file mode 100644
index 000000000..ee22b51dc
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Blur/HGaussianBlur.frag
@@ -0,0 +1,24 @@
+uniform sampler2D m_Texture; // this should hold the texture rendered by the horizontal blur pass
+uniform float m_Size;
+uniform float m_Scale;
+varying vec2 texCoord;
+
+
+void main(void)
+{ float blurSize = m_Scale/m_Size;
+ vec4 sum = vec4(0.0);
+
+ // blur in x (vertical)
+ // take nine samples, with the distance blurSize between them
+ sum += texture2D(m_Texture, vec2(texCoord.x- 4.0*blurSize, texCoord.y )) * 0.05;
+ sum += texture2D(m_Texture, vec2(texCoord.x- 3.0*blurSize, texCoord.y )) * 0.09;
+ sum += texture2D(m_Texture, vec2(texCoord.x - 2.0*blurSize, texCoord.y)) * 0.12;
+ sum += texture2D(m_Texture, vec2(texCoord.x- blurSize, texCoord.y )) * 0.15;
+ sum += texture2D(m_Texture, vec2(texCoord.x, texCoord.y)) * 0.16;
+ sum += texture2D(m_Texture, vec2(texCoord.x+ blurSize, texCoord.y )) * 0.15;
+ sum += texture2D(m_Texture, vec2(texCoord.x+ 2.0*blurSize, texCoord.y )) * 0.12;
+ sum += texture2D(m_Texture, vec2(texCoord.x+ 3.0*blurSize, texCoord.y )) * 0.09;
+ sum += texture2D(m_Texture, vec2(texCoord.x+ 4.0*blurSize, texCoord.y )) * 0.05;
+
+ gl_FragColor = sum;
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Blur/HGaussianBlur.j3md b/engine/src/core-data/Common/MatDefs/Blur/HGaussianBlur.j3md
new file mode 100644
index 000000000..ddb262e91
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Blur/HGaussianBlur.j3md
@@ -0,0 +1,21 @@
+MaterialDef Bloom {
+
+ MaterialParameters {
+ Int NumSamples
+ Texture2D Texture
+ Float Size
+ Float Scale
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Post/Post.vert
+ FragmentShader GLSL100: Common/MatDefs/Blur/HGaussianBlur.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ }
+
+ Technique FixedFunc {
+ }
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Blur/RadialBlur.frag b/engine/src/core-data/Common/MatDefs/Blur/RadialBlur.frag
new file mode 100644
index 000000000..b3adfd425
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Blur/RadialBlur.frag
@@ -0,0 +1,47 @@
+uniform sampler2D m_Texture;
+uniform float m_SampleDist;
+uniform float m_SampleStrength;
+uniform float m_Samples[10];
+varying vec2 texCoord;
+
+void main(void)
+{
+ // some sample positions
+ //float samples[10] = float[](-0.08,-0.05,-0.03,-0.02,-0.01,0.01,0.02,0.03,0.05,0.08);
+
+ // 0.5,0.5 is the center of the screen
+ // so substracting texCoord from it will result in
+ // a vector pointing to the middle of the screen
+ vec2 dir = 0.5 - texCoord;
+
+ // calculate the distance to the center of the screen
+ float dist = sqrt(dir.x*dir.x + dir.y*dir.y);
+
+ // normalize the direction (reuse the distance)
+ dir = dir/dist;
+
+ // this is the original colour of this fragment
+ // using only this would result in a nonblurred version
+ vec4 colorRes = texture2D(m_Texture,texCoord);
+
+ vec4 sum = colorRes;
+
+ // take 10 additional blur samples in the direction towards
+ // the center of the screen
+ for (int i = 0; i < 10; i++)
+ {
+ sum += texture2D( m_Texture, texCoord + dir * m_Samples[i] * m_SampleDist );
+ }
+
+ // we have taken eleven samples
+ sum *= 1.0/11.0;
+
+ // weighten the blur effect with the distance to the
+ // center of the screen ( further out is blurred more)
+ float t = dist * m_SampleStrength;
+ t = clamp( t ,0.0,1.0); //0 <= t <= 1
+
+ //Blend the original color with the averaged pixels
+ gl_FragColor =mix( colorRes, sum, t );
+
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Blur/RadialBlur.j3md b/engine/src/core-data/Common/MatDefs/Blur/RadialBlur.j3md
new file mode 100644
index 000000000..1e397fa5b
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Blur/RadialBlur.j3md
@@ -0,0 +1,36 @@
+MaterialDef Radial Blur {
+
+ MaterialParameters {
+ Int NumSamples
+ Texture2D Texture
+ Color Color
+ Float SampleDist
+ Float SampleStrength
+ FloatArray Samples
+ }
+
+ Technique {
+ VertexShader GLSL150: Common/MatDefs/Post/Post15.vert
+ FragmentShader GLSL150: Common/MatDefs/Blur/RadialBlur15.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ RESOLVE_MS : NumSamples
+ }
+ }
+
+ Technique {
+ VertexShader GLSL120: Common/MatDefs/Post/Post.vert
+ FragmentShader GLSL120: Common/MatDefs/Blur/RadialBlur.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ }
+
+ Technique FixedFunc {
+ }
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Blur/RadialBlur15.frag b/engine/src/core-data/Common/MatDefs/Blur/RadialBlur15.frag
new file mode 100644
index 000000000..8b1f1213e
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Blur/RadialBlur15.frag
@@ -0,0 +1,48 @@
+#import "Common/ShaderLib/MultiSample.glsllib"
+
+uniform COLORTEXTURE m_Texture;
+uniform float m_SampleDist;
+uniform float m_SampleStrength;
+uniform float m_Samples[10];
+in vec2 texCoord;
+
+void main(void)
+{
+ // some sample positions
+ //float samples[10] = float[](-0.08,-0.05,-0.03,-0.02,-0.01,0.01,0.02,0.03,0.05,0.08);
+
+ // 0.5,0.5 is the center of the screen
+ // so substracting texCoord from it will result in
+ // a vector pointing to the middle of the screen
+ vec2 dir = 0.5 - texCoord;
+
+ // calculate the distance to the center of the screen
+ float dist = sqrt(dir.x*dir.x + dir.y*dir.y);
+
+ // normalize the direction (reuse the distance)
+ dir = dir/dist;
+
+ // this is the original colour of this fragment
+ // using only this would result in a nonblurred version
+ vec4 colorRes = getColor(m_Texture,texCoord);
+
+ vec4 sum = colorRes;
+
+ // take 10 additional blur samples in the direction towards
+ // the center of the screen
+ for (int i = 0; i < 10; i++){
+ sum += getColor( m_Texture, texCoord + dir * m_Samples[i] * m_SampleDist );
+ }
+
+ // we have taken eleven samples
+ sum *= 1.0/11.0;
+
+ // weighten the blur effect with the distance to the
+ // center of the screen ( further out is blurred more)
+ float t = dist * m_SampleStrength;
+ t = clamp( t ,0.0,1.0); //0 <= t <= 1
+
+ //Blend the original color with the averaged pixels
+ gl_FragColor =mix( colorRes, sum, t );
+
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Blur/VGaussianBlur.frag b/engine/src/core-data/Common/MatDefs/Blur/VGaussianBlur.frag
new file mode 100644
index 000000000..3e20fe56d
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Blur/VGaussianBlur.frag
@@ -0,0 +1,25 @@
+uniform sampler2D m_Texture; // this should hold the texture rendered by the horizontal blur pass
+uniform float m_Size;
+uniform float m_Scale;
+varying vec2 texCoord;
+
+
+
+void main(void)
+{ float blurSize = m_Scale/m_Size;
+ vec4 sum = vec4(0.0);
+
+ // blur in y (vertical)
+ // take nine samples, with the distance blurSize between them
+ sum += texture2D(m_Texture, vec2(texCoord.x, texCoord.y - 4.0*blurSize)) * 0.05;
+ sum += texture2D(m_Texture, vec2(texCoord.x, texCoord.y - 3.0*blurSize)) * 0.09;
+ sum += texture2D(m_Texture, vec2(texCoord.x, texCoord.y - 2.0*blurSize)) * 0.12;
+ sum += texture2D(m_Texture, vec2(texCoord.x, texCoord.y - blurSize)) * 0.15;
+ sum += texture2D(m_Texture, vec2(texCoord.x, texCoord.y)) * 0.16;
+ sum += texture2D(m_Texture, vec2(texCoord.x, texCoord.y + blurSize)) * 0.15;
+ sum += texture2D(m_Texture, vec2(texCoord.x, texCoord.y + 2.0*blurSize)) * 0.12;
+ sum += texture2D(m_Texture, vec2(texCoord.x, texCoord.y + 3.0*blurSize)) * 0.09;
+ sum += texture2D(m_Texture, vec2(texCoord.x, texCoord.y + 4.0*blurSize)) * 0.05;
+
+ gl_FragColor = sum;
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Blur/VGaussianBlur.j3md b/engine/src/core-data/Common/MatDefs/Blur/VGaussianBlur.j3md
new file mode 100644
index 000000000..163f9f383
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Blur/VGaussianBlur.j3md
@@ -0,0 +1,21 @@
+MaterialDef Bloom {
+
+ MaterialParameters {
+ Int NumSamples
+ Texture2D Texture
+ Float Size
+ Float Scale
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Post/Post.vert
+ FragmentShader GLSL100: Common/MatDefs/Blur/VGaussianBlur.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ }
+
+ Technique FixedFunc {
+ }
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Gui/Gui.frag b/engine/src/core-data/Common/MatDefs/Gui/Gui.frag
new file mode 100644
index 000000000..caa666c1b
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Gui/Gui.frag
@@ -0,0 +1,16 @@
+#ifdef TEXTURE
+uniform sampler2D m_Texture;
+varying vec2 texCoord;
+#endif
+
+varying vec4 color;
+
+void main() {
+ #ifdef TEXTURE
+ vec4 texVal = texture2D(m_Texture, texCoord);
+ gl_FragColor = texVal * color;
+ #else
+ gl_FragColor = color;
+ #endif
+}
+
diff --git a/engine/src/core-data/Common/MatDefs/Gui/Gui.j3md b/engine/src/core-data/Common/MatDefs/Gui/Gui.j3md
new file mode 100644
index 000000000..a8dab82f4
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Gui/Gui.j3md
@@ -0,0 +1,26 @@
+MaterialDef Default GUI {
+
+ MaterialParameters {
+ Texture2D Texture
+ Color Color : Color
+ Boolean VertexColor
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Gui/Gui.vert
+ FragmentShader GLSL100: Common/MatDefs/Gui/Gui.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ TEXTURE : Texture
+ VERTEX_COLOR : VertexColor
+ }
+ }
+
+ Technique FixedFunc {
+ }
+
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Gui/Gui.vert b/engine/src/core-data/Common/MatDefs/Gui/Gui.vert
new file mode 100644
index 000000000..0591c5e89
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Gui/Gui.vert
@@ -0,0 +1,29 @@
+uniform mat4 g_WorldViewProjectionMatrix;
+uniform vec4 m_Color;
+
+attribute vec3 inPosition;
+
+#ifdef VERTEX_COLOR
+attribute vec4 inColor;
+#endif
+
+#ifdef TEXTURE
+attribute vec2 inTexCoord;
+varying vec2 texCoord;
+#endif
+
+varying vec4 color;
+
+void main() {
+ //vec2 pos = (g_WorldViewProjectionMatrix * inPosition).xy;
+ //gl_Position = vec4(pos, 0.0, 1.0);
+ gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);
+ #ifdef TEXTURE
+ texCoord = inTexCoord;
+ #endif
+ #ifdef VERTEX_COLOR
+ color = m_Color * inColor;
+ #else
+ color = m_Color;
+ #endif
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Hdr/LogLum.frag b/engine/src/core-data/Common/MatDefs/Hdr/LogLum.frag
new file mode 100644
index 000000000..68f08e9a6
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Hdr/LogLum.frag
@@ -0,0 +1,65 @@
+#import "Common/ShaderLib/Hdr.glsllib"
+
+uniform sampler2D m_Texture;
+varying vec2 texCoord;
+
+#ifdef BLOCKS
+ uniform vec2 m_PixelSize;
+ uniform vec2 m_BlockSize;
+ uniform float m_NumPixels;
+#endif
+
+vec4 blocks(vec2 halfBlockSize, vec2 pixelSize, float numPixels){
+ vec2 startUV = texCoord - halfBlockSize;
+ vec2 endUV = texCoord + halfBlockSize;
+
+ vec4 sum = vec4(0.0);
+ float numPix = 0.0;
+ //float maxLum = 0.0;
+
+ for (float x = startUV.x; x < endUV.x; x += pixelSize.x){
+ for (float y = startUV.y; y < endUV.y; y += pixelSize.y){
+ numPix += 1.0;
+ vec4 color = texture2D(m_Texture, vec2(x,y));
+
+ #ifdef ENCODE_LUM
+ color = HDR_EncodeLum(HDR_GetLum(color.rgb));
+ #endif
+ //#ifdef COMPUTE_MAX
+ //maxLum = max(color.r, maxLum);
+ //#endif
+ sum += color;
+ }
+ }
+ sum /= numPix;
+
+ #ifdef DECODE_LUM
+ sum = vec4(HDR_DecodeLum(sum));
+ //#ifdef COMPUTE_MAX
+ //maxLum = HDR_GetExpLum(maxLum);
+ //#endif
+ #endif
+
+ return sum;
+}
+
+vec4 fetch(){
+ vec4 color = texture2D(m_Texture, texCoord);
+ #ifdef ENCODE_LUM
+ return HDR_EncodeLum(HDR_GetLum(color.rgb));
+ #elif defined DECODE_LUM
+ return vec4(HDR_DecodeLum(color));
+ #else
+ return color;
+ #endif
+}
+
+void main() {
+ #ifdef BLOCKS
+ gl_FragColor = blocks(m_BlockSize * vec2(0.5), m_PixelSize, m_NumPixels);
+ #else
+ gl_FragColor = vec4(fetch());
+ #endif
+}
+
+
diff --git a/engine/src/core-data/Common/MatDefs/Hdr/LogLum.j3md b/engine/src/core-data/Common/MatDefs/Hdr/LogLum.j3md
new file mode 100644
index 000000000..0c4c6c889
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Hdr/LogLum.j3md
@@ -0,0 +1,31 @@
+MaterialDef Log Lum 2D {
+
+ MaterialParameters {
+ Texture2D Texture
+ Vector2 BlockSize
+ Vector2 PixelSize
+ Float NumPixels
+ Boolean DecodeLum
+ Boolean EncodeLum
+ Boolean Blocks
+ Boolean ComputeMax
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Gui/Gui.vert
+ FragmentShader GLSL100: Common/MatDefs/Hdr/LogLum.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ TEXTURE
+ ENCODE_LUM : EncodeLum
+ DECODE_LUM : DecodeLum
+ BLOCKS : Blocks
+ COMPUTE_MAX : ComputeMax
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Hdr/ToneMap.frag b/engine/src/core-data/Common/MatDefs/Hdr/ToneMap.frag
new file mode 100644
index 000000000..f22103072
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Hdr/ToneMap.frag
@@ -0,0 +1,31 @@
+#import "Common/ShaderLib/Hdr.glsllib"
+
+varying vec2 texCoord;
+
+uniform sampler2D m_Texture;
+uniform sampler2D m_Lum;
+uniform sampler2D m_Lum2;
+
+uniform float m_A;
+uniform float m_White;
+uniform float m_BlendFactor;
+uniform float m_Gamma;
+
+void main() {
+ float avgLumA = HDR_DecodeLum( texture2D(m_Lum, vec2(0.0)) );
+ float avgLumB = HDR_DecodeLum( texture2D(m_Lum2, vec2(0.0)) );
+ float lerpedLum = mix(avgLumA, avgLumB, m_BlendFactor);
+
+ vec4 color = texture2D(m_Texture, texCoord);
+ vec3 c1 = HDR_ToneMap(color.rgb, lerpedLum, m_A, m_White);
+ //vec3 c2 = HDR_ToneMap2(color.rgb, lerpedLum, m_A * vec2(0.25), m_White);
+
+ //float l1 = HDR_GetLuminance(c1);
+ //float l2 = HDR_GetLuminance(c2);
+
+ //vec3 final = mix(c2, c1, clamp(l1, 0.0, 1.0));
+
+ //tonedColor = pow(tonedColor, vec3(m_Gamma));
+ gl_FragColor = vec4(c1, color.a);
+}
+
diff --git a/engine/src/core-data/Common/MatDefs/Hdr/ToneMap.j3md b/engine/src/core-data/Common/MatDefs/Hdr/ToneMap.j3md
new file mode 100644
index 000000000..24fbd04ae
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Hdr/ToneMap.j3md
@@ -0,0 +1,23 @@
+MaterialDef Tone Mapper {
+ MaterialParameters {
+ Texture2D Texture
+ Texture2D Lum
+ Texture2D Lum2
+ Float BlendFactor
+ Float White
+ Float A
+ Float Gamma
+ }
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Gui/Gui.vert
+ FragmentShader GLSL100: Common/MatDefs/Hdr/ToneMap.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ TEXTURE
+ }
+ }
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Light/Deferred.frag b/engine/src/core-data/Common/MatDefs/Light/Deferred.frag
new file mode 100644
index 000000000..9fc7ebb8b
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Light/Deferred.frag
@@ -0,0 +1,146 @@
+#define ATTENUATION
+//#define HQ_ATTENUATION
+
+varying vec2 texCoord;
+
+uniform sampler2D m_DiffuseData;
+uniform sampler2D m_SpecularData;
+uniform sampler2D m_NormalData;
+uniform sampler2D m_DepthData;
+
+uniform vec3 m_FrustumCorner;
+uniform vec2 m_FrustumNearFar;
+
+uniform vec4 g_LightColor;
+uniform vec4 g_LightPosition;
+uniform vec3 g_CameraPosition;
+
+uniform mat4 m_ViewProjectionMatrixInverse;
+
+#ifdef COLORRAMP
+ uniform sampler2D m_ColorRamp;
+#endif
+
+float lightComputeDiffuse(in vec3 norm, in vec3 lightdir, in vec3 viewdir){
+ #ifdef MINNAERT
+ float NdotL = max(0.0, dot(norm, lightdir));
+ float NdotV = max(0.0, dot(norm, viewdir));
+ return NdotL * pow(max(NdotL * NdotV, 0.1), -1.0) * 0.5;
+ #else
+ return max(0.0, dot(norm, lightdir));
+ #endif
+}
+
+float lightComputeSpecular(in vec3 norm, in vec3 viewdir, in vec3 lightdir, in float shiny){
+//#ifdef LOW_QUALITY
+ // Blinn-Phong
+ // Note: preferably, H should be computed in the vertex shader
+ vec3 H = (viewdir + lightdir) * vec3(0.5);
+ return pow(max(dot(H, norm), 0.0), shiny);
+/*
+ #elif defined(WARDISO)
+ // Isotropic Ward
+ vec3 halfVec = normalize(viewdir + lightdir);
+ float NdotH = max(0.001, tangDot(norm, halfVec));
+ float NdotV = max(0.001, tangDot(norm, viewdir));
+ float NdotL = max(0.001, tangDot(norm, lightdir));
+ float a = tan(acos(NdotH));
+ float p = max(shiny/128.0, 0.001);
+ return NdotL * (1.0 / (4.0*3.14159265*p*p)) * (exp(-(a*a)/(p*p)) / (sqrt(NdotV * NdotL)));
+ #else
+ // Standard Phong
+ vec3 R = reflect(-lightdir, norm);
+ return pow(max(tangDot(R, viewdir), 0.0), shiny);
+ #endif
+*/
+}
+
+vec2 computeLighting(in vec3 wvPos, in vec3 wvNorm, in vec3 wvViewDir, in vec4 wvLightDir, in float shiny){
+ float diffuseFactor = lightComputeDiffuse(wvNorm, wvLightDir.xyz, wvViewDir);
+ float specularFactor = lightComputeSpecular(wvNorm, wvViewDir, wvLightDir.xyz, shiny);
+ return vec2(diffuseFactor, specularFactor) * vec2(wvLightDir.w);
+}
+
+vec3 decodeNormal(in vec4 enc){
+ vec4 nn = enc * vec4(2.0,2.0,0.0,0.0) + vec4(-1.0,-1.0,1.0,-1.0);
+ float l = dot(nn.xyz, -nn.xyw);
+ nn.z = l;
+ nn.xy *= sqrt(l);
+ return nn.xyz * vec3(2.0) + vec3(0.0,0.0,-1.0);
+}
+
+vec3 getPosition(in vec2 newTexCoord){
+ //Reconstruction from depth
+ float depth = texture2D(m_DepthData, newTexCoord).r;
+ //if (depth == 1.0)
+ // return vec3(0.0, 0.0, 2.0);
+ //depth = (2.0 * m_FrustumNearFar.x)
+ /// (m_FrustumNearFar.y + m_FrustumNearFar.x - depth * (m_FrustumNearFar.y-m_FrustumNearFar.x));
+
+ //one frustum corner method
+ //float x = mix(-m_FrustumCorner.x, m_FrustumCorner.x, newTexCoord.x);
+ //float y = mix(-m_FrustumCorner.y, m_FrustumCorner.y, newTexCoord.y);
+
+ //return depth * vec3(x, y, m_FrustumCorner.z);
+ vec4 pos;
+ pos.xy = (newTexCoord * vec2(2.0)) - vec2(1.0);
+ pos.z = depth;
+ pos.w = 1.0;
+ pos = m_ViewProjectionMatrixInverse * pos;
+ //pos /= pos.w;
+ return pos.xyz;
+}
+
+// JME3 lights in world space
+void lightComputeDir(in vec3 worldPos, in vec4 color, in vec4 position, out vec4 lightDir){
+ #ifdef DIR_LIGHT
+ lightDir.xyz = -position.xyz;
+ #else
+ lightDir.xyz = position.xyz - worldPos.xyz;
+ float dist = length(lightDir.xyz);
+ lightDir.w = clamp(1.0 - position.w * dist, 0.0, 1.0);
+ lightDir.xyz /= dist;
+ #endif
+
+/*
+ float posLight = step(0.5, color.w);
+ vec3 tempVec = position.xyz * sign(posLight - 0.5) - (worldPos * posLight);
+ #ifdef ATTENUATION
+ float dist = length(tempVec);
+ lightDir.w = clamp(1.0 - position.w * dist * posLight, 0.0, 1.0);
+ lightDir.xyz = tempVec / vec3(dist);
+ #ifdef HQ_ATTENUATION
+ lightVec = tempVec;
+ #endif
+ #else
+ lightDir = vec4(normalize(tempVec), 1.0);
+ #endif
+*/
+}
+
+void main(){
+ vec2 newTexCoord = texCoord;
+ vec4 diffuseColor = texture2D(m_DiffuseData, newTexCoord);
+ if (diffuseColor.a == 0.0)
+ discard;
+
+ vec4 specularColor = texture2D(m_SpecularData, newTexCoord);
+ vec3 worldPosition = getPosition(newTexCoord);
+ vec3 viewDir = normalize(g_CameraPosition - worldPosition);
+
+ vec4 normalInfo = vec4(texture2D(m_NormalData, newTexCoord).rg, 0.0, 0.0);
+ vec3 normal = decodeNormal(normalInfo);
+
+ vec4 lightDir;
+ lightComputeDir(worldPosition, g_LightColor, g_LightPosition, lightDir);
+
+ vec2 light = computeLighting(worldPosition, normal, viewDir, lightDir, specularColor.w*128.0);
+
+ #ifdef COLORRAMP
+ diffuseColor.rgb *= texture2D(m_ColorRamp, vec2(light.x, 0.0)).rgb;
+ specularColor.rgb *= texture2D(m_ColorRamp, vec2(light.y, 0.0)).rgb;
+ #endif
+
+ gl_FragColor = vec4(light.x * diffuseColor.xyz + light.y * specularColor.xyz, 1.0);
+ gl_FragColor.xyz *= g_LightColor.xyz;
+}
diff --git a/engine/src/core-data/Common/MatDefs/Light/Deferred.j3md b/engine/src/core-data/Common/MatDefs/Light/Deferred.j3md
new file mode 100644
index 000000000..c386a6165
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Light/Deferred.j3md
@@ -0,0 +1,61 @@
+MaterialDef Phong Lighting Deferred {
+
+ MaterialParameters {
+
+ // Use more efficent algorithms to improve performance
+ Boolean LowQuality
+
+ // Improve quality at the cost of performance
+ Boolean HighQuality
+
+ // Activate shading along the tangent, instead of the normal
+ // Requires tangent data to be available on the model.
+ Boolean VTangent
+
+ // Use minnaert diffuse instead of lambert
+ Boolean Minnaert
+
+ // Use ward specular instead of phong
+ Boolean WardIso
+
+ Texture2D DiffuseData
+ Texture2D SpecularData
+ Texture2D NormalData
+ Texture2D DepthData
+
+ Vector3 FrustumCorner
+ Vector2 FrustumNearFar
+ Matrix4 ViewProjectionMatrixInverse
+
+ // Color ramp, will map diffuse and specular values through it.
+ Texture2D ColorRamp
+ }
+
+ Technique {
+ LightMode MultiPass
+
+ VertexShader GLSL100: Common/MatDefs/Light/Deferred.vert
+ FragmentShader GLSL100: Common/MatDefs/Light/Deferred.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldViewMatrix
+ ViewMatrix
+ CameraPosition
+ }
+
+ Defines {
+ ATTENUATION : Attenuation
+ V_TANGENT : VTangent
+ MINNAERT : Minnaert
+ WARDISO : WardIso
+ LOW_QUALITY : LowQuality
+ HQ_ATTENUATION : HighQuality
+ COLORRAMP : ColorRamp
+ }
+ }
+
+ Technique FixedFunc {
+ }
+
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Light/Deferred.vert b/engine/src/core-data/Common/MatDefs/Light/Deferred.vert
new file mode 100644
index 000000000..0743cc1a9
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Light/Deferred.vert
@@ -0,0 +1,10 @@
+varying vec2 texCoord;
+
+attribute vec3 inPosition;
+attribute vec2 inTexCoord;
+
+void main(){
+ texCoord = inTexCoord;
+ vec4 pos = vec4(inPosition, 1.0);
+ gl_Position = vec4(sign(pos.xy-vec2(0.5)), 0.0, 1.0);
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Light/GBuf.frag b/engine/src/core-data/Common/MatDefs/Light/GBuf.frag
new file mode 100644
index 000000000..397062455
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Light/GBuf.frag
@@ -0,0 +1,86 @@
+#import "Common/ShaderLib/Optics.glsllib"
+
+uniform float m_Shininess;
+
+varying vec2 texCoord;
+varying vec4 AmbientSum;
+varying vec4 DiffuseSum;
+varying vec4 SpecularSum;
+
+varying float vDepth;
+varying vec3 vNormal;
+
+#ifdef DIFFUSEMAP
+ uniform sampler2D m_DiffuseMap;
+#endif
+
+#ifdef SPECULARMAP
+ uniform sampler2D m_SpecularMap;
+#endif
+
+#ifdef PARALLAXMAP
+ uniform sampler2D m_ParallaxMap;
+#endif
+
+#ifdef NORMALMAP
+ uniform sampler2D m_NormalMap;
+ varying mat3 tbnMat;
+#endif
+
+vec2 encodeNormal(in vec3 n){
+ vec2 enc = normalize(n.xy) * (sqrt(-n.z*0.5+0.5));
+ enc = enc*vec2(0.5)+vec2(0.5);
+ return enc;
+}
+
+void main(){
+ vec2 newTexCoord = texCoord;
+ float height = 0.0;
+ #if defined(PARALLAXMAP) || defined(NORMALMAP_PARALLAX)
+ #ifdef PARALLAXMAP
+ height = texture2D(m_ParallaxMap, texCoord).r;
+ #else
+ height = texture2D(m_NormalMap, texCoord).a;
+ #endif
+ float heightScale = 0.05;
+ float heightBias = heightScale * -0.5;
+ height = (height * heightScale + heightBias);
+ #endif
+
+
+ // ***********************
+ // Read from textures
+ // ***********************
+ #if defined(NORMALMAP) && !defined(VERTEX_LIGHTING)
+ vec4 normalHeight = texture2D(m_NormalMap, newTexCoord);
+ vec3 normal = (normalHeight.xyz * vec3(2.0) - vec3(1.0));
+ normal.y = -normal.y;
+
+ normal = tbnMat * normal;
+ #else
+ vec3 normal = vNormal;
+ #if !defined(LOW_QUALITY) && !defined(V_TANGENT)
+ normal = normalize(normal);
+ #endif
+ #endif
+
+ #ifdef DIFFUSEMAP
+ vec4 diffuseColor = texture2D(m_DiffuseMap, newTexCoord);
+ #else
+ vec4 diffuseColor = vec4(1.0);
+ #endif
+
+ #ifdef SPECULARMAP
+ vec4 specularColor = texture2D(m_SpecularMap, newTexCoord);
+ #else
+ vec4 specularColor = vec4(1.0);
+ #endif
+
+ diffuseColor.rgb *= DiffuseSum.rgb;
+ specularColor.rgb *= SpecularSum.rgb;
+
+ gl_FragData[0] = vec4(diffuseColor.rgb, 1.0);
+ gl_FragData[1] = vec4(encodeNormal(normal), 0.0, 0.0);
+ /*encodeNormal(vNormal));*/
+ gl_FragData[2] = vec4(specularColor.rgb, m_Shininess / 128.0);
+}
diff --git a/engine/src/core-data/Common/MatDefs/Light/GBuf.vert b/engine/src/core-data/Common/MatDefs/Light/GBuf.vert
new file mode 100644
index 000000000..f4ad19963
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Light/GBuf.vert
@@ -0,0 +1,71 @@
+uniform mat4 g_WorldViewProjectionMatrix;
+uniform mat4 g_WorldMatrix;
+
+uniform vec4 m_Ambient;
+uniform vec4 m_Diffuse;
+uniform vec4 m_Specular;
+uniform float m_Shininess;
+
+varying vec2 texCoord;
+
+varying vec4 AmbientSum;
+varying vec4 DiffuseSum;
+varying vec4 SpecularSum;
+
+attribute vec3 inPosition;
+attribute vec2 inTexCoord;
+attribute vec3 inNormal;
+
+#ifdef NORMALMAP
+attribute vec3 inTangent;
+varying mat3 tbnMat;
+#endif
+
+#ifdef VERTEX_COLOR
+ attribute vec4 inColor;
+#endif
+
+varying vec3 vNormal;
+varying float vDepth;
+
+void main(){
+ vec4 pos = vec4(inPosition, 1.0);
+ gl_Position = g_WorldViewProjectionMatrix * pos;
+ texCoord = inTexCoord;
+
+ #if defined(NORMALMAP)
+ vec4 wvNormal, wvTangent, wvBinormal;
+
+ wvNormal = vec4(inNormal, 0.0);
+ wvTangent = vec4(inTangent, 0.0);
+
+ wvNormal.xyz = normalize( (g_WorldMatrix * wvNormal).xyz );
+ wvTangent.xyz = normalize( (g_WorldMatrix * wvTangent).xyz );
+ wvBinormal.xyz = cross(wvNormal.xyz, wvTangent.xyz);
+ tbnMat = mat3(wvTangent.xyz, wvBinormal.xyz, wvNormal.xyz);
+
+ vNormal = wvNormal.xyz;
+ #else
+ vec4 wvNormal;
+ #ifdef V_TANGENT
+ wvNormal = vec4(inTangent, 0.0);
+ #else
+ wvNormal = vec4(inNormal, 0.0);
+ #endif
+ vNormal = normalize( (g_WorldMatrix * wvNormal).xyz );
+ #endif
+
+ #ifdef MATERIAL_COLORS
+ AmbientSum = m_Ambient;
+ DiffuseSum = m_Diffuse;
+ SpecularSum = m_Specular;
+ #else
+ AmbientSum = vec4(0.0);
+ DiffuseSum = vec4(1.0);
+ SpecularSum = vec4(1.0);
+ #endif
+
+ #ifdef VERTEX_COLOR
+ DiffuseSum *= inColor;
+ #endif
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Light/Glow.frag b/engine/src/core-data/Common/MatDefs/Light/Glow.frag
new file mode 100644
index 000000000..8b613f566
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Light/Glow.frag
@@ -0,0 +1,23 @@
+varying vec2 texCoord;
+
+#ifdef HAS_GLOWMAP
+ uniform sampler2D m_GlowMap;
+#endif
+
+#ifdef HAS_GLOWCOLOR
+ uniform vec4 m_GlowColor;
+#endif
+
+
+void main(){
+
+ #ifdef HAS_GLOWMAP
+ gl_FragColor = texture2D(m_GlowMap, texCoord);
+ #else
+ #ifdef HAS_GLOWCOLOR
+ gl_FragColor = m_GlowColor;
+ #else
+ gl_FragColor = vec4(0.0);
+ #endif
+ #endif
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Light/Lighting.frag b/engine/src/core-data/Common/MatDefs/Light/Lighting.frag
new file mode 100644
index 000000000..8d5c785e0
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Light/Lighting.frag
@@ -0,0 +1,207 @@
+#import "Common/ShaderLib/Optics.glsllib"
+#define ATTENUATION
+//#define HQ_ATTENUATION
+
+varying vec2 texCoord;
+
+varying vec4 AmbientSum;
+varying vec4 DiffuseSum;
+varying vec4 SpecularSum;
+
+#ifndef VERTEX_LIGHTING
+ varying vec3 vPosition;
+ varying vec3 vViewDir;
+ varying vec4 vLightDir;
+#endif
+
+#ifdef DIFFUSEMAP
+ uniform sampler2D m_DiffuseMap;
+#endif
+
+#ifdef SPECULARMAP
+ uniform sampler2D m_SpecularMap;
+#endif
+
+#ifdef PARALLAXMAP
+ uniform sampler2D m_ParallaxMap;
+#endif
+
+#ifdef NORMALMAP
+ uniform sampler2D m_NormalMap;
+#else
+ varying vec3 vNormal;
+#endif
+
+#ifdef ALPHAMAP
+ uniform sampler2D m_AlphaMap;
+#endif
+
+#ifdef COLORRAMP
+ uniform sampler2D m_ColorRamp;
+#endif
+uniform float m_AlphaDiscardThreshold;
+#ifndef VERTEX_LIGHTING
+uniform float m_Shininess;
+
+#ifdef HQ_ATTENUATION
+uniform vec4 g_LightPosition;
+varying vec3 lightVec;
+#endif
+
+#ifdef USE_REFLECTION
+ uniform float m_ReflectionPower;
+ uniform float m_ReflectionIntensity;
+ varying vec4 refVec;
+
+ uniform ENVMAP m_EnvMap;
+#endif
+
+float tangDot(in vec3 v1, in vec3 v2){
+ float d = dot(v1,v2);
+ #ifdef V_TANGENT
+ d = 1.0 - d*d;
+ return step(0.0, d) * sqrt(d);
+ #else
+ return d;
+ #endif
+}
+
+float lightComputeDiffuse(in vec3 norm, in vec3 lightdir, in vec3 viewdir){
+ #ifdef MINNAERT
+ float NdotL = max(0.0, dot(norm, lightdir));
+ float NdotV = max(0.0, dot(norm, viewdir));
+ return NdotL * pow(max(NdotL * NdotV, 0.1), -1.0) * 0.5;
+ #else
+ return max(0.0, dot(norm, lightdir));
+ #endif
+}
+
+float lightComputeSpecular(in vec3 norm, in vec3 viewdir, in vec3 lightdir, in float shiny){
+ #ifdef LOW_QUALITY
+ // Blinn-Phong
+ // Note: preferably, H should be computed in the vertex shader
+ vec3 H = (viewdir + lightdir) * vec3(0.5);
+ return pow(max(tangDot(H, norm), 0.0), shiny);
+ #elif defined(WARDISO)
+ // Isotropic Ward
+ vec3 halfVec = normalize(viewdir + lightdir);
+ float NdotH = max(0.001, tangDot(norm, halfVec));
+ float NdotV = max(0.001, tangDot(norm, viewdir));
+ float NdotL = max(0.001, tangDot(norm, lightdir));
+ float a = tan(acos(NdotH));
+ float p = max(shiny/128.0, 0.001);
+ return NdotL * (1.0 / (4.0*3.14159265*p*p)) * (exp(-(a*a)/(p*p)) / (sqrt(NdotV * NdotL)));
+ #else
+ // Standard Phong
+ vec3 R = reflect(-lightdir, norm);
+ return pow(max(tangDot(R, viewdir), 0.0), shiny);
+ #endif
+}
+
+vec2 computeLighting(in vec3 wvPos, in vec3 wvNorm, in vec3 wvViewDir, in vec3 wvLightDir){
+ float diffuseFactor = lightComputeDiffuse(wvNorm, wvLightDir, wvViewDir);
+ float specularFactor = lightComputeSpecular(wvNorm, wvViewDir, wvLightDir, m_Shininess);
+ specularFactor *= step(1.0, m_Shininess);
+
+ #ifdef HQ_ATTENUATION
+ float att = clamp(1.0 - g_LightPosition.w * length(lightVec), 0.0, 1.0);
+ #else
+ float att = vLightDir.w;
+ #endif
+
+ return vec2(diffuseFactor, specularFactor) * vec2(att);
+}
+#endif
+
+void main(){
+ vec2 newTexCoord;
+
+ #if defined(PARALLAXMAP) || defined(NORMALMAP_PARALLAX)
+ float h;
+ #ifdef PARALLAXMAP
+ h = texture2D(m_ParallaxMap, texCoord).r;
+ #else
+ h = texture2D(m_NormalMap, texCoord).a;
+ #endif
+ float heightScale = 0.05;
+ float heightBias = heightScale * -0.5;
+ vec3 normView = normalize(vViewDir);
+ h = (h * heightScale + heightBias) * normView.z;
+ newTexCoord = texCoord + (h * -normView.xy);
+ #else
+ newTexCoord = texCoord;
+ #endif
+
+ #ifdef DIFFUSEMAP
+ vec4 diffuseColor = texture2D(m_DiffuseMap, newTexCoord);
+ #else
+ vec4 diffuseColor = vec4(1.0);
+ #endif
+ float alpha = DiffuseSum.a * diffuseColor.a;
+ #ifdef ALPHAMAP
+ alpha = alpha * texture2D(m_AlphaMap, newTexCoord).r;
+ #endif
+ if(alpha < m_AlphaDiscardThreshold){
+ discard;
+ }
+
+ // ***********************
+ // Read from textures
+ // ***********************
+ #if defined(NORMALMAP) && !defined(VERTEX_LIGHTING)
+ vec4 normalHeight = texture2D(m_NormalMap, newTexCoord);
+ vec3 normal = (normalHeight.xyz * vec3(2.0) - vec3(1.0));
+ #ifdef LATC
+ normal.z = sqrt(1.0 - (normal.x * normal.x) - (normal.y * normal.y));
+ #endif
+ normal.y = -normal.y;
+ #elif !defined(VERTEX_LIGHTING)
+ vec3 normal = vNormal;
+ #if !defined(LOW_QUALITY) && !defined(V_TANGENT)
+ normal = normalize(normal);
+ #endif
+ #endif
+
+ #ifdef SPECULARMAP
+ vec4 specularColor = texture2D(m_SpecularMap, newTexCoord);
+ #else
+ vec4 specularColor = vec4(1.0);
+ #endif
+
+ #ifdef VERTEX_LIGHTING
+ vec2 light = vec2(AmbientSum.a, SpecularSum.a);
+ #ifdef COLORRAMP
+ light.x = texture2D(m_ColorRamp, vec2(light.x, 0.0)).r;
+ light.y = texture2D(m_ColorRamp, vec2(light.y, 0.0)).r;
+ #endif
+
+ gl_FragColor = AmbientSum * diffuseColor +
+ DiffuseSum * diffuseColor * light.x +
+ SpecularSum * specularColor * light.y;
+ #else
+ vec4 lightDir = vLightDir;
+ lightDir.xyz = normalize(lightDir.xyz);
+
+ vec2 light = computeLighting(vPosition, normal, vViewDir.xyz, lightDir.xyz);
+ #ifdef COLORRAMP
+ diffuseColor.rgb *= texture2D(m_ColorRamp, vec2(light.x, 0.0)).rgb;
+ specularColor.rgb *= texture2D(m_ColorRamp, vec2(light.y, 0.0)).rgb;
+ #endif
+
+ #ifdef USE_REFLECTION
+ vec4 refColor = Optics_GetEnvColor(m_EnvMap, refVec.xyz);
+
+ // Interpolate light specularity toward reflection color
+ // Multiply result by specular map
+ specularColor = mix(SpecularSum * light.y, refColor, refVec.w) * specularColor;
+
+ SpecularSum = vec4(1.0);
+ light.y = 1.0;
+ #endif
+
+ gl_FragColor = AmbientSum * diffuseColor +
+ DiffuseSum * diffuseColor * light.x +
+ SpecularSum * specularColor * light.y;
+ #endif
+ gl_FragColor.a = alpha;
+}
diff --git a/engine/src/core-data/Common/MatDefs/Light/Lighting.j3md b/engine/src/core-data/Common/MatDefs/Light/Lighting.j3md
new file mode 100644
index 000000000..7114be00b
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Light/Lighting.j3md
@@ -0,0 +1,219 @@
+MaterialDef Phong Lighting {
+
+ MaterialParameters {
+
+ // Compute vertex lighting in the shader
+ // For better performance
+ Boolean VertexLighting
+
+ // Use more efficent algorithms to improve performance
+ Boolean LowQuality
+
+ // Improve quality at the cost of performance
+ Boolean HighQuality
+
+ // Output alpha from the diffuse map
+ Boolean UseAlpha
+
+ // Apha threshold for fragment discarding
+ Float AlphaDiscardThreshold
+
+ // Normal map is in BC5/ATI2n/LATC/3Dc compression format
+ Boolean LATC
+
+ // Use the provided ambient, diffuse, and specular colors
+ Boolean UseMaterialColors
+
+ // Activate shading along the tangent, instead of the normal
+ // Requires tangent data to be available on the model.
+ Boolean VTangent
+
+ // Use minnaert diffuse instead of lambert
+ Boolean Minnaert
+
+ // Use ward specular instead of phong
+ Boolean WardIso
+
+ // Use vertex color as an additional diffuse color.
+ Boolean UseVertexColor
+
+ // Ambient color
+ Color Ambient
+
+ // Diffuse color
+ Color Diffuse : Color
+
+ // Specular color
+ Color Specular
+
+ // Specular power/shininess
+ Float Shininess
+
+ // Diffuse map
+ Texture2D DiffuseMap
+
+ // Normal map
+ Texture2D NormalMap
+
+ // Specular/gloss map
+ Texture2D SpecularMap
+
+ // Parallax/height map
+ Texture2D ParallaxMap
+
+ // Texture that specifies alpha values
+ Texture2D AlphaMap
+
+ // Color ramp, will map diffuse and specular values through it.
+ Texture2D ColorRamp
+
+ // Texture of the glowing parts of the material
+ Texture2D GlowMap
+
+ // The glow color of the object
+ Color GlowColor
+
+ // Parameters for fresnel
+ // X = bias
+ // Y = scale
+ // Z = power
+ Vector3 FresnelParams
+
+ // Env Map for reflection
+ TextureCubeMap EnvMap
+
+ // the env map is a spheremap and not a cube map
+ Boolean EnvMapAsSphereMap
+ }
+
+ Technique {
+
+ LightMode MultiPass
+
+ VertexShader GLSL100: Common/MatDefs/Light/Lighting.vert
+ FragmentShader GLSL100: Common/MatDefs/Light/Lighting.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ NormalMatrix
+ WorldViewMatrix
+ ViewMatrix
+ CameraPosition
+ WorldMatrix
+ }
+
+ Defines {
+ LATC : LATC
+ VERTEX_COLOR : UseVertexColor
+ VERTEX_LIGHTING : VertexLighting
+ ATTENUATION : Attenuation
+ MATERIAL_COLORS : UseMaterialColors
+ V_TANGENT : VTangent
+ MINNAERT : Minnaert
+ WARDISO : WardIso
+ LOW_QUALITY : LowQuality
+ HQ_ATTENUATION : HighQuality
+
+ DIFFUSEMAP : DiffuseMap
+ NORMALMAP : NormalMap
+ SPECULARMAP : SpecularMap
+ PARALLAXMAP : ParallaxMap
+ ALPHAMAP : AlphaMap
+ COLORRAMP : ColorRamp
+
+ USE_REFLECTION : EnvMap
+ SPHERE_MAP : SphereMap
+ }
+ }
+
+ Technique PreShadow {
+
+ VertexShader GLSL100 : Common/MatDefs/Shadow/PreShadow.vert
+ FragmentShader GLSL100 : Common/MatDefs/Shadow/PreShadow.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldViewMatrix
+ }
+
+ Defines {
+ DIFFUSEMAP_ALPHA : DiffuseMap
+ }
+
+ RenderState {
+ FaceCull Off
+ DepthTest On
+ DepthWrite On
+ PolyOffset 5 0
+ ColorWrite Off
+ }
+
+ }
+
+ Technique PreNormalPass {
+
+ VertexShader GLSL100 : Common/MatDefs/SSAO/normal.vert
+ FragmentShader GLSL100 : Common/MatDefs/SSAO/normal.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldViewMatrix
+ NormalMatrix
+ }
+
+ Defines {
+ DIFFUSEMAP_ALPHA : DiffuseMap
+ }
+
+ RenderState {
+
+ }
+
+ }
+
+ Technique GBuf {
+
+ VertexShader GLSL100: Common/MatDefs/Light/GBuf.vert
+ FragmentShader GLSL100: Common/MatDefs/Light/GBuf.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ NormalMatrix
+ WorldViewMatrix
+ WorldMatrix
+ }
+
+ Defines {
+ VERTEX_COLOR : UseVertexColor
+ MATERIAL_COLORS : UseMaterialColors
+ V_TANGENT : VTangent
+ MINNAERT : Minnaert
+ WARDISO : WardIso
+
+ DIFFUSEMAP : DiffuseMap
+ NORMALMAP : NormalMap
+ SPECULARMAP : SpecularMap
+ PARALLAXMAP : ParallaxMap
+ }
+ }
+
+ Technique FixedFunc {
+ LightMode FixedPipeline
+ }
+
+ Technique Glow {
+
+ VertexShader GLSL100: Common/MatDefs/Misc/SimpleTextured.vert
+ FragmentShader GLSL100: Common/MatDefs/Light/Glow.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ HAS_GLOWMAP : GlowMap
+ HAS_GLOWCOLOR : GlowColor
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Light/Lighting.vert b/engine/src/core-data/Common/MatDefs/Light/Lighting.vert
new file mode 100644
index 000000000..7de7713e4
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Light/Lighting.vert
@@ -0,0 +1,185 @@
+#define ATTENUATION
+//#define HQ_ATTENUATION
+
+uniform mat4 g_WorldViewProjectionMatrix;
+uniform mat4 g_WorldViewMatrix;
+uniform mat3 g_NormalMatrix;
+uniform mat4 g_ViewMatrix;
+
+uniform vec4 m_Ambient;
+uniform vec4 m_Diffuse;
+uniform vec4 m_Specular;
+uniform float m_Shininess;
+
+uniform vec4 g_LightColor;
+uniform vec4 g_LightPosition;
+uniform vec4 g_AmbientLightColor;
+
+varying vec2 texCoord;
+
+varying vec4 AmbientSum;
+varying vec4 DiffuseSum;
+varying vec4 SpecularSum;
+
+attribute vec3 inPosition;
+attribute vec2 inTexCoord;
+attribute vec3 inNormal;
+
+#ifdef HQ_ATTENUATION
+ varying vec3 lightVec;
+#endif
+
+#ifdef VERTEX_COLOR
+ attribute vec4 inColor;
+#endif
+
+#ifndef VERTEX_LIGHTING
+ attribute vec4 inTangent;
+
+ #ifndef NORMALMAP
+ varying vec3 vNormal;
+ #endif
+ varying vec3 vPosition;
+ varying vec3 vViewDir;
+ varying vec4 vLightDir;
+#endif
+
+#ifdef USE_REFLECTION
+ uniform vec3 g_CameraPosition;
+ uniform mat4 g_WorldMatrix;
+
+ uniform vec3 m_FresnelParams;
+ varying vec4 refVec;
+
+
+ /**
+ * Input:
+ * attribute inPosition
+ * attribute inNormal
+ * uniform g_WorldMatrix
+ * uniform g_CameraPosition
+ *
+ * Output:
+ * varying refVec
+ */
+ void computeRef(){
+ vec3 worldPos = (g_WorldMatrix * vec4(inPosition,1.0)).xyz;
+
+ vec3 I = normalize( g_CameraPosition - worldPos ).xyz;
+ vec3 N = normalize( (g_WorldMatrix * vec4(inNormal, 0.0)).xyz );
+
+ refVec.xyz = reflect(I, N);
+ refVec.w = m_FresnelParams.x + m_FresnelParams.y * pow(1.0 + dot(I, N), m_FresnelParams.z);
+ }
+#endif
+
+// JME3 lights in world space
+void lightComputeDir(in vec3 worldPos, in vec4 color, in vec4 position, out vec4 lightDir){
+ float posLight = step(0.5, color.w);
+ vec3 tempVec = position.xyz * sign(posLight - 0.5) - (worldPos * posLight);
+ #ifdef ATTENUATION
+ float dist = length(tempVec);
+ lightDir.w = clamp(1.0 - position.w * dist * posLight, 0.0, 1.0);
+ lightDir.xyz = tempVec / vec3(dist);
+ #ifdef HQ_ATTENUATION
+ lightVec = tempVec;
+ #endif
+ #else
+ lightDir = vec4(normalize(tempVec), 1.0);
+ #endif
+}
+
+#ifdef VERTEX_LIGHTING
+ float lightComputeDiffuse(in vec3 norm, in vec3 lightdir){
+ return max(0.0, dot(norm, lightdir));
+ }
+
+ float lightComputeSpecular(in vec3 norm, in vec3 viewdir, in vec3 lightdir, in float shiny){
+ #ifndef LOW_QUALITY
+ vec3 H = (viewdir + lightdir) * vec3(0.5);
+ return pow(max(dot(H, norm), 0.0), shiny);
+ #else
+ return 0.0;
+ #endif
+ }
+
+vec2 computeLighting(in vec3 wvPos, in vec3 wvNorm, in vec3 wvViewDir, in vec4 wvLightPos){
+ vec4 lightDir;
+ lightComputeDir(wvPos, g_LightColor, wvLightPos, lightDir);
+
+ float diffuseFactor = lightComputeDiffuse(wvNorm, lightDir.xyz);
+ float specularFactor = lightComputeSpecular(wvNorm, wvViewDir, lightDir.xyz, m_Shininess);
+ //specularFactor *= step(0.01, diffuseFactor);
+ return vec2(diffuseFactor, specularFactor) * vec2(lightDir.w);
+ }
+#endif
+
+void main(){
+ vec4 pos = vec4(inPosition, 1.0);
+ gl_Position = g_WorldViewProjectionMatrix * pos;
+ texCoord = inTexCoord;
+
+ vec3 wvPosition = (g_WorldViewMatrix * pos).xyz;
+ vec3 wvNormal = normalize(g_NormalMatrix * inNormal);
+ vec3 viewDir = normalize(-wvPosition);
+
+ //vec4 lightColor = g_LightColor[gl_InstanceID];
+ //vec4 lightPos = g_LightPosition[gl_InstanceID];
+ //vec4 wvLightPos = (g_ViewMatrix * vec4(lightPos.xyz, lightColor.w));
+ //wvLightPos.w = lightPos.w;
+
+ vec4 wvLightPos = (g_ViewMatrix * vec4(g_LightPosition.xyz, g_LightColor.w));
+ wvLightPos.w = g_LightPosition.w;
+ vec4 lightColor = g_LightColor;
+
+ #if defined(NORMALMAP) && !defined(VERTEX_LIGHTING)
+ vec3 wvTangent = normalize(g_NormalMatrix * inTangent.xyz);
+ vec3 wvBinormal = cross(wvNormal, wvTangent);
+
+ mat3 tbnMat = mat3(wvTangent, wvBinormal * -inTangent.w,wvNormal);
+
+ vPosition = wvPosition * tbnMat;
+ vViewDir = viewDir * tbnMat;
+ lightComputeDir(wvPosition, lightColor, wvLightPos, vLightDir);
+ vLightDir.xyz = (vLightDir.xyz * tbnMat).xyz;
+ #elif !defined(VERTEX_LIGHTING)
+ vNormal = wvNormal;
+
+ vPosition = wvPosition;
+ vViewDir = viewDir;
+
+ lightComputeDir(wvPosition, lightColor, wvLightPos, vLightDir);
+
+ #ifdef V_TANGENT
+ vNormal = normalize(g_NormalMatrix * inTangent.xyz);
+ vNormal = -cross(cross(vLightDir.xyz, vNormal), vNormal);
+ #endif
+ #endif
+
+ lightColor.w = 1.0;
+ #ifdef MATERIAL_COLORS
+ AmbientSum = m_Ambient * g_AmbientLightColor;
+ DiffuseSum = m_Diffuse * lightColor;
+ SpecularSum = m_Specular * lightColor;
+ #else
+ AmbientSum = vec4(0.2, 0.2, 0.2, 1.0) * g_AmbientLightColor; // Default: ambient color is dark gray
+ DiffuseSum = lightColor;
+ SpecularSum = lightColor;
+ #endif
+
+ #ifdef VERTEX_COLOR
+ AmbientSum *= inColor;
+ DiffuseSum *= inColor;
+ #endif
+
+ #ifdef VERTEX_LIGHTING
+ vec2 light = computeLighting(wvPosition, wvNormal, viewDir, wvLightPos);
+
+ AmbientSum.a = light.x;
+ SpecularSum.a = light.y;
+ #endif
+
+ #ifdef USE_REFLECTION
+ computeRef();
+ #endif
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/ColoredTextured.frag b/engine/src/core-data/Common/MatDefs/Misc/ColoredTextured.frag
new file mode 100644
index 000000000..272f100be
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/ColoredTextured.frag
@@ -0,0 +1,9 @@
+varying vec2 texCoord;
+
+uniform sampler2D m_ColorMap;
+uniform vec4 m_Color;
+
+void main(){
+ vec4 texColor = texture2D(m_ColorMap, texCoord);
+ gl_FragColor = vec4(mix(m_Color.rgb, texColor.rgb, texColor.a), 1.0);
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/ColoredTextured.j3md b/engine/src/core-data/Common/MatDefs/Misc/ColoredTextured.j3md
new file mode 100644
index 000000000..dde8ea87d
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/ColoredTextured.j3md
@@ -0,0 +1,20 @@
+MaterialDef Colored Textured {
+
+ MaterialParameters {
+ Texture2D ColorMap
+ Color Color
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Misc/ColoredTextured.vert
+ FragmentShader GLSL100: Common/MatDefs/Misc/ColoredTextured.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ }
+
+ Technique FixedFunc {
+ }
+
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/ColoredTextured.vert b/engine/src/core-data/Common/MatDefs/Misc/ColoredTextured.vert
new file mode 100644
index 000000000..572d84191
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/ColoredTextured.vert
@@ -0,0 +1,11 @@
+uniform mat4 g_WorldViewProjectionMatrix;
+
+attribute vec3 inPosition;
+attribute vec2 inTexCoord;
+
+varying vec2 texCoord;
+
+void main(){
+ gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);
+ texCoord = inTexCoord;
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/Particle.frag b/engine/src/core-data/Common/MatDefs/Misc/Particle.frag
new file mode 100644
index 000000000..3c378e6e6
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/Particle.frag
@@ -0,0 +1,26 @@
+// TODO: Fix this so normal particles don't need it.
+// Only needed for certain GPUs.
+//#version 120
+
+#ifdef USE_TEXTURE
+uniform sampler2D m_Texture;
+varying vec4 texCoord;
+#endif
+
+varying vec4 color;
+
+void main(){
+ if (color.a <= 0.01)
+ discard;
+
+ #ifdef USE_TEXTURE
+ #ifdef POINT_SPRITE
+ vec2 uv = mix(texCoord.xy, texCoord.zw, gl_PointCoord.xy);
+ #else
+ vec2 uv = texCoord.xy;
+ #endif
+ gl_FragColor = texture2D(m_Texture, uv) * color;
+ #else
+ gl_FragColor = color;
+ #endif
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/Particle.j3md b/engine/src/core-data/Common/MatDefs/Misc/Particle.j3md
new file mode 100644
index 000000000..25283b27d
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/Particle.j3md
@@ -0,0 +1,65 @@
+MaterialDef Point Sprite {
+
+ MaterialParameters {
+ Texture2D Texture
+ Float Quadratic
+ Boolean PointSprite
+
+ // Texture of the glowing parts of the material
+ Texture2D GlowMap
+ // The glow color of the object
+ Color GlowColor
+ }
+
+ Technique {
+
+ VertexShader GLSL100 : Common/MatDefs/Misc/Particle.vert
+ FragmentShader GLSL120 : Common/MatDefs/Misc/Particle.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldViewMatrix
+ WorldMatrix
+ CameraPosition
+ }
+
+ RenderState {
+ Blend AlphaAdditive
+ DepthWrite Off
+ PointSprite On
+ // AlphaTestFalloff 0.01
+ }
+
+ Defines {
+ USE_TEXTURE : Texture
+ POINT_SPRITE : PointSprite
+ }
+ }
+
+ Technique FixedFunc {
+ RenderState {
+ Blend AlphaAdditive
+ // DepthWrite Off
+ // AlphaTestFalloff 0.01
+ }
+ }
+
+ Technique Glow {
+
+ VertexShader GLSL100: Common/MatDefs/Misc/SimpleTextured.vert
+ FragmentShader GLSL100: Common/MatDefs/Light/Glow.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ HAS_GLOWMAP : GlowMap
+ HAS_GLOWCOLOR : GlowColor
+ }
+
+ RenderState {
+ PointSprite On
+ }
+ }
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/Particle.vert b/engine/src/core-data/Common/MatDefs/Misc/Particle.vert
new file mode 100644
index 000000000..9c2733615
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/Particle.vert
@@ -0,0 +1,42 @@
+uniform mat4 g_WorldViewProjectionMatrix;
+
+attribute vec3 inPosition;
+attribute vec4 inColor;
+attribute vec4 inTexCoord;
+
+varying vec4 color;
+
+#ifdef USE_TEXTURE
+varying vec4 texCoord;
+#endif
+
+#ifdef POINT_SPRITE
+uniform mat4 g_WorldViewMatrix;
+uniform mat4 g_WorldMatrix;
+uniform vec3 g_CameraPosition;
+uniform float m_Quadratic;
+const float SIZE_MULTIPLIER = 4.0;
+attribute float inSize;
+#endif
+
+void main(){
+ vec4 pos = vec4(inPosition, 1.0);
+
+ gl_Position = g_WorldViewProjectionMatrix * pos;
+ color = inColor;
+
+ #ifdef USE_TEXTURE
+ texCoord = inTexCoord;
+ #endif
+
+ #ifdef POINT_SPRITE
+ vec4 worldPos = g_WorldMatrix * pos;
+ float d = distance(g_CameraPosition.xyz, worldPos.xyz);
+ gl_PointSize = max(1.0, (inSize * SIZE_MULTIPLIER * m_Quadratic) / d);
+
+ //vec4 worldViewPos = g_WorldViewMatrix * pos;
+ //gl_PointSize = (inSize * SIZE_MULTIPLIER * m_Quadratic)*100.0 / worldViewPos.z;
+
+ color.a *= min(gl_PointSize, 1.0);
+ #endif
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/ShowNormals.frag b/engine/src/core-data/Common/MatDefs/Misc/ShowNormals.frag
new file mode 100644
index 000000000..93e488230
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/ShowNormals.frag
@@ -0,0 +1,5 @@
+varying vec3 normal;
+
+void main(){
+ gl_FragColor = vec4((normal * vec3(0.5)) + vec3(0.5), 1.0);
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/ShowNormals.j3md b/engine/src/core-data/Common/MatDefs/Misc/ShowNormals.j3md
new file mode 100644
index 000000000..db480b75f
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/ShowNormals.j3md
@@ -0,0 +1,10 @@
+MaterialDef Debug Normals {
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Misc/ShowNormals.vert
+ FragmentShader GLSL100: Common/MatDefs/Misc/ShowNormals.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ }
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/ShowNormals.vert b/engine/src/core-data/Common/MatDefs/Misc/ShowNormals.vert
new file mode 100644
index 000000000..3813043b0
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/ShowNormals.vert
@@ -0,0 +1,11 @@
+uniform mat4 g_WorldViewProjectionMatrix;
+
+attribute vec3 inPosition;
+attribute vec3 inNormal;
+
+varying vec3 normal;
+
+void main(){
+ gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition,1.0);
+ normal = inNormal;
+}
diff --git a/engine/src/core-data/Common/MatDefs/Misc/SimpleTextured.frag b/engine/src/core-data/Common/MatDefs/Misc/SimpleTextured.frag
new file mode 100644
index 000000000..395f3d181
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/SimpleTextured.frag
@@ -0,0 +1,27 @@
+#import "Common/ShaderLib/Texture.glsllib"
+
+varying vec2 texCoord;
+
+uniform sampler2D m_ColorMap;
+
+void main(){
+ //Texture_GetColor(m_ColorMap, texCoord)
+ //vec4 color = texture2D(m_ColorMap, texCoord);
+ //color.rgb *= color.a;
+ //gl_FragColor = vec4(color.a);
+
+ #ifdef NORMAL_LATC
+ vec3 newNorm = vec3(texture2D(m_ColorMap, texCoord).ag, 0.0);
+ newNorm = Common_UnpackNormal(newNorm);
+ newNorm.b = sqrt(1.0 - (newNorm.x * newNorm.x) - (newNorm.y * newNorm.y));
+ newNorm = Common_PackNormal(newNorm);
+ gl_FragColor = vec4(newNorm, 1.0);
+ #elif defined(SHOW_ALPHA)
+ gl_FragColor = vec4(texture2D(m_ColorMap, texCoord).a);
+ #else
+ gl_FragColor = Texture_GetColor(m_ColorMap, texCoord);
+ #endif
+ #ifdef NORMALIZE
+ gl_FragColor = vec4(normalize(gl_FragColor.xyz), gl_FragColor.a);
+ #endif
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/SimpleTextured.j3md b/engine/src/core-data/Common/MatDefs/Misc/SimpleTextured.j3md
new file mode 100644
index 000000000..c18559c70
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/SimpleTextured.j3md
@@ -0,0 +1,30 @@
+MaterialDef Plain Texture {
+
+ MaterialParameters {
+ Texture2D ColorMap
+ Boolean YCoCg
+ Boolean LATC
+ Boolean Normalize
+ Boolean ShowAlpha
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Misc/SimpleTextured.vert
+ FragmentShader GLSL100: Common/MatDefs/Misc/SimpleTextured.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ DXT_YCOCG : YCoCg
+ NORMAL_LATC : LATC
+ NORMALIZE : Normalize
+ SHOW_ALPHA : ShowAlpha
+ }
+ }
+
+ Technique FixedFunc {
+ }
+
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/SimpleTextured.vert b/engine/src/core-data/Common/MatDefs/Misc/SimpleTextured.vert
new file mode 100644
index 000000000..572d84191
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/SimpleTextured.vert
@@ -0,0 +1,11 @@
+uniform mat4 g_WorldViewProjectionMatrix;
+
+attribute vec3 inPosition;
+attribute vec2 inTexCoord;
+
+varying vec2 texCoord;
+
+void main(){
+ gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);
+ texCoord = inTexCoord;
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/Sky.frag b/engine/src/core-data/Common/MatDefs/Misc/Sky.frag
new file mode 100644
index 000000000..1c4dbbe96
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/Sky.frag
@@ -0,0 +1,16 @@
+#import "Common/ShaderLib/Optics.glsllib"
+
+uniform ENVMAP m_Texture;
+
+varying vec3 direction;
+
+void main() {
+ //gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
+
+ //gl_FragDepth = 1.0;
+ vec3 dir = normalize(direction);
+ gl_FragColor = Optics_GetEnvColor(m_Texture, direction);
+ //gl_FragColor = vec4(textureCube(m_Texture, dir).xyz, 1.0);
+ //gl_FragColor = vec4((dir * vec3(0.5)) + vec3(0.5), 1.0);
+}
+
diff --git a/engine/src/core-data/Common/MatDefs/Misc/Sky.j3md b/engine/src/core-data/Common/MatDefs/Misc/Sky.j3md
new file mode 100644
index 000000000..3a9c05ee0
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/Sky.j3md
@@ -0,0 +1,27 @@
+MaterialDef Sky Plane {
+ MaterialParameters {
+ TextureCubeMap Texture
+ Boolean SphereMap
+ Vector3 NormalScale
+ }
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Misc/Sky.vert
+ FragmentShader GLSL100: Common/MatDefs/Misc/Sky.frag
+
+ RenderState {
+ FaceCull Off
+ }
+
+ WorldParameters {
+ NormalMatrix
+ ViewMatrix
+ ProjectionMatrix
+ }
+
+ Defines {
+ SPHERE_MAP : SphereMap
+ }
+ }
+ Technique FixedFunc {
+ }
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/Sky.vert b/engine/src/core-data/Common/MatDefs/Misc/Sky.vert
new file mode 100644
index 000000000..63c58a453
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/Sky.vert
@@ -0,0 +1,24 @@
+uniform mat4 g_ViewMatrix;
+uniform mat4 g_ProjectionMatrix;
+uniform mat3 g_NormalMatrix;
+
+uniform vec3 m_NormalScale;
+
+attribute vec3 inPosition;
+attribute vec3 inNormal;
+
+varying vec3 direction;
+
+void main(){
+ // set w coordinate to 0
+ vec4 pos = vec4(inPosition, 0.0);
+
+ // compute rotation only for view matrix
+ pos = g_ViewMatrix * pos;
+
+ // now find projection
+ pos.w = 1.0;
+ gl_Position = g_ProjectionMatrix * pos;
+
+ direction = normalize(inNormal * m_NormalScale);
+}
diff --git a/engine/src/core-data/Common/MatDefs/Misc/SolidColor.j3md b/engine/src/core-data/Common/MatDefs/Misc/SolidColor.j3md
new file mode 100644
index 000000000..50c8b9d12
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/SolidColor.j3md
@@ -0,0 +1,42 @@
+MaterialDef Solid Color {
+
+ MaterialParameters {
+ Vector4 Color
+
+ // Texture of the glowing parts of the material
+ Texture2D GlowMap
+ // The glow color of the object
+ Color GlowColor
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Misc/Unshaded.vert
+ FragmentShader GLSL100: Common/MatDefs/Misc/Unshaded.frag
+
+ Defines {
+ HAS_COLOR : Color
+ }
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ }
+
+ Technique FixedFunc {
+ }
+
+ Technique Glow {
+
+ VertexShader GLSL100: Common/MatDefs/Misc/Unshaded.vert
+ FragmentShader GLSL100: Common/MatDefs/Light/Glow.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ HAS_GLOWMAP : GlowMap
+ HAS_GLOWCOLOR : GlowColor
+ }
+ }
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/Unshaded.frag b/engine/src/core-data/Common/MatDefs/Misc/Unshaded.frag
new file mode 100644
index 000000000..1f3811ad4
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/Unshaded.frag
@@ -0,0 +1,50 @@
+uniform vec4 m_Color;
+
+#if defined(HAS_COLORMAP) || (defined(HAS_LIGHTMAP) && !defined(SEPERATE_TEXCOORD))
+ #define NEED_TEXCOORD1
+#endif
+
+#ifdef HAS_COLORMAP
+ uniform sampler2D m_ColorMap;
+#endif
+
+#ifdef NEED_TEXCOORD1
+ varying vec2 texCoord1;
+#endif
+
+#ifdef HAS_LIGHTMAP
+ uniform sampler2D m_LightMap;
+ #ifdef SEPERATE_TEXCOORD
+ varying vec2 texCoord2;
+ #endif
+#endif
+
+#ifdef HAS_VERTEXCOLOR
+ varying vec4 vertColor;
+#endif
+
+void main(){
+ vec4 color = vec4(1.0);
+
+ #ifdef HAS_COLORMAP
+ color *= texture2D(m_ColorMap, texCoord1);
+ #endif
+
+ #ifdef HAS_VERTEXCOLOR
+ color *= vertColor;
+ #endif
+
+ #ifdef HAS_COLOR
+ color *= m_Color;
+ #endif
+
+ #ifdef HAS_LIGHTMAP
+ #ifdef SEPERATE_TEXCOORD
+ color.rgb *= texture2D(m_LightMap, texCoord2).rgb;
+ #else
+ color.rgb *= texture2D(m_LightMap, texCoord1).rgb;
+ #endif
+ #endif
+
+ gl_FragColor = color;
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/Unshaded.j3md b/engine/src/core-data/Common/MatDefs/Misc/Unshaded.j3md
new file mode 100644
index 000000000..c05f1aa2d
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/Unshaded.j3md
@@ -0,0 +1,69 @@
+MaterialDef Unshaded {
+
+ MaterialParameters {
+ Texture2D ColorMap
+ Texture2D LightMap
+ Color Color : Color
+ Boolean VertexColor
+ Boolean SeperateTexCoord
+
+ // Texture of the glowing parts of the material
+ Texture2D GlowMap
+ // The glow color of the object
+ Color GlowColor
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Misc/Unshaded.vert
+ FragmentShader GLSL100: Common/MatDefs/Misc/Unshaded.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ SEPERATE_TEXCOORD : SeperateTexCoord
+ HAS_COLORMAP : ColorMap
+ HAS_LIGHTMAP : LightMap
+ HAS_VERTEXCOLOR : VertexColor
+ HAS_COLOR : Color
+ }
+ }
+
+ Technique PreNormalPass {
+
+ VertexShader GLSL100 : Common/MatDefs/SSAO/normal.vert
+ FragmentShader GLSL100 : Common/MatDefs/SSAO/normal.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldViewMatrix
+ NormalMatrix
+ }
+
+ RenderState {
+
+ }
+
+ }
+
+
+ Technique Glow {
+
+ VertexShader GLSL100: Common/MatDefs/Misc/Unshaded.vert
+ FragmentShader GLSL100: Common/MatDefs/Light/Glow.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ HAS_GLOWMAP : GlowMap
+ HAS_GLOWCOLOR : GlowColor
+ }
+ }
+
+ Technique FixedFunc {
+ }
+
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/Unshaded.vert b/engine/src/core-data/Common/MatDefs/Misc/Unshaded.vert
new file mode 100644
index 000000000..39dd97300
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/Unshaded.vert
@@ -0,0 +1,37 @@
+uniform mat4 g_WorldViewProjectionMatrix;
+attribute vec3 inPosition;
+
+#if defined(HAS_COLORMAP) || (defined(HAS_LIGHTMAP) && !defined(SEPERATE_TEXCOORD))
+ #define NEED_TEXCOORD1
+#endif
+
+#ifdef NEED_TEXCOORD1
+ attribute vec2 inTexCoord;
+ varying vec2 texCoord1;
+#endif
+
+#ifdef SEPERATE_TEXCOORD
+ attribute vec2 inTexCoord2;
+ varying vec2 texCoord2;
+#endif
+
+#ifdef HAS_VERTEXCOLOR
+ attribute vec4 inColor;
+ varying vec4 vertColor;
+#endif
+
+void main(){
+ #ifdef NEED_TEXCOORD1
+ texCoord1 = inTexCoord;
+ #endif
+
+ #ifdef SEPERATE_TEXCOORD
+ texCoord2 = inTexCoord2;
+ #endif
+
+ #ifdef HAS_VERTEXCOLOR
+ vertColor = inColor;
+ #endif
+
+ gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/VertexColor.j3md b/engine/src/core-data/Common/MatDefs/Misc/VertexColor.j3md
new file mode 100644
index 000000000..09b3e7bb7
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/VertexColor.j3md
@@ -0,0 +1,16 @@
+MaterialDef Vertex Color {
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Misc/Unshaded.vert
+ FragmentShader GLSL100: Common/MatDefs/Misc/Unshaded.frag
+
+ Defines {
+ HAS_VERTEXCOLOR
+ }
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/WireColor.j3md b/engine/src/core-data/Common/MatDefs/Misc/WireColor.j3md
new file mode 100644
index 000000000..35debff16
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/WireColor.j3md
@@ -0,0 +1,36 @@
+MaterialDef Wire Color {
+
+ MaterialParameters {
+ Vector4 Color : Color
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Misc/Unshaded.vert
+ FragmentShader GLSL100: Common/MatDefs/Misc/Unshaded.frag
+
+ RenderState {
+ FaceCull Off
+ Blend Alpha
+ AlphaTestFalloff 0.01
+ Wireframe On
+ }
+
+ Defines {
+ HAS_COLOR : Color
+ }
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ }
+
+ Technique FixedFunc {
+ RenderState {
+ FaceCull Off
+ Blend Alpha
+ AlphaTestFalloff 0.01
+ Wireframe On
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Post/BloomExtract.j3md b/engine/src/core-data/Common/MatDefs/Post/BloomExtract.j3md
new file mode 100644
index 000000000..76614ccc7
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Post/BloomExtract.j3md
@@ -0,0 +1,43 @@
+MaterialDef Bloom {
+
+ MaterialParameters {
+ Int NumSamples
+ Texture2D Texture
+ Float ExposurePow
+ Float ExposureCutoff
+ Boolean Extract
+ Texture2D GlowMap
+ }
+
+ Technique {
+ VertexShader GLSL150: Common/MatDefs/Post/Post15.vert
+ FragmentShader GLSL150: Common/MatDefs/Post/bloomExtract15.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ HAS_GLOWMAP : GlowMap
+ DO_EXTRACT : Extract
+ RESOLVE_MS : NumSamples
+ }
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Post/Post.vert
+ FragmentShader GLSL100: Common/MatDefs/Post/bloomExtract.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ HAS_GLOWMAP : GlowMap
+ DO_EXTRACT : Extract
+ }
+ }
+
+ Technique FixedFunc {
+ }
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Post/BloomFinal.j3md b/engine/src/core-data/Common/MatDefs/Post/BloomFinal.j3md
new file mode 100644
index 000000000..68f8c287d
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Post/BloomFinal.j3md
@@ -0,0 +1,36 @@
+MaterialDef Bloom Final {
+
+ MaterialParameters {
+ Int NumSamples
+ Texture2D Texture
+ Texture2D BloomTex
+ Float BloomIntensity
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Post/Post15.vert
+ FragmentShader GLSL150: Common/MatDefs/Post/bloomFinal15.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ RESOLVE_MS : NumSamples
+ }
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Post/Post.vert
+ FragmentShader GLSL100: Common/MatDefs/Post/bloomFinal.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ }
+
+
+
+ Technique FixedFunc {
+ }
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Post/CartoonEdge.frag b/engine/src/core-data/Common/MatDefs/Post/CartoonEdge.frag
new file mode 100644
index 000000000..7ee7f789f
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Post/CartoonEdge.frag
@@ -0,0 +1,55 @@
+uniform vec4 m_EdgeColor;
+
+uniform float m_EdgeWidth;
+uniform float m_EdgeIntensity;
+
+uniform float m_NormalThreshold;
+uniform float m_DepthThreshold;
+
+uniform float m_NormalSensitivity;
+uniform float m_DepthSensitivity;
+
+varying vec2 texCoord;
+
+uniform sampler2D m_Texture;
+uniform sampler2D m_NormalsTexture;
+uniform sampler2D m_DepthTexture;
+
+uniform vec2 g_Resolution;
+
+vec4 fetchNormalDepth(vec2 tc){
+ vec4 nd;
+ nd.xyz = texture2D(m_NormalsTexture, tc).rgb;
+ nd.w = texture2D(m_DepthTexture, tc).r;
+ return nd;
+}
+
+void main(){
+ vec3 color = texture2D(m_Texture, texCoord).rgb;
+
+ vec2 edgeOffset = vec2(m_EdgeWidth) / g_Resolution;
+
+ vec4 n1 = fetchNormalDepth(texCoord + vec2(-1.0, -1.0) * edgeOffset);
+ vec4 n2 = fetchNormalDepth(texCoord + vec2( 1.0, 1.0) * edgeOffset);
+ vec4 n3 = fetchNormalDepth(texCoord + vec2(-1.0, 1.0) * edgeOffset);
+ vec4 n4 = fetchNormalDepth(texCoord + vec2( 1.0, -1.0) * edgeOffset);
+
+ // Work out how much the normal and depth values are changing.
+ vec4 diagonalDelta = abs(n1 - n2) + abs(n3 - n4);
+
+ float normalDelta = dot(diagonalDelta.xyz, vec3(1.0));
+ float depthDelta = diagonalDelta.w;
+
+ // Filter out very small changes, in order to produce nice clean results.
+ normalDelta = clamp((normalDelta - m_NormalThreshold) * m_NormalSensitivity, 0.0, 1.0);
+ depthDelta = clamp((depthDelta - m_DepthThreshold) * m_DepthSensitivity, 0.0, 1.0);
+
+ // Does this pixel lie on an edge?
+ float edgeAmount = clamp(normalDelta + depthDelta, 0.0, 1.0) * m_EdgeIntensity;
+
+ // Apply the edge detection result to the main scene color.
+ //color *= (1.0 - edgeAmount);
+ color = mix (color,m_EdgeColor.rgb,edgeAmount);
+
+ gl_FragColor = vec4(color, 1.0);
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Post/CartoonEdge.j3md b/engine/src/core-data/Common/MatDefs/Post/CartoonEdge.j3md
new file mode 100644
index 000000000..687193e8e
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Post/CartoonEdge.j3md
@@ -0,0 +1,48 @@
+MaterialDef Cartoon Edge {
+
+ MaterialParameters {
+ Int NumSamples
+ Int NumSamplesDepth
+ Texture2D Texture
+ Texture2D NormalsTexture
+ Texture2D DepthTexture
+ Color EdgeColor
+ Float EdgeWidth
+ Float EdgeIntensity
+ Float NormalThreshold
+ Float DepthThreshold
+ Float NormalSensitivity
+ Float DepthSensitivity
+ }
+
+ Technique {
+ VertexShader GLSL150: Common/MatDefs/Post/Post15.vert
+ FragmentShader GLSL150: Common/MatDefs/Post/CartoonEdge15.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldViewMatrix
+ Resolution
+ }
+
+ Defines {
+ RESOLVE_MS : NumSamples
+ RESOLVE_DEPTH_MS : NumSamplesDepth
+ }
+ }
+
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Post/Post.vert
+ FragmentShader GLSL100: Common/MatDefs/Post/CartoonEdge.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldViewMatrix
+ Resolution
+ }
+ }
+
+ Technique FixedFunc {
+ }
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Post/CartoonEdge15.frag b/engine/src/core-data/Common/MatDefs/Post/CartoonEdge15.frag
new file mode 100644
index 000000000..3c3921a98
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Post/CartoonEdge15.frag
@@ -0,0 +1,57 @@
+#import "Common/ShaderLib/MultiSample.glsllib"
+
+uniform COLORTEXTURE m_Texture;
+uniform DEPTHTEXTURE m_DepthTexture;
+
+uniform sampler2D m_NormalsTexture;
+uniform vec2 g_Resolution;
+
+uniform vec4 m_EdgeColor;
+
+uniform float m_EdgeWidth;
+uniform float m_EdgeIntensity;
+
+uniform float m_NormalThreshold;
+uniform float m_DepthThreshold;
+
+uniform float m_NormalSensitivity;
+uniform float m_DepthSensitivity;
+
+in vec2 texCoord;
+out vec4 outFragColor;
+
+vec4 fetchNormalDepth(vec2 tc){
+ vec4 nd;
+ nd.xyz = texture2D(m_NormalsTexture, tc).rgb;
+ nd.w = fetchTextureSample(m_DepthTexture, tc,0).r;
+ return nd;
+}
+
+void main(){
+ vec3 color = getColor(m_Texture, texCoord).rgb;
+
+ vec2 edgeOffset = vec2(m_EdgeWidth) / textureSize(m_NormalsTexture, 0);
+ vec4 n1 = fetchNormalDepth(texCoord + vec2(-1.0, -1.0) * edgeOffset);
+ vec4 n2 = fetchNormalDepth(texCoord + vec2( 1.0, 1.0) * edgeOffset);
+ vec4 n3 = fetchNormalDepth(texCoord + vec2(-1.0, 1.0) * edgeOffset);
+ vec4 n4 = fetchNormalDepth(texCoord + vec2( 1.0, -1.0) * edgeOffset);
+
+ // Work out how much the normal and depth values are changing.
+ vec4 diagonalDelta = abs(n1 - n2) + abs(n3 - n4);
+
+ float normalDelta = dot(diagonalDelta.xyz, vec3(1.0));
+ float depthDelta = diagonalDelta.w;
+
+ // Filter out very small changes, in order to produce nice clean results.
+ normalDelta = clamp((normalDelta - m_NormalThreshold) * m_NormalSensitivity, 0.0, 1.0);
+ depthDelta = clamp((depthDelta - m_DepthThreshold) * m_DepthSensitivity, 0.0, 1.0);
+
+ // Does this pixel lie on an edge?
+ float edgeAmount = clamp(normalDelta + depthDelta, 0.0, 1.0) * m_EdgeIntensity;
+
+ // Apply the edge detection result to the main scene color.
+ //color *= (1.0 - edgeAmount);
+ color = mix (color,m_EdgeColor.rgb,edgeAmount);
+
+ outFragColor = vec4(color, 1.0);
+}
diff --git a/engine/src/core-data/Common/MatDefs/Post/DepthOfField.frag b/engine/src/core-data/Common/MatDefs/Post/DepthOfField.frag
new file mode 100644
index 000000000..658da546f
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Post/DepthOfField.frag
@@ -0,0 +1,89 @@
+uniform sampler2D m_Texture;
+uniform sampler2D m_DepthTexture;
+varying vec2 texCoord;
+
+uniform float m_FocusRange;
+uniform float m_FocusDistance;
+uniform float m_XScale;
+uniform float m_YScale;
+
+vec2 m_NearFar = vec2( 0.1, 1000.0 );
+
+void main() {
+
+ vec4 texVal = texture2D( m_Texture, texCoord );
+
+ float zBuffer = texture2D( m_DepthTexture, texCoord ).r;
+
+ //
+ // z_buffer_value = a + b / z;
+ //
+ // Where:
+ // a = zFar / ( zFar - zNear )
+ // b = zFar * zNear / ( zNear - zFar )
+ // z = distance from the eye to the object
+ //
+ // Which means:
+ // zb - a = b / z;
+ // z * (zb - a) = b
+ // z = b / (zb - a)
+ //
+ float a = m_NearFar.y / (m_NearFar.y - m_NearFar.x);
+ float b = m_NearFar.y * m_NearFar.x / (m_NearFar.x - m_NearFar.y);
+ float z = b / (zBuffer - a);
+
+ // Above could be the same for any depth-based filter
+
+ // We want to be purely focused right at
+ // m_FocusDistance and be purely unfocused
+ // at +/- m_FocusRange to either side of that.
+ float unfocus = min( 1.0, abs( z - m_FocusDistance ) / m_FocusRange );
+
+ if( unfocus < 0.2 ) {
+ // If we are mostly in focus then don't bother with the
+ // convolution filter
+ gl_FragColor = texVal;
+ } else {
+ // Perform a wide convolution filter and we scatter it
+ // a bit to avoid some texture look-ups. Instead of
+ // a full 5x5 (25-1 lookups) we'll skip every other one
+ // to only perform 12.
+ // 1 0 1 0 1
+ // 0 1 0 1 0
+ // 1 0 x 0 1
+ // 0 1 0 1 0
+ // 1 0 1 0 1
+ //
+ // You can get away with 8 just around the outside but
+ // it looks more jittery to me.
+
+ vec4 sum = vec4(0.0);
+
+ float x = texCoord.x;
+ float y = texCoord.y;
+
+ float xScale = m_XScale;
+ float yScale = m_YScale;
+
+ // In order from lower left to right, depending on how you look at it
+ sum += texture2D( m_Texture, vec2(x - 2.0 * xScale, y - 2.0 * yScale) );
+ sum += texture2D( m_Texture, vec2(x - 0.0 * xScale, y - 2.0 * yScale) );
+ sum += texture2D( m_Texture, vec2(x + 2.0 * xScale, y - 2.0 * yScale) );
+ sum += texture2D( m_Texture, vec2(x - 1.0 * xScale, y - 1.0 * yScale) );
+ sum += texture2D( m_Texture, vec2(x + 1.0 * xScale, y - 1.0 * yScale) );
+ sum += texture2D( m_Texture, vec2(x - 2.0 * xScale, y - 0.0 * yScale) );
+ sum += texture2D( m_Texture, vec2(x + 2.0 * xScale, y - 0.0 * yScale) );
+ sum += texture2D( m_Texture, vec2(x - 1.0 * xScale, y + 1.0 * yScale) );
+ sum += texture2D( m_Texture, vec2(x + 1.0 * xScale, y + 1.0 * yScale) );
+ sum += texture2D( m_Texture, vec2(x - 2.0 * xScale, y + 2.0 * yScale) );
+ sum += texture2D( m_Texture, vec2(x - 0.0 * xScale, y + 2.0 * yScale) );
+ sum += texture2D( m_Texture, vec2(x + 2.0 * xScale, y + 2.0 * yScale) );
+
+ sum = sum / 12.0;
+
+ gl_FragColor = mix( texVal, sum, unfocus );
+
+ // I used this for debugging the range
+ // gl_FragColor.r = unfocus;
+}
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Post/DepthOfField.j3md b/engine/src/core-data/Common/MatDefs/Post/DepthOfField.j3md
new file mode 100644
index 000000000..db30a0975
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Post/DepthOfField.j3md
@@ -0,0 +1,25 @@
+MaterialDef Depth Of Field {
+
+ MaterialParameters {
+ Int NumSamples
+ Int NumSamplesDepth
+ Texture2D Texture
+ Texture2D DepthTexture
+ Float FocusRange;
+ Float FocusDistance;
+ Float XScale;
+ Float YScale;
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Post/Post.vert
+ FragmentShader GLSL100: Common/MatDefs/Post/DepthOfField.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ }
+
+ Technique FixedFunc {
+ }
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Post/Fade.frag b/engine/src/core-data/Common/MatDefs/Post/Fade.frag
new file mode 100644
index 000000000..b616239d0
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Post/Fade.frag
@@ -0,0 +1,11 @@
+uniform sampler2D m_Texture;
+varying vec2 texCoord;
+
+uniform float m_Value;
+
+void main() {
+ vec4 texVal = texture2D(m_Texture, texCoord);
+
+ gl_FragColor = texVal * m_Value;
+
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Post/Fade.j3md b/engine/src/core-data/Common/MatDefs/Post/Fade.j3md
new file mode 100644
index 000000000..e93b276b9
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Post/Fade.j3md
@@ -0,0 +1,34 @@
+MaterialDef Fade {
+
+ MaterialParameters {
+ Int NumSamples
+ Texture2D Texture
+ Float Value
+ }
+
+ Technique {
+ VertexShader GLSL150: Common/MatDefs/Post/Post15.vert
+ FragmentShader GLSL150: Common/MatDefs/Post/Fade15.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ RESOLVE_MS : NumSamples
+ }
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Post/Post.vert
+ FragmentShader GLSL100: Common/MatDefs/Post/Fade.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ }
+
+ Technique FixedFunc {
+ }
+
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Post/Fade15.frag b/engine/src/core-data/Common/MatDefs/Post/Fade15.frag
new file mode 100644
index 000000000..c99de34ad
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Post/Fade15.frag
@@ -0,0 +1,11 @@
+#import "Common/ShaderLib/MultiSample.glsllib"
+
+uniform COLORTEXTURE m_Texture;
+uniform float m_Value;
+
+in vec2 texCoord;
+
+void main() {
+ vec4 texVal = getColor(m_Texture, texCoord);
+ gl_FragColor = texVal * m_Value;
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Post/Fog.frag b/engine/src/core-data/Common/MatDefs/Post/Fog.frag
new file mode 100644
index 000000000..7321b1516
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Post/Fog.frag
@@ -0,0 +1,21 @@
+uniform sampler2D m_Texture;
+uniform sampler2D m_DepthTexture;
+varying vec2 texCoord;
+
+uniform vec4 m_FogColor;
+uniform float m_FogDensity;
+uniform float m_FogDistance;
+
+vec2 m_FrustumNearFar=vec2(1.0,m_FogDistance);
+const float LOG2 = 1.442695;
+
+void main() {
+ vec4 texVal = texture2D(m_Texture, texCoord);
+ float fogVal =texture2D(m_DepthTexture,texCoord).r;
+ float depth= (2.0 * m_FrustumNearFar.x) / (m_FrustumNearFar.y + m_FrustumNearFar.x - fogVal* (m_FrustumNearFar.y-m_FrustumNearFar.x));
+
+ float fogFactor = exp2( -m_FogDensity * m_FogDensity * depth * depth * LOG2 );
+ fogFactor = clamp(fogFactor, 0.0, 1.0);
+ gl_FragColor =mix(m_FogColor,texVal,fogFactor);
+
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Post/Fog.j3md b/engine/src/core-data/Common/MatDefs/Post/Fog.j3md
new file mode 100644
index 000000000..e5d6c8db7
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Post/Fog.j3md
@@ -0,0 +1,39 @@
+MaterialDef Fade {
+
+ MaterialParameters {
+ Int NumSamples
+ Int NumSamplesDepth
+ Texture2D Texture
+ Texture2D DepthTexture
+ Vector4 FogColor;
+ Float FogDensity;
+ Float FogDistance;
+ }
+
+ Technique {
+ VertexShader GLSL150: Common/MatDefs/Post/Post15.vert
+ FragmentShader GLSL150: Common/MatDefs/Post/Fog15.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ RESOLVE_MS : NumSamples
+ RESOLVE_DEPTH_MS : NumSamplesDepth
+ }
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Post/Post.vert
+ FragmentShader GLSL100: Common/MatDefs/Post/Fog.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ }
+
+ Technique FixedFunc {
+ }
+
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Post/Fog15.frag b/engine/src/core-data/Common/MatDefs/Post/Fog15.frag
new file mode 100644
index 000000000..65a340723
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Post/Fog15.frag
@@ -0,0 +1,24 @@
+#import "Common/ShaderLib/MultiSample.glsllib"
+
+uniform COLORTEXTURE m_Texture;
+uniform DEPTHTEXTURE m_DepthTexture;
+
+uniform vec4 m_FogColor;
+uniform float m_FogDensity;
+uniform float m_FogDistance;
+
+in vec2 texCoord;
+
+vec2 m_FrustumNearFar=vec2(1.0,m_FogDistance);
+const float LOG2 = 1.442695;
+
+void main() {
+ vec4 texVal = getColor(m_Texture, texCoord);
+ float fogVal = getDepth(m_DepthTexture,texCoord).r;
+ float depth= (2.0 * m_FrustumNearFar.x) / (m_FrustumNearFar.y + m_FrustumNearFar.x - fogVal* (m_FrustumNearFar.y-m_FrustumNearFar.x));
+
+ float fogFactor = exp2( -m_FogDensity * m_FogDensity * depth * depth * LOG2 );
+ fogFactor = clamp(fogFactor, 0.0, 1.0);
+ gl_FragColor =mix(m_FogColor,texVal,fogFactor);
+
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Post/LightScattering.frag b/engine/src/core-data/Common/MatDefs/Post/LightScattering.frag
new file mode 100644
index 000000000..683bbeaa7
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Post/LightScattering.frag
@@ -0,0 +1,36 @@
+uniform sampler2D m_Texture;
+uniform sampler2D m_DepthTexture;
+uniform int m_NbSamples;
+uniform float m_BlurStart;
+uniform float m_BlurWidth;
+uniform float m_LightDensity;
+uniform bool m_Display;
+
+varying vec2 lightPos;
+varying vec2 texCoord;
+
+void main(void)
+{
+ if(m_Display){
+
+ vec4 colorRes= texture2D(m_Texture,texCoord);
+ float factor=(m_BlurWidth/float(m_NbSamples-1.0));
+ float scale;
+ vec2 texCoo=texCoord-lightPos;
+ vec2 scaledCoord;
+ vec4 res = vec4(0.0);
+ for(int i=0; i 0.0)
+ return 1.0;
+
+ ivec2 texSize = textureSize(tex, 0);
+ vec2 pixSize = 1.0 / vec2(texSize);
+
+ float shadow = 0.0;
+ ivec2 o = ivec2(mod(floor(gl_FragCoord.xy), 2.0));
+ shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy+pixSize*(vec2(-1.5, 1.5)+o), projCoord.zw));
+ shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy+pixSize*(vec2( 0.5, 1.5)+o), projCoord.zw));
+ shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy+pixSize*(vec2(-1.5, -0.5)+o), projCoord.zw));
+ shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy+pixSize*(vec2( 0.5, -0.5)+o), projCoord.zw));
+ shadow *= 0.25;
+ return shadow;
+}
+
+float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){
+ float border = Shadow_BorderCheck(projCoord.xy);
+ if (border > 0.0)
+ return 1.0;
+
+ ivec2 texSize = textureSize(tex, 0);
+ #ifdef GL_ARB_texture_gather
+ vec4 coord = vec4(projCoord.xyz / projCoord.w,0.0);
+ vec4 gather = SHADOWGATHER(tex, coord);
+ #else
+ vec4 gather = vec4(0.0);
+ gather.x = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(0, 0));
+ gather.y = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(1, 0));
+ gather.z = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(0, 1));
+ gather.w = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(1, 1));
+ #endif
+
+ vec2 f = fract( projCoord.xy * texSize );
+ vec2 mx = mix( gather.xz, gather.yw, f.x );
+ return mix( mx.x, mx.y, f.y );
+}
+
+float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){
+ float pixSize = 1.0 / textureSize(tex,0).x;
+
+ float shadow = 0.0;
+ float border = Shadow_BorderCheck(projCoord.xy);
+ if (border > 0.0)
+ return 1.0;
+
+ float bound = KERNEL * 0.5 - 0.5;
+ bound *= PCFEDGE;
+ for (float y = -bound; y <= bound; y += PCFEDGE){
+ for (float x = -bound; x <= bound; x += PCFEDGE){
+ vec4 coord = vec4(projCoord.xy + vec2(x,y) * pixSize, projCoord.zw);
+ shadow += SHADOWCOMPARE(tex, coord);
+ }
+ }
+
+ shadow = shadow / (KERNEL * KERNEL);
+ return shadow;
+}
+
+void main(){
+ float shadow = 0.0;
+
+ if(shadowPosition < m_Splits.x){
+ shadow = GETSHADOW(m_ShadowMap0, projCoord0);
+ }else if( shadowPosition < m_Splits.y){
+ shadow = GETSHADOW(m_ShadowMap1, projCoord1);
+ }else if( shadowPosition < m_Splits.z){
+ shadow = GETSHADOW(m_ShadowMap2, projCoord2);
+ }else if( shadowPosition < m_Splits.w){
+ shadow = GETSHADOW(m_ShadowMap3, projCoord3);
+ }
+
+ shadow = shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity);
+ outFragColor = vec4(shadow, shadow, shadow, 1.0);
+}
+
diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PreShadow.frag b/engine/src/core-data/Common/MatDefs/Shadow/PreShadow.frag
new file mode 100644
index 000000000..5d957e908
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Shadow/PreShadow.frag
@@ -0,0 +1,15 @@
+varying vec2 texCoord;
+
+#ifdef DIFFUSEMAP_ALPHA
+uniform sampler2D m_DiffuseMap;
+#endif
+
+
+void main(){
+ #ifdef DIFFUSEMAP_ALPHA
+ if (texture2D(m_DiffuseMap, texCoord).a <= 0.50)
+ discard;
+ #endif
+
+ gl_FragColor = vec4(1.0);
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PreShadow.j3md b/engine/src/core-data/Common/MatDefs/Shadow/PreShadow.j3md
new file mode 100644
index 000000000..070949f42
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Shadow/PreShadow.j3md
@@ -0,0 +1,19 @@
+MaterialDef Pre Shadow {
+ Technique {
+ VertexShader GLSL100 : Common/MatDefs/Shadow/PreShadow.vert
+ FragmentShader GLSL100 : Common/MatDefs/Shadow/PreShadow.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldViewMatrix
+ }
+
+ RenderState {
+ FaceCull Off
+ DepthTest On
+ DepthWrite On
+ PolyOffset 5 0
+ ColorWrite Off
+ }
+ }
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PreShadow.vert b/engine/src/core-data/Common/MatDefs/Shadow/PreShadow.vert
new file mode 100644
index 000000000..fdd3a2511
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Shadow/PreShadow.vert
@@ -0,0 +1,12 @@
+attribute vec4 inPosition;
+attribute vec2 inTexCoord;
+
+uniform mat4 g_WorldViewProjectionMatrix;
+uniform mat4 g_WorldViewMatrix;
+
+varying vec2 texCoord;
+
+void main(){
+ gl_Position = g_WorldViewProjectionMatrix * inPosition;
+ texCoord = inTexCoord;
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Water/SimpleWater.j3md b/engine/src/core-data/Common/MatDefs/Water/SimpleWater.j3md
new file mode 100644
index 000000000..12883242f
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Water/SimpleWater.j3md
@@ -0,0 +1,34 @@
+MaterialDef Simple Water {
+
+ MaterialParameters {
+ Texture2D water_reflection
+ Texture2D water_refraction
+ Texture2D water_depthmap
+ Texture2D water_normalmap
+ Texture2D water_dudvmap
+ Vector4 waterColor
+ Vector3 lightPos
+ Float time
+ Float waterDepth
+ Vector4 distortionScale
+ Vector4 distortionMix
+ Vector4 texScale
+ Vector2 FrustumNearFar
+ Float waterTransparency
+ }
+
+ Technique {
+ VertexShader GLSL100: Common/MatDefs/Water/simple_water.vert
+ FragmentShader GLSL100: Common/MatDefs/Water/simple_water.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldViewMatrix
+ Resolution
+ CameraPosition
+ }
+ }
+
+ Technique FixedFunc {
+ }
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Water/Textures/dudv_map.jpg b/engine/src/core-data/Common/MatDefs/Water/Textures/dudv_map.jpg
new file mode 100644
index 000000000..a2734bbdf
Binary files /dev/null and b/engine/src/core-data/Common/MatDefs/Water/Textures/dudv_map.jpg differ
diff --git a/engine/src/core-data/Common/MatDefs/Water/Textures/foam.jpg b/engine/src/core-data/Common/MatDefs/Water/Textures/foam.jpg
new file mode 100644
index 000000000..fc17ac291
Binary files /dev/null and b/engine/src/core-data/Common/MatDefs/Water/Textures/foam.jpg differ
diff --git a/engine/src/core-data/Common/MatDefs/Water/Textures/foam2.jpg b/engine/src/core-data/Common/MatDefs/Water/Textures/foam2.jpg
new file mode 100644
index 000000000..23a4d0e21
Binary files /dev/null and b/engine/src/core-data/Common/MatDefs/Water/Textures/foam2.jpg differ
diff --git a/engine/src/core-data/Common/MatDefs/Water/Textures/foam3.jpg b/engine/src/core-data/Common/MatDefs/Water/Textures/foam3.jpg
new file mode 100644
index 000000000..f8b5e0d0e
Binary files /dev/null and b/engine/src/core-data/Common/MatDefs/Water/Textures/foam3.jpg differ
diff --git a/engine/src/core-data/Common/MatDefs/Water/Textures/gradient_map.jpg b/engine/src/core-data/Common/MatDefs/Water/Textures/gradient_map.jpg
new file mode 100644
index 000000000..d114d5f97
Binary files /dev/null and b/engine/src/core-data/Common/MatDefs/Water/Textures/gradient_map.jpg differ
diff --git a/engine/src/core-data/Common/MatDefs/Water/Textures/heightmap.jpg b/engine/src/core-data/Common/MatDefs/Water/Textures/heightmap.jpg
new file mode 100644
index 000000000..cfb58466c
Binary files /dev/null and b/engine/src/core-data/Common/MatDefs/Water/Textures/heightmap.jpg differ
diff --git a/engine/src/core-data/Common/MatDefs/Water/Water.frag b/engine/src/core-data/Common/MatDefs/Water/Water.frag
new file mode 100644
index 000000000..35eb9f00c
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Water/Water.frag
@@ -0,0 +1,297 @@
+// Water pixel shader
+// Copyright (C) JMonkeyEngine 3.0
+// by Remy Bouquet (nehon) for JMonkeyEngine 3.0
+// original HLSL version by Wojciech Toman 2009
+
+uniform sampler2D m_HeightMap;
+uniform sampler2D m_Texture;
+uniform sampler2D m_DepthTexture;
+uniform sampler2D m_NormalMap;
+uniform sampler2D m_FoamMap;
+uniform sampler2D m_ReflectionMap;
+
+uniform mat4 m_ViewProjectionMatrixInverse;
+uniform mat4 m_TextureProjMatrix;
+uniform vec3 m_CameraPosition;
+
+uniform float m_WaterHeight;
+uniform float m_Time;
+uniform float m_WaterTransparency;
+uniform float m_NormalScale;
+uniform float m_R0;
+uniform float m_MaxAmplitude;
+uniform vec3 m_LightDir;
+uniform vec4 m_LightColor;
+uniform float m_ShoreHardness;
+uniform float m_FoamHardness;
+uniform float m_RefractionStrength;
+uniform vec3 m_FoamExistence;
+uniform vec3 m_ColorExtinction;
+uniform float m_Shininess;
+uniform vec4 m_WaterColor;
+uniform vec4 m_DeepWaterColor;
+uniform vec2 m_WindDirection;
+uniform float m_SunScale;
+uniform float m_WaveScale;
+
+vec2 scale = vec2(m_WaveScale, m_WaveScale);
+float refractionScale = m_WaveScale;
+
+// Modifies 4 sampled normals. Increase first values to have more
+// smaller "waves" or last to have more bigger "waves"
+const vec4 normalModifier = vec4(3.0, 2.0, 4.0, 10.0);
+// Strength of displacement along normal.
+// Strength of displacement along normal.
+uniform float m_ReflectionDisplace;
+// Water transparency along eye vector.
+const float visibility = 3.0;
+// foam intensity
+uniform float m_FoamIntensity ;
+
+varying vec2 texCoord;
+
+mat3 MatrixInverse(in mat3 inMatrix){
+ float det = dot(cross(inMatrix[0], inMatrix[1]), inMatrix[2]);
+ mat3 T = transpose(inMatrix);
+ return mat3(cross(T[1], T[2]),
+ cross(T[2], T[0]),
+ cross(T[0], T[1])) / det;
+}
+
+
+mat3 computeTangentFrame(in vec3 N, in vec3 P, in vec2 UV) {
+ vec3 dp1 = dFdx(P);
+ vec3 dp2 = dFdy(P);
+ vec2 duv1 = dFdx(UV);
+ vec2 duv2 = dFdy(UV);
+
+ // solve the linear system
+ mat3 M = mat3(dp1, dp2, cross(dp1, dp2));
+ //vec3 dp1xdp2 = cross(dp1, dp2);
+ mat3 inverseM = MatrixInverse(M);
+ //mat2x3 inverseM = mat2x3(cross(dp2, dp1xdp2), cross(dp1xdp2, dp1));
+
+ vec3 T = inverseM * vec3(duv1.x, duv2.x, 0.0);
+ vec3 B = inverseM * vec3(duv1.y, duv2.y, 0.0);
+
+ //vec3 T = inverseM * vec2(duv1.x, duv2.x);
+ //vec3 B = inverseM * vec2(duv1.y, duv2.y);
+
+ // construct tangent frame
+ float maxLength = max(length(T), length(B));
+ T = T / maxLength;
+ B = B / maxLength;
+
+ //vec3 tangent = normalize(T);
+ //vec3 binormal = normalize(B);
+
+ return mat3(T, B, N);
+}
+
+float saturate(in float val){
+ return clamp(val,0.0,1.0);
+}
+
+vec3 saturate(in vec3 val){
+ return clamp(val,vec3(0.0),vec3(1.0));
+}
+
+
+vec3 getPosition(in float depth, in vec2 uv){
+ vec4 pos = vec4(uv, depth, 1.0) * 2.0 - 1.0;
+ pos = m_ViewProjectionMatrixInverse * pos;
+ return pos.xyz / pos.w;
+}
+
+// Function calculating fresnel term.
+// - normal - normalized normal vector
+// - eyeVec - normalized eye vector
+float fresnelTerm(in vec3 normal,in vec3 eyeVec){
+ float angle = 1.0 - saturate(dot(normal, eyeVec));
+ float fresnel = angle * angle;
+ fresnel = fresnel * fresnel;
+ fresnel = fresnel * angle;
+ return saturate(fresnel * (1.0 - saturate(m_R0)) + m_R0 - m_RefractionStrength);
+}
+
+void main(){
+ float sceneDepth = texture2D(m_DepthTexture, texCoord).r;
+ float isAtFarPlane = step(0.99998, sceneDepth);
+
+ vec3 color2 = texture2D(m_Texture, texCoord).rgb;
+ vec3 color = color2;
+
+ vec3 position = getPosition(sceneDepth,texCoord);
+
+ float level = m_WaterHeight;
+
+ // If we are underwater let's leave out complex computations
+ if(level >= m_CameraPosition.y){
+ gl_FragColor = vec4(color2, 1.0);
+ return;
+ }
+
+ //#ifndef ENABLE_RIPPLES
+ // This optimization won't work on NVIDIA cards if ripples are enabled
+ if(position.y > level + m_MaxAmplitude + isAtFarPlane * 100.0){
+ gl_FragColor = vec4(color2, 1.0);
+ return;
+ }
+ //#endif
+
+ vec3 eyeVec = position - m_CameraPosition;
+ float diff = level - position.y;
+ float cameraDepth = m_CameraPosition.y - position.y;
+
+ // Find intersection with water surface
+ vec3 eyeVecNorm = normalize(eyeVec);
+ float t = (level - m_CameraPosition.y) / eyeVecNorm.y;
+ vec3 surfacePoint = m_CameraPosition + eyeVecNorm * t;
+
+ vec2 texC;
+ int samples = 1;
+ #ifdef ENABLE_HQ_SHORELINE
+ samples = 10;
+ #endif
+ float biasFactor = 1.0/samples;
+ for (int i = 0; i < samples; i++){
+ texC = (surfacePoint.xz + eyeVecNorm.xz * biasFactor) * scale + m_Time * 0.03 * m_WindDirection;
+
+ float bias = texture2D(m_HeightMap, texC).r;
+
+ bias *= biasFactor;
+ level += bias * m_MaxAmplitude;
+ t = (level - m_CameraPosition.y) / eyeVecNorm.y;
+ surfacePoint = m_CameraPosition + eyeVecNorm * t;
+ }
+
+ float depth = length(position - surfacePoint);
+ float depth2 = surfacePoint.y - position.y;
+
+ // XXX: HACK ALERT: Increase water depth to infinity if at far plane
+ // Prevents "foam on horizon" issue
+ // For best results, replace the "100.0" below with the
+ // highest value in the m_ColorExtinction vec3
+ depth += isAtFarPlane * 100.0;
+ depth2 += isAtFarPlane * 100.0;
+
+ eyeVecNorm = normalize(m_CameraPosition - surfacePoint);
+
+ // Find normal of water surface
+ float normal1 = texture2D(m_HeightMap, (texC + vec2(-1.0, 0.0) / 256.0)).r;
+ float normal2 = texture2D(m_HeightMap, (texC + vec2(1.0, 0.0) / 256.0)).r;
+ float normal3 = texture2D(m_HeightMap, (texC + vec2(0.0, -1.0) / 256.0)).r;
+ float normal4 = texture2D(m_HeightMap, (texC + vec2(0.0, 1.0) / 256.0)).r;
+
+ vec3 myNormal = normalize(vec3((normal1 - normal2) * m_MaxAmplitude,m_NormalScale,(normal3 - normal4) * m_MaxAmplitude));
+ vec3 normal = vec3(0.0);
+
+ #ifdef ENABLE_RIPPLES
+ texC = surfacePoint.xz * 0.8 + m_WindDirection * m_Time* 1.6;
+ mat3 tangentFrame = computeTangentFrame(myNormal, eyeVecNorm, texC);
+ vec3 normal0a = normalize(tangentFrame*(2.0 * texture2D(m_NormalMap, texC).xyz - 1.0));
+
+ texC = surfacePoint.xz * 0.4 + m_WindDirection * m_Time* 0.8;
+ tangentFrame = computeTangentFrame(myNormal, eyeVecNorm, texC);
+ vec3 normal1a = normalize(tangentFrame*(2.0 * texture2D(m_NormalMap, texC).xyz - 1.0));
+
+ texC = surfacePoint.xz * 0.2 + m_WindDirection * m_Time * 0.4;
+ tangentFrame = computeTangentFrame(myNormal, eyeVecNorm, texC);
+ vec3 normal2a = normalize(tangentFrame*(2.0 * texture2D(m_NormalMap, texC).xyz - 1.0));
+
+ texC = surfacePoint.xz * 0.1 + m_WindDirection * m_Time * 0.2;
+ tangentFrame = computeTangentFrame(myNormal, eyeVecNorm, texC);
+ vec3 normal3a = normalize(tangentFrame*(2.0 * texture2D(m_NormalMap, texC).xyz - 1.0));
+
+ normal = normalize(normal0a * normalModifier.x + normal1a * normalModifier.y +normal2a * normalModifier.z + normal3a * normalModifier.w);
+ // XXX: Here's another way to fix the terrain edge issue,
+ // But it requires GLSL 1.3 and still looks kinda incorrect
+ // around edges
+ // To make the shader 1.2 compatible we use a trick :
+ // we clamp the x value of the normal and compare it to it's former value instead of using isnan.
+ normal = clamp(normal.x,0.0,1.0)!=normal.x ? myNormal : normal;
+ //if (position.y > level){
+ // gl_FragColor = vec4(color2 + normal*0.0001, 1.0);
+ // return;
+ //}
+ #else
+ normal = myNormal;
+ #endif
+
+ vec3 refraction = color2;
+ #ifdef ENABLE_REFRACTION
+ texC = texCoord.xy;
+ texC += sin(m_Time*1.8 + 3.0 * abs(position.y)) * (refractionScale * min(depth2, 1.0));
+ refraction = texture2D(m_Texture, texC).rgb;
+ #endif
+
+ vec3 waterPosition = surfacePoint.xyz;
+ waterPosition.y -= (level - m_WaterHeight);
+ vec4 texCoordProj = m_TextureProjMatrix * vec4(waterPosition, 1.0);
+
+ texCoordProj.x = texCoordProj.x + m_ReflectionDisplace * normal.x;
+ texCoordProj.z = texCoordProj.z + m_ReflectionDisplace * normal.z;
+ texCoordProj /= texCoordProj.w;
+ texCoordProj.y = 1.0 - texCoordProj.y;
+
+ vec3 reflection = texture2D(m_ReflectionMap, texCoordProj.xy).rgb;
+
+ float fresnel = fresnelTerm(normal, eyeVecNorm);
+
+ float depthN = depth * m_WaterTransparency;
+ float waterCol = saturate(length(m_LightColor.rgb) / m_SunScale);
+ refraction = mix(mix(refraction, m_WaterColor.rgb * waterCol, saturate(depthN / visibility)),
+ m_DeepWaterColor.rgb * waterCol, saturate(depth2 / m_ColorExtinction));
+
+ vec3 foam = vec3(0.0);
+ #ifdef ENABLE_FOAM
+ texC = (surfacePoint.xz + eyeVecNorm.xz * 0.1) * 0.05 + m_Time * 0.05 * m_WindDirection + sin(m_Time * 0.001 + position.x) * 0.005;
+ vec2 texCoord2 = (surfacePoint.xz + eyeVecNorm.xz * 0.1) * 0.05 + m_Time * 0.1 * m_WindDirection + sin(m_Time * 0.001 + position.z) * 0.005;
+
+ if(depth2 < m_FoamExistence.x){
+ foam = (texture2D(m_FoamMap, texC).r + texture2D(m_FoamMap, texCoord2)).rgb * m_FoamIntensity;
+ }else if(depth2 < m_FoamExistence.y){
+ foam = mix((texture2D(m_FoamMap, texC) + texture2D(m_FoamMap, texCoord2)) * m_FoamIntensity, vec4(0.0),
+ (depth2 - m_FoamExistence.x) / (m_FoamExistence.y - m_FoamExistence.x)).rgb;
+ }
+
+ if(m_MaxAmplitude - m_FoamExistence.z > 0.0001){
+ foam += ((texture2D(m_FoamMap, texC) + texture2D(m_FoamMap, texCoord2)) * m_FoamIntensity * m_FoamIntensity * 0.3 *
+ saturate((level - (m_WaterHeight + m_FoamExistence.z)) / (m_MaxAmplitude - m_FoamExistence.z))).rgb;
+ }
+ foam *= m_LightColor.rgb;
+ #endif
+
+ vec3 specular =vec3(0.0);
+ #ifdef ENABLE_SPECULAR
+ vec3 lightDir=normalize(m_LightDir);
+ vec3 mirrorEye = (2.0 * dot(eyeVecNorm, normal) * normal - eyeVecNorm);
+ float dotSpec = saturate(dot(mirrorEye.xyz, -lightDir) * 0.5 + 0.5);
+ specular = vec3((1.0 - fresnel) * saturate(-lightDir.y) * ((pow(dotSpec, 512.0)) * (m_Shininess * 1.8 + 0.2)));
+ specular += specular * 25.0 * saturate(m_Shininess - 0.05);
+ //foam does not shine
+ specular=specular * m_LightColor.rgb - (5.0 * foam);
+ #endif
+
+ color = mix(refraction, reflection, fresnel);
+ color = mix(refraction, color, saturate(depth * m_ShoreHardness));
+ color = saturate(color + max(specular, foam ));
+ color = mix(refraction, color, saturate(depth* m_FoamHardness));
+
+
+ // XXX: HACK ALERT:
+ // We trick the GeForces to think they have
+ // to calculate the derivatives for all these pixels by using step()!
+ // That way we won't get pixels around the edges of the terrain,
+ // Where the derivatives are undefined
+/* float coef=1.0;
+ if(position.y level){
+ color = color2;
+ }
+
+ gl_FragColor = vec4(color,0.0);
+
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Water/Water.j3md b/engine/src/core-data/Common/MatDefs/Water/Water.j3md
new file mode 100644
index 000000000..b1bda5481
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Water/Water.j3md
@@ -0,0 +1,77 @@
+MaterialDef Advanced Water {
+
+ MaterialParameters {
+ Int NumSamples
+ Int NumSamplesDepth
+ Texture2D FoamMap
+ Texture2D NormalMap
+ Texture2D ReflectionMap
+ Texture2D HeightMap
+ Texture2D Texture
+ Texture2D DepthTexture
+ Vector3 CameraPosition
+ Float Time
+ Vector3 frustumCorner
+ Matrix4 TextureProjMatrix
+ Matrix4 ViewProjectionMatrixInverse
+ Float WaterHeight
+ Vector3 LightDir
+ Float WaterTransparency
+ Float NormalScale
+ Float R0
+ Float MaxAmplitude
+ Color LightColor
+ Float ShoreHardness
+ Float FoamHardness
+ Float RefractionStrength
+ Float WaveScale
+ Vector3 FoamExistence
+ Float SunScale
+ Vector3 ColorExtinction
+ Float Shininess
+ Color WaterColor
+ Color DeepWaterColor
+ Vector2 WindDirection
+ Float ReflectionDisplace
+ Float FoamIntensity
+
+ Boolean UseRipples
+ Boolean UseHQShoreline
+ Boolean UseSpecular
+ Boolean UseFoam
+ Boolean UseRefraction
+ }
+
+ Technique {
+ VertexShader GLSL150 : Common/MatDefs/Post/Post15.vert
+ FragmentShader GLSL150 : Common/MatDefs/Water/Water15.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+
+ Defines {
+ RESOLVE_MS : NumSamples
+ RESOLVE_DEPTH_MS : NumSamplesDepth
+ }
+ }
+
+ Technique {
+ VertexShader GLSL100 : Common/MatDefs/Post/Post.vert
+ FragmentShader GLSL120 : Common/MatDefs/Water/Water.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ }
+ Defines {
+ ENABLE_RIPPLES : UseRipples
+ ENABLE_HQ_SHORELINE : UseHQShoreline
+ ENABLE_SPECULAR : UseSpecular
+ ENABLE_FOAM : UseFoam
+ ENABLE_REFRACTION : UseRefraction
+ }
+ }
+
+ Technique FixedFunc {
+ }
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Water/Water15.frag b/engine/src/core-data/Common/MatDefs/Water/Water15.frag
new file mode 100644
index 000000000..ee3d74487
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Water/Water15.frag
@@ -0,0 +1,307 @@
+#import "Common/ShaderLib/MultiSample.glsllib"
+
+// Water pixel shader
+// Copyright (C) JMonkeyEngine 3.0
+// by Remy Bouquet (nehon) for JMonkeyEngine 3.0
+// original HLSL version by Wojciech Toman 2009
+
+uniform COLORTEXTURE m_Texture;
+uniform DEPTHTEXTURE m_DepthTexture;
+
+
+uniform sampler2D m_HeightMap;
+uniform sampler2D m_NormalMap;
+uniform sampler2D m_FoamMap;
+uniform sampler2D m_ReflectionMap;
+
+uniform mat4 m_ViewProjectionMatrixInverse;
+uniform mat4 m_TextureProjMatrix;
+uniform vec3 m_CameraPosition;
+
+uniform float m_WaterHeight;
+uniform float m_Time;
+uniform float m_WaterTransparency;
+uniform float m_NormalScale;
+uniform float m_R0;
+uniform float m_MaxAmplitude;
+uniform vec3 m_LightDir;
+uniform vec4 m_LightColor;
+uniform float m_ShoreHardness;
+uniform float m_FoamHardness;
+uniform float m_RefractionStrength;
+uniform vec3 m_FoamExistence;
+uniform vec3 m_ColorExtinction;
+uniform float m_Shininess;
+uniform vec4 m_WaterColor;
+uniform vec4 m_DeepWaterColor;
+uniform vec2 m_WindDirection;
+uniform float m_SunScale;
+uniform float m_WaveScale;
+
+uniform bool m_UseRipples,
+ m_UseHQShoreline,
+ m_UseSpecular,
+ m_UseFoam,
+ m_UseRefraction;
+
+vec2 scale = vec2(m_WaveScale, m_WaveScale);
+float refractionScale = m_WaveScale;
+
+// Modifies 4 sampled normals. Increase first values to have more
+// smaller "waves" or last to have more bigger "waves"
+const vec4 normalModifier = vec4(3.0, 2.0, 4.0, 10.0);
+// Strength of displacement along normal.
+uniform float m_ReflectionDisplace;
+// Water transparency along eye vector.
+const float visibility = 3.0;
+// foam intensity
+uniform float m_FoamIntensity ;
+
+in vec2 texCoord;
+out vec4 outFragColor;
+
+mat3 MatrixInverse(in mat3 inMatrix){
+ float det = dot(cross(inMatrix[0], inMatrix[1]), inMatrix[2]);
+ mat3 T = transpose(inMatrix);
+ return mat3(cross(T[1], T[2]),
+ cross(T[2], T[0]),
+ cross(T[0], T[1])) / det;
+}
+
+
+mat3 computeTangentFrame(in vec3 N, in vec3 P, in vec2 UV) {
+ vec3 dp1 = dFdx(P);
+ vec3 dp2 = dFdy(P);
+ vec2 duv1 = dFdx(UV);
+ vec2 duv2 = dFdy(UV);
+
+ // solve the linear system
+ vec3 dp1xdp2 = cross(dp1, dp2);
+ mat2x3 inverseM = mat2x3(cross(dp2, dp1xdp2), cross(dp1xdp2, dp1));
+
+ vec3 T = inverseM * vec2(duv1.x, duv2.x);
+ vec3 B = inverseM * vec2(duv1.y, duv2.y);
+
+ // construct tangent frame
+ float maxLength = max(length(T), length(B));
+ T = T / maxLength;
+ B = B / maxLength;
+
+ return mat3(T, B, N);
+}
+
+float saturate(in float val){
+ return clamp(val,0.0,1.0);
+}
+
+vec3 saturate(in vec3 val){
+ return clamp(val,vec3(0.0),vec3(1.0));
+}
+
+vec3 getPosition(in float depth, in vec2 uv){
+ vec4 pos = vec4(uv, depth, 1.0) * 2.0 - 1.0;
+ pos = m_ViewProjectionMatrixInverse * pos;
+ return pos.xyz / pos.w;
+}
+
+// Function calculating fresnel term.
+// - normal - normalized normal vector
+// - eyeVec - normalized eye vector
+float fresnelTerm(in vec3 normal,in vec3 eyeVec){
+ float angle = 1.0 - max(0.0, dot(normal, eyeVec));
+ float fresnel = angle * angle;
+ fresnel = fresnel * fresnel;
+ fresnel = fresnel * angle;
+ return saturate(fresnel * (1.0 - saturate(m_R0)) + m_R0 - m_RefractionStrength);
+}
+
+// NOTE: This will be called even for single-sampling
+vec4 main_multiSample(int sampleNum){
+
+ float sceneDepth = fetchTextureSample(m_DepthTexture, texCoord, sampleNum).r;
+ vec3 color2 = fetchTextureSample(m_Texture, texCoord, sampleNum).rgb;
+
+ vec3 color = color2;
+ vec3 position = getPosition(sceneDepth, texCoord);
+
+ float level = m_WaterHeight;
+
+ // If we are underwater let's leave out complex computations
+ if(level >= m_CameraPosition.y){
+ return vec4(color2, 1.0);
+ }
+
+ float isAtFarPlane = step(0.99998, sceneDepth);
+ //#ifndef ENABLE_RIPPLES
+ // This optimization won't work on NVIDIA cards if ripples are enabled
+ if(position.y > level + m_MaxAmplitude + isAtFarPlane * 100.0){
+ return vec4(color2, 1.0);
+ }
+ //#endif
+
+ vec3 eyeVec = position - m_CameraPosition;
+ float diff = level - position.y;
+ float cameraDepth = m_CameraPosition.y - position.y;
+
+ // Find intersection with water surface
+ vec3 eyeVecNorm = normalize(eyeVec);
+ float t = (level - m_CameraPosition.y) / eyeVecNorm.y;
+ vec3 surfacePoint = m_CameraPosition + eyeVecNorm * t;
+
+ vec2 texC = vec2(0.0);
+ int samples = 1;
+ if (m_UseHQShoreline){
+ samples = 10;
+ }
+
+ float biasFactor = 1.0 / samples;
+ for (int i = 0; i < samples; i++){
+ texC = (surfacePoint.xz + eyeVecNorm.xz * biasFactor) * scale + m_Time * 0.03 * m_WindDirection;
+
+ float bias = texture(m_HeightMap, texC).r;
+
+ bias *= biasFactor;
+ level += bias * m_MaxAmplitude;
+ t = (level - m_CameraPosition.y) / eyeVecNorm.y;
+ surfacePoint = m_CameraPosition + eyeVecNorm * t;
+ }
+
+ float depth = length(position - surfacePoint);
+ float depth2 = surfacePoint.y - position.y;
+
+ // XXX: HACK ALERT: Increase water depth to infinity if at far plane
+ // Prevents "foam on horizon" issue
+ // For best results, replace the "100.0" below with the
+ // highest value in the m_ColorExtinction vec3
+ depth += isAtFarPlane * 100.0;
+ depth2 += isAtFarPlane * 100.0;
+
+ eyeVecNorm = normalize(m_CameraPosition - surfacePoint);
+
+ // Find normal of water surface
+ float normal1 = textureOffset(m_HeightMap, texC, ivec2(-1, 0)).r;
+ float normal2 = textureOffset(m_HeightMap, texC, ivec2( 1, 0)).r;
+ float normal3 = textureOffset(m_HeightMap, texC, ivec2( 0, -1)).r;
+ float normal4 = textureOffset(m_HeightMap, texC, ivec2( 0, 1)).r;
+
+ vec3 myNormal = normalize(vec3((normal1 - normal2) * m_MaxAmplitude,m_NormalScale,(normal3 - normal4) * m_MaxAmplitude));
+ vec3 normal = vec3(0.0);
+
+ if (m_UseRipples){
+ texC = surfacePoint.xz * 0.8 + m_WindDirection * m_Time* 1.6;
+ mat3 tangentFrame = computeTangentFrame(myNormal, eyeVecNorm, texC);
+ vec3 normal0a = normalize(tangentFrame*(2.0 * texture(m_NormalMap, texC).xyz - 1.0));
+
+ texC = surfacePoint.xz * 0.4 + m_WindDirection * m_Time* 0.8;
+ tangentFrame = computeTangentFrame(myNormal, eyeVecNorm, texC);
+ vec3 normal1a = normalize(tangentFrame*(2.0 * texture(m_NormalMap, texC).xyz - 1.0));
+
+ texC = surfacePoint.xz * 0.2 + m_WindDirection * m_Time * 0.4;
+ tangentFrame = computeTangentFrame(myNormal, eyeVecNorm, texC);
+ vec3 normal2a = normalize(tangentFrame*(2.0 * texture(m_NormalMap, texC).xyz - 1.0));
+
+ texC = surfacePoint.xz * 0.1 + m_WindDirection * m_Time * 0.2;
+ tangentFrame = computeTangentFrame(myNormal, eyeVecNorm, texC);
+ vec3 normal3a = normalize(tangentFrame*(2.0 * texture(m_NormalMap, texC).xyz - 1.0));
+
+ normal = normalize(normal0a * normalModifier.x + normal1a * normalModifier.y +normal2a * normalModifier.z + normal3a * normalModifier.w);
+ // XXX: Here's another way to fix the terrain edge issue,
+ // But it requires GLSL 1.3 and still looks kinda incorrect
+ // around edges
+ normal = isnan(normal.x) ? myNormal : normal;
+ //if (position.y > level){
+ // gl_FragColor = vec4(color2 + normal*0.0001, 1.0);
+ // return;
+ //}
+ }else{
+ normal = myNormal;
+ }
+
+ vec3 refraction = color2;
+ if (m_UseRefraction){
+ // texC = texCoord.xy+ m_ReflectionDisplace * normal.x;
+ texC = texCoord.xy;
+ texC += sin(m_Time*1.8 + 3.0 * abs(position.y)) * (refractionScale * min(depth2, 1.0));
+ #ifdef RESOLVE_MS
+ ivec2 iTexC = ivec2(texC * textureSize(m_Texture));
+ refraction = texelFetch(m_Texture, iTexC, sampleNum).rgb;
+ #else
+ ivec2 iTexC = ivec2(texC * textureSize(m_Texture, 0));
+ refraction = texelFetch(m_Texture, iTexC, 0).rgb;
+ #endif
+ }
+
+ vec3 waterPosition = surfacePoint.xyz;
+ waterPosition.y -= (level - m_WaterHeight);
+ vec4 texCoordProj = m_TextureProjMatrix * vec4(waterPosition, 1.0);
+
+ texCoordProj.x = texCoordProj.x + m_ReflectionDisplace * normal.x;
+ texCoordProj.z = texCoordProj.z + m_ReflectionDisplace * normal.z;
+ texCoordProj /= texCoordProj.w;
+ texCoordProj.y = 1.0 - texCoordProj.y;
+
+ vec3 reflection = texture(m_ReflectionMap, texCoordProj.xy).rgb;
+
+ float fresnel = fresnelTerm(normal, eyeVecNorm);
+
+ float depthN = depth * m_WaterTransparency;
+ float waterCol = saturate(length(m_LightColor.rgb) / m_SunScale);
+ refraction = mix(mix(refraction, m_WaterColor.rgb * waterCol, saturate(depthN / visibility)),
+ m_DeepWaterColor.rgb * waterCol, saturate(depth2 / m_ColorExtinction));
+
+ vec3 foam = vec3(0.0);
+ if (m_UseFoam){
+ texC = (surfacePoint.xz + eyeVecNorm.xz * 0.1) * 0.05 + m_Time * 0.05 * m_WindDirection + sin(m_Time * 0.001 + position.x) * 0.005;
+ vec2 texCoord2 = (surfacePoint.xz + eyeVecNorm.xz * 0.1) * 0.05 + m_Time * 0.1 * m_WindDirection + sin(m_Time * 0.001 + position.z) * 0.005;
+
+ if(depth2 < m_FoamExistence.x){
+ foam = (texture2D(m_FoamMap, texC).r + texture2D(m_FoamMap, texCoord2)).rgb * vec3(m_FoamIntensity);
+ }else if(depth2 < m_FoamExistence.y){
+ foam = mix((texture2D(m_FoamMap, texC) + texture2D(m_FoamMap, texCoord2)) * m_FoamIntensity , vec4(0.0),
+ (depth2 - m_FoamExistence.x) / (m_FoamExistence.y - m_FoamExistence.x)).rgb;
+ }
+
+
+ if(m_MaxAmplitude - m_FoamExistence.z> 0.0001){
+ foam += ((texture2D(m_FoamMap, texC) + texture2D(m_FoamMap, texCoord2)) * m_FoamIntensity * m_FoamIntensity * 0.3 *
+ saturate((level - (m_WaterHeight + m_FoamExistence.z)) / (m_MaxAmplitude - m_FoamExistence.z))).rgb;
+ }
+ foam *= m_LightColor.rgb;
+ }
+
+ vec3 specular = vec3(0.0);
+ if (m_UseSpecular){
+ vec3 lightDir=normalize(m_LightDir);
+ vec3 mirrorEye = (2.0 * dot(eyeVecNorm, normal) * normal - eyeVecNorm);
+ float dotSpec = saturate(dot(mirrorEye.xyz, -lightDir) * 0.5 + 0.5);
+ specular = vec3((1.0 - fresnel) * saturate(-lightDir.y) * ((pow(dotSpec, 512.0)) * (m_Shininess * 1.8 + 0.2)));
+ specular += specular * 25.0 * saturate(m_Shininess - 0.05);
+ //foam does not shine
+ specular=specular * m_LightColor.rgb - (5.0 * foam);
+ }
+
+ color = mix(refraction, reflection, fresnel);
+ color = mix(refraction, color, saturate(depth * m_ShoreHardness));
+ color = saturate(color + max(specular, foam ));
+ color = mix(refraction, color, saturate(depth* m_FoamHardness));
+
+
+ // XXX: HACK ALERT:
+ // We trick the GeForces to think they have
+ // to calculate the derivatives for all these pixels by using step()!
+ // That way we won't get pixels around the edges of the terrain,
+ // Where the derivatives are undefined
+ return vec4(mix(color, color2, step(level, position.y)), 1.0);
+}
+
+void main(){
+ #ifdef RESOLVE_MS
+ vec4 color = vec4(0.0);
+ for (int i = 0; i < m_NumSamples; i++){
+ color += main_multiSample(i);
+ }
+ gl_FragColor = color / m_NumSamples;
+ #else
+ outFragColor = main_multiSample(0);
+ #endif
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Water/simple_water.frag b/engine/src/core-data/Common/MatDefs/Water/simple_water.frag
new file mode 100644
index 000000000..fb1f3e21d
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Water/simple_water.frag
@@ -0,0 +1,125 @@
+/*
+GLSL conversion of Michael Horsch water demo
+http://www.bonzaisoftware.com/wfs.html
+Converted by Mars_999
+8/20/2005
+*/
+
+uniform sampler2D m_water_normalmap;
+uniform sampler2D m_water_reflection;
+uniform sampler2D m_water_refraction;
+uniform sampler2D m_water_dudvmap;
+uniform sampler2D m_water_depthmap;
+uniform vec4 m_waterColor;
+uniform float m_waterDepth;
+uniform vec4 m_distortionScale;
+uniform vec4 m_distortionMix;
+uniform vec4 m_texScale;
+uniform vec2 m_FrustumNearFar;
+uniform float m_waterTransparency;
+
+
+
+varying vec4 lightDir; //lightpos
+varying vec4 waterTex1; //moving texcoords
+varying vec4 waterTex2; //moving texcoords
+varying vec4 position; //for projection
+varying vec4 viewDir; //viewts
+varying vec4 viewLightDir;
+varying vec4 viewCamDir;
+
+//unit 0 = m_water_reflection
+//unit 1 = m_water_refraction
+//unit 2 = m_water_normalmap
+//unit 3 = m_water_dudvmap
+//unit 4 = m_water_depthmap
+
+ const vec4 two = vec4(2.0, 2.0, 2.0, 1.0);
+ const vec4 mone = vec4(-1.0, -1.0, -1.0, 1.0);
+
+ const vec4 ofive = vec4(0.5,0.5,0.5,1.0);
+
+ const float exponent = 64.0;
+
+float tangDot(in vec3 v1, in vec3 v2){
+ float d = dot(v1,v2);
+ #ifdef V_TANGENT
+ d = 1.0 - d*d;
+ return step(0.0, d) * sqrt(d);
+ #else
+ return d;
+ #endif
+}
+
+vec4 readDepth(vec2 uv){
+ float depth= (2.0 * m_FrustumNearFar.x) / (m_FrustumNearFar.y + m_FrustumNearFar.x - texture2D(m_water_depthmap, uv).r* (m_FrustumNearFar.y-m_FrustumNearFar.x));
+ return vec4( depth);
+}
+
+void main(void)
+{
+
+
+ vec4 lightTS = normalize(lightDir);
+ vec4 viewt = normalize(viewDir);
+ vec4 disdis = texture2D(m_water_dudvmap, vec2(waterTex2 * m_texScale));
+ vec4 fdist = texture2D(m_water_dudvmap, vec2(waterTex1 + disdis*m_distortionMix));
+ fdist =normalize( fdist * 2.0 - 1.0)* m_distortionScale;
+
+
+ //load normalmap
+ vec4 nmap = texture2D(m_water_normalmap, vec2(waterTex1 + disdis*m_distortionMix));
+ nmap = (nmap-ofive) * two;
+ // nmap = nmap*2.0-1.0;
+ vec4 vNorm = normalize(nmap);
+
+
+ vec4 projCoord = position / position.w;
+ projCoord =(projCoord+1.0)*0.5 + fdist;
+ projCoord = clamp(projCoord, 0.001, 0.999);
+
+ //load reflection,refraction and depth texture
+ vec4 refl = texture2D(m_water_reflection, vec2(projCoord.x,1.0-projCoord.y));
+ vec4 refr = texture2D(m_water_refraction, vec2(projCoord));
+ vec4 wdepth =readDepth(vec2(projCoord));
+
+ wdepth = vec4(pow(wdepth.x, m_waterDepth));
+ vec4 invdepth = 1.0 - wdepth;
+
+
+ // Blinn - Phong
+ // vec4 H = (viewt - lightTS);
+ // vec4 specular =vec4(pow(max(dot(H, vNorm), 0.0), exponent));
+
+// Standard Phong
+
+ // vec4 R =reflect(-L, vNorm);
+ // vec4 specular =vec4( pow(max(dot(R, E), 0.0),exponent));
+
+
+ //calculate specular highlight
+ vec4 L=normalize(viewLightDir);
+ vec4 E=normalize(viewCamDir);
+ vec4 vRef = normalize(reflect(-L,vNorm));
+ float stemp =max(0.0, dot( vRef,E) );
+ vec4 specular;
+ if(stemp>0.0){
+ stemp = pow(stemp, exponent);
+ specular = vec4(stemp);
+ }
+
+
+
+ vec4 fresnelTerm = vec4(0.02+0.97*pow((1.0-dot(normalize(viewt), vNorm)),5.0));
+
+
+
+ fresnelTerm=fresnelTerm*invdepth*m_waterTransparency;
+ fresnelTerm=clamp(fresnelTerm,0.0,1.0);
+
+ refr*=(fresnelTerm);
+ refr *= invdepth;
+ refr= refr+ m_waterColor*wdepth*fresnelTerm;
+
+ gl_FragColor =(refr+ refl*(1.0-fresnelTerm))+specular;
+}
diff --git a/engine/src/core-data/Common/MatDefs/Water/simple_water.vert b/engine/src/core-data/Common/MatDefs/Water/simple_water.vert
new file mode 100644
index 000000000..e6052d8d5
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Water/simple_water.vert
@@ -0,0 +1,87 @@
+/*
+GLSL conversion of Michael Horsch water demo
+http://www.bonzaisoftware.com/wfs.html
+Converted by Mars_999
+8/20/2005
+*/
+uniform vec3 m_lightPos;
+uniform float m_time;
+
+uniform mat4 g_WorldViewProjectionMatrix;
+uniform mat4 g_WorldViewMatrix;
+uniform mat4 g_ViewMatrix;
+uniform vec3 g_CameraPosition;
+uniform mat3 g_NormalMatrix;
+
+attribute vec4 inPosition;
+attribute vec2 inTexCoord;
+attribute vec3 inTangent;
+attribute vec3 inNormal;
+
+varying vec4 lightDir;
+varying vec4 waterTex1;
+varying vec4 waterTex2;
+varying vec4 position;
+varying vec4 viewDir;
+varying vec4 viewpos;
+varying vec4 viewLightDir;
+varying vec4 viewCamDir;
+
+
+//unit 0 = water_reflection
+//unit 1 = water_refraction
+//unit 2 = water_normalmap
+//unit 3 = water_dudvmap
+//unit 4 = water_depthmap
+
+void main(void)
+{
+ viewpos.x = g_CameraPosition.x;
+ viewpos.y = g_CameraPosition.y;
+ viewpos.z = g_CameraPosition.z;
+ viewpos.w = 1.0;
+
+ vec4 temp;
+ vec4 tangent = vec4(1.0, 0.0, 0.0, 0.0);
+ vec4 norm = vec4(0.0, 1.0, 0.0, 0.0);
+ vec4 binormal = vec4(0.0, 0.0, 1.0, 0.0);
+
+
+ temp = viewpos - inPosition;
+
+ viewDir.x = dot(temp, tangent);
+ viewDir.y = dot(temp, binormal);
+ viewDir.z = dot(temp, norm);
+ viewDir.w = 0.0;
+
+ temp = vec4(m_lightPos,1.0)- inPosition;
+ lightDir.x = dot(temp, tangent);
+ lightDir.y = dot(temp, binormal);
+ lightDir.z = dot(temp, norm);
+ lightDir.w = 0.0;
+
+ vec4 viewSpaceLightPos=g_ViewMatrix*vec4(m_lightPos,1.0);
+ vec4 viewSpacePos=g_WorldViewMatrix*inPosition;
+ vec3 wvNormal = normalize(g_NormalMatrix * inNormal);
+ vec3 wvTangent = normalize(g_NormalMatrix * inTangent);
+ vec3 wvBinormal = cross(wvNormal, wvTangent);
+ mat3 tbnMat = mat3(wvTangent, wvBinormal, wvNormal);
+
+ temp = viewSpaceLightPos - viewSpacePos;
+ viewLightDir.xyz=temp.xyz*tbnMat;
+ viewLightDir.w = 0.0;
+
+ temp = -viewSpacePos;
+ viewCamDir.xyz =temp.xyz*tbnMat;
+ viewCamDir.w = 0.0;
+
+
+ vec4 t1 = vec4(0.0, -m_time, 0.0,0.0);
+ vec4 t2 = vec4(0.0, m_time, 0.0,0.0);
+
+ waterTex1 =vec4(inTexCoord,0.0,0.0) + t1;
+ waterTex2 =vec4(inTexCoord ,0.0,0.0)+ t2;
+
+ position = g_WorldViewProjectionMatrix * inPosition;
+ gl_Position = position;
+}
diff --git a/engine/src/core-data/Common/Materials/RedColor.j3m b/engine/src/core-data/Common/Materials/RedColor.j3m
new file mode 100644
index 000000000..c7c8e77e6
--- /dev/null
+++ b/engine/src/core-data/Common/Materials/RedColor.j3m
@@ -0,0 +1,5 @@
+Material Red Color : Common/MatDefs/Misc/Unshaded.j3md {
+ MaterialParameters {
+ Color : 1 0 0 1
+ }
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/Materials/VertexColor.j3m b/engine/src/core-data/Common/Materials/VertexColor.j3m
new file mode 100644
index 000000000..ae7209204
--- /dev/null
+++ b/engine/src/core-data/Common/Materials/VertexColor.j3m
@@ -0,0 +1,5 @@
+Material Vertex Color Ext : Common/MatDefs/Misc/Unshaded.j3md {
+ MaterialParameters {
+ VertexColor : true
+ }
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/Materials/WhiteColor.j3m b/engine/src/core-data/Common/Materials/WhiteColor.j3m
new file mode 100644
index 000000000..1a5d78e95
--- /dev/null
+++ b/engine/src/core-data/Common/Materials/WhiteColor.j3m
@@ -0,0 +1,5 @@
+Material White Color : Common/MatDefs/Misc/Unshaded.j3md {
+ MaterialParameters {
+ Color : 1 1 1 1
+ }
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/ShaderLib/Bump.glsllib b/engine/src/core-data/Common/ShaderLib/Bump.glsllib
new file mode 100644
index 000000000..6b9149bcc
--- /dev/null
+++ b/engine/src/core-data/Common/ShaderLib/Bump.glsllib
@@ -0,0 +1,44 @@
+#define SCALE 0.12
+#define BIAS -0.04
+#define BIN_ITER 5
+
+#ifndef BUMP_HQ
+ #define LIN_ITER 5
+#endif
+
+vec2 Bump_DoOcclusionParallax(in sampler2D heightMap, in vec2 texCoord, in vec3 tanViewDir){
+ float size = 1.0 / float(BIN_ITER);
+
+ // depth
+ float d = 1.0;
+ // best depth
+ float bd = 0.0;
+
+ #ifdef BUMP_HQ
+ const int N = 8;
+ int LIN_ITER = mix(2 * N, N, tanViewDir.z);
+ #endif
+
+ // search from front to back
+ for (int i = 0; i < LIN_ITER; i++){
+ d -= dstep;
+ float h = texture2D(heightMap, dp + ds * (1.0 - d)).a;
+ if (bd < 0.005) // if no depth found yet
+ if (d <= h) bd = depth; // best depth
+ }
+
+ for (int i = 0; i < BIN_ITER; i++) {
+ size *= 0.5;
+ float t = texture2D(heightMap, dp + ds * (1.0 - d)).a;
+ if (d <= t) {
+ bd = depth;
+ d += 2 * size;
+ }
+ d -= size;
+ }
+}
+
+vec2 Bump_DoParallax(in sampler2D heightMap, in vec2 texCoord, in vec3 tanViewDir){
+ float h = texture2D(heightMap, texCoord).a * SCALE + BIAS;
+ return texCoord + h * tanViewDir.xy;
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/ShaderLib/Common.glsllib b/engine/src/core-data/Common/ShaderLib/Common.glsllib
new file mode 100644
index 000000000..8dce15bb4
--- /dev/null
+++ b/engine/src/core-data/Common/ShaderLib/Common.glsllib
@@ -0,0 +1,13 @@
+vec3 Common_UnpackNormal(in vec3 norm){
+ return (norm * vec3(2.0)) - vec3(1.0);
+}
+
+vec3 Common_UnpackNormalLA(in vec4 norm){
+ vec3 newNorm = norm.agb;
+ newNorm.b = sqrt(1.0 - (newNorm.x * newNorm.x) - (newNorm.y * newNorm.y));
+ return (newNorm * vec3(2.0)) - vec3(1.0);
+}
+
+vec3 Common_PackNormal(in vec3 norm){
+ return (norm * vec3(0.5)) + vec3(0.5);
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/ShaderLib/Fog.glsllib b/engine/src/core-data/Common/ShaderLib/Fog.glsllib
new file mode 100644
index 000000000..0a28362cc
--- /dev/null
+++ b/engine/src/core-data/Common/ShaderLib/Fog.glsllib
@@ -0,0 +1,41 @@
+#ifdef FOG
+
+#ifdef FOG_TEXTURE
+uniform sampler2D m_FogTexture;
+#endif
+
+uniform vec3 m_FogColor;
+
+// x == density
+// y == factor
+// z == ystart
+// w == yend
+uniform vec4 m_FogParams;
+
+varying vec3 fogCoord;
+
+void Fog_PerVertex(inout vec4 color, in vec3 wvPosition){
+ float density = g_FogParams.x;
+ float factor = g_FogParams.y;
+ float dist = length(wvPosition.xyz);
+
+ float yf = wvPosition.y;
+ float y0 = g_FogParams.z;
+ float y1 = g_FogParams.w;
+ float yh = (y1 - y0) * 0.5;
+
+ float fogAmt1 = max(step(yh, 0.0), smoothstep(0, yh, max(y1-yf, yf-y0)));
+ float fogAmt2 = exp(-density * density * dist * dist);
+
+ color.rgb = mix(color.rgb, m_FogColor, fogAmt1 * fogAmt2);
+}
+
+void Fog_PerPixel(inout vec4 color){
+ Fog_PerVertex(color, fogCoord);
+}
+
+void Fog_WVPos(in vec4 wvPosition){
+ fogCoord = wvPosition.xyz;
+}
+
+#endif
\ No newline at end of file
diff --git a/engine/src/core-data/Common/ShaderLib/Hdr.glsllib b/engine/src/core-data/Common/ShaderLib/Hdr.glsllib
new file mode 100644
index 000000000..5db1423ef
--- /dev/null
+++ b/engine/src/core-data/Common/ShaderLib/Hdr.glsllib
@@ -0,0 +1,65 @@
+const float epsilon = 0.0001;
+const vec3 lumConv = vec3(0.27, 0.67, 0.06);
+
+float HDR_GetLum(in vec3 color){
+ return dot(color, lumConv);
+}
+
+vec4 HDR_EncodeLum(in float lum){
+ float Le = 2.0 * log2(lum + epsilon) + 127.0;
+ vec4 result = vec4(0.0);
+ result.a = fract(Le);
+ result.rgb = vec3((Le - (floor(result.a * 255.0)) / 255.0) / 255.0);
+ return result;
+}
+
+float HDR_DecodeLum(in vec4 logLum){
+ float Le = logLum.r * 255.0 + logLum.a;
+ return exp2((Le - 127.0) / 2.0);
+}
+
+const mat3 rgbToXyz = mat3(
+ 0.2209, 0.3390, 0.4184,
+ 0.1138, 0.6780, 0.7319,
+ 0.0102, 0.1130, 0.2969);
+
+const mat3 xyzToRgb = mat3(
+ 6.0013, -2.700, -1.7995,
+ -1.332, 3.1029, -5.7720,
+ .3007, -1.088, 5.6268);
+
+vec4 HDR_LogLuvEncode(in vec3 rgb){
+ vec4 result;
+ vec3 Xp_Y_XYZp = rgb * rgbToXyz;
+ Xp_Y_XYZp = max(Xp_Y_XYZp, vec3(1e-6, 1e-6, 1e-6));
+ result.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;
+ float Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0;
+ result.w = fract(Le);
+ result.z = (Le - (floor(result.w * 255.0)) / 255.0) / 255.0;
+ return result;
+}
+
+vec3 HDR_LogLuvDecode(in vec4 logLuv){
+ float Le = logLuv.z * 255.0 + logLuv.w;
+ vec3 Xp_Y_XYZp;
+ Xp_Y_XYZp.y = exp2((Le - 127.0) / 2.0);
+ Xp_Y_XYZp.z = Xp_Y_XYZp.y / logLuv.y;
+ Xp_Y_XYZp.x = logLuv.x * Xp_Y_XYZp.z;
+ vec3 rgb = Xp_Y_XYZp * xyzToRgb;
+ return max(rgb, 0.0);
+}
+
+vec3 HDR_ToneMap(in vec3 color, in float lumAvg, in float a, in float white){
+ white *= white;
+ float lumHDR = HDR_GetLum(color);
+ float L = (a / lumAvg) * lumHDR;
+ float Ld = 1.0 + (L / white);
+ Ld = (Ld * L) / (1.0 + L);
+ return (color / lumHDR) * Ld;
+ //return color * vec3(Ld);
+}
+
+vec3 HDR_ToneMap2(in vec3 color, in float lumAvg, in float a, in float white){
+ float scale = a / (lumAvg + 0.001);
+ return (vec3(scale) * color) / (color + vec3(1.0));
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/ShaderLib/Lighting.glsllib b/engine/src/core-data/Common/ShaderLib/Lighting.glsllib
new file mode 100644
index 000000000..4d1b40436
--- /dev/null
+++ b/engine/src/core-data/Common/ShaderLib/Lighting.glsllib
@@ -0,0 +1,48 @@
+#ifndef NUM_LIGHTS
+ #define NUM_LIGHTS 4
+#endif
+
+uniform mat4 g_ViewMatrix;
+uniform vec4 g_LightPosition[NUM_LIGHTS];
+uniform vec4 g_g_LightColor[NUM_LIGHTS];
+uniform float m_Shininess;
+
+float Lighting_Diffuse(vec3 norm, vec3 lightdir){
+ return max(0.0, dot(norm, lightdir));
+}
+
+float Lighting_Specular(vec3 norm, vec3 viewdir, vec3 lightdir, float shiny){
+ vec3 refdir = reflect(-lightdir, norm);
+ return pow(max(dot(refdir, viewdir), 0.0), shiny);
+}
+
+void Lighting_Direction(vec3 worldPos, vec4 color, vec4 position, out vec4 lightDir){
+ float posLight = step(0.5, color.w);
+ vec3 tempVec = position.xyz * sign(posLight - 0.5) - (worldPos * posLight);
+ float dist = length(tempVec);
+
+ lightDir.w = clamp(1.0 - position.w * dist * posLight, 0.0, 1.0);
+ lightDir.xyz = tempVec / dist;
+}
+
+void Lighting_ComputePS(vec3 tanNormal, mat3 tbnMat,
+ int lightCount, out vec3 outDiffuse, out vec3 outSpecular){
+ // find tangent view dir & vert pos
+ vec3 tanViewDir = viewDir * tbnMat;
+
+ for (int i = 0; i < lightCount; i++){
+ // find light dir in tangent space, works for point & directional lights
+ vec4 wvLightPos = (g_ViewMatrix * vec4(g_LightPosition[i].xyz, g_LightColor[i].w));
+ wvLightPos.w = g_LightPosition[i].w;
+
+ vec4 tanLightDir;
+ Lighting_Direction(wvPosition, g_LightColor[i], wvLightPos, tanLightDir);
+ tanLightDir.xyz = tanLightDir.xyz * tbnMat;
+
+ vec3 lightScale = g_LightColor[i].rgb * tanLightDir.w;
+ float specular = Lighting_Specular(tanNormal, tanViewDir, tanLightDir.xyz, m_Shininess);
+ float diffuse = Lighting_Diffuse(tanNormal, tanLightDir.xyz);
+ outSpecular += specular * lightScale * step(0.01, diffuse) * g_LightColor[i].rgb;
+ outDiffuse += diffuse * lightScale * g_LightColor[i].rgb;
+ }
+}
diff --git a/engine/src/core-data/Common/ShaderLib/Math.glsllib b/engine/src/core-data/Common/ShaderLib/Math.glsllib
new file mode 100644
index 000000000..6f4cc9074
--- /dev/null
+++ b/engine/src/core-data/Common/ShaderLib/Math.glsllib
@@ -0,0 +1,4 @@
+/// Multiplies the vector by the quaternion, then returns the resultant vector.
+vec3 Math_QuaternionMult(in vec4 quat, in vec3 vec){
+ return vec + 2.0 * cross(quat.xyz, cross(quat.xyz, vec) + quat.w * vec);
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/ShaderLib/MultiSample.glsllib b/engine/src/core-data/Common/ShaderLib/MultiSample.glsllib
new file mode 100644
index 000000000..e0aa75377
--- /dev/null
+++ b/engine/src/core-data/Common/ShaderLib/MultiSample.glsllib
@@ -0,0 +1,49 @@
+#extension GL_ARB_texture_multisample : enable
+uniform int m_NumSamples;
+uniform int m_NumSamplesDepth;
+#ifdef RESOLVE_MS
+ #define COLORTEXTURE sampler2DMS
+#else
+ #define COLORTEXTURE sampler2D
+#endif
+
+#ifdef RESOLVE_DEPTH_MS
+ #define DEPTHTEXTURE sampler2DMS
+
+#else
+ #define DEPTHTEXTURE sampler2D
+#endif
+
+vec4 textureFetch(in sampler2DMS tex,in vec2 texC, in int numSamples){
+ ivec2 iTexC = ivec2(texC * textureSize(tex));
+ vec4 color = vec4(0.0);
+ for (int i=0;i= 1.0)
+ // return 1.0;
+ //else if (coord.x <= 0.0)
+ // return 1.0;
+ //else if (coord.y >= 1.0)
+ // return 1.0;
+ //else if (coord.y <= 0.0)
+ // return 1.0;
+ //else
+ // return 0.0;
+
+ // Fastest, "hack" method (uses 4-5 instructions)
+ vec4 t = vec4(coord.xy, 0.0, 1.0);
+ t = step(t.wwxy, t.xyzz);
+ return dot(t,t);
+}
+
+float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){
+ float shadow = 0.0;
+ vec2 o = mod(floor(gl_FragCoord.xy), 2.0);
+ shadow += Shadow_DoShadowCompareOffset(tex,projCoord,vec2(-1.5, 1.5) + o);
+ shadow += Shadow_DoShadowCompareOffset(tex,projCoord,vec2( 0.5, 1.5) + o);
+ shadow += Shadow_DoShadowCompareOffset(tex,projCoord,vec2(-1.5, -0.5) + o);
+ shadow += Shadow_DoShadowCompareOffset(tex,projCoord,vec2( 0.5, -0.5) + o);
+ shadow *= 0.25 ;
+ return shadow;
+}
+
+float Shadow_DoBilinear(in SHADOWMAP tex, in vec4 projCoord){
+ const vec2 size = vec2(256.0);
+ const vec2 pixel = vec2(1.0) / vec2(256.0);
+
+ vec2 tc = projCoord.xy * size;
+ vec2 bl = fract(tc);
+ vec2 dn = floor(tc) * pixel;
+ vec2 up = dn + pixel;
+
+ vec4 coord = vec4(dn.xy, projCoord.zw);
+ float s_00 = Shadow_DoShadowCompare(tex, coord);
+ s_00 = clamp(s_00, 0.0, 1.0);
+
+ coord = vec4(up.x, dn.y, projCoord.zw);
+ float s_10 = Shadow_DoShadowCompare(tex, coord);
+ s_10 = clamp(s_10, 0.0, 1.0);
+
+ coord = vec4(dn.x, up.y, projCoord.zw);
+ float s_01 = Shadow_DoShadowCompare(tex, coord);
+ s_01 = clamp(s_01, 0.0, 1.0);
+
+ coord = vec4(up.xy, projCoord.zw);
+ float s_11 = Shadow_DoShadowCompare(tex, coord);
+ s_11 = clamp(s_11, 0.0, 1.0);
+
+ float xb0 = mix(s_00, s_10, clamp(bl.x, 0.0, 1.0));
+ float xb1 = mix(s_01, s_11, clamp(bl.x, 0.0, 1.0));
+ float yb = mix(xb0, xb1, clamp(bl.y, 0.0, 1.0));
+ return yb;
+}
+
+float Shadow_DoPCF_2x2(in SHADOWMAP tex, in vec4 projCoord){
+
+ float shadow = 0.0;
+ float x,y;
+ for (y = -1.5 ; y <=1.5 ; y+=1.0)
+ for (x = -1.5 ; x <=1.5 ; x+=1.0)
+ shadow += clamp(Shadow_DoShadowCompareOffset(tex,projCoord,vec2(x,y)) +
+ Shadow_BorderCheck(projCoord.xy),
+ 0.0, 1.0);
+
+ shadow /= 16.0 ;
+ return shadow;
+}
+
+
+float Shadow_GetShadow(in SHADOWMAP tex, in vec4 projCoord){
+ return Shadow_DoDither_2x2(tex, projCoord) + Shadow_BorderCheck(projCoord.xy);
+}
+
+
diff --git a/engine/src/core-data/Common/ShaderLib/Skinning.glsllib b/engine/src/core-data/Common/ShaderLib/Skinning.glsllib
new file mode 100644
index 000000000..f0641b636
--- /dev/null
+++ b/engine/src/core-data/Common/ShaderLib/Skinning.glsllib
@@ -0,0 +1,36 @@
+#ifdef USE_HWSKINNING
+
+#ifndef NUM_BONES
+#error A required pre-processor define "NUM_BONES" is not set!
+#endif
+
+attribute vec4 inBoneWeight;
+attribute vec4 inBoneIndices;
+uniform mat4 m_BoneMatrices[NUM_BONES];
+
+void Skinning_Compute(inout vec4 position, inout vec4 normal){
+ vec4 index = inBoneIndices;
+ vec4 weight = inBoneWeight;
+
+ vec4 newPos = vec4(0.0);
+ vec4 newNormal = vec4(0.0);
+
+ for (float i = 0.0; i < 4.0; i += 1.0){
+ mat4 skinMat = m_BoneMatrices[int(index.x)];
+ newPos += weight.x * (skinMat * position);
+ newNormal += weight.x * (skinMat * normal);
+ index = index.yzwx;
+ weight = weight.yzwx;
+ }
+
+ position = newPos;
+ normal = newNormal;
+}
+
+#else
+
+void Skinning_Compute(inout vec4 position, inout vec4 normal){
+ // skinning disabled, leave position and normal unaltered
+}
+
+#endif
\ No newline at end of file
diff --git a/engine/src/core-data/Common/ShaderLib/Splatting.glsllib b/engine/src/core-data/Common/ShaderLib/Splatting.glsllib
new file mode 100644
index 000000000..044fee399
--- /dev/null
+++ b/engine/src/core-data/Common/ShaderLib/Splatting.glsllib
@@ -0,0 +1,10 @@
+void Splatting_Base(in sampler2D baseMap, in vec2 tc, in float scale, out vec3 outColor){
+ outColor = texture2D(baseMap, tc * vec2(scale)).rgb;
+}
+
+void Splatting_AlphaDetail(in sampler2D alphaMap, in sampler2D detailMap, in vec2 tc, in float scale, out vec3 outColor){
+ float alpha = sampler2D(alphaMap, tc).r;
+ vec3 color = sampler2D(detailMap, tc * vec2(scale)).rgb;
+ //outColor = mix(outColor, color, alpha);
+ outColor = outColor + color * vec3(alpha);
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/ShaderLib/Tangent.glsllib b/engine/src/core-data/Common/ShaderLib/Tangent.glsllib
new file mode 100644
index 000000000..308c13dd7
--- /dev/null
+++ b/engine/src/core-data/Common/ShaderLib/Tangent.glsllib
@@ -0,0 +1,11 @@
+uniform mat3 g_NormalMatrix;
+
+void Tangent_ComputeVS(out vec3 outNormal, out vec3 outTangent){
+ outNormal = normalize(g_NormalMatrix * inNormal);
+ outTangent = normalize(g_NormalMatrix * inTangent);
+}
+
+mat3 Tangent_GetBasis(){
+ vec3 wvBinormal = cross(wvNormal, wvTangent);
+ return mat3(wvTangent, wvBinormal, wvNormal);
+}
diff --git a/engine/src/core-data/Common/ShaderLib/Texture.glsllib b/engine/src/core-data/Common/ShaderLib/Texture.glsllib
new file mode 100644
index 000000000..829f51b47
--- /dev/null
+++ b/engine/src/core-data/Common/ShaderLib/Texture.glsllib
@@ -0,0 +1,41 @@
+#import "Common/ShaderLib/Common.glsllib"
+
+vec3 Texture_GetNormal(in sampler2D normalMap, in vec2 texCoord){
+ #ifdef NORMAL_LATC
+ return Common_UnpackNormalLA( texture2D(normalMap, texCoord) );
+ #else
+ return Common_UnpackNormal( texture2D(normalMap, texCoord).rgb );
+ #endif
+}
+
+#ifdef DXT_YCOCG
+const mat4 ycocg_mat = mat4( 1.0, -1.0, 0.0, 1.0,
+ 0.0, 1.0, -0.5 * 256.0 / 255.0, 1.0,
+ -1.0, -1.0, 256.0 / 255.0, 1.0,
+ 0.0, 0.0, 0.0, 0.0 );
+#endif
+
+vec4 Texture_GetColor(in sampler2D colorMap, in vec2 texCoord){
+ #ifdef DXT_YCOCG
+ vec4 color = texture2D(colorMap, texCoord);
+ // fast YCoCg decode:
+ color.z = 1.0 / ((color.z * ( 255.0 / 8.0 )) + 1.0);
+ color.xy *= color.z;
+ return color * ycocg_mat;
+
+ // slow decode:
+ //float Y = color.a;
+ //float scale = 1.0 / ((255.0 / 8.0) * color.b + 1.0);
+ //const float offset = 128.0 / 255.0;
+ //float Co = (color.r - offset) * scale;
+ //float Cg = (color.g - offset) * scale;
+
+ //float R = Y + Co - Cg;
+ //float G = Y + Cg;
+ //float B = Y - Co - Cg;
+
+ //return vec4(R, G, B, 1.0);
+ #else
+ return texture2D(colorMap, texCoord);
+ #endif
+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/ShaderLib/Ubo.glsllib b/engine/src/core-data/Common/ShaderLib/Ubo.glsllib
new file mode 100644
index 000000000..5dbb5b9e1
--- /dev/null
+++ b/engine/src/core-data/Common/ShaderLib/Ubo.glsllib
@@ -0,0 +1,15 @@
+
+
+#ifdef ENABLE_UBO
+ // #version 140
+ #extension GL_ARB_uniform_buffer_object : enable
+
+ #define START_MATPARAMS layout(std140) uniform matparams {
+ #define END_MATPARAMS }
+ #define MATPARAM
+ #define attribute in
+#else
+ #define START_MATPARAMS
+ #define END_MATPARAMS
+ #define MATPARAM uniform
+#endif
diff --git a/engine/src/core-data/Interface/Fonts/Console.fnt b/engine/src/core-data/Interface/Fonts/Console.fnt
new file mode 100644
index 000000000..15ea60cf7
--- /dev/null
+++ b/engine/src/core-data/Interface/Fonts/Console.fnt
@@ -0,0 +1,99 @@
+info face="Lucida Console" size=11 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1 outline=0
+common lineHeight=11 base=9 scaleW=256 scaleH=256 pages=1 packed=0 alphaChnl=0 redChnl=0 greenChnl=0 blueChnl=0
+page id=0 file="Console.png"
+chars count=95
+char id=32 x=61 y=18 width=1 height=0 xoffset=0 yoffset=11 xadvance=7 page=0 chnl=15
+char id=33 x=147 y=8 width=1 height=7 xoffset=3 yoffset=2 xadvance=7 page=0 chnl=15
+char id=34 x=26 y=19 width=4 height=3 xoffset=1 yoffset=1 xadvance=7 page=0 chnl=15
+char id=35 x=214 y=0 width=6 height=7 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
+char id=36 x=59 y=0 width=5 height=9 xoffset=1 yoffset=1 xadvance=7 page=0 chnl=15
+char id=37 x=142 y=0 width=7 height=7 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
+char id=38 x=150 y=0 width=7 height=7 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
+char id=39 x=31 y=19 width=1 height=3 xoffset=3 yoffset=1 xadvance=7 page=0 chnl=15
+char id=40 x=26 y=0 width=4 height=10 xoffset=2 yoffset=1 xadvance=7 page=0 chnl=15
+char id=41 x=16 y=0 width=4 height=10 xoffset=1 yoffset=1 xadvance=7 page=0 chnl=15
+char id=42 x=9 y=19 width=5 height=4 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=43 x=245 y=8 width=5 height=6 xoffset=1 yoffset=3 xadvance=7 page=0 chnl=15
+char id=44 x=15 y=19 width=2 height=4 xoffset=3 yoffset=7 xadvance=7 page=0 chnl=15
+char id=45 x=55 y=18 width=5 height=1 xoffset=1 yoffset=5 xadvance=7 page=0 chnl=15
+char id=46 x=41 y=19 width=2 height=2 xoffset=2 yoffset=7 xadvance=7 page=0 chnl=15
+char id=47 x=0 y=0 width=7 height=10 xoffset=0 yoffset=1 xadvance=7 page=0 chnl=15
+char id=48 x=31 y=11 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=49 x=37 y=11 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=50 x=132 y=9 width=4 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=51 x=127 y=9 width=4 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=52 x=43 y=11 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=53 x=142 y=8 width=4 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=54 x=103 y=9 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=55 x=49 y=11 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=56 x=55 y=10 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=57 x=61 y=10 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=58 x=6 y=19 width=2 height=6 xoffset=2 yoffset=3 xadvance=7 page=0 chnl=15
+char id=59 x=131 y=0 width=2 height=8 xoffset=2 yoffset=3 xadvance=7 page=0 chnl=15
+char id=60 x=188 y=8 width=6 height=6 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15
+char id=61 x=18 y=19 width=7 height=3 xoffset=0 yoffset=4 xadvance=7 page=0 chnl=15
+char id=62 x=202 y=8 width=6 height=6 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15
+char id=63 x=79 y=9 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=64 x=190 y=0 width=7 height=7 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
+char id=65 x=166 y=0 width=7 height=7 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
+char id=66 x=91 y=9 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=67 x=242 y=0 width=6 height=7 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
+char id=68 x=13 y=11 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=69 x=67 y=9 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=70 x=109 y=9 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=71 x=235 y=0 width=6 height=7 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
+char id=72 x=115 y=9 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=73 x=121 y=9 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=74 x=137 y=8 width=4 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=75 x=228 y=0 width=6 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=76 x=7 y=11 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=77 x=221 y=0 width=6 height=7 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
+char id=78 x=85 y=9 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=79 x=0 y=11 width=6 height=7 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
+char id=80 x=73 y=9 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=81 x=51 y=0 width=7 height=9 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
+char id=82 x=174 y=0 width=7 height=7 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
+char id=83 x=25 y=11 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=84 x=182 y=0 width=7 height=7 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
+char id=85 x=19 y=11 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=86 x=198 y=0 width=7 height=7 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
+char id=87 x=206 y=0 width=7 height=7 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
+char id=88 x=134 y=0 width=7 height=7 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
+char id=89 x=158 y=0 width=7 height=7 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
+char id=90 x=249 y=0 width=6 height=7 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
+char id=91 x=41 y=0 width=3 height=10 xoffset=2 yoffset=1 xadvance=7 page=0 chnl=15
+char id=92 x=8 y=0 width=7 height=10 xoffset=0 yoffset=1 xadvance=7 page=0 chnl=15
+char id=93 x=45 y=0 width=3 height=10 xoffset=1 yoffset=1 xadvance=7 page=0 chnl=15
+char id=94 x=149 y=8 width=7 height=6 xoffset=0 yoffset=1 xadvance=7 page=0 chnl=15
+char id=95 x=47 y=19 width=7 height=1 xoffset=0 yoffset=9 xadvance=7 page=0 chnl=15
+char id=96 x=44 y=19 width=2 height=2 xoffset=2 yoffset=0 xadvance=7 page=0 chnl=15
+char id=97 x=181 y=8 width=6 height=6 xoffset=1 yoffset=3 xadvance=7 page=0 chnl=15
+char id=98 x=87 y=0 width=5 height=8 xoffset=1 yoffset=1 xadvance=7 page=0 chnl=15
+char id=99 x=227 y=8 width=5 height=6 xoffset=1 yoffset=3 xadvance=7 page=0 chnl=15
+char id=100 x=111 y=0 width=5 height=8 xoffset=1 yoffset=1 xadvance=7 page=0 chnl=15
+char id=101 x=221 y=8 width=5 height=6 xoffset=1 yoffset=3 xadvance=7 page=0 chnl=15
+char id=102 x=73 y=0 width=6 height=8 xoffset=1 yoffset=1 xadvance=7 page=0 chnl=15
+char id=103 x=93 y=0 width=5 height=8 xoffset=1 yoffset=3 xadvance=7 page=0 chnl=15
+char id=104 x=99 y=0 width=5 height=8 xoffset=1 yoffset=1 xadvance=7 page=0 chnl=15
+char id=105 x=123 y=0 width=3 height=8 xoffset=1 yoffset=1 xadvance=7 page=0 chnl=15
+char id=106 x=36 y=0 width=4 height=10 xoffset=1 yoffset=1 xadvance=7 page=0 chnl=15
+char id=107 x=80 y=0 width=6 height=8 xoffset=1 yoffset=1 xadvance=7 page=0 chnl=15
+char id=108 x=127 y=0 width=3 height=8 xoffset=1 yoffset=1 xadvance=7 page=0 chnl=15
+char id=109 x=157 y=8 width=7 height=6 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15
+char id=110 x=209 y=8 width=5 height=6 xoffset=1 yoffset=3 xadvance=7 page=0 chnl=15
+char id=111 x=215 y=8 width=5 height=6 xoffset=1 yoffset=3 xadvance=7 page=0 chnl=15
+char id=112 x=117 y=0 width=5 height=8 xoffset=1 yoffset=3 xadvance=7 page=0 chnl=15
+char id=113 x=105 y=0 width=5 height=8 xoffset=1 yoffset=3 xadvance=7 page=0 chnl=15
+char id=114 x=239 y=8 width=5 height=6 xoffset=1 yoffset=3 xadvance=7 page=0 chnl=15
+char id=115 x=251 y=8 width=4 height=6 xoffset=1 yoffset=3 xadvance=7 page=0 chnl=15
+char id=116 x=97 y=9 width=5 height=7 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=15
+char id=117 x=0 y=19 width=5 height=6 xoffset=1 yoffset=3 xadvance=7 page=0 chnl=15
+char id=118 x=165 y=8 width=7 height=6 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15
+char id=119 x=173 y=8 width=7 height=6 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15
+char id=120 x=195 y=8 width=6 height=6 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15
+char id=121 x=65 y=0 width=7 height=8 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15
+char id=122 x=233 y=8 width=5 height=6 xoffset=1 yoffset=3 xadvance=7 page=0 chnl=15
+char id=123 x=31 y=0 width=4 height=10 xoffset=2 yoffset=1 xadvance=7 page=0 chnl=15
+char id=124 x=49 y=0 width=1 height=10 xoffset=3 yoffset=1 xadvance=7 page=0 chnl=15
+char id=125 x=21 y=0 width=4 height=10 xoffset=1 yoffset=1 xadvance=7 page=0 chnl=15
+char id=126 x=33 y=19 width=7 height=2 xoffset=0 yoffset=5 xadvance=7 page=0 chnl=15
diff --git a/engine/src/core-data/Interface/Fonts/Console.png b/engine/src/core-data/Interface/Fonts/Console.png
new file mode 100644
index 000000000..821f92a6a
Binary files /dev/null and b/engine/src/core-data/Interface/Fonts/Console.png differ
diff --git a/engine/src/core-data/Interface/Fonts/Default.fnt b/engine/src/core-data/Interface/Fonts/Default.fnt
new file mode 100644
index 000000000..97230e855
--- /dev/null
+++ b/engine/src/core-data/Interface/Fonts/Default.fnt
@@ -0,0 +1,229 @@
+info face="null" size=17 bold=0 italic=0 charset="ASCII" unicode=0 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1
+common lineHeight=21 base=26 scaleW=256 scaleH=256 pages=1 packed=0
+page id=0 file="Default.png"
+chars count=224
+char id=32 x=30 y=110 width=5 height=3 xoffset=0 yoffset=16 xadvance=4 page=0 chnl=0
+char id=33 x=110 y=60 width=5 height=16 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=34 x=185 y=95 width=6 height=8 xoffset=0 yoffset=2 xadvance=5 page=0 chnl=0
+char id=35 x=116 y=60 width=11 height=16 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=0
+char id=36 x=242 y=0 width=10 height=19 xoffset=0 yoffset=2 xadvance=9 page=0 chnl=0
+char id=37 x=189 y=22 width=14 height=17 xoffset=0 yoffset=3 xadvance=13 page=0 chnl=0
+char id=38 x=204 y=22 width=11 height=17 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=0
+char id=39 x=181 y=95 width=3 height=9 xoffset=0 yoffset=2 xadvance=2 page=0 chnl=0
+char id=40 x=0 y=22 width=5 height=19 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=41 x=6 y=22 width=5 height=19 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=42 x=161 y=95 width=9 height=11 xoffset=0 yoffset=6 xadvance=8 page=0 chnl=0
+char id=43 x=150 y=95 width=10 height=12 xoffset=0 yoffset=6 xadvance=9 page=0 chnl=0
+char id=44 x=192 y=95 width=5 height=8 xoffset=0 yoffset=14 xadvance=4 page=0 chnl=0
+char id=45 x=245 y=95 width=6 height=5 xoffset=0 yoffset=10 xadvance=5 page=0 chnl=0
+char id=46 x=0 y=110 width=5 height=5 xoffset=0 yoffset=14 xadvance=4 page=0 chnl=0
+char id=47 x=12 y=22 width=5 height=19 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=48 x=216 y=22 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=49 x=128 y=60 width=10 height=16 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=50 x=139 y=60 width=10 height=16 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=51 x=227 y=22 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=52 x=150 y=60 width=10 height=16 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=53 x=238 y=22 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=54 x=0 y=42 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=55 x=161 y=60 width=10 height=16 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=56 x=11 y=42 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=57 x=22 y=42 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=58 x=43 y=95 width=5 height=13 xoffset=0 yoffset=6 xadvance=4 page=0 chnl=0
+char id=59 x=172 y=60 width=5 height=16 xoffset=0 yoffset=6 xadvance=4 page=0 chnl=0
+char id=60 x=49 y=95 width=10 height=13 xoffset=0 yoffset=6 xadvance=9 page=0 chnl=0
+char id=61 x=198 y=95 width=10 height=7 xoffset=0 yoffset=9 xadvance=9 page=0 chnl=0
+char id=62 x=60 y=95 width=10 height=13 xoffset=0 yoffset=6 xadvance=9 page=0 chnl=0
+char id=63 x=178 y=60 width=10 height=16 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=64 x=18 y=22 width=13 height=19 xoffset=0 yoffset=3 xadvance=12 page=0 chnl=0
+char id=65 x=189 y=60 width=12 height=16 xoffset=0 yoffset=3 xadvance=11 page=0 chnl=0
+char id=66 x=202 y=60 width=11 height=16 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=0
+char id=67 x=33 y=42 width=11 height=17 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=0
+char id=68 x=214 y=60 width=12 height=16 xoffset=0 yoffset=3 xadvance=11 page=0 chnl=0
+char id=69 x=227 y=60 width=11 height=16 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=0
+char id=70 x=239 y=60 width=10 height=16 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=71 x=45 y=42 width=12 height=17 xoffset=0 yoffset=3 xadvance=11 page=0 chnl=0
+char id=72 x=0 y=78 width=13 height=16 xoffset=0 yoffset=3 xadvance=12 page=0 chnl=0
+char id=73 x=14 y=78 width=5 height=16 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=74 x=58 y=42 width=8 height=17 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=0
+char id=75 x=20 y=78 width=11 height=16 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=0
+char id=76 x=32 y=78 width=10 height=16 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=77 x=43 y=78 width=15 height=16 xoffset=0 yoffset=3 xadvance=14 page=0 chnl=0
+char id=78 x=59 y=78 width=13 height=16 xoffset=0 yoffset=3 xadvance=12 page=0 chnl=0
+char id=79 x=67 y=42 width=14 height=17 xoffset=0 yoffset=3 xadvance=13 page=0 chnl=0
+char id=80 x=73 y=78 width=11 height=16 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=0
+char id=81 x=32 y=22 width=14 height=19 xoffset=0 yoffset=3 xadvance=13 page=0 chnl=0
+char id=82 x=85 y=78 width=11 height=16 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=0
+char id=83 x=82 y=42 width=11 height=17 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=0
+char id=84 x=97 y=78 width=10 height=16 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=85 x=94 y=42 width=13 height=17 xoffset=0 yoffset=3 xadvance=12 page=0 chnl=0
+char id=86 x=108 y=78 width=12 height=16 xoffset=0 yoffset=3 xadvance=11 page=0 chnl=0
+char id=87 x=121 y=78 width=16 height=16 xoffset=0 yoffset=3 xadvance=15 page=0 chnl=0
+char id=88 x=138 y=78 width=12 height=16 xoffset=0 yoffset=3 xadvance=11 page=0 chnl=0
+char id=89 x=151 y=78 width=11 height=16 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=0
+char id=90 x=163 y=78 width=11 height=16 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=0
+char id=91 x=47 y=22 width=5 height=19 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=92 x=53 y=22 width=5 height=19 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=93 x=59 y=22 width=5 height=19 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=94 x=171 y=95 width=9 height=10 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=0
+char id=95 x=6 y=110 width=9 height=5 xoffset=0 yoffset=15 xadvance=8 page=0 chnl=0
+char id=96 x=209 y=95 width=4 height=7 xoffset=0 yoffset=2 xadvance=3 page=0 chnl=0
+char id=97 x=237 y=78 width=9 height=14 xoffset=0 yoffset=6 xadvance=8 page=0 chnl=0
+char id=98 x=145 y=22 width=10 height=18 xoffset=0 yoffset=2 xadvance=9 page=0 chnl=0
+char id=99 x=247 y=78 width=9 height=14 xoffset=0 yoffset=6 xadvance=8 page=0 chnl=0
+char id=100 x=156 y=22 width=10 height=18 xoffset=0 yoffset=2 xadvance=9 page=0 chnl=0
+char id=101 x=0 y=95 width=10 height=14 xoffset=0 yoffset=6 xadvance=9 page=0 chnl=0
+char id=102 x=108 y=42 width=6 height=17 xoffset=0 yoffset=2 xadvance=5 page=0 chnl=0
+char id=103 x=115 y=42 width=9 height=17 xoffset=0 yoffset=6 xadvance=8 page=0 chnl=0
+char id=104 x=125 y=42 width=10 height=17 xoffset=0 yoffset=2 xadvance=9 page=0 chnl=0
+char id=105 x=175 y=78 width=5 height=16 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=106 x=69 y=0 width=7 height=20 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=107 x=136 y=42 width=9 height=17 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=0
+char id=108 x=146 y=42 width=5 height=17 xoffset=0 yoffset=2 xadvance=4 page=0 chnl=0
+char id=109 x=71 y=95 width=15 height=13 xoffset=0 yoffset=6 xadvance=14 page=0 chnl=0
+char id=110 x=87 y=95 width=10 height=13 xoffset=0 yoffset=6 xadvance=9 page=0 chnl=0
+char id=111 x=11 y=95 width=10 height=14 xoffset=0 yoffset=6 xadvance=9 page=0 chnl=0
+char id=112 x=152 y=42 width=10 height=17 xoffset=0 yoffset=6 xadvance=9 page=0 chnl=0
+char id=113 x=163 y=42 width=10 height=17 xoffset=0 yoffset=6 xadvance=9 page=0 chnl=0
+char id=114 x=98 y=95 width=7 height=13 xoffset=0 yoffset=6 xadvance=6 page=0 chnl=0
+char id=115 x=22 y=95 width=9 height=14 xoffset=0 yoffset=6 xadvance=8 page=0 chnl=0
+char id=116 x=181 y=78 width=6 height=16 xoffset=0 yoffset=4 xadvance=5 page=0 chnl=0
+char id=117 x=32 y=95 width=10 height=14 xoffset=0 yoffset=6 xadvance=9 page=0 chnl=0
+char id=118 x=106 y=95 width=9 height=13 xoffset=0 yoffset=6 xadvance=8 page=0 chnl=0
+char id=119 x=116 y=95 width=13 height=13 xoffset=0 yoffset=6 xadvance=12 page=0 chnl=0
+char id=120 x=130 y=95 width=9 height=13 xoffset=0 yoffset=6 xadvance=8 page=0 chnl=0
+char id=121 x=174 y=42 width=9 height=17 xoffset=0 yoffset=6 xadvance=8 page=0 chnl=0
+char id=122 x=140 y=95 width=9 height=13 xoffset=0 yoffset=6 xadvance=8 page=0 chnl=0
+char id=123 x=65 y=22 width=5 height=19 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=124 x=71 y=22 width=5 height=19 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=125 x=77 y=22 width=5 height=19 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=126 x=214 y=95 width=10 height=7 xoffset=0 yoffset=9 xadvance=9 page=0 chnl=0
+char id=127 x=36 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=128 x=46 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=129 x=56 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=130 x=66 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=131 x=76 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=132 x=86 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=133 x=96 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=134 x=106 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=135 x=116 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=136 x=126 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=137 x=136 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=138 x=146 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=139 x=156 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=140 x=166 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=141 x=176 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=142 x=186 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=143 x=196 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=144 x=206 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=145 x=216 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=146 x=226 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=147 x=236 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=148 x=246 y=110 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=149 x=0 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=150 x=10 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=151 x=20 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=152 x=30 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=153 x=40 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=154 x=50 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=155 x=60 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=156 x=70 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=157 x=80 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=158 x=90 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=159 x=100 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=160 x=110 y=116 width=5 height=3 xoffset=0 yoffset=16 xadvance=4 page=0 chnl=0
+char id=161 x=116 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=162 x=126 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=163 x=136 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=164 x=146 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=165 x=156 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=166 x=166 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=167 x=176 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=168 x=225 y=95 width=13 height=6 xoffset=0 yoffset=3 xadvance=12 page=0 chnl=0
+char id=169 x=186 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=170 x=196 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=171 x=206 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=172 x=216 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=173 x=226 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=174 x=236 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=175 x=16 y=110 width=13 height=5 xoffset=0 yoffset=4 xadvance=12 page=0 chnl=0
+char id=176 x=246 y=116 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=177 x=0 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=178 x=10 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=179 x=20 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=180 x=239 y=95 width=5 height=6 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=181 x=30 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=182 x=40 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=183 x=50 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=184 x=60 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=185 x=70 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=186 x=80 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=187 x=90 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=188 x=100 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=189 x=110 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=190 x=120 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=191 x=130 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=192 x=77 y=0 width=12 height=20 xoffset=0 yoffset=-1 xadvance=11 page=0 chnl=0
+char id=193 x=90 y=0 width=12 height=20 xoffset=0 yoffset=-1 xadvance=11 page=0 chnl=0
+char id=194 x=83 y=22 width=12 height=19 xoffset=0 yoffset=0 xadvance=11 page=0 chnl=0
+char id=195 x=140 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=196 x=96 y=22 width=12 height=19 xoffset=0 yoffset=0 xadvance=11 page=0 chnl=0
+char id=197 x=103 y=0 width=12 height=20 xoffset=0 yoffset=-1 xadvance=11 page=0 chnl=0
+char id=198 x=150 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=199 x=160 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=200 x=116 y=0 width=11 height=20 xoffset=0 yoffset=-1 xadvance=10 page=0 chnl=0
+char id=201 x=128 y=0 width=11 height=20 xoffset=0 yoffset=-1 xadvance=10 page=0 chnl=0
+char id=202 x=109 y=22 width=11 height=19 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=0
+char id=203 x=121 y=22 width=11 height=19 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=0
+char id=204 x=140 y=0 width=5 height=20 xoffset=0 yoffset=-1 xadvance=4 page=0 chnl=0
+char id=205 x=146 y=0 width=5 height=20 xoffset=0 yoffset=-1 xadvance=4 page=0 chnl=0
+char id=206 x=133 y=22 width=5 height=19 xoffset=0 yoffset=0 xadvance=4 page=0 chnl=0
+char id=207 x=139 y=22 width=5 height=19 xoffset=0 yoffset=0 xadvance=4 page=0 chnl=0
+char id=208 x=188 y=78 width=12 height=16 xoffset=0 yoffset=3 xadvance=11 page=0 chnl=0
+char id=209 x=170 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=210 x=0 y=0 width=14 height=21 xoffset=0 yoffset=-1 xadvance=13 page=0 chnl=0
+char id=211 x=15 y=0 width=14 height=21 xoffset=0 yoffset=-1 xadvance=13 page=0 chnl=0
+char id=212 x=152 y=0 width=14 height=20 xoffset=0 yoffset=0 xadvance=13 page=0 chnl=0
+char id=213 x=180 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=214 x=167 y=0 width=14 height=20 xoffset=0 yoffset=0 xadvance=13 page=0 chnl=0
+char id=215 x=190 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=216 x=200 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=217 x=30 y=0 width=13 height=21 xoffset=0 yoffset=-1 xadvance=12 page=0 chnl=0
+char id=218 x=44 y=0 width=13 height=21 xoffset=0 yoffset=-1 xadvance=12 page=0 chnl=0
+char id=219 x=182 y=0 width=13 height=20 xoffset=0 yoffset=0 xadvance=12 page=0 chnl=0
+char id=220 x=196 y=0 width=13 height=20 xoffset=0 yoffset=0 xadvance=12 page=0 chnl=0
+char id=221 x=210 y=0 width=11 height=20 xoffset=0 yoffset=-1 xadvance=10 page=0 chnl=0
+char id=222 x=201 y=78 width=11 height=16 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=0
+char id=223 x=167 y=22 width=11 height=18 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=0
+char id=224 x=184 y=42 width=9 height=17 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=0
+char id=225 x=194 y=42 width=9 height=17 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=0
+char id=226 x=204 y=42 width=9 height=17 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=0
+char id=227 x=210 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=228 x=214 y=42 width=9 height=17 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=0
+char id=229 x=179 y=22 width=9 height=18 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=0
+char id=230 x=220 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=231 x=230 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=232 x=224 y=42 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=233 x=235 y=42 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=234 x=246 y=42 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=235 x=0 y=60 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=236 x=213 y=78 width=5 height=16 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=237 x=219 y=78 width=5 height=16 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=238 x=225 y=78 width=5 height=16 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=239 x=231 y=78 width=5 height=16 xoffset=0 yoffset=3 xadvance=4 page=0 chnl=0
+char id=240 x=11 y=60 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=241 x=240 y=120 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=242 x=22 y=60 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=243 x=33 y=60 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=244 x=44 y=60 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=245 x=0 y=124 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=246 x=55 y=60 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=247 x=10 y=124 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=248 x=20 y=124 width=9 height=3 xoffset=0 yoffset=16 xadvance=8 page=0 chnl=0
+char id=249 x=66 y=60 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=250 x=77 y=60 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=251 x=88 y=60 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=252 x=99 y=60 width=10 height=17 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=0
+char id=253 x=222 y=0 width=9 height=20 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=0
+char id=254 x=58 y=0 width=10 height=21 xoffset=0 yoffset=2 xadvance=9 page=0 chnl=0
+char id=255 x=232 y=0 width=9 height=20 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=0
+kernings count=0
diff --git a/engine/src/core-data/Interface/Fonts/Default.png b/engine/src/core-data/Interface/Fonts/Default.png
new file mode 100644
index 000000000..3c2dee217
Binary files /dev/null and b/engine/src/core-data/Interface/Fonts/Default.png differ
diff --git a/engine/src/core-plugins/com/jme3/export/binary/BinaryClassField.java b/engine/src/core-plugins/com/jme3/export/binary/BinaryClassField.java
new file mode 100644
index 000000000..20da0e735
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/export/binary/BinaryClassField.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.export.binary;
+
+class BinaryClassField {
+
+ public static final byte BYTE = 0;
+ public static final byte BYTE_1D = 1;
+ public static final byte BYTE_2D = 2;
+
+ public static final byte INT = 10;
+ public static final byte INT_1D = 11;
+ public static final byte INT_2D = 12;
+
+ public static final byte FLOAT = 20;
+ public static final byte FLOAT_1D = 21;
+ public static final byte FLOAT_2D = 22;
+
+ public static final byte DOUBLE = 30;
+ public static final byte DOUBLE_1D = 31;
+ public static final byte DOUBLE_2D = 32;
+
+ public static final byte LONG = 40;
+ public static final byte LONG_1D = 41;
+ public static final byte LONG_2D = 42;
+
+ public static final byte SHORT = 50;
+ public static final byte SHORT_1D = 51;
+ public static final byte SHORT_2D = 52;
+
+ public static final byte BOOLEAN = 60;
+ public static final byte BOOLEAN_1D = 61;
+ public static final byte BOOLEAN_2D = 62;
+
+ public static final byte STRING = 70;
+ public static final byte STRING_1D = 71;
+ public static final byte STRING_2D = 72;
+
+ public static final byte BITSET = 80;
+
+ public static final byte SAVABLE = 90;
+ public static final byte SAVABLE_1D = 91;
+ public static final byte SAVABLE_2D = 92;
+
+ public static final byte SAVABLE_ARRAYLIST = 100;
+ public static final byte SAVABLE_ARRAYLIST_1D = 101;
+ public static final byte SAVABLE_ARRAYLIST_2D = 102;
+
+ public static final byte SAVABLE_MAP = 105;
+ public static final byte STRING_SAVABLE_MAP = 106;
+ public static final byte INT_SAVABLE_MAP = 107;
+
+ public static final byte FLOATBUFFER_ARRAYLIST = 110;
+ public static final byte BYTEBUFFER_ARRAYLIST = 111;
+
+ public static final byte FLOATBUFFER = 120;
+ public static final byte INTBUFFER = 121;
+ public static final byte BYTEBUFFER = 122;
+ public static final byte SHORTBUFFER = 123;
+
+
+ byte type;
+ String name;
+ byte alias;
+
+ BinaryClassField(String name, byte alias, byte type) {
+ this.name = name;
+ this.alias = alias;
+ this.type = type;
+ }
+}
diff --git a/engine/src/core-plugins/com/jme3/export/binary/BinaryClassLoader.java b/engine/src/core-plugins/com/jme3/export/binary/BinaryClassLoader.java
new file mode 100644
index 000000000..7adf0be6d
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/export/binary/BinaryClassLoader.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.export.binary;
+
+import java.io.IOException;
+import java.util.logging.Logger;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.Savable;
+
+/**
+ * This class is mis-named and is located in an inappropriate package:
+ * It is not binary-specific (it is in fact used for XML format too), and it
+ * is not a java.lang.ClassLoader, which is what "class loader" is for Java
+ * developers.
+ *
+ * @author mpowell
+ */
+public class BinaryClassLoader {
+
+ /**
+ * fromName creates a new Savable from the provided class name. First registered modules
+ * are checked to handle special cases, if the modules do not handle the class name, the
+ * class is instantiated directly.
+ * @param className the class name to create.
+ * @param inputCapsule the InputCapsule that will be used for loading the Savable (to look up ctor parameters)
+ * @return the Savable instance of the class.
+ * @throws InstantiationException thrown if the class does not have an empty constructor.
+ * @throws IllegalAccessException thrown if the class is not accessable.
+ * @throws ClassNotFoundException thrown if the class name is not in the classpath.
+ * @throws IOException when loading ctor parameters fails
+ */
+ public static Savable fromName(String className, InputCapsule inputCapsule) throws InstantiationException,
+ IllegalAccessException, ClassNotFoundException, IOException {
+
+ try {
+ return (Savable)Class.forName(className).newInstance();
+ }
+ catch (InstantiationException e) {
+ Logger.getLogger(BinaryClassLoader.class.getName()).severe(
+ "Could not access constructor of class '" + className + "'! \n" +
+ "Some types need to have the BinaryImporter set up in a special way. Please doublecheck the setup.");
+ throw e;
+ }
+ catch (IllegalAccessException e) {
+ Logger.getLogger(BinaryClassLoader.class.getName()).severe(
+ e.getMessage() + " \n" +
+ "Some types need to have the BinaryImporter set up in a special way. Please doublecheck the setup.");
+ throw e;
+ }
+ }
+
+}
diff --git a/engine/src/core-plugins/com/jme3/export/binary/BinaryClassObject.java b/engine/src/core-plugins/com/jme3/export/binary/BinaryClassObject.java
new file mode 100644
index 000000000..4151496f0
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/export/binary/BinaryClassObject.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.export.binary;
+
+import java.util.HashMap;
+
+class BinaryClassObject {
+
+ // When exporting, use nameFields field, importing use aliasFields.
+ HashMap nameFields;
+ HashMap aliasFields;
+
+ byte[] alias;
+ String className;
+
+}
diff --git a/engine/src/core-plugins/com/jme3/export/binary/BinaryExporter.java b/engine/src/core-plugins/com/jme3/export/binary/BinaryExporter.java
new file mode 100644
index 000000000..c34672f26
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/export/binary/BinaryExporter.java
@@ -0,0 +1,381 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.export.binary;
+
+import com.jme3.export.JmeExporter;
+import com.jme3.export.Savable;
+import com.jme3.math.FastMath;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.logging.Logger;
+
+/**
+ * Exports to the jME Binary Format. Format descriptor: (each numbered item
+ * denotes a series of bytes that follows sequentially one after the next.)
+ *
+ * 1. "number of classes" - four bytes - int value representing the number of
+ * entries in the class lookup table.
+ *
+ *
+ * CLASS TABLE: There will be X blocks each consisting of numbers 2 thru 9,
+ * where X = the number read in 1.
+ *
+ *
+ * 2. "class alias" - 1...X bytes, where X = ((int) FastMath.log(aliasCount,
+ * 256) + 1) - an alias used when writing object data to match an object to its
+ * appropriate object class type.
+ *
+ *
+ * 3. "full class name size" - four bytes - int value representing number of
+ * bytes to read in for next field.
+ *
+ *
+ * 4. "full class name" - 1...X bytes representing a String value, where X = the
+ * number read in 3. The String is the fully qualified class name of the Savable
+ * class, eg "com.jme.math.Vector3f
"
+ *
+ *
+ * 5. "number of fields" - four bytes - int value representing number of blocks
+ * to read in next (numbers 6 - 9), where each block represents a field in this
+ * class.
+ *
+ *
+ * 6. "field alias" - 1 byte - the alias used when writing out fields in a
+ * class. Because it is a single byte, a single class can not save out more than
+ * a total of 256 fields.
+ *
+ *
+ * 7. "field type" - 1 byte - a value representing the type of data a field
+ * contains. This value is taken from the static fields of
+ * com.jme.util.export.binary.BinaryClassField
.
+ *
+ *
+ * 8. "field name size" - 4 bytes - int value representing the size of the next
+ * field.
+ *
+ *
+ * 9. "field name" - 1...X bytes representing a String value, where X = the
+ * number read in 8. The String is the full String value used when writing the
+ * current field.
+ *
+ *
+ * 10. "number of unique objects" - four bytes - int value representing the
+ * number of data entries in this file.
+ *
+ *
+ * DATA LOOKUP TABLE: There will be X blocks each consisting of numbers 11 and
+ * 12, where X = the number read in 10.
+ *
+ *
+ * 11. "data id" - four bytes - int value identifying a single unique object
+ * that was saved in this data file.
+ *
+ *
+ * 12. "data location" - four bytes - int value representing the offset in the
+ * object data portion of this file where the object identified in 11 is
+ * located.
+ *
+ *
+ * 13. "future use" - four bytes - hardcoded int value 1.
+ *
+ *
+ * 14. "root id" - four bytes - int value identifying the top level object.
+ *
+ *
+ * OBJECT DATA SECTION: There will be X blocks each consisting of numbers 15
+ * thru 19, where X = the number of unique location values named in 12.
+ *
+ * 15. "class alias" - see 2.
+ *
+ *
+ * 16. "data length" - four bytes - int value representing the length in bytes
+ * of data stored in fields 17 and 18 for this object.
+ *
+ *
+ * FIELD ENTRY: There will be X blocks each consisting of numbers 18 and 19
+ *
+ *
+ * 17. "field alias" - see 6.
+ *
+ *
+ * 18. "field data" - 1...X bytes representing the field data. The data length
+ * is dependent on the field type and contents.
+ *
+ *
+ * @author Joshua Slack
+ */
+
+public class BinaryExporter implements JmeExporter {
+ private static final Logger logger = Logger.getLogger(BinaryExporter.class
+ .getName());
+
+ protected int aliasCount = 1;
+ protected int idCount = 1;
+
+ protected IdentityHashMap contentTable
+ = new IdentityHashMap();
+
+ protected HashMap locationTable
+ = new HashMap();
+
+ // key - class name, value = bco
+ private HashMap classes
+ = new HashMap();
+
+ private ArrayList contentKeys = new ArrayList();
+
+ public static boolean debug = false;
+ public static boolean useFastBufs = true;
+
+ public BinaryExporter() {
+ }
+
+ public static BinaryExporter getInstance() {
+ return new BinaryExporter();
+ }
+
+ public boolean save(Savable object, OutputStream os) throws IOException {
+ // reset some vars
+ aliasCount = 1;
+ idCount = 1;
+ classes.clear();
+ contentTable.clear();
+ locationTable.clear();
+ contentKeys.clear();
+
+ int id = processBinarySavable(object);
+
+ // write out tag table
+ int ttbytes = 0;
+ int classNum = classes.keySet().size();
+ int aliasWidth = ((int) FastMath.log(classNum, 256) + 1); // make all
+ // aliases a
+ // fixed width
+ os.write(ByteUtils.convertToBytes(classNum));
+ for (String key : classes.keySet()) {
+ BinaryClassObject bco = classes.get(key);
+
+ // write alias
+ byte[] aliasBytes = fixClassAlias(bco.alias,
+ aliasWidth);
+ os.write(aliasBytes);
+ ttbytes += aliasWidth;
+
+ // write classname size & classname
+ byte[] classBytes = key.getBytes();
+ os.write(ByteUtils.convertToBytes(classBytes.length));
+ os.write(classBytes);
+ ttbytes += 4 + classBytes.length;
+
+ os.write(ByteUtils.convertToBytes(bco.nameFields.size()));
+
+ for (String fieldName : bco.nameFields.keySet()) {
+ BinaryClassField bcf = bco.nameFields.get(fieldName);
+ os.write(bcf.alias);
+ os.write(bcf.type);
+
+ // write classname size & classname
+ byte[] fNameBytes = fieldName.getBytes();
+ os.write(ByteUtils.convertToBytes(fNameBytes.length));
+ os.write(fNameBytes);
+ ttbytes += 2 + 4 + fNameBytes.length;
+ }
+ }
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ // write out data to a seperate stream
+ int location = 0;
+ // keep track of location for each piece
+ HashMap> alreadySaved = new HashMap>(
+ contentTable.size());
+ for (Savable savable : contentKeys) {
+ // look back at previous written data for matches
+ String savableName = savable.getClass().getName();
+ BinaryIdContentPair pair = contentTable.get(savable);
+ ArrayList bucket = alreadySaved
+ .get(savableName + getChunk(pair));
+ int prevLoc = findPrevMatch(pair, bucket);
+ if (prevLoc != -1) {
+ locationTable.put(pair.getId(), prevLoc);
+ continue;
+ }
+
+ locationTable.put(pair.getId(), location);
+ if (bucket == null) {
+ bucket = new ArrayList();
+ alreadySaved.put(savableName + getChunk(pair), bucket);
+ }
+ bucket.add(pair);
+ byte[] aliasBytes = fixClassAlias(classes.get(savableName).alias, aliasWidth);
+ out.write(aliasBytes);
+ location += aliasWidth;
+ BinaryOutputCapsule cap = contentTable.get(savable).getContent();
+ out.write(ByteUtils.convertToBytes(cap.bytes.length));
+ location += 4; // length of bytes
+ out.write(cap.bytes);
+ location += cap.bytes.length;
+ }
+
+ // write out location table
+ // tag/location
+ int locNum = locationTable.keySet().size();
+ os.write(ByteUtils.convertToBytes(locNum));
+ int locbytes = 0;
+ for (Integer key : locationTable.keySet()) {
+ os.write(ByteUtils.convertToBytes(key));
+ os.write(ByteUtils.convertToBytes(locationTable.get(key)));
+ locbytes += 8;
+ }
+
+ // write out number of root ids - hardcoded 1 for now
+ os.write(ByteUtils.convertToBytes(1));
+
+ // write out root id
+ os.write(ByteUtils.convertToBytes(id));
+
+ // append stream to the output stream
+ out.writeTo(os);
+
+
+ out = null;
+ os = null;
+
+ if (debug ) {
+ logger.info("Stats:");
+ logger.info("classes: " + classNum);
+ logger.info("class table: " + ttbytes + " bytes");
+ logger.info("objects: " + locNum);
+ logger.info("location table: " + locbytes + " bytes");
+ logger.info("data: " + location + " bytes");
+ }
+
+ return true;
+ }
+
+ protected String getChunk(BinaryIdContentPair pair) {
+ return new String(pair.getContent().bytes, 0, Math.min(64, pair
+ .getContent().bytes.length));
+ }
+
+ protected int findPrevMatch(BinaryIdContentPair oldPair,
+ ArrayList bucket) {
+ if (bucket == null)
+ return -1;
+ for (int x = bucket.size(); --x >= 0;) {
+ BinaryIdContentPair pair = bucket.get(x);
+ if (pair.getContent().equals(oldPair.getContent()))
+ return locationTable.get(pair.getId());
+ }
+ return -1;
+ }
+
+ protected byte[] fixClassAlias(byte[] bytes, int width) {
+ if (bytes.length != width) {
+ byte[] newAlias = new byte[width];
+ for (int x = width - bytes.length; x < width; x++)
+ newAlias[x] = bytes[x - bytes.length];
+ return newAlias;
+ }
+ return bytes;
+ }
+
+ public boolean save(Savable object, File f) throws IOException {
+ File parentDirectory = f.getParentFile();
+ if(parentDirectory != null && !parentDirectory.exists()) {
+ parentDirectory.mkdirs();
+ }
+
+ FileOutputStream fos = new FileOutputStream(f);
+ boolean rVal = save(object, fos);
+ fos.close();
+ return rVal;
+ }
+
+ public BinaryOutputCapsule getCapsule(Savable object) {
+ return contentTable.get(object).getContent();
+ }
+
+ public int processBinarySavable(Savable object) throws IOException {
+ if (object == null) {
+ return -1;
+ }
+ BinaryClassObject bco = classes.get(object.getClass().getName());
+ // is this class been looked at before? in tagTable?
+ if (bco == null) {
+ bco = new BinaryClassObject();
+ bco.alias = generateTag();
+ bco.nameFields = new HashMap();
+ classes.put(object.getClass().getName(), bco);
+ }
+
+ // is object in contentTable?
+ if (contentTable.get(object) != null) {
+ return (contentTable.get(object).getId());
+ }
+ BinaryIdContentPair newPair = generateIdContentPair(bco);
+ BinaryIdContentPair old = contentTable.put(object, newPair);
+ if (old == null) {
+ contentKeys.add(object);
+ }
+ object.write(this);
+ newPair.getContent().finish();
+ return newPair.getId();
+
+ }
+
+ protected byte[] generateTag() {
+ int width = ((int) FastMath.log(aliasCount, 256) + 1);
+ int count = aliasCount;
+ aliasCount++;
+ byte[] bytes = new byte[width];
+ for (int x = width - 1; x >= 0; x--) {
+ int pow = (int) FastMath.pow(256, x);
+ int factor = count / pow;
+ bytes[width - x - 1] = (byte) factor;
+ count %= pow;
+ }
+ return bytes;
+ }
+
+ protected BinaryIdContentPair generateIdContentPair(BinaryClassObject bco) {
+ BinaryIdContentPair pair = new BinaryIdContentPair(idCount++,
+ new BinaryOutputCapsule(this, bco));
+ return pair;
+ }
+}
\ No newline at end of file
diff --git a/engine/src/core-plugins/com/jme3/export/binary/BinaryIdContentPair.java b/engine/src/core-plugins/com/jme3/export/binary/BinaryIdContentPair.java
new file mode 100644
index 000000000..b999a3c1f
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/export/binary/BinaryIdContentPair.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.export.binary;
+
+class BinaryIdContentPair {
+
+ private int id;
+ private BinaryOutputCapsule content;
+
+ BinaryIdContentPair(int id, BinaryOutputCapsule content) {
+ this.id = id;
+ this.content = content;
+ }
+
+ BinaryOutputCapsule getContent() {
+ return content;
+ }
+
+ void setContent(BinaryOutputCapsule content) {
+ this.content = content;
+ }
+
+ int getId() {
+ return id;
+ }
+
+ void setId(int id) {
+ this.id = id;
+ }
+}
diff --git a/engine/src/core-plugins/com/jme3/export/binary/BinaryImporter.java b/engine/src/core-plugins/com/jme3/export/binary/BinaryImporter.java
new file mode 100644
index 000000000..c7b44c610
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/export/binary/BinaryImporter.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.export.binary;
+
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetManager;
+import com.jme3.asset.ModelKey;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.ReadListener;
+import com.jme3.export.Savable;
+import com.jme3.math.FastMath;
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.nio.ByteOrder;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * @author Joshua Slack
+ */
+public final class BinaryImporter implements JmeImporter {
+ private static final Logger logger = Logger.getLogger(BinaryImporter.class
+ .getName());
+
+ private AssetManager assetManager;
+
+ //Key - alias, object - bco
+ private HashMap classes
+ = new HashMap();
+ //Key - id, object - the savable
+ private HashMap contentTable
+ = new HashMap();
+ //Key - savable, object - capsule
+ private IdentityHashMap capsuleTable
+ = new IdentityHashMap();
+ //Key - id, opject - location in the file
+ private HashMap locationTable
+ = new HashMap();
+
+ public static boolean debug = false;
+
+ private byte[] dataArray;
+ private int aliasWidth;
+
+ private static final boolean fastRead = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN;
+
+ public BinaryImporter() {
+ }
+
+ public static boolean canUseFastBuffers(){
+ return fastRead;
+ }
+
+ public static BinaryImporter getInstance() {
+ return new BinaryImporter();
+ }
+
+ public void setAssetManager(AssetManager manager){
+ this.assetManager = manager;
+ }
+
+ public AssetManager getAssetManager(){
+ return assetManager;
+ }
+
+ public Object load(AssetInfo info){
+ if (!(info.getKey() instanceof ModelKey))
+ throw new IllegalArgumentException("Model assets must be loaded using a ModelKey");
+
+ assetManager = info.getManager();
+
+ try{
+ InputStream is = info.openStream();
+ Savable s = load(is);
+ is.close();
+ return s;
+ }catch (IOException ex){
+ logger.log(Level.SEVERE, "An error occured while loading jME binary object", ex);
+ }
+ return null;
+ }
+
+ public Savable load(InputStream is) throws IOException {
+ return load(is, null, null);
+ }
+
+ public Savable load(InputStream is, ReadListener listener) throws IOException {
+ return load(is, listener, null);
+ }
+
+ public Savable load(InputStream is, ReadListener listener, ByteArrayOutputStream baos) throws IOException {
+ contentTable.clear();
+ BufferedInputStream bis = new BufferedInputStream(is);
+ int numClasses = ByteUtils.readInt(bis);
+ int bytes = 4;
+ aliasWidth = ((int)FastMath.log(numClasses, 256) + 1);
+
+ classes.clear();
+ for(int i = 0; i < numClasses; i++) {
+ String alias = readString(bis, aliasWidth);
+
+ int classLength = ByteUtils.readInt(bis);
+ String className = readString(bis, classLength);
+ BinaryClassObject bco = new BinaryClassObject();
+ bco.alias = alias.getBytes();
+ bco.className = className;
+
+ int fields = ByteUtils.readInt(bis);
+ bytes += (8 + aliasWidth + classLength);
+
+ bco.nameFields = new HashMap(fields);
+ bco.aliasFields = new HashMap(fields);
+ for (int x = 0; x < fields; x++) {
+ byte fieldAlias = (byte)bis.read();
+ byte fieldType = (byte)bis.read();
+
+ int fieldNameLength = ByteUtils.readInt(bis);
+ String fieldName = readString(bis, fieldNameLength);
+ BinaryClassField bcf = new BinaryClassField(fieldName, fieldAlias, fieldType);
+ bco.nameFields.put(fieldName, bcf);
+ bco.aliasFields.put(fieldAlias, bcf);
+ bytes += (6 + fieldNameLength);
+ }
+ classes.put(alias, bco);
+ }
+ if (listener != null) listener.readBytes(bytes);
+
+ int numLocs = ByteUtils.readInt(bis);
+ bytes = 4;
+
+ capsuleTable.clear();
+ locationTable.clear();
+ for(int i = 0; i < numLocs; i++) {
+ int id = ByteUtils.readInt(bis);
+ int loc = ByteUtils.readInt(bis);
+ locationTable.put(id, loc);
+ bytes += 8;
+ }
+
+ @SuppressWarnings("unused")
+ int numbIDs = ByteUtils.readInt(bis); // XXX: NOT CURRENTLY USED
+ int id = ByteUtils.readInt(bis);
+ bytes += 8;
+ if (listener != null) listener.readBytes(bytes);
+
+ if (baos == null) {
+ baos = new ByteArrayOutputStream(bytes);
+ } else {
+ baos.reset();
+ }
+ int size = -1;
+ byte[] cache = new byte[4096];
+ while((size = bis.read(cache)) != -1) {
+ baos.write(cache, 0, size);
+ if (listener != null) listener.readBytes(size);
+ }
+ bis = null;
+
+ dataArray = baos.toByteArray();
+ baos = null;
+
+ Savable rVal = readObject(id);
+ if (debug) {
+ logger.info("Importer Stats: ");
+ logger.info("Tags: "+numClasses);
+ logger.info("Objects: "+numLocs);
+ logger.info("Data Size: "+dataArray.length);
+ }
+ dataArray = null;
+ return rVal;
+ }
+
+ public Savable load(URL f) throws IOException {
+ return load(f, null);
+ }
+
+ public Savable load(URL f, ReadListener listener) throws IOException {
+ InputStream is = f.openStream();
+ Savable rVal = load(is, listener);
+ is.close();
+ return rVal;
+ }
+
+ public Savable load(File f) throws IOException {
+ return load(f, null);
+ }
+
+ public Savable load(File f, ReadListener listener) throws IOException {
+ FileInputStream fis = new FileInputStream(f);
+ Savable rVal = load(fis, listener);
+ fis.close();
+ return rVal;
+ }
+
+ public Savable load(byte[] data) throws IOException {
+ ByteArrayInputStream bais = new ByteArrayInputStream(data);
+ Savable rVal = load(bais);
+ bais.close();
+ return rVal;
+ }
+
+ public BinaryInputCapsule getCapsule(Savable id) {
+ return capsuleTable.get(id);
+ }
+
+ protected String readString(InputStream f, int length) throws IOException {
+ byte[] data = new byte[length];
+ for(int j = 0; j < length; j++) {
+ data[j] = (byte)f.read();
+ }
+
+ return new String(data);
+ }
+
+ protected String readString(int length, int offset) throws IOException {
+ byte[] data = new byte[length];
+ for(int j = 0; j < length; j++) {
+ data[j] = dataArray[j+offset];
+ }
+
+ return new String(data);
+ }
+
+ public Savable readObject(int id) {
+
+ if(contentTable.get(id) != null) {
+ return contentTable.get(id);
+ }
+
+ try {
+ int loc = locationTable.get(id);
+
+ String alias = readString(aliasWidth, loc);
+ loc+=aliasWidth;
+
+ BinaryClassObject bco = classes.get(alias);
+
+ if(bco == null) {
+ logger.logp(Level.SEVERE, this.getClass().toString(), "readObject(int id)", "NULL class object: " + alias);
+ return null;
+ }
+
+ int dataLength = ByteUtils.convertIntFromBytes(dataArray, loc);
+ loc+=4;
+
+ BinaryInputCapsule cap = new BinaryInputCapsule(this, bco);
+ cap.setContent(dataArray, loc, loc+dataLength);
+
+ Savable out = BinaryClassLoader.fromName(bco.className, cap);
+
+ capsuleTable.put(out, cap);
+ contentTable.put(id, out);
+
+ out.read(this);
+
+ capsuleTable.remove(out);
+
+ return out;
+
+ } catch (IOException e) {
+ logger.logp(Level.SEVERE, this.getClass().toString(), "readObject(int id)", "Exception", e);
+ return null;
+ } catch (ClassNotFoundException e) {
+ logger.logp(Level.SEVERE, this.getClass().toString(), "readObject(int id)", "Exception", e);
+ return null;
+ } catch (InstantiationException e) {
+ logger.logp(Level.SEVERE, this.getClass().toString(), "readObject(int id)", "Exception", e);
+ return null;
+ } catch (IllegalAccessException e) {
+ logger.logp(Level.SEVERE, this.getClass().toString(), "readObject(int id)", "Exception", e);
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/engine/src/core-plugins/com/jme3/export/binary/BinaryInputCapsule.java b/engine/src/core-plugins/com/jme3/export/binary/BinaryInputCapsule.java
new file mode 100644
index 000000000..d68c5ef0c
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/export/binary/BinaryInputCapsule.java
@@ -0,0 +1,1373 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.export.binary;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.Savable;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.IntMap;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.nio.ShortBuffer;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * @author Joshua Slack
+ */
+final class BinaryInputCapsule implements InputCapsule {
+
+ private static final Logger logger = Logger
+ .getLogger(BinaryInputCapsule.class.getName());
+
+ protected BinaryImporter importer;
+ protected BinaryClassObject cObj;
+ protected HashMap fieldData;
+
+ protected int index = 0;
+
+ public BinaryInputCapsule(BinaryImporter importer, BinaryClassObject bco) {
+ this.importer = importer;
+ this.cObj = bco;
+ }
+
+ public void setContent(byte[] content, int start, int limit) {
+ fieldData = new HashMap();
+ for (index = start; index < limit;) {
+ byte alias = content[index];
+
+ index++;
+
+ try {
+ byte type = cObj.aliasFields.get(alias).type;
+ Object value = null;
+
+ switch (type) {
+ case BinaryClassField.BITSET: {
+ value = readBitSet(content);
+ break;
+ }
+ case BinaryClassField.BOOLEAN: {
+ value = readBoolean(content);
+ break;
+ }
+ case BinaryClassField.BOOLEAN_1D: {
+ value = readBooleanArray(content);
+ break;
+ }
+ case BinaryClassField.BOOLEAN_2D: {
+ value = readBooleanArray2D(content);
+ break;
+ }
+ case BinaryClassField.BYTE: {
+ value = readByte(content);
+ break;
+ }
+ case BinaryClassField.BYTE_1D: {
+ value = readByteArray(content);
+ break;
+ }
+ case BinaryClassField.BYTE_2D: {
+ value = readByteArray2D(content);
+ break;
+ }
+ case BinaryClassField.BYTEBUFFER: {
+ value = readByteBuffer(content);
+ break;
+ }
+ case BinaryClassField.DOUBLE: {
+ value = readDouble(content);
+ break;
+ }
+ case BinaryClassField.DOUBLE_1D: {
+ value = readDoubleArray(content);
+ break;
+ }
+ case BinaryClassField.DOUBLE_2D: {
+ value = readDoubleArray2D(content);
+ break;
+ }
+ case BinaryClassField.FLOAT: {
+ value = readFloat(content);
+ break;
+ }
+ case BinaryClassField.FLOAT_1D: {
+ value = readFloatArray(content);
+ break;
+ }
+ case BinaryClassField.FLOAT_2D: {
+ value = readFloatArray2D(content);
+ break;
+ }
+ case BinaryClassField.FLOATBUFFER: {
+ value = readFloatBuffer(content);
+ break;
+ }
+ case BinaryClassField.FLOATBUFFER_ARRAYLIST: {
+ value = readFloatBufferArrayList(content);
+ break;
+ }
+ case BinaryClassField.BYTEBUFFER_ARRAYLIST: {
+ value = readByteBufferArrayList(content);
+ break;
+ }
+ case BinaryClassField.INT: {
+ value = readInt(content);
+ break;
+ }
+ case BinaryClassField.INT_1D: {
+ value = readIntArray(content);
+ break;
+ }
+ case BinaryClassField.INT_2D: {
+ value = readIntArray2D(content);
+ break;
+ }
+ case BinaryClassField.INTBUFFER: {
+ value = readIntBuffer(content);
+ break;
+ }
+ case BinaryClassField.LONG: {
+ value = readLong(content);
+ break;
+ }
+ case BinaryClassField.LONG_1D: {
+ value = readLongArray(content);
+ break;
+ }
+ case BinaryClassField.LONG_2D: {
+ value = readLongArray2D(content);
+ break;
+ }
+ case BinaryClassField.SAVABLE: {
+ value = readSavable(content);
+ break;
+ }
+ case BinaryClassField.SAVABLE_1D: {
+ value = readSavableArray(content);
+ break;
+ }
+ case BinaryClassField.SAVABLE_2D: {
+ value = readSavableArray2D(content);
+ break;
+ }
+ case BinaryClassField.SAVABLE_ARRAYLIST: {
+ value = readSavableArray(content);
+ break;
+ }
+ case BinaryClassField.SAVABLE_ARRAYLIST_1D: {
+ value = readSavableArray2D(content);
+ break;
+ }
+ case BinaryClassField.SAVABLE_ARRAYLIST_2D: {
+ value = readSavableArray3D(content);
+ break;
+ }
+ case BinaryClassField.SAVABLE_MAP: {
+ value = readSavableMap(content);
+ break;
+ }
+ case BinaryClassField.STRING_SAVABLE_MAP: {
+ value = readStringSavableMap(content);
+ break;
+ }
+ case BinaryClassField.INT_SAVABLE_MAP: {
+ value = readIntSavableMap(content);
+ break;
+ }
+ case BinaryClassField.SHORT: {
+ value = readShort(content);
+ break;
+ }
+ case BinaryClassField.SHORT_1D: {
+ value = readShortArray(content);
+ break;
+ }
+ case BinaryClassField.SHORT_2D: {
+ value = readShortArray2D(content);
+ break;
+ }
+ case BinaryClassField.SHORTBUFFER: {
+ value = readShortBuffer(content);
+ break;
+ }
+ case BinaryClassField.STRING: {
+ value = readString(content);
+ break;
+ }
+ case BinaryClassField.STRING_1D: {
+ value = readStringArray(content);
+ break;
+ }
+ case BinaryClassField.STRING_2D: {
+ value = readStringArray2D(content);
+ break;
+ }
+
+ default:
+ // skip put statement
+ continue;
+ }
+
+ fieldData.put(alias, value);
+
+ } catch (IOException e) {
+ logger.logp(Level.SEVERE, this.getClass().toString(),
+ "setContent(byte[] content)", "Exception", e);
+ }
+ }
+ }
+
+ public BitSet readBitSet(String name, BitSet defVal) throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (BitSet) fieldData.get(field.alias);
+ }
+
+ public boolean readBoolean(String name, boolean defVal) throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return ((Boolean) fieldData.get(field.alias)).booleanValue();
+ }
+
+ public boolean[] readBooleanArray(String name, boolean[] defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (boolean[]) fieldData.get(field.alias);
+ }
+
+ public boolean[][] readBooleanArray2D(String name, boolean[][] defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (boolean[][]) fieldData.get(field.alias);
+ }
+
+ public byte readByte(String name, byte defVal) throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return ((Byte) fieldData.get(field.alias)).byteValue();
+ }
+
+ public byte[] readByteArray(String name, byte[] defVal) throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (byte[]) fieldData.get(field.alias);
+ }
+
+ public byte[][] readByteArray2D(String name, byte[][] defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (byte[][]) fieldData.get(field.alias);
+ }
+
+ public ByteBuffer readByteBuffer(String name, ByteBuffer defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (ByteBuffer) fieldData.get(field.alias);
+ }
+
+ @SuppressWarnings("unchecked")
+ public ArrayList readByteBufferArrayList(String name,
+ ArrayList defVal) throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (ArrayList) fieldData.get(field.alias);
+ }
+
+ public double readDouble(String name, double defVal) throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return ((Double) fieldData.get(field.alias)).doubleValue();
+ }
+
+ public double[] readDoubleArray(String name, double[] defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (double[]) fieldData.get(field.alias);
+ }
+
+ public double[][] readDoubleArray2D(String name, double[][] defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (double[][]) fieldData.get(field.alias);
+ }
+
+ public float readFloat(String name, float defVal) throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return ((Float) fieldData.get(field.alias)).floatValue();
+ }
+
+ public float[] readFloatArray(String name, float[] defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (float[]) fieldData.get(field.alias);
+ }
+
+ public float[][] readFloatArray2D(String name, float[][] defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (float[][]) fieldData.get(field.alias);
+ }
+
+ public FloatBuffer readFloatBuffer(String name, FloatBuffer defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (FloatBuffer) fieldData.get(field.alias);
+ }
+
+ @SuppressWarnings("unchecked")
+ public ArrayList readFloatBufferArrayList(String name,
+ ArrayList defVal) throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (ArrayList) fieldData.get(field.alias);
+ }
+
+ public int readInt(String name, int defVal) throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return ((Integer) fieldData.get(field.alias)).intValue();
+ }
+
+ public int[] readIntArray(String name, int[] defVal) throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (int[]) fieldData.get(field.alias);
+ }
+
+ public int[][] readIntArray2D(String name, int[][] defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (int[][]) fieldData.get(field.alias);
+ }
+
+ public IntBuffer readIntBuffer(String name, IntBuffer defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (IntBuffer) fieldData.get(field.alias);
+ }
+
+ public long readLong(String name, long defVal) throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return ((Long) fieldData.get(field.alias)).longValue();
+ }
+
+ public long[] readLongArray(String name, long[] defVal) throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (long[]) fieldData.get(field.alias);
+ }
+
+ public long[][] readLongArray2D(String name, long[][] defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (long[][]) fieldData.get(field.alias);
+ }
+
+ public Savable readSavable(String name, Savable defVal) throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ Object value = fieldData.get(field.alias);
+ if (value == null)
+ return null;
+ else if (value instanceof ID) {
+ value = importer.readObject(((ID) value).id);
+ fieldData.put(field.alias, value);
+ return (Savable) value;
+ } else
+ return defVal;
+ }
+
+ public Savable[] readSavableArray(String name, Savable[] defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ Object[] values = (Object[]) fieldData.get(field.alias);
+ if (values instanceof ID[]) {
+ values = resolveIDs(values);
+ fieldData.put(field.alias, values);
+ return (Savable[]) values;
+ } else
+ return defVal;
+ }
+
+ private Savable[] resolveIDs(Object[] values) {
+ if (values != null) {
+ Savable[] savables = new Savable[values.length];
+ for (int i = 0; i < values.length; i++) {
+ final ID id = (ID) values[i];
+ savables[i] = id != null ? importer.readObject(id.id) : null;
+ }
+ return savables;
+ } else {
+ return null;
+ }
+ }
+
+ public Savable[][] readSavableArray2D(String name, Savable[][] defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null ||!fieldData.containsKey(field.alias))
+ return defVal;
+ Object[][] values = (Object[][]) fieldData.get(field.alias);
+ if (values instanceof ID[][]) {
+ Savable[][] savables = new Savable[values.length][];
+ for (int i = 0; i < values.length; i++) {
+ if (values[i] != null) {
+ savables[i] = resolveIDs(values[i]);
+ } else savables[i] = null;
+ }
+ values = savables;
+ fieldData.put(field.alias, values);
+ }
+ return (Savable[][]) values;
+ }
+
+ public Savable[][][] readSavableArray3D(String name, Savable[][][] defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ Object[][][] values = (Object[][][]) fieldData.get(field.alias);
+ if (values instanceof ID[][][]) {
+ Savable[][][] savables = new Savable[values.length][][];
+ for (int i = 0; i < values.length; i++) {
+ if (values[i] != null) {
+ savables[i] = new Savable[values[i].length][];
+ for (int j = 0; j < values[i].length; j++) {
+ savables[i][j] = resolveIDs(values[i][j]);
+ }
+ } else savables[i] = null;
+ }
+ fieldData.put(field.alias, savables);
+ return savables;
+ } else
+ return defVal;
+ }
+
+ private ArrayList savableArrayListFromArray(Savable[] savables) {
+ if(savables == null) {
+ return null;
+ }
+ ArrayList arrayList = new ArrayList(savables.length);
+ for (int x = 0; x < savables.length; x++) {
+ arrayList.add(savables[x]);
+ }
+ return arrayList;
+ }
+
+ // Assumes array of size 2 arrays where pos 0 is key and pos 1 is value.
+ private Map savableMapFrom2DArray(Savable[][] savables) {
+ if(savables == null) {
+ return null;
+ }
+ Map map = new HashMap(savables.length);
+ for (int x = 0; x < savables.length; x++) {
+ map.put(savables[x][0], savables[x][1]);
+ }
+ return map;
+ }
+
+ private Map stringSavableMapFromKV(String[] keys, Savable[] values) {
+ if(keys == null || values == null) {
+ return null;
+ }
+
+ Map map = new HashMap(keys.length);
+ for (int x = 0; x < keys.length; x++)
+ map.put(keys[x], values[x]);
+
+ return map;
+ }
+
+ private IntMap intSavableMapFromKV(int[] keys, Savable[] values) {
+ if(keys == null || values == null) {
+ return null;
+ }
+
+ IntMap map = new IntMap(keys.length);
+ for (int x = 0; x < keys.length; x++)
+ map.put(keys[x], values[x]);
+
+ return map;
+ }
+
+ public ArrayList readSavableArrayList(String name, ArrayList defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ Object value = fieldData.get(field.alias);
+ if (value instanceof ID[]) {
+ // read Savable array and convert to ArrayList
+ Savable[] savables = readSavableArray(name, null);
+ value = savableArrayListFromArray(savables);
+ fieldData.put(field.alias, value);
+ }
+ return (ArrayList) value;
+ }
+
+ public ArrayList[] readSavableArrayListArray(String name, ArrayList[] defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ Object value = fieldData.get(field.alias);
+ if (value instanceof ID[][]) {
+ // read 2D Savable array and convert to ArrayList array
+ Savable[][] savables = readSavableArray2D(name, null);
+ if (savables != null) {
+ ArrayList[] arrayLists = new ArrayList[savables.length];
+ for (int i = 0; i < savables.length; i++) {
+ arrayLists[i] = savableArrayListFromArray(savables[i]);
+ }
+ value = arrayLists;
+ } else
+ value = defVal;
+ fieldData.put(field.alias, value);
+ }
+ return (ArrayList[]) value;
+ }
+
+ public ArrayList[][] readSavableArrayListArray2D(String name,
+ ArrayList[][] defVal) throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ Object value = fieldData.get(field.alias);
+ if (value instanceof ID[][][]) {
+ // read 3D Savable array and convert to 2D ArrayList array
+ Savable[][][] savables = readSavableArray3D(name, null);
+ if (savables != null && savables.length > 0) {
+ ArrayList[][] arrayLists = new ArrayList[savables.length][];
+ for (int i = 0; i < savables.length; i++) {
+ arrayLists[i] = new ArrayList[savables[i].length];
+ for (int j = 0; j < savables[i].length; j++) {
+ arrayLists[i][j] = savableArrayListFromArray(savables[i][j]);
+ }
+ }
+ value = arrayLists;
+ } else
+ value = defVal;
+ fieldData.put(field.alias, value);
+ }
+ return (ArrayList[][]) value;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Map extends Savable, ? extends Savable> readSavableMap(String name, Map extends Savable, ? extends Savable> defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ Object value = fieldData.get(field.alias);
+ if (value instanceof ID[][]) {
+ // read Savable array and convert to Map
+ Savable[][] savables = readSavableArray2D(name, null);
+ value = savableMapFrom2DArray(savables);
+ fieldData.put(field.alias, value);
+ }
+ return (Map extends Savable, ? extends Savable>) value;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Map readStringSavableMap(String name, Map defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ Object value = fieldData.get(field.alias);
+ if (value instanceof StringIDMap) {
+ // read Savable array and convert to Map values
+ StringIDMap in = (StringIDMap) value;
+ Savable[] values = resolveIDs(in.values);
+ value = stringSavableMapFromKV(in.keys, values);
+ fieldData.put(field.alias, value);
+ }
+ return (Map) value;
+ }
+
+ @SuppressWarnings("unchecked")
+ public IntMap extends Savable> readIntSavableMap(String name, IntMap extends Savable> defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ Object value = fieldData.get(field.alias);
+ if (value instanceof IntIDMap) {
+ // read Savable array and convert to Map values
+ IntIDMap in = (IntIDMap) value;
+ Savable[] values = resolveIDs(in.values);
+ value = intSavableMapFromKV(in.keys, values);
+ fieldData.put(field.alias, value);
+ }
+ return (IntMap) value;
+ }
+
+ public short readShort(String name, short defVal) throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return ((Short) fieldData.get(field.alias)).shortValue();
+ }
+
+ public short[] readShortArray(String name, short[] defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (short[]) fieldData.get(field.alias);
+ }
+
+ public short[][] readShortArray2D(String name, short[][] defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (short[][]) fieldData.get(field.alias);
+ }
+
+ public ShortBuffer readShortBuffer(String name, ShortBuffer defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (ShortBuffer) fieldData.get(field.alias);
+ }
+
+ public String readString(String name, String defVal) throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (String) fieldData.get(field.alias);
+ }
+
+ public String[] readStringArray(String name, String[] defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (String[]) fieldData.get(field.alias);
+ }
+
+ public String[][] readStringArray2D(String name, String[][] defVal)
+ throws IOException {
+ BinaryClassField field = cObj.nameFields.get(name);
+ if (field == null || !fieldData.containsKey(field.alias))
+ return defVal;
+ return (String[][]) fieldData.get(field.alias);
+ }
+
+ // byte primitive
+
+ protected byte readByte(byte[] content) throws IOException {
+ byte value = content[index];
+ index++;
+ return value;
+ }
+
+ protected byte readByteForBuffer(byte[] content) throws IOException {
+ byte value = content[index];
+ index++;
+ return value;
+ }
+
+ protected byte[] readByteArray(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ byte[] value = new byte[length];
+ for (int x = 0; x < length; x++)
+ value[x] = readByte(content);
+ return value;
+ }
+
+ protected byte[][] readByteArray2D(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ byte[][] value = new byte[length][];
+ for (int x = 0; x < length; x++)
+ value[x] = readByteArray(content);
+ return value;
+ }
+
+ // int primitive
+
+ protected int readIntForBuffer(byte[] content){
+ int number = ((content[index+3] & 0xFF) << 24)
+ + ((content[index+2] & 0xFF) << 16)
+ + ((content[index+1] & 0xFF) << 8)
+ + (content[index] & 0xFF);
+ index += 4;
+ return number;
+ }
+
+ protected int readInt(byte[] content) throws IOException {
+ byte[] bytes = inflateFrom(content, index);
+ index += 1 + bytes.length;
+ bytes = ByteUtils.rightAlignBytes(bytes, 4);
+ int value = ByteUtils.convertIntFromBytes(bytes);
+ if (value == BinaryOutputCapsule.NULL_OBJECT
+ || value == BinaryOutputCapsule.DEFAULT_OBJECT)
+ index -= 4;
+ return value;
+ }
+
+ protected int[] readIntArray(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ int[] value = new int[length];
+ for (int x = 0; x < length; x++)
+ value[x] = readInt(content);
+ return value;
+ }
+
+ protected int[][] readIntArray2D(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ int[][] value = new int[length][];
+ for (int x = 0; x < length; x++)
+ value[x] = readIntArray(content);
+ return value;
+ }
+
+ // float primitive
+
+ protected float readFloat(byte[] content) throws IOException {
+ float value = ByteUtils.convertFloatFromBytes(content, index);
+ index += 4;
+ return value;
+ }
+
+ protected float readFloatForBuffer(byte[] content) throws IOException {
+ int number = readIntForBuffer(content);
+ return Float.intBitsToFloat(number);
+ }
+
+ protected float[] readFloatArray(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ float[] value = new float[length];
+ for (int x = 0; x < length; x++)
+ value[x] = readFloat(content);
+ return value;
+ }
+
+ protected float[][] readFloatArray2D(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ float[][] value = new float[length][];
+ for (int x = 0; x < length; x++)
+ value[x] = readFloatArray(content);
+ return value;
+ }
+
+ // double primitive
+
+ protected double readDouble(byte[] content) throws IOException {
+ double value = ByteUtils.convertDoubleFromBytes(content, index);
+ index += 8;
+ return value;
+ }
+
+ protected double[] readDoubleArray(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ double[] value = new double[length];
+ for (int x = 0; x < length; x++)
+ value[x] = readDouble(content);
+ return value;
+ }
+
+ protected double[][] readDoubleArray2D(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ double[][] value = new double[length][];
+ for (int x = 0; x < length; x++)
+ value[x] = readDoubleArray(content);
+ return value;
+ }
+
+ // long primitive
+
+ protected long readLong(byte[] content) throws IOException {
+ byte[] bytes = inflateFrom(content, index);
+ index += 1 + bytes.length;
+ bytes = ByteUtils.rightAlignBytes(bytes, 8);
+ long value = ByteUtils.convertLongFromBytes(bytes);
+ return value;
+ }
+
+ protected long[] readLongArray(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ long[] value = new long[length];
+ for (int x = 0; x < length; x++)
+ value[x] = readLong(content);
+ return value;
+ }
+
+ protected long[][] readLongArray2D(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ long[][] value = new long[length][];
+ for (int x = 0; x < length; x++)
+ value[x] = readLongArray(content);
+ return value;
+ }
+
+ // short primitive
+
+ protected short readShort(byte[] content) throws IOException {
+ short value = ByteUtils.convertShortFromBytes(content, index);
+ index += 2;
+ return value;
+ }
+
+ protected short readShortForBuffer(byte[] content) throws IOException {
+ short number = (short) ((content[index+0] & 0xFF)
+ + ((content[index+1] & 0xFF) << 8));
+ index += 2;
+ return number;
+ }
+
+ protected short[] readShortArray(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ short[] value = new short[length];
+ for (int x = 0; x < length; x++)
+ value[x] = readShort(content);
+ return value;
+ }
+
+ protected short[][] readShortArray2D(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ short[][] value = new short[length][];
+ for (int x = 0; x < length; x++)
+ value[x] = readShortArray(content);
+ return value;
+ }
+
+ // boolean primitive
+
+ protected boolean readBoolean(byte[] content) throws IOException {
+ boolean value = ByteUtils.convertBooleanFromBytes(content, index);
+ index += 1;
+ return value;
+ }
+
+ protected boolean[] readBooleanArray(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ boolean[] value = new boolean[length];
+ for (int x = 0; x < length; x++)
+ value[x] = readBoolean(content);
+ return value;
+ }
+
+ protected boolean[][] readBooleanArray2D(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ boolean[][] value = new boolean[length][];
+ for (int x = 0; x < length; x++)
+ value[x] = readBooleanArray(content);
+ return value;
+ }
+
+ /*
+ * UTF-8 crash course:
+ *
+ * UTF-8 codepoints map to UTF-16 codepoints and vv, which is what Java uses for it's Strings.
+ * (so a UTF-8 codepoint can contain all possible values for a Java char)
+ *
+ * A UTF-8 codepoint can be 1, 2 or 3 bytes long. How long a codepint is can be told by reading the first byte:
+ * b < 0x80, 1 byte
+ * (b & 0xC0) == 0xC0, 2 bytes
+ * (b & 0xE0) == 0xE0, 3 bytes
+ *
+ * However there is an additional restriction to UTF-8, to enable you to find the start of a UTF-8 codepoint,
+ * if you start reading at a random point in a UTF-8 byte stream. That's why UTF-8 requires for the second and third byte of
+ * a multibyte codepoint:
+ * (b & 0x80) == 0x80 (in other words, first bit must be 1)
+ */
+ private final static int UTF8_START = 0; // next byte should be the start of a new
+ private final static int UTF8_2BYTE = 2; // next byte should be the second byte of a 2 byte codepoint
+ private final static int UTF8_3BYTE_1 = 3; // next byte should be the second byte of a 3 byte codepoint
+ private final static int UTF8_3BYTE_2 = 4; // next byte should be the third byte of a 3 byte codepoint
+ private final static int UTF8_ILLEGAL = 10; // not an UTF8 string
+
+ // String
+ protected String readString(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+
+ /*
+ * @see ISSUE 276
+ *
+ * We'll transfer the bytes into a seperate byte array.
+ * While we do that we'll take the opportunity to check if the byte data is valid UTF-8.
+ *
+ * If it is not UTF-8 it is most likely saved with the BinaryOutputCapsule bug, that saves Strings using their native
+ * encoding. Unfortunatly there is no way to know what encoding was used, so we'll parse using the most common one in
+ * that case; latin-1 aka ISO8859_1
+ *
+ * Encoding of "low" ASCII codepoint (in plain speak: when no special characters are used) will usually look the same
+ * for UTF-8 and the other 1 byte codepoint encodings (espc true for numbers and regular letters of the alphabet). So these
+ * are valid UTF-8 and will give the same result (at most a few charakters will appear different, such as the euro sign).
+ *
+ * However, when "high" codepoints are used (any codepoint that over 0x7F, in other words where the first bit is a 1) it's
+ * a different matter and UTF-8 and the 1 byte encoding greatly will differ, as well as most 1 byte encodings relative to each
+ * other.
+ *
+ * It is impossible to detect which one-byte encoding is used. Since UTF8 and practically all 1-byte encodings share the most
+ * used characters (the "none-high" ones) parsing them will give the same result. However, not all byte sequences are legal in
+ * UTF-8 (see explantion above). If not UTF-8 encoded content is detected we therefor fallback on latin1. We also log a warning.
+ *
+ * By this method we detect all use of 1 byte encoding if they:
+ * - use a "high" codepoint after a "low" codepoint or a sequence of codepoints that is valid as UTF-8 bytes, that starts with 1000
+ * - use a "low" codepoint after a "high" codepoint
+ * - use a "low" codepoint after "high" codepoint, after a "high" codepoint that starts with 1110
+ *
+ * In practise this means that unless 2 or 3 "high" codepoints are used after each other in proper order, we'll detect the string
+ * was not originally UTF-8 encoded.
+ *
+ */
+ byte[] bytes = new byte[length];
+ int utf8State = UTF8_START;
+ int b;
+ for (int x = 0; x < length; x++) {
+ bytes[x] = content[index++];
+ b = (int) bytes[x] & 0xFF; // unsign our byte
+
+ switch (utf8State) {
+ case UTF8_START:
+ if (b < 0x80) {
+ // good
+ }
+ else if ((b & 0xC0) == 0xC0) {
+ utf8State = UTF8_2BYTE;
+ }
+ else if ((b & 0xE0) == 0xE0) {
+ utf8State = UTF8_3BYTE_1;
+ }
+ else {
+ utf8State = UTF8_ILLEGAL;
+ }
+ break;
+ case UTF8_3BYTE_1:
+ case UTF8_3BYTE_2:
+ case UTF8_2BYTE:
+ if ((b & 0x80) == 0x80)
+ utf8State = utf8State == UTF8_3BYTE_1 ? UTF8_3BYTE_2 : UTF8_START;
+ else
+ utf8State = UTF8_ILLEGAL;
+ break;
+ }
+ }
+
+ try {
+ // even though so far the parsing might have been a legal UTF-8 sequence, only if a codepoint is fully given is it correct UTF-8
+ if (utf8State == UTF8_START) {
+ // Java misspells UTF-8 as UTF8 for official use in java.lang
+ return new String(bytes, "UTF8");
+ }
+ else {
+ logger.log(
+ Level.WARNING,
+ "Your export has been saved with an incorrect encoding for it's String fields which means it might not load correctly " +
+ "due to encoding issues. You should probably re-export your work. See ISSUE 276 in the jME issue tracker."
+ );
+ // We use ISO8859_1 to be consistent across platforms. We could default to native encoding, but this would lead to inconsistent
+ // behaviour across platforms!
+ // Developers that have previously saved their exports using the old exporter (wich uses native encoding), can temporarly
+ // remove the ""ISO8859_1" parameter, and change the above if statement to "if (false)".
+ // They should then import and re-export their models using the same enviroment they were orginally created in.
+ return new String(bytes, "ISO8859_1");
+ }
+ } catch (UnsupportedEncodingException uee) {
+ // as a last resort fall back to platform native.
+ // JavaDoc is vague about what happens when a decoding a String that contains un undecodable sequence
+ // it also doesn't specify which encodings have to be supported (though UTF-8 and ISO8859 have been in the SUN JRE since at least 1.1)
+ logger.log(
+ Level.SEVERE,
+ "Your export has been saved with an incorrect encoding or your version of Java is unable to decode the stored string. " +
+ "While your export may load correctly by falling back, using it on different platforms or java versions might lead to "+
+ "very strange inconsitenties. You should probably re-export your work. See ISSUE 276 in the jME issue tracker."
+ );
+ return new String(bytes);
+ }
+ }
+
+ protected String[] readStringArray(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ String[] value = new String[length];
+ for (int x = 0; x < length; x++)
+ value[x] = readString(content);
+ return value;
+ }
+
+ protected String[][] readStringArray2D(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ String[][] value = new String[length][];
+ for (int x = 0; x < length; x++)
+ value[x] = readStringArray(content);
+ return value;
+ }
+
+ // BitSet
+
+ protected BitSet readBitSet(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ BitSet value = new BitSet(length);
+ for (int x = 0; x < length; x++)
+ value.set(x, readBoolean(content));
+ return value;
+ }
+
+ // INFLATOR for int and long
+
+ protected static byte[] inflateFrom(byte[] contents, int index) {
+ byte firstByte = contents[index];
+ if (firstByte == BinaryOutputCapsule.NULL_OBJECT)
+ return ByteUtils.convertToBytes(BinaryOutputCapsule.NULL_OBJECT);
+ else if (firstByte == BinaryOutputCapsule.DEFAULT_OBJECT)
+ return ByteUtils.convertToBytes(BinaryOutputCapsule.DEFAULT_OBJECT);
+ else if (firstByte == 0)
+ return new byte[0];
+ else {
+ byte[] rVal = new byte[firstByte];
+ for (int x = 0; x < rVal.length; x++)
+ rVal[x] = contents[x + 1 + index];
+ return rVal;
+ }
+ }
+
+ // BinarySavable
+
+ protected ID readSavable(byte[] content) throws IOException {
+ int id = readInt(content);
+ if (id == BinaryOutputCapsule.NULL_OBJECT) {
+ return null;
+ }
+
+ return new ID(id);
+ }
+
+ // BinarySavable array
+
+ protected ID[] readSavableArray(byte[] content) throws IOException {
+ int elements = readInt(content);
+ if (elements == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ ID[] rVal = new ID[elements];
+ for (int x = 0; x < elements; x++) {
+ rVal[x] = readSavable(content);
+ }
+ return rVal;
+ }
+
+ protected ID[][] readSavableArray2D(byte[] content) throws IOException {
+ int elements = readInt(content);
+ if (elements == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ ID[][] rVal = new ID[elements][];
+ for (int x = 0; x < elements; x++) {
+ rVal[x] = readSavableArray(content);
+ }
+ return rVal;
+ }
+
+ protected ID[][][] readSavableArray3D(byte[] content) throws IOException {
+ int elements = readInt(content);
+ if (elements == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ ID[][][] rVal = new ID[elements][][];
+ for (int x = 0; x < elements; x++) {
+ rVal[x] = readSavableArray2D(content);
+ }
+ return rVal;
+ }
+
+ // BinarySavable map
+
+ protected ID[][] readSavableMap(byte[] content) throws IOException {
+ int elements = readInt(content);
+ if (elements == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ ID[][] rVal = new ID[elements][];
+ for (int x = 0; x < elements; x++) {
+ rVal[x] = readSavableArray(content);
+ }
+ return rVal;
+ }
+
+ protected StringIDMap readStringSavableMap(byte[] content) throws IOException {
+ int elements = readInt(content);
+ if (elements == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ String[] keys = readStringArray(content);
+ ID[] values = readSavableArray(content);
+ StringIDMap rVal = new StringIDMap();
+ rVal.keys = keys;
+ rVal.values = values;
+ return rVal;
+ }
+
+ protected IntIDMap readIntSavableMap(byte[] content) throws IOException {
+ int elements = readInt(content);
+ if (elements == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+ int[] keys = readIntArray(content);
+ ID[] values = readSavableArray(content);
+ IntIDMap rVal = new IntIDMap();
+ rVal.keys = keys;
+ rVal.values = values;
+ return rVal;
+ }
+
+
+ // ArrayList
+
+ protected ArrayList readFloatBufferArrayList(byte[] content)
+ throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT) {
+ return null;
+ }
+ ArrayList rVal = new ArrayList(length);
+ for (int x = 0; x < length; x++) {
+ rVal.add(readFloatBuffer(content));
+ }
+ return rVal;
+ }
+
+ // ArrayList
+
+ protected ArrayList readByteBufferArrayList(byte[] content)
+ throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT) {
+ return null;
+ }
+ ArrayList rVal = new ArrayList(length);
+ for (int x = 0; x < length; x++) {
+ rVal.add(readByteBuffer(content));
+ }
+ return rVal;
+ }
+
+ // NIO BUFFERS
+ // float buffer
+
+ protected FloatBuffer readFloatBuffer(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+
+ if (BinaryImporter.canUseFastBuffers()){
+ ByteBuffer value = BufferUtils.createByteBuffer(length * 4);
+ value.put(content, index, length * 4).rewind();
+ index += length * 4;
+ return value.asFloatBuffer();
+ }else{
+ FloatBuffer value = BufferUtils.createFloatBuffer(length);
+ for (int x = 0; x < length; x++) {
+ value.put(readFloatForBuffer(content));
+ }
+ value.rewind();
+ return value;
+ }
+ }
+
+ // int buffer
+
+ protected IntBuffer readIntBuffer(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+
+ if (BinaryImporter.canUseFastBuffers()){
+ ByteBuffer value = BufferUtils.createByteBuffer(length * 4);
+ value.put(content, index, length * 4).rewind();
+ index += length * 4;
+ return value.asIntBuffer();
+ }else{
+ IntBuffer value = BufferUtils.createIntBuffer(length);
+ for (int x = 0; x < length; x++) {
+ value.put(readIntForBuffer(content));
+ }
+ value.rewind();
+ return value;
+ }
+ }
+
+ // byte buffer
+
+ protected ByteBuffer readByteBuffer(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+
+ if (BinaryImporter.canUseFastBuffers()){
+ ByteBuffer value = BufferUtils.createByteBuffer(length);
+ value.put(content, index, length).rewind();
+ index += length;
+ return value;
+ }else{
+ ByteBuffer value = BufferUtils.createByteBuffer(length);
+ for (int x = 0; x < length; x++) {
+ value.put(readByteForBuffer(content));
+ }
+ value.rewind();
+ return value;
+ }
+ }
+
+ // short buffer
+
+ protected ShortBuffer readShortBuffer(byte[] content) throws IOException {
+ int length = readInt(content);
+ if (length == BinaryOutputCapsule.NULL_OBJECT)
+ return null;
+
+ if (BinaryImporter.canUseFastBuffers()){
+ ByteBuffer value = BufferUtils.createByteBuffer(length * 2);
+ value.put(content, index, length * 2).rewind();
+ index += length * 2;
+ return value.asShortBuffer();
+ }else{
+ ShortBuffer value = BufferUtils.createShortBuffer(length);
+ for (int x = 0; x < length; x++) {
+ value.put(readShortForBuffer(content));
+ }
+ value.rewind();
+ return value;
+ }
+ }
+
+ static private class ID {
+ public int id;
+
+ public ID(int id) {
+ this.id = id;
+ }
+ }
+
+ static private class StringIDMap {
+ public String[] keys;
+ public ID[] values;
+ }
+
+ static private class IntIDMap {
+ public int[] keys;
+ public ID[] values;
+ }
+
+ public > T readEnum(String name, Class enumType, T defVal) throws IOException {
+ String eVal = readString(name, defVal != null ? defVal.name() : null);
+ if (eVal != null) {
+ return Enum.valueOf(enumType, eVal);
+ } else {
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/engine/src/core-plugins/com/jme3/export/binary/BinaryLoaderModule.java b/engine/src/core-plugins/com/jme3/export/binary/BinaryLoaderModule.java
new file mode 100644
index 000000000..eae0be055
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/export/binary/BinaryLoaderModule.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.export.binary;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.Savable;
+import java.io.IOException;
+
+/**
+ * BinaryLoaderModule defines two methods, the first provides a key value to
+ * look for to issue the load command. This key is typically (and should be)
+ * the class name the loader is responsible for. While load handles creating
+ * a new instance of the class.
+ * @author mpowell
+ *
+ */
+interface BinaryLoaderModule {
+
+ String getKey();
+
+ /**
+ * The inputCapsule parameter is not used at all.
+ *
+ * The DOMOutputStream class calls this method with a null parameter, so
+ * if you make use of the parameter, either handle null 'inputCapsule'
+ * or rearrange the class hierarchy to satisfy DOMOuptutStream.
+ *
+ * @param inputCapsule A value which is currently ignored by all
+ * implementation classes.
+ */
+ Savable load(InputCapsule inputCapsule) throws IOException;
+}
diff --git a/engine/src/core-plugins/com/jme3/export/binary/BinaryOutputCapsule.java b/engine/src/core-plugins/com/jme3/export/binary/BinaryOutputCapsule.java
new file mode 100644
index 000000000..209a8ac17
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/export/binary/BinaryOutputCapsule.java
@@ -0,0 +1,937 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.export.binary;
+
+import com.jme3.export.OutputCapsule;
+import com.jme3.export.Savable;
+import com.jme3.util.IntMap;
+import com.jme3.util.IntMap.Entry;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.nio.ShortBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Map;
+
+/**
+ * @author Joshua Slack
+ */
+final class BinaryOutputCapsule implements OutputCapsule {
+
+ public static final int NULL_OBJECT = -1;
+ public static final int DEFAULT_OBJECT = -2;
+
+ public static byte[] NULL_BYTES = new byte[] { (byte) -1 };
+ public static byte[] DEFAULT_BYTES = new byte[] { (byte) -2 };
+
+ protected ByteArrayOutputStream baos;
+ protected byte[] bytes;
+ protected BinaryExporter exporter;
+ protected BinaryClassObject cObj;
+
+ public BinaryOutputCapsule(BinaryExporter exporter, BinaryClassObject bco) {
+ this.baos = new ByteArrayOutputStream();
+ this.exporter = exporter;
+ this.cObj = bco;
+ }
+
+ public void write(byte value, String name, byte defVal) throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.BYTE);
+ write(value);
+ }
+
+ public void write(byte[] value, String name, byte[] defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.BYTE_1D);
+ write(value);
+ }
+
+ public void write(byte[][] value, String name, byte[][] defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.BYTE_2D);
+ write(value);
+ }
+
+ public void write(int value, String name, int defVal) throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.INT);
+ write(value);
+ }
+
+ public void write(int[] value, String name, int[] defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.INT_1D);
+ write(value);
+ }
+
+ public void write(int[][] value, String name, int[][] defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.INT_2D);
+ write(value);
+ }
+
+ public void write(float value, String name, float defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.FLOAT);
+ write(value);
+ }
+
+ public void write(float[] value, String name, float[] defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.FLOAT_1D);
+ write(value);
+ }
+
+ public void write(float[][] value, String name, float[][] defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.FLOAT_2D);
+ write(value);
+ }
+
+ public void write(double value, String name, double defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.DOUBLE);
+ write(value);
+ }
+
+ public void write(double[] value, String name, double[] defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.DOUBLE_1D);
+ write(value);
+ }
+
+ public void write(double[][] value, String name, double[][] defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.DOUBLE_2D);
+ write(value);
+ }
+
+ public void write(long value, String name, long defVal) throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.LONG);
+ write(value);
+ }
+
+ public void write(long[] value, String name, long[] defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.LONG_1D);
+ write(value);
+ }
+
+ public void write(long[][] value, String name, long[][] defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.LONG_2D);
+ write(value);
+ }
+
+ public void write(short value, String name, short defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.SHORT);
+ write(value);
+ }
+
+ public void write(short[] value, String name, short[] defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.SHORT_1D);
+ write(value);
+ }
+
+ public void write(short[][] value, String name, short[][] defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.SHORT_2D);
+ write(value);
+ }
+
+ public void write(boolean value, String name, boolean defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.BOOLEAN);
+ write(value);
+ }
+
+ public void write(boolean[] value, String name, boolean[] defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.BOOLEAN_1D);
+ write(value);
+ }
+
+ public void write(boolean[][] value, String name, boolean[][] defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.BOOLEAN_2D);
+ write(value);
+ }
+
+ public void write(String value, String name, String defVal)
+ throws IOException {
+ if (value == null ? defVal == null : value.equals(defVal))
+ return;
+ writeAlias(name, BinaryClassField.STRING);
+ write(value);
+ }
+
+ public void write(String[] value, String name, String[] defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.STRING_1D);
+ write(value);
+ }
+
+ public void write(String[][] value, String name, String[][] defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.STRING_2D);
+ write(value);
+ }
+
+ public void write(BitSet value, String name, BitSet defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.BITSET);
+ write(value);
+ }
+
+ public void write(Savable object, String name, Savable defVal)
+ throws IOException {
+ if (object == defVal)
+ return;
+ writeAlias(name, BinaryClassField.SAVABLE);
+ write(object);
+ }
+
+ public void write(Savable[] objects, String name, Savable[] defVal)
+ throws IOException {
+ if (objects == defVal)
+ return;
+ writeAlias(name, BinaryClassField.SAVABLE_1D);
+ write(objects);
+ }
+
+ public void write(Savable[][] objects, String name, Savable[][] defVal)
+ throws IOException {
+ if (objects == defVal)
+ return;
+ writeAlias(name, BinaryClassField.SAVABLE_2D);
+ write(objects);
+ }
+
+ public void write(FloatBuffer value, String name, FloatBuffer defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.FLOATBUFFER);
+ write(value);
+ }
+
+ public void write(IntBuffer value, String name, IntBuffer defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.INTBUFFER);
+ write(value);
+ }
+
+ public void write(ByteBuffer value, String name, ByteBuffer defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.BYTEBUFFER);
+ write(value);
+ }
+
+ public void write(ShortBuffer value, String name, ShortBuffer defVal)
+ throws IOException {
+ if (value == defVal)
+ return;
+ writeAlias(name, BinaryClassField.SHORTBUFFER);
+ write(value);
+ }
+
+ public void writeFloatBufferArrayList(ArrayList array,
+ String name, ArrayList defVal) throws IOException {
+ if (array == defVal)
+ return;
+ writeAlias(name, BinaryClassField.FLOATBUFFER_ARRAYLIST);
+ writeFloatBufferArrayList(array);
+ }
+
+ public void writeByteBufferArrayList(ArrayList array,
+ String name, ArrayList defVal) throws IOException {
+ if (array == defVal)
+ return;
+ writeAlias(name, BinaryClassField.BYTEBUFFER_ARRAYLIST);
+ writeByteBufferArrayList(array);
+ }
+
+ public void writeSavableArrayList(ArrayList array, String name,
+ ArrayList defVal) throws IOException {
+ if (array == defVal)
+ return;
+ writeAlias(name, BinaryClassField.SAVABLE_ARRAYLIST);
+ writeSavableArrayList(array);
+ }
+
+ public void writeSavableArrayListArray(ArrayList[] array, String name,
+ ArrayList[] defVal) throws IOException {
+ if (array == defVal)
+ return;
+ writeAlias(name, BinaryClassField.SAVABLE_ARRAYLIST_1D);
+ writeSavableArrayListArray(array);
+ }
+
+ public void writeSavableArrayListArray2D(ArrayList[][] array, String name,
+ ArrayList[][] defVal) throws IOException {
+ if (array == defVal)
+ return;
+ writeAlias(name, BinaryClassField.SAVABLE_ARRAYLIST_2D);
+ writeSavableArrayListArray2D(array);
+ }
+
+ public void writeSavableMap(Map extends Savable, ? extends Savable> map,
+ String name, Map extends Savable, ? extends Savable> defVal)
+ throws IOException {
+ if (map == defVal)
+ return;
+ writeAlias(name, BinaryClassField.SAVABLE_MAP);
+ writeSavableMap(map);
+ }
+
+ public void writeStringSavableMap(Map map,
+ String name, Map defVal)
+ throws IOException {
+ if (map == defVal)
+ return;
+ writeAlias(name, BinaryClassField.STRING_SAVABLE_MAP);
+ writeStringSavableMap(map);
+ }
+
+ public void writeIntSavableMap(IntMap extends Savable> map,
+ String name, IntMap extends Savable> defVal)
+ throws IOException {
+ if (map == defVal)
+ return;
+ writeAlias(name, BinaryClassField.INT_SAVABLE_MAP);
+ writeIntSavableMap(map);
+ }
+
+ protected void writeAlias(String name, byte fieldType) throws IOException {
+ if (cObj.nameFields.get(name) == null)
+ generateAlias(name, fieldType);
+
+ byte alias = cObj.nameFields.get(name).alias;
+ write(alias);
+ }
+
+ // XXX: The generation of aliases is limited to 256 possible values.
+ // If we run into classes with more than 256 fields, we need to expand this.
+ // But I mean, come on...
+ protected void generateAlias(String name, byte type) {
+ byte alias = (byte) cObj.nameFields.size();
+ cObj.nameFields.put(name, new BinaryClassField(name, alias, type));
+ }
+
+ @Override
+ public boolean equals(Object arg0) {
+ if (!(arg0 instanceof BinaryOutputCapsule))
+ return false;
+
+ byte[] other = ((BinaryOutputCapsule) arg0).bytes;
+ if (bytes.length != other.length)
+ return false;
+ return Arrays.equals(bytes, other);
+ }
+
+ public void finish() {
+ // renamed to finish as 'finalize' in java.lang.Object should not be
+ // overridden like this
+ // - finalize should not be called directly but is called by garbage
+ // collection!!!
+ bytes = baos.toByteArray();
+ baos = null;
+ }
+
+ // byte primitive
+
+ protected void write(byte value) throws IOException {
+ baos.write(value);
+ }
+
+ protected void writeForBuffer(byte value) throws IOException {
+ baos.write(value);
+ }
+
+ protected void write(byte[] value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(value.length);
+ baos.write(value);
+ }
+
+ protected void write(byte[][] value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(value.length);
+ for (int x = 0; x < value.length; x++)
+ write(value[x]);
+ }
+
+ // int primitive
+
+ protected void write(int value) throws IOException {
+ baos.write(deflate(ByteUtils.convertToBytes(value)));
+ }
+
+ protected void writeForBuffer(int value) throws IOException {
+ byte[] byteArray = new byte[4];
+ byteArray[0] = (byte) value;
+ byteArray[1] = (byte) (value >> 8);
+ byteArray[2] = (byte) (value >> 16);
+ byteArray[3] = (byte) (value >> 24);
+ baos.write(byteArray);
+ }
+
+ protected void write(int[] value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(value.length);
+ for (int x = 0; x < value.length; x++)
+ write(value[x]);
+ }
+
+ protected void write(int[][] value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(value.length);
+ for (int x = 0; x < value.length; x++)
+ write(value[x]);
+ }
+
+ // float primitive
+
+ protected void write(float value) throws IOException {
+ baos.write(ByteUtils.convertToBytes(value));
+ }
+
+ protected void writeForBuffer(float value) throws IOException {
+ int integer = Float.floatToIntBits(value);
+ writeForBuffer(integer);
+ }
+
+ protected void write(float[] value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(value.length);
+ for (int x = 0; x < value.length; x++)
+ write(value[x]);
+ }
+
+ protected void write(float[][] value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(value.length);
+ for (int x = 0; x < value.length; x++)
+ write(value[x]);
+ }
+
+ // double primitive
+
+ protected void write(double value) throws IOException {
+ baos.write(ByteUtils.convertToBytes(value));
+ }
+
+ protected void write(double[] value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(value.length);
+ for (int x = 0; x < value.length; x++)
+ write(value[x]);
+ }
+
+ protected void write(double[][] value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(value.length);
+ for (int x = 0; x < value.length; x++)
+ write(value[x]);
+ }
+
+ // long primitive
+
+ protected void write(long value) throws IOException {
+ baos.write(deflate(ByteUtils.convertToBytes(value)));
+ }
+
+ protected void write(long[] value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(value.length);
+ for (int x = 0; x < value.length; x++)
+ write(value[x]);
+ }
+
+ protected void write(long[][] value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(value.length);
+ for (int x = 0; x < value.length; x++)
+ write(value[x]);
+ }
+
+ // short primitive
+
+ protected void write(short value) throws IOException {
+ baos.write(ByteUtils.convertToBytes(value));
+ }
+
+ protected void writeForBuffer(short value) throws IOException {
+ byte[] byteArray = new byte[2];
+ byteArray[0] = (byte) value;
+ byteArray[1] = (byte) (value >> 8);
+ baos.write(byteArray);
+ }
+
+ protected void write(short[] value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(value.length);
+ for (int x = 0; x < value.length; x++)
+ write(value[x]);
+ }
+
+ protected void write(short[][] value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(value.length);
+ for (int x = 0; x < value.length; x++)
+ write(value[x]);
+ }
+
+ // boolean primitive
+
+ protected void write(boolean value) throws IOException {
+ baos.write(ByteUtils.convertToBytes(value));
+ }
+
+ protected void write(boolean[] value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(value.length);
+ for (int x = 0; x < value.length; x++)
+ write(value[x]);
+ }
+
+ protected void write(boolean[][] value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(value.length);
+ for (int x = 0; x < value.length; x++)
+ write(value[x]);
+ }
+
+ // String
+
+ protected void write(String value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ // write our output as UTF-8. Java misspells UTF-8 as UTF8 for official use in java.lang
+ byte[] bytes = value.getBytes("UTF8");
+ write(bytes.length);
+ baos.write(bytes);
+ }
+
+ protected void write(String[] value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(value.length);
+ for (int x = 0; x < value.length; x++)
+ write(value[x]);
+ }
+
+ protected void write(String[][] value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(value.length);
+ for (int x = 0; x < value.length; x++)
+ write(value[x]);
+ }
+
+ // BitSet
+
+ protected void write(BitSet value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(value.size());
+ // TODO: MAKE THIS SMALLER
+ for (int x = 0, max = value.size(); x < max; x++)
+ write(value.get(x));
+ }
+
+ // DEFLATOR for int and long
+
+ protected static byte[] deflate(byte[] bytes) {
+ int size = bytes.length;
+ if (size == 4) {
+ int possibleMagic = ByteUtils.convertIntFromBytes(bytes);
+ if (possibleMagic == NULL_OBJECT)
+ return NULL_BYTES;
+ else if (possibleMagic == DEFAULT_OBJECT)
+ return DEFAULT_BYTES;
+ }
+ for (int x = 0; x < bytes.length; x++) {
+ if (bytes[x] != 0)
+ break;
+ size--;
+ }
+ if (size == 0)
+ return new byte[1];
+
+ byte[] rVal = new byte[1 + size];
+ rVal[0] = (byte) size;
+ for (int x = 1; x < rVal.length; x++)
+ rVal[x] = bytes[bytes.length - size - 1 + x];
+
+ return rVal;
+ }
+
+ // BinarySavable
+
+ protected void write(Savable object) throws IOException {
+ if (object == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ int id = exporter.processBinarySavable(object);
+ write(id);
+ }
+
+ // BinarySavable array
+
+ protected void write(Savable[] objects) throws IOException {
+ if (objects == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(objects.length);
+ for (int x = 0; x < objects.length; x++) {
+ write(objects[x]);
+ }
+ }
+
+ protected void write(Savable[][] objects) throws IOException {
+ if (objects == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(objects.length);
+ for (int x = 0; x < objects.length; x++) {
+ write(objects[x]);
+ }
+ }
+
+ // ArrayList
+
+ protected void writeSavableArrayList(ArrayList array) throws IOException {
+ if (array == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(array.size());
+ for (Object bs : array) {
+ write((Savable) bs);
+ }
+ }
+
+ protected void writeSavableArrayListArray(ArrayList[] array)
+ throws IOException {
+ if (array == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(array.length);
+ for (ArrayList bs : array) {
+ writeSavableArrayList(bs);
+ }
+ }
+
+ protected void writeSavableArrayListArray2D(ArrayList[][] array)
+ throws IOException {
+ if (array == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(array.length);
+ for (ArrayList[] bs : array) {
+ writeSavableArrayListArray(bs);
+ }
+ }
+
+ // Map
+
+ protected void writeSavableMap(
+ Map extends Savable, ? extends Savable> array) throws IOException {
+ if (array == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(array.size());
+ for (Savable key : array.keySet()) {
+ write(new Savable[] { key, array.get(key) });
+ }
+ }
+
+ protected void writeStringSavableMap(Map array)
+ throws IOException {
+ if (array == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(array.size());
+
+ // write String array for keys
+ String[] keys = array.keySet().toArray(new String[] {});
+ write(keys);
+
+ // write Savable array for values
+ Savable[] values = array.values().toArray(new Savable[] {});
+ write(values);
+ }
+
+ protected void writeIntSavableMap(IntMap extends Savable> array)
+ throws IOException {
+ if (array == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(array.size());
+
+ int[] keys = new int[array.size()];
+ Savable[] values = new Savable[keys.length];
+ int i = 0;
+ for (Entry extends Savable> entry : array){
+ keys[i] = entry.getKey();
+ values[i] = entry.getValue();
+ i++;
+ }
+
+ // write String array for keys
+ write(keys);
+
+ // write Savable array for values
+ write(values);
+ }
+
+ // ArrayList
+
+ protected void writeFloatBufferArrayList(ArrayList array)
+ throws IOException {
+ if (array == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(array.size());
+ for (FloatBuffer buf : array) {
+ write(buf);
+ }
+ }
+
+ // ArrayList
+
+ protected void writeByteBufferArrayList(ArrayList array)
+ throws IOException {
+ if (array == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ write(array.size());
+ for (ByteBuffer buf : array) {
+ write(buf);
+ }
+ }
+
+ // NIO BUFFERS
+ // float buffer
+
+ protected void write(FloatBuffer value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ value.rewind();
+ int length = value.limit();
+ write(length);
+ for (int x = 0; x < length; x++) {
+ writeForBuffer(value.get());
+ }
+ value.rewind();
+ }
+
+ // int buffer
+
+ protected void write(IntBuffer value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ value.rewind();
+ int length = value.limit();
+ write(length);
+
+ for (int x = 0; x < length; x++) {
+ writeForBuffer(value.get());
+ }
+ value.rewind();
+ }
+
+ // byte buffer
+
+ protected void write(ByteBuffer value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ value.rewind();
+ int length = value.limit();
+ write(length);
+ for (int x = 0; x < length; x++) {
+ writeForBuffer(value.get());
+ }
+ value.rewind();
+ }
+
+ // short buffer
+
+ protected void write(ShortBuffer value) throws IOException {
+ if (value == null) {
+ write(NULL_OBJECT);
+ return;
+ }
+ value.rewind();
+ int length = value.limit();
+ write(length);
+ for (int x = 0; x < length; x++) {
+ writeForBuffer(value.get());
+ }
+ value.rewind();
+ }
+
+ public void write(Enum value, String name, Enum defVal) throws IOException {
+ if (value == defVal)
+ return;
+ if (value == null) {
+ return;
+ } else {
+ write(value.name(), name, null);
+ }
+ }
+}
\ No newline at end of file
diff --git a/engine/src/core-plugins/com/jme3/export/binary/ByteUtils.java b/engine/src/core-plugins/com/jme3/export/binary/ByteUtils.java
new file mode 100644
index 000000000..bb835d0ab
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/export/binary/ByteUtils.java
@@ -0,0 +1,486 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.export.binary;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * ByteUtils
is a helper class for converting numeric primitives
+ * to and from byte representations.
+ *
+ * @author Joshua Slack
+ */
+public class ByteUtils {
+
+ /**
+ * Takes an InputStream and returns the complete byte content of it
+ *
+ * @param inputStream
+ * The input stream to read from
+ * @return The byte array containing the data from the input stream
+ * @throws java.io.IOException
+ * thrown if there is a problem reading from the input stream
+ * provided
+ */
+ public static byte[] getByteContent(InputStream inputStream)
+ throws IOException {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream(
+ 16 * 1024);
+ byte[] buffer = new byte[1024];
+ int byteCount = -1;
+ byte[] data = null;
+
+ // Read the byte content into the output stream first
+ while ((byteCount = inputStream.read(buffer)) > 0) {
+ outputStream.write(buffer, 0, byteCount);
+ }
+
+ // Set data with byte content from stream
+ data = outputStream.toByteArray();
+
+ // Release resources
+ outputStream.close();
+
+ return data;
+ }
+
+
+ // ********** byte <> short METHODS **********
+
+ /**
+ * Writes a short out to an OutputStream.
+ *
+ * @param outputStream
+ * The OutputStream the short will be written to
+ * @param value
+ * The short to write
+ * @throws IOException
+ * Thrown if there is a problem writing to the OutputStream
+ */
+ public static void writeShort(OutputStream outputStream, short value)
+ throws IOException {
+ byte[] byteArray = convertToBytes(value);
+
+ outputStream.write(byteArray);
+
+ return;
+ }
+
+ public static byte[] convertToBytes(short value) {
+ byte[] byteArray = new byte[2];
+
+ byteArray[0] = (byte) (value >> 8);
+ byteArray[1] = (byte) value;
+ return byteArray;
+ }
+
+ /**
+ * Read in a short from an InputStream
+ *
+ * @param inputStream
+ * The InputStream used to read the short
+ * @return A short, which is the next 2 bytes converted from the InputStream
+ * @throws IOException
+ * Thrown if there is a problem reading from the InputStream
+ */
+ public static short readShort(InputStream inputStream) throws IOException {
+ byte[] byteArray = new byte[2];
+
+ // Read in the next 2 bytes
+ inputStream.read(byteArray);
+
+ short number = convertShortFromBytes(byteArray);
+
+ return number;
+ }
+
+ public static short convertShortFromBytes(byte[] byteArray) {
+ return convertShortFromBytes(byteArray, 0);
+ }
+
+ public static short convertShortFromBytes(byte[] byteArray, int offset) {
+ // Convert it to a short
+ short number = (short) ((byteArray[offset+1] & 0xFF) + ((byteArray[offset+0] & 0xFF) << 8));
+ return number;
+ }
+
+
+ // ********** byte <> int METHODS **********
+
+ /**
+ * Writes an integer out to an OutputStream.
+ *
+ * @param outputStream
+ * The OutputStream the integer will be written to
+ * @param integer
+ * The integer to write
+ * @throws IOException
+ * Thrown if there is a problem writing to the OutputStream
+ */
+ public static void writeInt(OutputStream outputStream, int integer)
+ throws IOException {
+ byte[] byteArray = convertToBytes(integer);
+
+ outputStream.write(byteArray);
+
+ return;
+ }
+
+ public static byte[] convertToBytes(int integer) {
+ byte[] byteArray = new byte[4];
+
+ byteArray[0] = (byte) (integer >> 24);
+ byteArray[1] = (byte) (integer >> 16);
+ byteArray[2] = (byte) (integer >> 8);
+ byteArray[3] = (byte) integer;
+ return byteArray;
+ }
+
+ /**
+ * Read in an integer from an InputStream
+ *
+ * @param inputStream
+ * The InputStream used to read the integer
+ * @return An int, which is the next 4 bytes converted from the InputStream
+ * @throws IOException
+ * Thrown if there is a problem reading from the InputStream
+ */
+ public static int readInt(InputStream inputStream) throws IOException {
+ byte[] byteArray = new byte[4];
+
+ // Read in the next 4 bytes
+ inputStream.read(byteArray);
+
+ int number = convertIntFromBytes(byteArray);
+
+ return number;
+ }
+
+ public static int convertIntFromBytes(byte[] byteArray) {
+ return convertIntFromBytes(byteArray, 0);
+ }
+
+ public static int convertIntFromBytes(byte[] byteArray, int offset) {
+ // Convert it to an int
+ int number = ((byteArray[offset] & 0xFF) << 24)
+ + ((byteArray[offset+1] & 0xFF) << 16) + ((byteArray[offset+2] & 0xFF) << 8)
+ + (byteArray[offset+3] & 0xFF);
+ return number;
+ }
+
+
+ // ********** byte <> long METHODS **********
+
+ /**
+ * Writes a long out to an OutputStream.
+ *
+ * @param outputStream
+ * The OutputStream the long will be written to
+ * @param value
+ * The long to write
+ * @throws IOException
+ * Thrown if there is a problem writing to the OutputStream
+ */
+ public static void writeLong(OutputStream outputStream, long value)
+ throws IOException {
+ byte[] byteArray = convertToBytes(value);
+
+ outputStream.write(byteArray);
+
+ return;
+ }
+
+ public static byte[] convertToBytes(long n) {
+ byte[] bytes = new byte[8];
+
+ bytes[7] = (byte) (n);
+ n >>>= 8;
+ bytes[6] = (byte) (n);
+ n >>>= 8;
+ bytes[5] = (byte) (n);
+ n >>>= 8;
+ bytes[4] = (byte) (n);
+ n >>>= 8;
+ bytes[3] = (byte) (n);
+ n >>>= 8;
+ bytes[2] = (byte) (n);
+ n >>>= 8;
+ bytes[1] = (byte) (n);
+ n >>>= 8;
+ bytes[0] = (byte) (n);
+
+ return bytes;
+ }
+
+ /**
+ * Read in a long from an InputStream
+ *
+ * @param inputStream
+ * The InputStream used to read the long
+ * @return A long, which is the next 8 bytes converted from the InputStream
+ * @throws IOException
+ * Thrown if there is a problem reading from the InputStream
+ */
+ public static long readLong(InputStream inputStream) throws IOException {
+ byte[] byteArray = new byte[8];
+
+ // Read in the next 8 bytes
+ inputStream.read(byteArray);
+
+ long number = convertLongFromBytes(byteArray);
+
+ return number;
+ }
+
+ public static long convertLongFromBytes(byte[] bytes) {
+ return convertLongFromBytes(bytes, 0);
+ }
+
+ public static long convertLongFromBytes(byte[] bytes, int offset) {
+ // Convert it to an long
+ return ((((long) bytes[offset+7]) & 0xFF)
+ + ((((long) bytes[offset+6]) & 0xFF) << 8)
+ + ((((long) bytes[offset+5]) & 0xFF) << 16)
+ + ((((long) bytes[offset+4]) & 0xFF) << 24)
+ + ((((long) bytes[offset+3]) & 0xFF) << 32)
+ + ((((long) bytes[offset+2]) & 0xFF) << 40)
+ + ((((long) bytes[offset+1]) & 0xFF) << 48)
+ + ((((long) bytes[offset+0]) & 0xFF) << 56));
+ }
+
+
+ // ********** byte <> double METHODS **********
+
+ /**
+ * Writes a double out to an OutputStream.
+ *
+ * @param outputStream
+ * The OutputStream the double will be written to
+ * @param value
+ * The double to write
+ * @throws IOException
+ * Thrown if there is a problem writing to the OutputStream
+ */
+ public static void writeDouble(OutputStream outputStream, double value)
+ throws IOException {
+ byte[] byteArray = convertToBytes(value);
+
+ outputStream.write(byteArray);
+
+ return;
+ }
+
+ public static byte[] convertToBytes(double n) {
+ long bits = Double.doubleToLongBits(n);
+ return convertToBytes(bits);
+ }
+
+ /**
+ * Read in a double from an InputStream
+ *
+ * @param inputStream
+ * The InputStream used to read the double
+ * @return A double, which is the next 8 bytes converted from the InputStream
+ * @throws IOException
+ * Thrown if there is a problem reading from the InputStream
+ */
+ public static double readDouble(InputStream inputStream) throws IOException {
+ byte[] byteArray = new byte[8];
+
+ // Read in the next 8 bytes
+ inputStream.read(byteArray);
+
+ double number = convertDoubleFromBytes(byteArray);
+
+ return number;
+ }
+
+ public static double convertDoubleFromBytes(byte[] bytes) {
+ return convertDoubleFromBytes(bytes, 0);
+ }
+
+ public static double convertDoubleFromBytes(byte[] bytes, int offset) {
+ // Convert it to a double
+ long bits = convertLongFromBytes(bytes, offset);
+ return Double.longBitsToDouble(bits);
+ }
+
+ // ********** byte <> float METHODS **********
+
+ /**
+ * Writes an float out to an OutputStream.
+ *
+ * @param outputStream
+ * The OutputStream the float will be written to
+ * @param fVal
+ * The float to write
+ * @throws IOException
+ * Thrown if there is a problem writing to the OutputStream
+ */
+ public static void writeFloat(OutputStream outputStream, float fVal)
+ throws IOException {
+ byte[] byteArray = convertToBytes(fVal);
+
+ outputStream.write(byteArray);
+
+ return;
+ }
+
+ public static byte[] convertToBytes(float f) {
+ int temp = Float.floatToIntBits(f);
+ return convertToBytes(temp);
+ }
+
+ /**
+ * Read in a float from an InputStream
+ *
+ * @param inputStream
+ * The InputStream used to read the float
+ * @return A float, which is the next 4 bytes converted from the InputStream
+ * @throws IOException
+ * Thrown if there is a problem reading from the InputStream
+ */
+ public static float readFloat(InputStream inputStream) throws IOException {
+ byte[] byteArray = new byte[4];
+
+ // Read in the next 4 bytes
+ inputStream.read(byteArray);
+
+ float number = convertFloatFromBytes(byteArray);
+
+ return number;
+ }
+
+ public static float convertFloatFromBytes(byte[] byteArray) {
+ return convertFloatFromBytes(byteArray, 0);
+ }
+ public static float convertFloatFromBytes(byte[] byteArray, int offset) {
+ // Convert it to an int
+ int number = convertIntFromBytes(byteArray, offset);
+ return Float.intBitsToFloat(number);
+ }
+
+
+
+ // ********** byte <> boolean METHODS **********
+
+ /**
+ * Writes a boolean out to an OutputStream.
+ *
+ * @param outputStream
+ * The OutputStream the boolean will be written to
+ * @param bVal
+ * The boolean to write
+ * @throws IOException
+ * Thrown if there is a problem writing to the OutputStream
+ */
+ public static void writeBoolean(OutputStream outputStream, boolean bVal)
+ throws IOException {
+ byte[] byteArray = convertToBytes(bVal);
+
+ outputStream.write(byteArray);
+
+ return;
+ }
+
+ public static byte[] convertToBytes(boolean b) {
+ byte[] rVal = new byte[1];
+ rVal[0] = b ? (byte)1 : (byte)0;
+ return rVal;
+ }
+
+ /**
+ * Read in a boolean from an InputStream
+ *
+ * @param inputStream
+ * The InputStream used to read the boolean
+ * @return A boolean, which is the next byte converted from the InputStream (iow, byte != 0)
+ * @throws IOException
+ * Thrown if there is a problem reading from the InputStream
+ */
+ public static boolean readBoolean(InputStream inputStream) throws IOException {
+ byte[] byteArray = new byte[1];
+
+ // Read in the next byte
+ inputStream.read(byteArray);
+
+ return convertBooleanFromBytes(byteArray);
+ }
+
+ public static boolean convertBooleanFromBytes(byte[] byteArray) {
+ return convertBooleanFromBytes(byteArray, 0);
+ }
+ public static boolean convertBooleanFromBytes(byte[] byteArray, int offset) {
+ return byteArray[offset] != 0;
+ }
+
+
+ /**
+ * Properly reads in data from the given stream until the specified number
+ * of bytes have been read.
+ *
+ * @param store
+ * the byte array to store in. Should have a length > bytes
+ * @param bytes
+ * the number of bytes to read.
+ * @param is
+ * the stream to read from
+ * @return the store array for chaining purposes
+ * @throws IOException
+ * if an error occurs while reading from the stream
+ * @throws ArrayIndexOutOfBoundsException
+ * if bytes greater than the length of the store.
+ */
+ public static byte[] readData(byte[] store, int bytes, InputStream is) throws IOException {
+ for (int i = 0; i < bytes; i++) {
+ store[i] = (byte)is.read();
+ }
+ return store;
+ }
+
+ public static byte[] rightAlignBytes(byte[] bytes, int width) {
+ if (bytes.length != width) {
+ byte[] rVal = new byte[width];
+ for (int x = width - bytes.length; x < width; x++) {
+ rVal[x] = bytes[x - (width - bytes.length)];
+ }
+ return rVal;
+ }
+
+ return bytes;
+ }
+
+}
diff --git a/engine/src/core-plugins/com/jme3/font/plugins/BitmapFontLoader.java b/engine/src/core-plugins/com/jme3/font/plugins/BitmapFontLoader.java
new file mode 100644
index 000000000..8236e3acf
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/font/plugins/BitmapFontLoader.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.font.plugins;
+
+import com.jme3.font.*;
+import com.jme3.material.Material;
+import com.jme3.material.MaterialDef;
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetKey;
+import com.jme3.asset.AssetLoader;
+import com.jme3.asset.TextureKey;
+import com.jme3.material.RenderState.BlendMode;
+import com.jme3.math.ColorRGBA;
+import com.jme3.texture.Texture;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+public class BitmapFontLoader implements AssetLoader {
+
+ public Object load(AssetInfo info) throws IOException {
+ MaterialDef spriteMat =
+ (MaterialDef) info.getManager().loadAsset(new AssetKey("Common/MatDefs/Misc/Unshaded.j3md"));
+
+ BitmapCharacterSet charSet = new BitmapCharacterSet();
+ Material[] matPages = null;
+ BitmapFont font = new BitmapFont();
+
+ String folder = info.getKey().getFolder();
+
+ BufferedReader reader = new BufferedReader(new InputStreamReader(info.openStream()));
+ String regex = "[\\s=]+";
+
+ font.setCharSet(charSet);
+ while (reader.ready()){
+ String line = reader.readLine();
+ String[] tokens = line.split(regex);
+ if (tokens[0].equals("info")){
+ // Get rendered size
+ for (int i = 1; i < tokens.length; i++){
+ if (tokens[i].equals("size")){
+ charSet.setRenderedSize(Integer.parseInt(tokens[i + 1]));
+ }
+ }
+ }else if (tokens[0].equals("common")){
+ // Fill out BitmapCharacterSet fields
+ for (int i = 1; i < tokens.length; i++){
+ String token = tokens[i];
+ if (token.equals("lineHeight")){
+ charSet.setLineHeight(Integer.parseInt(tokens[i + 1]));
+ }else if (token.equals("base")){
+ charSet.setBase(Integer.parseInt(tokens[i + 1]));
+ }else if (token.equals("scaleW")){
+ charSet.setWidth(Integer.parseInt(tokens[i + 1]));
+ }else if (token.equals("scaleH")){
+ charSet.setHeight(Integer.parseInt(tokens[i + 1]));
+ }else if (token.equals("pages")){
+ // number of texture pages
+ matPages = new Material[Integer.parseInt(tokens[i + 1])];
+ font.setPages(matPages);
+ }
+ }
+ }else if (tokens[0].equals("page")){
+ int index = -1;
+ Texture tex = null;
+
+ for (int i = 1; i < tokens.length; i++){
+ String token = tokens[i];
+ if (token.equals("id")){
+ index = Integer.parseInt(tokens[i + 1]);
+ }else if (token.equals("file")){
+ String file = tokens[i + 1];
+ if (file.startsWith("\"")){
+ file = file.substring(1, file.length()-1);
+ }
+ TextureKey key = new TextureKey(folder + file, true);
+ key.setGenerateMips(false);
+ tex = info.getManager().loadTexture(key);
+ tex.setMagFilter(Texture.MagFilter.Bilinear);
+ tex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps);
+ }
+ }
+ // set page
+ if (index >= 0 && tex != null){
+ Material mat = new Material(spriteMat);
+ mat.setTexture("ColorMap", tex);
+ mat.setBoolean("VertexColor", true);
+ mat.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
+ matPages[index] = mat;
+ }
+ }else if (tokens[0].equals("char")){
+ // New BitmapCharacter
+ BitmapCharacter ch = null;
+ for (int i = 1; i < tokens.length; i++){
+ String token = tokens[i];
+ if (token.equals("id")){
+ int index = Integer.parseInt(tokens[i + 1]);
+ ch = new BitmapCharacter();
+ charSet.addCharacter(index, ch);
+ }else if (token.equals("x")){
+ ch.setX(Integer.parseInt(tokens[i + 1]));
+ }else if (token.equals("y")){
+ ch.setY(Integer.parseInt(tokens[i + 1]));
+ }else if (token.equals("width")){
+ ch.setWidth(Integer.parseInt(tokens[i + 1]));
+ }else if (token.equals("height")){
+ ch.setHeight(Integer.parseInt(tokens[i + 1]));
+ }else if (token.equals("xoffset")){
+ ch.setXOffset(Integer.parseInt(tokens[i + 1]));
+ }else if (token.equals("yoffset")){
+ ch.setYOffset(Integer.parseInt(tokens[i + 1]));
+ }else if (token.equals("xadvance")){
+ ch.setXAdvance(Integer.parseInt(tokens[i + 1]));
+ } else if (token.equals("page")) {
+ ch.setPage(Integer.parseInt(tokens[i + 1]));
+ }
+ }
+ }else if (tokens[0].equals("kerning")){
+ // Build kerning list
+ int index = 0;
+ int second = 0;
+ int amount = 0;
+
+ for (int i = 1; i < tokens.length; i++){
+ if (tokens[i].equals("first")){
+ index = Integer.parseInt(tokens[i + 1]);
+ }else if (tokens[i].equals("second")){
+ second = Integer.parseInt(tokens[i + 1]);
+ }else if (tokens[i].equals("amount")){
+ amount = Integer.parseInt(tokens[i + 1]);
+ }
+ }
+
+ BitmapCharacter ch = charSet.getCharacter(index);
+ ch.addKerning(second, amount);
+ }
+ }
+ reader.close();
+
+ return font;
+ }
+
+}
diff --git a/engine/src/core-plugins/com/jme3/material/plugins/J3MLoader.java b/engine/src/core-plugins/com/jme3/material/plugins/J3MLoader.java
new file mode 100644
index 000000000..d8477a46e
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/material/plugins/J3MLoader.java
@@ -0,0 +1,604 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.material.plugins;
+
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetKey;
+import com.jme3.material.*;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.asset.AssetLoader;
+import com.jme3.asset.AssetManager;
+import com.jme3.asset.TextureKey;
+import com.jme3.material.RenderState.BlendMode;
+import com.jme3.material.RenderState.FaceCullMode;
+import com.jme3.material.TechniqueDef.LightMode;
+import com.jme3.material.TechniqueDef.ShadowMode;
+import com.jme3.shader.Shader.ShaderType;
+import com.jme3.shader.VarType;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import com.jme3.util.BufferUtils;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.Locale;
+import java.util.Scanner;
+
+public class J3MLoader implements AssetLoader {
+
+ private AssetManager owner;
+ private Scanner scan;
+ private String fileName;
+
+ private MaterialDef materialDef;
+ private Material material;
+ private TechniqueDef technique;
+ private RenderState renderState;
+
+ private String shaderLang;
+ private String vertName;
+ private String fragName;
+
+ public J3MLoader(){
+ }
+
+ private void throwIfNequal(String expected, String got) throws IOException {
+ if (expected == null)
+ throw new IOException("Expected a statement, got '"+got+"'!");
+
+ if (!expected.equals(got))
+ throw new IOException("Expected '"+expected+"', got '"+got+"'!");
+ }
+
+ private void nextStatement(){
+ while (true){
+ if (scan.hasNext("\\}")){
+ break;
+ }else if (scan.hasNext("[\n;]")){
+ scan.next();
+ }else if (scan.hasNext("//")){
+ scan.useDelimiter("\n");
+ scan.next();
+ scan.useDelimiter("\\p{javaWhitespace}+");
+ }else{
+ break;
+ }
+ }
+ }
+
+ private String readString(String end){
+ scan.useDelimiter(end);
+ String str = scan.next();
+ scan.useDelimiter("\\p{javaWhitespace}+");
+ return str.trim();
+ }
+
+ private Image createColorTexture(ColorRGBA color){
+ if (color.getAlpha() == 1.0f){
+ // create RGB texture
+ ByteBuffer data = BufferUtils.createByteBuffer(3);
+ byte[] bytes = color.asBytesRGBA();
+ data.put(bytes[0]).put(bytes[1]).put(bytes[2]);
+ data.flip();
+
+ return new Image(Format.RGB8, 1, 1, data);
+ }else{
+ // create RGBA texture
+ ByteBuffer data = BufferUtils.createByteBuffer(4);
+ data.putInt(color.asIntRGBA());
+ data.flip();
+
+ return new Image(Format.RGBA8, 1, 1, data);
+ }
+ }
+
+ private void readShaderStatement(ShaderType type) throws IOException {
+ String lang = readString(":");
+
+ String word = scan.next();
+ throwIfNequal(":", word);
+
+ word = readString("[\n;(\\})]"); // new line, semicolon, comment or brace will end a statement
+ // locate source code
+
+ if (type == ShaderType.Vertex)
+ vertName = word;
+ else if (type == ShaderType.Fragment)
+ fragName = word;
+
+ shaderLang = lang;
+ }
+
+ private void readLightMode(){
+ String mode = readString("[\n;(\\})]");
+ LightMode lm = LightMode.valueOf(mode);
+ technique.setLightMode(lm);
+ }
+
+ private void readShadowMode(){
+ String mode = readString("[\n;(\\})]");
+ ShadowMode sm = ShadowMode.valueOf(mode);
+ technique.setShadowMode(sm);
+ }
+
+ private void readParam() throws IOException{
+ String word = scan.next();
+ VarType type;
+ if (word.equals("Color")){
+ type = VarType.Vector4;
+ }else{
+ type = VarType.valueOf(word);
+ }
+
+ word = readString("[\n;(//)(\\})]");
+ FixedFuncBinding ffBinding = null;
+ if (word.contains(":")){
+ // contains fixed func binding
+ String[] split = word.split(":");
+ word = split[0].trim();
+
+ try {
+ ffBinding = FixedFuncBinding.valueOf(split[1].trim());
+ } catch (IllegalArgumentException ex){
+ throw new IOException("FixedFuncBinding '" +
+ split[1] + "' does not exist!");
+ }
+ }
+ // TODO: add support for default vals
+ materialDef.addMaterialParam(type, word, null, ffBinding);
+ }
+
+ private void readValueParam() throws IOException{
+ String name = readString(":");
+ throwIfNequal(":", scan.next());
+
+ // parse value
+ MatParam p = material.getMaterialDef().getMaterialParam(name);
+ if (p == null)
+ throw new IOException("The material parameter: "+name+" is undefined.");
+
+ VarType type = p.getVarType();
+ if (type.isTextureType()){
+// String texturePath = readString("[\n;(//)(\\})]");
+ String texturePath = readString("[\n;(\\})]");
+ boolean flipY = false;
+ boolean repeat = false;
+ if (texturePath.startsWith("Flip Repeat ")){
+ texturePath = texturePath.substring(12).trim();
+ flipY = true;
+ repeat = true;
+ }else if (texturePath.startsWith("Flip ")){
+ texturePath = texturePath.substring(5).trim();
+ flipY = true;
+ }else if (texturePath.startsWith("Repeat ")){
+ texturePath = texturePath.substring(7).trim();
+ repeat = true;
+ }
+
+ TextureKey key = new TextureKey(texturePath, flipY);
+ key.setAsCube(type == VarType.TextureCubeMap);
+ key.setGenerateMips(true);
+
+ Texture tex = owner.loadTexture(key);
+ if (tex != null){
+ if (repeat)
+ tex.setWrap(WrapMode.Repeat);
+
+ material.setTextureParam(name, type, tex);
+ }
+ }else{
+ switch (type){
+ case Float:
+ material.setParam(name, type, scan.nextFloat());
+ break;
+ case Vector2:
+ material.setParam(name, type, new Vector2f(scan.nextFloat(),
+ scan.nextFloat()));
+ break;
+ case Vector3:
+ material.setParam(name, type, new Vector3f(scan.nextFloat(),
+ scan.nextFloat(),
+ scan.nextFloat()));
+ break;
+ case Vector4:
+ material.setParam(name, type, new ColorRGBA(scan.nextFloat(),
+ scan.nextFloat(),
+ scan.nextFloat(),
+ scan.nextFloat()));
+ break;
+ case Int:
+ material.setParam(name, type, scan.nextInt());
+ break;
+ case Boolean:
+ material.setParam(name, type, scan.nextBoolean());
+ break;
+ default:
+ throw new UnsupportedOperationException("Unknown type: "+type);
+ }
+ }
+ }
+
+ private void readMaterialParams() throws IOException{
+ nextStatement();
+
+ String word = scan.next();
+ throwIfNequal("{", word);
+
+ nextStatement();
+
+ while (true){
+ if (scan.hasNext("\\}")){
+ scan.next();
+ break;
+ }
+
+ readParam();
+ nextStatement();
+ }
+ }
+
+ private void readExtendingMaterialParams() throws IOException{
+ nextStatement();
+
+ String word = scan.next();
+ throwIfNequal("{", word);
+
+ nextStatement();
+
+ while (true){
+ if (scan.hasNext("\\}")){
+ scan.next();
+ break;
+ }
+
+ readValueParam();
+ nextStatement();
+ }
+ }
+
+ private void readWorldParams() throws IOException{
+ nextStatement();
+
+ String word = scan.next();
+ throwIfNequal("{", word);
+
+ nextStatement();
+
+ while (true){
+ if (scan.hasNext("\\}")){
+ scan.next();
+ break;
+ }
+
+ word = readString("[\n;(//)(\\})]");
+ if (word != null && !word.equals("")){
+ technique.addWorldParam(word);
+ }
+ nextStatement();
+ }
+ }
+
+ private boolean parseBoolean(String word){
+ return word != null && word.equals("On");
+ }
+
+ private void readRenderStateStatement() throws IOException{
+ String word = scan.next();
+ if (word.equals("Wireframe")){
+ renderState.setWireframe(parseBoolean(scan.next()));
+ }else if (word.equals("FaceCull")){
+ renderState.setFaceCullMode(FaceCullMode.valueOf(scan.next()));
+ }else if (word.equals("DepthWrite")){
+ renderState.setDepthWrite(parseBoolean(scan.next()));
+ }else if (word.equals("DepthTest")){
+ renderState.setDepthTest(parseBoolean(scan.next()));
+ }else if (word.equals("Blend")){
+ renderState.setBlendMode(BlendMode.valueOf(scan.next()));
+ }else if (word.equals("AlphaTestFalloff")){
+ renderState.setAlphaTest(true);
+ renderState.setAlphaFallOff(scan.nextFloat());
+ }else if (word.equals("PolyOffset")){
+ float factor = scan.nextFloat();
+ float units = scan.nextFloat();
+ renderState.setPolyOffset(factor, units);
+ }else if (word.equals("ColorWrite")){
+ renderState.setColorWrite(parseBoolean(scan.next()));
+ }else if (word.equals("PointSprite")){
+ renderState.setPointSprite(parseBoolean(scan.next()));
+ }else{
+ throwIfNequal(null, word);
+ }
+ }
+
+ private void readAdditionalRenderState() throws IOException{
+ nextStatement();
+
+ String word = scan.next();
+ throwIfNequal("{", word);
+
+ nextStatement();
+
+ renderState = material.getAdditionalRenderState();
+
+ while (true){
+ if (scan.hasNext("\\}")){
+ scan.next();
+ break;
+ }
+
+ readRenderStateStatement();
+ nextStatement();
+ }
+
+ renderState = null;
+ }
+
+ private void readRenderState() throws IOException{
+ nextStatement();
+
+ String word = scan.next();
+ throwIfNequal("{", word);
+
+ nextStatement();
+
+ renderState = new RenderState();
+
+ while (true){
+ if (scan.hasNext("\\}")){
+ scan.next();
+ break;
+ }
+
+ readRenderStateStatement();
+ nextStatement();
+ }
+
+ technique.setRenderState(renderState);
+ renderState = null;
+ }
+
+ private void readDefine(){
+ // stops at either next statement or colon
+ // ways to end a statement:
+ /*
+ Block {
+ Statement
+ Statement;
+ Statement //comment
+ Statement }
+ */
+ String defineName = readString("[\n;:(//)(\\})]");
+ if (defineName.equals(""))
+ return;
+
+ String matParamName = null;
+ if (scan.hasNext(":")){
+ scan.next();
+ // this time without colon
+ matParamName = readString("[\n;(//)(\\})]");
+ // add define <-> param mapping
+ technique.addShaderParamDefine(matParamName, defineName);
+ }else{
+ // add preset define
+ technique.addShaderPresetDefine(defineName, VarType.Boolean, true);
+ }
+ }
+
+ private void readDefines() throws IOException{
+ nextStatement();
+
+ String word = scan.next();
+ throwIfNequal("{", word);
+
+ nextStatement();
+
+ while (true){
+ if (scan.hasNext("\\}")){
+ scan.next();
+ break;
+ }
+
+ readDefine();
+ nextStatement();
+ }
+
+ }
+
+ private void readTechniqueStatement() throws IOException{
+ String word = scan.next();
+ if (word.equals("VertexShader")){
+ readShaderStatement(ShaderType.Vertex);
+ }else if (word.equals("FragmentShader")){
+ readShaderStatement(ShaderType.Fragment);
+ }else if (word.equals("LightMode")){
+ readLightMode();
+ }else if (word.equals("ShadowMode")){
+ readShadowMode();
+ }else if (word.equals("WorldParameters")){
+ readWorldParams();
+ }else if (word.equals("RenderState")){
+ readRenderState();
+ }else if (word.equals("Defines")){
+ readDefines();
+ }else{
+ throwIfNequal(null, word);
+ }
+ nextStatement();
+ }
+
+ private void readTransparentStatement() throws IOException{
+ String on = readString("[\n;(\\})]");
+ material.setTransparent(parseBoolean(on));
+ }
+
+ private void readTechnique() throws IOException{
+ String name = null;
+ if (!scan.hasNext("\\{")){
+ name = scan.next();
+ }
+ technique = new TechniqueDef(name);
+
+ String word = scan.next();
+ throwIfNequal("{", word);
+
+ nextStatement();
+
+ while (true){
+ if (scan.hasNext("\\}")){
+ scan.next();
+ break;
+ }
+
+ readTechniqueStatement();
+ }
+
+ if (vertName != null && fragName != null){
+ technique.setShaderFile(vertName, fragName, shaderLang);
+ }
+
+ materialDef.addTechniqueDef(technique);
+ technique = null;
+ vertName = null;
+ fragName = null;
+ shaderLang = null;
+ }
+
+ private void loadFromScanner() throws IOException{
+ nextStatement();
+
+ boolean extending = false;
+ String name = null;
+ String word = scan.next();
+ if (word.equals("Material")){
+ extending = true;
+ }else if (word.equals("MaterialDef")){
+ extending = false;
+ }else{
+ throw new IOException("Specified file is not a Material file");
+ }
+
+ nextStatement();
+
+ word = readString("[(\\{)(//)\n:]");
+ if (word == null || word.equals(""))
+ throw new IOException("Material name cannot be empty");
+
+ name = word;
+
+ nextStatement();
+
+ if (scan.hasNext(":")){
+ if (!extending){
+ throw new IOException("Must use 'Material' when extending.");
+ }
+
+ scan.next(); // skip colon
+ String extendedMat = readString("\\{");
+
+ MaterialDef def = (MaterialDef) owner.loadAsset(new AssetKey(extendedMat));
+ if (def == null)
+ throw new IOException("Extended material "+extendedMat+" cannot be found.");
+
+ material = new Material(def);
+ }else if (scan.hasNext("\\{")){
+ if (extending){
+ throw new IOException("Expected ':', got '{'");
+ }
+ materialDef = new MaterialDef(owner, name);
+ // NOTE: pass file name for defs so they can be loaded later
+ materialDef.setAssetName(fileName);
+ }
+ scan.next(); // skip {
+
+ nextStatement();
+
+ while (true){
+ if (scan.hasNext("\\}")){
+ scan.next();
+ break;
+ }
+
+ word = scan.next();
+ if (extending){
+ if (word.equals("MaterialParameters")){
+ readExtendingMaterialParams();
+ nextStatement();
+ }else if (word.equals("AdditionalRenderState")){
+ readAdditionalRenderState();
+ nextStatement();
+ }else if (word.equals("Transparent")){
+ readTransparentStatement();
+ nextStatement();
+ }
+ }else{
+ if (word.equals("Technique")){
+ readTechnique();
+ nextStatement();
+ }else if (word.equals("MaterialParameters")){
+ readMaterialParams();
+ nextStatement();
+ }else{
+ throw new IOException("Expected material statement, got '"+scan.next()+"'");
+ }
+ }
+ }
+ }
+
+ public Object load(AssetInfo info) throws IOException {
+ this.owner = info.getManager();
+
+ InputStream in = info.openStream();
+ try {
+ scan = new Scanner(in);
+ scan.useLocale(Locale.US);
+ this.fileName = info.getKey().getName();
+ loadFromScanner();
+ } finally {
+ if (in != null)
+ in.close();
+ }
+
+ if (material != null){
+ // material implementation
+ return material;
+ }else{
+ // material definition
+ return materialDef;
+ }
+ }
+
+}
diff --git a/engine/src/core-plugins/com/jme3/scene/plugins/MTLLoader.java b/engine/src/core-plugins/com/jme3/scene/plugins/MTLLoader.java
new file mode 100644
index 000000000..b8f5a1887
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/scene/plugins/MTLLoader.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene.plugins;
+
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetLoader;
+import com.jme3.asset.AssetManager;
+import com.jme3.asset.TextureKey;
+import com.jme3.material.MatParam;
+import com.jme3.material.Material;
+import com.jme3.material.MaterialList;
+import com.jme3.material.RenderState.BlendMode;
+import com.jme3.material.RenderState.FaceCullMode;
+import com.jme3.math.ColorRGBA;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Locale;
+import java.util.Scanner;
+
+public class MTLLoader implements AssetLoader {
+
+ protected Scanner scan;
+ protected MaterialList matList;
+ protected Material material;
+ protected AssetManager assetManager;
+ protected String folderName;
+
+ public void reset(){
+ scan = null;
+ matList = null;
+ material = null;
+ }
+
+ protected ColorRGBA readColor(){
+ ColorRGBA v = new ColorRGBA();
+ v.set(scan.nextFloat(), scan.nextFloat(), scan.nextFloat(), 1.0f);
+ return v;
+ }
+
+ protected void nextStatement(){
+ scan.useDelimiter("\n");
+ scan.next();
+ scan.useDelimiter("\\p{javaWhitespace}+");
+ }
+
+ protected void startMaterial(String name){
+ material = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
+ material.setBoolean("UseMaterialColors", true);
+ material.setColor("Ambient", ColorRGBA.DarkGray);
+ material.setColor("Diffuse", ColorRGBA.White);
+ material.setColor("Specular", ColorRGBA.Black);
+ material.setFloat("Shininess", 16f); // prevents "premature culling" bug
+ matList.put(name, material);
+ }
+
+ protected boolean readLine(){
+ if (!scan.hasNext()){
+ return false;
+ }
+
+ String cmd = scan.next();
+ if (cmd.startsWith("#")){
+ // skip entire comment until next line
+ }else if (cmd.equals("newmtl")){
+ String name = scan.next();
+ startMaterial(name);
+ }else if (cmd.equals("Ka") || cmd.equals("Ke") || cmd.equals("Ni") || cmd.equals("illum")){
+ // ignore it for now
+ }else if (cmd.equals("Kd")){
+ ColorRGBA color = readColor();
+ MatParam param = material.getParam("Diffuse");
+ if (param != null){
+ color.a = ((ColorRGBA) param.getValue()).getAlpha();
+ }
+ material.setColor("Diffuse", color);
+ }else if (cmd.equals("Ks")){
+ material.setColor("Specular", readColor());
+ }else if (cmd.equals("Ns")){
+ material.setFloat("Shininess", scan.nextFloat() /* (128f / 1000f)*/ );
+ }else if (cmd.equals("d")){
+ float alpha = scan.nextFloat();
+// if (alpha < 1f){
+// MatParam param = material.getParam("Diffuse");
+// ColorRGBA color;
+// if (param != null)
+// color = (ColorRGBA) param.getValue();
+// else
+// color = new ColorRGBA(ColorRGBA.White);
+//
+// color.a = scan.nextFloat();
+// material.setColor("Diffuse", color);
+// material.setBoolean("UseAlpha", true);
+// material.setTransparent(true);
+// material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
+// }
+ }else if (cmd.equals("map_Ka")){
+ // ignore it for now
+ }else if (cmd.equals("map_Kd")){
+ String path = scan.next();
+ String name = new File(path).getName();
+ TextureKey key = new TextureKey(folderName + name);
+ key.setGenerateMips(true);
+ Texture texture = assetManager.loadTexture(key);
+ if (texture != null){
+ texture.setWrap(WrapMode.Repeat);
+ material.setTexture("DiffuseMap", texture);
+ }
+ }else if (cmd.equals("map_bump") || cmd.equals("bump")){
+ if (material.getParam("NormalMap") == null){
+ String path = scan.next();
+ String name = new File(path).getName();
+ TextureKey key = new TextureKey(folderName + name);
+ key.setGenerateMips(true);
+ Texture texture = assetManager.loadTexture(key);
+ if (texture != null){
+ texture.setWrap(WrapMode.Repeat);
+ material.setTexture("NormalMap", texture);
+ if (texture.getImage().getFormat() == Format.LATC){
+ material.setBoolean("LATC", true);
+ }
+ }
+ }
+ }else if (cmd.equals("map_Ks")){
+ String path = scan.next();
+ String name = new File(path).getName();
+ TextureKey key = new TextureKey(folderName + name);
+ key.setGenerateMips(true);
+ Texture texture = assetManager.loadTexture(key);
+ if (texture != null){
+ texture.setWrap(WrapMode.Repeat);
+ material.setTexture("SpecularMap", texture);
+
+ // NOTE: since specular color is modulated with specmap
+ // make sure we have it set
+ material.setColor("Specular", ColorRGBA.White);
+ }
+ }else if (cmd.equals("map_d")){
+ String path = scan.next();
+ String name = new File(path).getName();
+ TextureKey key = new TextureKey(folderName + name);
+ key.setGenerateMips(true);
+ Texture texture = assetManager.loadTexture(key);
+ if (texture != null){
+ texture.setWrap(WrapMode.Repeat);
+ material.setTexture("AlphaMap", texture);
+ material.setTransparent(true);
+ material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
+ material.getAdditionalRenderState().setAlphaTest(true);
+ material.getAdditionalRenderState().setAlphaFallOff(0.01f);
+ }
+ }else{
+ System.out.println("Unknown statement in MTL! "+cmd);
+ }
+ nextStatement();
+
+ return true;
+ }
+
+ @SuppressWarnings("empty-statement")
+ public Object load(AssetInfo info){
+ this.assetManager = info.getManager();
+ folderName = info.getKey().getFolder();
+
+ InputStream in = info.openStream();
+ scan = new Scanner(in);
+ scan.useLocale(Locale.US);
+
+ matList = new MaterialList();
+ while (readLine());
+ MaterialList list = matList;
+
+ reset();
+
+ try{
+ in.close();
+ }catch (IOException ex){
+ }
+ return list;
+ }
+}
diff --git a/engine/src/core-plugins/com/jme3/scene/plugins/OBJLoader.java b/engine/src/core-plugins/com/jme3/scene/plugins/OBJLoader.java
new file mode 100644
index 000000000..99202d4b3
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/scene/plugins/OBJLoader.java
@@ -0,0 +1,546 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene.plugins;
+
+import com.jme3.asset.*;
+import com.jme3.material.Material;
+import com.jme3.material.MaterialList;
+import com.jme3.util.*;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.queue.RenderQueue.Bucket;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Mesh.Mode;
+import com.jme3.scene.Node;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.mesh.IndexBuffer;
+import com.jme3.scene.mesh.IndexIntBuffer;
+import com.jme3.scene.mesh.IndexShortBuffer;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.nio.ShortBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map.Entry;
+import java.util.Scanner;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
+
+/**
+ * Reads OBJ format models.
+ */
+public final class OBJLoader implements AssetLoader {
+
+ private static final Logger logger = Logger.getLogger(OBJLoader.class.getName());
+
+ protected final ArrayList verts = new ArrayList();
+ protected final ArrayList texCoords = new ArrayList();
+ protected final ArrayList norms = new ArrayList();
+ protected final ArrayList faces = new ArrayList();
+ protected final HashMap> matFaces = new HashMap>();
+ protected String currentMatName;
+
+ protected final HashMap vertIndexMap = new HashMap();
+ protected final IntMap indexVertMap = new IntMap();
+ protected int curIndex = 0;
+ protected int geomIndex = 0;
+
+ protected Scanner scan;
+ protected ModelKey key;
+ protected AssetManager assetManager;
+ protected MaterialList matList;
+
+ protected String objName;
+ protected Node objNode;
+
+ protected static class Vertex {
+
+ Vector3f v;
+ Vector2f vt;
+ Vector3f vn;
+ int index;
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final Vertex other = (Vertex) obj;
+ if (this.v != other.v && (this.v == null || !this.v.equals(other.v))) {
+ return false;
+ }
+ if (this.vt != other.vt && (this.vt == null || !this.vt.equals(other.vt))) {
+ return false;
+ }
+ if (this.vn != other.vn && (this.vn == null || !this.vn.equals(other.vn))) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 5;
+ hash = 53 * hash + (this.v != null ? this.v.hashCode() : 0);
+ hash = 53 * hash + (this.vt != null ? this.vt.hashCode() : 0);
+ hash = 53 * hash + (this.vn != null ? this.vn.hashCode() : 0);
+ return hash;
+ }
+ }
+
+ protected static class Face {
+ Vertex[] verticies;
+ }
+
+ public void reset(){
+ verts.clear();
+ texCoords.clear();
+ norms.clear();
+ faces.clear();
+ matFaces.clear();
+
+ vertIndexMap.clear();
+ indexVertMap.clear();
+
+ currentMatName = null;
+ curIndex = 0;
+ geomIndex = 0;
+ scan = null;
+ }
+
+ protected void findVertexIndex(Vertex vert){
+ Integer index = vertIndexMap.get(vert);
+ if (index != null){
+ vert.index = index.intValue();
+ }else{
+ vert.index = curIndex++;
+ vertIndexMap.put(vert, vert.index);
+ indexVertMap.put(vert.index, vert);
+ }
+ }
+
+ protected Face[] quadToTriangle(Face f){
+ assert f.verticies.length == 4;
+
+ Face[] t = new Face[]{ new Face(), new Face() };
+ t[0].verticies = new Vertex[3];
+ t[1].verticies = new Vertex[3];
+
+ Vertex v0 = f.verticies[0];
+ Vertex v1 = f.verticies[1];
+ Vertex v2 = f.verticies[2];
+ Vertex v3 = f.verticies[3];
+
+ // find the pair of verticies that is closest to each over
+ // v0 and v2
+ // OR
+ // v1 and v3
+ float d1 = v0.v.distanceSquared(v2.v);
+ float d2 = v1.v.distanceSquared(v3.v);
+ if (d1 < d2){
+ // put an edge in v0, v2
+ t[0].verticies[0] = v0;
+ t[0].verticies[1] = v1;
+ t[0].verticies[2] = v3;
+
+ t[1].verticies[0] = v1;
+ t[1].verticies[1] = v2;
+ t[1].verticies[2] = v3;
+ }else{
+ // put an edge in v1, v3
+ t[0].verticies[0] = v0;
+ t[0].verticies[1] = v1;
+ t[0].verticies[2] = v2;
+
+ t[1].verticies[0] = v0;
+ t[1].verticies[1] = v2;
+ t[1].verticies[2] = v3;
+ }
+
+ return t;
+ }
+
+ private ArrayList vertList = new ArrayList();
+
+ protected void readFace(){
+ Face f = new Face();
+ vertList.clear();
+
+ String line = scan.nextLine().trim();
+ String[] verticies = line.split(" ");
+ for (String vertex : verticies){
+ int v = 0;
+ int vt = 0;
+ int vn = 0;
+
+ String[] split = vertex.split("/");
+ if (split.length == 1){
+ v = Integer.parseInt(split[0]);
+ }else if (split.length == 2){
+ v = Integer.parseInt(split[0]);
+ vt = Integer.parseInt(split[1]);
+ }else if (split.length == 3 && !split[1].equals("")){
+ v = Integer.parseInt(split[0]);
+ vt = Integer.parseInt(split[1]);
+ vn = Integer.parseInt(split[2]);
+ }else if (split.length == 3){
+ v = Integer.parseInt(split[0]);
+ vn = Integer.parseInt(split[2]);
+ }
+
+ Vertex vx = new Vertex();
+ vx.v = verts.get(v - 1);
+
+ if (vt > 0)
+ vx.vt = texCoords.get(vt - 1);
+
+ if (vn > 0)
+ vx.vn = norms.get(vn - 1);
+
+ vertList.add(vx);
+ }
+
+ if (vertList.size() > 4 || vertList.size() <= 2)
+ logger.warning("Edge or polygon detected in OBJ. Ignored.");
+
+ f.verticies = new Vertex[vertList.size()];
+ for (int i = 0; i < vertList.size(); i++){
+ f.verticies[i] = vertList.get(i);
+ }
+
+ if (matList != null){
+ matFaces.get(currentMatName).add(f);
+ }else{
+ faces.add(f); // faces that belong to the default material
+ }
+ }
+
+ protected Vector3f readVector3(){
+ Vector3f v = new Vector3f();
+
+ v.set(Float.parseFloat(scan.next()),
+ Float.parseFloat(scan.next()),
+ Float.parseFloat(scan.next()));
+
+ return v;
+ }
+
+ protected Vector2f readVector2(){
+ Vector2f v = new Vector2f();
+
+ String line = scan.nextLine().trim();
+ String[] split = line.split(" ");
+ v.setX( Float.parseFloat(split[0]) );
+ v.setY( Float.parseFloat(split[1]) );
+
+// v.setX(scan.nextFloat());
+// if (scan.hasNextFloat()){
+// v.setY(scan.nextFloat());
+// if (scan.hasNextFloat()){
+// scan.nextFloat(); // ignore
+// }
+// }
+
+ return v;
+ }
+
+ protected void loadMtlLib(String name) throws IOException{
+ if (!name.toLowerCase().endsWith(".mtl"))
+ throw new IOException("Expected .mtl file! Got: " + name);
+
+ matList = (MaterialList) assetManager.loadAsset(key.getFolder() + name);
+
+ if (matList != null){
+ // create face lists for every material
+ for (String matName : matList.keySet()){
+ matFaces.put(matName, new ArrayList());
+ }
+ }else{
+ logger.log(Level.WARNING, "Can't find MTL file. " +
+ "Using default material for OBJ.");
+ }
+ }
+
+ private static final Pattern nl = Pattern.compile("\n");
+ private static final Pattern ws = Pattern.compile("\\p{javaWhitespace}+");
+
+ protected void nextStatement(){
+ scan.useDelimiter(nl);
+ scan.next();
+ scan.useDelimiter(ws);
+ }
+
+ protected boolean readLine() throws IOException{
+ if (!scan.hasNext()){
+ return false;
+ }
+
+ String cmd = scan.next();
+ if (cmd.startsWith("#")){
+ // skip entire comment until next line
+ nextStatement();
+ }else if (cmd.equals("v")){
+ // vertex position
+ verts.add(readVector3());
+ }else if (cmd.equals("vn")){
+ // vertex normal
+ norms.add(readVector3());
+ }else if (cmd.equals("vt")){
+ // texture coordinate
+ texCoords.add(readVector2());
+ }else if (cmd.equals("f")){
+ // face, can be triangle, quad, or polygon (unsupported)
+ readFace();
+ }else if (cmd.equals("usemtl")){
+ // use material from MTL lib for the following faces
+ currentMatName = scan.next();
+ }else if (cmd.equals("mtllib")){
+ // specify MTL lib to use for this OBJ file
+ String mtllib = scan.nextLine().trim();
+ loadMtlLib(mtllib);
+ }else if (cmd.equals("s") || cmd.equals("g")){
+ nextStatement();
+ }else{
+ // skip entire command until next line
+ System.out.println("Unknown statement in OBJ! "+cmd);
+ nextStatement();
+ }
+
+ return true;
+ }
+
+ protected Geometry createGeometry(ArrayList faceList, String matName) throws IOException{
+ if (faceList.size() == 0)
+ throw new IOException("No geometry data to generate mesh");
+
+ // Create mesh from the faces
+ Mesh mesh = constructMesh(faceList);
+
+ Geometry geom = new Geometry(objName + "-geom-" + (geomIndex++), mesh);
+
+ Material material = null;
+ if (matName != null && matList != null){
+ // Get material from material list
+ material = matList.get(matName);
+ }
+ if (material == null){
+ // create default material
+ material = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
+ material.setFloat("Shininess", 64);
+ }
+ geom.setMaterial(material);
+ if (material.isTransparent())
+ geom.setQueueBucket(Bucket.Transparent);
+ else
+ geom.setQueueBucket(Bucket.Opaque);
+
+ return geom;
+ }
+
+ protected Mesh constructMesh(ArrayList faceList){
+ Mesh m = new Mesh();
+ m.setMode(Mode.Triangles);
+
+ boolean hasTexCoord = false;
+ boolean hasNormals = false;
+
+ ArrayList newFaces = new ArrayList(faceList.size());
+ for (int i = 0; i < faceList.size(); i++){
+ Face f = faceList.get(i);
+
+ for (Vertex v : f.verticies){
+ findVertexIndex(v);
+
+ if (!hasTexCoord && v.vt != null)
+ hasTexCoord = true;
+ if (!hasNormals && v.vn != null)
+ hasNormals = true;
+ }
+
+ if (f.verticies.length == 4){
+ Face[] t = quadToTriangle(f);
+ newFaces.add(t[0]);
+ newFaces.add(t[1]);
+ }else{
+ newFaces.add(f);
+ }
+ }
+
+ FloatBuffer posBuf = BufferUtils.createFloatBuffer(vertIndexMap.size() * 3);
+ FloatBuffer normBuf = null;
+ FloatBuffer tcBuf = null;
+
+ if (hasNormals){
+ normBuf = BufferUtils.createFloatBuffer(vertIndexMap.size() * 3);
+ }
+ if (hasTexCoord){
+ tcBuf = BufferUtils.createFloatBuffer(vertIndexMap.size() * 2);
+ }
+
+ IndexBuffer indexBuf = null;
+ if (vertIndexMap.size() >= 65536){
+ // too many verticies: use intbuffer instead of shortbuffer
+ IntBuffer ib = BufferUtils.createIntBuffer(newFaces.size() * 3);
+ m.setBuffer(VertexBuffer.Type.Index, 3, ib);
+ indexBuf = new IndexIntBuffer(ib);
+ }else{
+ ShortBuffer sb = BufferUtils.createShortBuffer(newFaces.size() * 3);
+ m.setBuffer(VertexBuffer.Type.Index, 3, sb);
+ indexBuf = new IndexShortBuffer(sb);
+ }
+
+ int numFaces = newFaces.size();
+ for (int i = 0; i < numFaces; i++){
+ Face f = newFaces.get(i);
+ if (f.verticies.length != 3)
+ continue;
+
+ Vertex v0 = f.verticies[0];
+ Vertex v1 = f.verticies[1];
+ Vertex v2 = f.verticies[2];
+
+ posBuf.position(v0.index * 3);
+ posBuf.put(v0.v.x).put(v0.v.y).put(v0.v.z);
+ posBuf.position(v1.index * 3);
+ posBuf.put(v1.v.x).put(v1.v.y).put(v1.v.z);
+ posBuf.position(v2.index * 3);
+ posBuf.put(v2.v.x).put(v2.v.y).put(v2.v.z);
+
+ if (normBuf != null){
+ if (v0.vn != null){
+ normBuf.position(v0.index * 3);
+ normBuf.put(v0.vn.x).put(v0.vn.y).put(v0.vn.z);
+ normBuf.position(v1.index * 3);
+ normBuf.put(v1.vn.x).put(v1.vn.y).put(v1.vn.z);
+ normBuf.position(v2.index * 3);
+ normBuf.put(v2.vn.x).put(v2.vn.y).put(v2.vn.z);
+ }
+ }
+
+ if (tcBuf != null){
+ if (v0.vt != null){
+ tcBuf.position(v0.index * 2);
+ tcBuf.put(v0.vt.x).put(v0.vt.y);
+ tcBuf.position(v1.index * 2);
+ tcBuf.put(v1.vt.x).put(v1.vt.y);
+ tcBuf.position(v2.index * 2);
+ tcBuf.put(v2.vt.x).put(v2.vt.y);
+ }
+ }
+
+ int index = i * 3; // current face * 3 = current index
+ indexBuf.put(index, v0.index);
+ indexBuf.put(index+1, v1.index);
+ indexBuf.put(index+2, v2.index);
+ }
+
+ m.setBuffer(VertexBuffer.Type.Position, 3, posBuf);
+ m.setBuffer(VertexBuffer.Type.Normal, 3, normBuf);
+ m.setBuffer(VertexBuffer.Type.TexCoord, 2, tcBuf);
+ // index buffer was set on creation
+
+ m.setStatic();
+ m.updateBound();
+ m.updateCounts();
+ //m.setInterleaved();
+
+ // clear data generated face statements
+ // to prepare for next mesh
+ vertIndexMap.clear();
+ indexVertMap.clear();
+ curIndex = 0;
+
+ return m;
+ }
+
+ @SuppressWarnings("empty-statement")
+ public Object load(AssetInfo info) throws IOException{
+ key = (ModelKey) info.getKey();
+ assetManager = info.getManager();
+
+ if (!(info.getKey() instanceof ModelKey))
+ throw new IllegalArgumentException("Model assets must be loaded using a ModelKey");
+
+ InputStream in = info.openStream();
+ scan = new Scanner(in);
+ scan.useLocale(Locale.US);
+
+ objName = key.getName();
+ String folderName = key.getFolder();
+ String ext = key.getExtension();
+ objName = objName.substring(0, objName.length() - ext.length() - 1);
+ if (folderName != null && folderName.length() > 0){
+ objName = objName.substring(folderName.length());
+ }
+
+ objNode = new Node(objName + "-objnode");
+
+ while (readLine());
+
+ if (matFaces.size() > 0){
+ for (Entry> entry : matFaces.entrySet()){
+ ArrayList materialFaces = entry.getValue();
+ if (materialFaces.size() > 0){
+ Geometry geom = createGeometry(materialFaces, entry.getKey());
+ objNode.attachChild(geom);
+ }
+ }
+ }else if (faces.size() > 0){
+ // generate final geometry
+ Geometry geom = createGeometry(faces, null);
+ objNode.attachChild(geom);
+ }
+
+ reset();
+
+ try{
+ in.close();
+ }catch (IOException ex){
+ }
+
+ if (objNode.getQuantity() == 1)
+ // only 1 geometry, so no need to send node
+ return objNode.getChild(0);
+ else
+ return objNode;
+ }
+
+}
diff --git a/engine/src/core-plugins/com/jme3/texture/plugins/DDSLoader.java b/engine/src/core-plugins/com/jme3/texture/plugins/DDSLoader.java
new file mode 100644
index 000000000..1ff8b250b
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/texture/plugins/DDSLoader.java
@@ -0,0 +1,661 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.texture.plugins;
+
+import com.jme3.asset.*;
+import com.jme3.util.*;
+import com.jme3.asset.AssetLoader;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import java.io.DataInput;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+
+import java.util.ArrayList;
+import java.util.logging.Logger;
+
+/**
+ *
+ * DDSLoader
is an image loader that reads in a DirectX DDS file.
+ * Supports DXT1, DXT3, DXT5, RGB, RGBA, Grayscale, Alpha pixel formats.
+ * 2D images, mipmapped 2D images, and cubemaps.
+ *
+ * @author Gareth Jenkins-Jones
+ * @author Kirill Vainer
+ * @version $Id: DDSLoader.java,v 2.0 2008/8/15
+ */
+public class DDSLoader implements AssetLoader {
+
+ private static final Logger logger = Logger.getLogger(DDSLoader.class.getName());
+ private static final boolean forceRGBA = false;
+
+ private static final int DDSD_MANDATORY = 0x1007;
+ private static final int DDSD_MANDATORY_DX10 = 0x6;
+
+ private static final int DDSD_MIPMAPCOUNT = 0x20000;
+ private static final int DDSD_LINEARSIZE = 0x80000;
+ private static final int DDSD_DEPTH = 0x800000;
+
+ private static final int DDPF_ALPHAPIXELS = 0x1;
+ private static final int DDPF_FOURCC = 0x4;
+ private static final int DDPF_RGB = 0x40;
+ // used by compressonator to mark grayscale images, red channel mask is used for data and bitcount is 8
+ private static final int DDPF_GRAYSCALE = 0x20000;
+ // used by compressonator to mark alpha images, alpha channel mask is used for data and bitcount is 8
+ private static final int DDPF_ALPHA = 0x2;
+ // used by NVTextureTools to mark normal images.
+ private static final int DDPF_NORMAL = 0x80000000;
+
+ private static final int SWIZZLE_xGxR = 0x78477852;
+
+ private static final int DDSCAPS_COMPLEX = 0x8;
+ private static final int DDSCAPS_TEXTURE = 0x1000;
+ private static final int DDSCAPS_MIPMAP = 0x400000;
+
+ private static final int DDSCAPS2_CUBEMAP = 0x200;
+ private static final int DDSCAPS2_VOLUME = 0x200000;
+
+ private static final int PF_DXT1 = 0x31545844;
+ private static final int PF_DXT3 = 0x33545844;
+ private static final int PF_DXT5 = 0x35545844;
+ private static final int PF_ATI1 = 0x31495441;
+ private static final int PF_ATI2 = 0x32495441; // 0x41544932;
+ private static final int PF_DX10 = 0x30315844; // a DX10 format
+
+ private static final int DX10DIM_BUFFER = 0x1,
+ DX10DIM_TEXTURE1D = 0x2,
+ DX10DIM_TEXTURE2D = 0x3,
+ DX10DIM_TEXTURE3D = 0x4;
+
+ private static final int DX10MISC_GENERATE_MIPS = 0x1,
+ DX10MISC_TEXTURECUBE = 0x4;
+
+ private static final double LOG2 = Math.log(2);
+ private int width;
+ private int height;
+ private int depth;
+ private int flags;
+ private int pitchOrSize;
+ private int mipMapCount;
+ private int caps1;
+ private int caps2;
+ private boolean directx10;
+ private boolean compressed;
+ private boolean grayscaleOrAlpha;
+ private boolean normal;
+ private Format pixelFormat;
+ private int bpp;
+ private int[] sizes;
+ private int redMask, greenMask, blueMask, alphaMask;
+ private DataInput in;
+
+ public DDSLoader() {
+ }
+
+ public Object load(AssetInfo info) throws IOException{
+ if (!(info.getKey() instanceof TextureKey))
+ throw new IllegalArgumentException("Texture assets must be loaded using a TextureKey");
+
+ InputStream stream = info.openStream();
+ in = new LittleEndien(stream);
+ loadHeader();
+ ArrayList data = readData( ((TextureKey)info.getKey()).isFlipY() );
+ stream.close();
+
+ return new Image(pixelFormat, width, height, 0, data, sizes);
+ }
+
+ public Image load(InputStream stream) throws IOException{
+ in = new LittleEndien(stream);
+ loadHeader();
+ ArrayList data = readData(false);
+ return new Image(pixelFormat, width, height, 0, data, sizes);
+ }
+
+ private void loadDX10Header() throws IOException{
+ int dxgiFormat = in.readInt();
+ if (dxgiFormat != 83){
+ throw new IOException("Only DXGI_FORMAT_BC5_UNORM " +
+ "is supported for DirectX10 DDS! Got: "+dxgiFormat);
+ }
+ pixelFormat = Format.LATC;
+ bpp = 8;
+ compressed = true;
+
+ int resDim = in.readInt();
+ if (resDim == DX10DIM_TEXTURE3D){
+ // mark texture as 3D
+ }
+ int miscFlag = in.readInt();
+ int arraySize = in.readInt();
+ if (is(miscFlag, DX10MISC_TEXTURECUBE)){
+ // mark texture as cube
+ if (arraySize != 6){
+ throw new IOException("Cubemaps should consist of 6 images!");
+ }
+ }
+
+ in.skipBytes(4); // skip reserved value
+ }
+
+ /**
+ * Reads the header (first 128 bytes) of a DDS File
+ */
+ private void loadHeader() throws IOException {
+ if (in.readInt() != 0x20534444 || in.readInt() != 124) {
+ throw new IOException("Not a DDS file");
+ }
+
+ flags = in.readInt();
+
+ if (!is(flags, DDSD_MANDATORY) && !is(flags, DDSD_MANDATORY_DX10)) {
+ throw new IOException("Mandatory flags missing");
+ }
+
+ height = in.readInt();
+ width = in.readInt();
+ pitchOrSize = in.readInt();
+ depth = in.readInt();
+ mipMapCount = in.readInt();
+ in.skipBytes(44);
+ pixelFormat = null;
+ directx10 = false;
+ readPixelFormat();
+ caps1 = in.readInt();
+ caps2 = in.readInt();
+ in.skipBytes(12);
+
+ if (!directx10){
+ if (!is(caps1, DDSCAPS_TEXTURE)) {
+ throw new IOException("File is not a texture");
+ }
+
+ if (depth <= 0)
+ depth = 1;
+
+ if (is(caps2, DDSCAPS2_CUBEMAP)) {
+ depth = 6; // somewhat of a hack, force loading 6 textures if a cubemap
+ }
+ }
+
+ int expectedMipmaps = 1 + (int) Math.ceil(Math.log(Math.max(height, width)) / LOG2);
+
+ if (is(caps1, DDSCAPS_MIPMAP)) {
+ if (!is(flags, DDSD_MIPMAPCOUNT)) {
+ mipMapCount = expectedMipmaps;
+ } else if (mipMapCount != expectedMipmaps) {
+ // changed to warning- images often do not have the required amount,
+ // or specify that they have mipmaps but include only the top level..
+ logger.warning("Got " + mipMapCount + "mipmaps, expected" + expectedMipmaps);
+ }
+ } else {
+ mipMapCount = 1;
+ }
+
+ if (directx10){
+ loadDX10Header();
+ }
+
+ loadSizes();
+ }
+
+ /**
+ * Reads the PixelFormat structure in a DDS file
+ */
+ private void readPixelFormat() throws IOException {
+ int pfSize = in.readInt();
+ if (pfSize != 32) {
+ throw new IOException("Pixel format size is " + pfSize + ", not 32");
+ }
+
+ int pfFlags = in.readInt();
+ normal = is(pfFlags, DDPF_NORMAL);
+
+ if (is(pfFlags, DDPF_FOURCC)) {
+ compressed = true;
+ int fourcc = in.readInt();
+ int swizzle = in.readInt();
+ in.skipBytes(16);
+
+ switch (fourcc) {
+ case PF_DXT1:
+ bpp = 4;
+ if (is(pfFlags, DDPF_ALPHAPIXELS)) {
+ pixelFormat = Image.Format.DXT1A;
+ } else {
+ pixelFormat = Image.Format.DXT1;
+ }
+ break;
+ case PF_DXT3:
+ bpp = 8;
+ pixelFormat = Image.Format.DXT3;
+ break;
+ case PF_DXT5:
+ bpp = 8;
+ pixelFormat = Image.Format.DXT5;
+ if (swizzle == SWIZZLE_xGxR){
+ normal = true;
+ }
+ break;
+ case PF_ATI1:
+ bpp = 4;
+ pixelFormat = Image.Format.LTC;
+ break;
+ case PF_ATI2:
+ bpp = 8;
+ pixelFormat = Image.Format.LATC;
+ break;
+ case PF_DX10:
+ compressed = false;
+ directx10 = true;
+ // exit here, the rest of the structure is not valid
+ // the real format will be available in the DX10 header
+ return;
+ default:
+ throw new IOException("Unknown fourcc: " + string(fourcc) + ", " + Integer.toHexString(fourcc));
+ }
+
+ int size = ((width + 3) / 4) * ((height + 3) / 4) * bpp * 2;
+
+ if (is(flags, DDSD_LINEARSIZE)) {
+ if (pitchOrSize == 0) {
+ logger.warning("Must use linear size with fourcc");
+ pitchOrSize = size;
+ } else if (pitchOrSize != size) {
+ logger.warning("Expected size = " + size + ", real = " + pitchOrSize);
+ }
+ } else {
+ pitchOrSize = size;
+ }
+ } else {
+ compressed = false;
+
+ // skip fourCC
+ in.readInt();
+
+ bpp = in.readInt();
+ redMask = in.readInt();
+ greenMask = in.readInt();
+ blueMask = in.readInt();
+ alphaMask = in.readInt();
+
+ if (is(pfFlags, DDPF_RGB)) {
+ if (is(pfFlags, DDPF_ALPHAPIXELS)) {
+ pixelFormat = Format.RGBA8;
+ } else {
+ pixelFormat = Format.RGB8;
+ }
+ } else if (is(pfFlags, DDPF_GRAYSCALE) && is(pfFlags, DDPF_ALPHAPIXELS)){
+ switch (bpp) {
+ case 16:
+ pixelFormat = Format.Luminance8Alpha8;
+ break;
+ case 32:
+ pixelFormat = Format.Luminance16Alpha16;
+ break;
+ default:
+ throw new IOException("Unsupported GrayscaleAlpha BPP: " + bpp);
+ }
+ grayscaleOrAlpha = true;
+ } else if (is(pfFlags, DDPF_GRAYSCALE)) {
+ switch (bpp) {
+ case 8:
+ pixelFormat = Format.Luminance8;
+ break;
+ case 16:
+ pixelFormat = Format.Luminance16;
+ break;
+ default:
+ throw new IOException("Unsupported Grayscale BPP: " + bpp);
+ }
+ grayscaleOrAlpha = true;
+ } else if (is(pfFlags, DDPF_ALPHA)) {
+ switch (bpp) {
+ case 8:
+ pixelFormat = Format.Alpha8;
+ break;
+ case 16:
+ pixelFormat = Format.Alpha16;
+ break;
+ default:
+ throw new IOException("Unsupported Alpha BPP: " + bpp);
+ }
+ grayscaleOrAlpha = true;
+ } else {
+ throw new IOException("Unknown PixelFormat in DDS file");
+ }
+
+ int size = (bpp / 8 * width);
+
+ if (is(flags, DDSD_LINEARSIZE)) {
+ if (pitchOrSize == 0) {
+ logger.warning("Linear size said to contain valid value but does not");
+ pitchOrSize = size;
+ } else if (pitchOrSize != size) {
+ logger.warning("Expected size = " + size + ", real = " + pitchOrSize);
+ }
+ } else {
+ pitchOrSize = size;
+ }
+ }
+ }
+
+ /**
+ * Computes the sizes of each mipmap level in bytes, and stores it in sizes_[].
+ */
+ private void loadSizes() {
+ int mipWidth = width;
+ int mipHeight = height;
+
+ sizes = new int[mipMapCount];
+ int outBpp = pixelFormat.getBitsPerPixel();
+ for (int i = 0; i < mipMapCount; i++) {
+ int size;
+ if (compressed) {
+ size = ((mipWidth + 3) / 4) * ((mipHeight + 3) / 4) * outBpp * 2;
+ } else {
+ size = mipWidth * mipHeight * outBpp / 8;
+ }
+
+ sizes[i] = ((size + 3) / 4) * 4;
+
+ mipWidth = Math.max(mipWidth / 2, 1);
+ mipHeight = Math.max(mipHeight / 2, 1);
+ }
+ }
+
+ /**
+ * Flips the given image data on the Y axis.
+ * @param data Data array containing image data (without mipmaps)
+ * @param scanlineSize Size of a single scanline = width * bytesPerPixel
+ * @param height Height of the image in pixels
+ * @return The new data flipped by the Y axis
+ */
+ public byte[] flipData(byte[] data, int scanlineSize, int height) {
+ byte[] newData = new byte[data.length];
+
+ for (int y = 0; y < height; y++) {
+ System.arraycopy(data, y * scanlineSize,
+ newData, (height - y - 1) * scanlineSize,
+ scanlineSize);
+ }
+
+ return newData;
+ }
+
+ /**
+ * Reads a grayscale image with mipmaps from the InputStream
+ * @param flip Flip the loaded image by Y axis
+ * @param totalSize Total size of the image in bytes including the mipmaps
+ * @return A ByteBuffer containing the grayscale image data with mips.
+ * @throws java.io.IOException If an error occured while reading from InputStream
+ */
+ public ByteBuffer readGrayscale2D(boolean flip, int totalSize) throws IOException {
+ ByteBuffer buffer = BufferUtils.createByteBuffer(totalSize);
+
+ if (bpp == 8) {
+ logger.finest("Source image format: R8");
+ }
+
+ assert bpp == pixelFormat.getBitsPerPixel();
+
+ int mipWidth = width;
+ int mipHeight = height;
+
+ for (int mip = 0; mip < mipMapCount; mip++) {
+ byte[] data = new byte[sizes[mip]];
+ in.readFully(data);
+ if (flip) {
+ data = flipData(data, mipWidth * bpp / 8, mipHeight);
+ }
+ buffer.put(data);
+
+ mipWidth = Math.max(mipWidth / 2, 1);
+ mipHeight = Math.max(mipHeight / 2, 1);
+ }
+
+ return buffer;
+ }
+
+ /**
+ * Reads an uncompressed RGB or RGBA image.
+ *
+ * @param flip Flip the image on the Y axis
+ * @param totalSize Size of the image in bytes including mipmaps
+ * @return ByteBuffer containing image data with mipmaps in the format specified by pixelFormat_
+ * @throws java.io.IOException If an error occured while reading from InputStream
+ */
+ public ByteBuffer readRGB2D(boolean flip, int totalSize) throws IOException {
+ int redCount = count(redMask),
+ blueCount = count(blueMask),
+ greenCount = count(greenMask),
+ alphaCount = count(alphaMask);
+
+ if (redMask == 0x00FF0000 && greenMask == 0x0000FF00 && blueMask == 0x000000FF) {
+ if (alphaMask == 0xFF000000 && bpp == 32) {
+ logger.finest("Data source format: BGRA8");
+ } else if (bpp == 24) {
+ logger.finest("Data source format: BGR8");
+ }
+ }
+
+ int sourcebytesPP = bpp / 8;
+ int targetBytesPP = pixelFormat.getBitsPerPixel() / 8;
+
+ ByteBuffer dataBuffer = BufferUtils.createByteBuffer(totalSize);
+
+ int mipWidth = width;
+ int mipHeight = height;
+
+ int offset = 0;
+ byte[] b = new byte[sourcebytesPP];
+ for (int mip = 0; mip < mipMapCount; mip++) {
+ for (int y = 0; y < mipHeight; y++) {
+ for (int x = 0; x < mipWidth; x++) {
+ in.readFully(b);
+
+ int i = byte2int(b);
+
+ byte red = (byte) (((i & redMask) >> redCount));
+ byte green = (byte) (((i & greenMask) >> greenCount));
+ byte blue = (byte) (((i & blueMask) >> blueCount));
+ byte alpha = (byte) (((i & alphaMask) >> alphaCount));
+
+ if (flip) {
+ dataBuffer.position(offset + ((mipHeight - y - 1) * mipWidth + x) * targetBytesPP);
+ }
+ //else
+ // dataBuffer.position(offset + (y * width + x) * targetBytesPP);
+
+ if (alphaMask == 0) {
+ dataBuffer.put(red).put(green).put(blue);
+ } else {
+ dataBuffer.put(red).put(green).put(blue).put(alpha);
+ }
+ }
+ }
+
+ offset += mipWidth * mipHeight * targetBytesPP;
+
+ mipWidth = Math.max(mipWidth / 2, 1);
+ mipHeight = Math.max(mipHeight / 2, 1);
+ }
+
+ return dataBuffer;
+ }
+
+ /**
+ * Reads a DXT compressed image from the InputStream
+ *
+ * @param totalSize Total size of the image in bytes, including mipmaps
+ * @return ByteBuffer containing compressed DXT image in the format specified by pixelFormat_
+ * @throws java.io.IOException If an error occured while reading from InputStream
+ */
+ public ByteBuffer readDXT2D(boolean flip, int totalSize) throws IOException {
+ logger.finest("Source image format: DXT");
+
+ ByteBuffer buffer = BufferUtils.createByteBuffer(totalSize);
+
+ int mipWidth = width;
+ int mipHeight = height;
+
+ int offset = 0;
+ for (int mip = 0; mip < mipMapCount; mip++) {
+ if (flip){
+ byte[] data = new byte[sizes[mip]];
+ in.readFully(data);
+ ByteBuffer wrapped = ByteBuffer.wrap(data);
+ wrapped.rewind();
+ ByteBuffer flipped = DXTFlipper.flipDXT(wrapped, mipWidth, mipHeight, pixelFormat);
+ buffer.put(flipped);
+ }else{
+ byte[] data = new byte[sizes[mip]];
+ in.readFully(data);
+ buffer.put(data);
+ }
+
+ offset += sizes[mip];
+
+ mipWidth = Math.max(mipWidth / 2, 1);
+ mipHeight = Math.max(mipHeight / 2, 1);
+ }
+ buffer.rewind();
+
+ return buffer;
+ }
+
+ /**
+ * Reads the image data from the InputStream in the required format.
+ * If the file contains a cubemap image, it is loaded as 6 ByteBuffers
+ * (potentially containing mipmaps if they were specified), otherwise
+ * a single ByteBuffer is returned for a 2D image.
+ *
+ * @param flip Flip the image data or not.
+ * For cubemaps, each of the cubemap faces is flipped individually.
+ * If the image is DXT compressed, no flipping is done.
+ * @return An ArrayList containing a single ByteBuffer for a 2D image, or 6 ByteBuffers for a cubemap.
+ * The cubemap ByteBuffer order is PositiveX, NegativeX, PositiveY, NegativeY, PositiveZ, NegativeZ.
+ *
+ * @throws java.io.IOException If an error occured while reading from the stream.
+ */
+ public ArrayList readData(boolean flip) throws IOException {
+ int totalSize = 0;
+
+ for (int i = 0; i < sizes.length; i++) {
+ totalSize += sizes[i];
+ }
+
+ ArrayList allMaps = new ArrayList();
+ if (depth > 1){
+ for (int i = 0; i < depth; i++){
+ if (compressed) {
+ allMaps.add(readDXT2D(flip,totalSize));
+ } else if (grayscaleOrAlpha) {
+ allMaps.add(readGrayscale2D(flip, totalSize));
+ } else {
+ allMaps.add(readRGB2D(flip, totalSize));
+ }
+ }
+ } else {
+ if (compressed) {
+ allMaps.add(readDXT2D(flip,totalSize));
+ } else if (grayscaleOrAlpha) {
+ allMaps.add(readGrayscale2D(flip, totalSize));
+ } else {
+ allMaps.add(readRGB2D(flip, totalSize));
+ }
+ }
+
+ return allMaps;
+ }
+
+ /**
+ * Checks if flags contains the specified mask
+ */
+ private static final boolean is(int flags, int mask) {
+ return (flags & mask) == mask;
+ }
+
+ /**
+ * Counts the amount of bits needed to shift till bitmask n is at zero
+ * @param n Bitmask to test
+ */
+ private static int count(int n) {
+ if (n == 0) {
+ return 0;
+ }
+
+ int i = 0;
+ while ((n & 0x1) == 0) {
+ n = n >> 1;
+ i++;
+ if (i > 32) {
+ throw new RuntimeException(Integer.toHexString(n));
+ }
+ }
+
+ return i;
+ }
+
+ /**
+ * Converts a 1 to 4 sized byte array to an integer
+ */
+ private static int byte2int(byte[] b) {
+ if (b.length == 1) {
+ return b[0] & 0xFF;
+ } else if (b.length == 2) {
+ return (b[0] & 0xFF) | ((b[1] & 0xFF) << 8);
+ } else if (b.length == 3) {
+ return (b[0] & 0xFF) | ((b[1] & 0xFF) << 8) | ((b[2] & 0xFF) << 16);
+ } else if (b.length == 4) {
+ return (b[0] & 0xFF) | ((b[1] & 0xFF) << 8) | ((b[2] & 0xFF) << 16) | ((b[3] & 0xFF) << 24);
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Converts a int representing a FourCC into a String
+ */
+ private static final String string(int value) {
+ StringBuffer buf = new StringBuffer();
+
+ buf.append((char) (value & 0xFF));
+ buf.append((char) ((value & 0xFF00) >> 8));
+ buf.append((char) ((value & 0xFF0000) >> 16));
+ buf.append((char) ((value & 0xFF00000) >> 24));
+
+ return buf.toString();
+ }
+}
diff --git a/engine/src/core-plugins/com/jme3/texture/plugins/DXTFlipper.java b/engine/src/core-plugins/com/jme3/texture/plugins/DXTFlipper.java
new file mode 100644
index 000000000..1d3506fbc
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/texture/plugins/DXTFlipper.java
@@ -0,0 +1,317 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.texture.plugins;
+
+import com.jme3.math.FastMath;
+import com.jme3.texture.Image.Format;
+import com.jme3.util.BufferUtils;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ *
+ * @author Kirill Vainer
+ */
+public class DXTFlipper {
+
+ private static final ByteBuffer bb = ByteBuffer.allocate(8);
+
+ static {
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+ }
+
+ private static long readCode5(long data, int x, int y){
+ long shift = (4 * y + x) * 3;
+ long mask = 0x7;
+ mask <<= shift;
+ long code = data & mask;
+ code >>= shift;
+ return code;
+ }
+
+ private static long writeCode5(long data, int x, int y, long code){
+ long shift = (4 * y + x) * 3;
+ long mask = 0x7;
+ code = (code & mask) << shift;
+ mask <<= shift;
+ mask = ~mask;
+ data &= mask;
+ data |= code; // write new code
+ return data;
+ }
+
+ public static void flipDXT5Block(byte[] block, int h){
+ if (h == 1)
+ return;
+
+ byte c0 = block[0];
+ byte c1 = block[1];
+
+ bb.clear();
+ bb.put(block, 2, 6).flip();
+ bb.clear();
+ long l = bb.getLong();
+ long n = l;
+
+ if (h == 2){
+ n = writeCode5(n, 0, 0, readCode5(l, 0, 1));
+ n = writeCode5(n, 1, 0, readCode5(l, 1, 1));
+ n = writeCode5(n, 2, 0, readCode5(l, 2, 1));
+ n = writeCode5(n, 3, 0, readCode5(l, 3, 1));
+
+ n = writeCode5(n, 0, 1, readCode5(l, 0, 0));
+ n = writeCode5(n, 1, 1, readCode5(l, 1, 0));
+ n = writeCode5(n, 2, 1, readCode5(l, 2, 0));
+ n = writeCode5(n, 3, 1, readCode5(l, 3, 0));
+ }else{
+ n = writeCode5(n, 0, 0, readCode5(l, 0, 3));
+ n = writeCode5(n, 1, 0, readCode5(l, 1, 3));
+ n = writeCode5(n, 2, 0, readCode5(l, 2, 3));
+ n = writeCode5(n, 3, 0, readCode5(l, 3, 3));
+
+ n = writeCode5(n, 0, 1, readCode5(l, 0, 2));
+ n = writeCode5(n, 1, 1, readCode5(l, 1, 2));
+ n = writeCode5(n, 2, 1, readCode5(l, 2, 2));
+ n = writeCode5(n, 3, 1, readCode5(l, 3, 2));
+
+ n = writeCode5(n, 0, 2, readCode5(l, 0, 1));
+ n = writeCode5(n, 1, 2, readCode5(l, 1, 1));
+ n = writeCode5(n, 2, 2, readCode5(l, 2, 1));
+ n = writeCode5(n, 3, 2, readCode5(l, 3, 1));
+
+ n = writeCode5(n, 0, 3, readCode5(l, 0, 0));
+ n = writeCode5(n, 1, 3, readCode5(l, 1, 0));
+ n = writeCode5(n, 2, 3, readCode5(l, 2, 0));
+ n = writeCode5(n, 3, 3, readCode5(l, 3, 0));
+ }
+
+ bb.clear();
+ bb.putLong(n);
+ bb.clear();
+ bb.get(block, 2, 6).flip();
+
+ assert c0 == block[0] && c1 == block[1];
+ }
+
+ public static void flipDXT3Block(byte[] block, int h){
+ if (h == 1)
+ return;
+
+ // first row
+ byte tmp0 = block[0];
+ byte tmp1 = block[1];
+
+ if (h == 2){
+ block[0] = block[2];
+ block[1] = block[3];
+
+ block[2] = tmp0;
+ block[3] = tmp1;
+ }else{
+ // write last row to first row
+ block[0] = block[6];
+ block[1] = block[7];
+
+ // write first row to last row
+ block[6] = tmp0;
+ block[7] = tmp1;
+
+ // 2nd row
+ tmp0 = block[2];
+ tmp1 = block[3];
+
+ // write 3rd row to 2nd
+ block[2] = block[4];
+ block[3] = block[5];
+
+ // write 2nd row to 3rd
+ block[4] = tmp0;
+ block[5] = tmp1;
+ }
+ }
+
+ /**
+ * Flips a DXT color block or a DXT3 alpha block
+ * @param block
+ * @param h
+ */
+ public static void flipDXT1Block(byte[] block, int h){
+ byte tmp;
+ switch (h){
+ case 1:
+ return;
+ case 2:
+ // keep header intact (the two colors)
+ // header takes 4 bytes
+
+ // flip only two top rows
+ tmp = block[4+1];
+ block[4+1] = block[4+0];
+ block[4+0] = tmp;
+ return;
+ default:
+ // keep header intact (the two colors)
+ // header takes 4 bytes
+
+ // flip first & fourth row
+ tmp = block[4+3];
+ block[4+3] = block[4+0];
+ block[4+0] = tmp;
+
+ // flip second and third row
+ tmp = block[4+2];
+ block[4+2] = block[4+1];
+ block[4+1] = tmp;
+ return;
+ }
+ }
+
+ public static ByteBuffer flipDXT(ByteBuffer img, int w, int h, Format format){
+ int blocksX = (int) FastMath.ceil((float)w / 4f);
+ int blocksY = (int) FastMath.ceil((float)h / 4f);
+
+ int type;
+ switch (format){
+ case DXT1:
+ case DXT1A:
+ type = 1;
+ break;
+ case DXT3:
+ type = 2;
+ break;
+ case DXT5:
+ type = 3;
+ break;
+ case LATC:
+ type = 4;
+ break;
+ case LTC:
+ type = 5;
+ break;
+ default:
+ throw new IllegalArgumentException();
+ }
+
+ // DXT1 uses 8 bytes per block,
+ // DXT3, DXT5, LATC use 16 bytes per block
+ int bpb = type == 1 || type == 5 ? 8 : 16;
+
+ ByteBuffer retImg = BufferUtils.createByteBuffer(blocksX * blocksY * bpb);
+
+ if (h == 1){
+ retImg.put(img);
+ retImg.rewind();
+ return retImg;
+ }else if (h == 2){
+ byte[] colorBlock = new byte[8];
+ byte[] alphaBlock = type != 1 && type != 5 ? new byte[8] : null;
+ for (int x = 0; x < blocksX; x++){
+ // prepeare for block reading
+ int blockByteOffset = x * bpb;
+ img.position(blockByteOffset);
+ img.limit(blockByteOffset + bpb);
+
+ img.get(colorBlock);
+ if (type == 4 || type == 5)
+ flipDXT5Block(colorBlock, h);
+ else
+ flipDXT1Block(colorBlock, h);
+
+ // write block (no need to flip block indexes, only pixels
+ // inside block
+ retImg.put(colorBlock);
+
+ if (alphaBlock != null){
+ img.get(alphaBlock);
+ switch (type){
+ case 2:
+ flipDXT3Block(alphaBlock, h); break;
+ case 3:
+ case 4:
+ flipDXT5Block(alphaBlock, h);
+ break;
+ }
+ retImg.put(alphaBlock);
+ }
+ }
+ retImg.rewind();
+ return retImg;
+ }else if (h >= 4){
+ byte[] colorBlock = new byte[8];
+ byte[] alphaBlock = type != 1 && type != 5 ? new byte[8] : null;
+ for (int y = 0; y < blocksY; y++){
+ for (int x = 0; x < blocksX; x++){
+ // prepeare for block reading
+ int blockIdx = y * blocksX + x;
+ int blockByteOffset = blockIdx * bpb;
+
+ img.position(blockByteOffset);
+ img.limit(blockByteOffset + bpb);
+
+ blockIdx = (blocksY - y - 1) * blocksX + x;
+ blockByteOffset = blockIdx * bpb;
+
+ retImg.position(blockByteOffset);
+ retImg.limit(blockByteOffset + bpb);
+
+ if (alphaBlock != null){
+ img.get(alphaBlock);
+ switch (type){
+ case 2:
+ flipDXT3Block(alphaBlock, h);
+ break;
+ case 3:
+ case 4:
+ flipDXT5Block(alphaBlock, h);
+ break;
+ }
+ retImg.put(alphaBlock);
+ }
+
+ img.get(colorBlock);
+ if (type == 4 || type == 5)
+ flipDXT5Block(colorBlock, h);
+ else
+ flipDXT1Block(colorBlock, h);
+
+ retImg.put(colorBlock);
+ }
+ }
+ retImg.limit(retImg.capacity());
+ retImg.position(0);
+ return retImg;
+ }else{
+ return null;
+ }
+ }
+
+}
diff --git a/engine/src/core-plugins/com/jme3/texture/plugins/HDRLoader.java b/engine/src/core-plugins/com/jme3/texture/plugins/HDRLoader.java
new file mode 100644
index 000000000..bca3ff2b1
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/texture/plugins/HDRLoader.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.texture.plugins;
+
+import com.jme3.asset.*;
+import com.jme3.util.*;
+import com.jme3.math.FastMath;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.logging.Logger;
+
+public class HDRLoader implements AssetLoader {
+
+ private static final Logger logger = Logger.getLogger(HDRLoader.class.getName());
+
+ private boolean writeRGBE = false;
+ private ByteBuffer rleTempBuffer;
+ private ByteBuffer dataStore;
+ private final float[] tempF = new float[3];
+
+ public HDRLoader(boolean writeRGBE){
+ this.writeRGBE = writeRGBE;
+ }
+
+ public HDRLoader(){
+ }
+
+ public static final void convertFloatToRGBE(byte[] rgbe, float red, float green, float blue){
+ double max = red;
+ if (green > max) max = green;
+ if (blue > max) max = blue;
+ if (max < 1.0e-32){
+ rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
+ }else{
+ double exp = Math.ceil( Math.log10(max) / Math.log10(2) );
+ double divider = Math.pow(2.0, exp);
+ rgbe[0] = (byte) ((red / divider) * 255.0);
+ rgbe[1] = (byte) ((green / divider) * 255.0);
+ rgbe[2] = (byte) ((blue / divider) * 255.0);
+ rgbe[3] = (byte) (exp + 128.0);
+ }
+ }
+
+ public static final void convertRGBEtoFloat(byte[] rgbe, float[] rgbf){
+ int R = rgbe[0] & 0xFF,
+ G = rgbe[1] & 0xFF,
+ B = rgbe[2] & 0xFF,
+ E = rgbe[3] & 0xFF;
+
+ float e = (float) Math.pow(2f, E - (128 + 8) );
+ rgbf[0] = R * e;
+ rgbf[1] = G * e;
+ rgbf[2] = B * e;
+ }
+
+ public static final void convertRGBEtoFloat2(byte[] rgbe, float[] rgbf){
+ int R = rgbe[0] & 0xFF,
+ G = rgbe[1] & 0xFF,
+ B = rgbe[2] & 0xFF,
+ E = rgbe[3] & 0xFF;
+
+ float e = (float) Math.pow(2f, E - 128);
+ rgbf[0] = (R / 256.0f) * e;
+ rgbf[1] = (G / 256.0f) * e;
+ rgbf[2] = (B / 256.0f) * e;
+ }
+
+ public static final void convertRGBEtoFloat3(byte[] rgbe, float[] rgbf){
+ int R = rgbe[0] & 0xFF,
+ G = rgbe[1] & 0xFF,
+ B = rgbe[2] & 0xFF,
+ E = rgbe[3] & 0xFF;
+
+ float e = (float) Math.pow(2f, E - (128 + 8) );
+ rgbf[0] = R * e;
+ rgbf[1] = G * e;
+ rgbf[2] = B * e;
+ }
+
+ private short flip(int in){
+ return (short) ((in << 8 & 0xFF00) | (in >> 8));
+ }
+
+ private void writeRGBE(byte[] rgbe){
+ if (writeRGBE){
+ dataStore.put(rgbe);
+ }else{
+ convertRGBEtoFloat(rgbe, tempF);
+ dataStore.putShort(FastMath.convertFloatToHalf(tempF[0]))
+ .putShort(FastMath.convertFloatToHalf(tempF[1])).
+ putShort(FastMath.convertFloatToHalf(tempF[2]));
+ }
+ }
+
+ private String readString(InputStream is) throws IOException{
+ StringBuffer sb = new StringBuffer();
+ while (true){
+ int i = is.read();
+ if (i == 0x0a || i == -1) // new line or EOF
+ return sb.toString();
+
+ sb.append((char)i);
+ }
+ }
+
+ private boolean decodeScanlineRLE(InputStream in, int width) throws IOException{
+ // must deocde RLE data into temp buffer before converting to float
+ if (rleTempBuffer == null){
+ rleTempBuffer = BufferUtils.createByteBuffer(width * 4);
+ }else{
+ rleTempBuffer.clear();
+ if (rleTempBuffer.remaining() < width * 4)
+ rleTempBuffer = BufferUtils.createByteBuffer(width * 4);
+ }
+
+ // read each component seperately
+ for (int i = 0; i < 4; i++) {
+ // read WIDTH bytes for the channel
+ for (int j = 0; j < width;) {
+ int code = in.read();
+ if (code > 128) { // run
+ code -= 128;
+ int val = in.read();
+ while ((code--) != 0) {
+ rleTempBuffer.put( (j++) * 4 + i , (byte)val);
+ //scanline[j++][i] = val;
+ }
+ } else { // non-run
+ while ((code--) != 0) {
+ int val = in.read();
+ rleTempBuffer.put( (j++) * 4 + i, (byte)val);
+ //scanline[j++][i] = in.read();
+ }
+ }
+ }
+ }
+
+ rleTempBuffer.rewind();
+ byte[] rgbe = new byte[4];
+// float[] temp = new float[3];
+
+ // decode temp buffer into float data
+ for (int i = 0; i < width; i++){
+ rleTempBuffer.get(rgbe);
+ writeRGBE(rgbe);
+ }
+
+ return true;
+ }
+
+ private boolean decodeScanlineUncompressed(InputStream in, int width) throws IOException{
+ byte[] rgbe = new byte[4];
+
+ for (int i = 0; i < width; i+=3){
+ if (in.read(rgbe) < 1)
+ return false;
+
+ writeRGBE(rgbe);
+ }
+ return true;
+ }
+
+ private void decodeScanline(InputStream in, int width) throws IOException{
+ if (width < 8 || width > 0x7fff){
+ // too short/long for RLE compression
+ decodeScanlineUncompressed(in, width);
+ }
+
+ // check format
+ byte[] data = new byte[4];
+ in.read(data);
+ if (data[0] != 0x02 || data[1] != 0x02 || (data[2] & 0x80) != 0){
+ // not RLE data
+ decodeScanlineUncompressed(in, width-1);
+ }else{
+ // check scanline width
+ int readWidth = (data[2] & 0xFF) << 8 | (data[3] & 0xFF);
+ if (readWidth != width)
+ throw new IOException("Illegal scanline width in HDR file: "+width+" != "+readWidth);
+
+ // RLE data
+ decodeScanlineRLE(in, width);
+ }
+ }
+
+ public Image load(InputStream in, boolean flipY) throws IOException{
+ float gamma = -1f;
+ float exposure = -1f;
+ float[] colorcorr = new float[]{ -1f, -1f, -1f };
+
+ int width = -1, height = -1;
+ boolean verifiedFormat = false;
+
+ while (true){
+ String ln = readString(in);
+ ln = ln.trim();
+ if (ln.startsWith("#") || ln.equals("")){
+ if (ln.equals("#?RADIANCE") || ln.equals("#?RGBE"))
+ verifiedFormat = true;
+
+ continue; // comment or empty statement
+ } else if (ln.startsWith("+") || ln.startsWith("-")){
+ // + or - mark image resolution and start of data
+ String[] resData = ln.split(" ");
+ if (resData.length != 4){
+ throw new IOException("Invalid resolution string in HDR file");
+ }
+
+ if (!resData[0].equals("-Y") || !resData[2].equals("+X")){
+ logger.warning("Flipping/Rotating attributes ignored!");
+ }
+
+ //if (resData[0].endsWith("X")){
+ // first width then height
+ // width = Integer.parseInt(resData[1]);
+ // height = Integer.parseInt(resData[3]);
+ //}else{
+ width = Integer.parseInt(resData[3]);
+ height = Integer.parseInt(resData[1]);
+ //}
+
+ break;
+ } else {
+ // regular command
+ int index = ln.indexOf("=");
+ if (index < 1){
+ logger.fine("Ignored string: "+ln);
+ continue;
+ }
+
+ String var = ln.substring(0, index).trim().toLowerCase();
+ String value = ln.substring(index+1).trim().toLowerCase();
+ if (var.equals("format")){
+ if (!value.equals("32-bit_rle_rgbe") && !value.equals("32-bit_rle_xyze")){
+ throw new IOException("Unsupported format in HDR picture");
+ }
+ }else if (var.equals("exposure")){
+ exposure = Float.parseFloat(value);
+ }else if (var.equals("gamma")){
+ gamma = Float.parseFloat(value);
+ }else{
+ logger.warning("HDR Command ignored: "+ln);
+ }
+ }
+ }
+
+ assert width != -1 && height != -1;
+
+ if (!verifiedFormat)
+ logger.warning("Unsure if specified image is Radiance HDR");
+
+ // some HDR images can get pretty big
+ System.gc();
+
+ // each pixel times size of component times # of components
+ Format pixelFormat;
+ if (writeRGBE){
+ pixelFormat = Format.RGBA8;
+ }else{
+ pixelFormat = Format.RGB16F;
+ }
+
+ dataStore = BufferUtils.createByteBuffer(width * height * pixelFormat.getBitsPerPixel());
+
+ int bytesPerPixel = pixelFormat.getBitsPerPixel() / 8;
+ int scanLineBytes = bytesPerPixel * width;
+ for (int y = height - 1; y >= 0; y--) {
+ if (flipY)
+ dataStore.position(scanLineBytes * y);
+
+ decodeScanline(in, width);
+ }
+ in.close();
+
+ dataStore.rewind();
+ return new Image(pixelFormat, width, height, dataStore);
+ }
+
+ public Object load(AssetInfo info) throws IOException {
+ if (!(info.getKey() instanceof TextureKey))
+ throw new IllegalArgumentException("Texture assets must be loaded using a TextureKey");
+
+ boolean flip = ((TextureKey) info.getKey()).isFlipY();
+ InputStream in = info.openStream();
+ Image img = load(in, flip);
+ in.close();
+ return img;
+ }
+
+}
diff --git a/engine/src/core-plugins/com/jme3/texture/plugins/PFMLoader.java b/engine/src/core-plugins/com/jme3/texture/plugins/PFMLoader.java
new file mode 100644
index 000000000..a8af2f322
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/texture/plugins/PFMLoader.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.texture.plugins;
+
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetLoader;
+import com.jme3.asset.AssetManager;
+import com.jme3.asset.TextureKey;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.util.BufferUtils;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.logging.Logger;
+
+public class PFMLoader implements AssetLoader {
+
+ private static final Logger logger = Logger.getLogger(PFMLoader.class.getName());
+
+ private String readString(InputStream is) throws IOException{
+ StringBuffer sb = new StringBuffer();
+ while (true){
+ int i = is.read();
+ if (i == 0x0a || i == -1) // new line or EOF
+ return sb.toString();
+
+ sb.append((char)i);
+ }
+ }
+
+ private void flipScanline(byte[] scanline){
+ for (int i = 0; i < scanline.length; i+=4){
+ // flip first and fourth bytes
+ byte tmp = scanline[i+3];
+ scanline[i+3] = scanline[i+0];
+ scanline[i+0] = tmp;
+
+ // flip second and third bytes
+ tmp = scanline[i+2];
+ scanline[i+2] = scanline[i+1];
+ scanline[i+1] = tmp;
+ }
+ }
+
+ public Object load(AssetInfo info) throws IOException {
+ if (!(info.getKey() instanceof TextureKey))
+ throw new IllegalArgumentException("Texture assets must be loaded using a TextureKey");
+
+ InputStream in = info.openStream();
+ Format format = null;
+
+ String fmtStr = readString(in);
+ if (fmtStr.equals("PF")){
+ format = Format.RGB32F;
+ }else if (fmtStr.equals("Pf")){
+ format = Format.Luminance32F;
+ }else{
+ throw new IOException("File is not PFM format");
+ }
+
+ String sizeStr = readString(in);
+ int spaceIdx = sizeStr.indexOf(" ");
+ if (spaceIdx <= 0 || spaceIdx >= sizeStr.length() - 1)
+ throw new IOException("Invalid size syntax in PFM file");
+
+ int width = Integer.parseInt(sizeStr.substring(0,spaceIdx));
+ int height = Integer.parseInt(sizeStr.substring(spaceIdx+1));
+
+ if (width <= 0 || height <= 0)
+ throw new IOException("Invalid size specified in PFM file");
+
+ String scaleStr = readString(in);
+ float scale = Float.parseFloat(scaleStr);
+ ByteOrder order = scale < 0 ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN;
+ boolean needEndienFlip = order != ByteOrder.nativeOrder();
+ boolean needYFlip = ((TextureKey)info.getKey()).isFlipY();
+
+ // make sure all unneccessary stuff gets deleted from heap
+ // before allocating large amount of memory
+ System.gc();
+
+ int bytesPerPixel = format.getBitsPerPixel() / 8;
+ int scanLineBytes = bytesPerPixel * width;
+
+ ByteBuffer imageData = BufferUtils.createByteBuffer(width * height * bytesPerPixel);
+ byte[] scanline = new byte[width * bytesPerPixel];
+
+ for (int y = height - 1; y >= 0; y--) {
+ if (!needYFlip)
+ imageData.position(scanLineBytes * y);
+
+ int read = 0;
+ int off = 0;
+ do {
+ read = in.read(scanline, off, scanline.length - off);
+ off += read;
+ } while (read > 0);
+
+ if (needEndienFlip){
+ flipScanline(scanline);
+ }
+
+ imageData.put(scanline);
+ }
+ imageData.rewind();
+
+ return new Image(format, width, height, imageData);
+ }
+
+}
diff --git a/engine/src/core-plugins/com/jme3/texture/plugins/TGALoader.java b/engine/src/core-plugins/com/jme3/texture/plugins/TGALoader.java
new file mode 100644
index 000000000..f0f5910d2
--- /dev/null
+++ b/engine/src/core-plugins/com/jme3/texture/plugins/TGALoader.java
@@ -0,0 +1,509 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.texture.plugins;
+
+import com.jme3.math.FastMath;
+import com.jme3.asset.AssetLoader;
+import com.jme3.texture.Image;
+import com.jme3.util.BufferUtils;
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.TextureKey;
+import com.jme3.texture.Image.Format;
+import java.io.BufferedInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+
+/**
+ * TextureManager
provides static methods for building a
+ * Texture
object. Typically, the information supplied is the
+ * filename and the texture properties.
+ *
+ * @author Mark Powell
+ * @author Joshua Slack - cleaned, commented, added ability to read 16bit true color and color-mapped TGAs.
+ * @author Kirill Vainer - ported to jME3
+ * @version $Id: TGALoader.java 4131 2009-03-19 20:15:28Z blaine.dev $
+ */
+public final class TGALoader implements AssetLoader {
+
+ // 0 - no image data in file
+ public static final int TYPE_NO_IMAGE = 0;
+
+ // 1 - uncompressed, color-mapped image
+ public static final int TYPE_COLORMAPPED = 1;
+
+ // 2 - uncompressed, true-color image
+ public static final int TYPE_TRUECOLOR = 2;
+
+ // 3 - uncompressed, black and white image
+ public static final int TYPE_BLACKANDWHITE = 3;
+
+ // 9 - run-length encoded, color-mapped image
+ public static final int TYPE_COLORMAPPED_RLE = 9;
+
+ // 10 - run-length encoded, true-color image
+ public static final int TYPE_TRUECOLOR_RLE = 10;
+
+ // 11 - run-length encoded, black and white image
+ public static final int TYPE_BLACKANDWHITE_RLE = 11;
+
+ public Object load(AssetInfo info) throws IOException{
+ if (!(info.getKey() instanceof TextureKey))
+ throw new IllegalArgumentException("Texture assets must be loaded using a TextureKey");
+
+ boolean flip = ((TextureKey)info.getKey()).isFlipY();
+ InputStream in = info.openStream();
+ Image img = load(in, flip);
+// in.close();
+ return img;
+ }
+
+ /**
+ * loadImage
is a manual image loader which is entirely
+ * independent of AWT. OUT: RGB888 or RGBA8888 Image object
+ *
+ * @return Image
object that contains the
+ * image, either as a RGB888 or RGBA8888
+ * @param flip
+ * Flip the image vertically
+ * @param exp32
+ * Add a dummy Alpha channel to 24b RGB image.
+ * @param fis
+ * InputStream of an uncompressed 24b RGB or 32b RGBA TGA
+ * @throws java.io.IOException
+ */
+ public static Image load(InputStream in, boolean flip) throws IOException {
+ boolean flipH = false;
+
+ // open a stream to the file
+ DataInputStream dis = new DataInputStream(new BufferedInputStream(in));
+
+ // ---------- Start Reading the TGA header ---------- //
+ // length of the image id (1 byte)
+ int idLength = dis.readUnsignedByte();
+
+ // Type of color map (if any) included with the image
+ // 0 - no color map data is included
+ // 1 - a color map is included
+ int colorMapType = dis.readUnsignedByte();
+
+ // Type of image being read:
+ int imageType = dis.readUnsignedByte();
+
+ // Read Color Map Specification (5 bytes)
+ // Index of first color map entry (if we want to use it, uncomment and remove extra read.)
+// short cMapStart = flipEndian(dis.readShort());
+ dis.readShort();
+ // number of entries in the color map
+ short cMapLength = flipEndian(dis.readShort());
+ // number of bits per color map entry
+ int cMapDepth = dis.readUnsignedByte();
+
+ // Read Image Specification (10 bytes)
+ // horizontal coordinate of lower left corner of image. (if we want to use it, uncomment and remove extra read.)
+// int xOffset = flipEndian(dis.readShort());
+ dis.readShort();
+ // vertical coordinate of lower left corner of image. (if we want to use it, uncomment and remove extra read.)
+// int yOffset = flipEndian(dis.readShort());
+ dis.readShort();
+ // width of image - in pixels
+ int width = flipEndian(dis.readShort());
+ // height of image - in pixels
+ int height = flipEndian(dis.readShort());
+ // bits per pixel in image.
+ int pixelDepth = dis.readUnsignedByte();
+ int imageDescriptor = dis.readUnsignedByte();
+ if ((imageDescriptor & 32) != 0) // bit 5 : if 1, flip top/bottom ordering
+ flip = !flip;
+ if ((imageDescriptor & 16) != 0) // bit 4 : if 1, flip left/right ordering
+ flipH = !flipH;
+
+ // ---------- Done Reading the TGA header ---------- //
+
+ // Skip image ID
+ if (idLength > 0)
+ in.skip(idLength);
+
+ ColorMapEntry[] cMapEntries = null;
+ if (colorMapType != 0) {
+ // read the color map.
+ int bytesInColorMap = (cMapDepth * cMapLength) >> 3;
+ int bitsPerColor = Math.min(cMapDepth/3 , 8);
+
+ byte[] cMapData = new byte[bytesInColorMap];
+ in.read(cMapData);
+
+ // Only go to the trouble of constructing the color map
+ // table if this is declared a color mapped image.
+ if (imageType == TYPE_COLORMAPPED || imageType == TYPE_COLORMAPPED_RLE) {
+ cMapEntries = new ColorMapEntry[cMapLength];
+ int alphaSize = cMapDepth - (3*bitsPerColor);
+ float scalar = 255f / (FastMath.pow(2, bitsPerColor) - 1);
+ float alphaScalar = 255f / (FastMath.pow(2, alphaSize) - 1);
+ for (int i = 0; i < cMapLength; i++) {
+ ColorMapEntry entry = new ColorMapEntry();
+ int offset = cMapDepth * i;
+ entry.red = (byte)(int)(getBitsAsByte(cMapData, offset, bitsPerColor) * scalar);
+ entry.green = (byte)(int)(getBitsAsByte(cMapData, offset+bitsPerColor, bitsPerColor) * scalar);
+ entry.blue = (byte)(int)(getBitsAsByte(cMapData, offset+(2*bitsPerColor), bitsPerColor) * scalar);
+ if (alphaSize <= 0)
+ entry.alpha = (byte)255;
+ else
+ entry.alpha = (byte)(int)(getBitsAsByte(cMapData, offset+(3*bitsPerColor), alphaSize) * alphaScalar);
+
+ cMapEntries[i] = entry;
+ }
+ }
+ }
+
+
+ // Allocate image data array
+ Format format = null;
+ byte[] rawData = null;
+ int dl;
+ if (pixelDepth == 32) {
+ rawData = new byte[width * height * 4];
+ dl = 4;
+ } else {
+ rawData = new byte[width * height * 3];
+ dl = 3;
+ }
+ int rawDataIndex = 0;
+
+ if (imageType == TYPE_TRUECOLOR) {
+ byte red = 0;
+ byte green = 0;
+ byte blue = 0;
+ byte alpha = 0;
+
+ // Faster than doing a 16-or-24-or-32 check on each individual pixel,
+ // just make a seperate loop for each.
+ if (pixelDepth == 16) {
+ byte[] data = new byte[2];
+ float scalar = 255f/31f;
+ for (int i = 0; i <= (height - 1); i++) {
+ if (!flip)
+ rawDataIndex = (height - 1 - i) * width * dl;
+ for (int j = 0; j < width; j++) {
+ data[1] = dis.readByte();
+ data[0] = dis.readByte();
+ rawData[rawDataIndex++] = (byte)(int)(getBitsAsByte(data, 1, 5) * scalar);
+ rawData[rawDataIndex++] = (byte)(int)(getBitsAsByte(data, 6, 5) * scalar);
+ rawData[rawDataIndex++] = (byte)(int)(getBitsAsByte(data, 11, 5) * scalar);
+ if (dl == 4) {
+ // create an alpha channel
+ alpha = getBitsAsByte(data, 0, 1);
+ if (alpha == 1) alpha = (byte)255;
+ rawData[rawDataIndex++] = alpha;
+ }
+ }
+ }
+
+ format = dl == 4 ? Format.RGBA8 : Format.RGB8;
+ } else if (pixelDepth == 24){
+ for (int y = 0; y < height; y++) {
+ if (!flip)
+ rawDataIndex = (height - 1 - y) * width * dl;
+ else
+ rawDataIndex = y * width * dl;
+
+ dis.readFully(rawData, rawDataIndex, width * dl);
+// for (int x = 0; x < width; x++) {
+ //read scanline
+// blue = dis.readByte();
+// green = dis.readByte();
+// red = dis.readByte();
+// rawData[rawDataIndex++] = red;
+// rawData[rawDataIndex++] = green;
+// rawData[rawDataIndex++] = blue;
+// }
+ }
+ format = Format.BGR8;
+ } else if (pixelDepth == 32){
+ for (int i = 0; i <= (height - 1); i++) {
+ if (!flip)
+ rawDataIndex = (height - 1 - i) * width * dl;
+
+ for (int j = 0; j < width; j++) {
+ blue = dis.readByte();
+ green = dis.readByte();
+ red = dis.readByte();
+ alpha = dis.readByte();
+ rawData[rawDataIndex++] = red;
+ rawData[rawDataIndex++] = green;
+ rawData[rawDataIndex++] = blue;
+ rawData[rawDataIndex++] = alpha;
+ }
+ }
+ format = Format.RGBA8;
+ }else{
+ throw new IOException("Unsupported TGA true color depth: "+pixelDepth);
+ }
+ } else if( imageType == TYPE_TRUECOLOR_RLE ) {
+ byte red = 0;
+ byte green = 0;
+ byte blue = 0;
+ byte alpha = 0;
+ // Faster than doing a 16-or-24-or-32 check on each individual pixel,
+ // just make a seperate loop for each.
+ if( pixelDepth == 32 ){
+ for( int i = 0; i <= ( height - 1 ); ++i ){
+ if( !flip ){
+ rawDataIndex = ( height - 1 - i ) * width * dl;
+ }
+
+ for( int j = 0; j < width; ++j ){
+ // Get the number of pixels the next chunk covers (either packed or unpacked)
+ int count = dis.readByte();
+ if( ( count & 0x80 ) != 0 ){
+ // Its an RLE packed block - use the following 1 pixel for the next pixels
+ count &= 0x07f;
+ j += count;
+ blue = dis.readByte();
+ green = dis.readByte();
+ red = dis.readByte();
+ alpha = dis.readByte();
+ while( count-- >= 0 ){
+ rawData[rawDataIndex++] = red;
+ rawData[rawDataIndex++] = green;
+ rawData[rawDataIndex++] = blue;
+ rawData[rawDataIndex++] = alpha;
+ }
+ } else{
+ // Its not RLE packed, but the next pixels are raw.
+ j += count;
+ while( count-- >= 0 ){
+ blue = dis.readByte();
+ green = dis.readByte();
+ red = dis.readByte();
+ alpha = dis.readByte();
+ rawData[rawDataIndex++] = red;
+ rawData[rawDataIndex++] = green;
+ rawData[rawDataIndex++] = blue;
+ rawData[rawDataIndex++] = alpha;
+ }
+ }
+ }
+ }
+ format = Format.RGBA8;
+ } else if( pixelDepth == 24 ){
+ for( int i = 0; i <= ( height - 1 ); i++ ){
+ if( !flip ){
+ rawDataIndex = ( height - 1 - i ) * width * dl;
+ }
+ for( int j = 0; j < width; ++j ){
+ // Get the number of pixels the next chunk covers (either packed or unpacked)
+ int count = dis.readByte();
+ if( ( count & 0x80 ) != 0 ){
+ // Its an RLE packed block - use the following 1 pixel for the next pixels
+ count &= 0x07f;
+ j += count;
+ blue = dis.readByte();
+ green = dis.readByte();
+ red = dis.readByte();
+ while( count-- >= 0 ){
+ rawData[rawDataIndex++] = red;
+ rawData[rawDataIndex++] = green;
+ rawData[rawDataIndex++] = blue;
+ }
+ } else{
+ // Its not RLE packed, but the next pixels are raw.
+ j += count;
+ while( count-- >= 0 ){
+ blue = dis.readByte();
+ green = dis.readByte();
+ red = dis.readByte();
+ rawData[rawDataIndex++] = red;
+ rawData[rawDataIndex++] = green;
+ rawData[rawDataIndex++] = blue;
+ }
+ }
+ }
+ }
+ format = Format.RGB8;
+ } else if( pixelDepth == 16 ){
+ byte[] data = new byte[ 2 ];
+ float scalar = 255f / 31f;
+ for( int i = 0; i <= ( height - 1 ); i++ ){
+ if( !flip ){
+ rawDataIndex = ( height - 1 - i ) * width * dl;
+ }
+ for( int j = 0; j < width; j++ ){
+ // Get the number of pixels the next chunk covers (either packed or unpacked)
+ int count = dis.readByte();
+ if( ( count & 0x80 ) != 0 ){
+ // Its an RLE packed block - use the following 1 pixel for the next pixels
+ count &= 0x07f;
+ j += count;
+ data[1] = dis.readByte();
+ data[0] = dis.readByte();
+ blue = (byte) (int) ( getBitsAsByte( data, 1, 5 ) * scalar );
+ green = (byte) (int) ( getBitsAsByte( data, 6, 5 ) * scalar );
+ red = (byte) (int) ( getBitsAsByte( data, 11, 5 ) * scalar );
+ while( count-- >= 0 ){
+ rawData[rawDataIndex++] = red;
+ rawData[rawDataIndex++] = green;
+ rawData[rawDataIndex++] = blue;
+ }
+ } else{
+ // Its not RLE packed, but the next pixels are raw.
+ j += count;
+ while( count-- >= 0 ){
+ data[1] = dis.readByte();
+ data[0] = dis.readByte();
+ blue = (byte) (int) ( getBitsAsByte( data, 1, 5 ) * scalar );
+ green = (byte) (int) ( getBitsAsByte( data, 6, 5 ) * scalar );
+ red = (byte) (int) ( getBitsAsByte( data, 11, 5 ) * scalar );
+ rawData[rawDataIndex++] = red;
+ rawData[rawDataIndex++] = green;
+ rawData[rawDataIndex++] = blue;
+ }
+ }
+ }
+ }
+ format = Format.RGB8;
+ } else{
+ throw new IOException( "Unsupported TGA true color depth: " + pixelDepth );
+ }
+
+ } else if( imageType == TYPE_COLORMAPPED ){
+ int bytesPerIndex = pixelDepth / 8;
+
+ if (bytesPerIndex == 1) {
+ for (int i = 0; i <= (height - 1); i++) {
+ if (!flip)
+ rawDataIndex = (height - 1 - i) * width * dl;
+ for (int j = 0; j < width; j++) {
+ int index = dis.readUnsignedByte();
+ if (index >= cMapEntries.length || index < 0)
+ throw new IOException("TGA: Invalid color map entry referenced: "+index);
+
+ ColorMapEntry entry = cMapEntries[index];
+ rawData[rawDataIndex++] = entry.red;
+ rawData[rawDataIndex++] = entry.green;
+ rawData[rawDataIndex++] = entry.blue;
+ if (dl == 4) {
+ rawData[rawDataIndex++] = entry.alpha;
+ }
+
+ }
+ }
+ } else if (bytesPerIndex == 2) {
+ for (int i = 0; i <= (height - 1); i++) {
+ if (!flip)
+ rawDataIndex = (height - 1 - i) * width * dl;
+ for (int j = 0; j < width; j++) {
+ int index = flipEndian(dis.readShort());
+ if (index >= cMapEntries.length || index < 0)
+ throw new IOException("TGA: Invalid color map entry referenced: "+index);
+
+ ColorMapEntry entry = cMapEntries[index];
+ rawData[rawDataIndex++] = entry.red;
+ rawData[rawDataIndex++] = entry.green;
+ rawData[rawDataIndex++] = entry.blue;
+ if (dl == 4) {
+ rawData[rawDataIndex++] = entry.alpha;
+ }
+ }
+ }
+ } else {
+ throw new IOException("TGA: unknown colormap indexing size used: "+bytesPerIndex);
+ }
+
+ format = dl == 4 ? Format.RGBA8 : Format.RGB8;
+ }
+
+
+ in.close();
+ // Get a pointer to the image memory
+ ByteBuffer scratch = BufferUtils.createByteBuffer(rawData.length);
+ scratch.clear();
+ scratch.put(rawData);
+ scratch.rewind();
+ // Create the Image object
+ Image textureImage = new Image();
+ textureImage.setFormat(format);
+ textureImage.setWidth(width);
+ textureImage.setHeight(height);
+ textureImage.setData(scratch);
+ return textureImage;
+ }
+
+ private static byte getBitsAsByte(byte[] data, int offset, int length) {
+ int offsetBytes = offset / 8;
+ int indexBits = offset % 8;
+ int rVal = 0;
+
+ // start at data[offsetBytes]... spill into next byte as needed.
+ for (int i = length; --i >=0;) {
+ byte b = data[offsetBytes];
+ int test = indexBits == 7 ? 1 : 2 << (6-indexBits);
+ if ((b & test) != 0) {
+ if (i == 0)
+ rVal++;
+ else
+ rVal += (2 << i-1);
+ }
+ indexBits++;
+ if (indexBits == 8) {
+ indexBits = 0;
+ offsetBytes++;
+ }
+ }
+
+ return (byte)rVal;
+ }
+
+ /**
+ * flipEndian
is used to flip the endian bit of the header
+ * file.
+ *
+ * @param signedShort
+ * the bit to flip.
+ * @return the flipped bit.
+ */
+ private static short flipEndian(short signedShort) {
+ int input = signedShort & 0xFFFF;
+ return (short) (input << 8 | (input & 0xFF00) >>> 8);
+ }
+
+ static class ColorMapEntry {
+ byte red, green, blue, alpha;
+
+ @Override
+ public String toString() {
+ return "entry: "+red+","+green+","+blue+","+alpha;
+ }
+ }
+}
diff --git a/engine/src/core/checkers/quals/DefaultLocation.java b/engine/src/core/checkers/quals/DefaultLocation.java
new file mode 100644
index 000000000..77d8ebe1d
--- /dev/null
+++ b/engine/src/core/checkers/quals/DefaultLocation.java
@@ -0,0 +1,25 @@
+package checkers.quals;
+
+/**
+ * Specifies the locations to which a {@link DefaultQualifier} annotation applies.
+ *
+ * @see DefaultQualifier
+ */
+public enum DefaultLocation {
+
+ /** Apply default annotations to all unannotated types. */
+ ALL,
+
+ /** Apply default annotations to all unannotated types except the raw types
+ * of locals. */
+ ALL_EXCEPT_LOCALS,
+
+ /** Apply default annotations to unannotated upper bounds: both
+ * explicit ones in extends clauses, and implicit upper bounds
+ * when no explicit extends or super clause is
+ * present. */
+ // Especially useful for parametrized classes that provide a lot of
+ // static methods with the same generic parameters as the class.
+ UPPER_BOUNDS;
+
+}
diff --git a/engine/src/core/checkers/quals/DefaultQualifier.java b/engine/src/core/checkers/quals/DefaultQualifier.java
new file mode 100644
index 000000000..c6fa2c25e
--- /dev/null
+++ b/engine/src/core/checkers/quals/DefaultQualifier.java
@@ -0,0 +1,42 @@
+package checkers.quals;
+
+import java.lang.annotation.*;
+import static java.lang.annotation.ElementType.*;
+import java.lang.annotation.Target;
+
+/**
+ * Applied to a declaration of a package, type, method, variable, etc.,
+ * specifies that the given annotation should be the default. The default is
+ * applied to all types within the declaration for which no other
+ * annotation is explicitly written.
+ * If multiple DefaultQualifier annotations are in scope, the innermost one
+ * takes precedence.
+ * DefaultQualifier takes precedence over {@link DefaultQualifierInHierarchy}.
+ *
+ *
+ * If you wish to write multiple @DefaultQualifier annotations (for
+ * unrelated type systems, or with different {@code locations} fields) at
+ * the same location, use {@link DefaultQualifiers}.
+ *
+ * @see DefaultLocation
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({CONSTRUCTOR, METHOD, FIELD, LOCAL_VARIABLE, PARAMETER, TYPE})
+public @interface DefaultQualifier {
+
+ /**
+ * The name of the default annotation. It may be a short name like
+ * "NonNull", if an appropriate import statement exists. Otherwise, it
+ * should be fully-qualified, like "checkers.nullness.quals.NonNull".
+ *
+ *
+ * To prevent affecting other type systems, always specify an annotation
+ * in your own type hierarchy. (For example, do not set
+ * "checkers.quals.Unqualified" as the default.)
+ */
+ String value();
+
+ /** @return the locations to which the annotation should be applied */
+ DefaultLocation[] locations() default {DefaultLocation.ALL};
+}
diff --git a/engine/src/core/checkers/quals/DefaultQualifierInHierarchy.java b/engine/src/core/checkers/quals/DefaultQualifierInHierarchy.java
new file mode 100644
index 000000000..619a751d8
--- /dev/null
+++ b/engine/src/core/checkers/quals/DefaultQualifierInHierarchy.java
@@ -0,0 +1,27 @@
+package checkers.quals;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+
+import java.lang.annotation.*;
+
+/**
+ * Indicates that the annotated qualifier is the default qualifier in the
+ * qualifier hierarchy: it applies if the programmer writes no explicit
+ * qualifier.
+ *
+ *
+ * The {@link DefaultQualifier} annotation, which targets Java code elements,
+ * takes precedence over {@code DefaultQualifierInHierarchy}.
+ *
+ *
+ * Each type qualifier hierarchy may have at most one qualifier marked as
+ * {@code DefaultQualifierInHierarchy}.
+ *
+ * @see checkers.quals.DefaultQualifier
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ANNOTATION_TYPE)
+public @interface DefaultQualifierInHierarchy {
+
+}
diff --git a/engine/src/core/checkers/quals/DefaultQualifiers.java b/engine/src/core/checkers/quals/DefaultQualifiers.java
new file mode 100644
index 000000000..bf3349e23
--- /dev/null
+++ b/engine/src/core/checkers/quals/DefaultQualifiers.java
@@ -0,0 +1,33 @@
+package checkers.quals;
+
+import static java.lang.annotation.ElementType.*;
+
+import java.lang.annotation.*;
+
+/**
+ * Specifies the annotations to be included in a type without having to provide
+ * them explicitly.
+ *
+ * This annotation permits specifying multiple default qualifiers for more
+ * than one type system. It is necessary because Java forbids multiple
+ * annotations of the same name at a single location.
+ *
+ * Example:
+ *
+ *
+ * @DefaultQualifiers({
+ * @DefaultQualifier("NonNull"),
+ * @DefaultQualifier(value = "Interned", locations = ALL_EXCEPT_LOCALS),
+ * @DefaultQualifier("Tainted")
+ * })
+ *
+ *
+ * @see DefaultQualifier
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({CONSTRUCTOR, METHOD, FIELD, LOCAL_VARIABLE, PARAMETER, TYPE})
+public @interface DefaultQualifiers {
+ /** The default qualifier settings */
+ DefaultQualifier[] value() default { };
+}
diff --git a/engine/src/core/checkers/quals/Dependent.java b/engine/src/core/checkers/quals/Dependent.java
new file mode 100644
index 000000000..8c96cf66d
--- /dev/null
+++ b/engine/src/core/checkers/quals/Dependent.java
@@ -0,0 +1,35 @@
+package checkers.quals;
+
+import java.lang.annotation.*;
+
+/**
+ * Refines the qualified type of the annotated field or variable based on the
+ * qualified type of the receiver. The annotation declares a relationship
+ * between multiple type qualifier hierarchies.
+ *
+ *
Example:
+ * Consider a field, {@code lock}, that is only initialized if the
+ * enclosing object (the receiver), is marked as {@code ThreadSafe}.
+ * Such a field can be declared as:
+ *
+ *
+ * private @Nullable @Dependent(result=NonNull.class, when=ThreadSafe.class)
+ * Lock lock;
+ *
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+//@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})
+public @interface Dependent {
+
+ /**
+ * The class of the refined qualifier to be applied.
+ */
+ Class extends Annotation> result();
+
+ /**
+ * The qualifier class of the receiver that causes the {@code result}
+ * qualifier to be applied.
+ */
+ Class extends Annotation> when();
+}
diff --git a/engine/src/core/checkers/quals/SubtypeOf.java b/engine/src/core/checkers/quals/SubtypeOf.java
new file mode 100644
index 000000000..ffc8be045
--- /dev/null
+++ b/engine/src/core/checkers/quals/SubtypeOf.java
@@ -0,0 +1,59 @@
+package checkers.quals;
+
+import java.lang.annotation.*;
+
+/**
+ * A meta-annotation to specify all the qualifiers that the given qualifier
+ * is a subtype of. This provides a declarative way to specify the type
+ * qualifier hierarchy. (Alternatively, the hierarchy can be defined
+ * procedurally by subclassing {@link checkers.types.QualifierHierarchy} or
+ * {@link checkers.types.TypeHierarchy}.)
+ *
+ *
+ * Example:
+ *
@SubtypeOf( { Nullable.class } )
+ * public @interface NonNull { }
+ *
+ *
+ *
+ *
+ * If a qualified type is a subtype of the same type without any qualifier,
+ * then use Unqualified.class
in place of a type qualifier
+ * class. For example, to express that @Encrypted C
+ * is a subtype of C
(for every class
+ * C
), and likewise for @Interned
, write:
+ *
+ *
@SubtypeOf(Unqualified.class)
+ * public @interface Encrypted { }
+ *
+ * @SubtypeOf(Unqualified.class)
+ * public @interface Interned { }
+ *
+ *
+ *
+ *
+ * For the root type qualifier in the qualifier hierarchy (i.e., the
+ * qualifier that is a supertype of all other qualifiers in the given
+ * hierarchy), use an empty set of values:
+ *
+ *
@SubtypeOf( { } )
+ * public @interface Nullable { }
+ *
+ * @SubtypeOf( {} )
+ * public @interface ReadOnly { }
+ *
+ *
+ *
+ * Together, all the @SubtypeOf meta-annotations fully describe the type
+ * qualifier hierarchy.
+ * No @SubtypeOf meta-annotation is needed on (or can be written on) the
+ * Unqualified pseudo-qualifier, whose position in the hierarchy is
+ * inferred from the meta-annotations on the explicit qualifiers.
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.ANNOTATION_TYPE)
+public @interface SubtypeOf {
+ /** An array of the supertype qualifiers of the annotated qualifier **/
+ Class extends Annotation>[] value();
+}
diff --git a/engine/src/core/checkers/quals/TypeQualifier.java b/engine/src/core/checkers/quals/TypeQualifier.java
new file mode 100644
index 000000000..25c4f7bbd
--- /dev/null
+++ b/engine/src/core/checkers/quals/TypeQualifier.java
@@ -0,0 +1,16 @@
+package checkers.quals;
+
+import java.lang.annotation.*;
+
+/**
+ * A meta-annotation indicating that the annotated annotation is a type
+ * qualifier.
+ *
+ * Examples of such qualifiers: {@code @ReadOnly}, {@code @NonNull}
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.ANNOTATION_TYPE)
+public @interface TypeQualifier {
+
+}
diff --git a/engine/src/core/checkers/quals/Unqualified.java b/engine/src/core/checkers/quals/Unqualified.java
new file mode 100644
index 000000000..a210b704b
--- /dev/null
+++ b/engine/src/core/checkers/quals/Unqualified.java
@@ -0,0 +1,16 @@
+package checkers.quals;
+
+import java.lang.annotation.Target;
+
+/**
+ * A special annotation intended solely for representing an unqualified type in
+ * the qualifier hierarchy, as an argument to {@link SubtypeOf#value()},
+ * in the type qualifiers declarations.
+ *
+ *
+ * Programmers cannot write this in source code.
+ */
+@TypeQualifier
+@SubtypeOf({})
+@Target({}) // empty target prevents programmers from writing this in a program
+public @interface Unqualified { }
diff --git a/engine/src/core/checkers/quals/Unused.java b/engine/src/core/checkers/quals/Unused.java
new file mode 100644
index 000000000..98d99fa68
--- /dev/null
+++ b/engine/src/core/checkers/quals/Unused.java
@@ -0,0 +1,44 @@
+package checkers.quals;
+
+import static java.lang.annotation.ElementType.*;
+
+import java.lang.annotation.*;
+
+/**
+ * Declares that the field may not be accessed if the receiver is of the
+ * specified qualifier type (or any supertype).
+ *
+ * This property is verified by the checker that type-checks the {@code
+ * when} element value qualifier.
+ *
+ *
Example
+ * Consider a class, {@code Table}, with a locking field, {@code lock}. The
+ * lock is used when a {@code Table} instance is shared across threads. When
+ * running in a local thread, the {@code lock} field ought not to be used.
+ *
+ * You can declare this behavior in the following way:
+ *
+ *
+ * class Table {
+ * private @Unused(when=LocalToThread.class) final Lock lock;
+ * ...
+ * }
+ *
+ *
+ * The checker for {@code @LocalToThread} would issue an error for the following code:
+ *
+ * @LocalToThread Table table = ...;
+ * ... table.lock ...;
+ *
+ *
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({FIELD})
+public @interface Unused {
+ /**
+ * The field that is annotated with @Unused may not be accessed via a
+ * receiver that is annotated with the "when" annotation.
+ */
+ Class extends Annotation> when();
+}
diff --git a/engine/src/core/checkers/quals/package-info.java b/engine/src/core/checkers/quals/package-info.java
new file mode 100644
index 000000000..5ae80a7d6
--- /dev/null
+++ b/engine/src/core/checkers/quals/package-info.java
@@ -0,0 +1,10 @@
+/**
+ * Contains the basic annotations to be used by all type systems
+ * and meta-annotations to qualify annotations (qualifiers).
+ *
+ * They may serve as documentation for the type qualifiers, and aid the
+ * Checker Framework to infer the relations between the type qualifiers.
+ *
+ * @checker.framework.manual #writing-a-checker Writing a checker
+ */
+package checkers.quals;
diff --git a/engine/src/core/com/jme3/animation/AnimChannel.java b/engine/src/core/com/jme3/animation/AnimChannel.java
new file mode 100644
index 000000000..efa4da032
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/AnimChannel.java
@@ -0,0 +1,350 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.animation;
+
+import com.jme3.math.FastMath;
+import java.util.BitSet;
+
+/**
+ * AnimChannel
provides controls, such as play, pause,
+ * fast forward, etc, for an animation. The animation
+ * channel may influence the entire model or specific bones of the model's
+ * skeleton. A single model may have multiple animation channels influencing
+ * various parts of its body. For example, a character model may have an
+ * animation channel for its feet, and another for its torso, and
+ * the animations for each channel are controlled independently.
+ *
+ * @author Kirill Vainer
+ */
+public final class AnimChannel {
+
+ private AnimControl control;
+
+// private ArrayList affectedBones;
+ private BitSet affectedBones;
+
+ private BoneAnimation animation;
+ private BoneAnimation blendFrom;
+ private float time;
+ private float speed;
+ private float timeBlendFrom;
+ private float speedBlendFrom;
+
+ private LoopMode loopMode, loopModeBlendFrom;
+ private float defaultBlendTime = 0.15f;
+
+ private float blendAmount = 1f;
+ private float blendRate = 0;
+
+ private static float clampWrapTime(float t, float max, LoopMode loopMode){
+ if (t < 0f){
+ //float tMod = -(-t % max);
+ switch (loopMode){
+ case DontLoop:
+ return 0;
+ case Cycle:
+ return t;
+ case Loop:
+ return max - t;
+ }
+ }else if (t > max){
+ switch (loopMode){
+ case DontLoop:
+ return max;
+ case Cycle:
+ return /*-max;*/-(2f * max - t);
+ case Loop:
+ return t - max;
+ }
+ }
+
+ return t;
+ }
+
+ AnimChannel(AnimControl control){
+ this.control = control;
+ }
+
+ /**
+ * @return The name of the currently playing animation, or null if
+ * none is assigned.
+ *
+ * @see AnimChannel#setAnim(java.lang.String)
+ */
+ public String getAnimationName() {
+ return animation != null ? animation.getName() : null;
+ }
+
+ /**
+ * @return The loop mode currently set for the animation. The loop mode
+ * determines what will happen to the animation once it finishes
+ * playing.
+ *
+ * For more information, see the LoopMode enum class.
+ * @see LoopMode
+ * @see AnimChannel#setLoopMode(com.jme3.animation.LoopMode)
+ */
+ public LoopMode getLoopMode() {
+ return loopMode;
+ }
+
+ /**
+ * @param loopMode Set the loop mode for the channel. The loop mode
+ * determines what will happen to the animation once it finishes
+ * playing.
+ *
+ * For more information, see the LoopMode enum class.
+ * @see LoopMode
+ */
+ public void setLoopMode(LoopMode loopMode) {
+ this.loopMode = loopMode;
+ }
+
+ /**
+ * @return The speed that is assigned to the animation channel. The speed
+ * is a scale value starting from 0.0, at 1.0 the animation will play
+ * at its default speed.
+ *
+ * @see AnimChannel#setSpeed(float)
+ */
+ public float getSpeed() {
+ return speed;
+ }
+
+ /**
+ * @param speed Set the speed of the animation channel. The speed
+ * is a scale value starting from 0.0, at 1.0 the animation will play
+ * at its default speed.
+ */
+ public void setSpeed(float speed) {
+ this.speed = speed;
+ }
+
+ /**
+ * @return The time of the currently playing animation. The time
+ * starts at 0 and continues on until getAnimMaxTime().
+ *
+ * @see AnimChannel#setTime(float)
+ */
+ public float getTime() {
+ return time;
+ }
+
+ /**
+ * @param time Set the time of the currently playing animation, the time
+ * is clamped from 0 to getAnimMaxTime().
+ */
+ public void setTime(float time) {
+ this.time = FastMath.clamp(time, 0, getAnimMaxTime());
+ }
+
+ /**
+ * @return The length of the currently playing animation, or zero
+ * if no animation is playing.
+ *
+ * @see AnimChannel#getTime()
+ */
+ public float getAnimMaxTime(){
+ return animation != null ? animation.getLength() : 0f;
+ }
+
+ /**
+ * Set the current animation that is played by this AnimChannel.
+ * This resets the time to zero, and optionally blends the animation
+ * over blendTime
seconds with the currently playing animation.
+ * Notice that this method will reset the control's speed to 1.0.
+ *
+ * @param name The name of the animation to play
+ * @param blendTime The blend time over which to blend the new animation
+ * with the old one. If zero, then no blending will occur and the new
+ * animation will be applied instantly.
+ */
+ public void setAnim(String name, float blendTime){
+ if (name == null)
+ throw new NullPointerException();
+
+ if (blendTime < 0f)
+ throw new IllegalArgumentException("blendTime cannot be less than zero");
+
+ BoneAnimation anim = control.animationMap.get(name);
+ if (anim == null)
+ throw new IllegalArgumentException("Cannot find animation named: '"+name+"'");
+
+ control.notifyAnimChange(this, name);
+
+ if (animation != null && blendTime > 0f){
+ // activate blending
+ blendFrom = animation;
+ timeBlendFrom = time;
+ speedBlendFrom = speed;
+ loopModeBlendFrom = loopMode;
+ blendAmount = 0f;
+ blendRate = 1f / blendTime;
+ }
+
+ animation = anim;
+ time = 0;
+ speed = 1f;
+ loopMode = LoopMode.Loop;
+ }
+
+ /**
+ *
+ * @param name
+ */
+ public void setAnim(String name){
+ setAnim(name, defaultBlendTime);
+ }
+
+ /**
+ * Add all the bones of the model's skeleton to be
+ * influenced by this animation channel.
+ */
+ public void addAllBones() {
+ affectedBones = null;
+ }
+
+ /**
+ * Add a single bone to be influenced by this animation channel.
+ */
+ public void addBone(String name) {
+ addBone(control.getSkeleton().getBone(name));
+ }
+
+ /**
+ * Add a single bone to be influenced by this animation channel.
+ */
+ public void addBone(Bone bone) {
+ int boneIndex = control.getSkeleton().getBoneIndex(bone);
+ if(affectedBones == null) {
+ affectedBones = new BitSet(control.getSkeleton().getBoneCount());
+ }
+ affectedBones.set(boneIndex);
+ }
+
+ /**
+ * Add bones to be influenced by this animation channel starting from the
+ * given bone name and going toward the root bone.
+ */
+ public void addToRootBone(String name) {
+ addToRootBone(control.getSkeleton().getBone(name));
+ }
+
+ /**
+ * Add bones to be influenced by this animation channel starting from the
+ * given bone and going toward the root bone.
+ */
+ public void addToRootBone(Bone bone) {
+ addBone(bone);
+ while (bone.getParent() != null) {
+ bone = bone.getParent();
+ addBone(bone);
+ }
+ }
+
+ /**
+ * Add bones to be influenced by this animation channel, starting
+ * from the given named bone and going toward its children.
+ */
+ public void addFromRootBone(String name) {
+ addFromRootBone(control.getSkeleton().getBone(name));
+ }
+
+ /**
+ * Add bones to be influenced by this animation channel, starting
+ * from the given bone and going toward its children.
+ */
+ public void addFromRootBone(Bone bone) {
+ addBone(bone);
+ if (bone.getChildren() == null)
+ return;
+ for (Bone childBone : bone.getChildren()) {
+ addBone(childBone);
+ addFromRootBone(childBone);
+ }
+ }
+
+
+ void reset(){
+ animation = null;
+ blendFrom = null;
+ }
+
+ void update(float tpf) {
+ if (animation == null)
+ return;
+
+ if (blendFrom != null){
+ blendFrom.setTime(timeBlendFrom, control.skeleton, 1f - blendAmount, affectedBones);
+ timeBlendFrom += tpf * speedBlendFrom;
+ timeBlendFrom = clampWrapTime(timeBlendFrom,
+ blendFrom.getLength(),
+ loopModeBlendFrom);
+ if (timeBlendFrom < 0){
+ timeBlendFrom = -timeBlendFrom;
+ speedBlendFrom = -speedBlendFrom;
+ }
+
+ blendAmount += tpf * blendRate;
+ if (blendAmount > 1f){
+ blendAmount = 1f;
+ blendFrom = null;
+ }
+ }
+
+ animation.setTime(time, control.skeleton, blendAmount, affectedBones);
+ time += tpf * speed;
+
+ if (animation.getLength() > 0){
+ if (time >= animation.getLength())
+ control.notifyAnimCycleDone(this, animation.getName());
+ else if (time < 0)
+ control.notifyAnimCycleDone(this, animation.getName());
+ }
+
+ time = clampWrapTime(time, animation.getLength(), loopMode);
+ if (time < 0){
+ time = -time;
+ speed = -speed;
+ }
+
+
+ }
+
+ public AnimControl getControl() {
+ return control;
+ }
+
+
+
+}
diff --git a/engine/src/core/com/jme3/animation/AnimControl.java b/engine/src/core/com/jme3/animation/AnimControl.java
new file mode 100644
index 000000000..587db4b01
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/AnimControl.java
@@ -0,0 +1,512 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.animation;
+
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.OutputCapsule;
+import com.jme3.export.Savable;
+import com.jme3.math.FastMath;
+import com.jme3.math.Matrix4f;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.control.AbstractControl;
+import com.jme3.scene.control.Control;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+
+/**
+ * AnimControl
is a Spatial control that allows manipulation
+ * of skeletal animation.
+ *
+ * The control currently supports:
+ * 1) Animation blending/transitions
+ * 2) Multiple animation channels
+ * 3) Multiple skins
+ * 4) Animation event listeners
+ * 5) Animated model cloning
+ * 6) Animated model binary import/export
+ *
+ * Planned:
+ * 1) Hardware skinning
+ * 2) Morph/Pose animation
+ * 3) Attachments
+ * 4) Add/remove skins
+ *
+ * @author Kirill Vainer
+ */
+public final class AnimControl extends AbstractControl implements Savable, Cloneable {
+
+ /**
+ * List of targets which this controller effects.
+ */
+ Mesh[] targets;
+
+ /**
+ * Skeleton object must contain corresponding data for the targets' weight buffers.
+ */
+ Skeleton skeleton;
+
+ /**
+ * List of animations
+ */
+ HashMap animationMap;
+
+ /**
+ * Animation channels
+ */
+ transient ArrayList channels
+ = new ArrayList();
+
+ transient ArrayList listeners
+ = new ArrayList();
+
+ /**
+ * Create a new AnimControl
that will animate the given skins
+ * using the skeleton and provided animations.
+ *
+ * @param model The root node of all the skins specified in
+ * meshes
argument.
+ * @param meshes The skins, or meshes, to animate. Should have
+ * properly set BoneIndex and BoneWeight buffers.
+ * @param skeleton The skeleton structure represents a bone hierarchy
+ * to be animated.
+ */
+ public AnimControl(Node model, Mesh[] meshes, Skeleton skeleton){
+ super(model);
+
+ this.skeleton = skeleton;
+ this.targets = meshes;
+ reset();
+ }
+
+ /**
+ * Used only for Saving/Loading models (all parameters of the non-default
+ * constructor are restored from the saved model, but the object must be
+ * constructed beforehand)
+ */
+ public AnimControl() {
+ }
+
+ public Control cloneForSpatial(Spatial spatial){
+ try {
+ Node clonedNode = (Node) spatial;
+ AnimControl clone = (AnimControl) super.clone();
+ clone.spatial = spatial;
+ clone.skeleton = new Skeleton(skeleton);
+ Mesh[] meshes = new Mesh[targets.length];
+ for (int i = 0; i < meshes.length; i++) {
+ meshes[i] = ((Geometry) clonedNode.getChild(i)).getMesh();
+ }
+ for (int i = meshes.length; i < clonedNode.getQuantity(); i++){
+ // go through attachment nodes, apply them to correct bone
+ Spatial child = clonedNode.getChild(i);
+ if (child instanceof Node){
+ Node clonedAttachNode = (Node) child;
+ Bone originalBone = (Bone) clonedAttachNode.getUserData("AttachedBone");
+
+ if (originalBone != null){
+ Bone clonedBone = clone.skeleton.getBone(originalBone.getName());
+
+ clonedAttachNode.setUserData("AttachedBone", clonedBone);
+ clonedBone.setAttachmentsNode(clonedAttachNode);
+ }
+ }
+ }
+ clone.targets = meshes;
+ clone.channels = new ArrayList();
+ return clone;
+ } catch (CloneNotSupportedException ex) {
+ throw new AssertionError();
+ }
+ }
+
+ /**
+ * @param animations Set the animations that this AnimControl
+ * will be capable of playing. The animations should be compatible
+ * with the skeleton given in the constructor.
+ */
+ public void setAnimations(HashMap animations){
+ animationMap = animations;
+ }
+
+ /**
+ * Retrieve an animation from the list of animations.
+ * @param name The name of the animation to retrieve.
+ * @return The animation corresponding to the given name, or null, if no
+ * such named animation exists.
+ */
+ public BoneAnimation getAnim(String name){
+ return animationMap.get(name);
+ }
+
+ /**
+ * Adds an animation to be available for playing to this
+ * AnimControl
.
+ * @param anim The animation to add.
+ */
+ public void addAnim(BoneAnimation anim){
+ animationMap.put(anim.getName(), anim);
+ }
+
+ /**
+ * Remove an animation so that it is no longer available for playing.
+ * @param anim The animation to remove.
+ */
+ public void removeAnim(BoneAnimation anim){
+ if (!animationMap.containsKey(anim.getName()))
+ throw new IllegalArgumentException("Given animation does not exist " +
+ "in this AnimControl");
+
+ animationMap.remove(anim.getName());
+ }
+
+ public Node getAttachmentsNode(String boneName) {
+ Bone b = skeleton.getBone(boneName);
+ if (b == null)
+ throw new IllegalArgumentException("Given bone name does not exist " +
+ "in the skeleton.");
+
+ Node n = b.getAttachmentsNode();
+ Node model = (Node) spatial;
+ model.attachChild(n);
+ return n;
+ }
+
+ /**
+ * Create a new animation channel, by default assigned to all bones
+ * in the skeleton.
+ *
+ * @return A new animation channel for this AnimControl
.
+ */
+ public AnimChannel createChannel(){
+ AnimChannel channel = new AnimChannel(this);
+ channels.add(channel);
+ return channel;
+ }
+
+ /**
+ * Return the animation channel at the given index.
+ * @param index The index, starting at 0, to retrieve the AnimChannel
.
+ * @return The animation channel at the given index, or throws an exception
+ * if the index is out of bounds.
+ *
+ * @throws IndexOutOfBoundsException If no channel exists at the given index.
+ */
+ public AnimChannel getChannel(int index){
+ return channels.get(index);
+ }
+
+ /**
+ * @return The number of channels that are controlled by this
+ * AnimControl
.
+ *
+ * @see AnimControl#createChannel()
+ */
+ public int getNumChannels(){
+ return channels.size();
+ }
+
+ /**
+ * Clears all the channels that were created.
+ *
+ * @see AnimControl#createChannel()
+ */
+ public void clearChannels(){
+ channels.clear();
+ }
+
+ /**
+ * @return The skeleton of this AnimControl
.
+ */
+ public Skeleton getSkeleton() {
+ return skeleton;
+ }
+
+ /**
+ * @return The targets, or skins, being influenced by this
+ * AnimControl
.
+ */
+ public Mesh[] getTargets() {
+ return targets;
+ }
+
+ /**
+ * Adds a new listener to receive animation related events.
+ * @param listener The listener to add.
+ */
+ public void addListener(AnimEventListener listener){
+ if (listeners.contains(listener))
+ throw new IllegalArgumentException("The given listener is already " +
+ "registed at this AnimControl");
+
+ listeners.add(listener);
+ }
+
+ /**
+ * Removes the given listener from listening to events.
+ * @param listener
+ * @see AnimControl#addListener(com.jme3.animation.AnimEventListener)
+ */
+ public void removeListener(AnimEventListener listener){
+ if (!listeners.remove(listener))
+ throw new IllegalArgumentException("The given listener is not " +
+ "registed at this AnimControl");
+ }
+
+ /**
+ * Clears all the listeners added to this AnimControl
+ *
+ * @see AnimControl#addListener(com.jme3.animation.AnimEventListener)
+ */
+ public void clearListeners(){
+ listeners.clear();
+ }
+
+ void notifyAnimChange(AnimChannel channel, String name){
+ for (int i = 0; i < listeners.size(); i++){
+ listeners.get(i).onAnimChange(this, channel, name);
+ }
+ }
+
+ void notifyAnimCycleDone(AnimChannel channel, String name){
+ for (int i = 0; i < listeners.size(); i++){
+ listeners.get(i).onAnimCycleDone(this, channel, name);
+ }
+ }
+
+ final void reset(){
+ resetToBind();
+ if (skeleton != null){
+ skeleton.resetAndUpdate();
+ }
+ }
+
+ void resetToBind(){
+ for (int i = 0; i < targets.length; i++){
+ Mesh mesh = targets[i];
+ if (targets[i].getBuffer(Type.BindPosePosition) != null){
+ VertexBuffer bi = mesh.getBuffer(Type.BoneIndex);
+ ByteBuffer bib = (ByteBuffer) bi.getData();
+ if (!bib.hasArray())
+ mesh.prepareForAnim(true); // prepare for software animation
+
+ VertexBuffer bindPos = mesh.getBuffer(Type.BindPosePosition);
+ VertexBuffer bindNorm = mesh.getBuffer(Type.BindPoseNormal);
+ VertexBuffer pos = mesh.getBuffer(Type.Position);
+ VertexBuffer norm = mesh.getBuffer(Type.Normal);
+ FloatBuffer pb = (FloatBuffer) pos.getData();
+ FloatBuffer nb = (FloatBuffer) norm.getData();
+ FloatBuffer bpb = (FloatBuffer) bindPos.getData();
+ FloatBuffer bnb = (FloatBuffer) bindNorm.getData();
+ pb.clear();
+ nb.clear();
+ bpb.clear();
+ bnb.clear();
+ pb.put(bpb).clear();
+ nb.put(bnb).clear();
+ }
+ }
+ }
+
+ /**
+ * @return The names of all animations that this AnimControl
+ * can play.
+ */
+ public Collection getAnimationNames(){
+ return animationMap.keySet();
+ }
+
+ /**
+ * Returns the length of the given named animation.
+ * @param name The name of the animation
+ * @return The length of time, in seconds, of the named animation.
+ */
+ public float getAnimationLength(String name){
+ BoneAnimation a = animationMap.get(name);
+ if (a == null)
+ throw new IllegalArgumentException("The animation " + name +
+ " does not exist in this AnimControl");
+
+ return a.getLength();
+ }
+
+ @Override
+ protected void controlUpdate(float tpf) {
+ resetToBind(); // reset morph meshes to bind pose
+ skeleton.reset(); // reset skeleton to bind pose
+
+ for (int i = 0; i < channels.size(); i++){
+ channels.get(i).update(tpf);
+ }
+
+ skeleton.updateWorldVectors();
+ // here update the targets verticles if no hardware skinning supported
+
+ Matrix4f[] offsetMatrices = skeleton.computeSkinningMatrices();
+
+ // if hardware skinning is supported, the matrices and weight buffer
+ // will be sent by the SkinningShaderLogic object assigned to the shader
+ for (int i = 0; i < targets.length; i++){
+ // only update targets with bone-vertex assignments
+ if (targets[i].getBuffer(Type.BoneIndex) != null)
+ softwareSkinUpdate(targets[i], offsetMatrices);
+ }
+ }
+
+ @Override
+ protected void controlRender(RenderManager rm, ViewPort vp) {
+ }
+
+ private void softwareSkinUpdate(Mesh mesh, Matrix4f[] offsetMatrices){
+ int maxWeightsPerVert = mesh.getMaxNumWeights();
+ int fourMinusMaxWeights = 4 - maxWeightsPerVert;
+
+ // NOTE: This code assumes the vertex buffer is in bind pose
+ // resetToBind() has been called this frame
+ VertexBuffer vb = mesh.getBuffer(Type.Position);
+ FloatBuffer fvb = (FloatBuffer) vb.getData();
+ fvb.rewind();
+
+ VertexBuffer nb = mesh.getBuffer(Type.Normal);
+ FloatBuffer fnb = (FloatBuffer) nb.getData();
+ fnb.rewind();
+
+ // get boneIndexes and weights for mesh
+ ByteBuffer ib = (ByteBuffer) mesh.getBuffer(Type.BoneIndex).getData();
+ FloatBuffer wb = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData();
+
+ ib.rewind();
+ wb.rewind();
+
+ float[] weights = wb.array();
+ byte[] indices = ib.array();
+ int idxWeights = 0;
+
+ TempVars vars = TempVars.get();
+ float[] posBuf = vars.skinPositions;
+ float[] normBuf = vars.skinNormals;
+
+ int iterations = (int) FastMath.ceil(fvb.capacity() / ((float)posBuf.length));
+ int bufLength = posBuf.length * 3;
+ for (int i = iterations-1; i >= 0; i--){
+ // read next set of positions and normals from native buffer
+ bufLength = Math.min(posBuf.length, fvb.remaining());
+ fvb.get(posBuf, 0, bufLength);
+ fnb.get(normBuf, 0, bufLength);
+ int verts = bufLength / 3;
+ int idxPositions = 0;
+
+ // iterate vertices and apply skinning transform for each effecting bone
+ for (int vert = verts - 1; vert >= 0; vert--){
+ float nmx = normBuf[idxPositions];
+ float vtx = posBuf[idxPositions++];
+ float nmy = normBuf[idxPositions];
+ float vty = posBuf[idxPositions++];
+ float nmz = normBuf[idxPositions];
+ float vtz = posBuf[idxPositions++];
+
+ float rx=0, ry=0, rz=0, rnx=0, rny=0, rnz=0;
+
+ for (int w = maxWeightsPerVert - 1; w >= 0; w--){
+ float weight = weights[idxWeights];
+ Matrix4f mat = offsetMatrices[indices[idxWeights++]];
+
+ rx += (mat.m00 * vtx + mat.m01 * vty + mat.m02 * vtz + mat.m03) * weight;
+ ry += (mat.m10 * vtx + mat.m11 * vty + mat.m12 * vtz + mat.m13) * weight;
+ rz += (mat.m20 * vtx + mat.m21 * vty + mat.m22 * vtz + mat.m23) * weight;
+
+ rnx += (nmx * mat.m00 + nmy * mat.m01 + nmz * mat.m02) * weight;
+ rny += (nmx * mat.m10 + nmy * mat.m11 + nmz * mat.m12) * weight;
+ rnz += (nmx * mat.m20 + nmy * mat.m21 + nmz * mat.m22) * weight;
+ }
+
+ idxWeights += fourMinusMaxWeights;
+
+ idxPositions -= 3;
+ normBuf[idxPositions] = rnx;
+ posBuf[idxPositions++] = rx;
+ normBuf[idxPositions] = rny;
+ posBuf[idxPositions++] = ry;
+ normBuf[idxPositions] = rnz;
+ posBuf[idxPositions++] = rz;
+ }
+
+
+ fvb.position(fvb.position()-bufLength);
+ fvb.put(posBuf, 0, bufLength);
+ fnb.position(fnb.position()-bufLength);
+ fnb.put(normBuf, 0, bufLength);
+ }
+
+ vb.updateData(fvb);
+ nb.updateData(fnb);
+
+// mesh.updateBound();
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException{
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(targets, "targets", null);
+ oc.write(skeleton, "skeleton", null);
+ oc.writeStringSavableMap(animationMap, "animations", null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException{
+ super.read(im);
+ InputCapsule in = im.getCapsule(this);
+ Savable[] sav = in.readSavableArray("targets", null);
+ if (sav != null){
+ targets = new Mesh[sav.length];
+ System.arraycopy(sav, 0, targets, 0, sav.length);
+ }
+ skeleton = (Skeleton) in.readSavable("skeleton", null);
+ animationMap = (HashMap) in.readStringSavableMap("animations", null);
+ }
+
+}
diff --git a/engine/src/core/com/jme3/animation/AnimEventListener.java b/engine/src/core/com/jme3/animation/AnimEventListener.java
new file mode 100644
index 000000000..22a0b98b2
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/AnimEventListener.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.animation;
+
+/**
+ * AnimEventListener
allows user code to receive various
+ * events regarding an AnimControl. For example, when an animation cycle is done.
+ *
+ * @author Kirill Vainer
+ */
+public interface AnimEventListener {
+
+ /**
+ * Invoked when an animation "cycle" is done. For non-looping animations,
+ * this event is invoked when the animation is finished playing. For
+ * looping animations, this even is invoked each time the animation is restarted.
+ *
+ * @param control The control to which the listener is assigned.
+ * @param channel The channel being altered
+ * @param animName The new animation that is done.
+ */
+ public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName);
+
+ /**
+ * Invoked when a animation is set to play by the user on the given channel.
+ *
+ * @param control The control to which the listener is assigned.
+ * @param channel The channel being altered
+ * @param animName The new animation name set.
+ */
+ public void onAnimChange(AnimControl control, AnimChannel channel, String animName);
+
+}
diff --git a/engine/src/core/com/jme3/animation/AnimationPath.java b/engine/src/core/com/jme3/animation/AnimationPath.java
new file mode 100644
index 000000000..5870e4308
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/AnimationPath.java
@@ -0,0 +1,885 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.animation;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.control.AbstractControl;
+import com.jme3.scene.control.Control;
+import com.jme3.scene.shape.Box;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ *
+ *
+ * @deprecated use {@link MotionPath}
+ */
+@Deprecated
+public class AnimationPath extends AbstractControl {
+
+ private boolean playing = false;
+ private int currentWayPoint;
+ private float currentValue;
+ private List wayPoints = new ArrayList();
+ private Node debugNode;
+ private AssetManager assetManager;
+ private List listeners;
+ private Vector3f curveDirection;
+ private Vector3f lookAt;
+ private Vector3f upVector;
+ private Quaternion rotation;
+ private float duration = 5f;
+ private List segmentsLength;
+ private float totalLength;
+ private List CRcontrolPoints;
+ private float speed;
+ private float curveTension = 0.5f;
+ private boolean loop = false;
+ private boolean cycle = false;
+
+ @Override
+ protected void controlUpdate(float tpf) {
+
+ }
+
+ @Override
+ protected void controlRender(RenderManager rm, ViewPort vp) {
+
+ }
+
+ /**
+ * Enum for the different type of target direction behavior
+ */
+ @Deprecated
+ public enum Direction {
+
+ /**
+ * the target stay in the starting direction
+ */
+ None,
+ /**
+ * The target rotates with the direction of the path
+ */
+ Path,
+ /**
+ * The target rotates with the direction of the path but with the additon of a rtotation
+ * you need to use the setRotation mathod when using this Direction
+ */
+ PathAndRotation,
+ /**
+ * The target rotates with the given rotation
+ */
+ Rotation,
+ /**
+ * The target looks at a point
+ * You need to use the setLookAt method when using this direction
+ */
+ LookAt
+ }
+ private Direction directionType = Direction.None;
+
+ @Deprecated
+ public enum PathInterpolation {
+
+ /**
+ * Compute a linear path between the waypoints
+ */
+ Linear,
+ /**
+ * Compute a Catmull-Rom spline path between the waypoints
+ * see http://www.mvps.org/directx/articles/catmull/
+ */
+ CatmullRom
+ }
+ private PathInterpolation pathInterpolation = PathInterpolation.CatmullRom;
+
+ /**
+ * Create an animation Path for this target
+ * @param target
+ */
+ @Deprecated
+ public AnimationPath(Spatial target) {
+ super();
+ this.spatial = target;
+ target.addControl(this);
+ }
+
+ /**
+ * don't use this contructor use AnimationPath(Spatial target)
+ */
+ @Deprecated
+ public AnimationPath() {
+ super();
+ }
+
+ @Deprecated
+ public Control cloneForSpatial(Spatial spatial) {
+ AnimationPath path = new AnimationPath(spatial);
+ for (Iterator it = wayPoints.iterator(); it.hasNext();) {
+ Vector3f vector3f = it.next();
+ path.addWayPoint(vector3f);
+ }
+ for (Iterator it = listeners.iterator(); it.hasNext();) {
+ AnimationPathListener animationPathListener = it.next();
+ path.addListener(animationPathListener);
+ }
+ return path;
+ }
+
+ @Override
+ public void setSpatial(Spatial spatial) {
+ this.spatial = spatial;
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return enabled;
+ }
+ float eps = 0.0001f;
+
+ /**
+ * Cal each update, don't call this method, it's for internal use only
+ * @param tpf
+ */
+ @Override
+ public void update(float tpf) {
+
+ if (enabled) {
+ if (playing) {
+ spatial.setLocalTranslation(interpolatePath(tpf));
+ computeTargetDirection();
+
+ if (currentValue >= 1.0f) {
+ currentValue = 0;
+ currentWayPoint++;
+ triggerWayPointReach(currentWayPoint);
+ }
+ if (currentWayPoint == wayPoints.size() - 1) {
+ if (loop) {
+ currentWayPoint = 0;
+ } else {
+ stop();
+ }
+ }
+ }
+ }
+ }
+
+ private Vector3f interpolatePath(float tpf) {
+ Vector3f temp = null;
+ float val;
+ switch (pathInterpolation) {
+ case CatmullRom:
+
+ val = tpf * speed;
+ currentValue += eps;
+ temp = FastMath.interpolateCatmullRom(currentValue, curveTension, CRcontrolPoints.get(currentWayPoint), CRcontrolPoints.get(currentWayPoint + 1), CRcontrolPoints.get(currentWayPoint + 2), CRcontrolPoints.get(currentWayPoint + 3));
+ float dist = temp.subtract(spatial.getLocalTranslation()).length();
+
+ while (dist < val) {
+ currentValue += eps;
+ temp = FastMath.interpolateCatmullRom(currentValue, curveTension, CRcontrolPoints.get(currentWayPoint), CRcontrolPoints.get(currentWayPoint + 1), CRcontrolPoints.get(currentWayPoint + 2), CRcontrolPoints.get(currentWayPoint + 3));
+ dist = temp.subtract(spatial.getLocalTranslation()).length();
+ }
+ if (directionType == Direction.Path || directionType == Direction.PathAndRotation) {
+ curveDirection = temp.subtract(spatial.getLocalTranslation()).normalizeLocal();
+ }
+ break;
+ case Linear:
+ val = duration * segmentsLength.get(currentWayPoint) / totalLength;
+ currentValue = Math.min(currentValue + tpf / val, 1.0f);
+ temp = FastMath.interpolateLinear(currentValue, wayPoints.get(currentWayPoint), wayPoints.get(currentWayPoint + 1));
+ curveDirection = wayPoints.get(currentWayPoint + 1).subtract(wayPoints.get(currentWayPoint)).normalizeLocal();
+ break;
+ default:
+ break;
+ }
+ return temp;
+ }
+
+ private void computeTargetDirection() {
+ switch (directionType) {
+ case Path:
+ Quaternion q = new Quaternion();
+ q.lookAt(curveDirection, Vector3f.UNIT_Y);
+ spatial.setLocalRotation(q);
+ break;
+ case LookAt:
+ if (lookAt != null) {
+ spatial.lookAt(lookAt, upVector);
+ }
+ break;
+ case PathAndRotation:
+ if (rotation != null) {
+ Quaternion q2 = new Quaternion();
+ q2.lookAt(curveDirection, Vector3f.UNIT_Y);
+ q2.multLocal(rotation);
+ spatial.setLocalRotation(q2);
+ }
+ break;
+ case Rotation:
+ if (rotation != null) {
+ spatial.setLocalRotation(rotation);
+ }
+ break;
+ case None:
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void attachDebugNode(Node root) {
+ if (debugNode == null) {
+ debugNode = new Node("AnimationPathFor" + spatial.getName());
+ Material m = assetManager.loadMaterial("Common/Materials/RedColor.j3m");
+ for (Iterator it = wayPoints.iterator(); it.hasNext();) {
+ Vector3f cp = it.next();
+ Geometry geo = new Geometry("box", new Box(cp, 0.3f, 0.3f, 0.3f));
+ geo.setMaterial(m);
+ debugNode.attachChild(geo);
+
+ }
+ switch (pathInterpolation) {
+ case CatmullRom:
+ debugNode.attachChild(CreateCatmullRomPath());
+ break;
+ case Linear:
+ debugNode.attachChild(CreateLinearPath());
+ break;
+ default:
+ debugNode.attachChild(CreateLinearPath());
+ break;
+ }
+
+ root.attachChild(debugNode);
+ }
+ }
+
+ private Geometry CreateLinearPath() {
+
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/WireColor.j3md");
+ mat.setColor("Color", ColorRGBA.Blue);
+
+ float[] array = new float[wayPoints.size() * 3];
+ short[] indices = new short[(wayPoints.size() - 1) * 2];
+ int i = 0;
+ int cpt = 0;
+ int k = 0;
+ int j = 0;
+ for (Iterator it = wayPoints.iterator(); it.hasNext();) {
+ Vector3f vector3f = it.next();
+ array[i] = vector3f.x;
+ i++;
+ array[i] = vector3f.y;
+ i++;
+ array[i] = vector3f.z;
+ i++;
+ if (it.hasNext()) {
+ k = j;
+ indices[cpt] = (short) k;
+ cpt++;
+ k++;
+ indices[cpt] = (short) k;
+ cpt++;
+ j++;
+ }
+ }
+
+ Mesh lineMesh = new Mesh();
+ lineMesh.setMode(Mesh.Mode.Lines);
+ lineMesh.setBuffer(VertexBuffer.Type.Position, 3, array);
+ lineMesh.setBuffer(VertexBuffer.Type.Index, (wayPoints.size() - 1) * 2, indices);
+ lineMesh.updateBound();
+ lineMesh.updateCounts();
+
+ Geometry lineGeometry = new Geometry("line", lineMesh);
+ lineGeometry.setMaterial(mat);
+ return lineGeometry;
+ }
+
+ private Geometry CreateCatmullRomPath() {
+
+ Material mat = new Material(assetManager, "Common/MatDefs/Misc/WireColor.j3md");
+ mat.setColor("Color", ColorRGBA.Blue);
+ int nbSubSegments = 10;
+
+ float[] array = new float[(((wayPoints.size() - 1) * nbSubSegments) + 1) * 3];
+ short[] indices = new short[((wayPoints.size() - 1) * nbSubSegments) * 2];
+ int i = 0;
+ int cptCP = 0;
+ for (Iterator it = wayPoints.iterator(); it.hasNext();) {
+ Vector3f vector3f = it.next();
+ array[i] = vector3f.x;
+ i++;
+ array[i] = vector3f.y;
+ i++;
+ array[i] = vector3f.z;
+ i++;
+ if (it.hasNext()) {
+ for (int j = 1; j < nbSubSegments; j++) {
+ Vector3f temp = FastMath.interpolateCatmullRom((float) j / nbSubSegments, curveTension, CRcontrolPoints.get(cptCP),
+ CRcontrolPoints.get(cptCP + 1), CRcontrolPoints.get(cptCP + 2), CRcontrolPoints.get(cptCP + 3));
+ array[i] = temp.x;
+ i++;
+ array[i] = temp.y;
+ i++;
+ array[i] = temp.z;
+ i++;
+ }
+ }
+ cptCP++;
+ }
+
+ i = 0;
+ int k = 0;
+ for (int j = 0; j < ((wayPoints.size() - 1) * nbSubSegments); j++) {
+ k = j;
+ indices[i] = (short) k;
+ i++;
+ k++;
+ indices[i] = (short) k;
+ i++;
+ }
+
+
+
+ Mesh lineMesh = new Mesh();
+ lineMesh.setMode(Mesh.Mode.Lines);
+ lineMesh.setBuffer(VertexBuffer.Type.Position, 3, array);
+ lineMesh.setBuffer(VertexBuffer.Type.Index, ((wayPoints.size() - 1) * nbSubSegments) * 2, indices);
+ lineMesh.updateBound();
+ lineMesh.updateCounts();
+
+ Geometry lineGeometry = new Geometry("line", lineMesh);
+ lineGeometry.setMaterial(mat);
+ return lineGeometry;
+ }
+
+ private void initCatmullRomWayPoints(List list) {
+ if (CRcontrolPoints == null) {
+ CRcontrolPoints = new ArrayList();
+ } else {
+ CRcontrolPoints.clear();
+ }
+ int nb = list.size() - 1;
+
+ if (cycle) {
+ CRcontrolPoints.add(list.get(list.size() - 2));
+ } else {
+ CRcontrolPoints.add(list.get(0).subtract(list.get(1).subtract(list.get(0))));
+ }
+
+ for (Iterator it = list.iterator(); it.hasNext();) {
+ Vector3f vector3f = it.next();
+ CRcontrolPoints.add(vector3f);
+ }
+ if (cycle) {
+ CRcontrolPoints.add(list.get(1));
+ } else {
+ CRcontrolPoints.add(list.get(nb).add(list.get(nb).subtract(list.get(nb - 1))));
+ }
+
+ }
+
+ @Override
+ public void render(RenderManager rm, ViewPort vp) {
+ //nothing to render
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.writeSavableArrayList((ArrayList) wayPoints, "wayPoints", null);
+ oc.write(lookAt, "lookAt", Vector3f.ZERO);
+ oc.write(upVector, "upVector", Vector3f.UNIT_Y);
+ oc.write(rotation, "rotation", Quaternion.IDENTITY);
+ oc.write(duration, "duration", 5f);
+ oc.write(directionType, "directionType", Direction.None);
+ oc.write(pathInterpolation, "pathInterpolation", PathInterpolation.CatmullRom);
+ float list[]=new float[segmentsLength.size()];
+ for (int i=0;i) in.readSavableArrayList("wayPoints", null);
+ lookAt = (Vector3f) in.readSavable("lookAt", Vector3f.ZERO);
+ upVector = (Vector3f) in.readSavable("upVector", Vector3f.UNIT_Y);
+ rotation = (Quaternion) in.readSavable("rotation", Quaternion.IDENTITY);
+ duration = in.readFloat("duration", 5f);
+ float list[]=in.readFloatArray("segmentsLength", null);
+ if (list!=null){
+ segmentsLength=new ArrayList();
+ for (int i=0;i) in.readSavableArrayList("CRControlPoints", null);
+ speed = in.readFloat("speed", 0);
+ curveTension = in.readFloat("curveTension", 0.5f);
+ cycle = in.readBoolean("cycle", false);
+ loop = in.readBoolean("loop", false);
+ }
+
+ /**
+ * plays the animation
+ */
+ @Deprecated
+ public void play() {
+ playing = true;
+ }
+
+ /**
+ * pauses the animation
+ */
+ @Deprecated
+ public void pause() {
+ playing = false;
+ }
+
+ /**
+ * stops the animation, next time play() is called the animation will start from the begining.
+ */
+ @Deprecated
+ public void stop() {
+ playing = false;
+ currentWayPoint = 0;
+ }
+
+ /**
+ * Addsa waypoint to the path
+ * @param wayPoint a position in world space
+ */
+ @Deprecated
+ public void addWayPoint(Vector3f wayPoint) {
+ if (wayPoints.size() > 2 && this.cycle) {
+ wayPoints.remove(wayPoints.size() - 1);
+ }
+ wayPoints.add(wayPoint);
+ if (wayPoints.size() >= 2 && this.cycle) {
+ wayPoints.add(wayPoints.get(0));
+ }
+ if (wayPoints.size() > 1) {
+ computeTotalLentgh();
+ }
+ }
+
+
+ private void computeTotalLentgh() {
+ totalLength = 0;
+ float l = 0;
+ if (segmentsLength == null) {
+ segmentsLength = new ArrayList();
+ } else {
+ segmentsLength.clear();
+ }
+ if (pathInterpolation == PathInterpolation.Linear) {
+ if (wayPoints.size() > 1) {
+ for (int i = 0; i < wayPoints.size() - 1; i++) {
+ l = wayPoints.get(i + 1).subtract(wayPoints.get(i)).length();
+ segmentsLength.add(l);
+ totalLength += l;
+ }
+ }
+ } else {
+ initCatmullRomWayPoints(wayPoints);
+ computeCatmulLength();
+ }
+ }
+
+ private void computeCatmulLength() {
+ float l = 0;
+ if (wayPoints.size() > 1) {
+ for (int i = 0; i < wayPoints.size() - 1; i++) {
+ l = getCatmullRomP1toP2Length(CRcontrolPoints.get(i),
+ CRcontrolPoints.get(i + 1), CRcontrolPoints.get(i + 2), CRcontrolPoints.get(i + 3), 0, 1);
+ segmentsLength.add(l);
+ totalLength += l;
+ }
+ }
+ speed = totalLength / duration;
+ }
+
+ /**
+ * retruns the length of the path in world units
+ * @return the length
+ */
+ @Deprecated
+ public float getLength() {
+ return totalLength;
+ }
+ //Compute lenght of p1 to p2 arc segment
+ //TODO extract to FastMath class
+
+ private float getCatmullRomP1toP2Length(Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3, float startRange, float endRange) {
+
+ float epsilon = 0.001f;
+ float middleValue = (startRange + endRange) * 0.5f;
+ Vector3f start = p1;
+ if (startRange != 0) {
+ start = FastMath.interpolateCatmullRom(startRange, curveTension, p0, p1, p2, p3);
+ }
+ Vector3f end = p2;
+ if (endRange != 1) {
+ end = FastMath.interpolateCatmullRom(endRange, curveTension, p0, p1, p2, p3);
+ }
+ Vector3f middle = FastMath.interpolateCatmullRom(middleValue, curveTension, p0, p1, p2, p3);
+ float l = end.subtract(start).length();
+ float l1 = middle.subtract(start).length();
+ float l2 = end.subtract(middle).length();
+ float len = l1 + l2;
+ if (l + epsilon < len) {
+ l1 = getCatmullRomP1toP2Length(p0, p1, p2, p3, startRange, middleValue);
+ l2 = getCatmullRomP1toP2Length(p0, p1, p2, p3, middleValue, endRange);
+ }
+ l = l1 + l2;
+ return l;
+ }
+
+ /**
+ * returns the waypoint at the given index
+ * @param i the index
+ * @return returns the waypoint position
+ */
+ @Deprecated
+ public Vector3f getWayPoint(int i) {
+ return wayPoints.get(i);
+ }
+
+ /**
+ * remove the waypoint from the path
+ * @param wayPoint the waypoint to remove
+ */
+ @Deprecated
+ public void removeWayPoint(Vector3f wayPoint) {
+ wayPoints.remove(wayPoint);
+ if (wayPoints.size() > 1) {
+ computeTotalLentgh();
+ }
+ }
+
+ /**
+ * remove the waypoint at the given index from the path
+ * @param i the index of the waypoint to remove
+ */
+ @Deprecated
+ public void removeWayPoint(int i) {
+ removeWayPoint(wayPoints.get(i));
+ }
+
+ /**
+ * returns an iterator on the waypoints collection
+ * @return
+ */
+ @Deprecated
+ public Iterator iterator() {
+ return wayPoints.iterator();
+ }
+
+ /**
+ * return the type of path interpolation for this path
+ * @return the path interpolation
+ */
+ @Deprecated
+ public PathInterpolation getPathInterpolation() {
+ return pathInterpolation;
+ }
+
+ /**
+ * sets the path interpolation for this path
+ * @param pathInterpolation
+ */
+ @Deprecated
+ public void setPathInterpolation(PathInterpolation pathInterpolation) {
+ this.pathInterpolation = pathInterpolation;
+ computeTotalLentgh();
+ if (debugNode != null) {
+ Node parent = debugNode.getParent();
+ debugNode.removeFromParent();
+ debugNode.detachAllChildren();
+ debugNode = null;
+ attachDebugNode(parent);
+ }
+ }
+
+ /**
+ * disable the display of the path and the waypoints
+ */
+ @Deprecated
+ public void disableDebugShape() {
+
+ debugNode.detachAllChildren();
+ debugNode = null;
+ assetManager = null;
+ }
+
+ /**
+ * enable the display of the path and the waypoints
+ * @param manager the assetManager
+ * @param rootNode the node where the debug shapes must be attached
+ */
+ @Deprecated
+ public void enableDebugShape(AssetManager manager, Node rootNode) {
+ assetManager = manager;
+ computeTotalLentgh();
+ attachDebugNode(rootNode);
+ }
+
+ /**
+ * Adds an animation pathListener to the path
+ * @param listener the AnimationPathListener to attach
+ */
+ @Deprecated
+ public void addListener(AnimationPathListener listener) {
+ if (listeners == null) {
+ listeners = new ArrayList();
+ }
+ listeners.add(listener);
+ }
+
+ /**
+ * remove the given listener
+ * @param listener the listener to remove
+ */
+ @Deprecated
+ public void removeListener(AnimationPathListener listener) {
+ if (listeners != null) {
+ listeners.remove(listener);
+ }
+ }
+
+ /**
+ * return the number of waypoints of this path
+ * @return
+ */
+ @Deprecated
+ public int getNbWayPoints() {
+ return wayPoints.size();
+ }
+
+ private void triggerWayPointReach(int wayPointIndex) {
+ for (Iterator it = listeners.iterator(); it.hasNext();) {
+ AnimationPathListener listener = it.next();
+ listener.onWayPointReach(this, wayPointIndex);
+ }
+ }
+
+ /**
+ * returns the direction type of the target
+ * @return the direction type
+ */
+ @Deprecated
+ public Direction getDirectionType() {
+ return directionType;
+ }
+
+ /**
+ * Sets the direction type of the target
+ * On each update the direction given to the target can have different behavior
+ * See the Direction Enum for explanations
+ * @param directionType the direction type
+ */
+ @Deprecated
+ public void setDirectionType(Direction directionType) {
+ this.directionType = directionType;
+ }
+
+ /**
+ * Set the lookAt for the target
+ * This can be used only if direction Type is Direction.LookAt
+ * @param lookAt the position to look at
+ * @param upVector the up vector
+ */
+ @Deprecated
+ public void setLookAt(Vector3f lookAt, Vector3f upVector) {
+ this.lookAt = lookAt;
+ this.upVector = upVector;
+ }
+
+ /**
+ * returns the rotation of the target
+ * @return the rotation quaternion
+ */
+ @Deprecated
+ public Quaternion getRotation() {
+ return rotation;
+ }
+
+ /**
+ * sets the rotation of the target
+ * This can be used only if direction Type is Direction.PathAndRotation or Direction.Rotation
+ * With PathAndRotation the target will face the direction of the path multiplied by the given Quaternion.
+ * With Rotation the rotation of the target will be set with the given Quaternion.
+ * @param rotation the rotation quaternion
+ */
+ @Deprecated
+ public void setRotation(Quaternion rotation) {
+ this.rotation = rotation;
+ }
+
+ @Deprecated
+ public float getDuration() {
+ return duration;
+ }
+
+ /**
+ * Sets the duration of the animation
+ * @param duration
+ */
+ @Deprecated
+ public void setDuration(float duration) {
+ this.duration = duration;
+ speed = totalLength / duration;
+ }
+
+ @Deprecated
+ public float getCurveTension() {
+ return curveTension;
+ }
+
+ /**
+ * sets the tension of the curve (only for catmull rom) 0.0 will give a linear curve, 1.0 a round curve
+ * @param curveTension
+ */
+ @Deprecated
+ public void setCurveTension(float curveTension) {
+ this.curveTension = curveTension;
+ computeTotalLentgh();
+ if (debugNode != null) {
+ Node parent = debugNode.getParent();
+ debugNode.removeFromParent();
+ debugNode.detachAllChildren();
+ debugNode = null;
+ attachDebugNode(parent);
+ }
+ }
+
+ /**
+ * Sets the path to be a cycle
+ * @param cycle
+ */
+ @Deprecated
+ public void setCycle(boolean cycle) {
+
+ if (wayPoints.size() >= 2) {
+ if (this.cycle && !cycle) {
+ wayPoints.remove(wayPoints.size() - 1);
+ }
+ if (!this.cycle && cycle) {
+ wayPoints.add(wayPoints.get(0));
+ System.out.println("adding first wp");
+ }
+ this.cycle = cycle;
+ computeTotalLentgh();
+ if (debugNode != null) {
+ Node parent = debugNode.getParent();
+ debugNode.removeFromParent();
+ debugNode.detachAllChildren();
+ debugNode = null;
+ attachDebugNode(parent);
+ }
+ } else {
+ this.cycle = cycle;
+ }
+ }
+
+ @Deprecated
+ public boolean isCycle() {
+ return cycle;
+ }
+
+ /**
+ * returs true is the animation loops
+ * @return
+ */
+ @Deprecated
+ public boolean isLoop() {
+ return loop;
+ }
+
+ /**
+ * Loops the animation
+ * @param loop
+ */
+ @Deprecated
+ public void setLoop(boolean loop) {
+ this.loop = loop;
+ }
+
+ @Override
+ public String toString() {
+ return "AnimationPath{" + "playing=" + playing + "currentWayPoint=" + currentWayPoint + "currentValue=" + currentValue + "wayPoints=" + wayPoints + "debugNode=" + debugNode + "assetManager=" + assetManager + "listeners=" + listeners + "curveDirection=" + curveDirection + "lookAt=" + lookAt + "upVector=" + upVector + "rotation=" + rotation + "duration=" + duration + "segmentsLength=" + segmentsLength + "totalLength=" + totalLength + "CRcontrolPoints=" + CRcontrolPoints + "speed=" + speed + "curveTension=" + curveTension + "loop=" + loop + "cycle=" + cycle + "directionType=" + directionType + "pathInterpolation=" + pathInterpolation + "eps=" + eps + '}';
+ }
+
+
+}
diff --git a/engine/src/core/com/jme3/animation/AnimationPathListener.java b/engine/src/core/com/jme3/animation/AnimationPathListener.java
new file mode 100644
index 000000000..bd157110f
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/AnimationPathListener.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.animation;
+
+/**
+ *
+ * @author Nehon
+ * @deprecated use {@link MotionPathListener}
+ */
+@Deprecated
+public interface AnimationPathListener {
+
+ /**
+ * Triggers every time the target reach a waypoint on the path
+ * @param path the animation path on wich the even has been triggered
+ * @param wayPointIndex the index of the way point reached
+ */
+ public void onWayPointReach(AnimationPath path,int wayPointIndex);
+
+}
diff --git a/engine/src/core/com/jme3/animation/Bone.java b/engine/src/core/com/jme3/animation/Bone.java
new file mode 100644
index 000000000..a08e1b242
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/Bone.java
@@ -0,0 +1,539 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.animation;
+
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.OutputCapsule;
+import com.jme3.export.Savable;
+import com.jme3.math.Matrix3f;
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Node;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * Bone
describes a bone in the bone-weight skeletal animation
+ * system. A bone contains a name and an index, as well as relevant
+ * transformation data.
+ *
+ * @author Kirill Vainer
+ */
+public final class Bone implements Savable {
+
+ private String name;
+ private Bone parent;
+ private final ArrayList children = new ArrayList();
+ /**
+ * If enabled, user can control bone transform with setUserTransforms.
+ * Animation transforms are not applied to this bone when enabled.
+ */
+ private boolean userControl = false;
+ /**
+ * The attachment node.
+ */
+ private Node attachNode;
+ /**
+ * Initial transform is the local bind transform of this bone.
+ * PARENT SPACE -> BONE SPACE
+ */
+ private Vector3f initialPos;
+ private Quaternion initialRot;
+ private Vector3f initialScale = new Vector3f(1.0f, 1.0f, 1.0f);
+ /**
+ * The inverse world bind transform.
+ * BONE SPACE -> MODEL SPACE
+ */
+ private Vector3f worldBindInversePos;
+ private Quaternion worldBindInverseRot;
+ private Vector3f worldBindInverseScale;
+ /**
+ * The local animated transform combined with the local bind transform and parent world transform
+ */
+ private Vector3f localPos = new Vector3f();
+ private Quaternion localRot = new Quaternion();
+ private Vector3f localScale = new Vector3f(1.0f, 1.0f, 1.0f);
+ /**
+ * MODEL SPACE -> BONE SPACE (in animated state)
+ */
+ private Vector3f worldPos = new Vector3f();
+ private Quaternion worldRot = new Quaternion();
+ private Vector3f worldScale = new Vector3f();
+
+ /**
+ * Creates a new bone with the given name.
+ *
+ * @param name Name to give to this bone
+ */
+ public Bone(String name) {
+ this.name = name;
+
+ initialPos = new Vector3f();
+ initialRot = new Quaternion();
+ initialScale = new Vector3f();
+
+ worldBindInversePos = new Vector3f();
+ worldBindInverseRot = new Quaternion();
+ worldBindInverseScale = new Vector3f();
+ }
+
+ /**
+ * Copy constructor. local bind and world inverse bind transforms shallow copied.
+ * @param source
+ */
+ Bone(Bone source) {
+ this.name = source.name;
+
+ userControl = source.userControl;
+
+ initialPos = source.initialPos;
+ initialRot = source.initialRot;
+ initialScale = source.initialScale;
+
+ worldBindInversePos = source.worldBindInversePos;
+ worldBindInverseRot = source.worldBindInverseRot;
+ worldBindInverseScale = source.worldBindInverseScale;
+
+ // parent and children will be assigned manually..
+ }
+
+ /**
+ * Used for binary loading as a Savable; the object must be constructed,
+ * then the parameters usually present in the constructor for this class are
+ * restored from the file the object was saved to.
+ */
+ public Bone() {
+ }
+
+ /**
+ * @return The name of the bone, set in the constructor.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @return The parent bone of this bone, or null if it is a root bone.
+ */
+ public Bone getParent() {
+ return parent;
+ }
+
+ /**
+ * @return All the children bones of this bone.
+ */
+ public ArrayList getChildren() {
+ return children;
+ }
+
+ /**
+ * @return The local position of the bone, relative to the parent bone.
+ */
+ public Vector3f getLocalPosition() {
+ return localPos;
+ }
+
+ /**
+ * @return The local rotation of the bone, relative to the parent bone.
+ */
+ public Quaternion getLocalRotation() {
+ return localRot;
+ }
+
+ /**
+ * @return The local scale of the bone, relative to the parent bone.
+ */
+ public Vector3f getLocalScale() {
+ return localScale;
+ }
+
+ /**
+ * @return The position of the bone in model space.
+ */
+ public Vector3f getModelSpacePosition() {
+ return worldPos;
+ }
+
+ /**
+ * @return The rotation of the bone in model space.
+ */
+ public Quaternion getModelSpaceRotation() {
+ return worldRot;
+ }
+
+ /**
+ * @return The scale of the bone in model space.
+ */
+ public Vector3f getModelSpaceScale() {
+ return worldScale;
+ }
+
+ public Vector3f getWorldBindInversePosition() {
+ return worldBindInversePos;
+ }
+
+ public Quaternion getWorldBindInverseRotation() {
+ return worldBindInverseRot;
+ }
+
+ public Vector3f getWorldBindInverseScale() {
+ return worldBindInverseScale;
+ }
+
+ /**
+ * If enabled, user can control bone transform with setUserTransforms.
+ * Animation transforms are not applied to this bone when enabled.
+ */
+ public void setUserControl(boolean enable) {
+ userControl = enable;
+ }
+
+ /**
+ * Add a new child to this bone. Shouldn't be used by user code.
+ * Can corrupt skeleton.
+ * @param bone
+ */
+ public void addChild(Bone bone) {
+ children.add(bone);
+ bone.parent = this;
+ }
+
+ /**
+ * Updates the world transforms for this bone, and, possibly the attach node if not null.
+ */
+ final void updateWorldVectors() {
+ if (parent != null) {
+
+ //rotation
+ parent.worldRot.mult(localRot, worldRot);
+
+ //scale
+ //For scale parent scale is not taken into account!
+ // worldScale.set(localScale);
+ parent.worldScale.mult(localScale, worldScale);
+
+ //translation
+ //scale and rotation of parent affect bone position
+ parent.worldRot.mult(localPos, worldPos);
+ worldPos.multLocal(parent.worldScale);
+ worldPos.addLocal(parent.worldPos);
+
+
+
+ } else {
+ worldRot.set(localRot);
+ worldPos.set(localPos);
+ worldScale.set(localScale);
+ }
+
+ if (attachNode != null) {
+ attachNode.setLocalTranslation(worldPos);
+ attachNode.setLocalRotation(worldRot);
+ attachNode.setLocalScale(worldScale);
+ }
+ }
+
+ /**
+ * Updates world transforms for this bone and it's children.
+ */
+ final void update() {
+ this.updateWorldVectors();
+
+ for (int i = children.size() - 1; i >= 0; i--) {
+ children.get(i).update();
+ }
+ }
+
+ /**
+ * Saves the current bone state as its binding pose, including its children.
+ */
+ void setBindingPose() {
+ initialPos.set(localPos);
+ initialRot.set(localRot);
+ initialScale.set(localScale);
+
+ if (worldBindInversePos == null) {
+ worldBindInversePos = new Vector3f();
+ worldBindInverseRot = new Quaternion();
+ worldBindInverseScale = new Vector3f();
+ }
+
+ // Save inverse derived position/scale/orientation, used for calculate offset transform later
+ worldBindInversePos.set(worldPos);
+ worldBindInversePos.negateLocal();
+
+ worldBindInverseRot.set(worldRot);
+ worldBindInverseRot.inverseLocal();
+
+ worldBindInverseScale.set(Vector3f.UNIT_XYZ);
+ worldBindInverseScale.divideLocal(worldScale);
+
+ for (Bone b : children) {
+ b.setBindingPose();
+ }
+ }
+
+ /**
+ * Reset the bone and it's children to bind pose.
+ */
+ final void reset() {
+ if (!userControl) {
+ localPos.set(initialPos);
+ localRot.set(initialRot);
+ localScale.set(initialScale);
+ }
+
+ for (int i = children.size() - 1; i >= 0; i--) {
+ children.get(i).reset();
+ }
+ }
+
+ //Temp 3x3 rotation matrix for transform computation
+ Matrix3f rotMat=new Matrix3f();
+ /**
+ * Stores the skinning transform in the specified Matrix4f.
+ * The skinning transform applies the animation of the bone to a vertex.
+ * @param m
+ */
+ void getOffsetTransform(Matrix4f m, Quaternion tmp1, Vector3f tmp2, Vector3f tmp3) {
+
+ //Computing scale
+ Vector3f scale = worldScale.mult(worldBindInverseScale, tmp3);
+
+ //computing rotation
+ Quaternion rotate = worldRot.mult(worldBindInverseRot, tmp1);
+
+ //computing translation
+ //translation depend on rotation and scale
+ Vector3f translate = worldPos.add(rotate.mult(scale.mult(worldBindInversePos, tmp2), tmp2), tmp2);
+
+ //populating the matrix
+ m.loadIdentity();
+ m.setTransform(translate, scale, rotate.toRotationMatrix(rotMat));
+ }
+
+ /**
+ * Set user transform.
+ * @see setUserControl
+ */
+ public void setUserTransforms(Vector3f translation, Quaternion rotation, Vector3f scale) {
+ if (!userControl) {
+ throw new IllegalStateException("User control must be on bone to allow user transforms");
+ }
+
+ localPos.set(initialPos);
+ localRot.set(initialRot);
+ localScale.set(initialScale);
+
+ localPos.addLocal(translation);
+ localRot = localRot.mult(rotation);
+ localScale.addLocal(scale);
+ }
+
+ /**
+ * Must update all bones in skeleton for this to work.
+ * @param translation
+ * @param rotation
+ *///TODO: add scale here ???
+ public void setUserTransformsWorld(Vector3f translation, Quaternion rotation) {
+ if (!userControl) {
+ throw new IllegalStateException("User control must be on bone to allow user transforms");
+ }
+
+ worldPos.set(translation);
+ worldRot.set(rotation);
+ }
+
+ /**
+ * Returns the attachment node.
+ * Attach models and effects to this node to make
+ * them follow this bone's motions.
+ */
+ public Node getAttachmentsNode() {
+ if (attachNode == null) {
+ attachNode = new Node(name + "_attachnode");
+ attachNode.setUserData("AttachedBone", this);
+ }
+ return attachNode;
+ }
+
+ /**
+ * Used internally after model cloning.
+ * @param attachNode
+ */
+ public void setAttachmentsNode(Node attachNode) {
+ this.attachNode = attachNode;
+ }
+
+ /**
+ * Sets the local animation transform of this bone.
+ * Bone is assumed to be in bind pose when this is called.
+ */
+ void setAnimTransforms(Vector3f translation, Quaternion rotation, Vector3f scale) {
+ if (userControl) {
+ return;
+ }
+
+// localPos.addLocal(translation);
+// localRot.multLocal(rotation);
+ //localRot = localRot.mult(rotation);
+
+ localPos.set(initialPos).addLocal(translation);
+ localRot.set(initialRot).multLocal(rotation);
+ if (scale != null) {
+ localScale.set(initialScale).multLocal(scale);
+ }
+ }
+
+ void blendAnimTransforms(Vector3f translation, Quaternion rotation, Vector3f scale, float weight) {
+ if (userControl) {
+ return;
+ }
+
+ TempVars vars = TempVars.get();
+ assert vars.lock();
+
+ Vector3f tmpV = vars.vect1;
+ Vector3f tmpV2 = vars.vect2;
+ Quaternion tmpQ = vars.quat1;
+
+ //location
+ tmpV.set(initialPos).addLocal(translation);
+ localPos.interpolate(tmpV, weight);
+
+ //rotation
+ tmpQ.set(initialRot).multLocal(rotation);
+ localRot.slerp(tmpQ, weight);
+
+ //scale
+ if (scale != null) {
+ tmpV2.set(initialScale).addLocal(translation);
+ localScale.interpolate(tmpV2, weight);
+ }
+
+
+ assert vars.unlock();
+ }
+
+ /**
+ * Sets local bind transform for bone.
+ * Call setBindingPose() after all of the skeleton bones' bind transforms are set to save them.
+ */
+ public void setBindTransforms(Vector3f translation, Quaternion rotation, Vector3f scale) {
+ initialPos.set(translation);
+ initialRot.set(rotation);
+ if (scale != null) {
+ initialScale.set(scale);
+ }
+
+ localPos.set(translation);
+ localRot.set(rotation);
+ if (scale != null) {
+ localScale.set(scale);
+ }
+ }
+
+ void setAnimTransforms(Vector3f translation, Quaternion rotation) {
+ this.setAnimTransforms(translation, rotation, Vector3f.UNIT_XYZ);
+ }
+
+ public void setBindTransforms(Vector3f translation, Quaternion rotation) {
+ this.setBindTransforms(translation, rotation, Vector3f.UNIT_XYZ);
+ }
+
+ private String toString(int depth) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < depth; i++) {
+ sb.append('-');
+ }
+
+ sb.append(name).append(" bone\n");
+ for (Bone child : children) {
+ sb.append(child.toString(depth + 1));
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public String toString() {
+ return this.toString(0);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule input = im.getCapsule(this);
+
+ name = input.readString("name", null);
+ initialPos = (Vector3f) input.readSavable("initialPos", null);
+ initialRot = (Quaternion) input.readSavable("initialRot", null);
+ initialScale = (Vector3f) input.readSavable("initialScale", new Vector3f(1.0f, 1.0f, 1.0f));
+ attachNode = (Node) input.readSavable("attachNode", null);
+
+ localPos.set(initialPos);
+ localRot.set(initialRot);
+
+ ArrayList childList = input.readSavableArrayList("children", null);
+ for (int i = childList.size() - 1; i >= 0; i--) {
+ this.addChild(childList.get(i));
+ }
+
+ // NOTE: Parent skeleton will call update() then setBindingPose()
+ // after Skeleton has been de-serialized.
+ // Therefore, worldBindInversePos and worldBindInverseRot
+ // will be reconstructed based on that information.
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule output = ex.getCapsule(this);
+
+ output.write(name, "name", null);
+ output.write(attachNode, "attachNode", null);
+ output.write(initialPos, "initialPos", null);
+ output.write(initialRot, "initialRot", null);
+ output.write(initialScale, "initialScale", new Vector3f(1.0f, 1.0f, 1.0f));
+ output.writeSavableArrayList(children, "children", null);
+ }
+
+ public Vector3f getInitialPos() {
+ return initialPos;
+ }
+
+ public Quaternion getInitialRot() {
+ return initialRot;
+ }
+}
diff --git a/engine/src/core/com/jme3/animation/BoneAnimation.java b/engine/src/core/com/jme3/animation/BoneAnimation.java
new file mode 100644
index 000000000..cbbbe71fb
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/BoneAnimation.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.animation;
+
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.OutputCapsule;
+import com.jme3.export.Savable;
+import java.io.IOException;
+import java.util.BitSet;
+
+/**
+ * Bone animation updates each of it's tracks with the skeleton and time
+ * to apply the animation.
+ */
+public final class BoneAnimation implements Savable {
+
+ private static final long serialVersionUID = 1L;
+
+ private String name;
+ private float length;
+
+ private BoneTrack[] tracks;
+
+ public BoneAnimation(String name, float length){
+ this.name = name;
+ this.length = length;
+ }
+
+ /**
+ * Serialization-only. Do not use.
+ */
+ public BoneAnimation(){
+ }
+
+ public String getName(){
+ return name;
+ }
+
+ public float getLength(){
+ return length;
+ }
+
+ public void setTracks(BoneTrack[] tracks){
+ this.tracks = tracks;
+ }
+
+ public BoneTrack[] getTracks(){
+ return tracks;
+ }
+
+ void setTime(float time, Skeleton skeleton, float weight, BitSet affectedBones){
+ for (int i = 0; i < tracks.length; i++){
+ if (affectedBones == null
+ || affectedBones.get(tracks[i].getTargetBoneIndex()))
+ tracks[i].setTime(time, skeleton, weight);
+ }
+ }
+
+// void setTime(float time, Skeleton skeleton, float weight, ArrayList affectedBones){
+// for (int i = 0; i < tracks.length; i++){
+// if (affectedBones == null
+// || affectedBones.contains(tracks[i].getTargetBoneIndex()))
+// tracks[i].setTime(time, skeleton, weight);
+// }
+// }
+
+ public String toString(){
+ return "BoneAnim[name="+name+", length="+length+"]";
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule out = e.getCapsule(this);
+ out.write(name, "name", null);
+ out.write(length, "length", 0f);
+ out.write(tracks, "tracks", null);
+ }
+
+ public void read(JmeImporter i) throws IOException {
+ InputCapsule in = i.getCapsule(this);
+ name = in.readString("name", null);
+ length = in.readFloat("length", 0f);
+
+ Savable[] sav = in.readSavableArray("tracks", null);
+ if (sav != null){
+ tracks = new BoneTrack[sav.length];
+ System.arraycopy(sav, 0, tracks, 0, sav.length);
+ }
+ }
+
+}
diff --git a/engine/src/core/com/jme3/animation/BoneTrack.java b/engine/src/core/com/jme3/animation/BoneTrack.java
new file mode 100644
index 000000000..14f2974be
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/BoneTrack.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.animation;
+
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.OutputCapsule;
+import com.jme3.export.Savable;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+
+/**
+ * Contains a list of transforms and times for each keyframe.
+ */
+public final class BoneTrack implements Savable {
+
+ /**
+ * Bone index in the skeleton which this track effects.
+ */
+ private int targetBoneIndex;
+ /**
+ * Transforms and times for track.
+ */
+ private CompactVector3Array translations;
+ private CompactQuaternionArray rotations;
+ private CompactVector3Array scales;
+ private float[] times;
+ // temp vectors for interpolation
+ private transient final Vector3f tempV = new Vector3f();
+ private transient final Quaternion tempQ = new Quaternion();
+ private transient final Vector3f tempS = new Vector3f();
+ private transient final Vector3f tempV2 = new Vector3f();
+ private transient final Quaternion tempQ2 = new Quaternion();
+ private transient final Vector3f tempS2 = new Vector3f();
+
+ /**
+ * Serialization-only. Do not use.
+ */
+ public BoneTrack() {
+ }
+
+ public BoneTrack(int targetBoneIndex, float[] times, Vector3f[] translations, Quaternion[] rotations) {
+ this.targetBoneIndex = targetBoneIndex;
+ this.setKeyframes(times, translations, rotations);
+ }
+
+ public BoneTrack(int targetBoneIndex, float[] times, Vector3f[] translations, Quaternion[] rotations, Vector3f[] scales) {
+ this(targetBoneIndex, times, translations, rotations);
+ this.setKeyframes(times, translations, rotations);
+ }
+
+ public BoneTrack(int targetBoneIndex) {
+ this.targetBoneIndex = targetBoneIndex;
+ }
+
+ public int getTargetBoneIndex() {
+ return targetBoneIndex;
+ }
+
+ public Quaternion[] getRotations() {
+ return rotations.toObjectArray();
+ }
+
+ public Vector3f[] getScales() {
+ return scales == null ? null : scales.toObjectArray();
+ }
+
+ public float[] getTimes() {
+ return times;
+ }
+
+ public Vector3f[] getTranslations() {
+ return translations.toObjectArray();
+ }
+
+ public void setKeyframes(float[] times, Vector3f[] translations, Quaternion[] rotations) {
+ if (times.length == 0) {
+ throw new RuntimeException("BoneTrack with no keyframes!");
+ }
+
+ assert times.length == translations.length && times.length == rotations.length;
+
+ this.times = times;
+ this.translations = new CompactVector3Array();
+ this.translations.add(translations);
+ this.translations.freeze();
+ this.rotations = new CompactQuaternionArray();
+ this.rotations.add(rotations);
+ this.rotations.freeze();
+ }
+
+ public void setKeyframes(float[] times, Vector3f[] translations, Quaternion[] rotations, Vector3f[] scales) {
+ this.setKeyframes(times, translations, rotations);
+ assert times.length == scales.length;
+ if (scales != null) {
+ this.scales = new CompactVector3Array();
+ this.scales.add(scales);
+ this.scales.freeze();
+ }
+ }
+
+ /**
+ * Modify the bone which this track modifies in the skeleton to contain
+ * the correct animation transforms for a given time.
+ * The transforms can be interpolated in some method from the keyframes.
+ */
+ public void setTime(float time, Skeleton skeleton, float weight) {
+ Bone target = skeleton.getBone(targetBoneIndex);
+
+ int lastFrame = times.length - 1;
+ if (time < 0 || lastFrame == 0) {
+ rotations.get(0, tempQ);
+ translations.get(0, tempV);
+ if (scales != null) {
+ scales.get(0, tempS);
+ }
+ } else if (time >= times[lastFrame]) {
+ rotations.get(lastFrame, tempQ);
+ translations.get(lastFrame, tempV);
+ if (scales != null) {
+ scales.get(lastFrame, tempS);
+ }
+ } else {
+ int startFrame = 0;
+ int endFrame = 1;
+ // use lastFrame so we never overflow the array
+ int i;
+ for (i = 0; i < lastFrame && times[i] < time; i++) {
+ startFrame = i;
+ }
+ //i is now startFrame+1;
+ endFrame = i ;
+
+ float blend = (time - times[startFrame])
+ / (times[endFrame] - times[startFrame]);
+
+ rotations.get(startFrame, tempQ);
+ translations.get(startFrame, tempV);
+ if (scales != null) {
+ scales.get(startFrame, tempS);
+ }
+ rotations.get(endFrame, tempQ2);
+ translations.get(endFrame, tempV2);
+ if (scales != null) {
+ scales.get(endFrame, tempS2);
+ }
+ tempQ.slerp(tempQ2, blend);
+ tempV.interpolate(tempV2, blend);
+ tempS.interpolate(tempS2, blend);
+ }
+
+ if (weight != 1f) {
+// tempQ.slerp(Quaternion.IDENTITY, 1f - weight);
+// tempV.multLocal(weight);
+ target.blendAnimTransforms(tempV, tempQ, scales != null?tempS:null, weight);
+// target.setAnimTransforms(tempV, tempQ);
+ } else {
+ target.setAnimTransforms(tempV, tempQ, scales != null?tempS:null);
+ }
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(targetBoneIndex, "boneIndex", 0);
+ oc.write(translations, "translations", null);
+ oc.write(rotations, "rotations", null);
+ oc.write(times, "times", null);
+ oc.write(scales, "scales", null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ targetBoneIndex = ic.readInt("boneIndex", 0);
+
+ translations = (CompactVector3Array) ic.readSavable("translations", null);
+
+ rotations = (CompactQuaternionArray) ic.readSavable("rotations", null);
+ times = ic.readFloatArray("times", null);
+ scales = (CompactVector3Array) ic.readSavable("scales", null);
+
+
+ //Backward compatibility for old j3o files generated before revision 6807
+ if (translations == null) {
+ Savable[] sav = ic.readSavableArray("translations", null);
+ if (sav != null) {
+ translations = new CompactVector3Array();
+ Vector3f[] transCopy = new Vector3f[sav.length];
+ System.arraycopy(sav, 0, transCopy, 0, sav.length);
+ translations.add(transCopy);
+ translations.freeze();
+ }
+ }
+ if (rotations == null) {
+ Savable[] sav = ic.readSavableArray("rotations", null);
+ if (sav != null) {
+ rotations = new CompactQuaternionArray();
+ Quaternion[] rotCopy = new Quaternion[sav.length];
+ System.arraycopy(sav, 0, rotCopy, 0, sav.length);
+ rotations.add(rotCopy);
+ rotations.freeze();
+ }
+ }
+
+
+ }
+}
diff --git a/engine/src/core/com/jme3/animation/CompactArray.java b/engine/src/core/com/jme3/animation/CompactArray.java
new file mode 100644
index 000000000..cf24844b5
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/CompactArray.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.animation;
+
+import java.lang.reflect.Array;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * Object is indexed and stored in primitive float[]
+ * @author Lim, YongHoon
+ * @param
+ */
+public abstract class CompactArray {
+
+ private Map indexPool = new HashMap();
+
+ protected int[] index;
+ protected float[] array;
+ private boolean invalid;
+
+ public CompactArray() {
+ }
+
+ /**
+ * create array using serialized data
+ * @param compressedArray
+ * @param index
+ */
+ public CompactArray(float[] compressedArray, int[] index) {
+ this.array = compressedArray;
+ this.index = index;
+ }
+
+ /**
+ * Add objects.
+ * They are serialized automatically when get() method is called.
+ * @param objArray
+ */
+ public void add(T... objArray) {
+ if (objArray == null || objArray.length == 0)
+ return;
+ invalid = true;
+ int base = 0;
+ if (index == null) {
+ index = new int[objArray.length];
+ } else {
+ if (indexPool.isEmpty()) {
+ throw new RuntimeException("Internal is already fixed");
+ }
+ base = index.length;
+
+ int[] tmp = new int[base + objArray.length];
+ System.arraycopy(index, 0, tmp, 0, index.length);
+ index = tmp;
+ //index = Arrays.copyOf(index, base+objArray.length);
+ }
+ for (int j=0; j < objArray.length; j++) {
+ T obj = objArray[j];
+ if (obj == null) {
+ index[base+j] = -1;
+ } else {
+ Integer i = indexPool.get(obj);
+ if (i == null) {
+ i = indexPool.size();
+ indexPool.put(obj, i);
+ }
+ index[base+j] = i;
+ }
+ }
+ }
+
+ /**
+ * release objects.
+ * add() method call is not allowed anymore.
+ */
+ public void freeze() {
+ serialize();
+ indexPool.clear();
+ }
+
+ /**
+ * @param index
+ * @param value
+ */
+ public final void set(int index, T value) {
+ int j = getCompactIndex(index);
+ serialize(j, value);
+ }
+
+ public final T get(int index, T store) {
+ serialize();
+ int j = getCompactIndex(index);
+ return deserialize(j, store);
+ }
+
+ public final float[] getSerializedData() {
+ serialize();
+ return array;
+ }
+
+ public final void serialize() {
+ if (invalid) {
+ int newSize = indexPool.size()*getTupleSize();
+ if (array == null || Array.getLength(array) < newSize) {
+ array = ensureCapacity(array, newSize);
+ for (Map.Entry entry : indexPool.entrySet()) {
+ int i = entry.getValue();
+ T obj = entry.getKey();
+ serialize(i, obj);
+ }
+ }
+ invalid = false;
+ }
+ }
+
+ /**
+ * @return compacted array's primitive size
+ */
+ protected final int getSerializedSize() {
+ return Array.getLength(getSerializedData());
+ }
+
+ protected float[] ensureCapacity(float[] arr, int size) {
+ if (arr == null) {
+ return new float[size];
+ } else if (arr.length >= size) {
+ return arr;
+ } else {
+ float[] tmp = new float[size];
+ System.arraycopy(arr, 0, tmp, 0, arr.length);
+ return tmp;
+ //return Arrays.copyOf(arr, size);
+ }
+ }
+
+ public final int[] getIndex(T... objArray) {
+ int[] index = new int[objArray.length];
+ for (int i = 0; i < index.length; i++) {
+ T obj = objArray[i];
+ index[i] = obj!=null? indexPool.get(obj): -1;
+ }
+ return index;
+ }
+
+ /**
+ * @param objIndex
+ * @return object index in the compacted object array
+ */
+ public int getCompactIndex(int objIndex) {
+ return index!=null ? index[objIndex]: objIndex;
+ }
+
+ /**
+ * @return uncompressed object size
+ */
+ public final int getTotalObjectSize() {
+ assert getSerializedSize()%getTupleSize() == 0;
+ return index!=null? index.length: getSerializedSize()/getTupleSize();
+ }
+
+ /**
+ * @return compressed object size
+ */
+ public final int getCompactObjectSize() {
+ assert getSerializedSize()%getTupleSize() == 0;
+ return getSerializedSize()/getTupleSize();
+ }
+
+ /**
+ * decompress and return object array
+ * @return decompress and return object array
+ */
+ public final T[] toObjectArray() {
+ try {
+ T[] compactArr = (T[]) Array.newInstance(getElementClass(), getSerializedSize()/getTupleSize());
+ for (int i = 0; i < compactArr.length; i++) {
+ compactArr[i] = getElementClass().newInstance();
+ deserialize(i, compactArr[i]);
+ }
+
+ T[] objArr = (T[]) Array.newInstance(getElementClass(), getTotalObjectSize());
+ for (int i = 0; i < objArr.length; i++) {
+ int compactIndex = getCompactIndex(i);
+ objArr[i] = compactArr[compactIndex];
+ }
+ return objArr;
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+
+ /**
+ * serialize object
+ * @param compactIndex compacted object index
+ * @param store
+ */
+ protected abstract void serialize(int compactIndex, T store);
+
+ /**
+ * deserialize object
+ * @param compactIndex compacted object index
+ * @param store
+ * @return
+ */
+ protected abstract T deserialize(int compactIndex, T store);
+
+ /**
+ * serialized size of one object element
+ * @return
+ */
+ protected abstract int getTupleSize();
+
+ protected abstract Class getElementClass();
+
+}
diff --git a/engine/src/core/com/jme3/animation/CompactQuaternionArray.java b/engine/src/core/com/jme3/animation/CompactQuaternionArray.java
new file mode 100644
index 000000000..da4b7a1d4
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/CompactQuaternionArray.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.animation;
+
+import java.io.IOException;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.export.Savable;
+import com.jme3.math.Quaternion;
+
+/**
+ * Serialize and compress Quaternion[] by indexing same values
+ * It is converted to float[]
+ * @author Lim, YongHoon
+ */
+public class CompactQuaternionArray extends CompactArray implements Savable {
+
+ public CompactQuaternionArray() {
+ }
+
+ public CompactQuaternionArray(float[] dataArray, int[] index) {
+ super(dataArray, index);
+ }
+
+ @Override
+ protected final int getTupleSize() {
+ return 4;
+ }
+
+ @Override
+ protected final Class getElementClass() {
+ return Quaternion.class;
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ serialize();
+ OutputCapsule out = ex.getCapsule(this);
+ out.write(array, "array", null);
+ out.write(index, "index", null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule in = im.getCapsule(this);
+ array = in.readFloatArray("array", null);
+ index = in.readIntArray("index", null);
+ }
+
+ @Override
+ protected void serialize(int i, Quaternion store) {
+ int j = i*getTupleSize();
+ array[j] = store.getX();
+ array[j+1] = store.getY();
+ array[j+2] = store.getZ();
+ array[j+3] = store.getW();
+ }
+
+ @Override
+ protected Quaternion deserialize(int i, Quaternion store) {
+ int j = i*getTupleSize();
+ store.set(array[j], array[j+1], array[j+2], array[j+3]);
+ return store;
+ }
+}
diff --git a/engine/src/core/com/jme3/animation/CompactVector3Array.java b/engine/src/core/com/jme3/animation/CompactVector3Array.java
new file mode 100644
index 000000000..af54aa73d
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/CompactVector3Array.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.animation;
+
+import java.io.IOException;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.export.Savable;
+import com.jme3.math.Vector3f;
+
+/**
+ * Serialize and compress Vector3f[] by indexing same values
+ * @author Lim, YongHoon
+ */
+public class CompactVector3Array extends CompactArray implements Savable {
+
+ public CompactVector3Array() {
+ }
+
+ public CompactVector3Array(float[] dataArray, int[] index) {
+ super(dataArray, index);
+ }
+
+ @Override
+ protected final int getTupleSize() {
+ return 3;
+ }
+
+ @Override
+ protected final Class getElementClass() {
+ return Vector3f.class;
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ serialize();
+ OutputCapsule out = ex.getCapsule(this);
+ out.write(array, "array", null);
+ out.write(index, "index", null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule in = im.getCapsule(this);
+ array = in.readFloatArray("array", null);
+ index = in.readIntArray("index", null);
+ }
+
+ @Override
+ protected void serialize(int i, Vector3f store) {
+ int j = i*getTupleSize();
+ array[j] = store.getX();
+ array[j+1] = store.getY();
+ array[j+2] = store.getZ();
+ }
+
+ @Override
+ protected Vector3f deserialize(int i, Vector3f store) {
+ int j = i*getTupleSize();
+ store.set(array[j], array[j+1], array[j+2]);
+ return store;
+ }
+}
\ No newline at end of file
diff --git a/engine/src/core/com/jme3/animation/LoopMode.java b/engine/src/core/com/jme3/animation/LoopMode.java
new file mode 100644
index 000000000..b74461e88
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/LoopMode.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.animation;
+
+/**
+ * LoopMode
determines how animations repeat, or if they
+ * do not repeat.
+ */
+public enum LoopMode {
+ /**
+ * The animation will play repeatedly, when it reaches the end
+ * the animation will play again from the beginning, and so on.
+ */
+ Loop,
+
+ /**
+ * The animation will not loop. It will play until the last frame, and then
+ * freeze at that frame. It is possible to decide to play a new animation
+ * when that happens by using a AnimEventListener.
+ */
+ DontLoop,
+
+ /**
+ * The animation will cycle back and forth. When reaching the end, the
+ * animation will play backwards from the last frame until it reaches
+ * the first frame.
+ */
+ Cycle,
+
+}
diff --git a/engine/src/core/com/jme3/animation/MeshAnimation.java b/engine/src/core/com/jme3/animation/MeshAnimation.java
new file mode 100644
index 000000000..3322d4ace
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/MeshAnimation.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.animation;
+
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.OutputCapsule;
+import com.jme3.export.Savable;
+import com.jme3.scene.Mesh;
+import java.io.IOException;
+import java.io.Serializable;
+
+public class MeshAnimation implements Serializable, Savable {
+
+ private static final long serialVersionUID = 1L;
+
+ private String name;
+ private float length;
+ private Track[] tracks;
+
+ public MeshAnimation(String name, float length){
+ this.name = name;
+ this.length = length;
+ }
+
+ public String getName(){
+ return name;
+ }
+
+ public float getLength(){
+ return length;
+ }
+
+ public void setTracks(Track[] tracks){
+ this.tracks = tracks;
+ }
+
+ public Track[] getTracks(){
+ return tracks;
+ }
+
+ public void setTime(float time, Mesh[] targets, float weight){
+ for (int i = 0; i < tracks.length; i++){
+ tracks[i].setTime(time, targets, weight);
+ }
+ }
+
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule out = e.getCapsule(this);
+ out.write(name, "name", "");
+ out.write(length, "length", -1f);
+ out.write(tracks, "tracks", null);
+ }
+
+
+ public void read(JmeImporter i) throws IOException {
+ InputCapsule in = i.getCapsule(this);
+ name = in.readString("name", "");
+ length = in.readFloat("length", -1f);
+ tracks = (Track[]) in.readSavableArray("tracks", null);
+ }
+
+}
diff --git a/engine/src/core/com/jme3/animation/MeshAnimationLoader.java b/engine/src/core/com/jme3/animation/MeshAnimationLoader.java
new file mode 100644
index 000000000..354430d04
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/MeshAnimationLoader.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.animation;
+
+//import static com.jmex.model.XMLUtil.getAttribute;
+//import static com.jmex.model.XMLUtil.getIntAttribute;
+//
+//import java.util.ArrayList;
+//import java.util.List;
+//import java.util.Map;
+//
+//import org.w3c.dom.Node;
+//
+//import com.jme.math.Vector3f;
+//import com.jmex.model.XMLUtil;
+//import com.jmex.model.ogrexml.anim.PoseTrack.PoseFrame;
+
+/**
+ * Utility class used by OgreLoader to load poses and mesh animations.
+ */
+public class MeshAnimationLoader {
+
+// public static void loadMeshAnimations(Node animationsNode, List poseList, OgreMesh sharedgeom, List submeshes, Map animations){
+// Node animationNode = animationsNode.getFirstChild();
+// while (animationNode != null){
+// if (animationNode.getNodeName().equals("animation")){
+// MeshAnimation mAnim =
+// loadMeshAnimation(animationNode, poseList, sharedgeom, submeshes);
+//
+// Animation anim = animations.get(mAnim.getName());
+// if (anim != null){
+// anim.setMeshAnimation(mAnim);
+// }else{
+// anim = new Animation(null, mAnim);
+// animations.put(anim.getName(), anim);
+// }
+// }
+// animationNode = animationNode.getNextSibling();
+// }
+//
+//// Map> trimeshPoses = new HashMap>();
+////
+//// // find the poses for each mesh
+//// for (Pose p : poses){
+//// List poseList = trimeshPoses.get(p.getTarget());
+//// if (poseList == null){
+//// poseList = new ArrayList();
+//// trimeshPoses.put(p.getTarget(), poseList);
+//// }
+////
+//// poseList.add(p);
+//// }
+////
+//// for (Map.Entry> poseEntry: trimeshPoses){
+//// PoseController
+//// }
+// }
+//
+// public static MeshAnimation loadMeshAnimation(Node animationNode, List poseList, OgreMesh sharedgeom, List submeshes){
+// String name = XMLUtil.getAttribute(animationNode, "name");
+// float length = XMLUtil.getFloatAttribute(animationNode, "length");
+//
+// MeshAnimation anim = new MeshAnimation(name, length);
+// List tracks = new ArrayList();
+//
+// Node tracksNode = XMLUtil.getChildNode(animationNode, "tracks");
+// if (tracksNode != null){
+// Node trackNode = tracksNode.getFirstChild();
+// while (trackNode != null){
+// if (trackNode.getNodeName().equals("track")){
+// int targetMeshIndex;
+// if (XMLUtil.getAttribute(trackNode, "target").equals("mesh")){
+// targetMeshIndex = -1;
+// }else{
+// if (XMLUtil.getAttribute(trackNode, "index") == null)
+// targetMeshIndex = 0;
+// else
+// targetMeshIndex = getIntAttribute(trackNode, "index");
+// }
+//
+// if (XMLUtil.getAttribute(trackNode, "type").equals("pose")){
+// PoseTrack pt = loadPoseTrack(trackNode, targetMeshIndex, poseList);
+// tracks.add(pt);
+// }else{
+// throw new UnsupportedOperationException("Morph animations not supported!");
+// }
+// }
+//
+// trackNode = trackNode.getNextSibling();
+// }
+// }
+//
+// anim.setTracks(tracks.toArray(new Track[0]));
+//
+// return anim;
+// }
+//
+// public static List loadPoses(Node posesNode, OgreMesh sharedgeom, List submeshes){
+// List poses = new ArrayList();
+// Node poseNode = posesNode.getFirstChild();
+// while (poseNode != null){
+// if (poseNode.getNodeName().equals("pose")){
+// int targetMeshIndex = 0;
+// if (getAttribute(poseNode, "target").equals("mesh")){
+// targetMeshIndex = -1;
+// }else{
+// if (getAttribute(poseNode, "index") == null)
+// targetMeshIndex = 0;
+// else
+// targetMeshIndex = getIntAttribute(poseNode, "index");
+// }
+//
+// Pose p = MeshAnimationLoader.loadPose(poseNode, targetMeshIndex);
+// poses.add(p);
+// }
+//
+// poseNode = poseNode.getNextSibling();
+// }
+//
+// return poses;
+// }
+//
+// public static Pose loadPose(Node poseNode, int targetMeshIndex){
+// String name = XMLUtil.getAttribute(poseNode, "name");
+//
+// List offsets = new ArrayList();
+// List indices = new ArrayList();
+//
+// Node poseoffsetNode = poseNode.getFirstChild();
+// while (poseoffsetNode != null){
+// if (poseoffsetNode.getNodeName().equals("poseoffset")){
+// int vertIndex = XMLUtil.getIntAttribute(poseoffsetNode, "index");
+// Vector3f offset = new Vector3f();
+// offset.x = XMLUtil.getFloatAttribute(poseoffsetNode, "x");
+// offset.y = XMLUtil.getFloatAttribute(poseoffsetNode, "y");
+// offset.z = XMLUtil.getFloatAttribute(poseoffsetNode, "z");
+//
+// offsets.add(offset);
+// indices.add(vertIndex);
+// }
+//
+// poseoffsetNode = poseoffsetNode.getNextSibling();
+// }
+//
+// int[] indicesArray = new int[indices.size()];
+// for (int i = 0; i < indicesArray.length; i++){
+// indicesArray[i] = indices.get(i);
+// }
+//
+// Pose pose = new Pose(name,
+// targetMeshIndex,
+// offsets.toArray(new Vector3f[0]),
+// indicesArray);
+//
+// return pose;
+// }
+//
+// public static PoseTrack loadPoseTrack(Node trackNode, int targetMeshIndex, List posesList){
+// List times = new ArrayList();
+// List frames = new ArrayList();
+//
+// Node keyframesNode = XMLUtil.getChildNode(trackNode, "keyframes");
+// Node keyframeNode = keyframesNode.getFirstChild();
+// while (keyframeNode != null){
+// if (keyframeNode.getNodeName().equals("keyframe")){
+// float time = XMLUtil.getFloatAttribute(keyframeNode, "time");
+// List poses = new ArrayList();
+// List weights = new ArrayList();
+//
+// Node poserefNode = keyframeNode.getFirstChild();
+// while (poserefNode != null){
+// if (poserefNode.getNodeName().equals("poseref")){
+// int poseindex = XMLUtil.getIntAttribute(poserefNode, "poseindex");
+// poses.add(posesList.get(poseindex));
+// float weight = XMLUtil.getFloatAttribute(poserefNode, "influence");
+// weights.add(weight);
+// }
+//
+// poserefNode = poserefNode.getNextSibling();
+// }
+//
+// // convert poses and weights to arrays and create a PoseFrame
+// float[] weightsArray = new float[weights.size()];
+// for (int i = 0; i < weightsArray.length; i++){
+// weightsArray[i] = weights.get(i);
+// }
+// PoseFrame frame = new PoseFrame(poses.toArray(new Pose[0]), weightsArray);
+//
+// times.add(time);
+// frames.add(frame);
+// }
+//
+// keyframeNode = keyframeNode.getNextSibling();
+// }
+//
+// // convert times and frames to arrays and write to the track
+// float[] timesArray = new float[times.size()];
+// for (int i = 0; i < timesArray.length; i++){
+// timesArray[i] = times.get(i);
+// }
+//
+// PoseTrack track = new PoseTrack(targetMeshIndex,
+// timesArray,
+// frames.toArray(new PoseFrame[0]));
+//
+// return track;
+// }
+
+}
diff --git a/engine/src/core/com/jme3/animation/Pose.java b/engine/src/core/com/jme3/animation/Pose.java
new file mode 100644
index 000000000..e6a4e443b
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/Pose.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.animation;
+
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.OutputCapsule;
+import com.jme3.export.Savable;
+import com.jme3.math.Vector3f;
+import com.jme3.util.BufferUtils;
+import java.io.IOException;
+import java.io.Serializable;
+import java.nio.FloatBuffer;
+
+/**
+ * A pose is a list of offsets that say where a mesh verticles should be for this pose.
+ */
+public final class Pose implements Serializable, Savable {
+
+ private static final long serialVersionUID = 1L;
+
+ private String name;
+ private int targetMeshIndex;
+
+ private Vector3f[] offsets;
+ private int[] indices;
+
+ private transient final Vector3f tempVec = new Vector3f();
+ private transient final Vector3f tempVec2 = new Vector3f();
+
+ public Pose(String name, int targetMeshIndex, Vector3f[] offsets, int[] indices){
+ this.name = name;
+ this.targetMeshIndex = targetMeshIndex;
+ this.offsets = offsets;
+ this.indices = indices;
+ }
+
+ public int getTargetMeshIndex(){
+ return targetMeshIndex;
+ }
+
+
+ /**
+ * Applies the offsets of this pose to the vertex buffer given by the blend factor.
+ *
+ * @param blend Blend factor, 0 = no change to vert buf, 1 = apply full offsets
+ * @param vertbuf Vertex buffer to apply this pose to
+ */
+ public void apply(float blend, FloatBuffer vertbuf){
+ for (int i = 0; i < indices.length; i++){
+ Vector3f offset = offsets[i];
+ int vertIndex = indices[i];
+
+ tempVec.set(offset).multLocal(blend);
+
+ // aquire vert
+ BufferUtils.populateFromBuffer(tempVec2, vertbuf, vertIndex);
+
+ // add offset multiplied by factor
+ tempVec2.addLocal(tempVec);
+
+ // write modified vert
+ BufferUtils.setInBuffer(tempVec2, vertbuf, vertIndex);
+ }
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule out = e.getCapsule(this);
+ out.write(name, "name", "");
+ out.write(targetMeshIndex, "meshIndex", -1);
+ out.write(offsets, "offsets", null);
+ out.write(indices, "indices", null);
+ }
+
+ public void read(JmeImporter i) throws IOException {
+ InputCapsule in = i.getCapsule(this);
+ name = in.readString("name", "");
+ targetMeshIndex = in.readInt("meshIndex", -1);
+ offsets = (Vector3f[]) in.readSavableArray("offsets", null);
+ indices = in.readIntArray("indices", null);
+ }
+}
diff --git a/engine/src/core/com/jme3/animation/PoseTrack.java b/engine/src/core/com/jme3/animation/PoseTrack.java
new file mode 100644
index 000000000..484a8ef3e
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/PoseTrack.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.animation;
+
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.OutputCapsule;
+import com.jme3.export.Savable;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Type;
+import java.io.IOException;
+import java.io.Serializable;
+import java.nio.FloatBuffer;
+
+/**
+ * A single track of pose animation associated with a certain mesh.
+ */
+public final class PoseTrack extends Track {
+
+ private static final long serialVersionUID = 1L;
+
+ private PoseFrame[] frames;
+ private float[] times;
+
+ public static class PoseFrame implements Savable {
+
+ private static final long serialVersionUID = 1L;
+
+ Pose[] poses;
+ float[] weights;
+
+ public PoseFrame(Pose[] poses, float[] weights){
+ this.poses = poses;
+ this.weights = weights;
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule out = e.getCapsule(this);
+ out.write(poses, "poses", null);
+ out.write(weights, "weights", null);
+ }
+
+ public void read(JmeImporter i) throws IOException {
+ InputCapsule in = i.getCapsule(this);
+ poses = (Pose[]) in.readSavableArray("poses", null);
+ weights = in.readFloatArray("weights", null);
+ }
+ }
+
+ public PoseTrack(int targetMeshIndex, float[] times, PoseFrame[] frames){
+ super(targetMeshIndex);
+ this.times = times;
+ this.frames = frames;
+ }
+
+ private void applyFrame(Mesh target, int frameIndex, float weight){
+ PoseFrame frame = frames[frameIndex];
+ VertexBuffer pb = target.getBuffer(Type.Position);
+ for (int i = 0; i < frame.poses.length; i++){
+ Pose pose = frame.poses[i];
+ float poseWeight = frame.weights[i] * weight;
+
+ pose.apply(poseWeight, (FloatBuffer) pb.getData());
+ }
+
+ // force to re-upload data to gpu
+ pb.updateData(pb.getData());
+ }
+
+ public void setTime(float time, Mesh[] targets, float weight) {
+ Mesh target = targets[targetMeshIndex];
+ if (time < times[0]){
+ applyFrame(target, 0, weight);
+ }else if (time > times[times.length-1]){
+ applyFrame(target, times.length-1, weight);
+ } else{
+ int startFrame = 0;
+ for (int i = 0; i < times.length; i++){
+ if (times[i] < time)
+ startFrame = i;
+ }
+
+ int endFrame = startFrame + 1;
+ float blend = (time - times[startFrame]) / (times[endFrame] - times[startFrame]);
+ applyFrame(target, startFrame, blend * weight);
+ applyFrame(target, endFrame, (1f-blend) * weight);
+ }
+ }
+
+ @Override
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule out = e.getCapsule(this);
+ out.write(frames, "frames", null);
+ out.write(times, "times", null);
+ }
+
+ @Override
+ public void read(JmeImporter i) throws IOException {
+ InputCapsule in = i.getCapsule(this);
+ frames = (PoseFrame[]) in.readSavableArray("frames", null);
+ times = in.readFloatArray("times", null);
+ }
+}
diff --git a/engine/src/core/com/jme3/animation/Skeleton.java b/engine/src/core/com/jme3/animation/Skeleton.java
new file mode 100644
index 000000000..add2f18be
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/Skeleton.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.animation;
+
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.OutputCapsule;
+import com.jme3.export.Savable;
+import com.jme3.math.Matrix4f;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A skeleton is a hierarchy of bones.
+ * Skeleton updates the world transforms to reflect the current local
+ * animated matrixes.
+ */
+public final class Skeleton implements Savable {
+
+ private Bone[] rootBones;
+ private Bone[] boneList;
+ /**
+ * Contains the skinning matrices, multiplying it by a vertex effected by a bone
+ * will cause it to go to the animated position.
+ */
+ private transient Matrix4f[] skinningMatrixes;
+
+ /**
+ * Creates a skeleton from a bone list. The root bone is found automatically.
+ * @param boneList
+ */
+ public Skeleton(Bone[] boneList) {
+ this.boneList = boneList;
+
+ List rootBoneList = new ArrayList();
+ for (int i = boneList.length - 1; i >= 0; i--) {
+ Bone b = boneList[i];
+ if (b.getParent() == null) {
+ rootBoneList.add(b);
+ }
+ }
+ rootBones = rootBoneList.toArray(new Bone[rootBoneList.size()]);
+
+ createSkinningMatrices();
+
+ for (int i = rootBones.length - 1; i >= 0; i--) {
+ Bone rootBone = rootBones[i];
+ rootBone.update();
+ rootBone.setBindingPose();
+ }
+ }
+
+ /**
+ * Copy constructor.
+ * Most of the skeleton data is deeply-copied except the bone bind and inverseBind transforms.
+ * @param source
+ */
+ public Skeleton(Skeleton source) {
+ Bone[] sourceList = source.boneList;
+ boneList = new Bone[sourceList.length];
+ for (int i = 0; i < sourceList.length; i++) {
+ boneList[i] = new Bone(sourceList[i]);
+ }
+
+ rootBones = new Bone[source.rootBones.length];
+ for (int i = 0; i < rootBones.length; i++) {
+ rootBones[i] = recreateBoneStructure(source.rootBones[i]);
+ }
+ createSkinningMatrices();
+
+ for (int i = rootBones.length - 1; i >= 0; i--) {
+ rootBones[i].update();
+ }
+ }
+
+ /**
+ * Used for binary loading as a Savable; the object must be constructed,
+ * then the parameters usually present in the constructor for this class are
+ * restored from the file the object was saved to.
+ */
+ public Skeleton() {
+ }
+
+ private void createSkinningMatrices() {
+ skinningMatrixes = new Matrix4f[boneList.length];
+ for (int i = 0; i < skinningMatrixes.length; i++) {
+ skinningMatrixes[i] = new Matrix4f();
+ }
+ }
+
+ private Bone recreateBoneStructure(Bone sourceRoot) {
+ Bone targetRoot = getBone(sourceRoot.getName());
+ List children = sourceRoot.getChildren();
+ for (int i = 0; i < children.size(); i++) {
+ Bone sourceChild = children.get(i);
+ // find my version of the child
+ Bone targetChild = getBone(sourceChild.getName());
+ targetRoot.addChild(targetChild);
+ recreateBoneStructure(sourceChild);
+ }
+
+ return targetRoot;
+ }
+
+ /**
+ * Updates world transforms for all bones in this skeleton.
+ * Typically called after setting local animation transforms.
+ */
+ public void updateWorldVectors() {
+ for (int i = rootBones.length - 1; i >= 0; i--) {
+ rootBones[i].update();
+ }
+ }
+
+ /**
+ * Saves the current skeleton state as it's binding pose.
+ */
+ public void setBindingPose() {
+ for (int i = rootBones.length - 1; i >= 0; i--) {
+ rootBones[i].setBindingPose();
+ }
+ }
+
+ /**
+ * Reset the skeleton to bind pose.
+ */
+ public final void reset() {
+ for (int i = rootBones.length - 1; i >= 0; i--) {
+ rootBones[i].reset();
+ }
+ }
+
+ public final void resetAndUpdate() {
+ for (int i = rootBones.length - 1; i >= 0; i--) {
+ Bone rootBone = rootBones[i];
+ rootBone.reset();
+ rootBone.update();
+ }
+ }
+
+ public Bone[] getRoots() {
+ return rootBones;
+ }
+
+ public Bone getBone(int index) {
+ return boneList[index];
+ }
+
+ public Bone getBone(String name) {
+ for (int i = 0; i < boneList.length; i++) {
+ if (boneList[i].getName().equals(name)) {
+ return boneList[i];
+ }
+ }
+
+ return null;
+ }
+
+ public int getBoneIndex(Bone bone) {
+ for (int i = 0; i < boneList.length; i++) {
+ if (boneList[i] == bone) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ public int getBoneIndex(String name) {
+ for (int i = 0; i < boneList.length; i++) {
+ if (boneList[i].getName().equals(name)) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ public Matrix4f[] computeSkinningMatrices() {
+ TempVars vars = TempVars.get();
+ assert vars.lock();
+ for (int i = 0; i < boneList.length; i++) {
+ boneList[i].getOffsetTransform(skinningMatrixes[i], vars.quat1, vars.vect1, vars.vect2);
+ }
+ assert vars.unlock();
+ return skinningMatrixes;
+ }
+
+ public int getBoneCount() {
+ return boneList.length;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Skeleton - ").append(boneList.length).append(" bones, ").append(rootBones.length).append(" roots\n");
+ for (Bone rootBone : rootBones) {
+ sb.append(rootBone.toString());
+ }
+ return sb.toString();
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule input = im.getCapsule(this);
+
+ Savable[] boneRootsAsSav = input.readSavableArray("rootBones", null);
+ rootBones = new Bone[boneRootsAsSav.length];
+ System.arraycopy(boneRootsAsSav, 0, rootBones, 0, boneRootsAsSav.length);
+
+ Savable[] boneListAsSavable = input.readSavableArray("boneList", null);
+ boneList = new Bone[boneListAsSavable.length];
+ System.arraycopy(boneListAsSavable, 0, boneList, 0, boneListAsSavable.length);
+
+ createSkinningMatrices();
+
+ for (Bone rootBone : rootBones) {
+ rootBone.update();
+ rootBone.setBindingPose();
+ }
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule output = ex.getCapsule(this);
+ output.write(rootBones, "rootBones", null);
+ output.write(boneList, "boneList", null);
+ }
+}
diff --git a/engine/src/core/com/jme3/animation/Track.java b/engine/src/core/com/jme3/animation/Track.java
new file mode 100644
index 000000000..a330665b6
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/Track.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.animation;
+
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.Savable;
+import com.jme3.scene.Mesh;
+import java.io.IOException;
+import java.io.Serializable;
+
+/**
+ * A single track of mesh animation (either morph or pose based).
+ * Currently morph animations are not supported (only pose).
+ */
+public abstract class Track implements Serializable, Savable {
+
+ private static final long serialVersionUID = 1L;
+
+ protected int targetMeshIndex;
+
+ public Track(int targetMeshIndex){
+ this.targetMeshIndex = targetMeshIndex;
+ }
+
+ public int getTargetMeshIndex(){
+ return targetMeshIndex;
+ }
+
+ public abstract void setTime(float time, Mesh[] targets, float weight);
+
+ public void write(JmeExporter ex) throws IOException{
+ ex.getCapsule(this).write(targetMeshIndex, "meshIndex", 0);
+ }
+
+ public void read(JmeImporter im) throws IOException{
+ targetMeshIndex = im.getCapsule(this).readInt("meshIndex", 0);
+ }
+}
diff --git a/engine/src/core/com/jme3/animation/package.html b/engine/src/core/com/jme3/animation/package.html
new file mode 100644
index 000000000..873875647
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/package.html
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+The com.jme3.animation
package contains various classes
+for managing animation inside a jME3 application. Currently, the majority
+of classes are for handling skeletal animation. The primary control class is
+the {@link com.jme3.animation.AnimControl}, through which animations can be played,
+looped, combined, transitioned, etc.
+
+Usage
+
+
+
+// Create or load a model with skeletal animation:
+Spatial model = assetManager.loadModel("...");
+
+// Retrieve the AnimControl.
+AnimControl animCtrl = model.getControl(AnimControl.class);
+
+// Create an animation channel, by default assigned to all bones.
+AnimChannel animChan = animCtrl.createChannel();
+
+// Play an animation
+animChan.setAnim("MyAnim");
+
+
+
Skeletal Animation System
+
+
+jME3 uses a system of bone-weights: A vertex is assigned up to 4 bones by which
+it is influenced and 4 weights that describe how much the bone influences the
+vertex. The maximum weight value being 1.0, and the requirement that all 4 weights
+for a given vertex must sum to 1.0. This data is specified
+for each skin/mesh that is influenced by the animation control via the
+{link com.jme3.scene.VertexBuffer}s BoneWeight
and BoneIndex
.
+The BoneIndex buffer must be of the format UnsignedByte
, thus
+placing the limit of up to 256 bones for a skeleton. The BoneWeight buffer
+should be of the format Float
. Both buffers should reference 4
+bones, even if the maximum number of bones any vertex is influenced is less or more
+than 4.
+If a vertex is influenced by less than 4 bones, the indices following the last
+valid bone should be 0 and the weights following the last valid bone should be 0.0.
+The buffers are designed in such a way so as to permit hardware skinning.
+
+The {@link com.jme3.animation.Skeleton} class describes a bone heirarchy with one
+or more root bones having children, thus containing all bones of the skeleton.
+In addition to accessing the bones in the skeleton via the tree heirarchy, it
+is also possible to access bones via index. The index for any given bone is
+arbitrary and does not depend on the bone's location in the tree hierarchy.
+It is this index that is specified in the BoneIndex VertexBuffer mentioned above
+, and is also used to apply transformations to the bones through the animations.
+
+Every bone has a local and model space transformation. The local space
+transformation is relative to its parent, if it has one, otherwise it is relative
+to the model. The model space transformation is relative to model space.
+The bones additionally have a bind pose transformation, which describes
+the transformations for bones when no animated pose is applied to the skeleton.
+All bones must have a bind pose transformation before they can be
+animated. To set the bind pose for the skeleton, set the local (relative
+to parent) transformations for all the bones using the call
+{@link com.jme3.animation.Bone#setBindTransforms(com.jme3.math.Vector3f, com.jme3.math.Quaternion) }.
+Then call {@link com.jme3.animation.Skeleton#updateWorldVectors() } followed by
+{@link com.jme3.animation.Skeleton#setBindingPose() }.
+
+Animations are stored in a HashMap object, accessed by name. An animation
+is simply a list of tracks, each track describes a timeline with each keyframe
+having a transformation. For bone animations, every track is assigned to a bone,
+while for morph animations, every track is assigned to a mesh.
+
+
+
+
diff --git a/engine/src/core/com/jme3/app/AppTask.java b/engine/src/core/com/jme3/app/AppTask.java
new file mode 100644
index 000000000..efda06c46
--- /dev/null
+++ b/engine/src/core/com/jme3/app/AppTask.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.app;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * AppTask
is used in AppTaskQueue
to manage tasks that have
+ * yet to be accomplished. The AppTask system is used to execute tasks either
+ * in the OpenGL/Render thread, or outside of it.
+ *
+ * @author Matthew D. Hicks, lazloh
+ */
+public class AppTask implements Future {
+ private static final Logger logger = Logger.getLogger(AppTask.class
+ .getName());
+
+ private final Callable callable;
+
+ private V result;
+ private ExecutionException exception;
+ private boolean cancelled, finished;
+ private final ReentrantLock stateLock = new ReentrantLock();
+ private final Condition finishedCondition = stateLock.newCondition();
+
+ public AppTask(Callable callable) {
+ this.callable = callable;
+ }
+
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ // TODO mayInterruptIfRunning was ignored in previous code, should this param be removed?
+ stateLock.lock();
+ try {
+ if (result != null) {
+ return false;
+ }
+ cancelled = true;
+
+ finishedCondition.signalAll();
+
+ return true;
+ } finally {
+ stateLock.unlock();
+ }
+ }
+
+ public V get() throws InterruptedException, ExecutionException {
+ stateLock.lock();
+ try {
+ while (!isDone()) {
+ finishedCondition.await();
+ }
+ if (exception != null) {
+ throw exception;
+ }
+ return result;
+ } finally {
+ stateLock.unlock();
+ }
+ }
+
+ public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+ stateLock.lock();
+ try {
+ if (!isDone()) {
+ finishedCondition.await(timeout, unit);
+ }
+ if (exception != null) {
+ throw exception;
+ }
+ if (result == null) {
+ throw new TimeoutException("Object not returned in time allocated.");
+ }
+ return result;
+ } finally {
+ stateLock.unlock();
+ }
+ }
+
+ public boolean isCancelled() {
+ stateLock.lock();
+ try {
+ return cancelled;
+ } finally {
+ stateLock.unlock();
+ }
+ }
+
+ public boolean isDone() {
+ stateLock.lock();
+ try {
+ return finished || cancelled || (exception != null);
+ } finally {
+ stateLock.unlock();
+ }
+ }
+
+ public Callable getCallable() {
+ return callable;
+ }
+
+ public void invoke() {
+ try {
+ final V tmpResult = callable.call();
+
+ stateLock.lock();
+ try {
+ result = tmpResult;
+ finished = true;
+
+ finishedCondition.signalAll();
+ } finally {
+ stateLock.unlock();
+ }
+ } catch (Exception e) {
+ logger.logp(Level.SEVERE, this.getClass().toString(), "invoke()", "Exception", e);
+
+ stateLock.lock();
+ try {
+ exception = new ExecutionException(e);
+
+ finishedCondition.signalAll();
+ } finally {
+ stateLock.unlock();
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/engine/src/core/com/jme3/app/Application.java b/engine/src/core/com/jme3/app/Application.java
new file mode 100644
index 000000000..5630bca08
--- /dev/null
+++ b/engine/src/core/com/jme3/app/Application.java
@@ -0,0 +1,500 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.app;
+
+import com.jme3.app.state.AppStateManager;
+import com.jme3.input.JoyInput;
+import com.jme3.input.KeyInput;
+import com.jme3.input.MouseInput;
+import com.jme3.system.*;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.Renderer;
+import com.jme3.asset.AssetManager;
+import com.jme3.audio.AudioRenderer;
+import com.jme3.audio.Listener;
+import com.jme3.input.InputManager;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.Future;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * The Application
class represents an instance of a
+ * real-time 3D rendering jME application.
+ *
+ * An Application
provides all the tools that are commonly used in jME3
+ * applications.
+ *
+ * jME3 applications should extend this class and call start() to begin the
+ * application.
+ *
+ */
+public class Application implements SystemListener {
+
+ private static final Logger logger = Logger.getLogger(Application.class.getName());
+
+ protected AssetManager assetManager;
+
+ protected AudioRenderer audioRenderer;
+ protected Renderer renderer;
+ protected RenderManager renderManager;
+ protected ViewPort viewPort;
+ protected ViewPort guiViewPort;
+
+ protected JmeContext context;
+ protected AppSettings settings;
+ protected Timer timer;
+ protected Camera cam;
+ protected Listener listener;
+
+ protected boolean inputEnabled = true;
+ protected boolean pauseOnFocus = true;
+ protected float speed = 1f;
+ protected boolean paused = false;
+ protected MouseInput mouseInput;
+ protected KeyInput keyInput;
+ protected JoyInput joyInput;
+ protected InputManager inputManager;
+ protected AppStateManager stateManager;
+
+ private final ConcurrentLinkedQueue> taskQueue = new ConcurrentLinkedQueue>();
+
+ /**
+ * Create a new instance of Application
.
+ */
+ public Application(){
+ }
+
+ public boolean isPauseOnLostFocus() {
+ return pauseOnFocus;
+ }
+
+ public void setPauseOnLostFocus(boolean pauseOnLostFocus) {
+ this.pauseOnFocus = pauseOnLostFocus;
+ }
+
+ public void setAssetManager(AssetManager assetManager){
+ if (this.assetManager != null)
+ throw new IllegalStateException("Can only set asset manager"
+ + " before initialization.");
+
+ this.assetManager = assetManager;
+ }
+
+ private void initAssetManager(){
+ if (settings != null){
+ String assetCfg = settings.getString("AssetConfigURL");
+ if (assetCfg != null){
+ URL url = null;
+ try {
+ url = new URL(assetCfg);
+ } catch (MalformedURLException ex) {
+ }
+ if (url == null) {
+ url = Application.class.getResource(assetCfg);
+ if (url == null) {
+ logger.log(Level.SEVERE, "Unable to access AssetConfigURL in asset config:{0}", assetCfg);
+ return;
+ }
+ }
+ assetManager = JmeSystem.newAssetManager(url);
+ }
+ }
+ if (assetManager == null){
+ assetManager = JmeSystem.newAssetManager(
+ Thread.currentThread().getContextClassLoader()
+ .getResource("com/jme3/asset/Desktop.cfg"));
+ }
+ }
+
+ /**
+ * Set the display settings to define the display created. Examples of
+ * display parameters include display pixel width and height,
+ * color bit depth, z-buffer bits, anti-aliasing samples, and update frequency.
+ *
+ * @param settings The settings to set.
+ */
+ public void setSettings(AppSettings settings){
+ this.settings = settings;
+ if (context != null && settings.useInput() != inputEnabled){
+ // may need to create or destroy input based
+ // on settings change
+ inputEnabled = !inputEnabled;
+ if (inputEnabled){
+ initInput();
+ }else{
+ destroyInput();
+ }
+ }else{
+ inputEnabled = settings.useInput();
+ }
+ }
+
+ private void initDisplay(){
+ // aquire important objects
+ // from the context
+ settings = context.getSettings();
+ timer = context.getTimer();
+
+ renderer = context.getRenderer();
+ }
+
+ private void initAudio(){
+ if (settings.getAudioRenderer() != null){
+ audioRenderer = JmeSystem.newAudioRenderer(settings);
+ audioRenderer.initialize();
+
+ listener = new Listener();
+ audioRenderer.setListener(listener);
+ }
+ }
+
+ /**
+ * Creates the camera to use for rendering. Default values are perspective
+ * projection with 45° field of view, with near and far values 1 and 1000
+ * units respectively.
+ */
+ private void initCamera(){
+ cam = new Camera(settings.getWidth(), settings.getHeight());
+
+ cam.setFrustumPerspective(45f, (float)cam.getWidth() / cam.getHeight(), 1f, 1000f);
+ cam.setLocation(new Vector3f(0f, 0f, 10f));
+ cam.lookAt(new Vector3f(0f, 0f, 0f), Vector3f.UNIT_Y);
+
+ renderManager = new RenderManager(renderer);
+ //Remy - 09/14/2010 setted the timer in the renderManager
+ renderManager.setTimer(timer);
+ viewPort = renderManager.createMainView("Default", cam);
+ viewPort.setClearEnabled(true);
+
+ // Create a new cam for the gui
+ Camera guiCam = new Camera(settings.getWidth(), settings.getHeight());
+ guiViewPort = renderManager.createPostView("Gui Default", guiCam);
+ guiViewPort.setClearEnabled(false);
+ }
+
+ /**
+ * Initializes mouse and keyboard input. Also
+ * initializes joystick input if joysticks are enabled in the
+ * AppSettings.
+ */
+ private void initInput(){
+ mouseInput = context.getMouseInput();
+ if (mouseInput != null)
+ mouseInput.initialize();
+
+ keyInput = context.getKeyInput();
+ if (keyInput != null)
+ keyInput.initialize();
+
+ if (!settings.getBoolean("DisableJoysticks")){
+ joyInput = context.getJoyInput();
+ if (joyInput != null)
+ joyInput.initialize();
+ }
+
+ inputManager = new InputManager(mouseInput, keyInput, joyInput);
+ }
+
+ private void initStateManager(){
+ stateManager = new AppStateManager(this);
+ }
+
+ /**
+ * @return The asset manager for this application.
+ */
+ public AssetManager getAssetManager(){
+ return assetManager;
+ }
+
+ /**
+ * @return the input manager.
+ */
+ public InputManager getInputManager(){
+ return inputManager;
+ }
+
+ /**
+ * @return the app state manager
+ */
+ public AppStateManager getStateManager() {
+ return stateManager;
+ }
+
+ /**
+ * @return the render manager
+ */
+ public RenderManager getRenderManager() {
+ return renderManager;
+ }
+
+ /**
+ * @return The renderer for the application, or null if was not started yet.
+ */
+ public Renderer getRenderer(){
+ return renderer;
+ }
+
+ /**
+ * @return The audio renderer for the application, or null if was not started yet.
+ */
+ public AudioRenderer getAudioRenderer() {
+ return audioRenderer;
+ }
+
+ /**
+ * @return The listener object for audio
+ */
+ public Listener getListener() {
+ return listener;
+ }
+
+ /**
+ * @return The display context for the application, or null if was not
+ * started yet.
+ */
+ public JmeContext getContext(){
+ return context;
+ }
+
+ /**
+ * @return The camera for the application, or null if was not started yet.
+ */
+ public Camera getCamera(){
+ return cam;
+ }
+
+ public void start(){
+ start(JmeContext.Type.Display);
+ }
+
+ /**
+ * Starts the application. Creating a display and running the main loop.
+ */
+ public void start(JmeContext.Type contextType){
+ if (context != null && context.isCreated()){
+ logger.warning("start() called when application already created!");
+ return;
+ }
+
+ if (settings == null){
+ settings = new AppSettings(true);
+ }
+
+ logger.log(Level.FINE, "Starting application: {0}", getClass().getName());
+ context = JmeSystem.newContext(settings, contextType);
+ context.setSystemListener(this);
+ context.create(false);
+ }
+
+ public void createCanvas(){
+ if (context != null && context.isCreated()){
+ logger.warning("createCanvas() called when application already created!");
+ return;
+ }
+
+ if (settings == null){
+ settings = new AppSettings(true);
+ }
+
+ logger.log(Level.FINE, "Starting application: {0}", getClass().getName());
+ context = JmeSystem.newContext(settings, JmeContext.Type.Canvas);
+ context.setSystemListener(this);
+ }
+
+ public void startCanvas(){
+ startCanvas(false);
+ }
+
+ public void startCanvas(boolean waitFor){
+ context.create(waitFor);
+ }
+
+ public void reshape(int w, int h){
+ renderManager.notifyReshape(w, h);
+ }
+
+ public void restart(){
+ context.setSettings(settings);
+ context.restart();
+ }
+
+ public void stop(){
+ stop(false);
+ }
+
+ /**
+ * Requests the display to close, shutting down the main loop
+ * and making neccessary cleanup operations.
+ */
+ public void stop(boolean waitFor){
+ logger.log(Level.FINE, "Closing application: {0}", getClass().getName());
+ context.destroy(waitFor);
+ }
+
+ /**
+ * Do not call manually.
+ * Callback from ContextListener.
+ *
+ * Initializes the Application
, by creating a display and
+ * default camera. If display settings are not specified, a default
+ * 640x480 display is created. Default values are used for the camera;
+ * perspective projection with 45° field of view, with near
+ * and far values 1 and 1000 units respectively.
+ */
+ public void initialize(){
+ if (assetManager == null){
+ initAssetManager();
+ }
+
+ initDisplay();
+ initCamera();
+
+ if (inputEnabled){
+ initInput();
+ }
+ initAudio();
+ initStateManager();
+
+ // update timer so that the next delta is not too large
+// timer.update();
+ timer.reset();
+
+ // user code here..
+ }
+
+ public void handleError(String errMsg, Throwable t){
+ logger.log(Level.SEVERE, errMsg, t);
+ }
+
+ public void gainFocus(){
+ if (pauseOnFocus){
+ paused = false;
+ context.setAutoFlushFrames(true);
+ if (inputManager != null)
+ inputManager.reset();
+ }
+ }
+
+ public void loseFocus(){
+ if (pauseOnFocus){
+ paused = true;
+ context.setAutoFlushFrames(false);
+ }
+ }
+
+ public void requestClose(boolean esc){
+ context.destroy(false);
+ }
+
+ public Future enqueue(Callable callable) {
+ AppTask task = new AppTask(callable);
+ taskQueue.add(task);
+ return task;
+ }
+
+ /**
+ * Do not call manually.
+ * Callback from ContextListener.
+ */
+ public void update(){
+ AppTask> task = taskQueue.poll();
+ toploop: do {
+ if (task == null) break;
+ while (task.isCancelled()) {
+ task = taskQueue.poll();
+ if (task == null) break toploop;
+ }
+ task.invoke();
+ } while (((task = taskQueue.poll()) != null));
+
+ if (speed == 0 || paused)
+ return;
+
+ timer.update();
+
+ if (inputEnabled){
+ inputManager.update(timer.getTimePerFrame());
+ }
+
+ if (audioRenderer != null){
+ audioRenderer.update(timer.getTimePerFrame());
+ }
+
+ // user code here..
+ }
+
+ protected void destroyInput(){
+ if (mouseInput != null)
+ mouseInput.destroy();
+
+ if (keyInput != null)
+ keyInput.destroy();
+
+ if (joyInput != null)
+ joyInput.destroy();
+
+ inputManager = null;
+ }
+
+ /**
+ * Do not call manually.
+ * Callback from ContextListener.
+ */
+ public void destroy(){
+ stateManager.cleanup();
+
+ destroyInput();
+ if (audioRenderer != null)
+ audioRenderer.cleanup();
+
+ timer.reset();
+ }
+
+ public ViewPort getGuiViewPort() {
+ return guiViewPort;
+ }
+
+ public ViewPort getViewPort() {
+ return viewPort;
+ }
+
+
+
+}
diff --git a/engine/src/core/com/jme3/app/SimpleApplication.java b/engine/src/core/com/jme3/app/SimpleApplication.java
new file mode 100644
index 000000000..fa7648ceb
--- /dev/null
+++ b/engine/src/core/com/jme3/app/SimpleApplication.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.app;
+
+import com.jme3.font.BitmapFont;
+import com.jme3.font.BitmapText;
+import com.jme3.input.FlyByCamera;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.queue.RenderQueue.Bucket;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial.CullHint;
+import com.jme3.system.AppSettings;
+import com.jme3.system.JmeContext.Type;
+import com.jme3.system.JmeSystem;
+import com.jme3.util.BufferUtils;
+
+/**
+ * SimpleApplication
extends the {@link com.jme3.app.Application}
+ * class to provide default functionality like a first-person camera,
+ * and an accessible root node that is updated and rendered regularly.
+ * Additionally, SimpleApplication
will display a statistics view
+ * using the {@link com.jme3.app.StatsView} class. It will display
+ * the current frames-per-second value on-screen in addition to the statistics.
+ * Several keys have special functionality in SimpleApplication
:
+ *
+ *
+ * Esc - Close the application
+ * C - Display the camera position and rotation in the console.
+ * M - Display memory usage in the console.
+ *
+ */
+public abstract class SimpleApplication extends Application {
+
+ protected Node rootNode = new Node("Root Node");
+ protected Node guiNode = new Node("Gui Node");
+ protected float secondCounter = 0.0f;
+ protected BitmapText fpsText;
+ protected BitmapFont guiFont;
+ protected StatsView statsView;
+ protected FlyByCamera flyCam;
+ protected boolean showSettings = true;
+ private AppActionListener actionListener = new AppActionListener();
+
+ private class AppActionListener implements ActionListener {
+
+ public void onAction(String name, boolean value, float tpf) {
+ if (!value) {
+ return;
+ }
+
+ if (name.equals("SIMPLEAPP_Exit")) {
+ stop();
+ } else if (name.equals("SIMPLEAPP_CameraPos")) {
+ if (cam != null) {
+ Vector3f loc = cam.getLocation();
+ Quaternion rot = cam.getRotation();
+ System.out.println("Camera Position: ("
+ + loc.x + ", " + loc.y + ", " + loc.z + ")");
+ System.out.println("Camera Rotation: " + rot);
+ System.out.println("Camera Direction: " + cam.getDirection());
+ }
+ } else if (name.equals("SIMPLEAPP_Memory")) {
+ BufferUtils.printCurrentDirectMemory(null);
+ }
+ }
+ }
+
+ public SimpleApplication() {
+ super();
+ }
+
+ @Override
+ public void start() {
+ // set some default settings in-case
+ // settings dialog is not shown
+ boolean loadSettings = false;
+ if (settings == null) {
+ setSettings(new AppSettings(true));
+ loadSettings = true;
+ }
+
+ // show settings dialog
+ if (showSettings) {
+ if (!JmeSystem.showSettingsDialog(settings, loadSettings)) {
+ return;
+ }
+ }
+ //re-setting settings they can have been merged from the registry.
+ setSettings(settings);
+ super.start();
+ }
+
+ /**
+ * Retrieves flyCam
+ * @return flyCam Camera object
+ *
+ */
+ public FlyByCamera getFlyByCamera() {
+ return flyCam;
+ }
+
+ /**
+ * Retrieves guiNode
+ * @return guiNode Node object
+ *
+ */
+ public Node getGuiNode() {
+ return guiNode;
+ }
+
+ /**
+ * Retrieves rootNode
+ * @return rootNode Node object
+ *
+ */
+ public Node getRootNode() {
+ return rootNode;
+ }
+
+ public boolean isShowSettings() {
+ return showSettings;
+ }
+
+ /**
+ * Toggles settings window to display at start-up
+ * @param showSettings Sets true/false
+ *
+ */
+ public void setShowSettings(boolean showSettings) {
+ this.showSettings = showSettings;
+ }
+
+ /**
+ * Attaches FPS statistics to guiNode and displays it on the screen.
+ *
+ */
+ public void loadFPSText() {
+ guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
+ fpsText = new BitmapText(guiFont, false);
+ fpsText.setLocalTranslation(0, fpsText.getLineHeight(), 0);
+ fpsText.setText("Frames per second");
+ guiNode.attachChild(fpsText);
+ }
+
+ /**
+ * Attaches Statistics View to guiNode and displays it on the screen
+ * above FPS statistics line.
+ *
+ */
+ public void loadStatsView() {
+ statsView = new StatsView("Statistics View", assetManager, renderer.getStatistics());
+// move it up so it appears above fps text
+ statsView.setLocalTranslation(0, fpsText.getLineHeight(), 0);
+ guiNode.attachChild(statsView);
+ }
+
+ @Override
+ public void initialize() {
+ super.initialize();
+
+ guiNode.setQueueBucket(Bucket.Gui);
+ guiNode.setCullHint(CullHint.Never);
+ loadFPSText();
+ loadStatsView();
+ viewPort.attachScene(rootNode);
+ guiViewPort.attachScene(guiNode);
+
+ if (inputManager != null) {
+ flyCam = new FlyByCamera(cam);
+ flyCam.setMoveSpeed(1f);
+ flyCam.registerWithInput(inputManager);
+
+ if (context.getType() == Type.Display) {
+ inputManager.addMapping("SIMPLEAPP_Exit", new KeyTrigger(KeyInput.KEY_ESCAPE));
+ }
+
+ inputManager.addMapping("SIMPLEAPP_CameraPos", new KeyTrigger(KeyInput.KEY_C));
+ inputManager.addMapping("SIMPLEAPP_Memory", new KeyTrigger(KeyInput.KEY_M));
+ inputManager.addListener(actionListener, "SIMPLEAPP_Exit",
+ "SIMPLEAPP_CameraPos", "SIMPLEAPP_Memory");
+ }
+
+ // call user code
+ simpleInitApp();
+ }
+
+ @Override
+ public void update() {
+ super.update(); // makes sure to execute AppTasks
+ if (speed == 0 || paused) {
+ return;
+ }
+
+ float tpf = timer.getTimePerFrame() * speed;
+
+ secondCounter += timer.getTimePerFrame();
+ int fps = (int) timer.getFrameRate();
+ if (secondCounter >= 1.0f) {
+ fpsText.setText("Frames per second: " + fps);
+ secondCounter = 0.0f;
+ }
+
+ // update states
+ stateManager.update(tpf);
+
+ // simple update and root node
+ simpleUpdate(tpf);
+ rootNode.updateLogicalState(tpf);
+ guiNode.updateLogicalState(tpf);
+ rootNode.updateGeometricState();
+ guiNode.updateGeometricState();
+
+ // render states
+ stateManager.render(renderManager);
+ renderManager.render(tpf);
+ simpleRender(renderManager);
+ stateManager.postRender();
+ }
+
+ public abstract void simpleInitApp();
+
+ public void simpleUpdate(float tpf) {
+ }
+
+ public void simpleRender(RenderManager rm) {
+ }
+}
diff --git a/engine/src/core/com/jme3/app/StatsView.java b/engine/src/core/com/jme3/app/StatsView.java
new file mode 100644
index 000000000..48df4a9a3
--- /dev/null
+++ b/engine/src/core/com/jme3/app/StatsView.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.app;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.font.BitmapFont;
+import com.jme3.font.BitmapText;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.Statistics;
+import com.jme3.renderer.ViewPort;
+import com.jme3.renderer.queue.RenderQueue.Bucket;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.control.Control;
+
+/**
+ * The StatsView
provides a heads-up display (HUD) of various
+ * statistics of rendering. The data is retrieved every frame from a
+ * {@link com.jme3.renderer.Statistics} and then displayed on screen.
+ *
+ * Usage:
+ * To use the stats view, you need to retrieve the
+ * {@link com.jme3.renderer.Statistics} from the
+ * {@link com.jme3.renderer.Renderer} used by the application. Then, attach
+ * the StatsView
to the scene graph.
+ *
+ * Statistics stats = renderer.getStatistics();
+ * StatsView statsView = new StatsView("MyStats", assetManager, stats);
+ * rootNode.attachChild(statsView);
+ *
+ */
+public class StatsView extends Node implements Control {
+
+ private BitmapText[] labels;
+ private Statistics statistics;
+
+ private String[] statLabels;
+ private int[] statData;
+
+ private final StringBuilder stringBuilder = new StringBuilder();
+
+ public StatsView(String name, AssetManager manager, Statistics stats){
+ super(name);
+
+ setQueueBucket(Bucket.Gui);
+ setCullHint(CullHint.Never);
+
+ statistics = stats;
+
+ statLabels = statistics.getLabels();
+ statData = new int[statLabels.length];
+ labels = new BitmapText[statLabels.length];
+
+ BitmapFont font = manager.loadFont("Interface/Fonts/Console.fnt");
+ for (int i = 0; i < labels.length; i++){
+ labels[i] = new BitmapText(font);
+ labels[i].setLocalTranslation(0, labels[i].getLineHeight() * (i+1), 0);
+ attachChild(labels[i]);
+ }
+
+ addControl(this);
+ }
+
+ public void update(float tpf) {
+ statistics.getData(statData);
+ for (int i = 0; i < labels.length; i++) {
+ stringBuilder.setLength(0);
+ stringBuilder.append(statLabels[i]).append(" = ").append(statData[i]);
+ labels[i].setText(stringBuilder);
+ }
+ statistics.clearFrame();
+ }
+
+ public Control cloneForSpatial(Spatial spatial) {
+ return (Control) spatial;
+ }
+
+ public void setSpatial(Spatial spatial) {
+ }
+
+ public void setEnabled(boolean enabled) {
+ }
+
+ public boolean isEnabled() {
+ return true;
+ }
+
+ public void render(RenderManager rm, ViewPort vp) {
+ }
+
+}
diff --git a/engine/src/core/com/jme3/app/package.html b/engine/src/core/com/jme3/app/package.html
new file mode 100644
index 000000000..1f6e68d12
--- /dev/null
+++ b/engine/src/core/com/jme3/app/package.html
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+The com.jme3.application
provides a toolset for jME3 applications
+to interact with various components of the engine. Typically, the
+{@link com.jme3.app.Application} class will be extended and the update() method
+implemented to provide functionality for the main loop.
+
+An Application
will typically provide the following services:
+
+ {@link com.jme3.asset.AssetManager} - A system for finding and loading
+ data assets included with the application, such as models and textures.
+ {@link com.jme3.renderer.RenderManager} - A high-level rendering
+ interface for 3D graphics, manages viewports and scenes assigned
+ to the viewports, as well as general high-level rendering.
+ {@link com.jme3.input.InputManager} - An interface for handling input
+ from devices such as keyboard, mouse, and gamepad/joystick.
+ {@link com.jme3.app.state.AppStateManager} - Manager for
+ {@link com.jme3.app.state.AppState}s, which are specific application
+ functionality to be executed inside the main loop.
+ {@link com.jme3.audio.AudioRenderer} - Allows playing sound effects and
+ music.
+ {@link com.jme3.system.Timer} - The timer keeps track of time and allows
+ computing the time since the last frame (TPF) that is neccessary
+ for framerate-independent updates and motion.
+ {@link com.jme3.system.AppSettings} - A database containing various
+ settings for the application. These settings may be set by the user
+ or the application itself.
+
+
+
+Usage
+
+An example use of the Application class is as follows
+
+
+
+public class ExampleUse extends Application {
+
+ private Node rootNode = new Node("Root Node");
+
+ public static void main(String[] args){
+ ExampleUse app = new ExampleUse();
+ app.start();
+ }
+
+ @Override
+ public void initialize(){
+ super.initialize();
+
+ // attach root node to viewport
+ viewPort.attachScene(rootNode);
+ }
+
+ @Override
+ public void update(){
+ super.update();
+
+ float tpf = timer.getTimePerFrame();
+
+ // update rootNode
+ rootNode.updateLogicalState(tpf);
+ rootNode.updateGeometricState();
+
+ // render the viewports
+ renderManager.render(tpf);
+ }
+}
+
+
+
+
+
+
diff --git a/engine/src/core/com/jme3/app/state/AbstractAppState.java b/engine/src/core/com/jme3/app/state/AbstractAppState.java
new file mode 100644
index 000000000..a64a23311
--- /dev/null
+++ b/engine/src/core/com/jme3/app/state/AbstractAppState.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.app.state;
+
+import com.jme3.app.Application;
+import com.jme3.renderer.RenderManager;
+
+/**
+ * AbstractAppState
implements some common methods
+ * that make creation of AppStates easier.
+ * @author Kirill Vainer
+ */
+public class AbstractAppState implements AppState {
+
+ /**
+ * initialized
is set to true when the method
+ * {@link AbstractAppState#initialize(com.jme3.app.state.AppStateManager, com.jme3.app.Application) }
+ * is called. When {@link AbstractAppState#cleanup() } is called, initialized
+ * is set back to false.
+ */
+ protected boolean initialized = false;
+ private boolean active = true;
+
+ public void initialize(AppStateManager stateManager, Application app) {
+ initialized = true;
+ }
+
+ public boolean isInitialized() {
+ return initialized;
+ }
+
+ public void setActive(boolean active) {
+ this.active = active;
+ }
+
+ public boolean isActive() {
+ return active;
+ }
+
+ public void stateAttached(AppStateManager stateManager) {
+ }
+
+ public void stateDetached(AppStateManager stateManager) {
+ }
+
+ public void update(float tpf) {
+ }
+
+ public void render(RenderManager rm) {
+ }
+
+ public void postRender(){
+
+ }
+
+ public void cleanup() {
+ initialized = false;
+ }
+
+}
diff --git a/engine/src/core/com/jme3/app/state/AppState.java b/engine/src/core/com/jme3/app/state/AppState.java
new file mode 100644
index 000000000..c8c1f66a1
--- /dev/null
+++ b/engine/src/core/com/jme3/app/state/AppState.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.app.state;
+
+import com.jme3.app.Application;
+import com.jme3.renderer.RenderManager;
+
+/**
+ * AppState represents a continously executing code inside the main loop.
+ * An AppState
can track when it is attached to the
+ * {@link AppStateManager} or when it is detached. AppState
s
+ * are initialized in the render thread, upon a call to {@link AppState#initialize(com.jme3.app.state.AppStateManager, com.jme3.app.Application) }
+ * and are de-initialized upon a call to {@link AppState#cleanup()}.
+ * Implementations should return the correct value with a call to
+ * {@link AppState#isInitialized() } as specified above.
+ *
+ *
+ * @author Kirill Vainer
+ */
+public interface AppState {
+
+ /**
+ * Called to initialize the AppState.
+ *
+ * @param stateManager The state manager
+ * @param app
+ */
+ public void initialize(AppStateManager stateManager, Application app);
+
+ /**
+ * @return True if initialize()
was called on the state,
+ * false otherwise.
+ */
+ public boolean isInitialized();
+
+ /**
+ * Activate or deactivate the functionality of the AppState
.
+ * The effect of this call depends on implementation. An
+ * AppState
starts as being active by default.
+ *
+ * @param active activate the AppState or not.
+ */
+ public void setActive(boolean active);
+
+ /**
+ * @return True if the AppState
is active, false otherwise.
+ *
+ * @see AppState#setActive(boolean)
+ */
+ public boolean isActive();
+
+ /**
+ * Called when the state was attached.
+ *
+ * @param stateManager State manager to which the state was attached to.
+ */
+ public void stateAttached(AppStateManager stateManager);
+
+ /**
+ * Called when the state was detached.
+ *
+ * @param stateManager The state manager from which the state was detached from.
+ */
+ public void stateDetached(AppStateManager stateManager);
+
+ /**
+ * Called to update the state.
+ *
+ * @param tpf Time per frame.
+ */
+ public void update(float tpf);
+
+ /**
+ * Render the state.
+ *
+ * @param rm RenderManager
+ */
+ public void render(RenderManager rm);
+
+ /**
+ * Called after all rendering commands are flushed.
+ */
+ public void postRender();
+
+ /**
+ * Cleanup the game state.
+ */
+ public void cleanup();
+
+}
diff --git a/engine/src/core/com/jme3/app/state/AppStateManager.java b/engine/src/core/com/jme3/app/state/AppStateManager.java
new file mode 100644
index 000000000..2ea5b5ad7
--- /dev/null
+++ b/engine/src/core/com/jme3/app/state/AppStateManager.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.app.state;
+
+import com.jme3.app.Application;
+import com.jme3.renderer.RenderManager;
+import java.util.ArrayList;
+
+/**
+ * The AppStateManager
holds a list of {@link AppState}s which
+ * it will update and render.
+ * When an {@link AppState} is attached or detached, the
+ * {@link AppState#stateAttached(com.jme3.app.state.AppStateManager) } and
+ * {@link AppState#stateDetached(com.jme3.app.state.AppStateManager) } methods
+ * will be called respectively.
+ *
+ * @author Kirill Vainer
+ */
+public class AppStateManager {
+
+ private final ArrayList states = new ArrayList();
+ private final Application app;
+
+ public AppStateManager(Application app){
+ this.app = app;
+ }
+
+ /**
+ * Attach a state to the AppStateManager, the same state cannot be attached
+ * twice.
+ *
+ * @param state The state to attach
+ * @return True if the state was successfully attached, false if the state
+ * was already attached.
+ */
+ public boolean attach(AppState state){
+ synchronized (states){
+ if (!states.contains(state)){
+ state.stateAttached(this);
+ states.add(state);
+ return true;
+ }else{
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Detaches the state from the AppStateManager.
+ *
+ * @param state The state to detach
+ * @return True if the state was detached successfully, false
+ * if the state was not attached in the first place.
+ */
+ public boolean detach(AppState state){
+ synchronized (states){
+ if (states.contains(state)){
+ state.stateDetached(this);
+ states.remove(state);
+ return true;
+ }else{
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Check if a state is attached or not.
+ *
+ * @param state The state to check
+ * @return True if the state is currently attached to this AppStateManager.
+ *
+ * @see AppStateManager#attach(com.jme3.app.state.AppState)
+ */
+ public boolean hasState(AppState state){
+ synchronized (states){
+ return states.contains(state);
+ }
+ }
+
+ /**
+ * Returns the first state that is an instance of subclass of the specified class.
+ * @param
+ * @param stateClass
+ * @return First attached state that is an instance of stateClass
+ */
+ public T getState(Class stateClass){
+ synchronized (states){
+ int num = states.size();
+ for (int i = 0; i < num; i++){
+ AppState state = states.get(i);
+ if (stateClass.isAssignableFrom(state.getClass())){
+ return (T) state;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Calls update for attached states, do not call directly.
+ * @param tpf Time per frame.
+ */
+ public void update(float tpf){
+ synchronized (states){
+ int num = states.size();
+ for (int i = 0; i < num; i++){
+ AppState state = states.get(i);
+ if (!state.isInitialized())
+ state.initialize(this, app);
+
+ if (state.isActive()) {
+ state.update(tpf);
+ }
+ }
+ }
+ }
+
+ /**
+ * Calls render for all attached states, do not call directly.
+ * @param rm The RenderManager
+ */
+ public void render(RenderManager rm){
+ synchronized (states){
+ int num = states.size();
+ for (int i = 0; i < num; i++){
+ AppState state = states.get(i);
+ if (!state.isInitialized())
+ state.initialize(this, app);
+
+ if (state.isActive()) {
+ state.render(rm);
+ }
+ }
+ }
+ }
+
+ /**
+ * Calls render for all attached states, do not call directly.
+ * @param rm The RenderManager
+ */
+ public void postRender(){
+ synchronized (states){
+ int num = states.size();
+ for (int i = 0; i < num; i++){
+ AppState state = states.get(i);
+ if (!state.isInitialized())
+ state.initialize(this, app);
+
+ if (state.isActive()) {
+ state.postRender();
+ }
+ }
+ }
+ }
+
+ /**
+ * Calls cleanup on attached states, do not call directly.
+ */
+ public void cleanup(){
+ synchronized (states){
+ for (int i = 0; i < states.size(); i++){
+ AppState state = states.get(i);
+ state.cleanup();
+ }
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/asset/Asset.java b/engine/src/core/com/jme3/asset/Asset.java
new file mode 100644
index 000000000..0af00aa4f
--- /dev/null
+++ b/engine/src/core/com/jme3/asset/Asset.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.asset;
+
+/**
+ * Implementing the asset interface allows use of smart asset management.
+ */
+public interface Asset {
+ public void setKey(AssetKey key);
+ public AssetKey getKey();
+}
diff --git a/engine/src/core/com/jme3/asset/AssetConfig.java b/engine/src/core/com/jme3/asset/AssetConfig.java
new file mode 100644
index 000000000..8b1517092
--- /dev/null
+++ b/engine/src/core/com/jme3/asset/AssetConfig.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.asset;
+
+import java.io.DataInput;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Scanner;
+
+/**
+ * AssetConfig
loads a config file to configure the asset manager.
+ *
+ * The config file is specified with the following format:
+ *
+ * "LOADER" : ( ",")*
+ * "LOCATOR" : ( ",")*
+ *
+ *
+ * @author Kirill Vainer
+ */
+public class AssetConfig {
+
+ private AssetManager manager;
+
+ public AssetConfig(AssetManager manager){
+ this.manager = manager;
+ }
+
+ public void loadText(InputStream in) throws IOException{
+ Scanner scan = new Scanner(in);
+ while (scan.hasNext()){
+ String cmd = scan.next();
+ if (cmd.equals("LOADER")){
+ String loaderClass = scan.next();
+ String colon = scan.next();
+ if (!colon.equals(":")){
+ throw new IOException("Expected ':', got '"+colon+"'");
+ }
+ String extensionsList = scan.nextLine();
+ String[] extensions = extensionsList.split(",");
+ for (int i = 0; i < extensions.length; i++){
+ extensions[i] = extensions[i].trim();
+ }
+ manager.registerLoader(loaderClass, extensions);
+ }else if (cmd.equals("LOCATOR")){
+ String rootPath = scan.next();
+ String locatorClass = scan.nextLine().trim();
+ manager.registerLocator(rootPath, locatorClass);
+ }else{
+ throw new IOException("Expected command, got '"+cmd+"'");
+ }
+ }
+ }
+
+ private static String readString(DataInput dataIn) throws IOException{
+ int length = dataIn.readUnsignedShort();
+ char[] chrs = new char[length];
+ for (int i = 0; i < length; i++){
+ chrs[i] = (char) dataIn.readUnsignedByte();
+ }
+ return String.valueOf(chrs);
+ }
+
+ /*
+ public void loadBinary(DataInput dataIn) throws IOException{
+ // read signature and version
+
+ // how many locator entries?
+ int locatorEntries = dataIn.readUnsignedShort();
+ for (int i = 0; i < locatorEntries; i++){
+ String locatorClazz = readString(dataIn);
+ String rootPath = readString(dataIn);
+ manager.registerLocator(rootPath, locatorClazz);
+ }
+
+ int loaderEntries = dataIn.readUnsignedShort();
+ for (int i = 0; i < loaderEntries; i++){
+ String loaderClazz = readString(dataIn);
+ int numExtensions = dataIn.readUnsignedByte();
+ String[] extensions = new String[numExtensions];
+ for (int j = 0; j < numExtensions; j++){
+ extensions[j] = readString(dataIn);
+ }
+
+ manager.registerLoader(loaderClazz, extensions);
+ }
+ }
+ */
+}
diff --git a/engine/src/core/com/jme3/asset/AssetEventListener.java b/engine/src/core/com/jme3/asset/AssetEventListener.java
new file mode 100644
index 000000000..133c0e763
--- /dev/null
+++ b/engine/src/core/com/jme3/asset/AssetEventListener.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.asset;
+
+/**
+ * AssetEventListener
is an interface for listening to various
+ * events happening inside {@link AssetManager}. For now, it is possible
+ * to receive an event when an asset has been requested
+ * (one of the AssetManager.load***() methods were called), or when
+ * an asset has been loaded.
+ *
+ * @author Kirill Vainer
+ */
+public interface AssetEventListener {
+
+ /**
+ * Called when an asset has been successfully loaded (e.g: loaded from
+ * file system and parsed).
+ *
+ * @param key the AssetKey for the asset loaded.
+ */
+ public void assetLoaded(AssetKey key);
+
+ /**
+ * Called when an asset has been requested (e.g any of the load*** methods
+ * in AssetManager are called).
+ * In contrast to the assetLoaded() method, this one will be called even
+ * if the asset has failed to load, or if it was retrieved from the cache.
+ *
+ * @param key
+ */
+ public void assetRequested(AssetKey key);
+
+}
diff --git a/engine/src/core/com/jme3/asset/AssetInfo.java b/engine/src/core/com/jme3/asset/AssetInfo.java
new file mode 100644
index 000000000..786b9fafd
--- /dev/null
+++ b/engine/src/core/com/jme3/asset/AssetInfo.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.asset;
+
+import java.io.InputStream;
+
+/**
+ * The result of locating an asset through an AssetKey. Provides
+ * a means to read the asset data through an InputStream.
+ *
+ * @author Kirill Vainer
+ */
+public abstract class AssetInfo {
+
+ protected AssetManager manager;
+ protected AssetKey key;
+
+ public AssetInfo(AssetManager manager, AssetKey key) {
+ this.manager = manager;
+ this.key = key;
+ }
+
+ public AssetKey getKey() {
+ return key;
+ }
+
+ public AssetManager getManager() {
+ return manager;
+ }
+
+ @Override
+ public String toString(){
+ return getClass().getName() + "[" + "key=" + key + "]";
+ }
+
+ /**
+ * Implementations of this method should return an {@link InputStream}
+ * allowing access to the data represented by the {@link AssetKey}.
+ * @return The asset data.
+ */
+ public abstract InputStream openStream();
+
+}
diff --git a/engine/src/core/com/jme3/asset/AssetKey.java b/engine/src/core/com/jme3/asset/AssetKey.java
new file mode 100644
index 000000000..e906f139a
--- /dev/null
+++ b/engine/src/core/com/jme3/asset/AssetKey.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.asset;
+
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.OutputCapsule;
+import com.jme3.export.Savable;
+import java.io.IOException;
+
+/**
+ * AssetKey
is a key that is used to
+ * look up a resource from a cache.
+ * This class should be immutable.
+ */
+public class AssetKey implements Savable {
+
+ protected String name;
+ protected transient String folder;
+ protected transient String extension;
+
+ public AssetKey(String name){
+ this.name = name;
+ this.extension = getExtension(name);
+ }
+
+ public AssetKey(){
+ }
+
+ protected static String getExtension(String name){
+ int idx = name.lastIndexOf('.');
+ //workaround for filenames ending with xml and another dot ending before that (my.mesh.xml)
+ if(name.toLowerCase().indexOf(".xml")==name.length()-4){
+ idx = name.substring(0, idx).lastIndexOf('.');
+ if(idx==-1){
+ idx=name.lastIndexOf('.');
+ }
+ }
+ if (idx <= 0 || idx == name.length() - 1)
+ return "";
+ else
+ return name.substring(idx+1).toLowerCase();
+ }
+
+ protected static String getFolder(String name){
+ int idx = name.lastIndexOf('/');
+ if (idx <= 0 || idx == name.length() - 1)
+ return "";
+ else
+ return name.substring(0, idx+1);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @return The extension of the AssetKey
's name. For example,
+ * the name "Interface/Logo/Monkey.png" has an extension of "png".
+ */
+ public String getExtension() {
+ return extension;
+ }
+
+ public String getFolder(){
+ if (folder == null)
+ folder = getFolder(name);
+
+ return folder;
+ }
+
+ /**
+ * Do any post-processing on the resource after it has been loaded.
+ * @param asset
+ */
+ public Object postProcess(Object asset){
+ return asset;
+ }
+
+ /**
+ * Create a new instance of the asset, based on a prototype that is stored
+ * in the cache. Implementations are allowed to return the given parameter
+ * as-is if it is considered that cloning is not necessary for that particular
+ * asset type.
+ *
+ * @param asset The asset to be cloned.
+ * @return The asset, possibly cloned.
+ */
+ public Object createClonedInstance(Object asset){
+ return asset;
+ }
+
+ /**
+ * @return True if the asset for this key should be cached. Subclasses
+ * should override this method if they want to override caching behavior.
+ */
+ public boolean shouldCache(){
+ return true;
+ }
+
+ /**
+ * @return Should return true, if the asset objects implement the "Asset"
+ * interface and want to be removed from the cache when no longer
+ * referenced in user-code.
+ */
+ public boolean useSmartCache(){
+ return false;
+ }
+
+ @Override
+ public boolean equals(Object other){
+ if (!(other instanceof AssetKey)){
+ return false;
+ }
+ return name.equals(((AssetKey)other).name);
+ }
+
+ @Override
+ public int hashCode(){
+ return name.hashCode();
+ }
+
+ @Override
+ public String toString(){
+ return name;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(name, "name", null);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ name = ic.readString("name", null);
+ extension = getExtension(name);
+ }
+
+}
diff --git a/engine/src/core/com/jme3/asset/AssetLoader.java b/engine/src/core/com/jme3/asset/AssetLoader.java
new file mode 100644
index 000000000..4ebffc54a
--- /dev/null
+++ b/engine/src/core/com/jme3/asset/AssetLoader.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.asset;
+
+import java.io.IOException;
+
+/**
+ * An interface for asset loaders. An AssetLoader
is responsible
+ * for loading a certain type of asset associated with file extension(s).
+ * The loader will load the data in the provided {@link AssetInfo} object by
+ * calling {@link AssetInfo#openStream() }, returning an object representing
+ * the parsed data.
+ */
+public interface AssetLoader {
+
+ /**
+ * Loads asset from the given input stream, parsing it into
+ * an application-usable object.
+ *
+ * @return An object representing the resource.
+ * @throws java.io.IOException If an I/O error occurs while loading
+ */
+ public Object load(AssetInfo assetInfo) throws IOException;
+}
diff --git a/engine/src/core/com/jme3/asset/AssetLocator.java b/engine/src/core/com/jme3/asset/AssetLocator.java
new file mode 100644
index 000000000..c0cb831df
--- /dev/null
+++ b/engine/src/core/com/jme3/asset/AssetLocator.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.asset;
+
+/**
+ * AssetLocator
is used to locate a resource based on an AssetKey.
+ *
+ * @author Kirill Vainer
+ */
+public interface AssetLocator {
+ /**
+ * @param rootPath The root path where to look for assets.
+ * Typically this method will only be called once per
+ * instance of an asset locator.
+ */
+ public void setRootPath(String rootPath);
+
+ /**
+ * Request to locate an asset. The asset key
+ * contains a name identifying the asset.
+ * If an asset was not found, null should be returned.
+ * The {@link AssetInfo} implementation provided should have a proper
+ * return value for its {@link AssetInfo#openStream() } method.
+ *
+ * @param manager
+ * @param key
+ * @return
+ */
+ public AssetInfo locate(AssetManager manager, AssetKey key);
+}
diff --git a/engine/src/core/com/jme3/asset/AssetManager.java b/engine/src/core/com/jme3/asset/AssetManager.java
new file mode 100644
index 000000000..ad44c5151
--- /dev/null
+++ b/engine/src/core/com/jme3/asset/AssetManager.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.asset;
+
+import com.jme3.audio.AudioKey;
+import com.jme3.audio.AudioData;
+import com.jme3.font.BitmapFont;
+import com.jme3.material.Material;
+import com.jme3.scene.Spatial;
+import com.jme3.shader.Shader;
+import com.jme3.shader.ShaderKey;
+import com.jme3.texture.Texture;
+
+/**
+ * AssetManager
provides an interface for managing the data assets
+ * of a jME3 application.
+ */
+public interface AssetManager {
+
+ /**
+ * Registers a loader for the given extensions.
+ * @param loaderClassName
+ * @param extensions
+ */
+ public void registerLoader(String loaderClassName, String ... extensions);
+
+ /**
+ * Registers an {@link AssetLocator} by using a class name, instead of
+ * a class instance. See the {@link AssetManager#registerLocator(java.lang.String, java.lang.Class) }
+ * method for more information.
+ *
+ * @param rootPath The root path from which to locate assets, implementation
+ * dependent.
+ * @param locatorClassName The full class name of the {@link AssetLocator}
+ * implementation.
+ */
+ public void registerLocator(String rootPath, String locatorClassName);
+
+ /**
+ *
+ * @param loaderClass
+ * @param extensions
+ */
+ public void registerLoader(Class extends AssetLoader> loaderClass, String ... extensions);
+
+ /**
+ * Registers the given locator class for locating assets with this
+ * AssetManager
. {@link AssetLocator}s are invoked in the order
+ * they were registered, to locate the asset by the {@link AssetKey}.
+ * Once an {@link AssetLocator} returns a non-null AssetInfo, it is sent
+ * to the {@link AssetLoader} to load the asset.
+ *
+ * @param rootPath Specifies the root path from which to locate assets
+ * for the given {@link AssetLocator}. The purpose of this parameter
+ * depends on the type of the {@link AssetLocator}.
+ * @param locatorClass The class type of the {@link AssetLocator} to register.
+ *
+ * @see AssetLocator#setRootPath(java.lang.String)
+ * @see AssetLocator#locate(com.jme3.asset.AssetManager, com.jme3.asset.AssetKey)
+ */
+ public void registerLocator(String rootPath, Class extends AssetLocator> locatorClass);
+
+ /**
+ * Set an {@link AssetEventListener} to receive events from this
+ * AssetManager
. There can only be one {@link AssetEventListener}
+ * associated with an AssetManager
+ *
+ * @param listener
+ */
+ public void setAssetEventListener(AssetEventListener listener);
+
+ /**
+ * Manually locates an asset with the given {@link AssetKey}. This method
+ * should be used for debugging or internal uses.
+ * The call will attempt to locate the asset by invoking the
+ * {@link AssetLocator} that are registered with this AssetManager
,
+ * in the same way that the {@link AssetManager#loadAsset(com.jme3.asset.AssetKey) }
+ * method locates assets.
+ *
+ * @param key The {@link AssetKey} to locate.
+ * @return The {@link AssetInfo} object returned from the {@link AssetLocator}
+ * that located the asset, or null if the asset cannot be located.
+ */
+ public AssetInfo locateAsset(AssetKey> key);
+
+ /**
+ * Load an asset from a key, the asset will be located
+ * by one of the {@link AssetLocator} implementations provided in the
+ * {@link AssetManager#registerLocator(java.lang.String, java.lang.Class) }
+ * call. If located successfully, it will be loaded via the the appropriate
+ * {@link AssetLoader} implementation based on the file's extension, as
+ * specified in the call
+ * {@link AssetManager#registerLoader(java.lang.Class, java.lang.String[]) }.
+ *
+ * @param The object type that will be loaded from the AssetKey instance.
+ * @param key The AssetKey
+ * @return The loaded asset, or null if it was failed to be located
+ * or loaded.
+ */
+ public T loadAsset(AssetKey key);
+
+ /**
+ * Load a named asset by name, calling this method
+ * is the same as calling
+ *
+ * loadAsset(new AssetKey(name)).
+ *
+ *
+ * @param name The name of the asset to load.
+ * @return The loaded asset, or null if failed to be loaded.
+ *
+ * @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
+ */
+ public Object loadAsset(String name);
+
+ /**
+ * Loads texture file, supported types are BMP, JPG, PNG, GIF,
+ * TGA and DDS.
+ *
+ * @param key The {@link TextureKey} to use for loading.
+ * @return The loaded texture, or null if failed to be loaded.
+ *
+ * @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
+ */
+ public Texture loadTexture(TextureKey key);
+
+ /**
+ * Loads texture file, supported types are BMP, JPG, PNG, GIF,
+ * TGA and DDS.
+ *
+ * @param name The name of the texture to load.
+ * @return
+ *
+ * @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
+ */
+ public Texture loadTexture(String name);
+
+ /**
+ * Load audio file, supported types are WAV or OGG.
+ * @param key
+ * @return
+ *
+ * @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
+ */
+ public AudioData loadAudio(AudioKey key);
+
+ /**
+ * Load audio file, supported types are WAV or OGG.
+ * The file is loaded without stream-mode.
+ * @param name
+ * @return
+ *
+ * @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
+ */
+ public AudioData loadAudio(String name);
+
+ /**
+ * Loads a named model. Models can be jME3 object files (J3O) or
+ * OgreXML/OBJ files.
+ * @param key
+ * @return
+ *
+ * @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
+ */
+ public Spatial loadModel(ModelKey key);
+
+ /**
+ * Loads a named model. Models can be jME3 object files (J3O) or
+ * OgreXML/OBJ files.
+ * @param name
+ * @return
+ *
+ * @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
+ */
+ public Spatial loadModel(String name);
+
+ /**
+ * Load a material (J3M) file.
+ * @param name
+ * @return
+ *
+ * @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
+ */
+ public Material loadMaterial(String name);
+
+ /**
+ * Loads shader file(s), shouldn't be used by end-user in most cases.
+ *
+ * @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
+ */
+ public Shader loadShader(ShaderKey key);
+
+ /**
+ * Load a font file. Font files are in AngelCode text format,
+ * and are with the extension "fnt".
+ *
+ * @param name
+ * @return
+ *
+ * @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
+ */
+ public BitmapFont loadFont(String name);
+}
diff --git a/engine/src/core/com/jme3/asset/ModelKey.java b/engine/src/core/com/jme3/asset/ModelKey.java
new file mode 100644
index 000000000..42f1e0c79
--- /dev/null
+++ b/engine/src/core/com/jme3/asset/ModelKey.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.asset;
+
+import com.jme3.scene.Spatial;
+
+/**
+ *
+ * @author Kirill Vainer
+ */
+public class ModelKey extends AssetKey {
+
+ public ModelKey(String name){
+ super(name);
+ }
+
+ public ModelKey(){
+ super();
+ }
+
+ @Override
+ public Object createClonedInstance(Object asset){
+ Spatial model = (Spatial) asset;
+ return model.clone();
+ }
+
+}
diff --git a/engine/src/core/com/jme3/asset/TextureKey.java b/engine/src/core/com/jme3/asset/TextureKey.java
new file mode 100644
index 000000000..d3d778df6
--- /dev/null
+++ b/engine/src/core/com/jme3/asset/TextureKey.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.asset;
+
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.OutputCapsule;
+import com.jme3.texture.Image;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture2D;
+import com.jme3.texture.TextureCubeMap;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+public class TextureKey extends AssetKey {
+
+ private boolean generateMips;
+ private boolean flipY;
+ private boolean asCube;
+ private int anisotropy;
+
+ public TextureKey(String name, boolean flipY){
+ super(name);
+ this.flipY = flipY;
+ }
+
+ public TextureKey(String name){
+ super(name);
+ this.flipY = true;
+ }
+
+ public TextureKey(){
+ }
+
+ @Override
+ public String toString(){
+ return name + (flipY ? " (Flipped)" : "") + (asCube ? " (Cube)" : "") + (generateMips ? " (Mipmaped)" : "");
+ }
+
+ /**
+ * Enable smart caching for textures
+ * @return
+ */
+ @Override
+ public boolean useSmartCache(){
+ return true;
+ }
+
+ public Object createClonedInstance(Object asset){
+ Texture tex = (Texture) asset;
+ return tex.createSimpleClone();
+ }
+
+ @Override
+ public Object postProcess(Object asset){
+ Image img = (Image) asset;
+ if (img == null)
+ return null;
+
+ Texture tex;
+ if (isAsCube()){
+ if (isFlipY()){
+ // also flip -y and +y image in cubemap
+ ByteBuffer pos_y = img.getData(2);
+ img.setData(2, img.getData(3));
+ img.setData(3, pos_y);
+ }
+ tex = new TextureCubeMap();
+ }else{
+ tex = new Texture2D();
+ }
+
+ // enable mipmaps if image has them
+ // or generate them if requested by user
+ if (img.hasMipmaps() || isGenerateMips())
+ tex.setMinFilter(Texture.MinFilter.Trilinear);
+
+ tex.setAnisotropicFilter(getAnisotropy());
+ tex.setName(getName());
+ tex.setImage(img);
+ return tex;
+ }
+
+ public boolean isFlipY() {
+ return flipY;
+ }
+
+ public int getAnisotropy() {
+ return anisotropy;
+ }
+
+ public void setAnisotropy(int anisotropy) {
+ this.anisotropy = anisotropy;
+ }
+
+ public boolean isAsCube() {
+ return asCube;
+ }
+
+ public void setAsCube(boolean asCube) {
+ this.asCube = asCube;
+ }
+
+ public boolean isGenerateMips() {
+ return generateMips;
+ }
+
+ public void setGenerateMips(boolean generateMips) {
+ this.generateMips = generateMips;
+ }
+
+ public void write(JmeExporter ex) throws IOException{
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(flipY, "flip_y", false);
+ oc.write(generateMips, "generate_mips", false);
+ oc.write(asCube, "as_cubemap", false);
+ oc.write(anisotropy, "anisotropy", 0);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException{
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ flipY = ic.readBoolean("flip_y", false);
+ generateMips = ic.readBoolean("generate_mips", false);
+ asCube = ic.readBoolean("as_cubemap", false);
+ anisotropy = ic.readInt("anisotropy", 0);
+ }
+}
diff --git a/engine/src/core/com/jme3/audio/ALObject.java b/engine/src/core/com/jme3/audio/ALObject.java
new file mode 100644
index 000000000..1076d234e
--- /dev/null
+++ b/engine/src/core/com/jme3/audio/ALObject.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.audio;
+
+public abstract class ALObject {
+
+ protected int id = -1;
+ protected Object handleRef = null;
+ protected boolean updateNeeded = true;
+
+ public void setId(int id){
+ if (this.id != -1)
+ throw new IllegalStateException("ID has already been set for this AL object.");
+
+ this.id = id;
+ }
+
+ public int getId(){
+ return id;
+ }
+
+ public void setUpdateNeeded(){
+ updateNeeded = true;
+ }
+
+ public void clearUpdateNeeded(){
+ updateNeeded = false;
+ }
+
+ public boolean isUpdateNeeded(){
+ return updateNeeded;
+ }
+
+ @Override
+ public String toString(){
+ return getClass().getSimpleName() + " " + Integer.toHexString(hashCode());
+ }
+
+ public abstract void resetObject();
+
+ public abstract void deleteObject(AudioRenderer r);
+
+}
diff --git a/engine/src/core/com/jme3/audio/AudioBuffer.java b/engine/src/core/com/jme3/audio/AudioBuffer.java
new file mode 100644
index 000000000..ed5dbefcd
--- /dev/null
+++ b/engine/src/core/com/jme3/audio/AudioBuffer.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.audio;
+
+import com.jme3.audio.AudioData.DataType;
+import java.nio.ByteBuffer;
+
+/**
+ * An AudioBuffer
is an implementation of AudioData
+ * where the audio is buffered (stored in memory). All parts of it
+ * are accessable at any time.
+ * AudioBuffers are useful for short sounds, like effects, etc.
+ *
+ * @author Kirill
+ */
+public class AudioBuffer extends AudioData {
+
+ /**
+ * The audio data buffer. Should be direct and native ordered.
+ */
+ protected ByteBuffer audioData;
+
+ public AudioBuffer(){
+ }
+
+ public DataType getDataType() {
+ return DataType.Buffer;
+ }
+
+ /**
+ * @return The duratiion of the audio in seconds. It is expected
+ * that audio is uncompressed.
+ */
+ public float getDuration(){
+ int bytesPerSec = (bitsPerSample / 8) * channels * sampleRate;
+ if (audioData != null)
+ return (float) audioData.capacity() / bytesPerSec;
+ else
+ return Float.NaN; // unknown
+ }
+
+ @Override
+ public String toString(){
+ return getClass().getSimpleName() +
+ "[id="+id+", ch="+channels+", bits="+bitsPerSample +
+ ", rate="+sampleRate+", duration="+getDuration()+"]";
+ }
+
+ /**
+ * Update the data in the buffer with new data.
+ * @param data
+ */
+ public void updateData(ByteBuffer data){
+ this.audioData = data;
+ updateNeeded = true;
+ }
+
+ /**
+ * @return The buffered audio data.
+ */
+ public ByteBuffer getData(){
+ return audioData;
+ }
+
+ public void resetObject() {
+ id = -1;
+ setUpdateNeeded();
+ }
+
+ public void deleteObject(AudioRenderer ar) {
+ ar.deleteAudioData(this);
+ }
+
+}
diff --git a/engine/src/core/com/jme3/audio/AudioData.java b/engine/src/core/com/jme3/audio/AudioData.java
new file mode 100644
index 000000000..33492305a
--- /dev/null
+++ b/engine/src/core/com/jme3/audio/AudioData.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.audio;
+
+/**
+ * AudioData
is an abstract representation
+ * of audio data. There are two ways to handle audio data, short audio files
+ * are to be stored entirely in memory, while long audio files (music) is
+ * streamed from the hard drive as it is played.
+ *
+ * @author Kirill
+ */
+public abstract class AudioData extends ALObject {
+
+ protected int sampleRate;
+ protected int channels;
+ protected int bitsPerSample;
+
+ public enum DataType {
+ Buffer,
+ Stream
+ }
+
+ /**
+ * @return The data type, either Buffer
or Stream
.
+ */
+ public abstract DataType getDataType();
+
+ /**
+ * @return the duration in seconds of the audio clip.
+ */
+ public abstract float getDuration();
+
+ /**
+ * @return Bits per single sample from a channel.
+ */
+ public int getBitsPerSample() {
+ return bitsPerSample;
+ }
+
+ /**
+ * @return Number of channels. 1 for mono, 2 for stereo, etc.
+ */
+ public int getChannels() {
+ return channels;
+ }
+
+ /**
+ * @return The sample rate, or how many samples per second.
+ */
+ public int getSampleRate() {
+ return sampleRate;
+ }
+
+ /**
+ * Setup the format of the audio data.
+ * @param channels # of channels, 1 = mono, 2 = stereo
+ * @param bitsPerSample Bits per sample, e.g 8 bits, 16 bits.
+ * @param sampleRate Sample rate, 44100, 22050, etc.
+ */
+ public void setupFormat(int channels, int bitsPerSample, int sampleRate){
+ if (id != -1)
+ throw new IllegalStateException("Already set up");
+
+ this.channels = channels;
+ this.bitsPerSample = bitsPerSample;
+ this.sampleRate = sampleRate;
+ }
+
+}
diff --git a/engine/src/core/com/jme3/audio/AudioKey.java b/engine/src/core/com/jme3/audio/AudioKey.java
new file mode 100644
index 000000000..616b5eb68
--- /dev/null
+++ b/engine/src/core/com/jme3/audio/AudioKey.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.audio;
+
+import com.jme3.asset.AssetKey;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.OutputCapsule;
+import java.io.IOException;
+
+/**
+ * AudioKey
is extending AssetKey by holding stream flag.
+ *
+ * @author Kirill
+ */
+public class AudioKey extends AssetKey {
+
+ private boolean stream;
+
+ /**
+ * Create a new AudioKey
+ *
+ * @param name Name of the asset
+ * @param stream If true, the audio will be streamed from harddrive,
+ * otherwise it will be buffered entirely and then played.
+ */
+ public AudioKey(String name, boolean stream){
+ super(name);
+ this.stream = stream;
+ }
+
+ public AudioKey(String name){
+ super(name);
+ this.stream = false;
+ }
+
+ public AudioKey(){
+ }
+
+ @Override
+ public String toString(){
+ return name + (stream ? "/S" : "");
+ }
+
+ public boolean isStream() {
+ return stream;
+ }
+
+ public boolean shouldCache(){
+ return !stream;
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException{
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(stream, "do_stream", false);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException{
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ stream = ic.readBoolean("do_stream", false);
+ }
+
+}
diff --git a/engine/src/core/com/jme3/audio/AudioNode.java b/engine/src/core/com/jme3/audio/AudioNode.java
new file mode 100644
index 000000000..c7f177d7e
--- /dev/null
+++ b/engine/src/core/com/jme3/audio/AudioNode.java
@@ -0,0 +1,393 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.audio;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Node;
+import java.io.IOException;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class AudioNode extends Node {
+
+ protected AudioRenderer renderer;
+
+ protected boolean loop = false;
+ protected float volume = 1;
+ protected float pitch = 1;
+ protected float timeOffset = 0;
+ protected Filter dryFilter;
+ protected AudioKey key;
+ protected transient AudioData data = null;
+ protected transient Status status = Status.Stopped;
+ protected transient int channel = -1;
+ protected Vector3f velocity = new Vector3f();
+ protected boolean reverbEnabled = true;
+ protected float maxDistance = 20; // 20 meters
+ protected float refDistance = 10; // 10 meters
+ protected Filter reverbFilter;
+ private boolean directional = false;
+ protected Vector3f direction = new Vector3f(0, 0, 1);
+ protected float innerAngle = 360;
+ protected float outerAngle = 360;
+ private boolean positional = true;
+
+ public enum Status {
+ Playing,
+ Paused,
+ Stopped,
+ }
+
+ public AudioNode() {
+ }
+
+ public AudioNode(AudioData ad, AudioKey key) {
+ this();
+ setAudioData(ad, key);
+ }
+
+ public AudioNode(AssetManager manager, String name, boolean stream) {
+ this();
+ this.key = new AudioKey(name, stream);
+ this.data = (AudioData) manager.loadAsset(key);
+ }
+
+ public AudioNode(AssetManager manager, String name) {
+ this(manager, name, false);
+ }
+
+ public void setChannel(AudioRenderer renderer, int channel) {
+ if (status != Status.Stopped) {
+ throw new IllegalStateException("Can only set source id when stopped");
+ }
+
+ this.renderer = renderer;
+ this.channel = channel;
+ }
+
+ public int getChannel() {
+ return channel;
+ }
+
+ public Filter getDryFilter() {
+ return dryFilter;
+ }
+
+ public void setDryFilter(Filter dryFilter) {
+ if (this.dryFilter != null) {
+ throw new IllegalStateException("Filter already set");
+ }
+
+ this.dryFilter = dryFilter;
+ if (renderer != null)
+ renderer.updateSourceParam(this, AudioParam.DryFilter);
+ }
+
+ public void setAudioData(AudioData ad, AudioKey key) {
+ if (data != null) {
+ throw new IllegalStateException("Cannot change data once its set");
+ }
+
+ data = ad;
+ this.key = key;
+ }
+
+ public AudioData getAudioData() {
+ return data;
+ }
+
+ public Status getStatus() {
+ return status;
+ }
+
+ public void setStatus(Status status) {
+ this.status = status;
+ }
+
+ public boolean isLooping() {
+ return loop;
+ }
+
+ public void setLooping(boolean loop) {
+ this.loop = loop;
+ if (renderer != null)
+ renderer.updateSourceParam(this, AudioParam.Looping);
+ }
+
+ public float getPitch() {
+ return pitch;
+ }
+
+ public void setPitch(float pitch) {
+ if (pitch < 0.5f || pitch > 2.0f) {
+ throw new IllegalArgumentException("Pitch must be between 0.5 and 2.0");
+ }
+
+ this.pitch = pitch;
+ if (renderer != null)
+ renderer.updateSourceParam(this, AudioParam.Pitch);
+ }
+
+ public float getVolume() {
+ return volume;
+ }
+
+ public void setVolume(float volume) {
+ if (volume < 0f) {
+ throw new IllegalArgumentException("Volume cannot be negative");
+ }
+
+ this.volume = volume;
+ if (renderer != null)
+ renderer.updateSourceParam(this, AudioParam.Volume);
+ }
+
+ public float getTimeOffset() {
+ return timeOffset;
+ }
+
+ public void setTimeOffset(float timeOffset) {
+ if (timeOffset < 0f) {
+ throw new IllegalArgumentException("Time offset cannot be negative");
+ }
+
+ this.timeOffset = timeOffset;
+ }
+
+ public Vector3f getVelocity() {
+ return velocity;
+ }
+
+ public void setVelocity(Vector3f velocity) {
+ this.velocity.set(velocity);
+ if (renderer != null)
+ renderer.updateSourceParam(this, AudioParam.Velocity);
+ }
+
+ public boolean isReverbEnabled() {
+ return reverbEnabled;
+ }
+
+ public void setReverbEnabled(boolean reverbEnabled) {
+ this.reverbEnabled = reverbEnabled;
+ if (renderer != null)
+ renderer.updateSourceParam(this, AudioParam.ReverbEnabled);
+ }
+
+ public Filter getReverbFilter() {
+ return reverbFilter;
+ }
+
+ public void setReverbFilter(Filter reverbFilter) {
+ if (this.reverbFilter != null) {
+ throw new IllegalStateException("Filter already set");
+ }
+
+ this.reverbFilter = reverbFilter;
+ if (renderer != null)
+ renderer.updateSourceParam(this, AudioParam.ReverbFilter);
+ }
+
+ public float getMaxDistance() {
+ return maxDistance;
+ }
+
+ public void setMaxDistance(float maxDistance) {
+ if (maxDistance < 0) {
+ throw new IllegalArgumentException("Max distance cannot be negative");
+ }
+
+ this.maxDistance = maxDistance;
+ if (renderer != null)
+ renderer.updateSourceParam(this, AudioParam.MaxDistance);
+ }
+
+ public float getRefDistance() {
+ return refDistance;
+ }
+
+ public void setRefDistance(float refDistance) {
+ if (refDistance < 0) {
+ throw new IllegalArgumentException("Reference distance cannot be negative");
+ }
+
+ this.refDistance = refDistance;
+ if (renderer != null)
+ renderer.updateSourceParam(this, AudioParam.RefDistance);
+ }
+
+ public boolean isDirectional() {
+ return directional;
+ }
+
+ public void setDirectional(boolean directional) {
+ this.directional = directional;
+ if (renderer != null)
+ renderer.updateSourceParam(this, AudioParam.IsDirectional);
+ }
+
+ public Vector3f getDirection() {
+ return direction;
+ }
+
+ public void setDirection(Vector3f direction) {
+ this.direction = direction;
+ if (renderer != null)
+ renderer.updateSourceParam(this, AudioParam.Direction);
+ }
+
+ public float getInnerAngle() {
+ return innerAngle;
+ }
+
+ public void setInnerAngle(float innerAngle) {
+ this.innerAngle = innerAngle;
+ if (renderer != null)
+ renderer.updateSourceParam(this, AudioParam.InnerAngle);
+ }
+
+ public float getOuterAngle() {
+ return outerAngle;
+ }
+
+ public void setOuterAngle(float outerAngle) {
+ this.outerAngle = outerAngle;
+ if (renderer != null)
+ renderer.updateSourceParam(this, AudioParam.OuterAngle);
+ }
+
+ public boolean isPositional() {
+ return positional;
+ }
+
+ public void setPositional(boolean inHeadspace) {
+ this.positional = inHeadspace;
+ if (renderer != null)
+ renderer.updateSourceParam(this, AudioParam.IsPositional);
+ }
+
+ @Override
+ public void updateGeometricState(){
+ boolean updatePos = false;
+ if ((refreshFlags & RF_TRANSFORM) != 0){
+ updatePos = true;
+ }
+
+ super.updateGeometricState();
+
+ if (updatePos && renderer != null)
+ renderer.updateSourceParam(this, AudioParam.Position);
+ }
+
+ @Deprecated
+ public boolean isUpdateNeeded(){
+ return true;
+ }
+
+ @Deprecated
+ public void clearUpdateNeeded(){
+ }
+
+// @Override
+// public AudioNode clone(){
+// try{
+// return (AudioNode) super.clone();
+// }catch (CloneNotSupportedException ex){
+// return null;
+// }
+// }
+
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(key, "key", null);
+ oc.write(loop, "looping", false);
+ oc.write(volume, "volume", 1);
+ oc.write(pitch, "pitch", 1);
+ oc.write(timeOffset, "time_offset", 0);
+ oc.write(dryFilter, "dry_filter", null);
+
+ oc.write(velocity, "velocity", null);
+ oc.write(reverbEnabled, "reverb_enabled", false);
+ oc.write(reverbFilter, "reverb_filter", null);
+ oc.write(maxDistance, "max_distance", 20);
+ oc.write(refDistance, "ref_distance", 10);
+
+ oc.write(directional, "directional", false);
+ oc.write(direction, "direction", null);
+ oc.write(innerAngle, "inner_angle", 360);
+ oc.write(outerAngle, "outer_angle", 360);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ key = (AudioKey) ic.readSavable("key", null);
+ loop = ic.readBoolean("looping", false);
+ volume = ic.readFloat("volume", 1);
+ pitch = ic.readFloat("pitch", 1);
+ timeOffset = ic.readFloat("time_offset", 0);
+ dryFilter = (Filter) ic.readSavable("dry_filter", null);
+
+ velocity = (Vector3f) ic.readSavable("velocity", null);
+ reverbEnabled = ic.readBoolean("reverb_enabled", false);
+ reverbFilter = (Filter) ic.readSavable("reverb_filter", null);
+ maxDistance = ic.readFloat("max_distance", 20);
+ refDistance = ic.readFloat("ref_distance", 10);
+
+ directional = ic.readBoolean("directional", false);
+ direction = (Vector3f) ic.readSavable("direction", null);
+ innerAngle = ic.readFloat("inner_angle", 360);
+ outerAngle = ic.readFloat("outer_angle", 360);
+ data = im.getAssetManager().loadAudio(key);
+ }
+
+ public String toString() {
+ String ret = getClass().getSimpleName()
+ + "[status=" + status;
+ if (volume != 1f) {
+ ret += ", vol=" + volume;
+ }
+ if (pitch != 1f) {
+ ret += ", pitch=" + pitch;
+ }
+ return ret + "]";
+ }
+}
diff --git a/engine/src/core/com/jme3/audio/AudioParam.java b/engine/src/core/com/jme3/audio/AudioParam.java
new file mode 100644
index 000000000..bf53c24a6
--- /dev/null
+++ b/engine/src/core/com/jme3/audio/AudioParam.java
@@ -0,0 +1,19 @@
+package com.jme3.audio;
+
+public enum AudioParam {
+ Volume,
+ Pitch,
+ Looping,
+ Position,
+ IsPositional,
+ Direction,
+ IsDirectional,
+ Velocity,
+ OuterAngle,
+ InnerAngle,
+ RefDistance,
+ MaxDistance,
+ DryFilter,
+ ReverbFilter,
+ ReverbEnabled;
+}
diff --git a/engine/src/core/com/jme3/audio/AudioRenderer.java b/engine/src/core/com/jme3/audio/AudioRenderer.java
new file mode 100644
index 000000000..468b6aed9
--- /dev/null
+++ b/engine/src/core/com/jme3/audio/AudioRenderer.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.audio;
+
+/**
+ * Interface to be implemented by audio renderers.
+ *
+ * @author Kirill Vainer
+ */
+public interface AudioRenderer {
+
+ /**
+ * @param listener The listener camera, all 3D sounds will be
+ * oriented around the listener.
+ */
+ public void setListener(Listener listener);
+
+ /**
+ * Sets the environment, used for reverb effects.
+ *
+ * @see PointAudioSource#setReverbEnabled(boolean)
+ * @param env The environment to set.
+ */
+ public void setEnvironment(Environment env);
+
+ public void playSourceInstance(AudioNode src);
+ public void playSource(AudioNode src);
+ public void pauseSource(AudioNode src);
+ public void stopSource(AudioNode src);
+
+ public void updateSourceParam(AudioNode src, AudioParam param);
+ public void updateListenerParam(Listener listener, ListenerParam param);
+
+ public void deleteAudioData(AudioData ad);
+
+ /**
+ * Initializes the renderer. Should be the first method called
+ * before using the system.
+ */
+ public void initialize();
+
+ /**
+ * Update the audio system. Must be called periodically.
+ * @param tpf Time per frame.
+ */
+ public void update(float tpf);
+
+ /**
+ * Cleanup/destroy the audio system. Call this when app closes.
+ */
+ public void cleanup();
+}
diff --git a/engine/src/core/com/jme3/audio/AudioStream.java b/engine/src/core/com/jme3/audio/AudioStream.java
new file mode 100644
index 000000000..76fbd327c
--- /dev/null
+++ b/engine/src/core/com/jme3/audio/AudioStream.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.audio;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * AudioStream
is an implementation of AudioData that
+ * acquires the audio from an InputStream. Audio can be streamed
+ * from network, hard drive etc. It is assumed the data coming
+ * from the input stream is uncompressed.
+ *
+ * @author Kirill Vainer
+ */
+public class AudioStream extends AudioData implements Closeable{
+
+ protected InputStream in;
+ private float duration = -1f;
+ private boolean open = false;
+ private int[] ids;
+
+ public AudioStream(){
+ }
+
+ public void updateData(InputStream in, float duration){
+ if (id != -1 || this.in != null)
+ throw new IllegalStateException("Data already set!");
+
+ this.in = in;
+ this.duration = duration;
+ open = true;
+ }
+
+ /**
+ * Reads samples from the stream. The format of the data
+ * depends on the getSampleRate(), getChannels(), getBitsPerSample()
+ * values.
+ *
+ * @param buf Buffer where to read the samples
+ * @param offset The offset in the buffer where to read samples
+ * @param length The length inside the buffer where to read samples
+ * @return number of bytes read.
+ */
+ public int readSamples(byte[] buf, int offset, int length){
+ if (!open)
+ return -1;
+
+ try{
+ return in.read(buf, offset, length);
+ }catch (IOException ex){
+ return -1;
+ }
+ }
+
+ /**
+ * Reads samples from the stream.
+ *
+ * @see AudioStream#readSamples(byte[], int, int)
+ * @param buf Buffer where to read the samples
+ * @return number of bytes read.
+ */
+ public int readSamples(byte[] buf){
+ return readSamples(buf, 0, buf.length);
+ }
+
+ public float getDuration(){
+ return duration;
+ }
+
+ @Override
+ public int getId(){
+ throw new RuntimeException("Don't use getId() on streams");
+ }
+
+ public void setId(int id){
+ throw new RuntimeException("Don't use setId() on streams");
+ }
+
+ public void initIds(int count){
+ ids = new int[count];
+ }
+
+ public int getId(int index){
+ return ids[index];
+ }
+
+ public void setId(int index, int id){
+ ids[index] = id;
+ }
+
+ public int[] getIds(){
+ return ids;
+ }
+
+ public void setIds(int[] ids){
+ this.ids = ids;
+ }
+
+ @Override
+ public DataType getDataType() {
+ return DataType.Stream;
+ }
+
+ @Override
+ public void resetObject() {
+ id = -1;
+ ids = null;
+ setUpdateNeeded();
+ }
+
+ @Override
+ public void deleteObject(AudioRenderer r) {
+ r.deleteAudioData(this);
+ }
+
+ /**
+ * @return Whether the stream is open or not. Reading from a closed
+ * stream will always return eof.
+ */
+ public boolean isOpen(){
+ return open;
+ }
+
+ /**
+ * Closes the stream, releasing all data relating to it. Reading
+ * from the stream will return eof.
+ * @throws IOException
+ */
+ public void close() {
+ if (in != null && open){
+ try{
+ in.close();
+ }catch (IOException ex){
+ }
+ open = false;
+ }else{
+ throw new RuntimeException("AudioStream is already closed!");
+ }
+ }
+
+}
diff --git a/engine/src/core/com/jme3/audio/Environment.java b/engine/src/core/com/jme3/audio/Environment.java
new file mode 100644
index 000000000..9a1279eed
--- /dev/null
+++ b/engine/src/core/com/jme3/audio/Environment.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.audio;
+
+import com.jme3.math.FastMath;
+
+/**
+ * Audio environment, for reverb effects.
+ * @author Kirill
+ */
+public class Environment {
+
+ private float airAbsorbGainHf = 0.99426f;
+ private float roomRolloffFactor = 0;
+
+ private float decayTime = 1.49f;
+ private float decayHFRatio = 0.54f;
+
+ private float density = 1.0f;
+ private float diffusion = 0.3f;
+
+ private float gain = 0.316f;
+ private float gainHf = 0.022f;
+
+ private float lateReverbDelay = 0.088f;
+ private float lateReverbGain = 0.768f;
+
+ private float reflectDelay = 0.162f;
+ private float reflectGain = 0.052f;
+
+ private boolean decayHfLimit = true;
+
+ public static final Environment Garage, Dungeon, Cavern, AcousticLab, Closet;
+
+ static {
+ Garage = new Environment(1, 1, 1, 1, .9f, .5f, .751f, .0039f, .661f, .0137f);
+ Dungeon = new Environment(.75f, 1, 1, .75f, 1.6f, 1, 0.95f, 0.0026f, 0.93f, 0.0103f);
+ Cavern = new Environment(.5f, 1, 1, .5f, 2.25f, 1, .908f, .0103f, .93f, .041f);
+ AcousticLab = new Environment(.5f, 1, 1, 1, .28f, 1, .87f, .002f, .81f, .008f);
+ Closet = new Environment(1, 1, 1, 1, .15f, 1, .6f, .0025f, .5f, .0006f);
+ }
+
+ private static final float eaxDbToAmp(float eaxDb){
+ float dB = eaxDb / 2000f;
+ return FastMath.pow(10f, dB);
+ }
+
+ public Environment(){
+ }
+
+ public Environment(Environment source) {
+ this.airAbsorbGainHf = source.airAbsorbGainHf;
+ this.roomRolloffFactor = source.roomRolloffFactor;
+ this.decayTime = source.decayTime;
+ this.decayHFRatio = source.decayHFRatio;
+ this.density = source.density;
+ this.diffusion = source.diffusion;
+ this.gain = source.gain;
+ this.gainHf = source.gainHf;
+ this.lateReverbDelay = source.lateReverbDelay;
+ this.lateReverbGain = source.lateReverbGain;
+ this.reflectDelay = source.reflectDelay;
+ this.reflectGain = source.reflectGain;
+ this.decayHfLimit = source.decayHfLimit;
+ }
+
+ public Environment(float density, float diffusion, float gain, float gainHf,
+ float decayTime, float decayHf, float reflGain,
+ float reflDelay, float lateGain, float lateDelay){
+ this.decayTime = decayTime;
+ this.decayHFRatio = decayHf;
+ this.density = density;
+ this.diffusion = diffusion;
+ this.gain = gain;
+ this.gainHf = gainHf;
+ this.lateReverbDelay = lateDelay;
+ this.lateReverbGain = lateGain;
+ this.reflectDelay = reflDelay;
+ this.reflectGain = reflGain;
+ }
+
+ public Environment(float[] e){
+ if (e.length != 28)
+ throw new IllegalArgumentException("Not an EAX preset");
+
+ // skip env id
+ // e[0]
+ // skip room size
+ // e[1]
+
+// density = 0;
+ diffusion = e[2];
+ gain = eaxDbToAmp(e[3]); // convert
+ gainHf = eaxDbToAmp(e[4]) / eaxDbToAmp(e[5]); // convert
+ decayTime = e[6];
+ decayHFRatio = e[7] / e[8];
+ reflectGain = eaxDbToAmp(e[9]); // convert
+ reflectDelay = e[10];
+
+ // skip 3 pan values
+ // e[11] e[12] e[13]
+
+ lateReverbGain = eaxDbToAmp(e[14]); // convert
+ lateReverbDelay = e[15];
+
+ // skip 3 pan values
+ // e[16] e[17] e[18]
+
+ // skip echo time, echo damping, mod time, mod damping
+ // e[19] e[20] e[21] e[22]
+
+ airAbsorbGainHf = eaxDbToAmp(e[23]);
+
+ // skip HF Reference and LF Reference
+ // e[24] e[25]
+
+ roomRolloffFactor = e[26];
+
+ // skip flags
+ // e[27]
+ }
+
+ public float getAirAbsorbGainHf() {
+ return airAbsorbGainHf;
+ }
+
+ public void setAirAbsorbGainHf(float airAbsorbGainHf) {
+ this.airAbsorbGainHf = airAbsorbGainHf;
+ }
+
+ public float getDecayHFRatio() {
+ return decayHFRatio;
+ }
+
+ public void setDecayHFRatio(float decayHFRatio) {
+ this.decayHFRatio = decayHFRatio;
+ }
+
+ public boolean isDecayHfLimit() {
+ return decayHfLimit;
+ }
+
+ public void setDecayHfLimit(boolean decayHfLimit) {
+ this.decayHfLimit = decayHfLimit;
+ }
+
+ public float getDecayTime() {
+ return decayTime;
+ }
+
+ public void setDecayTime(float decayTime) {
+ this.decayTime = decayTime;
+ }
+
+ public float getDensity() {
+ return density;
+ }
+
+ public void setDensity(float density) {
+ this.density = density;
+ }
+
+ public float getDiffusion() {
+ return diffusion;
+ }
+
+ public void setDiffusion(float diffusion) {
+ this.diffusion = diffusion;
+ }
+
+ public float getGain() {
+ return gain;
+ }
+
+ public void setGain(float gain) {
+ this.gain = gain;
+ }
+
+ public float getGainHf() {
+ return gainHf;
+ }
+
+ public void setGainHf(float gainHf) {
+ this.gainHf = gainHf;
+ }
+
+ public float getLateReverbDelay() {
+ return lateReverbDelay;
+ }
+
+ public void setLateReverbDelay(float lateReverbDelay) {
+ this.lateReverbDelay = lateReverbDelay;
+ }
+
+ public float getLateReverbGain() {
+ return lateReverbGain;
+ }
+
+ public void setLateReverbGain(float lateReverbGain) {
+ this.lateReverbGain = lateReverbGain;
+ }
+
+ public float getReflectDelay() {
+ return reflectDelay;
+ }
+
+ public void setReflectDelay(float reflectDelay) {
+ this.reflectDelay = reflectDelay;
+ }
+
+ public float getReflectGain() {
+ return reflectGain;
+ }
+
+ public void setReflectGain(float reflectGain) {
+ this.reflectGain = reflectGain;
+ }
+
+ public float getRoomRolloffFactor() {
+ return roomRolloffFactor;
+ }
+
+ public void setRoomRolloffFactor(float roomRolloffFactor) {
+ this.roomRolloffFactor = roomRolloffFactor;
+ }
+}
diff --git a/engine/src/core/com/jme3/audio/Filter.java b/engine/src/core/com/jme3/audio/Filter.java
new file mode 100644
index 000000000..d930d9e4f
--- /dev/null
+++ b/engine/src/core/com/jme3/audio/Filter.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.audio;
+
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.Savable;
+import java.io.IOException;
+
+public class Filter implements Savable {
+
+ protected int id = -1;
+ protected boolean updateNeeded = true;
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public void clearUpdateNeeded(){
+ this.updateNeeded = false;
+ }
+
+ public boolean isUpdateNeeded() {
+ return updateNeeded;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ // nothing to save
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ // nothing to read
+ }
+
+}
diff --git a/engine/src/core/com/jme3/audio/Listener.java b/engine/src/core/com/jme3/audio/Listener.java
new file mode 100644
index 000000000..0b405b653
--- /dev/null
+++ b/engine/src/core/com/jme3/audio/Listener.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.audio;
+
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+
+public class Listener {
+
+ private Vector3f location;
+ private Vector3f velocity;
+ private Quaternion rotation;
+ private float volume = 1;
+ private AudioRenderer renderer;
+
+ public Listener(){
+ location = new Vector3f();
+ velocity = new Vector3f();
+ rotation = new Quaternion();
+ }
+
+ public Listener(Listener source){
+ location = source.location.clone();
+ velocity = source.velocity.clone();
+ rotation = source.rotation.clone();
+ volume = source.volume;
+ }
+
+ public void setRenderer(AudioRenderer renderer){
+ this.renderer = renderer;
+ }
+
+ /**
+ *
+ * @return
+ * @deprecated Use {@link Listener#getVolume() }
+ */
+ @Deprecated
+ public float getGain() {
+ return getVolume();
+ }
+
+ /**
+ *
+ * @param gain
+ * @deprecated Use {@link Listener#setVolume(float) }
+ */
+ @Deprecated
+ public void setGain(float gain) {
+ setVolume(gain);
+ }
+
+ public float getVolume() {
+ return volume;
+ }
+
+ public void setVolume(float volume) {
+ this.volume = volume;
+ if (renderer != null)
+ renderer.updateListenerParam(this, ListenerParam.Volume);
+ }
+
+ public Vector3f getLocation() {
+ return location;
+ }
+
+ public Quaternion getRotation() {
+ return rotation;
+ }
+
+ public Vector3f getVelocity() {
+ return velocity;
+ }
+
+ public Vector3f getLeft(){
+ return rotation.getRotationColumn(0);
+ }
+
+ public Vector3f getUp(){
+ return rotation.getRotationColumn(1);
+ }
+
+ public Vector3f getDirection(){
+ return rotation.getRotationColumn(2);
+ }
+
+ public void setLocation(Vector3f location) {
+ this.location.set(location);
+ if (renderer != null)
+ renderer.updateListenerParam(this, ListenerParam.Position);
+ }
+
+ public void setRotation(Quaternion rotation) {
+ this.rotation.set(rotation);
+ if (renderer != null)
+ renderer.updateListenerParam(this, ListenerParam.Rotation);
+ }
+
+ public void setVelocity(Vector3f velocity) {
+ this.velocity.set(velocity);
+ if (renderer != null)
+ renderer.updateListenerParam(this, ListenerParam.Velocity);
+ }
+
+ @Deprecated
+ public boolean isRefreshNeeded(){
+ return true;
+ }
+
+ public void clearRefreshNeeded(){
+ }
+
+}
diff --git a/engine/src/core/com/jme3/audio/ListenerParam.java b/engine/src/core/com/jme3/audio/ListenerParam.java
new file mode 100644
index 000000000..6b3b55e07
--- /dev/null
+++ b/engine/src/core/com/jme3/audio/ListenerParam.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.audio;
+
+public enum ListenerParam {
+ Position,
+ Velocity,
+ Rotation,
+ Volume;
+}
diff --git a/engine/src/core/com/jme3/audio/LowPassFilter.java b/engine/src/core/com/jme3/audio/LowPassFilter.java
new file mode 100644
index 000000000..d2b633239
--- /dev/null
+++ b/engine/src/core/com/jme3/audio/LowPassFilter.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.audio;
+
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.OutputCapsule;
+import java.io.IOException;
+
+public class LowPassFilter extends Filter {
+
+ protected float volume, highFreqVolume;
+
+ public LowPassFilter(float volume, float highFreqVolume) {
+ setVolume(volume);
+ setHighFreqVolume(highFreqVolume);
+ }
+
+ public float getHighFreqVolume() {
+ return highFreqVolume;
+ }
+
+ public void setHighFreqVolume(float highFreqVolume) {
+ if (highFreqVolume < 0 || highFreqVolume > 1)
+ throw new IllegalArgumentException("High freq volume must be between 0 and 1");
+
+ this.highFreqVolume = highFreqVolume;
+ this.updateNeeded = true;
+ }
+
+ public float getVolume() {
+ return volume;
+ }
+
+ public void setVolume(float volume) {
+ if (volume < 0 || volume > 1)
+ throw new IllegalArgumentException("Volume must be between 0 and 1");
+
+ this.volume = volume;
+ this.updateNeeded = true;
+ }
+
+ public void write(JmeExporter ex) throws IOException{
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(volume, "volume", 0);
+ oc.write(highFreqVolume, "hf_volume", 0);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException{
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ volume = ic.readFloat("volume", 0);
+ highFreqVolume = ic.readFloat("hf_volume", 0);
+ }
+
+}
diff --git a/engine/src/core/com/jme3/audio/QueuedAudioRenderer.java b/engine/src/core/com/jme3/audio/QueuedAudioRenderer.java
new file mode 100644
index 000000000..1e6121700
--- /dev/null
+++ b/engine/src/core/com/jme3/audio/QueuedAudioRenderer.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.audio;
+
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.Queue;
+
+@Deprecated
+public class QueuedAudioRenderer implements AudioRenderer, Runnable {
+
+ private static final float UPDATE_RATE = 0.01f;
+
+ private AudioRenderer wrapped;
+ private final Thread thread = new Thread(this, "jME3 Audio Thread");
+ private final Queue commandQueue = new LinkedList();
+
+ public void updateListenerParam(Listener listener, ListenerParam param) {
+ }
+
+ private enum CmdType {
+ Cleanup,
+ SetListenerParams,
+ SetEnvParams,
+ PlaySourceInstance,
+ PlaySource,
+ PauseSource,
+ StopSource,
+ }
+
+ private static class Command {
+
+ private CmdType type;
+ private Object[] args;
+
+ public Command(CmdType type, Object ... args) {
+ this.type = type;
+ this.args = args;
+ }
+
+ }
+
+ public QueuedAudioRenderer(AudioRenderer toWrap){
+ wrapped = toWrap;
+ }
+
+ public void run(){
+ wrapped.initialize();
+ long updateRateNanos = (long) (UPDATE_RATE * 1000000000);
+ mainloop: while (true){
+ long startTime = System.nanoTime();
+
+ // execute commands and update
+ synchronized (thread){
+ while (commandQueue.size() > 0){
+ Command cmd = commandQueue.remove();
+ if (cmd.type == CmdType.Cleanup)
+ break mainloop;
+
+ executeCommand(cmd);
+ }
+ }
+ wrapped.update(UPDATE_RATE);
+
+ long endTime = System.nanoTime();
+ long diffTime = endTime - startTime;
+
+ if (diffTime < updateRateNanos){
+ long desiredEndTime = startTime + updateRateNanos;
+ while (System.nanoTime() < desiredEndTime){
+ try{
+ Thread.sleep(1);
+ }catch (InterruptedException ex){
+ }
+ }
+ }
+ }
+ commandQueue.clear();
+ wrapped.cleanup();
+ }
+
+ private void enqueueCommand(Command cmd){
+ synchronized (thread){
+ commandQueue.add(cmd);
+ }
+ }
+
+ public void initialize(){
+ if (!thread.isAlive()){
+ thread.setDaemon(true);
+ thread.setPriority(Thread.NORM_PRIORITY+1);
+ thread.start();
+ }else{
+ throw new IllegalStateException("Initialize already called");
+ }
+ }
+
+ public void cleanup(){
+ if (thread.isAlive()){
+ enqueueCommand(new Command(CmdType.Cleanup, (Object)null ));
+ }else{
+ throw new IllegalStateException("Already cleaned up or not initialized");
+ }
+ }
+
+ private void executeCommand(Command cmd){
+ System.out.println("-> " + cmd.type + Arrays.toString(cmd.args));
+ switch (cmd.type){
+ case SetListenerParams:
+ wrapped.setListener( (Listener) cmd.args[0]);
+ break;
+ case PlaySource:
+ wrapped.playSource( (AudioNode) cmd.args[0] );
+ break;
+ case PauseSource:
+ wrapped.pauseSource( (AudioNode) cmd.args[0] );
+ break;
+ case StopSource:
+ wrapped.stopSource( (AudioNode) cmd.args[0] );
+ break;
+ }
+ }
+
+ public void updateSourceParam(AudioNode node, AudioParam param){
+
+ }
+
+ public void setListener(Listener listener){
+ enqueueCommand(new Command(CmdType.SetListenerParams, new Listener(listener)));
+ }
+
+ public void setEnvironment(Environment env){
+ enqueueCommand(new Command(CmdType.SetEnvParams, new Environment(env)));
+ }
+
+ public void playSourceInstance(AudioNode src){
+ enqueueCommand(new Command(CmdType.PlaySourceInstance, src));
+ }
+
+ public void playSource(AudioNode src){
+ enqueueCommand(new Command(CmdType.PlaySource, src));
+ }
+
+ public void pauseSource(AudioNode src){
+ enqueueCommand(new Command(CmdType.PauseSource, src));
+ }
+
+ public void stopSource(AudioNode src){
+ enqueueCommand(new Command(CmdType.StopSource, src));
+ }
+
+ public void deleteAudioData(AudioData ad){
+ }
+
+ public void update(float tpf){
+ // do nothing. updated in thread.
+ }
+
+}
diff --git a/engine/src/core/com/jme3/audio/plugins/WAVLoader.java b/engine/src/core/com/jme3/audio/plugins/WAVLoader.java
new file mode 100644
index 000000000..39bb133f9
--- /dev/null
+++ b/engine/src/core/com/jme3/audio/plugins/WAVLoader.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.audio.plugins;
+
+import com.jme3.audio.AudioBuffer;
+import com.jme3.audio.AudioData;
+import com.jme3.audio.AudioStream;
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetLoader;
+import com.jme3.audio.AudioKey;
+import com.jme3.asset.TextureKey;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.LittleEndien;
+import java.io.DataInput;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.logging.Logger;
+
+public class WAVLoader implements AssetLoader {
+
+ private static final Logger logger = Logger.getLogger(WAVLoader.class.getName());
+
+ // all these are in big endian
+ private static final int i_RIFF = 0x46464952;
+ private static final int i_WAVE = 0x45564157;
+ private static final int i_fmt = 0x20746D66 ;
+ private static final int i_data = 0x61746164;
+
+ private static final int[] index_table =
+ {
+ -1, -1, -1, -1, 2, 4, 6, 8,
+ -1, -1, -1, -1, 2, 4, 6, 8
+ };
+ private static final int[] step_table =
+ {
+ 7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
+ 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
+ 50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
+ 130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
+ 337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
+ 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
+ 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
+ 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
+ 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
+ };
+
+ private boolean readStream = false;
+
+ private AudioBuffer audioBuffer;
+ private AudioStream audioStream;
+ private AudioData audioData;
+ private int bytesPerSec;
+ private int dataLength;
+ private float duration;
+
+ private DataInput in;
+ private InputStream stream;
+
+ private boolean adpcm = false;
+ private int predictor;
+ private int step_index;
+ private int step;
+
+ private void readFormatChunk(int size) throws IOException{
+ // if other compressions are supported, size doesn't have to be 16
+// if (size != 16)
+// logger.warning("Expected size of format chunk to be 16");
+
+ int compression = in.readShort();
+ if (compression == 1){
+
+ }else if (compression == 17){
+ adpcm = true;
+ }else{
+ throw new IOException("WAV Loader only supports PCM or ADPCM wave files");
+ }
+
+ int channels = in.readShort();
+ int sampleRate = in.readInt();
+
+ bytesPerSec = in.readInt(); // used to calculate duration
+
+ int bytesPerSample = in.readShort();
+ int bitsPerSample = in.readShort();
+
+ int expectedBytesPerSec = (bitsPerSample * channels * sampleRate) / 8;
+ if (expectedBytesPerSec != bytesPerSec){
+ logger.warning("Expected "+expectedBytesPerSec+" bytes per second, got "+bytesPerSec);
+ }
+ duration = dataLength / bytesPerSec;
+
+ if (!adpcm){
+ if (bitsPerSample != 8 && bitsPerSample != 16)
+ throw new IOException("Only 8 and 16 bits per sample are supported!");
+
+ if ( (bitsPerSample / 8) * channels != bytesPerSample)
+ throw new IOException("Invalid bytes per sample value");
+
+ if (bytesPerSample * sampleRate != bytesPerSec)
+ throw new IOException("Invalid bytes per second value");
+
+ audioData.setupFormat(channels, bitsPerSample, sampleRate);
+
+ int remaining = size - 16;
+ if (remaining > 0)
+ in.skipBytes(remaining);
+ }else{
+ if (bitsPerSample != 4)
+ throw new IOException("IMA ADPCM header currupt");
+
+ predictor = in.readShort();
+ step_index = in.readByte(); // ????
+ int what = in.readByte(); // skip reserved byte
+ step = index_table[what];
+
+ audioData.setupFormat(channels, 16, sampleRate);
+ }
+ }
+
+ private int decodeNibble(int nibble){
+ step = step_table[step_index];
+ step_index += index_table[nibble];
+
+ if (step_index < 0)
+ step_index = 0;
+ else if (step_index > 88)
+ step_index = 88;
+
+ boolean sign = (nibble & 8) != 0;
+ int delta = nibble & 7;
+
+ int diff = (2 * delta + 1) * step;
+ if (sign) predictor -= diff;
+ else predictor += diff;
+
+ predictor &= 0xFFFF;
+
+ return predictor;
+ }
+
+// private ByteBuffer decodeAdpcm(int len){
+// dataLength = len * 4; // 4 bits per sample to 16 bits per sample
+// }
+
+ private void readDataChunkForBuffer(int len) throws IOException{
+ dataLength = len;
+ ByteBuffer data = BufferUtils.createByteBuffer(dataLength);
+ byte[] buf = new byte[512];
+ int read = 0;
+ while ( (read = stream.read(buf)) > 0){
+ data.put(buf, 0, read);
+ }
+ data.flip();
+ audioBuffer.updateData(data);
+ stream.close();
+ }
+
+ public Object load(AssetInfo info) throws IOException {
+ this.stream = info.openStream();
+ in = new LittleEndien(stream);
+
+ int sig = in.readInt();
+ if (sig != i_RIFF)
+ throw new IOException("File is not a WAVE file");
+
+ // skip size
+ in.readInt();
+ if (in.readInt() != i_WAVE)
+ throw new IOException("WAVE File does not contain audio");
+
+ readStream = ((AudioKey)info.getKey()).isStream();
+
+ if (readStream){
+ audioStream = new AudioStream();
+ audioData = audioStream;
+ }else{
+ audioBuffer = new AudioBuffer();
+ audioData = audioBuffer;
+ }
+
+ while (true){
+ int type = in.readInt();
+ int len = in.readInt();
+
+ switch (type){
+ case i_fmt:
+ readFormatChunk(len);
+ break;
+ case i_data:
+ if (readStream){
+ audioStream.updateData(stream, duration);
+ }else{
+ readDataChunkForBuffer(len);
+ }
+ return audioData;
+ default:
+ int skipped = in.skipBytes(len);
+ if (skipped <= 0)
+ return null;
+
+ break;
+ }
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/bounding/BoundingBox.java b/engine/src/core/com/jme3/bounding/BoundingBox.java
new file mode 100644
index 000000000..6d9894901
--- /dev/null
+++ b/engine/src/core/com/jme3/bounding/BoundingBox.java
@@ -0,0 +1,960 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.bounding;
+
+import com.jme3.collision.Collidable;
+import com.jme3.collision.CollisionResult;
+import com.jme3.collision.CollisionResults;
+import com.jme3.collision.UnsupportedCollisionException;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import java.io.IOException;
+import java.nio.FloatBuffer;
+
+import com.jme3.math.FastMath;
+import com.jme3.math.Matrix3f;
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Plane;
+import com.jme3.math.Ray;
+import com.jme3.math.Transform;
+import com.jme3.math.Triangle;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Mesh;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.TempVars;
+//import com.jme.scene.TriMesh;
+
+/**
+ *