Alrik 9 years ago
commit f9f3c0185e
  1. 17
      .gitignore
  2. 20
      .travis.yml
  3. 7
      CONTRIBUTING.md
  4. 32
      build.gradle
  5. 47
      common.gradle
  6. 4
      gradle.properties
  7. 4
      jme3-android/src/main/java/com/jme3/system/android/JmeAndroidSystem.java
  8. 4
      jme3-blender/src/main/java/com/jme3/asset/BlenderKey.java
  9. 77
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java
  10. 137
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java
  11. 86
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderLoader.java
  12. 11
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/Ipo.java
  13. 23
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionIK.java
  14. 2
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/curves/CurvesTemporalMesh.java
  15. 1
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/landscape/LandscapeHelper.java
  16. 2
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/materials/MaterialContext.java
  17. 8
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/TemporalMesh.java
  18. 8
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/SubdivisionSurfaceModifier.java
  19. 3
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/CombinedTexture.java
  20. 1
      jme3-bullet-native/src/native/cpp/com_jme3_bullet_collision_shapes_GImpactCollisionShape.cpp
  21. 57
      jme3-bullet-native/src/native/cpp/com_jme3_bullet_objects_PhysicsRigidBody.cpp
  22. 27
      jme3-bullet-native/src/native/cpp/com_jme3_bullet_objects_PhysicsRigidBody.h
  23. 4
      jme3-bullet-native/src/native/cpp/jmeBulletUtil.cpp
  24. 2
      jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/CapsuleCollisionShape.java
  25. 46
      jme3-bullet/src/main/java/com/jme3/bullet/objects/PhysicsRigidBody.java
  26. 63
      jme3-core/build.gradle
  27. 1
      jme3-core/src/main/java/com/jme3/animation/Bone.java
  28. 21
      jme3-core/src/main/java/com/jme3/asset/ImplHandler.java
  29. 38
      jme3-core/src/main/java/com/jme3/audio/openal/ALAudioRenderer.java
  30. 28
      jme3-core/src/main/java/com/jme3/bounding/BoundingBox.java
  31. 38
      jme3-core/src/main/java/com/jme3/bounding/BoundingSphere.java
  32. 7
      jme3-core/src/main/java/com/jme3/bounding/BoundingVolume.java
  33. 48
      jme3-core/src/main/java/com/jme3/bounding/Intersection.java
  34. 2
      jme3-core/src/main/java/com/jme3/cinematic/Cinematic.java
  35. 10
      jme3-core/src/main/java/com/jme3/cinematic/events/AnimationEvent.java
  36. 8
      jme3-core/src/main/java/com/jme3/collision/bih/BIHTree.java
  37. 1
      jme3-core/src/main/java/com/jme3/font/BitmapTextPage.java
  38. 10
      jme3-core/src/main/java/com/jme3/input/DefaultJoystickAxis.java
  39. 3
      jme3-core/src/main/java/com/jme3/input/FlyByCamera.java
  40. 57
      jme3-core/src/main/java/com/jme3/input/InputManager.java
  41. 18
      jme3-core/src/main/java/com/jme3/light/AmbientLight.java
  42. 9
      jme3-core/src/main/java/com/jme3/light/DefaultLightFilter.java
  43. 39
      jme3-core/src/main/java/com/jme3/light/DirectionalLight.java
  44. 28
      jme3-core/src/main/java/com/jme3/light/Light.java
  45. 73
      jme3-core/src/main/java/com/jme3/light/PointLight.java
  46. 156
      jme3-core/src/main/java/com/jme3/light/SpotLight.java
  47. 5
      jme3-core/src/main/java/com/jme3/material/Material.java
  48. 27
      jme3-core/src/main/java/com/jme3/material/TechniqueDef.java
  49. 19
      jme3-core/src/main/java/com/jme3/math/FastMath.java
  50. 71
      jme3-core/src/main/java/com/jme3/math/Matrix4f.java
  51. 44
      jme3-core/src/main/java/com/jme3/math/Transform.java
  52. 13
      jme3-core/src/main/java/com/jme3/post/Filter.java
  53. 18
      jme3-core/src/main/java/com/jme3/post/FilterPostProcessor.java
  54. 2
      jme3-core/src/main/java/com/jme3/renderer/Limits.java
  55. 9
      jme3-core/src/main/java/com/jme3/renderer/RenderContext.java
  56. 20
      jme3-core/src/main/java/com/jme3/renderer/RenderManager.java
  57. 1
      jme3-core/src/main/java/com/jme3/renderer/opengl/GL.java
  58. 2
      jme3-core/src/main/java/com/jme3/renderer/opengl/GL2.java
  59. 1
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLExt.java
  60. 359
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java
  61. 324
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLTracer.java
  62. 2
      jme3-core/src/main/java/com/jme3/renderer/opengl/TextureUtil.java
  63. 29
      jme3-core/src/main/java/com/jme3/scene/BatchNode.java
  64. 30
      jme3-core/src/main/java/com/jme3/scene/Node.java
  65. 1
      jme3-core/src/main/java/com/jme3/scene/debug/Grid.java
  66. 2
      jme3-core/src/main/java/com/jme3/scene/debug/WireFrustum.java
  67. 7
      jme3-core/src/main/java/com/jme3/scene/shape/Curve.java
  68. 1
      jme3-core/src/main/java/com/jme3/scene/shape/Cylinder.java
  69. 1
      jme3-core/src/main/java/com/jme3/scene/shape/Quad.java
  70. 2
      jme3-core/src/main/java/com/jme3/scene/shape/Sphere.java
  71. 2
      jme3-core/src/main/java/com/jme3/shadow/AbstractShadowRenderer.java
  72. 2
      jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowRenderer.java
  73. 2
      jme3-core/src/main/java/com/jme3/shadow/PointLightShadowRenderer.java
  74. 2
      jme3-core/src/main/java/com/jme3/shadow/SpotLightShadowRenderer.java
  75. 31
      jme3-core/src/main/java/com/jme3/system/AppSettings.java
  76. 4
      jme3-core/src/main/java/com/jme3/system/JmeSystemDelegate.java
  77. 66
      jme3-core/src/main/java/com/jme3/system/JmeVersion.java
  78. 5
      jme3-core/src/main/java/com/jme3/system/Platform.java
  79. 2
      jme3-core/src/main/java/com/jme3/texture/Image.java
  80. 14
      jme3-core/src/main/java/com/jme3/texture/Texture.java
  81. 3
      jme3-core/src/main/java/com/jme3/texture/image/DefaultImageRaster.java
  82. 9
      jme3-core/src/main/java/com/jme3/texture/image/LastTextureState.java
  83. 7
      jme3-core/src/main/java/com/jme3/util/BufferUtils.java
  84. 15
      jme3-core/src/main/java/com/jme3/util/ListMap.java
  85. 4
      jme3-core/src/main/java/com/jme3/util/LittleEndien.java
  86. 6
      jme3-core/src/main/java/com/jme3/util/TangentBinormalGenerator.java
  87. 2
      jme3-core/src/main/resources/Common/MatDefs/Gui/Gui.frag
  88. 15
      jme3-core/src/main/resources/Common/MatDefs/Gui/Gui.j3md
  89. 2
      jme3-core/src/main/resources/Common/MatDefs/Gui/Gui.vert
  90. 15
      jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.frag
  91. 14
      jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.vert
  92. 1
      jme3-core/src/main/resources/Common/MatDefs/Misc/ColoredTextured.j3md
  93. 2
      jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.frag
  94. 23
      jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.j3md
  95. 1
      jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.vert
  96. 128
      jme3-core/src/main/resources/Common/MatDefs/Misc/UnshadedNodes.j3md
  97. 5
      jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Basic/TextureFetch.j3sn
  98. 2
      jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Basic/texture.frag
  99. 3
      jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Basic/texture15.frag
  100. 14
      jme3-core/src/main/resources/Common/ShaderLib/GLSL150Compat.glsllib
  101. Some files were not shown because too many files have changed in this diff Show More

17
.gitignore vendored

@ -1,11 +1,13 @@
/.gradle/ /.gradle/
/.nb-gradle/private/ /.nb-gradle/private/
/.nb-gradle/profiles/private/ /.nb-gradle/profiles/private/
/.idea/
/dist/ /dist/
/build/ /build/
/netbeans/ /netbeans/
/sdk/jdks/local/
/jme3-core/build/ /jme3-core/build/
/jme3-core/src/main/java/com/jme3/system/JmeVersion.java /jme3-core/src/main/resources/com/jme3/system/version.properties
/jme3-plugins/build/ /jme3-plugins/build/
/jme3-desktop/build/ /jme3-desktop/build/
/jme3-android-native/build/ /jme3-android-native/build/
@ -80,6 +82,7 @@
/sdk/jme3-vehicle-creator/build/ /sdk/jme3-vehicle-creator/build/
/sdk/jme3-welcome-screen/build/ /sdk/jme3-welcome-screen/build/
/sdk/jme3-glsl-support/build/ /sdk/jme3-glsl-support/build/
/sdk/jme3-dark-laf/build/
/sdk/nbproject/private/ /sdk/nbproject/private/
/sdk/jme3-scenecomposer/nbproject/private/ /sdk/jme3-scenecomposer/nbproject/private/
/sdk/jme3-core/nbproject/private/ /sdk/jme3-core/nbproject/private/
@ -125,6 +128,7 @@
*.so *.so
*.jnilib *.jnilib
*.dylib *.dylib
*.iml
/sdk/dist/ /sdk/dist/
!/jme3-bullet-native/libs/native/windows/x86_64/bulletjme.dll !/jme3-bullet-native/libs/native/windows/x86_64/bulletjme.dll
!/jme3-bullet-native/libs/native/windows/x86/bulletjme.dll !/jme3-bullet-native/libs/native/windows/x86/bulletjme.dll
@ -132,3 +136,14 @@
!/jme3-bullet-native/libs/native/osx/x86_64/libbulletjme.dylib !/jme3-bullet-native/libs/native/osx/x86_64/libbulletjme.dylib
!/jme3-bullet-native/libs/native/linux/x86/libbulletjme.so !/jme3-bullet-native/libs/native/linux/x86/libbulletjme.so
!/jme3-bullet-native/libs/native/linux/x86_64/libbulletjme.so !/jme3-bullet-native/libs/native/linux/x86_64/libbulletjme.so
/.nb-gradle/
/sdk/ant-jme/nbproject/private/
/sdk/nbi/stub/ext/engine/nbproject/private/
/sdk/nbi/stub/ext/components/products/jdk/nbproject/private/
/sdk/nbi/stub/ext/components/products/blender/nbproject/private/
/sdk/nbi/stub/ext/components/products/helloworld/nbproject/private/
/sdk/BasicGameTemplate/nbproject/private/
/sdk/nbi/stub/ext/components/products/jdk/build/
/sdk/nbi/stub/ext/components/products/jdk/dist/
/sdk/jme3-dark-laf/nbproject/private/
jme3-lwjgl3/build/

@ -8,9 +8,9 @@ cache:
- gradle-cache - gradle-cache
- netbeans - netbeans
branches: # branches:
only: # only:
- master # - master
notifications: notifications:
slack: slack:
@ -25,16 +25,26 @@ install:
script: script:
- ./gradlew check - ./gradlew check
- ./gradlew createZipDistribution - ./gradlew createZipDistribution
- "[ $TRAVIS_BRANCH == 'master' ] && [ $TRAVIS_PULL_REQUEST == 'false' ] && ./gradlew uploadArchives || :"
before_deploy:
- export RELEASE_DIST=$(ls build/distributions/*.zip)
deploy: deploy:
provider: releases provider: releases
api_key: api_key:
secure: "KbFiMt0a8FxUKvCJUYwikLYaqqGMn1p6k4OsXnGqwptQZEUIayabNLHeaD2kTNT3e6AY1ETwQLff/lB2LttmIo4g5NWW63g1K3A/HwgnhJwETengiProZ/Udl+ugPeDL/+ar43HUhFq4knBnzFKnEcHAThTPVqH/RMDvZf1UUYI=" secure: PuEsJd6juXBH29ByITW3ntSAyrwWs0IeFvXJ5Y2YlhojhSMtTwkoWeB6YmDJWP4fhzbajk4TQ1HlOX2IxJXSW/8ShOEIUlGXz9fHiST0dkSM+iRAUgC5enCLW5ITPTiem7eY9ZhS9miIam7ngce9jHNMh75PTzZrEJtezoALT9w=
file: build/distributions/jME3.1.0_snapshot-github_2015-06-20.zip file_glob: true
file: "${RELEASE_DIST}"
skip_cleanup: true skip_cleanup: true
on: on:
repo: jMonkeyEngine/jmonkeyengine
tags: true tags: true
before_install:
- git fetch --unshallow
- "[ $TRAVIS_PULL_REQUEST == 'false' ] && openssl aes-256-cbc -K $encrypted_a1949b55824a_key -iv $encrypted_a1949b55824a_iv -in private/www-updater.key.enc -out private/www-updater.key -d || :"
# before_install: # before_install:
# required libs for android build tools # required libs for android build tools
# sudo apt-get update # sudo apt-get update

@ -16,13 +16,6 @@ When you're ready to submit your code, just make a [pull request](https://help.g
- When committing, always be sure to run an update before you commit. If there is a conflict between the latest revision and your patch after the update, then it is your responsibility to track down the update that caused the conflict and determine the issue (and fix it). In the case where the breaking commit has no thread linked (and one cannot be found in the forum), then the contributor should contact an administrator and wait for feedback before committing. - When committing, always be sure to run an update before you commit. If there is a conflict between the latest revision and your patch after the update, then it is your responsibility to track down the update that caused the conflict and determine the issue (and fix it). In the case where the breaking commit has no thread linked (and one cannot be found in the forum), then the contributor should contact an administrator and wait for feedback before committing.
- If your code is committed and it introduces new functionality, please edit the wiki accordingly. We can easily roll back to previous revisions, so just do your best; point us to it and we’ll see if it sticks! - If your code is committed and it introduces new functionality, please edit the wiki accordingly. We can easily roll back to previous revisions, so just do your best; point us to it and we’ll see if it sticks!
**Note to Eclipse users:** The Eclipse [git client does not support https](http://hub.jmonkeyengine.org/forum/topic/problem-cloning-the-new-git-repository/#post-265594). The current workaround is to use the command line to clone the repository.
To import the local repository as a project follow these steps:
1. Add a line 'apply plugin: eclipse' to your common.gradle file in the main project directory.
2. Navigate to the project directory in command line and execute command 'gradle eclipse'. This will load all the dependancies for eclipse.
3. In Eclipse, add the repository as an existing Java Project.
p.s. We will try hold ourselves to a [certain standard](http://www.defmacro.org/2013/04/03/issue-etiquette.html) when it comes to GitHub etiquette. If at any point we fail to uphold this standard, let us know. p.s. We will try hold ourselves to a [certain standard](http://www.defmacro.org/2013/04/03/issue-etiquette.html) when it comes to GitHub etiquette. If at any point we fail to uphold this standard, let us know.
#### Core Contributors #### Core Contributors

@ -10,6 +10,8 @@ buildscript {
} }
apply plugin: 'base' apply plugin: 'base'
apply from: file('version.gradle')
apply from: file('upload.gradle')
// This is applied to all sub projects // This is applied to all sub projects
subprojects { subprojects {
@ -59,8 +61,7 @@ task libDist(dependsOn: subprojects.build) << {
} }
task createZipDistribution(type:Zip,dependsOn:["dist","libDist"], description:"Package the nightly zip distribution"){ task createZipDistribution(type:Zip,dependsOn:["dist","libDist"], description:"Package the nightly zip distribution"){
archiveName "jME"+jmeVersion+"_"+jmeVersionTag+"_"+new Date().format("yyyy-MM-dd")+".zip" archiveName "jME" + jmeFullVersion + ".zip"
into("/") { into("/") {
from {"./dist"} from {"./dist"}
} }
@ -114,7 +115,12 @@ task wrapper(type: Wrapper, description: 'Creates and deploys the Gradle wrapper
gradleVersion = '2.2.1' gradleVersion = '2.2.1'
} }
String findNDK() { ext {
ndkCommandPath = ""
ndkExists = false
}
task configureAndroidNDK {
def ndkBuildFile = "ndk-build" def ndkBuildFile = "ndk-build"
// if windows, use ndk-build.cmd instead // if windows, use ndk-build.cmd instead
if (System.properties['os.name'].toLowerCase().contains('windows')) { if (System.properties['os.name'].toLowerCase().contains('windows')) {
@ -127,27 +133,13 @@ String findNDK() {
if (System.env.ANDROID_NDK != null) { if (System.env.ANDROID_NDK != null) {
ndkBuildPath = System.env.ANDROID_NDK + File.separator + ndkBuildFile ndkBuildPath = System.env.ANDROID_NDK + File.separator + ndkBuildFile
} }
if (new File(ndkBuildPath).exists()) {
return ndkBuildPath
} else {
return null
}
}
boolean checkNdkExists(String ndkCommandPath) { if (new File(ndkBuildPath).exists()) {
// String ndkCommandPath = findNDK() ndkExists = true
if (ndkCommandPath != null && new File(ndkCommandPath).exists()) { ndkCommandPath = ndkBuildPath
return true
} else {
return false
} }
} }
ext {
ndkCommandPath = findNDK()
ndkExists = checkNdkExists(ndkCommandPath)
}
//class IncrementalReverseTask extends DefaultTask { //class IncrementalReverseTask extends DefaultTask {
// @InputDirectory // @InputDirectory
// def File inputDir // def File inputDir

@ -4,10 +4,9 @@
apply plugin: 'java' apply plugin: 'java'
apply plugin: 'maven' apply plugin: 'maven'
apply plugin: 'maven-publish'
group = 'com.jme3' group = 'com.jme3'
version = jmeVersion + '-' + jmeVersionTag version = jmePomVersion
sourceCompatibility = '1.6' sourceCompatibility = '1.6'
[compileJava, compileTestJava]*.options*.encoding = 'UTF-8' [compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
@ -19,9 +18,23 @@ repositories {
} }
} }
configurations {
deployerJars
}
dependencies { dependencies {
// Adding dependencies here will add the dependencies to each subproject. // Adding dependencies here will add the dependencies to each subproject.
testCompile group: 'junit', name: 'junit', version: '4.10' testCompile group: 'junit', name: 'junit', version: '4.12'
testCompile group: 'org.mockito', name: 'mockito-core', version: '2.0.28-beta'
testCompile group: 'org.easytesting', name: 'fest-assert-core', version: '2.0M10'
deployerJars "org.apache.maven.wagon:wagon-ssh:2.9"
}
jar {
manifest {
attributes 'Implementation-Title': 'jMonkeyEngine',
'Implementation-Version': jmeFullVersion
}
} }
javadoc { javadoc {
@ -56,16 +69,18 @@ artifacts {
} }
} }
publishing { uploadArchives {
publications { repositories.mavenDeployer {
maven(MavenPublication) { configuration = configurations.deployerJars
from components.java
artifact sourcesJar // disable this otherwise it will fill up the server with stale jars
artifact javadocJar uniqueVersion = false
repository(url: "scp://updates.jmonkeyengine.org/var/www/updates/maven") {
authentication(userName: "www-updater", privateKey: "private/www-updater.key")
}
pom.withXml { pom.project {
asNode().children().last() + {
resolveStrategy = Closure.DELEGATE_FIRST
name POM_NAME name POM_NAME
description POM_DESCRIPTION description POM_DESCRIPTION
url POM_URL url POM_URL
@ -83,14 +98,6 @@ publishing {
} }
} }
} }
}
}
repositories {
maven {
url "${rootProject.buildDir}/repo" // change to point to your repo, e.g. http://my.org/repo
}
}
} }
task createFolders(description: 'Creates the source folders if they do not exist.') doLast { task createFolders(description: 'Creates the source folders if they do not exist.') doLast {

@ -3,7 +3,9 @@ jmeVersion = 3.1.0
# Version used for application and settings folder, no spaces! # Version used for application and settings folder, no spaces!
jmeMainVersion = 3.1 jmeMainVersion = 3.1
# Version addition pre-alpha-svn, Stable, Beta # Version addition pre-alpha-svn, Stable, Beta
jmeVersionTag = snapshot-github jmeVersionTag = SNAPSHOT
# Increment this each time jmeVersionTag changes but jmeVersion stays the same
jmeVersionTagID = 0
# specify if JavaDoc should be built # specify if JavaDoc should be built
buildJavaDoc = true buildJavaDoc = true

@ -122,9 +122,13 @@ public class JmeAndroidSystem extends JmeSystemDelegate {
return Platform.Android_ARM6; return Platform.Android_ARM6;
} else if (arch.contains("v7")) { } else if (arch.contains("v7")) {
return Platform.Android_ARM7; return Platform.Android_ARM7;
} else if (arch.contains("v8")) {
return Platform.Android_ARM8;
} else { } else {
return Platform.Android_ARM5; // unknown ARM return Platform.Android_ARM5; // unknown ARM
} }
} else if (arch.contains("aarch")) {
return Platform.Android_ARM8;
} else { } else {
return Platform.Android_Other; return Platform.Android_Other;
} }

@ -230,18 +230,22 @@ public class BlenderKey extends ModelKey {
} }
/** /**
* Not used any more.
* This method sets the asset root path. * This method sets the asset root path.
* @param assetRootPath * @param assetRootPath
* the assets root path * the assets root path
*/ */
@Deprecated
public void setAssetRootPath(String assetRootPath) { public void setAssetRootPath(String assetRootPath) {
this.assetRootPath = assetRootPath; this.assetRootPath = assetRootPath;
} }
/** /**
* Not used any more.
* This method returns the asset root path. * This method returns the asset root path.
* @return the asset root path * @return the asset root path
*/ */
@Deprecated
public String getAssetRootPath() { public String getAssetRootPath() {
return assetRootPath; return assetRootPath;
} }

@ -31,8 +31,6 @@
*/ */
package com.jme3.scene.plugins.blender; package com.jme3.scene.plugins.blender;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -40,25 +38,17 @@ import java.util.Map.Entry;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import com.jme3.animation.Animation;
import com.jme3.asset.AssetNotFoundException; import com.jme3.asset.AssetNotFoundException;
import com.jme3.asset.BlenderKey; import com.jme3.asset.BlenderKey;
import com.jme3.export.Savable; import com.jme3.export.Savable;
import com.jme3.light.Light;
import com.jme3.math.FastMath; import com.jme3.math.FastMath;
import com.jme3.math.Quaternion; import com.jme3.math.Quaternion;
import com.jme3.post.Filter;
import com.jme3.renderer.Camera;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType; import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType;
import com.jme3.scene.plugins.blender.file.BlenderFileException; import com.jme3.scene.plugins.blender.file.BlenderFileException;
import com.jme3.scene.plugins.blender.file.Pointer; import com.jme3.scene.plugins.blender.file.Pointer;
import com.jme3.scene.plugins.blender.file.Structure; import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.blender.materials.MaterialContext;
import com.jme3.scene.plugins.blender.meshes.TemporalMesh;
import com.jme3.scene.plugins.blender.objects.Properties; import com.jme3.scene.plugins.blender.objects.Properties;
import com.jme3.texture.Texture;
/** /**
* A purpose of the helper class is to split calculation code into several classes. Each helper after use should be cleared because it can * A purpose of the helper class is to split calculation code into several classes. Each helper after use should be cleared because it can
@ -157,7 +147,6 @@ public abstract class AbstractBlenderHelper {
* @throws BlenderFileException * @throws BlenderFileException
* and exception is throw when problems with reading a blend file occur * and exception is throw when problems with reading a blend file occur
*/ */
@SuppressWarnings("unchecked")
protected Object loadLibrary(Structure id) throws BlenderFileException { protected Object loadLibrary(Structure id) throws BlenderFileException {
Pointer pLib = (Pointer) id.getFieldValue("lib"); Pointer pLib = (Pointer) id.getFieldValue("lib");
if (pLib.isNotNull()) { if (pLib.isNotNull()) {
@ -167,79 +156,21 @@ public abstract class AbstractBlenderHelper {
String path = library.getFieldValue("filepath").toString(); String path = library.getFieldValue("filepath").toString();
if (!blenderContext.getLinkedFeatures().keySet().contains(path)) { if (!blenderContext.getLinkedFeatures().keySet().contains(path)) {
File file = new File(path);
List<String> pathsToCheck = new ArrayList<String>();
String currentPath = file.getName();
do {
pathsToCheck.add(currentPath);
file = file.getParentFile();
if (file != null) {
currentPath = file.getName() + '/' + currentPath;
}
} while (file != null);
Spatial loadedAsset = null; Spatial loadedAsset = null;
BlenderKey blenderKey = null; BlenderKey blenderKey = new BlenderKey(path);
for (String p : pathsToCheck) {
blenderKey = new BlenderKey(p);
blenderKey.setLoadUnlinkedAssets(true); blenderKey.setLoadUnlinkedAssets(true);
try { try {
loadedAsset = blenderContext.getAssetManager().loadAsset(blenderKey); loadedAsset = blenderContext.getAssetManager().loadAsset(blenderKey);
break;// break if no exception was thrown
} catch (AssetNotFoundException e) { } catch (AssetNotFoundException e) {
LOGGER.log(Level.FINEST, "Cannot locate linked resource at path: {0}.", p); LOGGER.log(Level.FINEST, "Cannot locate linked resource at path: {0}.", path);
}
} }
if (loadedAsset != null) { if (loadedAsset != null) {
Map<String, Map<String, Object>> linkedData = loadedAsset.getUserData("linkedData"); Map<String, Map<String, Object>> linkedData = loadedAsset.getUserData("linkedData");
for (Entry<String, Map<String, Object>> entry : linkedData.entrySet()) { for (Entry<String, Map<String, Object>> entry : linkedData.entrySet()) {
String linkedDataFilePath = "this".equals(entry.getKey()) ? path : entry.getKey(); String linkedDataFilePath = "this".equals(entry.getKey()) ? path : entry.getKey();
blenderContext.getLinkedFeatures().put(linkedDataFilePath, entry.getValue());
List<Node> scenes = (List<Node>) entry.getValue().get("scenes");
for (Node scene : scenes) {
blenderContext.addLinkedFeature(linkedDataFilePath, "SC" + scene.getName(), scene);
}
List<Node> objects = (List<Node>) entry.getValue().get("objects");
for (Node object : objects) {
blenderContext.addLinkedFeature(linkedDataFilePath, "OB" + object.getName(), object);
}
List<TemporalMesh> meshes = (List<TemporalMesh>) entry.getValue().get("meshes");
for (TemporalMesh mesh : meshes) {
blenderContext.addLinkedFeature(linkedDataFilePath, "ME" + mesh.getName(), mesh);
}
List<MaterialContext> materials = (List<MaterialContext>) entry.getValue().get("materials");
for (MaterialContext materialContext : materials) {
blenderContext.addLinkedFeature(linkedDataFilePath, "MA" + materialContext.getName(), materialContext);
}
List<Texture> textures = (List<Texture>) entry.getValue().get("textures");
for (Texture texture : textures) {
blenderContext.addLinkedFeature(linkedDataFilePath, "TE" + texture.getName(), texture);
}
List<Texture> images = (List<Texture>) entry.getValue().get("images");
for (Texture image : images) {
blenderContext.addLinkedFeature(linkedDataFilePath, "IM" + image.getName(), image);
}
List<Animation> animations = (List<Animation>) entry.getValue().get("animations");
for (Animation animation : animations) {
blenderContext.addLinkedFeature(linkedDataFilePath, "AC" + animation.getName(), animation);
}
List<Camera> cameras = (List<Camera>) entry.getValue().get("cameras");
for (Camera camera : cameras) {
blenderContext.addLinkedFeature(linkedDataFilePath, "CA" + camera.getName(), camera);
}
List<Light> lights = (List<Light>) entry.getValue().get("lights");
for (Light light : lights) {
blenderContext.addLinkedFeature(linkedDataFilePath, "LA" + light.getName(), light);
}
Spatial sky = (Spatial) entry.getValue().get("sky");
if (sky != null) {
blenderContext.addLinkedFeature(linkedDataFilePath, sky.getName(), sky);
}
List<Filter> filters = (List<Filter>) entry.getValue().get("filters");
for (Filter filter : filters) {
blenderContext.addLinkedFeature(linkedDataFilePath, filter.getName(), filter);
}
} }
} else { } else {
LOGGER.log(Level.WARNING, "No features loaded from path: {0}.", path); LOGGER.log(Level.WARNING, "No features loaded from path: {0}.", path);

@ -44,8 +44,11 @@ import com.jme3.animation.Bone;
import com.jme3.animation.Skeleton; import com.jme3.animation.Skeleton;
import com.jme3.asset.AssetManager; import com.jme3.asset.AssetManager;
import com.jme3.asset.BlenderKey; import com.jme3.asset.BlenderKey;
import com.jme3.light.Light;
import com.jme3.material.Material; import com.jme3.material.Material;
import com.jme3.math.ColorRGBA; import com.jme3.math.ColorRGBA;
import com.jme3.post.Filter;
import com.jme3.renderer.Camera;
import com.jme3.scene.Node; import com.jme3.scene.Node;
import com.jme3.scene.plugins.blender.animations.BlenderAction; import com.jme3.scene.plugins.blender.animations.BlenderAction;
import com.jme3.scene.plugins.blender.animations.BoneContext; import com.jme3.scene.plugins.blender.animations.BoneContext;
@ -55,6 +58,8 @@ import com.jme3.scene.plugins.blender.file.DnaBlockData;
import com.jme3.scene.plugins.blender.file.FileBlockHeader; import com.jme3.scene.plugins.blender.file.FileBlockHeader;
import com.jme3.scene.plugins.blender.file.FileBlockHeader.BlockCode; import com.jme3.scene.plugins.blender.file.FileBlockHeader.BlockCode;
import com.jme3.scene.plugins.blender.file.Structure; import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.blender.materials.MaterialContext;
import com.jme3.texture.Texture;
/** /**
* The class that stores temporary data and manages it during loading the belnd * The class that stores temporary data and manages it during loading the belnd
@ -77,7 +82,7 @@ public class BlenderContext {
/** The asset manager. */ /** The asset manager. */
private AssetManager assetManager; private AssetManager assetManager;
/** The blocks read from the file. */ /** The blocks read from the file. */
protected List<FileBlockHeader> blocks; protected List<FileBlockHeader> blocks = new ArrayList<FileBlockHeader>();
/** /**
* A map containing the file block headers. The key is the old memory address. * A map containing the file block headers. The key is the old memory address.
*/ */
@ -233,6 +238,7 @@ public class BlenderContext {
* the block header to store * the block header to store
*/ */
public void addFileBlockHeader(Long oldMemoryAddress, FileBlockHeader fileBlockHeader) { public void addFileBlockHeader(Long oldMemoryAddress, FileBlockHeader fileBlockHeader) {
blocks.add(fileBlockHeader);
fileBlockHeadersByOma.put(oldMemoryAddress, fileBlockHeader); fileBlockHeadersByOma.put(oldMemoryAddress, fileBlockHeader);
List<FileBlockHeader> headers = fileBlockHeadersByCode.get(fileBlockHeader.getCode()); List<FileBlockHeader> headers = fileBlockHeadersByCode.get(fileBlockHeader.getCode());
if (headers == null) { if (headers == null) {
@ -242,6 +248,13 @@ public class BlenderContext {
headers.add(fileBlockHeader); headers.add(fileBlockHeader);
} }
/**
* @return the block headers
*/
public List<FileBlockHeader> getBlocks() {
return blocks;
}
/** /**
* This method returns the block header of a given memory address. If the * This method returns the block header of a given memory address. If the
* header is not present then null is returned. * header is not present then null is returned.
@ -332,22 +345,14 @@ public class BlenderContext {
* The method adds linked content to the blender context. * The method adds linked content to the blender context.
* @param blenderFilePath * @param blenderFilePath
* the path of linked blender file * the path of linked blender file
* @param featureName * @param featureGroup
* the linked feature name * the linked feature group (ie. scenes, materials, meshes, etc.)
* @param feature * @param feature
* the linked feature * the linked feature
*/ */
public void addLinkedFeature(String blenderFilePath, String featureName, Object feature) { @Deprecated
if (feature != null) { public void addLinkedFeature(String blenderFilePath, String featureGroup, Object feature) {
Map<String, Object> linkedFeatures = this.linkedFeatures.get(blenderFilePath); // the method is deprecated and empty at the moment
if (linkedFeatures == null) {
linkedFeatures = new HashMap<String, Object>();
this.linkedFeatures.put(blenderFilePath, linkedFeatures);
}
if (!linkedFeatures.containsKey(featureName)) {
linkedFeatures.put(featureName, feature);
}
}
} }
/** /**
@ -358,9 +363,106 @@ public class BlenderContext {
* the feature name we want to get * the feature name we want to get
* @return linked feature or null if none was found * @return linked feature or null if none was found
*/ */
@SuppressWarnings("unchecked")
public Object getLinkedFeature(String blenderFilePath, String featureName) { public Object getLinkedFeature(String blenderFilePath, String featureName) {
Map<String, Object> linkedFeatures = this.linkedFeatures.get(blenderFilePath); Map<String, Object> linkedFeatures = this.linkedFeatures.get(blenderFilePath);
return linkedFeatures != null ? linkedFeatures.get(featureName) : null; if(linkedFeatures != null) {
String namePrefix = (featureName.charAt(0) + "" + featureName.charAt(1)).toUpperCase();
featureName = featureName.substring(2);
if("SC".equals(namePrefix)) {
List<Node> scenes = (List<Node>) linkedFeatures.get("scenes");
if(scenes != null) {
for(Node scene : scenes) {
if(featureName.equals(scene.getName())) {
return scene;
}
}
}
} else if("OB".equals(namePrefix)) {
List<Node> features = (List<Node>) linkedFeatures.get("objects");
if(features != null) {
for(Node feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
} else if("ME".equals(namePrefix)) {
List<Node> features = (List<Node>) linkedFeatures.get("meshes");
if(features != null) {
for(Node feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
} else if("MA".equals(namePrefix)) {
List<MaterialContext> features = (List<MaterialContext>) linkedFeatures.get("materials");
if(features != null) {
for(MaterialContext feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
} else if("TX".equals(namePrefix)) {
List<Texture> features = (List<Texture>) linkedFeatures.get("textures");
if(features != null) {
for(Texture feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
} else if("IM".equals(namePrefix)) {
List<Texture> features = (List<Texture>) linkedFeatures.get("images");
if(features != null) {
for(Texture feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
} else if("AC".equals(namePrefix)) {
List<Animation> features = (List<Animation>) linkedFeatures.get("animations");
if(features != null) {
for(Animation feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
} else if("CA".equals(namePrefix)) {
List<Camera> features = (List<Camera>) linkedFeatures.get("cameras");
if(features != null) {
for(Camera feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
} else if("LA".equals(namePrefix)) {
List<Light> features = (List<Light>) linkedFeatures.get("lights");
if(features != null) {
for(Light feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
} else if("FI".equals(featureName)) {
List<Filter> features = (List<Filter>) linkedFeatures.get("lights");
if(features != null) {
for(Filter feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
}
}
return null;
} }
/** /**
@ -659,4 +761,9 @@ public class BlenderContext {
public static enum LoadedDataType { public static enum LoadedDataType {
STRUCTURE, FEATURE, TEMPORAL_MESH; STRUCTURE, FEATURE, TEMPORAL_MESH;
} }
@Override
public String toString() {
return blenderKey == null ? "BlenderContext [key = null]" : "BlenderContext [ key = " + blenderKey.toString() + " ]";
}
} }

@ -31,6 +31,9 @@
*/ */
package com.jme3.scene.plugins.blender; package com.jme3.scene.plugins.blender;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@ -41,9 +44,13 @@ import java.util.logging.Logger;
import com.jme3.animation.Animation; import com.jme3.animation.Animation;
import com.jme3.asset.AssetInfo; import com.jme3.asset.AssetInfo;
import com.jme3.asset.AssetKey;
import com.jme3.asset.AssetLoader; import com.jme3.asset.AssetLoader;
import com.jme3.asset.AssetLocator;
import com.jme3.asset.AssetManager;
import com.jme3.asset.BlenderKey; import com.jme3.asset.BlenderKey;
import com.jme3.asset.ModelKey; import com.jme3.asset.ModelKey;
import com.jme3.asset.StreamAssetInfo;
import com.jme3.light.Light; import com.jme3.light.Light;
import com.jme3.math.ColorRGBA; import com.jme3.math.ColorRGBA;
import com.jme3.post.Filter; import com.jme3.post.Filter;
@ -81,22 +88,17 @@ import com.jme3.texture.Texture;
public class BlenderLoader implements AssetLoader { public class BlenderLoader implements AssetLoader {
private static final Logger LOGGER = Logger.getLogger(BlenderLoader.class.getName()); private static final Logger LOGGER = Logger.getLogger(BlenderLoader.class.getName());
/** The blocks read from the file. */
protected List<FileBlockHeader> blocks;
/** The blender context. */
protected BlenderContext blenderContext;
@Override @Override
public Spatial load(AssetInfo assetInfo) throws IOException { public Spatial load(AssetInfo assetInfo) throws IOException {
try { try {
this.setup(assetInfo); BlenderContext blenderContext = this.setup(assetInfo);
AnimationHelper animationHelper = blenderContext.getHelper(AnimationHelper.class); AnimationHelper animationHelper = blenderContext.getHelper(AnimationHelper.class);
animationHelper.loadAnimations(); animationHelper.loadAnimations();
BlenderKey blenderKey = blenderContext.getBlenderKey(); BlenderKey blenderKey = blenderContext.getBlenderKey();
LoadedFeatures loadedFeatures = new LoadedFeatures(); LoadedFeatures loadedFeatures = new LoadedFeatures();
for (FileBlockHeader block : blocks) { for (FileBlockHeader block : blenderContext.getBlocks()) {
switch (block.getCode()) { switch (block.getCode()) {
case BLOCK_OB00: case BLOCK_OB00:
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class); ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
@ -181,7 +183,7 @@ public class BlenderLoader implements AssetLoader {
LOGGER.fine("Loading scenes and attaching them to the root object."); LOGGER.fine("Loading scenes and attaching them to the root object.");
for (FileBlockHeader sceneBlock : loadedFeatures.sceneBlocks) { for (FileBlockHeader sceneBlock : loadedFeatures.sceneBlocks) {
loadedFeatures.scenes.add(this.toScene(sceneBlock.getStructure(blenderContext))); loadedFeatures.scenes.add(this.toScene(sceneBlock.getStructure(blenderContext), blenderContext));
} }
LOGGER.fine("Creating the root node of the model and applying loaded nodes of the scene and loaded features to it."); LOGGER.fine("Creating the root node of the model and applying loaded nodes of the scene and loaded features to it.");
@ -220,7 +222,7 @@ public class BlenderLoader implements AssetLoader {
} catch (Exception e) { } catch (Exception e) {
throw new IOException("Unexpected importer exception occured: " + e.getLocalizedMessage(), e); throw new IOException("Unexpected importer exception occured: " + e.getLocalizedMessage(), e);
} finally { } finally {
this.clear(); this.clear(assetInfo);
} }
} }
@ -228,11 +230,12 @@ public class BlenderLoader implements AssetLoader {
* This method converts the given structure to a scene node. * This method converts the given structure to a scene node.
* @param structure * @param structure
* structure of a scene * structure of a scene
* @param blenderContext the blender context
* @return scene's node * @return scene's node
* @throws BlenderFileException * @throws BlenderFileException
* an exception throw when problems with blender file occur * an exception throw when problems with blender file occur
*/ */
private Node toScene(Structure structure) throws BlenderFileException { private Node toScene(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class); ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
Node result = new Node(structure.getName()); Node result = new Node(structure.getName());
List<Structure> base = ((Structure) structure.getFieldValue("base")).evaluateListBase(); List<Structure> base = ((Structure) structure.getFieldValue("base")).evaluateListBase();
@ -265,7 +268,7 @@ public class BlenderLoader implements AssetLoader {
* @throws BlenderFileException * @throws BlenderFileException
* an exception is throw when something wrong happens with blender file * an exception is throw when something wrong happens with blender file
*/ */
protected void setup(AssetInfo assetInfo) throws BlenderFileException { protected BlenderContext setup(AssetInfo assetInfo) throws BlenderFileException {
// registering loaders // registering loaders
ModelKey modelKey = (ModelKey) assetInfo.getKey(); ModelKey modelKey = (ModelKey) assetInfo.getKey();
BlenderKey blenderKey; BlenderKey blenderKey;
@ -273,16 +276,15 @@ public class BlenderLoader implements AssetLoader {
blenderKey = (BlenderKey) modelKey; blenderKey = (BlenderKey) modelKey;
} else { } else {
blenderKey = new BlenderKey(modelKey.getName()); blenderKey = new BlenderKey(modelKey.getName());
blenderKey.setAssetRootPath(modelKey.getFolder());
} }
// opening stream // opening stream
BlenderInputStream inputStream = new BlenderInputStream(assetInfo.openStream()); BlenderInputStream inputStream = new BlenderInputStream(assetInfo.openStream());
// reading blocks // reading blocks
blocks = new ArrayList<FileBlockHeader>(); List<FileBlockHeader> blocks = new ArrayList<FileBlockHeader>();
FileBlockHeader fileBlock; FileBlockHeader fileBlock;
blenderContext = new BlenderContext(); BlenderContext blenderContext = new BlenderContext();
blenderContext.setBlenderVersion(inputStream.getVersionNumber()); blenderContext.setBlenderVersion(inputStream.getVersionNumber());
blenderContext.setAssetManager(assetInfo.getManager()); blenderContext.setAssetManager(assetInfo.getManager());
blenderContext.setInputStream(inputStream); blenderContext.setInputStream(inputStream);
@ -317,15 +319,19 @@ public class BlenderLoader implements AssetLoader {
if (sceneFileBlock != null) { if (sceneFileBlock != null) {
blenderContext.setSceneStructure(sceneFileBlock.getStructure(blenderContext)); blenderContext.setSceneStructure(sceneFileBlock.getStructure(blenderContext));
} }
// adding locator for linked content
assetInfo.getManager().registerLocator(assetInfo.getKey().getName(), LinkedContentLocator.class);
return blenderContext;
} }
/** /**
* The internal data is only needed during loading so make it unreachable so that the GC can release * The internal data is only needed during loading so make it unreachable so that the GC can release
* that memory (which can be quite large amount). * that memory (which can be quite large amount).
*/ */
protected void clear() { protected void clear(AssetInfo assetInfo) {
blenderContext = null; assetInfo.getManager().unregisterLocator(assetInfo.getKey().getName(), LinkedContentLocator.class);
blocks = null;
} }
/** /**
@ -362,4 +368,50 @@ public class BlenderLoader implements AssetLoader {
*/ */
private ColorRGBA backgroundColor = ColorRGBA.Gray; private ColorRGBA backgroundColor = ColorRGBA.Gray;
} }
public static class LinkedContentLocator implements AssetLocator {
private File rootFolder;
@Override
public void setRootPath(String rootPath) {
rootFolder = new File(rootPath);
if(rootFolder.isFile()) {
rootFolder = rootFolder.getParentFile();
}
}
@SuppressWarnings("rawtypes")
@Override
public AssetInfo locate(AssetManager manager, AssetKey key) {
if(key instanceof BlenderKey) {
File linkedAbsoluteFile = new File(key.getName());
if(linkedAbsoluteFile.exists() && linkedAbsoluteFile.isFile()) {
try {
return new StreamAssetInfo(manager, key, new FileInputStream(linkedAbsoluteFile));
} catch (FileNotFoundException e) {
return null;
}
}
File linkedFileInCurrentAssetFolder = new File(rootFolder, linkedAbsoluteFile.getName());
if(linkedFileInCurrentAssetFolder.exists() && linkedFileInCurrentAssetFolder.isFile()) {
try {
return new StreamAssetInfo(manager, key, new FileInputStream(linkedFileInCurrentAssetFolder));
} catch (FileNotFoundException e) {
return null;
}
}
File linkedFileInCurrentFolder = new File(".", linkedAbsoluteFile.getName());
if(linkedFileInCurrentFolder.exists() && linkedFileInCurrentFolder.isFile()) {
try {
return new StreamAssetInfo(manager, key, new FileInputStream(linkedFileInCurrentFolder));
} catch (FileNotFoundException e) {
return null;
}
}
}
return null;
}
}
} }

@ -145,7 +145,7 @@ public class Ipo {
float[] times = new float[framesAmount + 1]; float[] times = new float[framesAmount + 1];
Vector3f[] translations = new Vector3f[framesAmount + 1]; Vector3f[] translations = new Vector3f[framesAmount + 1];
float[] translation = new float[] { localTranslation.x, localTranslation.y, localTranslation.z }; float[] translation = new float[3];
Quaternion[] rotations = new Quaternion[framesAmount + 1]; Quaternion[] rotations = new Quaternion[framesAmount + 1];
float[] quaternionRotation = new float[] { localRotation.getX(), localRotation.getY(), localRotation.getZ(), localRotation.getW(), }; float[] quaternionRotation = new float[] { localRotation.getX(), localRotation.getY(), localRotation.getZ(), localRotation.getW(), };
float[] eulerRotation = localRotation.toAngles(null); float[] eulerRotation = localRotation.toAngles(null);
@ -165,6 +165,8 @@ public class Ipo {
// calculating track data // calculating track data
for (int frame = startFrame; frame <= stopFrame; ++frame) { for (int frame = startFrame; frame <= stopFrame; ++frame) {
boolean translationSet = false;
translation[0] = translation[1] = translation[2] = 0;
int index = frame - startFrame; int index = frame - startFrame;
times[index] = index * timeBetweenFrames;// start + (frame - 1) * timeBetweenFrames; times[index] = index * timeBetweenFrames;// start + (frame - 1) * timeBetweenFrames;
for (int j = 0; j < bezierCurves.length; ++j) { for (int j = 0; j < bezierCurves.length; ++j) {
@ -173,15 +175,18 @@ public class Ipo {
// LOCATION // LOCATION
case AC_LOC_X: case AC_LOC_X:
translation[0] = (float) value; translation[0] = (float) value;
translationSet = true;
break; break;
case AC_LOC_Y: case AC_LOC_Y:
if (swapAxes && value != 0) { if (swapAxes && value != 0) {
value = -value; value = -value;
} }
translation[yIndex] = (float) value; translation[yIndex] = (float) value;
translationSet = true;
break; break;
case AC_LOC_Z: case AC_LOC_Z:
translation[zIndex] = (float) value; translation[zIndex] = (float) value;
translationSet = true;
break; break;
// EULER ROTATION // EULER ROTATION
@ -235,7 +240,11 @@ public class Ipo {
LOGGER.log(Level.WARNING, "Unknown ipo curve type: {0}.", bezierCurves[j].getType()); LOGGER.log(Level.WARNING, "Unknown ipo curve type: {0}.", bezierCurves[j].getType());
} }
} }
if(translationSet) {
translations[index] = localRotation.multLocal(new Vector3f(translation[0], translation[1], translation[2])); translations[index] = localRotation.multLocal(new Vector3f(translation[0], translation[1], translation[2]));
} else {
translations[index] = new Vector3f();
}
if(boneContext != null) { if(boneContext != null) {
if(boneContext.getBone().getParent() == null && boneContext.is(BoneContext.NO_LOCAL_LOCATION)) { if(boneContext.getBone().getParent() == null && boneContext.is(BoneContext.NO_LOCAL_LOCATION)) {

@ -174,7 +174,7 @@ public class ConstraintDefinitionIK extends ConstraintDefinition {
private static class BonesChain extends ArrayList<BoneContext> { private static class BonesChain extends ArrayList<BoneContext> {
private static final long serialVersionUID = -1850524345643600718L; private static final long serialVersionUID = -1850524345643600718L;
private List<Matrix> bonesMatrices = new ArrayList<Matrix>(); private List<Matrix> localBonesMatrices = new ArrayList<Matrix>();
public BonesChain(Bone bone, boolean useTail, int bonesAffected, Collection<Long> alteredOmas, BlenderContext blenderContext) { public BonesChain(Bone bone, boolean useTail, int bonesAffected, Collection<Long> alteredOmas, BlenderContext blenderContext) {
if (bone != null) { if (bone != null) {
@ -187,12 +187,21 @@ public class ConstraintDefinitionIK extends ConstraintDefinition {
this.add(boneContext); this.add(boneContext);
alteredOmas.add(boneContext.getBoneOma()); alteredOmas.add(boneContext.getBoneOma());
Space space = this.size() < bonesAffected ? Space.CONSTRAINT_SPACE_LOCAL : Space.CONSTRAINT_SPACE_WORLD; Transform transform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD);
Transform transform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), space); localBonesMatrices.add(new DTransform(transform).toMatrix());
bonesMatrices.add(new DTransform(transform).toMatrix());
bone = bone.getParent(); bone = bone.getParent();
} }
if(localBonesMatrices.size() > 0) {
// making the matrices describe the local transformation
Matrix parentWorldMatrix = localBonesMatrices.get(localBonesMatrices.size() - 1);
for(int i=localBonesMatrices.size() - 2;i>=0;--i) {
SimpleMatrix m = parentWorldMatrix.invert().mult(localBonesMatrices.get(i));
parentWorldMatrix = localBonesMatrices.get(i);
localBonesMatrices.set(i, new Matrix(m));
}
}
} }
} }
@ -211,16 +220,16 @@ public class ConstraintDefinitionIK extends ConstraintDefinition {
SimpleMatrix m = parentWorldMatrix.invert().mult(boneMatrix); SimpleMatrix m = parentWorldMatrix.invert().mult(boneMatrix);
boneMatrix = new Matrix(m); boneMatrix = new Matrix(m);
} }
bonesMatrices.set(index, boneMatrix); localBonesMatrices.set(index, boneMatrix);
} }
public Matrix getWorldMatrix(int index) { public Matrix getWorldMatrix(int index) {
if (index == this.size() - 1) { if (index == this.size() - 1) {
return new Matrix(bonesMatrices.get(this.size() - 1)); return new Matrix(localBonesMatrices.get(this.size() - 1));
} }
SimpleMatrix result = this.getWorldMatrix(index + 1); SimpleMatrix result = this.getWorldMatrix(index + 1);
result = result.mult(bonesMatrices.get(index)); result = result.mult(localBonesMatrices.get(index));
return new Matrix(result); return new Matrix(result);
} }
} }

@ -180,8 +180,8 @@ public class CurvesTemporalMesh extends TemporalMesh {
if (bevelObject != null && beziers.size() > 0) { if (bevelObject != null && beziers.size() > 0) {
this.append(this.applyBevelAndTaper(this, bevelObject, taperObject, blenderContext)); this.append(this.applyBevelAndTaper(this, bevelObject, taperObject, blenderContext));
} else { } else {
int originalVerticesAmount = vertices.size();
for (BezierLine bezierLine : beziers) { for (BezierLine bezierLine : beziers) {
int originalVerticesAmount = vertices.size();
vertices.add(bezierLine.vertices[0]); vertices.add(bezierLine.vertices[0]);
Vector3f v = bezierLine.vertices[1].subtract(bezierLine.vertices[0]).normalizeLocal(); Vector3f v = bezierLine.vertices[1].subtract(bezierLine.vertices[0]).normalizeLocal();
float temp = v.x; float temp = v.x;

@ -81,6 +81,7 @@ public class LandscapeHelper extends AbstractBlenderHelper {
if ((mode & MODE_MIST) != 0) { if ((mode & MODE_MIST) != 0) {
LOGGER.fine("Loading fog."); LOGGER.fine("Loading fog.");
result = new FogFilter(); result = new FogFilter();
result.setName("FIfog");
result.setFogColor(this.toBackgroundColor(worldStructure)); result.setFogColor(this.toBackgroundColor(worldStructure));
} }
return result; return result;

@ -168,9 +168,11 @@ public final class MaterialContext implements Savable {
this.setTexture(material, combinedTexture.getMappingType(), combinedTexture.getResultTexture()); this.setTexture(material, combinedTexture.getMappingType(), combinedTexture.getResultTexture());
List<Vector2f> uvs = combinedTexture.getResultUVS(); List<Vector2f> uvs = combinedTexture.getResultUVS();
if(uvs != null && uvs.size() > 0) {
VertexBuffer uvCoordsBuffer = new VertexBuffer(TextureHelper.TEXCOORD_TYPES[textureIndex++]); VertexBuffer uvCoordsBuffer = new VertexBuffer(TextureHelper.TEXCOORD_TYPES[textureIndex++]);
uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, BufferUtils.createFloatBuffer(uvs.toArray(new Vector2f[uvs.size()]))); uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, BufferUtils.createFloatBuffer(uvs.toArray(new Vector2f[uvs.size()])));
geometry.getMesh().setBuffer(uvCoordsBuffer); geometry.getMesh().setBuffer(uvCoordsBuffer);
}//uvs might be null if the user assigned non existing UV coordinates group name to the mesh (this should be fixed in blender file)
} else { } else {
LOGGER.log(Level.WARNING, "The texture could not be applied because JME only supports up to {0} different UV's.", TextureHelper.TEXCOORD_TYPES.length); LOGGER.log(Level.WARNING, "The texture could not be applied because JME only supports up to {0} different UV's.", TextureHelper.TEXCOORD_TYPES.length);
} }

@ -46,6 +46,8 @@ import com.jme3.scene.plugins.blender.objects.Properties;
*/ */
public class TemporalMesh extends Geometry { public class TemporalMesh extends Geometry {
private static final Logger LOGGER = Logger.getLogger(TemporalMesh.class.getName()); private static final Logger LOGGER = Logger.getLogger(TemporalMesh.class.getName());
/** A minimum weight value. */
private static final double MINIMUM_BONE_WEIGHT = FastMath.DBL_EPSILON;
/** The blender context. */ /** The blender context. */
protected final BlenderContext blenderContext; protected final BlenderContext blenderContext;
@ -530,7 +532,11 @@ public class TemporalMesh extends Geometry {
for (Entry<String, Integer> entry : boneIndexes.entrySet()) { for (Entry<String, Integer> entry : boneIndexes.entrySet()) {
if (vertexGroupsForVertex.containsKey(entry.getKey())) { if (vertexGroupsForVertex.containsKey(entry.getKey())) {
float weight = vertexGroupsForVertex.get(entry.getKey()); float weight = vertexGroupsForVertex.get(entry.getKey());
if (weight > 0) {// no need to use such weights if (weight > MINIMUM_BONE_WEIGHT) {
// only values of weight greater than MINIMUM_BONE_WEIGHT are used
// if all non zero weights were used, and they were samm enough, problems with normalisation would occur
// because adding a very small value to 1.0 will give 1.0
// so in order to avoid such errors, which can cause severe animation artifacts we need to use some minimum weight value
boneBuffersForVertex.put(weight, entry.getValue()); boneBuffersForVertex.put(weight, entry.getValue());
} }
} }

@ -2,6 +2,7 @@ package com.jme3.scene.plugins.blender.modifiers;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
@ -129,6 +130,8 @@ public class SubdivisionSurfaceModifier extends Modifier {
for (int i = 0; i < temporalMesh.getVertexCount(); ++i) { for (int i = 0; i < temporalMesh.getVertexCount(); ++i) {
// finding adjacent edges that were created by dividing original edges // finding adjacent edges that were created by dividing original edges
List<Edge> adjacentOriginalEdges = new ArrayList<Edge>(); List<Edge> adjacentOriginalEdges = new ArrayList<Edge>();
Collection<Edge> adjacentEdges = temporalMesh.getAdjacentEdges(i);
if(adjacentEdges != null) {// this can be null if a vertex with index 'i' is not connected to any face nor edge
for (Edge edge : temporalMesh.getAdjacentEdges(i)) { for (Edge edge : temporalMesh.getAdjacentEdges(i)) {
if (verticesOnOriginalEdges.contains(edge.getFirstIndex()) || verticesOnOriginalEdges.contains(edge.getSecondIndex())) { if (verticesOnOriginalEdges.contains(edge.getFirstIndex()) || verticesOnOriginalEdges.contains(edge.getSecondIndex())) {
adjacentOriginalEdges.add(edge); adjacentOriginalEdges.add(edge);
@ -136,6 +139,9 @@ public class SubdivisionSurfaceModifier extends Modifier {
} }
creasePoints.add(new CreasePoint(i, boundaryVertices.contains(i), adjacentOriginalEdges, temporalMesh)); creasePoints.add(new CreasePoint(i, boundaryVertices.contains(i), adjacentOriginalEdges, temporalMesh));
} else {
creasePoints.add(null);//the count of crease points must be equal to vertex count; otherwise we'll get IndexOutofBoundsException later
}
} }
Vector3f[] averageVert = new Vector3f[temporalMesh.getVertexCount()]; Vector3f[] averageVert = new Vector3f[temporalMesh.getVertexCount()];
@ -174,6 +180,7 @@ public class SubdivisionSurfaceModifier extends Modifier {
} }
for (int i = 0; i < averageVert.length; ++i) { for (int i = 0; i < averageVert.length; ++i) {
if(averageVert[i] != null && averageCount[i] > 0) {
Vector3f v = temporalMesh.getVertices().get(i); Vector3f v = temporalMesh.getVertices().get(i);
averageVert[i].divideLocal(averageCount[i]); averageVert[i].divideLocal(averageCount[i]);
@ -194,6 +201,7 @@ public class SubdivisionSurfaceModifier extends Modifier {
} }
} }
} }
}
/** /**
* The method performs a simple subdivision of the mesh. * The method performs a simple subdivision of the mesh.

@ -158,6 +158,9 @@ public class CombinedTexture {
} else { } else {
resultUVS = userDefinedUVCoordinates.get(textureData.uvCoordinatesName); resultUVS = userDefinedUVCoordinates.get(textureData.uvCoordinatesName);
} }
if(resultUVS == null && LOGGER.isLoggable(Level.WARNING)) {
LOGGER.warning("The texture " + textureData.texture.getName() + " has assigned non existing UV coordinates group: " + textureData.uvCoordinatesName + ".");
}
masterUserUVSetName = textureData.uvCoordinatesName; masterUserUVSetName = textureData.uvCoordinatesName;
} else { } else {
TemporalMesh temporalMesh = (TemporalMesh) blenderContext.getLoadedFeature(geometriesOMA, LoadedDataType.TEMPORAL_MESH); TemporalMesh temporalMesh = (TemporalMesh) blenderContext.getLoadedFeature(geometriesOMA, LoadedDataType.TEMPORAL_MESH);

@ -51,6 +51,7 @@ extern "C" {
jmeClasses::initJavaClasses(env); jmeClasses::initJavaClasses(env);
btTriangleIndexVertexArray* array = reinterpret_cast<btTriangleIndexVertexArray*>(meshId); btTriangleIndexVertexArray* array = reinterpret_cast<btTriangleIndexVertexArray*>(meshId);
btGImpactMeshShape* shape = new btGImpactMeshShape(array); btGImpactMeshShape* shape = new btGImpactMeshShape(array);
shape->updateBound();
return reinterpret_cast<jlong>(shape); return reinterpret_cast<jlong>(shape);
} }

@ -811,37 +811,70 @@ extern "C" {
/* /*
* Class: com_jme3_bullet_objects_PhysicsRigidBody * Class: com_jme3_bullet_objects_PhysicsRigidBody
* Method: getAngularFactor * Method: getAngularFactor
* Signature: (J)F * Signature: (JLcom/jme3/math/Vector3f;)V
*/ */
JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getAngularFactor JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getAngularFactor
(JNIEnv *env, jobject object, jlong bodyId) { (JNIEnv *env, jobject object, jlong bodyId, jobject factor) {
btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId); btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
if (body == NULL) { if (body == NULL) {
jclass newExc = env->FindClass("java/lang/NullPointerException"); jclass newExc = env->FindClass("java/lang/NullPointerException");
env->ThrowNew(newExc, "The native object does not exist."); env->ThrowNew(newExc, "The native object does not exist.");
return 0; return;
} }
return body->getAngularFactor().getX(); jmeBulletUtil::convert(env, &body->getAngularFactor(), factor);
} }
/* /*
* Class: com_jme3_bullet_objects_PhysicsRigidBody * Class: com_jme3_bullet_objects_PhysicsRigidBody
* Method: setAngularFactor * Method: setAngularFactor
* Signature: (JF)V * Signature: (JLcom/jme3/math/Vector3f;)V
*/ */
JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setAngularFactor JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setAngularFactor
(JNIEnv *env, jobject object, jlong bodyId, jfloat value) { (JNIEnv *env, jobject object, jlong bodyId, jobject factor) {
btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId); btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
if (body == NULL) { if (body == NULL) {
jclass newExc = env->FindClass("java/lang/NullPointerException"); jclass newExc = env->FindClass("java/lang/NullPointerException");
env->ThrowNew(newExc, "The native object does not exist."); env->ThrowNew(newExc, "The native object does not exist.");
return; return;
} }
btVector3 vec1 = btVector3(); btVector3 vec = btVector3();
vec1.setX(value); jmeBulletUtil::convert(env, factor, &vec);
vec1.setY(value); body->setAngularFactor(vec);
vec1.setZ(value); }
body->setAngularFactor(vec1);
/*
* Class: com_jme3_bullet_objects_PhysicsRigidBody
* Method: getLinearFactor
* Signature: (JLcom/jme3/math/Vector3f;)V
*/
JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getLinearFactor
(JNIEnv *env, jobject object, jlong bodyId, jobject factor) {
btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
if (body == NULL) {
jclass newExc = env->FindClass("java/lang/NullPointerException");
env->ThrowNew(newExc, "The native object does not exist.");
return;
}
jmeBulletUtil::convert(env, &body->getLinearFactor(), factor);
}
/*
* Class: com_jme3_bullet_objects_PhysicsRigidBody
* Method: setLinearFactor
* Signature: (JLcom/jme3/math/Vector3f;)V
*/
JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setLinearFactor
(JNIEnv *env, jobject object, jlong bodyId, jobject factor) {
btRigidBody* body = reinterpret_cast<btRigidBody*>(bodyId);
if (body == NULL) {
jclass newExc = env->FindClass("java/lang/NullPointerException");
env->ThrowNew(newExc, "The native object does not exist.");
return;
}
btVector3 vec = btVector3();
jmeBulletUtil::convert(env, factor, &vec);
body->setLinearFactor(vec);
} }
#ifdef __cplusplus #ifdef __cplusplus

@ -396,18 +396,35 @@ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getAngula
/* /*
* Class: com_jme3_bullet_objects_PhysicsRigidBody * Class: com_jme3_bullet_objects_PhysicsRigidBody
* Method: getAngularFactor * Method: getAngularFactor
* Signature: (J)F * Signature: (JLcom/jme3/math/Vector3f;)V
*/ */
JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getAngularFactor JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getAngularFactor
(JNIEnv *, jobject, jlong); (JNIEnv *, jobject, jlong, jobject);
/* /*
* Class: com_jme3_bullet_objects_PhysicsRigidBody * Class: com_jme3_bullet_objects_PhysicsRigidBody
* Method: setAngularFactor * Method: setAngularFactor
* Signature: (JF)V * Signature: (JLcom/jme3/math/Vector3f;)V
*/ */
JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setAngularFactor JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setAngularFactor
(JNIEnv *, jobject, jlong, jfloat); (JNIEnv *, jobject, jlong, jobject);
/*
* Class: com_jme3_bullet_objects_PhysicsRigidBody
* Method: getLinearFactor
* Signature: (JLcom/jme3/math/Vector3f;)V
*/
JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getLinearFactor
(JNIEnv *, jobject, jlong, jobject);
/*
* Class: com_jme3_bullet_objects_PhysicsRigidBody
* Method: setLinearFactor
* Signature: (JLcom/jme3/math/Vector3f;)V
*/
JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_setLinearFactor
(JNIEnv *, jobject, jlong, jobject);
#ifdef __cplusplus #ifdef __cplusplus
} }

@ -351,7 +351,7 @@ void jmeBulletUtil::addResult(JNIEnv* env, jobject resultlist, btVector3* hitnor
env->SetFloatField(singleresult, jmeClasses::PhysicsRay_hitfraction, m_hitFraction); env->SetFloatField(singleresult, jmeClasses::PhysicsRay_hitfraction, m_hitFraction);
env->SetObjectField(singleresult, jmeClasses::PhysicsRay_collisionObject, up1->javaCollisionObject); env->SetObjectField(singleresult, jmeClasses::PhysicsRay_collisionObject, up1->javaCollisionObject);
env->CallVoidMethod(resultlist, jmeClasses::PhysicsRay_addmethod, singleresult); env->CallBooleanMethod(resultlist, jmeClasses::PhysicsRay_addmethod, singleresult);
if (env->ExceptionCheck()) { if (env->ExceptionCheck()) {
env->Throw(env->ExceptionOccurred()); env->Throw(env->ExceptionOccurred());
return; return;
@ -371,7 +371,7 @@ void jmeBulletUtil::addSweepResult(JNIEnv* env, jobject resultlist, btVector3* h
env->SetFloatField(singleresult, jmeClasses::PhysicsSweep_hitfraction, m_hitFraction); env->SetFloatField(singleresult, jmeClasses::PhysicsSweep_hitfraction, m_hitFraction);
env->SetObjectField(singleresult, jmeClasses::PhysicsSweep_collisionObject, up1->javaCollisionObject); env->SetObjectField(singleresult, jmeClasses::PhysicsSweep_collisionObject, up1->javaCollisionObject);
env->CallVoidMethod(resultlist, jmeClasses::PhysicsSweep_addmethod, singleresult); env->CallBooleanMethod(resultlist, jmeClasses::PhysicsSweep_addmethod, singleresult);
if (env->ExceptionCheck()) { if (env->ExceptionCheck()) {
env->Throw(env->ExceptionOccurred()); env->Throw(env->ExceptionOccurred());
return; return;

@ -93,8 +93,10 @@ public class CapsuleCollisionShape extends CollisionShape{
*/ */
@Override @Override
public void setScale(Vector3f scale) { public void setScale(Vector3f scale) {
if (!scale.equals(Vector3f.UNIT_XYZ)) {
Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "CapsuleCollisionShape cannot be scaled"); Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "CapsuleCollisionShape cannot be scaled");
} }
}
public void write(JmeExporter ex) throws IOException { public void write(JmeExporter ex) throws IOException {
super.write(ex); super.write(ex);

@ -627,16 +627,44 @@ public class PhysicsRigidBody extends PhysicsCollisionObject {
private native float getAngularSleepingThreshold(long objectId); private native float getAngularSleepingThreshold(long objectId);
public float getAngularFactor() { public float getAngularFactor() {
return getAngularFactor(objectId); return getAngularFactor(null).getX();
} }
private native float getAngularFactor(long objectId); public Vector3f getAngularFactor(Vector3f store) {
// doing like this prevent from breaking the API
if (store == null) {
store = new Vector3f();
}
getAngularFactor(objectId, store);
return store;
}
private native void getAngularFactor(long objectId, Vector3f vec);
public void setAngularFactor(float factor) { public void setAngularFactor(float factor) {
setAngularFactor(objectId, new Vector3f(factor, factor, factor));
}
public void setAngularFactor(Vector3f factor) {
setAngularFactor(objectId, factor); setAngularFactor(objectId, factor);
} }
private native void setAngularFactor(long objectId, float factor); private native void setAngularFactor(long objectId, Vector3f factor);
public Vector3f getLinearFactor() {
Vector3f vec = new Vector3f();
getLinearFactor(objectId, vec);
return vec;
}
private native void getLinearFactor(long objectId, Vector3f vec);
public void setLinearFactor(Vector3f factor) {
setLinearFactor(objectId, factor);
}
private native void setLinearFactor(long objectId, Vector3f factor);
/** /**
* do not use manually, joints are added automatically * do not use manually, joints are added automatically
@ -673,7 +701,13 @@ public class PhysicsRigidBody extends PhysicsCollisionObject {
capsule.write(getGravity(), "gravity", Vector3f.ZERO); capsule.write(getGravity(), "gravity", Vector3f.ZERO);
capsule.write(getFriction(), "friction", 0.5f); capsule.write(getFriction(), "friction", 0.5f);
capsule.write(getRestitution(), "restitution", 0); capsule.write(getRestitution(), "restitution", 0);
Vector3f angularFactor = getAngularFactor(null);
if (angularFactor.x == angularFactor.y && angularFactor.y == angularFactor.z) {
capsule.write(getAngularFactor(), "angularFactor", 1); capsule.write(getAngularFactor(), "angularFactor", 1);
} else {
capsule.write(getAngularFactor(null), "angularFactor", Vector3f.UNIT_XYZ);
capsule.write(getLinearFactor(), "linearFactor", Vector3f.UNIT_XYZ);
}
capsule.write(kinematic, "kinematic", false); capsule.write(kinematic, "kinematic", false);
capsule.write(getLinearDamping(), "linearDamping", 0); capsule.write(getLinearDamping(), "linearDamping", 0);
@ -703,7 +737,13 @@ public class PhysicsRigidBody extends PhysicsCollisionObject {
setKinematic(capsule.readBoolean("kinematic", false)); setKinematic(capsule.readBoolean("kinematic", false));
setRestitution(capsule.readFloat("restitution", 0)); setRestitution(capsule.readFloat("restitution", 0));
Vector3f angularFactor = (Vector3f) capsule.readSavable("angularFactor", null);
if(angularFactor == null) {
setAngularFactor(capsule.readFloat("angularFactor", 1)); setAngularFactor(capsule.readFloat("angularFactor", 1));
} else {
setAngularFactor(angularFactor);
setLinearFactor((Vector3f) capsule.readSavable("linearFactor", Vector3f.UNIT_XYZ.clone()));
}
setDamping(capsule.readFloat("linearDamping", 0), capsule.readFloat("angularDamping", 0)); setDamping(capsule.readFloat("linearDamping", 0), capsule.readFloat("angularDamping", 0));
setSleepingThresholds(capsule.readFloat("linearSleepingThreshold", 0.8f), capsule.readFloat("angularSleepingThreshold", 1.0f)); setSleepingThresholds(capsule.readFloat("linearSleepingThreshold", 0.8f), capsule.readFloat("angularSleepingThreshold", 1.0f));
setCcdMotionThreshold(capsule.readFloat("ccdMotionThreshold", 0)); setCcdMotionThreshold(capsule.readFloat("ccdMotionThreshold", 0));

@ -10,57 +10,30 @@ sourceSets {
srcDir 'src/tools/java' srcDir 'src/tools/java'
} }
} }
} test {
java {
buildscript { srcDir 'src/test/java'
repositories {
mavenCentral()
} }
dependencies {
classpath 'org.ajoberstar:gradle-git:1.0.0-rc.1'
} }
} }
import java.text.SimpleDateFormat task updateVersionPropertiesFile << {
import org.ajoberstar.grgit.* def verfile = file('src/main/resources/com/jme3/system/version.properties')
verfile.text = "# THIS IS AN AUTO-GENERATED FILE..\n" +
task updateVersion << { "# DO NOT MODIFY!\n" +
"build.date=${jmeBuildDate}\n" +
def verfile = file('src/main/java/com/jme3/system/JmeVersion.java') "git.revision=${jmeRevision}\n" +
def jmeGitHash "git.branch=${jmeBranchName}\n" +
def jmeShortGitHash "git.hash=${jmeGitHash}\n" +
def jmeBuildDate "git.hash.short=${jmeShortGitHash}\n" +
def jmeBranchName "git.tag=${jmeGitTag}\n" +
"name.full=jMonkeyEngine ${jmeFullVersion}\n" +
try { "version.full=${jmeFullVersion}\n" +
def grgit = Grgit.open(project.file('.').parent) "version.number=${jmeVersion}\n" +
jmeGitHash = grgit.head().id "version.tag=${jmeVersionTag}"
jmeShortGitHash = grgit.head().abbreviatedId
jmeBuildDate = new SimpleDateFormat("yyyy-MM-dd").format(new Date())
jmeBranchName = grgit.branch.current.name
} catch (ex) {
// Failed to get repo info
logger.warn("Failed to get repository info: " + ex.message + ". " + \
"Only partial build info will be generated.")
jmeGitHash = ""
jmeShortGitHash = ""
jmeBuildDate = new SimpleDateFormat("yyyy-MM-dd").format(new Date())
jmeBranchName = "unknown"
}
verfile.text = "\npackage com.jme3.system;\n\n" +
"/**\n * THIS IS AN AUTO-GENERATED FILE..\n * DO NOT MODIFY!\n */\n" +
"public class JmeVersion {\n" +
" public static final String BUILD_DATE = \"${jmeBuildDate}\";\n" +
" public static final String BRANCH_NAME = \"${jmeBranchName}\";\n" +
" public static final String GIT_HASH = \"${jmeGitHash}\";\n" +
" public static final String GIT_SHORT_HASH = \"${jmeShortGitHash}\";\n" +
" public static final String FULL_NAME = \"jMonkeyEngine ${jmeVersion} (${jmeVersionTag})\";\n" +
"}\n"
} }
compileJava.dependsOn(updateVersion) compileJava.dependsOn(updateVersionPropertiesFile)
dependencies { dependencies {
} }

@ -553,7 +553,6 @@ public final class Bone implements Savable {
Vector3f translate = modelPos.add(rotate.mult(scale.mult(modelBindInversePos, tmp2), tmp2), tmp2); Vector3f translate = modelPos.add(rotate.mult(scale.mult(modelBindInversePos, tmp2), tmp2), tmp2);
// Populating the matrix // Populating the matrix
outTransform.loadIdentity();
outTransform.setTransform(translate, scale, rotate.toRotationMatrix(tmp4)); outTransform.setTransform(translate, scale, rotate.toRotationMatrix(tmp4));
} }

@ -75,7 +75,7 @@ final class ImplHandler {
this.assetManager = assetManager; this.assetManager = assetManager;
} }
protected static class ImplThreadLocal<T> extends ThreadLocal { protected static class ImplThreadLocal<T> extends ThreadLocal<T> {
private final Class<T> type; private final Class<T> type;
private final String path; private final String path;
@ -112,9 +112,13 @@ final class ImplHandler {
} }
@Override @Override
protected Object initialValue(){ protected T initialValue(){
try { try {
return type.newInstance(); T obj = type.newInstance();
if (path != null) {
((AssetLocator)obj).setRootPath(path);
}
return obj;
} catch (InstantiationException ex) { } catch (InstantiationException ex) {
logger.log(Level.SEVERE,"Cannot create locator of type {0}, does" logger.log(Level.SEVERE,"Cannot create locator of type {0}, does"
+ " the class have an empty and publically accessible"+ + " the class have an empty and publically accessible"+
@ -169,15 +173,12 @@ final class ImplHandler {
return null; return null;
} }
for (ImplThreadLocal local : locatorsList){ for (ImplThreadLocal<AssetLocator> local : locatorsList){
AssetLocator locator = (AssetLocator) local.get(); AssetInfo info = local.get().locate(assetManager, key);
if (local.getPath() != null){ if (info != null) {
locator.setRootPath((String) local.getPath());
}
AssetInfo info = locator.locate(assetManager, key);
if (info != null)
return info; return info;
} }
}
return null; return null;
} }

@ -31,22 +31,20 @@
*/ */
package com.jme3.audio.openal; package com.jme3.audio.openal;
import com.jme3.audio.AudioSource.Status;
import com.jme3.audio.*; import com.jme3.audio.*;
import com.jme3.audio.AudioSource.Status;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
import com.jme3.util.BufferUtils; import com.jme3.util.BufferUtils;
import com.jme3.util.NativeObjectManager; import com.jme3.util.NativeObjectManager;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.FloatBuffer; import java.nio.FloatBuffer;
import java.nio.IntBuffer; import java.nio.IntBuffer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import static com.jme3.audio.openal.AL.*; import static com.jme3.audio.openal.AL.*;
import static com.jme3.audio.openal.ALC.*;
import static com.jme3.audio.openal.EFX.*;
public class ALAudioRenderer implements AudioRenderer, Runnable { public class ALAudioRenderer implements AudioRenderer, Runnable {
@ -102,16 +100,6 @@ public class ALAudioRenderer implements AudioRenderer, Runnable {
return; return;
} }
String deviceName = alc.alcGetString(ALC.ALC_DEVICE_SPECIFIER);
logger.log(Level.INFO, "Audio Device: {0}", deviceName);
logger.log(Level.INFO, "Audio Vendor: {0}", al.alGetString(AL_VENDOR));
logger.log(Level.INFO, "Audio Renderer: {0}", al.alGetString(AL_RENDERER));
logger.log(Level.INFO, "Audio Version: {0}", al.alGetString(AL_VERSION));
logger.log(Level.INFO, "ALC extensions: {0}", alc.alcGetString(ALC.ALC_EXTENSIONS));
logger.log(Level.INFO, "AL extensions: {0}", al.alGetString(AL_EXTENSIONS));
// Find maximum # of sources supported by this implementation // Find maximum # of sources supported by this implementation
ArrayList<Integer> channelList = new ArrayList<Integer>(); ArrayList<Integer> channelList = new ArrayList<Integer>();
for (int i = 0; i < MAX_NUM_CHANNELS; i++) { for (int i = 0; i < MAX_NUM_CHANNELS; i++) {
@ -131,7 +119,25 @@ public class ALAudioRenderer implements AudioRenderer, Runnable {
ib = BufferUtils.createIntBuffer(channels.length); ib = BufferUtils.createIntBuffer(channels.length);
chanSrcs = new AudioSource[channels.length]; chanSrcs = new AudioSource[channels.length];
logger.log(Level.INFO, "AudioRenderer supports {0} channels", channels.length); final String deviceName = alc.alcGetString(ALC.ALC_DEVICE_SPECIFIER);
logger.log(Level.INFO, "Audio Renderer Information\n" +
" * Device: {0}\n" +
" * Vendor: {1}\n" +
" * Renderer: {2}\n" +
" * Version: {3}\n" +
" * Supported channels: {4}\n" +
" * ALC extensions: {5}\n" +
" * AL extensions: {6}",
new Object[]{
deviceName,
al.alGetString(AL_VENDOR),
al.alGetString(AL_RENDERER),
al.alGetString(AL_VERSION),
channels.length,
alc.alcGetString(ALC.ALC_EXTENSIONS),
al.alGetString(AL_EXTENSIONS)
});
// Pause device is a feature used specifically on Android // Pause device is a feature used specifically on Android
// where the application could be closed but still running, // where the application could be closed but still running,
@ -153,7 +159,7 @@ public class ALAudioRenderer implements AudioRenderer, Runnable {
alc.alcGetInteger(EFX.ALC_MAX_AUXILIARY_SENDS, ib, 1); alc.alcGetInteger(EFX.ALC_MAX_AUXILIARY_SENDS, ib, 1);
auxSends = ib.get(0); auxSends = ib.get(0);
logger.log(Level.INFO, "Audio max auxilary sends: {0}", auxSends); logger.log(Level.INFO, "Audio max auxiliary sends: {0}", auxSends);
// create slot // create slot
ib.position(0).limit(1); ib.position(0).limit(1);

@ -41,6 +41,7 @@ import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule; import com.jme3.export.OutputCapsule;
import com.jme3.math.*; import com.jme3.math.*;
import com.jme3.scene.Mesh; import com.jme3.scene.Mesh;
import com.jme3.scene.Spatial;
import com.jme3.util.TempVars; import com.jme3.util.TempVars;
import java.io.IOException; import java.io.IOException;
import java.nio.FloatBuffer; import java.nio.FloatBuffer;
@ -313,7 +314,7 @@ public class BoundingBox extends BoundingVolume {
transMatrix.absoluteLocal(); transMatrix.absoluteLocal();
Vector3f scale = trans.getScale(); Vector3f scale = trans.getScale();
vars.vect1.set(xExtent * scale.x, yExtent * scale.y, zExtent * scale.z); vars.vect1.set(xExtent * FastMath.abs(scale.x), yExtent * FastMath.abs(scale.y), zExtent * FastMath.abs(scale.z));
transMatrix.mult(vars.vect1, vars.vect2); transMatrix.mult(vars.vect1, vars.vect2);
// Assign the biggest rotations after scales. // Assign the biggest rotations after scales.
box.xExtent = FastMath.abs(vars.vect2.getX()); box.xExtent = FastMath.abs(vars.vect2.getX());
@ -593,18 +594,7 @@ public class BoundingBox extends BoundingVolume {
* @see BoundingVolume#intersectsSphere(com.jme3.bounding.BoundingSphere) * @see BoundingVolume#intersectsSphere(com.jme3.bounding.BoundingSphere)
*/ */
public boolean intersectsSphere(BoundingSphere bs) { public boolean intersectsSphere(BoundingSphere bs) {
assert Vector3f.isValidVector(center) && Vector3f.isValidVector(bs.center); return bs.intersectsBoundingBox(this);
if (FastMath.abs(center.x - bs.center.x) < bs.getRadius()
+ xExtent
&& FastMath.abs(center.y - bs.center.y) < bs.getRadius()
+ yExtent
&& FastMath.abs(center.z - bs.center.z) < bs.getRadius()
+ zExtent) {
return true;
}
return false;
} }
/** /**
@ -790,6 +780,7 @@ public class BoundingBox extends BoundingVolume {
} }
} }
@Override
public int collideWith(Collidable other, CollisionResults results) { public int collideWith(Collidable other, CollisionResults results) {
if (other instanceof Ray) { if (other instanceof Ray) {
Ray ray = (Ray) other; Ray ray = (Ray) other;
@ -802,6 +793,15 @@ public class BoundingBox extends BoundingVolume {
return 1; return 1;
} }
return 0; return 0;
} else if (other instanceof BoundingVolume) {
if (intersects((BoundingVolume) other)) {
CollisionResult r = new CollisionResult();
results.addCollision(r);
return 1;
}
return 0;
} else if (other instanceof Spatial) {
return ((Spatial)other).collideWith(this, results);
} else { } else {
throw new UnsupportedCollisionException("With: " + other.getClass().getSimpleName()); throw new UnsupportedCollisionException("With: " + other.getClass().getSimpleName());
} }
@ -818,6 +818,8 @@ public class BoundingBox extends BoundingVolume {
return 1; return 1;
} }
return 0; return 0;
} else if (other instanceof BoundingVolume) {
return intersects((BoundingVolume) other) ? 1 : 0;
} else { } else {
throw new UnsupportedCollisionException("With: " + other.getClass().getSimpleName()); throw new UnsupportedCollisionException("With: " + other.getClass().getSimpleName());
} }

@ -38,6 +38,7 @@ import com.jme3.collision.UnsupportedCollisionException;
import com.jme3.export.JmeExporter; import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter; import com.jme3.export.JmeImporter;
import com.jme3.math.*; import com.jme3.math.*;
import com.jme3.scene.Spatial;
import com.jme3.util.BufferUtils; import com.jme3.util.BufferUtils;
import com.jme3.util.TempVars; import com.jme3.util.TempVars;
import java.io.IOException; import java.io.IOException;
@ -670,15 +671,7 @@ public class BoundingSphere extends BoundingVolume {
* @see com.jme.bounding.BoundingVolume#intersectsSphere(com.jme.bounding.BoundingSphere) * @see com.jme.bounding.BoundingVolume#intersectsSphere(com.jme.bounding.BoundingSphere)
*/ */
public boolean intersectsSphere(BoundingSphere bs) { public boolean intersectsSphere(BoundingSphere bs) {
assert Vector3f.isValidVector(center) && Vector3f.isValidVector(bs.center); return Intersection.intersect(bs, center, radius);
TempVars vars = TempVars.get();
Vector3f diff = center.subtract(bs.center, vars.vect1);
float rsum = getRadius() + bs.getRadius();
boolean eq = (diff.dot(diff) <= rsum * rsum);
vars.release();
return eq;
} }
/* /*
@ -687,18 +680,7 @@ public class BoundingSphere extends BoundingVolume {
* @see com.jme.bounding.BoundingVolume#intersectsBoundingBox(com.jme.bounding.BoundingBox) * @see com.jme.bounding.BoundingVolume#intersectsBoundingBox(com.jme.bounding.BoundingBox)
*/ */
public boolean intersectsBoundingBox(BoundingBox bb) { public boolean intersectsBoundingBox(BoundingBox bb) {
assert Vector3f.isValidVector(center) && Vector3f.isValidVector(bb.center); return Intersection.intersect(bb, center, radius);
if (FastMath.abs(bb.center.x - center.x) < getRadius()
+ bb.xExtent
&& FastMath.abs(bb.center.y - center.y) < getRadius()
+ bb.yExtent
&& FastMath.abs(bb.center.z - center.z) < getRadius()
+ bb.zExtent) {
return true;
}
return false;
} }
/* /*
@ -1013,17 +995,29 @@ public class BoundingSphere extends BoundingVolume {
} else if (other instanceof Triangle){ } else if (other instanceof Triangle){
Triangle t = (Triangle) other; Triangle t = (Triangle) other;
return collideWithTri(t, results); return collideWithTri(t, results);
} else if (other instanceof BoundingVolume) {
if (intersects((BoundingVolume)other)) {
CollisionResult result = new CollisionResult();
results.addCollision(result);
return 1;
}
return 0;
} else if (other instanceof Spatial) {
return ((Spatial)other).collideWith(this, results);
} else { } else {
throw new UnsupportedCollisionException(); throw new UnsupportedCollisionException();
} }
} }
@Override public int collideWith(Collidable other) { @Override
public int collideWith(Collidable other) {
if (other instanceof Ray) { if (other instanceof Ray) {
Ray ray = (Ray) other; Ray ray = (Ray) other;
return collideWithRay(ray); return collideWithRay(ray);
} else if (other instanceof Triangle){ } else if (other instanceof Triangle){
return super.collideWith(other); return super.collideWith(other);
} else if (other instanceof BoundingVolume) {
return intersects((BoundingVolume)other) ? 1 : 0;
} else { } else {
throw new UnsupportedCollisionException(); throw new UnsupportedCollisionException();
} }

@ -327,12 +327,13 @@ public abstract class BoundingVolume implements Savable, Cloneable, Collidable {
public int collideWith(Collidable other) { public int collideWith(Collidable other) {
TempVars tempVars = TempVars.get(); TempVars tempVars = TempVars.get();
try {
CollisionResults tempResults = tempVars.collisionResults; CollisionResults tempResults = tempVars.collisionResults;
tempResults.clear(); tempResults.clear();
int retval = collideWith(other, tempResults); return collideWith(other, tempResults);
} finally {
tempVars.release(); tempVars.release();
return retval;
} }
}
} }

@ -41,9 +41,55 @@ import static java.lang.Math.min;
/** /**
* This class includes some utility methods for computing intersection * This class includes some utility methods for computing intersection
* between bounding volumes and triangles. * between bounding volumes and triangles.
*
* @author Kirill * @author Kirill
*/ */
public class Intersection { public final class Intersection {
private Intersection() {
}
public static boolean intersect(BoundingSphere sphere, Vector3f center, float radius) {
assert Vector3f.isValidVector(center) && Vector3f.isValidVector(sphere.center);
TempVars vars = TempVars.get();
try {
Vector3f diff = center.subtract(sphere.center, vars.vect1);
float rsum = sphere.getRadius() + radius;
return (diff.dot(diff) <= rsum * rsum);
} finally {
vars.release();
}
}
public static boolean intersect(BoundingBox bbox, Vector3f center, float radius) {
assert Vector3f.isValidVector(center) && Vector3f.isValidVector(bbox.center);
// Arvo's algorithm
float distSqr = radius * radius;
float minX = bbox.center.x - bbox.xExtent;
float maxX = bbox.center.x + bbox.xExtent;
float minY = bbox.center.y - bbox.yExtent;
float maxY = bbox.center.y + bbox.yExtent;
float minZ = bbox.center.z - bbox.zExtent;
float maxZ = bbox.center.z + bbox.zExtent;
if (center.x < minX) distSqr -= FastMath.sqr(center.x - minX);
else if (center.x > maxX) distSqr -= FastMath.sqr(center.x - maxX);
if (center.y < minY) distSqr -= FastMath.sqr(center.y - minY);
else if (center.y > maxY) distSqr -= FastMath.sqr(center.y - maxY);
if (center.z < minZ) distSqr -= FastMath.sqr(center.z - minZ);
else if (center.z > maxZ) distSqr -= FastMath.sqr(center.z - maxZ);
return distSqr > 0;
}
private static final void findMinMax(float x0, float x1, float x2, Vector3f minMax) { private static final void findMinMax(float x0, float x1, float x2, Vector3f minMax) {
minMax.set(x0, x0, 0); minMax.set(x0, x0, 0);

@ -702,8 +702,10 @@ public class Cinematic extends AbstractCinematicEvent implements AppState {
dispose(); dispose();
cinematicEvents.clear(); cinematicEvents.clear();
timeLine.clear(); timeLine.clear();
if (eventsData != null) {
eventsData.clear(); eventsData.clear();
} }
}
/** /**
* used internally to cleanup the cinematic. Called when the clear() method * used internally to cleanup the cinematic. Called when the clear() method

@ -43,7 +43,7 @@ import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule; import com.jme3.export.OutputCapsule;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -431,15 +431,17 @@ public class AnimationEvent extends AbstractCinematicEvent {
@Override @Override
public void dispose() { public void dispose() {
super.dispose(); super.dispose();
if (cinematic != null) {
Object o = cinematic.getEventData(MODEL_CHANNELS, model); Object o = cinematic.getEventData(MODEL_CHANNELS, model);
if (o != null) { if (o != null) {
ArrayList<AnimChannel> list = (ArrayList<AnimChannel>) o; Collection<AnimChannel> values = ((HashMap<Integer, AnimChannel>) o).values();
list.remove(channel); while (values.remove(channel));
if (list.isEmpty()) { if (values.isEmpty()) {
cinematic.removeEventData(MODEL_CHANNELS, model); cinematic.removeEventData(MODEL_CHANNELS, model);
} }
} }
cinematic = null; cinematic = null;
channel = null; channel = null;
} }
}
} }

@ -48,6 +48,7 @@ import com.jme3.math.Vector3f;
import com.jme3.scene.CollisionData; import com.jme3.scene.CollisionData;
import com.jme3.scene.Mesh; import com.jme3.scene.Mesh;
import com.jme3.scene.Mesh.Mode; import com.jme3.scene.Mesh.Mode;
import com.jme3.scene.VertexBuffer;
import com.jme3.scene.VertexBuffer.Type; import com.jme3.scene.VertexBuffer.Type;
import com.jme3.scene.mesh.IndexBuffer; import com.jme3.scene.mesh.IndexBuffer;
import com.jme3.scene.mesh.VirtualIndexBuffer; import com.jme3.scene.mesh.VirtualIndexBuffer;
@ -114,8 +115,13 @@ public class BIHTree implements CollisionData {
bihSwapTmp = new float[9]; bihSwapTmp = new float[9];
FloatBuffer vb = (FloatBuffer) mesh.getBuffer(Type.Position).getData(); VertexBuffer vBuffer = mesh.getBuffer(Type.Position);
if(vBuffer == null){
throw new IllegalArgumentException("A mesh should at least contain a Position buffer");
}
IndexBuffer ib = mesh.getIndexBuffer(); IndexBuffer ib = mesh.getIndexBuffer();
FloatBuffer vb = (FloatBuffer) vBuffer.getData();
if (ib == null) { if (ib == null) {
ib = new VirtualIndexBuffer(mesh.getVertexCount(), mesh.getMode()); ib = new VirtualIndexBuffer(mesh.getVertexCount(), mesh.getMode());
} else if (mesh.getMode() != Mode.Triangles) { } else if (mesh.getMode() != Mode.Triangles) {

@ -59,6 +59,7 @@ class BitmapTextPage extends Geometry {
BitmapTextPage(BitmapFont font, boolean arrayBased, int page) { BitmapTextPage(BitmapFont font, boolean arrayBased, int page) {
super("BitmapFont", new Mesh()); super("BitmapFont", new Mesh());
setRequiresUpdates(false);
setBatchHint(BatchHint.Never); setBatchHint(BatchHint.Never);
if (font == null) { if (font == null) {
throw new IllegalArgumentException("font cannot be null."); throw new IllegalArgumentException("font cannot be null.");

@ -72,9 +72,11 @@ public class DefaultJoystickAxis implements JoystickAxis {
* @param negativeMapping The mapping to receive events when the axis is positive * @param negativeMapping The mapping to receive events when the axis is positive
*/ */
public void assignAxis(String positiveMapping, String negativeMapping){ public void assignAxis(String positiveMapping, String negativeMapping){
if (axisIndex != -1) {
inputManager.addMapping(positiveMapping, new JoyAxisTrigger(parent.getJoyId(), axisIndex, false)); inputManager.addMapping(positiveMapping, new JoyAxisTrigger(parent.getJoyId(), axisIndex, false));
inputManager.addMapping(negativeMapping, new JoyAxisTrigger(parent.getJoyId(), axisIndex, true)); inputManager.addMapping(negativeMapping, new JoyAxisTrigger(parent.getJoyId(), axisIndex, true));
} }
}
/** /**
* Returns the joystick to which this axis object belongs. * Returns the joystick to which this axis object belongs.
@ -133,6 +135,14 @@ public class DefaultJoystickAxis implements JoystickAxis {
return deadZone; return deadZone;
} }
/**
* Sets/overrides the dead zone for this axis. This indicates that values
* within +/- deadZone should be ignored.
*/
public void setDeadZone( float f ) {
this.deadZone = f;
}
@Override @Override
public String toString(){ public String toString(){
return "JoystickAxis[name=" + name + ", parent=" + parent.getName() + ", id=" + axisIndex return "JoystickAxis[name=" + name + ", parent=" + parent.getName() + ", id=" + axisIndex

@ -277,8 +277,7 @@ public class FlyByCamera implements AnalogListener, ActionListener {
} }
/** /**
* Registers the FlyByCamera to receive input events from the provided * Unregisters the FlyByCamera from the event Dispatcher.
* Dispatcher.
*/ */
public void unregisterInput(){ public void unregisterInput(){

@ -39,6 +39,7 @@ import com.jme3.math.FastMath;
import com.jme3.math.Vector2f; import com.jme3.math.Vector2f;
import com.jme3.util.IntMap; import com.jme3.util.IntMap;
import com.jme3.util.IntMap.Entry; import com.jme3.util.IntMap.Entry;
import com.jme3.util.SafeArrayList;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.logging.Level; import java.util.logging.Level;
@ -96,16 +97,15 @@ public class InputManager implements RawInputListener {
private boolean eventsPermitted = false; private boolean eventsPermitted = false;
private boolean mouseVisible = true; private boolean mouseVisible = true;
private boolean safeMode = false; private boolean safeMode = false;
private float axisDeadZone = 0.05f; private float globalAxisDeadZone = 0.05f;
private Vector2f cursorPos = new Vector2f(); private final Vector2f cursorPos = new Vector2f();
private Joystick[] joysticks; private Joystick[] joysticks;
private final IntMap<ArrayList<Mapping>> bindings = new IntMap<ArrayList<Mapping>>(); private final IntMap<ArrayList<Mapping>> bindings = new IntMap<ArrayList<Mapping>>();
private final HashMap<String, Mapping> mappings = new HashMap<String, Mapping>(); private final HashMap<String, Mapping> mappings = new HashMap<String, Mapping>();
private final IntMap<Long> pressedButtons = new IntMap<Long>(); private final IntMap<Long> pressedButtons = new IntMap<Long>();
private final IntMap<Float> axisValues = new IntMap<Float>(); private final IntMap<Float> axisValues = new IntMap<Float>();
private ArrayList<RawInputListener> rawListeners = new ArrayList<RawInputListener>(); private final SafeArrayList<RawInputListener> rawListeners = new SafeArrayList<RawInputListener>(RawInputListener.class);
private RawInputListener[] rawListenerArray = null; private final ArrayList<InputEvent> inputQueue = new ArrayList<InputEvent>();
private ArrayList<InputEvent> inputQueue = new ArrayList<InputEvent>();
private static class Mapping { private static class Mapping {
@ -248,8 +248,8 @@ public class InputManager implements RawInputListener {
} }
} }
private void invokeAnalogsAndActions(int hash, float value, boolean applyTpf) { private void invokeAnalogsAndActions(int hash, float value, float effectiveDeadZone, boolean applyTpf) {
if (value < axisDeadZone) { if (value < effectiveDeadZone) {
invokeAnalogs(hash, value, !applyTpf); invokeAnalogs(hash, value, !applyTpf);
return; return;
} }
@ -287,12 +287,14 @@ public class InputManager implements RawInputListener {
/** /**
* Callback from RawInputListener. Do not use. * Callback from RawInputListener. Do not use.
*/ */
@Override
public void beginInput() { public void beginInput() {
} }
/** /**
* Callback from RawInputListener. Do not use. * Callback from RawInputListener. Do not use.
*/ */
@Override
public void endInput() { public void endInput() {
} }
@ -304,17 +306,18 @@ public class InputManager implements RawInputListener {
int joyId = evt.getJoyIndex(); int joyId = evt.getJoyIndex();
int axis = evt.getAxisIndex(); int axis = evt.getAxisIndex();
float value = evt.getValue(); float value = evt.getValue();
if (value < axisDeadZone && value > -axisDeadZone) { float effectiveDeadZone = Math.max(globalAxisDeadZone, evt.getAxis().getDeadZone());
if (value < effectiveDeadZone && value > -effectiveDeadZone) {
int hash1 = JoyAxisTrigger.joyAxisHash(joyId, axis, true); int hash1 = JoyAxisTrigger.joyAxisHash(joyId, axis, true);
int hash2 = JoyAxisTrigger.joyAxisHash(joyId, axis, false); int hash2 = JoyAxisTrigger.joyAxisHash(joyId, axis, false);
Float val1 = axisValues.get(hash1); Float val1 = axisValues.get(hash1);
Float val2 = axisValues.get(hash2); Float val2 = axisValues.get(hash2);
if (val1 != null && val1.floatValue() > axisDeadZone) { if (val1 != null && val1 > effectiveDeadZone) {
invokeActions(hash1, false); invokeActions(hash1, false);
} }
if (val2 != null && val2.floatValue() > axisDeadZone) { if (val2 != null && val2 > effectiveDeadZone) {
invokeActions(hash2, false); invokeActions(hash2, false);
} }
@ -328,11 +331,11 @@ public class InputManager implements RawInputListener {
// Clear the reverse direction's actions in case we // Clear the reverse direction's actions in case we
// crossed center too quickly // crossed center too quickly
Float otherVal = axisValues.get(otherHash); Float otherVal = axisValues.get(otherHash);
if (otherVal != null && otherVal.floatValue() > axisDeadZone) { if (otherVal != null && otherVal > effectiveDeadZone) {
invokeActions(otherHash, false); invokeActions(otherHash, false);
} }
invokeAnalogsAndActions(hash, -value, true); invokeAnalogsAndActions(hash, -value, effectiveDeadZone, true);
axisValues.put(hash, -value); axisValues.put(hash, -value);
axisValues.remove(otherHash); axisValues.remove(otherHash);
} else { } else {
@ -342,11 +345,11 @@ public class InputManager implements RawInputListener {
// Clear the reverse direction's actions in case we // Clear the reverse direction's actions in case we
// crossed center too quickly // crossed center too quickly
Float otherVal = axisValues.get(otherHash); Float otherVal = axisValues.get(otherHash);
if (otherVal != null && otherVal.floatValue() > axisDeadZone) { if (otherVal != null && otherVal > effectiveDeadZone) {
invokeActions(otherHash, false); invokeActions(otherHash, false);
} }
invokeAnalogsAndActions(hash, value, true); invokeAnalogsAndActions(hash, value, effectiveDeadZone, true);
axisValues.put(hash, value); axisValues.put(hash, value);
axisValues.remove(otherHash); axisValues.remove(otherHash);
} }
@ -355,6 +358,7 @@ public class InputManager implements RawInputListener {
/** /**
* Callback from RawInputListener. Do not use. * Callback from RawInputListener. Do not use.
*/ */
@Override
public void onJoyAxisEvent(JoyAxisEvent evt) { public void onJoyAxisEvent(JoyAxisEvent evt) {
if (!eventsPermitted) { if (!eventsPermitted) {
throw new UnsupportedOperationException("JoyInput has raised an event at an illegal time."); throw new UnsupportedOperationException("JoyInput has raised an event at an illegal time.");
@ -376,6 +380,7 @@ public class InputManager implements RawInputListener {
/** /**
* Callback from RawInputListener. Do not use. * Callback from RawInputListener. Do not use.
*/ */
@Override
public void onJoyButtonEvent(JoyButtonEvent evt) { public void onJoyButtonEvent(JoyButtonEvent evt) {
if (!eventsPermitted) { if (!eventsPermitted) {
throw new UnsupportedOperationException("JoyInput has raised an event at an illegal time."); throw new UnsupportedOperationException("JoyInput has raised an event at an illegal time.");
@ -391,15 +396,15 @@ public class InputManager implements RawInputListener {
if (evt.getDX() != 0) { if (evt.getDX() != 0) {
float val = Math.abs(evt.getDX()) / 1024f; float val = Math.abs(evt.getDX()) / 1024f;
invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_X, evt.getDX() < 0), val, false); invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_X, evt.getDX() < 0), val, globalAxisDeadZone, false);
} }
if (evt.getDY() != 0) { if (evt.getDY() != 0) {
float val = Math.abs(evt.getDY()) / 1024f; float val = Math.abs(evt.getDY()) / 1024f;
invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_Y, evt.getDY() < 0), val, false); invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_Y, evt.getDY() < 0), val, globalAxisDeadZone, false);
} }
if (evt.getDeltaWheel() != 0) { if (evt.getDeltaWheel() != 0) {
float val = Math.abs(evt.getDeltaWheel()) / 100f; float val = Math.abs(evt.getDeltaWheel()) / 100f;
invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_WHEEL, evt.getDeltaWheel() < 0), val, false); invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_WHEEL, evt.getDeltaWheel() < 0), val, globalAxisDeadZone, false);
} }
} }
@ -419,6 +424,7 @@ public class InputManager implements RawInputListener {
/** /**
* Callback from RawInputListener. Do not use. * Callback from RawInputListener. Do not use.
*/ */
@Override
public void onMouseMotionEvent(MouseMotionEvent evt) { public void onMouseMotionEvent(MouseMotionEvent evt) {
if (!eventsPermitted) { if (!eventsPermitted) {
throw new UnsupportedOperationException("MouseInput has raised an event at an illegal time."); throw new UnsupportedOperationException("MouseInput has raised an event at an illegal time.");
@ -437,6 +443,7 @@ public class InputManager implements RawInputListener {
/** /**
* Callback from RawInputListener. Do not use. * Callback from RawInputListener. Do not use.
*/ */
@Override
public void onMouseButtonEvent(MouseButtonEvent evt) { public void onMouseButtonEvent(MouseButtonEvent evt) {
if (!eventsPermitted) { if (!eventsPermitted) {
throw new UnsupportedOperationException("MouseInput has raised an event at an illegal time."); throw new UnsupportedOperationException("MouseInput has raised an event at an illegal time.");
@ -459,6 +466,7 @@ public class InputManager implements RawInputListener {
/** /**
* Callback from RawInputListener. Do not use. * Callback from RawInputListener. Do not use.
*/ */
@Override
public void onKeyEvent(KeyInputEvent evt) { public void onKeyEvent(KeyInputEvent evt) {
if (!eventsPermitted) { if (!eventsPermitted) {
throw new UnsupportedOperationException("KeyInput has raised an event at an illegal time."); throw new UnsupportedOperationException("KeyInput has raised an event at an illegal time.");
@ -477,7 +485,7 @@ public class InputManager implements RawInputListener {
* @param deadZone the deadzone for joystick axes. * @param deadZone the deadzone for joystick axes.
*/ */
public void setAxisDeadZone(float deadZone) { public void setAxisDeadZone(float deadZone) {
this.axisDeadZone = deadZone; this.globalAxisDeadZone = deadZone;
} }
/** /**
@ -486,7 +494,7 @@ public class InputManager implements RawInputListener {
* @return the deadzone for joystick axes. * @return the deadzone for joystick axes.
*/ */
public float getAxisDeadZone() { public float getAxisDeadZone() {
return axisDeadZone; return globalAxisDeadZone;
} }
/** /**
@ -721,7 +729,6 @@ public class InputManager implements RawInputListener {
*/ */
public void addRawInputListener(RawInputListener listener) { public void addRawInputListener(RawInputListener listener) {
rawListeners.add(listener); rawListeners.add(listener);
rawListenerArray = null;
} }
/** /**
@ -734,7 +741,6 @@ public class InputManager implements RawInputListener {
*/ */
public void removeRawInputListener(RawInputListener listener) { public void removeRawInputListener(RawInputListener listener) {
rawListeners.remove(listener); rawListeners.remove(listener);
rawListenerArray = null;
} }
/** /**
@ -744,13 +750,6 @@ public class InputManager implements RawInputListener {
*/ */
public void clearRawInputListeners() { public void clearRawInputListeners() {
rawListeners.clear(); rawListeners.clear();
rawListenerArray = null;
}
private RawInputListener[] getRawListenerArray() {
if (rawListenerArray == null)
rawListenerArray = rawListeners.toArray(new RawInputListener[rawListeners.size()]);
return rawListenerArray;
} }
/** /**
@ -813,7 +812,7 @@ public class InputManager implements RawInputListener {
private void processQueue() { private void processQueue() {
int queueSize = inputQueue.size(); int queueSize = inputQueue.size();
RawInputListener[] array = getRawListenerArray(); RawInputListener[] array = rawListeners.getArray();
for (RawInputListener listener : array) { for (RawInputListener listener : array) {
listener.beginInput(); listener.beginInput();

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2009-2012 jMonkeyEngine * Copyright (c) 2009-2012, 2015 jMonkeyEngine
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -32,6 +32,8 @@
package com.jme3.light; package com.jme3.light;
import com.jme3.bounding.BoundingBox; import com.jme3.bounding.BoundingBox;
import com.jme3.bounding.BoundingSphere;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera; import com.jme3.renderer.Camera;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
@ -49,11 +51,23 @@ import com.jme3.util.TempVars;
*/ */
public class AmbientLight extends Light { public class AmbientLight extends Light {
public AmbientLight() {
}
public AmbientLight(ColorRGBA color) {
super(color);
}
@Override @Override
public boolean intersectsBox(BoundingBox box, TempVars vars) { public boolean intersectsBox(BoundingBox box, TempVars vars) {
return true; return true;
} }
@Override
public boolean intersectsSphere(BoundingSphere sphere, TempVars vars) {
return true;
}
@Override @Override
public boolean intersectsFrustum(Camera camera, TempVars vars) { public boolean intersectsFrustum(Camera camera, TempVars vars) {
return true; return true;
@ -61,6 +75,8 @@ public class AmbientLight extends Light {
@Override @Override
public void computeLastDistance(Spatial owner) { public void computeLastDistance(Spatial owner) {
// ambient lights must always be before directional lights.
lastDistance = -2;
} }
@Override @Override

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2009-2014 jMonkeyEngine * Copyright (c) 2009-2015 jMonkeyEngine
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -44,6 +44,7 @@ public final class DefaultLightFilter implements LightFilter {
private Camera camera; private Camera camera;
private final HashSet<Light> processedLights = new HashSet<Light>(); private final HashSet<Light> processedLights = new HashSet<Light>();
@Override
public void setCamera(Camera camera) { public void setCamera(Camera camera) {
this.camera = camera; this.camera = camera;
for (Light light : processedLights) { for (Light light : processedLights) {
@ -51,6 +52,7 @@ public final class DefaultLightFilter implements LightFilter {
} }
} }
@Override
public void filterLights(Geometry geometry, LightList filteredLightList) { public void filterLights(Geometry geometry, LightList filteredLightList) {
TempVars vars = TempVars.get(); TempVars vars = TempVars.get();
try { try {
@ -76,8 +78,9 @@ public final class DefaultLightFilter implements LightFilter {
} }
} else if (bv instanceof BoundingSphere) { } else if (bv instanceof BoundingSphere) {
if (!Float.isInfinite( ((BoundingSphere)bv).getRadius() )) { if (!Float.isInfinite( ((BoundingSphere)bv).getRadius() )) {
// Non-infinite bounding sphere... Not supported yet. if (!light.intersectsSphere((BoundingSphere)bv, vars)) {
throw new UnsupportedOperationException("Only AABB supported for now"); continue;
}
} }
} }

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2009-2012 jMonkeyEngine * Copyright (c) 2009-2012, 2015 jMonkeyEngine
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -32,10 +32,12 @@
package com.jme3.light; package com.jme3.light;
import com.jme3.bounding.BoundingBox; import com.jme3.bounding.BoundingBox;
import com.jme3.bounding.BoundingSphere;
import com.jme3.export.InputCapsule; import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter; import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter; import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule; import com.jme3.export.OutputCapsule;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera; import com.jme3.renderer.Camera;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
@ -53,9 +55,35 @@ public class DirectionalLight extends Light {
protected Vector3f direction = new Vector3f(0f, -1f, 0f); protected Vector3f direction = new Vector3f(0f, -1f, 0f);
/**
* Creates a DirectionalLight
*/
public DirectionalLight() {
}
/**
* Creates a DirectionalLight with the given direction
* @param direction the light's direction
*/
public DirectionalLight(Vector3f direction) {
setDirection(direction);
}
/**
* Creates a DirectionalLight with the given direction and the given color
* @param direction the light's direction
* @param color the light's color
*/
public DirectionalLight(Vector3f direction, ColorRGBA color) {
super(color);
setDirection(direction);
}
@Override @Override
public void computeLastDistance(Spatial owner) { public void computeLastDistance(Spatial owner) {
lastDistance = 0; // directional lights are always closest to their owner // directional lights are after ambient lights
// but before all other lights.
lastDistance = -1;
} }
/** /**
@ -77,7 +105,7 @@ public class DirectionalLight extends Light {
* *
* @param dir the direction of the light. * @param dir the direction of the light.
*/ */
public void setDirection(Vector3f dir){ public final void setDirection(Vector3f dir){
direction.set(dir); direction.set(dir);
if (!direction.isUnitVector()) { if (!direction.isUnitVector()) {
direction.normalizeLocal(); direction.normalizeLocal();
@ -89,6 +117,11 @@ public class DirectionalLight extends Light {
return true; return true;
} }
@Override
public boolean intersectsSphere(BoundingSphere sphere, TempVars vars) {
return true;
}
@Override @Override
public boolean intersectsFrustum(Camera camera, TempVars vars) { public boolean intersectsFrustum(Camera camera, TempVars vars) {
return true; return true;

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2009-2012 jMonkeyEngine * Copyright (c) 2009-2012, 2015 jMonkeyEngine
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -32,6 +32,7 @@
package com.jme3.light; package com.jme3.light;
import com.jme3.bounding.BoundingBox; import com.jme3.bounding.BoundingBox;
import com.jme3.bounding.BoundingSphere;
import com.jme3.export.*; import com.jme3.export.*;
import com.jme3.math.ColorRGBA; import com.jme3.math.ColorRGBA;
import com.jme3.renderer.Camera; import com.jme3.renderer.Camera;
@ -94,7 +95,7 @@ public abstract class Light implements Savable, Cloneable {
} }
} }
protected ColorRGBA color = new ColorRGBA(1f,1f,1f,1f); protected ColorRGBA color = new ColorRGBA(ColorRGBA.White);
/** /**
* Used in LightList for caching the distance * Used in LightList for caching the distance
@ -115,6 +116,13 @@ public abstract class Light implements Savable, Cloneable {
boolean frustumCheckNeeded = true; boolean frustumCheckNeeded = true;
boolean intersectsFrustum = false; boolean intersectsFrustum = false;
protected Light() {
}
protected Light(ColorRGBA color) {
setColor(color);
}
/** /**
* Returns the color of the light. * Returns the color of the light.
* *
@ -190,7 +198,21 @@ public abstract class Light implements Savable, Cloneable {
public abstract boolean intersectsBox(BoundingBox box, TempVars vars); public abstract boolean intersectsBox(BoundingBox box, TempVars vars);
/** /**
* Determines if the lgiht intersects with the given camera frustum. * Determines if the light intersects with the given bounding sphere.
* <p>
* For non-local lights, such as {@link DirectionalLight directional lights},
* {@link AmbientLight ambient lights}, or {@link PointLight point lights}
* without influence radius, this method should always return true.
*
* @param sphere The sphere to check intersection against.
* @param vars TempVars in case it is needed.
*
* @return True if the light intersects the sphere, false otherwise.
*/
public abstract boolean intersectsSphere(BoundingSphere sphere, TempVars vars);
/**
* Determines if the light intersects with the given camera frustum.
* *
* For non-local lights, such as {@link DirectionalLight directional lights}, * For non-local lights, such as {@link DirectionalLight directional lights},
* {@link AmbientLight ambient lights}, or {@link PointLight point lights} * {@link AmbientLight ambient lights}, or {@link PointLight point lights}

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2009-2012 jMonkeyEngine * Copyright (c) 2009-2012, 2015 jMonkeyEngine
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -34,12 +34,13 @@ package com.jme3.light;
import com.jme3.bounding.BoundingBox; import com.jme3.bounding.BoundingBox;
import com.jme3.bounding.BoundingSphere; import com.jme3.bounding.BoundingSphere;
import com.jme3.bounding.BoundingVolume; import com.jme3.bounding.BoundingVolume;
import com.jme3.bounding.Intersection;
import com.jme3.export.InputCapsule; import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter; import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter; import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule; import com.jme3.export.OutputCapsule;
import com.jme3.math.FastMath; import com.jme3.math.FastMath;
import com.jme3.math.Plane; import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera; import com.jme3.renderer.Camera;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
@ -62,6 +63,52 @@ public class PointLight extends Light {
protected float radius = 0; protected float radius = 0;
protected float invRadius = 0; protected float invRadius = 0;
/**
* Creates a PointLight
*/
public PointLight() {
}
/**
* Creates a PointLight at the given position
* @param position the position in world space
*/
public PointLight(Vector3f position) {
setPosition(position);
}
/**
* Creates a PointLight at the given position and with the given color
* @param position the position in world space
* @param color the light color
*/
public PointLight(Vector3f position, ColorRGBA color) {
super(color);
setPosition(position);
}
/**
* Creates a PointLight at the given position, with the given color and the
* given radius
* @param position the position in world space
* @param color the light color
* @param radius the light radius
*/
public PointLight(Vector3f position, ColorRGBA color, float radius) {
this(position, color);
setRadius(radius);
}
/**
* Creates a PointLight at the given position, with the given radius
* @param position the position in world space
* @param radius the light radius
*/
public PointLight(Vector3f position, float radius) {
this(position);
setRadius(radius);
}
@Override @Override
public void computeLastDistance(Spatial owner) { public void computeLastDistance(Spatial owner) {
if (owner.getWorldBound() != null) { if (owner.getWorldBound() != null) {
@ -88,7 +135,7 @@ public class PointLight extends Light {
* *
* @param position the world space position of the light. * @param position the world space position of the light.
*/ */
public void setPosition(Vector3f position) { public final void setPosition(Vector3f position) {
this.position.set(position); this.position.set(position);
} }
@ -115,13 +162,13 @@ public class PointLight extends Light {
* *
* @throws IllegalArgumentException If radius is negative * @throws IllegalArgumentException If radius is negative
*/ */
public void setRadius(float radius) { public final void setRadius(float radius) {
if (radius < 0) { if (radius < 0) {
throw new IllegalArgumentException("Light radius cannot be negative"); throw new IllegalArgumentException("Light radius cannot be negative");
} }
this.radius = radius; this.radius = radius;
if (radius != 0) { if (radius != 0f) {
this.invRadius = 1 / radius; this.invRadius = 1f / radius;
} else { } else {
this.invRadius = 0; this.invRadius = 0;
} }
@ -146,9 +193,17 @@ public class PointLight extends Light {
return true; return true;
} else { } else {
// Sphere v. box collision // Sphere v. box collision
return FastMath.abs(box.getCenter().x - position.x) < radius + box.getXExtent() return Intersection.intersect(box, position, radius);
&& FastMath.abs(box.getCenter().y - position.y) < radius + box.getYExtent() }
&& FastMath.abs(box.getCenter().z - position.z) < radius + box.getZExtent(); }
@Override
public boolean intersectsSphere(BoundingSphere sphere, TempVars vars) {
if (this.radius == 0) {
return true;
} else {
// Sphere v. sphere collision
return Intersection.intersect(sphere, position, radius);
} }
} }

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2009-2012 jMonkeyEngine * Copyright (c) 2009-2012, 2015 jMonkeyEngine
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -32,8 +32,11 @@
package com.jme3.light; package com.jme3.light;
import com.jme3.bounding.BoundingBox; import com.jme3.bounding.BoundingBox;
import com.jme3.bounding.BoundingSphere;
import com.jme3.bounding.BoundingVolume; import com.jme3.bounding.BoundingVolume;
import com.jme3.bounding.Intersection;
import com.jme3.export.*; import com.jme3.export.*;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath; import com.jme3.math.FastMath;
import com.jme3.math.Plane; import com.jme3.math.Plane;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
@ -44,36 +47,121 @@ import java.io.IOException;
/** /**
* Represents a spot light. * Represents a spot light.
* A spot light emmit a cone of light from a position and in a direction. * A spot light emits a cone of light from a position and in a direction.
* It can be used to fake torch lights or car's lights. * It can be used to fake torch lights or cars' lights.
* <p> * <p>
* In addition to a position and a direction, spot lights also have a range which * In addition to a position and a direction, spot lights also have a range which
* can be used to attenuate the influence of the light depending on the * can be used to attenuate the influence of the light depending on the
* distance between the light and the effected object. * distance between the light and the affected object.
* Also the angle of the cone can be tweaked by changing the spot inner angle and the spot outer angle. * Also the angle of the cone can be tweaked by changing the spot inner angle and the spot outer angle.
* the spot inner angle determin the cone of light where light has full influence. * the spot inner angle determines the cone of light where light has full influence.
* the spot outer angle determin the cone global cone of light of the spot light. * the spot outer angle determines the cone global cone of light of the spot light.
* the light intensity slowly decrease between the inner cone and the outer cone. * the light intensity slowly decreases between the inner cone and the outer cone.
* @author Nehon * @author Nehon
*/ */
public class SpotLight extends Light { public class SpotLight extends Light {
protected Vector3f position = new Vector3f(); protected Vector3f position = new Vector3f();
protected Vector3f direction = new Vector3f(0,-1,0); protected Vector3f direction = new Vector3f(0, -1, 0);
protected float spotInnerAngle = FastMath.QUARTER_PI / 8; protected float spotInnerAngle = FastMath.QUARTER_PI / 8;
protected float spotOuterAngle = FastMath.QUARTER_PI / 6; protected float spotOuterAngle = FastMath.QUARTER_PI / 6;
protected float spotRange = 100; protected float spotRange = 100;
protected float invSpotRange = 1f / 100; protected float invSpotRange = 1f / 100;
protected float packedAngleCos=0; protected float packedAngleCos = 0;
protected float outerAngleCosSqr, outerAngleSinSqr; protected float outerAngleCosSqr, outerAngleSinSqr;
protected float outerAngleSinRcp, outerAngleSin, outerAngleCos; protected float outerAngleSinRcp, outerAngleSin, outerAngleCos;
/**
* Creates a SpotLight.
*/
public SpotLight() { public SpotLight() {
super(); super();
computeAngleParameters(); computeAngleParameters();
} }
/**
* Creates a SpotLight at the given position and with the given direction.
* @param position the position in world space.
* @param direction the direction of the light.
*/
public SpotLight(Vector3f position, Vector3f direction) {
this();
setPosition(position);
setDirection(direction);
}
/**
* Creates a SpotLight at the given position, with the given direction, and the
* given range.
* @param position the position in world space.
* @param direction the direction of the light.
* @param range the spot light range
*/
public SpotLight(Vector3f position, Vector3f direction, float range) {
this();
setPosition(position);
setDirection(direction);
this.spotRange = range;
}
/**
* Creates a SpotLight at the given position, with the given direction and
* the given color.
* @param position the position in world space.
* @param direction the direction of the light.
* @param color the light's color.
*/
public SpotLight(Vector3f position, Vector3f direction, ColorRGBA color) {
super(color);
computeAngleParameters();
setPosition(position);
setDirection(direction);
}
/**
* Creates a SpotLight at the given position, with the given direction,
* the given range and the given color.
* @param position the position in world space.
* @param direction the direction of the light.
* @param range the spot light range
* @param color the light's color.
*/
public SpotLight(Vector3f position, Vector3f direction, float range, ColorRGBA color) {
super(color);
computeAngleParameters();
setPosition(position);
setDirection(direction);
this.spotRange = range;
}
/**
* Creates a SpotLight at the given position, with the given direction,
* the given color and the given inner and outer angles
* (controls the falloff of the light)
*
* @param position the position in world space.
* @param direction the direction of the light.
* @param range the spot light range
* @param color the light's color.
* @param innerAngle the inner angle of the spot light.
* @param outerAngle the outer angle of the spot light.
*
* @see SpotLight#setSpotInnerAngle(float)
* @see SpotLight#setSpotOuterAngle(float)
*/
public SpotLight(Vector3f position, Vector3f direction, float range, ColorRGBA color, float innerAngle, float outerAngle) {
super(color);
this.spotInnerAngle = innerAngle;
this.spotOuterAngle = outerAngle;
computeAngleParameters();
setPosition(position);
setDirection(direction);
this.spotRange = range;
}
private void computeAngleParameters() { private void computeAngleParameters() {
float innerCos = FastMath.cos(spotInnerAngle); float innerCos = FastMath.cos(spotInnerAngle);
outerAngleCos = FastMath.cos(spotOuterAngle); outerAngleCos = FastMath.cos(spotOuterAngle);
@ -102,9 +190,7 @@ public class SpotLight extends Light {
if (this.spotRange > 0f) { if (this.spotRange > 0f) {
// Check spot range first. // Check spot range first.
// Sphere v. box collision // Sphere v. box collision
if (FastMath.abs(box.getCenter().x - position.x) >= spotRange + box.getXExtent() if (!Intersection.intersect(box, position, spotRange)) {
|| FastMath.abs(box.getCenter().y - position.y) >= spotRange + box.getYExtent()
|| FastMath.abs(box.getCenter().z - position.z) >= spotRange + box.getZExtent()) {
return false; return false;
} }
} }
@ -140,8 +226,48 @@ public class SpotLight extends Light {
} }
@Override @Override
public boolean intersectsFrustum(Camera cam, TempVars vars) { public boolean intersectsSphere(BoundingSphere sphere, TempVars vars) {
if (this.spotRange > 0f) {
// Check spot range first.
// Sphere v. sphere collision
if (!Intersection.intersect(sphere, position, spotRange)) {
return false;
}
}
float otherRadiusSquared = FastMath.sqr(sphere.getRadius());
float otherRadius = sphere.getRadius();
// Check if sphere is within spot angle.
// Cone v. sphere collision.
Vector3f E = direction.mult(otherRadius * outerAngleSinRcp, vars.vect1);
Vector3f U = position.subtract(E, vars.vect2);
Vector3f D = sphere.getCenter().subtract(U, vars.vect3);
float dsqr = D.dot(D);
float e = direction.dot(D);
if (e > 0f && e * e >= dsqr * outerAngleCosSqr) {
D = sphere.getCenter().subtract(position, vars.vect3);
dsqr = D.dot(D);
e = -direction.dot(D);
if (e > 0f && e * e >= dsqr * outerAngleSinSqr) {
return dsqr <= otherRadiusSquared;
} else {
return true;
}
}
return false;
}
@Override
public boolean intersectsFrustum(Camera cam, TempVars vars) {
if (spotRange == 0) {
// The algorithm below does not support infinite spot range.
return true;
}
Vector3f farPoint = vars.vect1.set(position).addLocal(vars.vect2.set(direction).multLocal(spotRange)); Vector3f farPoint = vars.vect1.set(position).addLocal(vars.vect2.set(direction).multLocal(spotRange));
for (int i = 5; i >= 0; i--) { for (int i = 5; i >= 0; i--) {
//check origin against the plane //check origin against the plane
@ -189,7 +315,7 @@ public class SpotLight extends Light {
return direction; return direction;
} }
public void setDirection(Vector3f direction) { public final void setDirection(Vector3f direction) {
this.direction.set(direction); this.direction.set(direction);
} }
@ -197,7 +323,7 @@ public class SpotLight extends Light {
return position; return position;
} }
public void setPosition(Vector3f position) { public final void setPosition(Vector3f position) {
this.position.set(position); this.position.set(position);
} }

@ -1147,10 +1147,11 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
*/ */
public void render(Geometry geom, LightList lights, RenderManager rm) { public void render(Geometry geom, LightList lights, RenderManager rm) {
autoSelectTechnique(rm); autoSelectTechnique(rm);
TechniqueDef techDef = technique.getDef();
Renderer r = rm.getRenderer(); if (techDef.isNoRender()) return;
TechniqueDef techDef = technique.getDef(); Renderer r = rm.getRenderer();
if (rm.getForcedRenderState() != null) { if (rm.getForcedRenderState() != null) {
r.applyRenderState(rm.getForcedRenderState()); r.applyRenderState(rm.getForcedRenderState());

@ -102,6 +102,7 @@ public class TechniqueDef implements Savable {
private List<ShaderNode> shaderNodes; private List<ShaderNode> shaderNodes;
private ShaderGenerationInfo shaderGenerationInfo; private ShaderGenerationInfo shaderGenerationInfo;
private boolean noRender = false;
private RenderState renderState; private RenderState renderState;
private RenderState forcedRenderState; private RenderState forcedRenderState;
@ -201,6 +202,28 @@ public class TechniqueDef implements Savable {
this.renderState = renderState; this.renderState = renderState;
} }
/**
* Sets if this technique should not be used to render.
*
* @param noRender not render or render ?
*
* @see NoRender
*/
public void setNoRender(boolean noRender) {
this.noRender = noRender;
}
/**
* Returns true if this technique should not be used to render.
* (eg. to not render a material with default technique)
*
* @return true if this technique should not be rendered, false otherwise.
*
*/
public boolean isNoRender(){
return noRender;
}
/** /**
* @deprecated jME3 always requires shaders now * @deprecated jME3 always requires shaders now
*/ */
@ -448,6 +471,7 @@ public class TechniqueDef implements Savable {
oc.write(lightMode, "lightMode", LightMode.Disable); oc.write(lightMode, "lightMode", LightMode.Disable);
oc.write(shadowMode, "shadowMode", ShadowMode.Disable); oc.write(shadowMode, "shadowMode", ShadowMode.Disable);
oc.write(renderState, "renderState", null); oc.write(renderState, "renderState", null);
oc.write(noRender, "noRender", false);
oc.write(usesNodes, "usesNodes", false); oc.write(usesNodes, "usesNodes", false);
oc.writeSavableArrayList((ArrayList)shaderNodes,"shaderNodes", null); oc.writeSavableArrayList((ArrayList)shaderNodes,"shaderNodes", null);
oc.write(shaderGenerationInfo, "shaderGenerationInfo", null); oc.write(shaderGenerationInfo, "shaderGenerationInfo", null);
@ -470,6 +494,7 @@ public class TechniqueDef implements Savable {
lightMode = ic.readEnum("lightMode", LightMode.class, LightMode.Disable); lightMode = ic.readEnum("lightMode", LightMode.class, LightMode.Disable);
shadowMode = ic.readEnum("shadowMode", ShadowMode.class, ShadowMode.Disable); shadowMode = ic.readEnum("shadowMode", ShadowMode.class, ShadowMode.Disable);
renderState = (RenderState) ic.readSavable("renderState", null); renderState = (RenderState) ic.readSavable("renderState", null);
noRender = ic.readBoolean("noRender", false);
if (ic.getSavableVersion(TechniqueDef.class) == 0) { if (ic.getSavableVersion(TechniqueDef.class) == 0) {
// Old version // Old version
@ -525,6 +550,6 @@ public class TechniqueDef implements Savable {
//todo: make toString return something usefull //todo: make toString return something usefull
@Override @Override
public String toString() { public String toString() {
return "TechniqueDef{" + "requiredCaps=" + requiredCaps + ", name=" + name /*+ ", vertName=" + vertName + ", fragName=" + fragName + ", vertLanguage=" + vertLanguage + ", fragLanguage=" + fragLanguage */+ ", presetDefines=" + presetDefines + ", usesNodes=" + usesNodes + ", shaderNodes=" + shaderNodes + ", shaderGenerationInfo=" + shaderGenerationInfo + ", renderState=" + renderState + ", forcedRenderState=" + forcedRenderState + ", lightMode=" + lightMode + ", shadowMode=" + shadowMode + ", defineParams=" + defineParams + ", worldBinds=" + worldBinds + '}'; return "TechniqueDef{" + "requiredCaps=" + requiredCaps + ", name=" + name /*+ ", vertName=" + vertName + ", fragName=" + fragName + ", vertLanguage=" + vertLanguage + ", fragLanguage=" + fragLanguage */+ ", presetDefines=" + presetDefines + ", usesNodes=" + usesNodes + ", shaderNodes=" + shaderNodes + ", shaderGenerationInfo=" + shaderGenerationInfo + ", renderState=" + renderState + ", forcedRenderState=" + forcedRenderState + ", lightMode=" + lightMode + ", shadowMode=" + shadowMode + ", defineParams=" + defineParams + ", worldBinds=" + worldBinds + ", noRender=" + noRender + '}';
} }
} }

@ -87,8 +87,25 @@ final public class FastMath {
return (number > 0) && (number & (number - 1)) == 0; return (number > 0) && (number & (number - 1)) == 0;
} }
/**
* Get the next power of two of the given number.
*
* E.g. for an input 100, this returns 128.
* Returns 1 for all numbers <= 1.
*
* @param number The number to obtain the POT for.
* @return The next power of two.
*/
public static int nearestPowerOfTwo(int number) { public static int nearestPowerOfTwo(int number) {
return (int) Math.pow(2, Math.ceil(Math.log(number) / Math.log(2))); number--;
number |= number >> 1;
number |= number >> 2;
number |= number >> 4;
number |= number >> 8;
number |= number >> 16;
number++;
number += (number == 0) ? 1 : 0;
return number;
} }
/** /**

@ -154,53 +154,34 @@ public final class Matrix4f implements Savable, Cloneable, java.io.Serializable
} }
public void fromFrame(Vector3f location, Vector3f direction, Vector3f up, Vector3f left) { public void fromFrame(Vector3f location, Vector3f direction, Vector3f up, Vector3f left) {
loadIdentity();
TempVars vars = TempVars.get(); TempVars vars = TempVars.get();
try {
Vector3f f = vars.vect1.set(direction); Vector3f fwdVector = vars.vect1.set(direction);
Vector3f s = vars.vect2.set(f).crossLocal(up); Vector3f leftVector = vars.vect2.set(fwdVector).crossLocal(up);
Vector3f u = vars.vect3.set(s).crossLocal(f); Vector3f upVector = vars.vect3.set(leftVector).crossLocal(fwdVector);
// s.normalizeLocal();
// u.normalizeLocal(); m00 = leftVector.x;
m01 = leftVector.y;
m00 = s.x; m02 = leftVector.z;
m01 = s.y; m03 = -leftVector.dot(location);
m02 = s.z;
m10 = upVector.x;
m10 = u.x; m11 = upVector.y;
m11 = u.y; m12 = upVector.z;
m12 = u.z; m13 = -upVector.dot(location);
m20 = -f.x; m20 = -fwdVector.x;
m21 = -f.y; m21 = -fwdVector.y;
m22 = -f.z; m22 = -fwdVector.z;
m23 = fwdVector.dot(location);
// m00 = -left.x;
// m10 = -left.y; m30 = 0f;
// m20 = -left.z; m31 = 0f;
// m32 = 0f;
// m01 = up.x; m33 = 1f;
// m11 = up.y; } finally {
// m21 = up.z;
//
// m02 = -direction.x;
// m12 = -direction.y;
// m22 = -direction.z;
//
Matrix4f transMatrix = vars.tempMat4;
transMatrix.loadIdentity();
transMatrix.m03 = -location.x;
transMatrix.m13 = -location.y;
transMatrix.m23 = -location.z;
this.multLocal(transMatrix);
vars.release(); vars.release();
}
// transMatrix.multLocal(this);
// set(transMatrix);
} }
/** /**

@ -49,7 +49,7 @@ public final class Transform implements Savable, Cloneable, java.io.Serializable
private Quaternion rot = new Quaternion(); private Quaternion rot = new Quaternion();
private Vector3f translation = new Vector3f(); private Vector3f translation = new Vector3f();
private Vector3f scale = new Vector3f(1,1,1); private Vector3f scale = new Vector3f(1, 1, 1);
public Transform(Vector3f translation, Quaternion rot){ public Transform(Vector3f translation, Quaternion rot){
this.translation.set(translation); this.translation.set(translation);
@ -283,9 +283,32 @@ public final class Transform implements Savable, Cloneable, java.io.Serializable
* Loads the identity. Equal to translation=0,0,0 scale=1,1,1 rot=0,0,0,1. * Loads the identity. Equal to translation=0,0,0 scale=1,1,1 rot=0,0,0,1.
*/ */
public void loadIdentity() { public void loadIdentity() {
translation.set(0,0,0); translation.set(0, 0, 0);
scale.set(1,1,1); scale.set(1, 1, 1);
rot.set(0,0,0,1); rot.set(0, 0, 0, 1);
}
@Override
public int hashCode() {
int hash = 7;
hash = 89 * hash + rot.hashCode();
hash = 89 * hash + translation.hashCode();
hash = 89 * hash + scale.hashCode();
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Transform other = (Transform) obj;
return this.translation.equals(other.translation)
&& this.scale.equals(other.scale)
&& this.rot.equals(other.rot);
} }
@Override @Override
@ -307,22 +330,21 @@ public final class Transform implements Savable, Cloneable, java.io.Serializable
return this; return this;
} }
@Override
public void write(JmeExporter e) throws IOException { public void write(JmeExporter e) throws IOException {
OutputCapsule capsule = e.getCapsule(this); OutputCapsule capsule = e.getCapsule(this);
capsule.write(rot, "rot", new Quaternion()); capsule.write(rot, "rot", Quaternion.IDENTITY);
capsule.write(translation, "translation", Vector3f.ZERO); capsule.write(translation, "translation", Vector3f.ZERO);
capsule.write(scale, "scale", Vector3f.UNIT_XYZ); capsule.write(scale, "scale", Vector3f.UNIT_XYZ);
} }
@Override
public void read(JmeImporter e) throws IOException { public void read(JmeImporter e) throws IOException {
InputCapsule capsule = e.getCapsule(this); InputCapsule capsule = e.getCapsule(this);
rot = (Quaternion)capsule.readSavable("rot", new Quaternion()); rot.set((Quaternion)capsule.readSavable("rot", Quaternion.IDENTITY));
translation = (Vector3f)capsule.readSavable("translation", null); translation.set((Vector3f)capsule.readSavable("translation", Vector3f.ZERO));
if( translation == null ) { scale.set((Vector3f)capsule.readSavable("scale", Vector3f.UNIT_XYZ));
translation = new Vector3f();
}
scale = (Vector3f)capsule.readSavable("scale", Vector3f.UNIT_XYZ);
} }
@Override @Override

@ -409,6 +409,19 @@ public abstract class Filter implements Savable {
return true; return true;
} }
/**
* Override this method and return true if you want the scene (input) texture
* to use bilinear filtering or false to use nearest filtering.
*
* Typically filters that perform samples <em>in between</em> pixels
* should enable filtering.
*
* @return true to use linear filtering, false to use nearest filtering.
*/
protected boolean isRequiresBilinear() {
return false;
}
/** /**
* returns the list of the postRender passes * returns the list of the postRender passes
* @return * @return

@ -38,6 +38,7 @@ import com.jme3.renderer.*;
import com.jme3.renderer.queue.RenderQueue; import com.jme3.renderer.queue.RenderQueue;
import com.jme3.texture.FrameBuffer; import com.jme3.texture.FrameBuffer;
import com.jme3.texture.Image.Format; import com.jme3.texture.Image.Format;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture2D; import com.jme3.texture.Texture2D;
import com.jme3.ui.Picture; import com.jme3.ui.Picture;
import com.jme3.util.SafeArrayList; import com.jme3.util.SafeArrayList;
@ -285,6 +286,12 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
} }
} }
boolean wantsBilinear = filter.isRequiresBilinear();
if (wantsBilinear) {
tex.setMagFilter(Texture.MagFilter.Bilinear);
tex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps);
}
buff = outputBuffer; buff = outputBuffer;
if (i != lastFilterIndex) { if (i != lastFilterIndex) {
buff = filter.getRenderFrameBuffer(); buff = filter.getRenderFrameBuffer();
@ -293,6 +300,11 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
} }
renderProcessing(r, buff, mat); renderProcessing(r, buff, mat);
filter.postFilter(r, buff); filter.postFilter(r, buff);
if (wantsBilinear) {
tex.setMagFilter(Texture.MagFilter.Nearest);
tex.setMinFilter(Texture.MinFilter.NearestNoMipMaps);
}
} }
} }
} }
@ -300,7 +312,7 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
public void postFrame(FrameBuffer out) { public void postFrame(FrameBuffer out) {
FrameBuffer sceneBuffer = renderFrameBuffer; FrameBuffer sceneBuffer = renderFrameBuffer;
if (renderFrameBufferMS != null && !renderer.getCaps().contains(Caps.OpenGL31)) { if (renderFrameBufferMS != null && !renderer.getCaps().contains(Caps.OpenGL32)) {
renderer.copyFrameBuffer(renderFrameBufferMS, renderFrameBuffer, true); renderer.copyFrameBuffer(renderFrameBufferMS, renderFrameBuffer, true);
} else if (renderFrameBufferMS != null) { } else if (renderFrameBufferMS != null) {
sceneBuffer = renderFrameBufferMS; sceneBuffer = renderFrameBufferMS;
@ -443,7 +455,7 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
//antialiasing on filters only supported in opengl 3 due to depth read problem //antialiasing on filters only supported in opengl 3 due to depth read problem
if (numSamples > 1 && caps.contains(Caps.FrameBufferMultisample)) { if (numSamples > 1 && caps.contains(Caps.FrameBufferMultisample)) {
renderFrameBufferMS = new FrameBuffer(width, height, numSamples); renderFrameBufferMS = new FrameBuffer(width, height, numSamples);
if (caps.contains(Caps.OpenGL31)) { if (caps.contains(Caps.OpenGL32)) {
Texture2D msColor = new Texture2D(width, height, numSamples, fbFormat); Texture2D msColor = new Texture2D(width, height, numSamples, fbFormat);
Texture2D msDepth = new Texture2D(width, height, numSamples, Format.Depth); Texture2D msDepth = new Texture2D(width, height, numSamples, Format.Depth);
renderFrameBufferMS.setDepthTexture(msDepth); renderFrameBufferMS.setDepthTexture(msDepth);
@ -456,7 +468,7 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
} }
} }
if (numSamples <= 1 || !caps.contains(Caps.OpenGL31)) { if (numSamples <= 1 || !caps.contains(Caps.OpenGL32)) {
renderFrameBuffer = new FrameBuffer(width, height, 1); renderFrameBuffer = new FrameBuffer(width, height, 1);
renderFrameBuffer.setDepthBuffer(Format.Depth); renderFrameBuffer.setDepthBuffer(Format.Depth);
filterTexture = new Texture2D(width, height, fbFormat); filterTexture = new Texture2D(width, height, fbFormat);

@ -77,4 +77,6 @@ public enum Limits {
DepthTextureSamples, DepthTextureSamples,
VertexUniformVectors, VertexUniformVectors,
TextureAnisotropy,
} }

@ -241,12 +241,12 @@ public class RenderContext {
public IDList attribIndexList = new IDList(); public IDList attribIndexList = new IDList();
/** /**
* depth tets function * depth test function
*/ */
public RenderState.TestFunction depthFunc = RenderState.TestFunction.LessOrEqual; public RenderState.TestFunction depthFunc = RenderState.TestFunction.Less;
/** /**
* alpha tets function * alpha test function
*/ */
public RenderState.TestFunction alphaFunc = RenderState.TestFunction.Greater; public RenderState.TestFunction alphaFunc = RenderState.TestFunction.Greater;
@ -255,8 +255,6 @@ public class RenderContext {
public ColorRGBA clearColor = new ColorRGBA(0,0,0,0); public ColorRGBA clearColor = new ColorRGBA(0,0,0,0);
public boolean seamlessCubemap = false;
/** /**
* Reset the RenderContext to default GL state * Reset the RenderContext to default GL state
*/ */
@ -308,6 +306,5 @@ public class RenderContext {
depthFunc = RenderState.TestFunction.LessOrEqual; depthFunc = RenderState.TestFunction.LessOrEqual;
alphaFunc = RenderState.TestFunction.Greater; alphaFunc = RenderState.TestFunction.Greater;
clearColor.set(0,0,0,0); clearColor.set(0,0,0,0);
seamlessCubemap = false;
} }
} }

@ -746,27 +746,43 @@ public class RenderManager {
} }
/** /**
* Sets the light filter to use when rendering Lighted Geometries * Sets the light filter to use when rendering lit Geometries.
* *
* @see LightFilter * @see LightFilter
* @param lightFilter The light filter tose. Set it to null if you want all lights to be rendered * @param lightFilter The light filter. Set it to null if you want all lights to be rendered.
*/ */
public void setLightFilter(LightFilter lightFilter) { public void setLightFilter(LightFilter lightFilter) {
this.lightFilter = lightFilter; this.lightFilter = lightFilter;
} }
/**
* Defines what light mode will be selected when a technique offers several light modes.
* @param preferredLightMode The light mode to use.
*/
public void setPreferredLightMode(TechniqueDef.LightMode preferredLightMode) { public void setPreferredLightMode(TechniqueDef.LightMode preferredLightMode) {
this.preferredLightMode = preferredLightMode; this.preferredLightMode = preferredLightMode;
} }
/**
* returns the preferred light mode.
* @return the light mode.
*/
public TechniqueDef.LightMode getPreferredLightMode() { public TechniqueDef.LightMode getPreferredLightMode() {
return preferredLightMode; return preferredLightMode;
} }
/**
* returns the number of lights used for each pass when the light mode is single pass.
* @return the number of lights.
*/
public int getSinglePassLightBatchSize() { public int getSinglePassLightBatchSize() {
return singlePassLightBatchSize; return singlePassLightBatchSize;
} }
/**
* Sets the number of lights to use for each pass when the light mode is single pass.
* @param singlePassLightBatchSize the number of lights.
*/
public void setSinglePassLightBatchSize(int singlePassLightBatchSize) { public void setSinglePassLightBatchSize(int singlePassLightBatchSize) {
this.singlePassLightBatchSize = singlePassLightBatchSize; this.singlePassLightBatchSize = singlePassLightBatchSize;
} }

@ -161,6 +161,7 @@ public interface GL {
public static final int GL_TEXTURE_CUBE_MAP_NEGATIVE_Y = 0x8518; public static final int GL_TEXTURE_CUBE_MAP_NEGATIVE_Y = 0x8518;
public static final int GL_TEXTURE_CUBE_MAP_POSITIVE_Z = 0x8519; public static final int GL_TEXTURE_CUBE_MAP_POSITIVE_Z = 0x8519;
public static final int GL_TEXTURE_CUBE_MAP_NEGATIVE_Z = 0x851A; public static final int GL_TEXTURE_CUBE_MAP_NEGATIVE_Z = 0x851A;
public static final int GL_TEXTURE_BASE_LEVEL = 0x813C;
public static final int GL_TEXTURE_MAG_FILTER = 0x2800; public static final int GL_TEXTURE_MAG_FILTER = 0x2800;
public static final int GL_TEXTURE_MAX_LEVEL = 0x813D; public static final int GL_TEXTURE_MAX_LEVEL = 0x813D;
public static final int GL_TEXTURE_MIN_FILTER = 0x2801; public static final int GL_TEXTURE_MIN_FILTER = 0x2801;

@ -44,7 +44,7 @@ public interface GL2 extends GL {
public static final int GL_ALPHA_TEST = 0xBC0; public static final int GL_ALPHA_TEST = 0xBC0;
public static final int GL_BGR = 0x80E0; public static final int GL_BGR = 0x80E0;
public static final int GL_BGRA = 0x80E1; public static final int GL_BGRA = 0x80E1;
public static final int GL_COMPARE_R_TO_TEXTURE = 0x884E; public static final int GL_COMPARE_REF_TO_TEXTURE = 0x884E;
public static final int GL_DEPTH_COMPONENT24 = 0x81A6; public static final int GL_DEPTH_COMPONENT24 = 0x81A6;
public static final int GL_DEPTH_COMPONENT32 = 0x81A7; public static final int GL_DEPTH_COMPONENT32 = 0x81A7;
public static final int GL_DEPTH_TEXTURE_MODE = 0x884B; public static final int GL_DEPTH_TEXTURE_MODE = 0x884B;

@ -68,6 +68,7 @@ public interface GLExt {
public static final int GL_MAX_DEPTH_TEXTURE_SAMPLES = 0x910F; public static final int GL_MAX_DEPTH_TEXTURE_SAMPLES = 0x910F;
public static final int GL_MAX_DRAW_BUFFERS_ARB = 0x8824; public static final int GL_MAX_DRAW_BUFFERS_ARB = 0x8824;
public static final int GL_MAX_SAMPLES_EXT = 0x8D57; public static final int GL_MAX_SAMPLES_EXT = 0x8D57;
public static final int GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT = 0x84FF;
public static final int GL_MULTISAMPLE_ARB = 0x809D; public static final int GL_MULTISAMPLE_ARB = 0x809D;
public static final int GL_NUM_PROGRAM_BINARY_FORMATS = 0x87FE; public static final int GL_NUM_PROGRAM_BINARY_FORMATS = 0x87FE;
public static final int GL_PIXEL_PACK_BUFFER_ARB = 0x88EB; public static final int GL_PIXEL_PACK_BUFFER_ARB = 0x88EB;

@ -51,7 +51,9 @@ import com.jme3.texture.FrameBuffer;
import com.jme3.texture.FrameBuffer.RenderBuffer; import com.jme3.texture.FrameBuffer.RenderBuffer;
import com.jme3.texture.Image; import com.jme3.texture.Image;
import com.jme3.texture.Texture; import com.jme3.texture.Texture;
import com.jme3.texture.Texture.ShadowCompareMode;
import com.jme3.texture.Texture.WrapAxis; import com.jme3.texture.Texture.WrapAxis;
import com.jme3.texture.image.LastTextureState;
import com.jme3.util.BufferUtils; import com.jme3.util.BufferUtils;
import com.jme3.util.ListMap; import com.jme3.util.ListMap;
import com.jme3.util.MipMapGenerator; import com.jme3.util.MipMapGenerator;
@ -68,7 +70,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import jme3tools.shader.ShaderDebug; import jme3tools.shader.ShaderDebug;
public class GLRenderer implements Renderer { public final class GLRenderer implements Renderer {
private static final Logger logger = Logger.getLogger(GLRenderer.class.getName()); private static final Logger logger = Logger.getLogger(GLRenderer.class.getName());
private static final boolean VALIDATE_SHADER = false; private static final boolean VALIDATE_SHADER = false;
@ -374,15 +376,18 @@ public class GLRenderer implements Renderer {
if (hasExtension("GL_EXT_texture_filter_anisotropic")) { if (hasExtension("GL_EXT_texture_filter_anisotropic")) {
caps.add(Caps.TextureFilterAnisotropic); caps.add(Caps.TextureFilterAnisotropic);
limits.put(Limits.TextureAnisotropy, getInteger(GLExt.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT));
} }
if (hasExtension("GL_EXT_framebuffer_object") || gl3 != null) { if (hasExtension("GL_EXT_framebuffer_object")
|| caps.contains(Caps.OpenGL30)
|| caps.contains(Caps.OpenGLES20)) {
caps.add(Caps.FrameBuffer); caps.add(Caps.FrameBuffer);
limits.put(Limits.RenderBufferSize, getInteger(GLFbo.GL_MAX_RENDERBUFFER_SIZE_EXT)); limits.put(Limits.RenderBufferSize, getInteger(GLFbo.GL_MAX_RENDERBUFFER_SIZE_EXT));
limits.put(Limits.FrameBufferAttachments, getInteger(GLFbo.GL_MAX_COLOR_ATTACHMENTS_EXT)); limits.put(Limits.FrameBufferAttachments, getInteger(GLFbo.GL_MAX_COLOR_ATTACHMENTS_EXT));
if (hasExtension("GL_EXT_framebuffer_blit")) { if (hasExtension("GL_EXT_framebuffer_blit") || caps.contains(Caps.OpenGL30)) {
caps.add(Caps.FrameBufferBlit); caps.add(Caps.FrameBufferBlit);
} }
@ -401,7 +406,7 @@ public class GLRenderer implements Renderer {
} }
} }
if (hasExtension("GL_ARB_draw_buffers")) { if (hasExtension("GL_ARB_draw_buffers") || caps.contains(Caps.OpenGL30)) {
limits.put(Limits.FrameBufferMrtAttachments, getInteger(GLExt.GL_MAX_DRAW_BUFFERS_ARB)); limits.put(Limits.FrameBufferMrtAttachments, getInteger(GLExt.GL_MAX_DRAW_BUFFERS_ARB));
if (limits.get(Limits.FrameBufferMrtAttachments) > 1) { if (limits.get(Limits.FrameBufferMrtAttachments) > 1) {
caps.add(Caps.FrameBufferMRT); caps.add(Caps.FrameBufferMRT);
@ -502,6 +507,11 @@ public class GLRenderer implements Renderer {
// Initialize default state.. // Initialize default state..
gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1); gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1);
if (caps.contains(Caps.SeamlessCubemap)) {
// Enable this globally. Should be OK.
gl.glEnable(GLExt.GL_TEXTURE_CUBE_MAP_SEAMLESS);
}
if (caps.contains(Caps.CoreProfile)) { if (caps.contains(Caps.CoreProfile)) {
// Core Profile requires VAO to be bound. // Core Profile requires VAO to be bound.
gl3.glGenVertexArrays(intBuf16); gl3.glGenVertexArrays(intBuf16);
@ -607,13 +617,12 @@ public class GLRenderer implements Renderer {
if (state.isDepthTest() && !context.depthTestEnabled) { if (state.isDepthTest() && !context.depthTestEnabled) {
gl.glEnable(GL.GL_DEPTH_TEST); gl.glEnable(GL.GL_DEPTH_TEST);
gl.glDepthFunc(convertTestFunction(context.depthFunc));
context.depthTestEnabled = true; context.depthTestEnabled = true;
} else if (!state.isDepthTest() && context.depthTestEnabled) { } else if (!state.isDepthTest() && context.depthTestEnabled) {
gl.glDisable(GL.GL_DEPTH_TEST); gl.glDisable(GL.GL_DEPTH_TEST);
context.depthTestEnabled = false; context.depthTestEnabled = false;
} }
if (state.getDepthFunc() != context.depthFunc) { if (state.isDepthTest() && state.getDepthFunc() != context.depthFunc) {
gl.glDepthFunc(convertTestFunction(state.getDepthFunc())); gl.glDepthFunc(convertTestFunction(state.getDepthFunc()));
context.depthFunc = state.getDepthFunc(); context.depthFunc = state.getDepthFunc();
} }
@ -1064,6 +1073,9 @@ public class GLRenderer implements Renderer {
stringBuf.append("\n"); stringBuf.append("\n");
} else { } else {
if (gles2) { if (gles2) {
// request GLSL ES (1.00) when compiling under GLES2.
stringBuf.append("#version 100\n");
if (source.getType() == ShaderType.Fragment) { if (source.getType() == ShaderType.Fragment) {
// GLES2 requires precision qualifier. // GLES2 requires precision qualifier.
stringBuf.append("precision mediump float;\n"); stringBuf.append("precision mediump float;\n");
@ -1080,6 +1092,7 @@ public class GLRenderer implements Renderer {
if (linearizeSrgbImages) { if (linearizeSrgbImages) {
stringBuf.append("#define SRGB 1\n"); stringBuf.append("#define SRGB 1\n");
} }
stringBuf.append("#define ").append(source.getType().name().toUpperCase()).append("_SHADER 1\n");
stringBuf.append(source.getDefines()); stringBuf.append(source.getDefines());
stringBuf.append(source.getSource()); stringBuf.append(source.getSource());
@ -1422,7 +1435,7 @@ public class GLRenderer implements Renderer {
// NOTE: For depth textures, sets nearest/no-mips mode // NOTE: For depth textures, sets nearest/no-mips mode
// Required to fix "framebuffer unsupported" // Required to fix "framebuffer unsupported"
// for old NVIDIA drivers! // for old NVIDIA drivers!
setupTextureParams(tex); setupTextureParams(0, tex);
} }
glfbo.glFramebufferTexture2DEXT(GLFbo.GL_FRAMEBUFFER_EXT, glfbo.glFramebufferTexture2DEXT(GLFbo.GL_FRAMEBUFFER_EXT,
@ -1450,24 +1463,43 @@ public class GLRenderer implements Renderer {
} }
} }
private void bindFrameBuffer(FrameBuffer fb) {
if (fb == null) {
if (context.boundFBO != 0) {
glfbo.glBindFramebufferEXT(GLFbo.GL_FRAMEBUFFER_EXT, 0);
statistics.onFrameBufferUse(null, true);
context.boundFBO = 0;
context.boundFB = null;
}
} else {
assert fb.getId() != -1 && fb.getId() != 0;
if (context.boundFBO != fb.getId()) {
glfbo.glBindFramebufferEXT(GLFbo.GL_FRAMEBUFFER_EXT, fb.getId());
context.boundFBO = fb.getId();
context.boundFB = fb;
statistics.onFrameBufferUse(fb, true);
} else {
statistics.onFrameBufferUse(fb, false);
}
}
}
public void updateFrameBuffer(FrameBuffer fb) { public void updateFrameBuffer(FrameBuffer fb) {
if (fb.getNumColorBuffers() == 0 && fb.getDepthBuffer() == null) {
throw new IllegalArgumentException("The framebuffer: " + fb
+ "\nDoesn't have any color/depth buffers");
}
int id = fb.getId(); int id = fb.getId();
if (id == -1) { if (id == -1) {
// create FBO
glfbo.glGenFramebuffersEXT(intBuf1); glfbo.glGenFramebuffersEXT(intBuf1);
id = intBuf1.get(0); id = intBuf1.get(0);
fb.setId(id); fb.setId(id);
objManager.registerObject(fb); objManager.registerObject(fb);
statistics.onNewFrameBuffer(); statistics.onNewFrameBuffer();
} }
if (context.boundFBO != id) { bindFrameBuffer(fb);
glfbo.glBindFramebufferEXT(GLFbo.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(); FrameBuffer.RenderBuffer depthBuf = fb.getDepthBuffer();
if (depthBuf != null) { if (depthBuf != null) {
@ -1479,6 +1511,7 @@ public class GLRenderer implements Renderer {
updateFrameBufferAttachment(fb, colorBuf); updateFrameBufferAttachment(fb, colorBuf);
} }
setReadDrawBuffers(fb);
checkFrameBufferError(); checkFrameBufferError();
fb.clearUpdateNeeded(); fb.clearUpdateNeeded();
@ -1506,93 +1539,45 @@ public class GLRenderer implements Renderer {
} }
public void setMainFrameBufferOverride(FrameBuffer fb) { public void setMainFrameBufferOverride(FrameBuffer fb) {
mainFbOverride = fb; mainFbOverride = null;
if (context.boundFBO == 0) {
// Main FB is now set to fb, make sure its bound
setFrameBuffer(fb);
} }
mainFbOverride = fb;
public void setFrameBuffer(FrameBuffer fb) {
if (fb == null && mainFbOverride != null) {
fb = mainFbOverride;
} }
if (context.boundFB == fb) { public void setReadDrawBuffers(FrameBuffer fb) {
if (fb == null || !fb.isUpdateNeeded()) { if (gl2 == null) {
return; return;
} }
}
if (!caps.contains(Caps.FrameBuffer)) { final int NONE = -2;
throw new RendererException("Framebuffer objects are not supported" final int INITIAL = -1;
+ " by the video hardware"); final int MRT_OFF = 100;
}
// generate mipmaps for last FB if needed
if (context.boundFB != null) {
for (int i = 0; i < context.boundFB.getNumColorBuffers(); i++) {
RenderBuffer rb = context.boundFB.getColorBuffer(i);
Texture tex = rb.getTexture();
if (tex != null
&& tex.getMinFilter().usesMipMapLevels()) {
setTexture(0, rb.getTexture());
int textureType = convertTextureType(tex.getType(), tex.getImage().getMultiSamples(), rb.getFace());
glfbo.glGenerateMipmapEXT(textureType);
}
}
}
if (fb == null) { if (fb == null) {
// unbind any fbos // Set Read/Draw buffers to initial value.
if (context.boundFBO != 0) { if (context.boundDrawBuf != INITIAL) {
glfbo.glBindFramebufferEXT(GLFbo.GL_FRAMEBUFFER_EXT, 0);
statistics.onFrameBufferUse(null, true);
context.boundFBO = 0;
}
// select back buffer
if (gl2 != null) {
if (context.boundDrawBuf != -1) {
gl2.glDrawBuffer(context.initialDrawBuf); gl2.glDrawBuffer(context.initialDrawBuf);
context.boundDrawBuf = -1; context.boundDrawBuf = INITIAL;
} }
if (context.boundReadBuf != -1) { if (context.boundReadBuf != INITIAL) {
gl2.glReadBuffer(context.initialReadBuf); gl2.glReadBuffer(context.initialReadBuf);
context.boundReadBuf = -1; context.boundReadBuf = INITIAL;
}
}
context.boundFB = null;
} else {
if (fb.getNumColorBuffers() == 0 && fb.getDepthBuffer() == null) {
throw new IllegalArgumentException("The framebuffer: " + fb
+ "\nDoesn't have any color/depth buffers");
}
if (fb.isUpdateNeeded()) {
updateFrameBuffer(fb);
} }
// update viewport to reflect framebuffer's resolution
setViewPort(0, 0, fb.getWidth(), fb.getHeight());
if (context.boundFBO != fb.getId()) {
glfbo.glBindFramebufferEXT(GLFbo.GL_FRAMEBUFFER_EXT, fb.getId());
statistics.onFrameBufferUse(fb, true);
context.boundFBO = fb.getId();
} else { } else {
statistics.onFrameBufferUse(fb, false);
}
if (fb.getNumColorBuffers() == 0) { if (fb.getNumColorBuffers() == 0) {
// make sure to select NONE as draw buf // make sure to select NONE as draw buf
// no color buffer attached. select NONE // no color buffer attached.
if (gl2 != null) { if (gl2 != null) {
if (context.boundDrawBuf != -2) { if (context.boundDrawBuf != NONE) {
gl2.glDrawBuffer(GL.GL_NONE); gl2.glDrawBuffer(GL.GL_NONE);
context.boundDrawBuf = -2; context.boundDrawBuf = NONE;
} }
if (context.boundReadBuf != -2) { if (context.boundReadBuf != NONE) {
gl2.glReadBuffer(GL.GL_NONE); gl2.glReadBuffer(GL.GL_NONE);
context.boundReadBuf = -2; context.boundReadBuf = NONE;
} }
} }
} else { } else {
@ -1612,7 +1597,7 @@ public class GLRenderer implements Renderer {
+ " by the video hardware!"); + " by the video hardware!");
} }
if (context.boundDrawBuf != 100 + fb.getNumColorBuffers()) { if (context.boundDrawBuf != MRT_OFF + fb.getNumColorBuffers()) {
intBuf16.clear(); intBuf16.clear();
for (int i = 0; i < fb.getNumColorBuffers(); i++) { for (int i = 0; i < fb.getNumColorBuffers(); i++) {
intBuf16.put(GLFbo.GL_COLOR_ATTACHMENT0_EXT + i); intBuf16.put(GLFbo.GL_COLOR_ATTACHMENT0_EXT + i);
@ -1620,7 +1605,7 @@ public class GLRenderer implements Renderer {
intBuf16.flip(); intBuf16.flip();
glext.glDrawBuffers(intBuf16); glext.glDrawBuffers(intBuf16);
context.boundDrawBuf = 100 + fb.getNumColorBuffers(); context.boundDrawBuf = MRT_OFF + fb.getNumColorBuffers();
} }
} else { } else {
RenderBuffer rb = fb.getColorBuffer(fb.getTargetIndex()); RenderBuffer rb = fb.getColorBuffer(fb.getTargetIndex());
@ -1633,8 +1618,56 @@ public class GLRenderer implements Renderer {
} }
} }
} }
}
}
public void setFrameBuffer(FrameBuffer fb) {
if (fb == null && mainFbOverride != null) {
fb = mainFbOverride;
}
if (context.boundFB == fb) {
if (fb == null || !fb.isUpdateNeeded()) {
return;
}
}
if (!caps.contains(Caps.FrameBuffer)) {
throw new RendererException("Framebuffer objects are not supported"
+ " by the video hardware");
}
assert fb.getId() >= 0; // generate mipmaps for last FB if needed
if (context.boundFB != null) {
for (int i = 0; i < context.boundFB.getNumColorBuffers(); i++) {
RenderBuffer rb = context.boundFB.getColorBuffer(i);
Texture tex = rb.getTexture();
if (tex != null
&& tex.getMinFilter().usesMipMapLevels()) {
setTexture(0, rb.getTexture());
int textureType = convertTextureType(tex.getType(), tex.getImage().getMultiSamples(), rb.getFace());
glfbo.glGenerateMipmapEXT(textureType);
}
}
}
if (fb == null) {
bindFrameBuffer(null);
setReadDrawBuffers(null);
} else {
if (fb.isUpdateNeeded()) {
updateFrameBuffer(fb);
} else {
bindFrameBuffer(fb);
setReadDrawBuffers(fb);
}
// update viewport to reflect framebuffer's resolution
setViewPort(0, 0, fb.getWidth(), fb.getHeight());
assert fb.getId() > 0;
assert context.boundFBO == fb.getId(); assert context.boundFBO == fb.getId();
context.boundFB = fb; context.boundFB = fb;
@ -1806,87 +1839,80 @@ public class GLRenderer implements Renderer {
} }
@SuppressWarnings("fallthrough") @SuppressWarnings("fallthrough")
private void setupTextureParams(Texture tex) { private void setupTextureParams(int unit, Texture tex) {
Image image = tex.getImage(); Image image = tex.getImage();
int target = convertTextureType(tex.getType(), image != null ? image.getMultiSamples() : 1, -1); int target = convertTextureType(tex.getType(), image != null ? image.getMultiSamples() : 1, -1);
boolean haveMips = true; boolean haveMips = true;
if (image != null) { if (image != null) {
haveMips = image.isGeneratedMipmapsRequired() || image.hasMipmaps(); haveMips = image.isGeneratedMipmapsRequired() || image.hasMipmaps();
} }
// filter things LastTextureState curState = image.getLastTextureState();
if (image.getLastTextureState().magFilter != tex.getMagFilter()) {
int magFilter = convertMagFilter(tex.getMagFilter()); if (curState.magFilter != tex.getMagFilter()) {
gl.glTexParameteri(target, GL.GL_TEXTURE_MAG_FILTER, magFilter); bindTextureAndUnit(target, image, unit);
image.getLastTextureState().magFilter = tex.getMagFilter(); gl.glTexParameteri(target, GL.GL_TEXTURE_MAG_FILTER, convertMagFilter(tex.getMagFilter()));
} curState.magFilter = tex.getMagFilter();
if (image.getLastTextureState().minFilter != tex.getMinFilter()) {
int minFilter = convertMinFilter(tex.getMinFilter(), haveMips);
gl.glTexParameteri(target, GL.GL_TEXTURE_MIN_FILTER, minFilter);
image.getLastTextureState().minFilter = tex.getMinFilter();
}
if (caps.contains(Caps.SeamlessCubemap) && tex.getType() == Texture.Type.CubeMap) {
if (haveMips && !context.seamlessCubemap) {
// We can enable seamless cubemap filtering.
gl.glEnable(GLExt.GL_TEXTURE_CUBE_MAP_SEAMLESS);
context.seamlessCubemap = true;
} else if (!haveMips && context.seamlessCubemap) {
// For skyboxes (no mipmaps), disable seamless cubemap filtering.
gl.glDisable(GLExt.GL_TEXTURE_CUBE_MAP_SEAMLESS);
context.seamlessCubemap = false;
} }
if (curState.minFilter != tex.getMinFilter()) {
bindTextureAndUnit(target, image, unit);
gl.glTexParameteri(target, GL.GL_TEXTURE_MIN_FILTER, convertMinFilter(tex.getMinFilter(), haveMips));
curState.minFilter = tex.getMinFilter();
} }
if (caps.contains(Caps.TextureFilterAnisotropic)
if (tex.getAnisotropicFilter() > 1) { && curState.anisoFilter != tex.getAnisotropicFilter()) {
if (caps.contains(Caps.TextureFilterAnisotropic)) { bindTextureAndUnit(target, image, unit);
gl.glTexParameterf(target, gl.glTexParameterf(target,
GLExt.GL_TEXTURE_MAX_ANISOTROPY_EXT, GLExt.GL_TEXTURE_MAX_ANISOTROPY_EXT,
tex.getAnisotropicFilter()); tex.getAnisotropicFilter());
} curState.anisoFilter = tex.getAnisotropicFilter();
} }
// repeat modes
switch (tex.getType()) { switch (tex.getType()) {
case ThreeDimensional: case ThreeDimensional:
case CubeMap: // cubemaps use 3D coords case CubeMap: // cubemaps use 3D coords
if (gl2 != null && image.getLastTextureState().rWrap != tex.getWrap(WrapAxis.R)) { if (gl2 != null && curState.rWrap != tex.getWrap(WrapAxis.R)) {
bindTextureAndUnit(target, image, unit);
gl2.glTexParameteri(target, GL2.GL_TEXTURE_WRAP_R, convertWrapMode(tex.getWrap(WrapAxis.R))); gl2.glTexParameteri(target, GL2.GL_TEXTURE_WRAP_R, convertWrapMode(tex.getWrap(WrapAxis.R)));
image.getLastTextureState().rWrap = tex.getWrap(WrapAxis.R); curState.rWrap = tex.getWrap(WrapAxis.R);
} }
//There is no break statement on purpose here //There is no break statement on purpose here
case TwoDimensional: case TwoDimensional:
case TwoDimensionalArray: case TwoDimensionalArray:
if (image.getLastTextureState().tWrap != tex.getWrap(WrapAxis.T)) { if (curState.tWrap != tex.getWrap(WrapAxis.T)) {
bindTextureAndUnit(target, image, unit);
gl.glTexParameteri(target, GL.GL_TEXTURE_WRAP_T, convertWrapMode(tex.getWrap(WrapAxis.T))); gl.glTexParameteri(target, GL.GL_TEXTURE_WRAP_T, convertWrapMode(tex.getWrap(WrapAxis.T)));
image.getLastTextureState().tWrap = tex.getWrap(WrapAxis.T); image.getLastTextureState().tWrap = tex.getWrap(WrapAxis.T);
} }
if (image.getLastTextureState().sWrap != tex.getWrap(WrapAxis.S)) { if (curState.sWrap != tex.getWrap(WrapAxis.S)) {
bindTextureAndUnit(target, image, unit);
gl.glTexParameteri(target, GL.GL_TEXTURE_WRAP_S, convertWrapMode(tex.getWrap(WrapAxis.S))); gl.glTexParameteri(target, GL.GL_TEXTURE_WRAP_S, convertWrapMode(tex.getWrap(WrapAxis.S)));
image.getLastTextureState().sWrap = tex.getWrap(WrapAxis.S); curState.sWrap = tex.getWrap(WrapAxis.S);
} }
break; break;
default: default:
throw new UnsupportedOperationException("Unknown texture type: " + tex.getType()); throw new UnsupportedOperationException("Unknown texture type: " + tex.getType());
} }
if(tex.isNeedCompareModeUpdate() && gl2 != null){ ShadowCompareMode texCompareMode = tex.getShadowCompareMode();
// R to Texture compare mode if (gl2 != null && curState.shadowCompareMode != texCompareMode) {
if (tex.getShadowCompareMode() != Texture.ShadowCompareMode.Off) { bindTextureAndUnit(target, image, unit);
gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_MODE, GL2.GL_COMPARE_R_TO_TEXTURE); if (texCompareMode != ShadowCompareMode.Off) {
gl2.glTexParameteri(target, GL2.GL_DEPTH_TEXTURE_MODE, GL2.GL_INTENSITY); gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_MODE, GL2.GL_COMPARE_REF_TO_TEXTURE);
if (tex.getShadowCompareMode() == Texture.ShadowCompareMode.GreaterOrEqual) { if (texCompareMode == ShadowCompareMode.GreaterOrEqual) {
gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_FUNC, GL.GL_GEQUAL); gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_FUNC, GL.GL_GEQUAL);
} else { } else {
gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_FUNC, GL.GL_LEQUAL); gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_FUNC, GL.GL_LEQUAL);
} }
}else{ } else {
//restoring default value
gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_MODE, GL.GL_NONE); gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_MODE, GL.GL_NONE);
} }
tex.compareModeUpdated(); curState.shadowCompareMode = texCompareMode;
} }
// If at this point we didn't bind the texture, bind it now
bindTextureOnly(target, image, unit);
} }
/** /**
@ -1944,6 +1970,50 @@ public class GLRenderer implements Renderer {
} }
} }
/**
* Ensures that the texture is bound to the given unit
* and that the unit is currently active (for modification).
*
* @param target The texture target, one of GL_TEXTURE_***
* @param img The image texture to bind
* @param unit At what unit to bind the texture.
*/
private void bindTextureAndUnit(int target, Image img, int unit) {
if (context.boundTextureUnit != unit) {
gl.glActiveTexture(GL.GL_TEXTURE0 + unit);
context.boundTextureUnit = unit;
}
if (context.boundTextures[unit] != img) {
gl.glBindTexture(target, img.getId());
context.boundTextures[unit] = img;
statistics.onTextureUse(img, true);
} else {
statistics.onTextureUse(img, false);
}
}
/**
* Ensures that the texture is bound to the given unit,
* but does not care if the unit is active (for rendering).
*
* @param target The texture target, one of GL_TEXTURE_***
* @param img The image texture to bind
* @param unit At what unit to bind the texture.
*/
private void bindTextureOnly(int target, Image img, int unit) {
if (context.boundTextures[unit] != img) {
if (context.boundTextureUnit != unit) {
gl.glActiveTexture(GL.GL_TEXTURE0 + unit);
context.boundTextureUnit = unit;
}
gl.glBindTexture(target, img.getId());
context.boundTextures[unit] = img;
statistics.onTextureUse(img, true);
} else {
statistics.onTextureUse(img, false);
}
}
/** /**
* Uploads the given image to the GL driver. * Uploads the given image to the GL driver.
* *
@ -1967,17 +2037,7 @@ public class GLRenderer implements Renderer {
// bind texture // bind texture
int target = convertTextureType(type, img.getMultiSamples(), -1); int target = convertTextureType(type, img.getMultiSamples(), -1);
if (context.boundTextures[unit] != img) { bindTextureAndUnit(target, img, unit);
if (context.boundTextureUnit != unit) {
gl.glActiveTexture(GL.GL_TEXTURE0 + unit);
context.boundTextureUnit = unit;
}
gl.glBindTexture(target, texId);
context.boundTextures[unit] = img;
statistics.onTextureUse(img, true);
}
if (!img.hasMipmaps() && img.isGeneratedMipmapsRequired()) { if (!img.hasMipmaps() && img.isGeneratedMipmapsRequired()) {
// Image does not have mipmaps, but they are required. // Image does not have mipmaps, but they are required.
@ -2086,6 +2146,7 @@ public class GLRenderer implements Renderer {
img.clearUpdateNeeded(); img.clearUpdateNeeded();
} }
@Override
public void setTexture(int unit, Texture tex) { public void setTexture(int unit, Texture tex) {
Image image = tex.getImage(); Image image = tex.getImage();
if (image.isUpdateNeeded() || (image.isGeneratedMipmapsRequired() && !image.isMipmapsGenerated())) { if (image.isUpdateNeeded() || (image.isGeneratedMipmapsRequired() && !image.isMipmapsGenerated())) {
@ -2112,24 +2173,7 @@ public class GLRenderer implements Renderer {
int texId = image.getId(); int texId = image.getId();
assert texId != -1; assert texId != -1;
Image[] textures = context.boundTextures; setupTextureParams(unit, tex);
int type = convertTextureType(tex.getType(), image.getMultiSamples(), -1);
if (textures[unit] != image) {
if (context.boundTextureUnit != unit) {
gl.glActiveTexture(GL.GL_TEXTURE0 + unit);
context.boundTextureUnit = unit;
}
gl.glBindTexture(type, texId);
textures[unit] = image;
statistics.onTextureUse(image, true);
} else {
statistics.onTextureUse(image, false);
}
setupTextureParams(tex);
} }
public void modifyTexture(Texture tex, Image pixels, int x, int y) { public void modifyTexture(Texture tex, Image pixels, int x, int y) {
@ -2623,12 +2667,13 @@ public class GLRenderer implements Renderer {
} }
} }
clearVertexAttribs();
if (indices != null) { if (indices != null) {
drawTriangleList(indices, mesh, count); drawTriangleList(indices, mesh, count);
} else { } else {
drawTriangleArray(mesh.getMode(), count, mesh.getVertexCount()); drawTriangleArray(mesh.getMode(), count, mesh.getVertexCount());
} }
clearVertexAttribs();
} }
public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) { public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) {

@ -36,8 +36,14 @@ import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Proxy; import java.lang.reflect.Proxy;
import java.nio.Buffer;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer; import java.nio.IntBuffer;
import java.nio.LongBuffer;
import java.nio.ShortBuffer;
import java.util.HashMap; import java.util.HashMap;
/** /**
@ -51,6 +57,17 @@ public final class GLTracer implements InvocationHandler {
private final IntMap<String> constMap; private final IntMap<String> constMap;
private static final HashMap<String, IntMap<Void>> nonEnumArgMap = new HashMap<String, IntMap<Void>>(); private static final HashMap<String, IntMap<Void>> nonEnumArgMap = new HashMap<String, IntMap<Void>>();
private static final String ANSI_RESET = "\u001B[0m";
private static final String ANSI_BRIGHT = "\u001B[1m";
private static final String ANSI_BLACK = "\u001B[30m";
private static final String ANSI_RED = "\u001B[31m";
private static final String ANSI_GREEN = "\u001B[32m";
private static final String ANSI_YELLOW = "\u001B[33m";
private static final String ANSI_BLUE = "\u001B[34m";
private static final String ANSI_MAGENTA = "\u001B[35m";
private static final String ANSI_CYAN = "\u001B[36m";
private static final String ANSI_WHITE = "\u001B[37m";
private static void noEnumArgs(String method, int... argSlots) { private static void noEnumArgs(String method, int... argSlots) {
IntMap<Void> argSlotsMap = new IntMap<Void>(); IntMap<Void> argSlotsMap = new IntMap<Void>();
for (int argSlot : argSlots) { for (int argSlot : argSlots) {
@ -174,100 +191,305 @@ public final class GLTracer implements InvocationHandler {
new GLTracer(glInterface, constMap)); new GLTracer(glInterface, constMap));
} }
private String translateInteger(String method, int value, int argIndex) { private void printStyle(String style, String string) {
IntMap<Void> argSlotMap = nonEnumArgMap.get(method); System.out.print(style + string + ANSI_RESET);
if (argSlotMap != null && argSlotMap.containsKey(argIndex)) { }
return Integer.toString(value);
private void print(String string) {
System.out.print(string);
}
private void printInt(int value) {
print(Integer.toString(value));
} }
private void printEnum(int value) {
String enumName = constMap.get(value); String enumName = constMap.get(value);
if (enumName != null) { if (enumName != null) {
return enumName; if (enumName.startsWith("GL_")) {
enumName = enumName.substring(3);
}
if (enumName.endsWith("_EXT") || enumName.endsWith("_ARB")) {
enumName = enumName.substring(0, enumName.length() - 4);
}
printStyle(ANSI_GREEN, enumName);
} else { } else {
return "GL_ENUM_" + Integer.toHexString(value); printStyle(ANSI_GREEN, "ENUM_" + Integer.toHexString(value));
//throw new IllegalStateException("Untranslatable enum encountered on " + method +
// " at argument " + argIndex + " with value " + value);
} }
} }
private String translateString(String value) { private void printIntOrEnum(String method, int value, int argIndex) {
return "\"" + value.replaceAll("\0", "\\\\0") + "\""; IntMap<Void> argSlotMap = nonEnumArgMap.get(method);
if (argSlotMap != null && argSlotMap.containsKey(argIndex)) {
printInt(value);
} else {
printEnum(value);
}
} }
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { private void printNewLine() {
Object result = method.invoke(obj, args); System.out.println();
String methodName = method.getName(); }
private void printString(String value) {
if (value.length() > 150) {
value = value.substring(0, 150) + "...";
}
StringBuilder sb = new StringBuilder();
sb.append(ANSI_YELLOW);
sb.append("\"");
sb.append(ANSI_RESET);
for (String line : value.split("\n")) {
sb.append(ANSI_YELLOW);
sb.append(line.replaceAll("\0", "\\\\0"));
sb.append(ANSI_RESET);
sb.append("\n");
}
if (sb.length() > 1 && sb.charAt(sb.length() - 1) == '\n') {
sb.setLength(sb.length() - 1);
}
sb.append(ANSI_YELLOW);
sb.append("\"");
sb.append(ANSI_RESET);
print(sb.toString());
}
private void printBoolean(boolean bool) {
printStyle(ANSI_BLUE, bool ? "true" : "false");
}
private void printBuffer(Buffer buffer) {
StringBuilder sb = new StringBuilder();
sb.append(ANSI_MAGENTA);
if (buffer instanceof ByteBuffer) {
sb.append("byte");
} else if (buffer instanceof ShortBuffer) {
sb.append("short");
} else if (buffer instanceof CharBuffer) {
sb.append("char");
} else if (buffer instanceof FloatBuffer) {
sb.append("float");
} else if (buffer instanceof IntBuffer) {
sb.append("int");
} else if (buffer instanceof LongBuffer) {
sb.append("long");
} else if (buffer instanceof DoubleBuffer) {
sb.append("double");
} else {
throw new UnsupportedOperationException();
}
sb.append(ANSI_RESET);
sb.append("[");
if (buffer.position() == 0
&& buffer.limit() == buffer.capacity()) {
// Common case. Just print buffer size.
sb.append(buffer.capacity());
} else {
sb.append("pos=").append(buffer.position());
sb.append(" lim=").append(buffer.limit());
sb.append(" cap=").append(buffer.capacity());
}
sb.append("]");
print(sb.toString());
}
private void printMethodName(String methodName) {
if (methodName.startsWith("gl")) { if (methodName.startsWith("gl")) {
System.out.print(methodName); // GL calls which actually draw (as opposed to change state)
System.out.print("("); // will be printed in darker color
if (args != null) { methodName = methodName.substring(2);
Class<?>[] paramTypes = method.getParameterTypes(); if (methodName.equals("Clear")
|| methodName.equals("DrawRangeElements")) {
print(methodName);
} else {
if (methodName.endsWith("EXT")) {
methodName = methodName.substring(0, methodName.length() - 3);
}
printStyle(ANSI_BRIGHT, methodName);
}
} else if (methodName.equals("resetStats")) {
printStyle(ANSI_RED, "-- frame boundary --");
}
}
private void printArgsClear(int mask) {
boolean needAPipe = false;
print("(");
if ((mask & GL.GL_COLOR_BUFFER_BIT) != 0) {
printStyle(ANSI_GREEN, "COLOR_BUFFER_BIT");
needAPipe = true;
}
if ((mask & GL.GL_DEPTH_BUFFER_BIT) != 0) {
if (needAPipe) {
print(" | ");
}
printStyle(ANSI_GREEN, "DEPTH_BUFFER_BIT");
}
if ((mask & GL.GL_STENCIL_BUFFER_BIT) != 0) {
if (needAPipe) {
print(" | ");
}
printStyle(ANSI_GREEN, "STENCIL_BUFFER_BIT");
}
print(")");
}
private void printArgsGetInteger(Object[] args) {
print("(");
int param = (Integer)args[0];
IntBuffer ib = (IntBuffer) args[1];
printEnum(param);
print(", ");
printOut();
if (param == GL2.GL_DRAW_BUFFER || param == GL2.GL_READ_BUFFER) {
printEnum(ib.get(0));
} else {
printInt(ib.get(0));
}
print(")");
}
private void printArgsTexParameter(Object[] args) {
print("(");
int target = (Integer) args[0];
int param = (Integer) args[1];
int value = (Integer) args[2];
printEnum(target);
print(", ");
printEnum(param);
print(", ");
if (param == GL.GL_TEXTURE_BASE_LEVEL
|| param == GL.GL_TEXTURE_MAX_LEVEL) {
printInt(value);
} else {
printEnum(value);
}
print(")");
}
private void printOut() {
printStyle(ANSI_CYAN, "out=");
}
private void printResult(String methodName, Object result, Class<?> returnType) {
if (returnType != void.class) {
print(" = ");
if (result instanceof String) {
printString((String) result);
} else if (returnType == int.class) {
int val = (Integer) result;
printIntOrEnum(methodName, val, -1);
} else if (returnType == boolean.class) {
printBoolean((Boolean)result);
} else {
print(" = ???");
}
}
}
private void printNull() {
printStyle(ANSI_BLUE, "null");
}
private void printArgs(String methodName, Object[] args, Class<?>[] paramTypes) {
if (methodName.equals("glClear")) {
printArgsClear((Integer)args[0]);
return;
} else if (methodName.equals("glTexParameteri")) {
printArgsTexParameter(args);
return;
} else if (methodName.equals("glGetInteger")) {
printArgsGetInteger(args);
return;
}
if (args == null) {
print("()");
return;
}
print("(");
for (int i = 0; i < args.length; i++) { for (int i = 0; i < args.length; i++) {
if (paramTypes[i] == int.class) { if (paramTypes[i] == int.class) {
int val = (Integer)args[i]; int val = (Integer)args[i];
System.out.print(translateInteger(methodName, val, i)); printIntOrEnum(methodName, val, i);
} else if (paramTypes[i] == boolean.class) {
printBoolean((Boolean)args[i]);
} else if (paramTypes[i] == String.class) { } else if (paramTypes[i] == String.class) {
System.out.print(translateString((String)args[i])); printString((String)args[i]);
} else if (paramTypes[i] == String[].class) { } else if (paramTypes[i] == String[].class) {
String[] arr = (String[]) args[i]; String[] arr = (String[]) args[i];
if (arr.length == 1) { if (arr.length == 1) {
if (arr[0].length() > 150) { printString(arr[0]);
System.out.print("\"" + arr[0].substring(0, 150) + "...\"");
} else {
System.out.print("\"" + arr[0] + "\"");
}
} else { } else {
System.out.print("String[" + arr.length + "]"); print("string[" + arr.length + "]");
} }
} else if (args[i] instanceof IntBuffer) { } else if (args[i] instanceof IntBuffer) {
IntBuffer buf = (IntBuffer) args[i]; IntBuffer buf = (IntBuffer) args[i];
if (buf.capacity() == 16) { if (buf.capacity() == 16) {
int val = buf.get(0); int val = buf.get(0);
System.out.print("out=" + translateInteger(methodName, val, i)); printOut();
printIntOrEnum(methodName, val, i);
} else if (buf.capacity() == 1) { } else if (buf.capacity() == 1) {
System.out.print("out=" + buf.get(0)); printOut();
print(Integer.toString(buf.get(0)));
} else { } else {
System.out.print(args[i]); printBuffer(buf);
} }
} else if (args[i] instanceof ByteBuffer) { } else if (args[i] instanceof ByteBuffer) {
ByteBuffer bb = (ByteBuffer)args[i]; ByteBuffer bb = (ByteBuffer)args[i];
if (bb.capacity() == 250) { if (bb.capacity() == 250) {
if (bb.get(0) != 0) { printOut();
System.out.print("out=GL_TRUE"); printBoolean(bb.get(0) != 0);
} else {
System.out.print("out=GL_FALSE");
}
} else { } else {
System.out.print(args[i]); printBuffer(bb);
} }
} else if (args[i] instanceof Buffer) {
printBuffer((Buffer)args[i]);
} else if (args[i] != null) {
print(args[i].toString());
} else { } else {
System.out.print(args[i]); printNull();
} }
if (i != args.length - 1) { if (i != args.length - 1) {
System.out.print(", "); System.out.print(", ");
} }
} }
print(")");
} }
System.out.print(")"); @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
printMethodName(methodName);
if (method.getReturnType() != void.class) { if (methodName.startsWith("gl")) {
if (result instanceof String) { try {
System.out.println(" = " + translateString((String)result)); // Try to evaluate result first, so we can see output values.
} else if (method.getReturnType() == int.class) { Object result = method.invoke(obj, args);
int val = (Integer)result; printArgs(methodName, args, method.getParameterTypes());
System.out.println(" = " + translateInteger(methodName, val, -1)); printResult(methodName, result, method.getReturnType());
} else if (method.getReturnType() == boolean.class) { printNewLine();
boolean val = (Boolean)result; return result;
if (val) System.out.println(" = GL_TRUE"); } catch (Throwable ex) {
else System.out.println(" = GL_FALSE"); // Execution failed, print args anyway
} else { // but output values will be incorrect.
System.out.println(" = ???"); printArgs(methodName, args, method.getParameterTypes());
printNewLine();
System.out.println("\tException occurred!");
System.out.println(ex.toString());
throw ex;
} }
} else { } else {
System.out.println(); printNewLine();
return method.invoke(obj, args);
} }
} }
return result;
}
} }

@ -90,6 +90,8 @@ final class TextureUtil {
} }
public GLImageFormat getImageFormatWithError(Format fmt, boolean isSrgb) { public GLImageFormat getImageFormatWithError(Format fmt, boolean isSrgb) {
//if the passed format is one kind of depth there isno point in getting the srgb format;
isSrgb = isSrgb && fmt != Format.Depth && fmt != Format.Depth16 && fmt != Format.Depth24 && fmt != Format.Depth24Stencil8 && fmt != Format.Depth32 && fmt != Format.Depth32F;
GLImageFormat glFmt = getImageFormat(fmt, isSrgb); GLImageFormat glFmt = getImageFormat(fmt, isSrgb);
if (glFmt == null && isSrgb) { if (glFmt == null && isSrgb) {
glFmt = getImageFormat(fmt, false); glFmt = getImageFormat(fmt, false);

@ -119,19 +119,6 @@ public class BatchNode extends GeometryGroupNode {
setNeedsFullRebatch(true); setNeedsFullRebatch(true);
} }
@Override
public void updateGeometricState() {
if (!children.isEmpty()) {
for (Batch batch : batches.getArray()) {
if (batch.needMeshUpdate) {
batch.geometry.updateModelBound();
batch.geometry.updateWorldBound();
batch.needMeshUpdate = false;
}
}
}
super.updateGeometricState();
}
protected Matrix4f getTransformMatrix(Geometry g){ protected Matrix4f getTransformMatrix(Geometry g){
return g.cachedWorldMat; return g.cachedWorldMat;
@ -169,7 +156,7 @@ public class BatchNode extends GeometryGroupNode {
nvb.updateData(normBuf); nvb.updateData(normBuf);
batch.needMeshUpdate = true; batch.geometry.updateModelBound();
} }
} }
@ -234,7 +221,7 @@ public class BatchNode extends GeometryGroupNode {
batch.geometry.setMesh(m); batch.geometry.setMesh(m);
batch.geometry.getMesh().updateCounts(); batch.geometry.getMesh().updateCounts();
batch.geometry.getMesh().updateBound(); batch.geometry.updateModelBound();
batches.add(batch); batches.add(batch);
} }
if (batches.size() > 0) { if (batches.size() > 0) {
@ -457,6 +444,7 @@ public class BatchNode extends GeometryGroupNode {
int maxWeights = -1; int maxWeights = -1;
Mesh.Mode mode = null; Mesh.Mode mode = null;
float lineWidth = 1f;
for (Geometry geom : geometries) { for (Geometry geom : geometries) {
totalVerts += geom.getVertexCount(); totalVerts += geom.getVertexCount();
totalTris += geom.getTriangleCount(); totalTris += geom.getTriangleCount();
@ -465,6 +453,7 @@ public class BatchNode extends GeometryGroupNode {
maxVertCount = geom.getVertexCount(); maxVertCount = geom.getVertexCount();
} }
Mesh.Mode listMode; Mesh.Mode listMode;
float listLineWidth = 1f;
int components; int components;
switch (geom.getMesh().getMode()) { switch (geom.getMesh().getMode()) {
case Points: case Points:
@ -475,6 +464,7 @@ public class BatchNode extends GeometryGroupNode {
case LineStrip: case LineStrip:
case Lines: case Lines:
listMode = Mesh.Mode.Lines; listMode = Mesh.Mode.Lines;
listLineWidth = geom.getMesh().getLineWidth();
components = 2; components = 2;
break; break;
case TriangleFan: case TriangleFan:
@ -506,11 +496,19 @@ public class BatchNode extends GeometryGroupNode {
+ " primitive types: " + mode + " != " + listMode); + " primitive types: " + mode + " != " + listMode);
} }
mode = listMode; mode = listMode;
if (mode == Mesh.Mode.Lines) {
if (lineWidth != 1f && listLineWidth != lineWidth) {
throw new UnsupportedOperationException("When using Mesh Line mode, cannot combine meshes with different line width "
+ lineWidth + " != " + listLineWidth);
}
lineWidth = listLineWidth;
}
compsForBuf[VertexBuffer.Type.Index.ordinal()] = components; compsForBuf[VertexBuffer.Type.Index.ordinal()] = components;
} }
outMesh.setMaxNumWeights(maxWeights); outMesh.setMaxNumWeights(maxWeights);
outMesh.setMode(mode); outMesh.setMode(mode);
outMesh.setLineWidth(lineWidth);
if (totalVerts >= 65536) { if (totalVerts >= 65536) {
// make sure we create an UnsignedInt buffer so // make sure we create an UnsignedInt buffer so
// we can fit all of the meshes // we can fit all of the meshes
@ -748,7 +746,6 @@ public class BatchNode extends GeometryGroupNode {
} }
} }
Geometry geometry; Geometry geometry;
boolean needMeshUpdate = false;
} }
protected void setNeedsFullRebatch(boolean needsFullRebatch) { protected void setNeedsFullRebatch(boolean needsFullRebatch) {

@ -570,6 +570,35 @@ public class Node extends Spatial {
// optimization: try collideWith BoundingVolume to avoid possibly redundant tests on children // optimization: try collideWith BoundingVolume to avoid possibly redundant tests on children
// number 4 in condition is somewhat arbitrary. When there is only one child, the boundingVolume test is redundant at all. // number 4 in condition is somewhat arbitrary. When there is only one child, the boundingVolume test is redundant at all.
// The idea is when there are few children, it can be too expensive to test boundingVolume first. // The idea is when there are few children, it can be too expensive to test boundingVolume first.
/*
I'm removing this change until some issues can be addressed and I really
think it needs to be implemented a better way anyway.
First, it causes issues for anyone doing collideWith() with BoundingVolumes
and expecting it to trickle down to the children. For example, children
with BoundingSphere bounding volumes and collideWith(BoundingSphere). Doing
a collision check at the parent level then has to do a BoundingSphere to BoundingBox
collision which isn't resolved. (Having to come up with a collision point in that
case is tricky and the first sign that this is the wrong approach.)
Second, the rippling changes this caused to 'optimize' collideWith() for this
special use-case are another sign that this approach was a bit dodgy. The whole
idea of calculating a full collision just to see if the two shapes collide at all
is very wasteful.
A proper implementation should support a simpler boolean check that doesn't do
all of that calculation. For example, if 'other' is also a BoundingVolume (ie: 99.9%
of all non-Ray cases) then a direct BV to BV intersects() test can be done. So much
faster. And if 'other' _is_ a Ray then the BV.intersects(Ray) call can be done.
I don't have time to do it right now but I'll at least un-break a bunch of peoples'
code until it can be 'optimized' properly. Hopefully it's not too late to back out
the other dodgy ripples this caused. -pspeed (hindsight-expert ;))
Note: the code itself is relatively simple to implement but I don't have time to
a) test it, and b) see if '> 4' is still a decent check for it. Could be it's fast
enough to do all the time for > 1.
if (children.size() > 4) if (children.size() > 4)
{ {
BoundingVolume bv = this.getWorldBound(); BoundingVolume bv = this.getWorldBound();
@ -578,6 +607,7 @@ public class Node extends Spatial {
// collideWith without CollisionResults parameter used to avoid allocation when possible // collideWith without CollisionResults parameter used to avoid allocation when possible
if (bv.collideWith(other) == 0) return 0; if (bv.collideWith(other) == 0) return 0;
} }
*/
for (Spatial child : children.getArray()){ for (Spatial child : children.getArray()){
total += child.collideWith(other, results); total += child.collideWith(other, results);
} }

@ -99,6 +99,7 @@ public class Grid extends Mesh {
updateBound(); updateBound();
updateCounts(); updateCounts();
setStatic();
} }
} }

@ -35,6 +35,7 @@ import com.jme3.math.Vector3f;
import com.jme3.scene.Mesh; import com.jme3.scene.Mesh;
import com.jme3.scene.VertexBuffer; import com.jme3.scene.VertexBuffer;
import com.jme3.scene.VertexBuffer.Type; import com.jme3.scene.VertexBuffer.Type;
import com.jme3.scene.VertexBuffer.Usage;
import com.jme3.util.BufferUtils; import com.jme3.util.BufferUtils;
import java.nio.FloatBuffer; import java.nio.FloatBuffer;
@ -62,6 +63,7 @@ public class WireFrustum extends Mesh {
3, 7, 3, 7,
} }
); );
getBuffer(Type.Index).setUsage(Usage.Static);
setMode(Mode.Lines); setMode(Mode.Lines);
} }

@ -198,6 +198,12 @@ public class Curve extends Mesh {
* points * points
*/ */
private void createNurbMesh(int nbSubSegments) { private void createNurbMesh(int nbSubSegments) {
if(spline.getControlPoints() != null && spline.getControlPoints().size() > 0) {
if(nbSubSegments == 0) {
nbSubSegments = spline.getControlPoints().size() + 1;
} else {
nbSubSegments = spline.getControlPoints().size() * nbSubSegments + 1;
}
float minKnot = spline.getMinNurbKnot(); float minKnot = spline.getMinNurbKnot();
float maxKnot = spline.getMaxNurbKnot(); float maxKnot = spline.getMaxNurbKnot();
float deltaU = (maxKnot - minKnot) / nbSubSegments; float deltaU = (maxKnot - minKnot) / nbSubSegments;
@ -228,6 +234,7 @@ public class Curve extends Mesh {
this.updateBound(); this.updateBound();
this.updateCounts(); this.updateCounts();
} }
}
private void createLinearMesh() { private void createLinearMesh() {
float[] array = new float[spline.getControlPoints().size() * 3]; float[] array = new float[spline.getControlPoints().size() * 3];

@ -389,6 +389,7 @@ public class Cylinder extends Mesh {
} }
updateBound(); updateBound();
setStatic();
} }
@Override @Override

@ -124,6 +124,7 @@ public class Quad extends Mesh {
} }
updateBound(); updateBound();
setStatic();
} }

@ -299,7 +299,6 @@ public class Sphere extends Mesh {
} }
updateBound(); updateBound();
setStatic();
} }
/** /**
@ -400,6 +399,7 @@ public class Sphere extends Mesh {
this.interior = interior; this.interior = interior;
setGeometryData(); setGeometryData();
setIndexData(); setIndexData();
setStatic();
} }
public void read(JmeImporter e) throws IOException { public void read(JmeImporter e) throws IOException {

@ -459,7 +459,7 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable
debug = true; debug = true;
} }
abstract void getReceivers(GeometryList lightReceivers); protected abstract void getReceivers(GeometryList lightReceivers);
public void postFrame(FrameBuffer out) { public void postFrame(FrameBuffer out) {
if (skipPostPass) { if (skipPostPass) {

@ -192,7 +192,7 @@ public class DirectionalLightShadowRenderer extends AbstractShadowRenderer {
} }
@Override @Override
void getReceivers(GeometryList lightReceivers) { protected void getReceivers(GeometryList lightReceivers) {
if (lightReceivers.size()==0) { if (lightReceivers.size()==0) {
for (Spatial scene : viewPort.getScenes()) { for (Spatial scene : viewPort.getScenes()) {
ShadowUtil.getGeometriesInCamFrustum(scene, viewPort.getCamera(), RenderQueue.ShadowMode.Receive, lightReceivers); ShadowUtil.getGeometriesInCamFrustum(scene, viewPort.getCamera(), RenderQueue.ShadowMode.Receive, lightReceivers);

@ -139,7 +139,7 @@ public class PointLightShadowRenderer extends AbstractShadowRenderer {
} }
@Override @Override
void getReceivers(GeometryList lightReceivers) { protected void getReceivers(GeometryList lightReceivers) {
lightReceivers.clear(); lightReceivers.clear();
for (Spatial scene : viewPort.getScenes()) { for (Spatial scene : viewPort.getScenes()) {
ShadowUtil.getLitGeometriesInViewPort(scene, viewPort.getCamera(), shadowCams, RenderQueue.ShadowMode.Receive, lightReceivers); ShadowUtil.getLitGeometriesInViewPort(scene, viewPort.getCamera(), shadowCams, RenderQueue.ShadowMode.Receive, lightReceivers);

@ -151,7 +151,7 @@ public class SpotLightShadowRenderer extends AbstractShadowRenderer {
} }
@Override @Override
void getReceivers(GeometryList lightReceivers) { protected void getReceivers(GeometryList lightReceivers) {
lightReceivers.clear(); lightReceivers.clear();
Camera[] cameras = new Camera[1]; Camera[] cameras = new Camera[1];
cameras[0] = shadowCam; cameras[0] = shadowCam;

@ -54,6 +54,8 @@ import java.util.prefs.Preferences;
*/ */
public final class AppSettings extends HashMap<String, Object> { public final class AppSettings extends HashMap<String, Object> {
private static final long serialVersionUID = 1L;
private static final AppSettings defaults = new AppSettings(false); private static final AppSettings defaults = new AppSettings(false);
/** /**
@ -109,6 +111,33 @@ public final class AppSettings extends HashMap<String, Object> {
*/ */
public static final String ANDROID_OPENAL_SOFT = "OpenAL_SOFT"; public static final String ANDROID_OPENAL_SOFT = "OpenAL_SOFT";
/**
* Use JogAmp's JOGL as the display system, with the OpenGL forward compatible profile
* <p>
* N.B: This backend is EXPERIMENTAL
*
* @see AppSettings#setRenderer(java.lang.String)
*/
public static final String JOGL_OPENGL_FORWARD_COMPATIBLE = "JOGL_OPENGL_FORWARD_COMPATIBLE";
/**
* Use JogAmp's JOGL as the display system with the backward compatible profile
* <p>
* N.B: This backend is EXPERIMENTAL
*
* @see AppSettings#setRenderer(java.lang.String)
*/
public static final String JOGL_OPENGL_BACKWARD_COMPATIBLE = "JOGL_OPENGL_BACKWARD_COMPATIBLE";
/**
* Use JogAmp's JOAL as the display system
* <p>
* N.B: This backend is EXPERIMENTAL
*
* @see AppSettings#setRenderer(java.lang.String)
*/
public static final String JOAL = "JOAL";
static { static {
defaults.put("Width", 640); defaults.put("Width", 640);
defaults.put("Height", 480); defaults.put("Height", 480);
@ -118,7 +147,7 @@ public final class AppSettings extends HashMap<String, Object> {
defaults.put("StencilBits", 0); defaults.put("StencilBits", 0);
defaults.put("Samples", 0); defaults.put("Samples", 0);
defaults.put("Fullscreen", false); defaults.put("Fullscreen", false);
defaults.put("Title", "jMonkey Engine 3.0"); defaults.put("Title", JmeVersion.FULL_NAME);
defaults.put("Renderer", LWJGL_OPENGL2); defaults.put("Renderer", LWJGL_OPENGL2);
defaults.put("AudioRenderer", LWJGL_OPENAL); defaults.put("AudioRenderer", LWJGL_OPENAL);
defaults.put("DisableJoysticks", true); defaults.put("DisableJoysticks", true);

@ -153,6 +153,10 @@ public abstract class JmeSystemDelegate {
return false; return false;
} else if (arch.equals("universal")) { } else if (arch.equals("universal")) {
return false; return false;
} else if (arch.equals("aarch32")) {
return false;
} else if (arch.equals("aarch64")) {
return true;
} else if (arch.equals("arm")) { } else if (arch.equals("arm")) {
return false; return false;
} else { } else {

@ -0,0 +1,66 @@
/*
* Copyright (c) 2009-2015 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;
import java.io.IOException;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Pulls in version info from the version.properties file.
*
* @author Kirill Vainer
*/
public class JmeVersion {
private static final Logger logger = Logger.getLogger(JmeVersion.class.getName());
private static final Properties props = new Properties();
static {
try {
props.load(JmeVersion.class.getResourceAsStream("version.properties"));
} catch (IOException ex) {
logger.log(Level.WARNING, "Unable to read version info!", ex);
}
}
public static final String BUILD_DATE = props.getProperty("build.date", "1900-01-01");
public static final String BRANCH_NAME = props.getProperty("git.branch", "unknown");
public static final String GIT_HASH = props.getProperty("git.hash", "");
public static final String GIT_SHORT_HASH = props.getProperty("git.hash.short", "");
public static final String GIT_TAG = props.getProperty("git.tag", "");
public static final String VERSION_NUMBER = props.getProperty("version.number", "");
public static final String VERSION_TAG = props.getProperty("version.tag", "");
public static final String VERSION_FULL = props.getProperty("version.full", "");
public static final String FULL_NAME = props.getProperty("name.full", "jMonkeyEngine (unknown version)");
}

@ -88,6 +88,11 @@ public enum Platform {
*/ */
Android_ARM7, Android_ARM7,
/**
* Android ARM8
*/
Android_ARM8,
/** /**
* Android x86 * Android x86
*/ */

@ -1041,6 +1041,7 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ {
capsule.write(mipMapSizes, "mipMapSizes", null); capsule.write(mipMapSizes, "mipMapSizes", null);
capsule.write(multiSamples, "multiSamples", 1); capsule.write(multiSamples, "multiSamples", 1);
capsule.writeByteBufferArrayList(data, "data", null); capsule.writeByteBufferArrayList(data, "data", null);
capsule.write(colorSpace, "colorSpace", null);
} }
public void read(JmeImporter e) throws IOException { public void read(JmeImporter e) throws IOException {
@ -1052,6 +1053,7 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ {
mipMapSizes = capsule.readIntArray("mipMapSizes", null); mipMapSizes = capsule.readIntArray("mipMapSizes", null);
multiSamples = capsule.readInt("multiSamples", 1); multiSamples = capsule.readInt("multiSamples", 1);
data = (ArrayList<ByteBuffer>) capsule.readByteBufferArrayList("data", null); data = (ArrayList<ByteBuffer>) capsule.readByteBufferArrayList("data", null);
colorSpace = capsule.readEnum("colorSpace", ColorSpace.class, null);
if (mipMapSizes != null) { if (mipMapSizes != null) {
needGeneratedMips = false; needGeneratedMips = false;

@ -316,7 +316,6 @@ public abstract class Texture implements CloneableSmartAsset, Savable, Cloneable
private MinFilter minificationFilter = MinFilter.BilinearNoMipMaps; private MinFilter minificationFilter = MinFilter.BilinearNoMipMaps;
private MagFilter magnificationFilter = MagFilter.Bilinear; private MagFilter magnificationFilter = MagFilter.Bilinear;
private ShadowCompareMode shadowCompareMode = ShadowCompareMode.Off; private ShadowCompareMode shadowCompareMode = ShadowCompareMode.Off;
private boolean needCompareModeUpdate = false;
private int anisotropicFilter; private int anisotropicFilter;
/** /**
@ -404,7 +403,6 @@ public abstract class Texture implements CloneableSmartAsset, Savable, Cloneable
"compareMode can not be null."); "compareMode can not be null.");
} }
this.shadowCompareMode = compareMode; this.shadowCompareMode = compareMode;
needCompareModeUpdate = true;
} }
/** /**
@ -489,7 +487,7 @@ public abstract class Texture implements CloneableSmartAsset, Savable, Cloneable
/** /**
* @return the anisotropic filtering level for this texture. Default value * @return the anisotropic filtering level for this texture. Default value
* is 0 (use value from config), * is 0 (use value from config),
* 1 means 1x (no anisotrophy), 2 means x2, 4 is x4, etc. * 1 means 1x (no anisotropy), 2 means x2, 4 is x4, etc.
*/ */
public int getAnisotropicFilter() { public int getAnisotropicFilter() {
return anisotropicFilter; return anisotropicFilter;
@ -636,14 +634,4 @@ public abstract class Texture implements CloneableSmartAsset, Savable, Cloneable
magnificationFilter = capsule.readEnum("magnificationFilter", magnificationFilter = capsule.readEnum("magnificationFilter",
MagFilter.class, MagFilter.Bilinear); MagFilter.class, MagFilter.Bilinear);
} }
public boolean isNeedCompareModeUpdate() {
return needCompareModeUpdate;
}
public void compareModeUpdated() {
this.needCompareModeUpdate = false;
}
} }

@ -51,7 +51,8 @@ public class DefaultImageRaster extends ImageRaster {
private void rangeCheck(int x, int y) { private void rangeCheck(int x, int y) {
if (x < 0 || y < 0 || x >= width || y >= height) { if (x < 0 || y < 0 || x >= width || y >= height) {
throw new IllegalArgumentException("x and y must be inside the image dimensions"); throw new IllegalArgumentException("x and y must be inside the image dimensions:"
+ x + ", " + y + " in:" + width + ", " + height);
} }
} }

@ -45,9 +45,11 @@ public final class LastTextureState {
public Texture.WrapMode sWrap, tWrap, rWrap; public Texture.WrapMode sWrap, tWrap, rWrap;
public Texture.MagFilter magFilter; public Texture.MagFilter magFilter;
public Texture.MinFilter minFilter; public Texture.MinFilter minFilter;
public int anisoFilter;
public Texture.ShadowCompareMode shadowCompareMode;
public LastTextureState() { public LastTextureState() {
// All parameters initialized to null (meaning unset). reset();
} }
public void reset() { public void reset() {
@ -56,5 +58,10 @@ public final class LastTextureState {
rWrap = null; rWrap = null;
magFilter = null; magFilter = null;
minFilter = null; minFilter = null;
anisoFilter = 0;
// The default in OpenGL is OFF, so we avoid setting this per texture
// if its not used.
shadowCompareMode = Texture.ShadowCompareMode.Off;
} }
} }

@ -36,6 +36,7 @@ import com.jme3.math.Quaternion;
import com.jme3.math.Vector2f; import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
import com.jme3.math.Vector4f; import com.jme3.math.Vector4f;
import java.io.UnsupportedEncodingException;
import java.lang.ref.PhantomReference; import java.lang.ref.PhantomReference;
import java.lang.ref.Reference; import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue; import java.lang.ref.ReferenceQueue;
@ -1010,11 +1011,15 @@ public final class BufferUtils {
} }
public static ByteBuffer createByteBuffer(String data) { public static ByteBuffer createByteBuffer(String data) {
byte[] bytes = data.getBytes(); try {
byte[] bytes = data.getBytes("UTF-8");
ByteBuffer bb = createByteBuffer(bytes.length); ByteBuffer bb = createByteBuffer(bytes.length);
bb.put(bytes); bb.put(bytes);
bb.flip(); bb.flip();
return bb; return bb;
} catch (UnsupportedEncodingException ex) {
throw new UnsupportedOperationException(ex);
}
} }
/** /**

@ -43,21 +43,6 @@ import java.util.Map.Entry;
*/ */
public final class ListMap<K, V> extends AbstractMap<K, V> implements Cloneable, Serializable { public final class ListMap<K, V> extends AbstractMap<K, V> implements Cloneable, Serializable {
public static void main(String[] args){
Map<String, String> map = new ListMap<String, String>();
map.put( "bob", "hello");
System.out.println(map.get("bob"));
map.remove("bob");
System.out.println(map.size());
map.put("abc", "1");
map.put("def", "2");
map.put("ghi", "3");
map.put("jkl", "4");
map.put("mno", "5");
System.out.println(map.get("ghi"));
}
private final static class ListMapEntry<K, V> implements Map.Entry<K, V>, Cloneable { private final static class ListMapEntry<K, V> implements Map.Entry<K, V>, Cloneable {
private final K key; private final K key;

@ -42,7 +42,6 @@ import java.io.*;
public class LittleEndien extends InputStream implements DataInput { public class LittleEndien extends InputStream implements DataInput {
protected BufferedInputStream in; protected BufferedInputStream in;
protected BufferedReader inRead;
/** /**
* Creates a new LittleEndien reader from the given input stream. The * Creates a new LittleEndien reader from the given input stream. The
@ -51,7 +50,6 @@ public class LittleEndien extends InputStream implements DataInput {
*/ */
public LittleEndien(InputStream in) { public LittleEndien(InputStream in) {
this.in = new BufferedInputStream(in); this.in = new BufferedInputStream(in);
inRead = new BufferedReader(new InputStreamReader(in));
} }
public int read() throws IOException { public int read() throws IOException {
@ -141,7 +139,7 @@ public class LittleEndien extends InputStream implements DataInput {
} }
public String readLine() throws IOException { public String readLine() throws IOException {
return inRead.readLine(); throw new IOException("Unsupported operation");
} }
public String readUTF() throws IOException { public String readUTF() throws IOException {

@ -612,9 +612,9 @@ public class TangentBinormalGenerator {
normal.normalizeLocal(); normal.normalizeLocal();
return new TriangleData( return new TriangleData(
tangent, tangent.clone(),
binormal, binormal.clone(),
normal); normal.clone());
} finally { } finally {
tmp.release(); tmp.release();
} }

@ -1,3 +1,5 @@
#import "Common/ShaderLib/GLSLCompat.glsllib"
#ifdef TEXTURE #ifdef TEXTURE
uniform sampler2D m_Texture; uniform sampler2D m_Texture;
varying vec2 texCoord; varying vec2 texCoord;

@ -7,8 +7,8 @@ MaterialDef Default GUI {
} }
Technique { Technique {
VertexShader GLSL100: Common/MatDefs/Gui/Gui.vert VertexShader GLSL150: Common/MatDefs/Gui/Gui.vert
FragmentShader GLSL100: Common/MatDefs/Gui/Gui.frag FragmentShader GLSL150: Common/MatDefs/Gui/Gui.frag
WorldParameters { WorldParameters {
WorldViewProjectionMatrix WorldViewProjectionMatrix
@ -21,6 +21,17 @@ MaterialDef Default GUI {
} }
Technique { Technique {
VertexShader GLSL100: Common/MatDefs/Gui/Gui.vert
FragmentShader GLSL100: Common/MatDefs/Gui/Gui.frag
WorldParameters {
WorldViewProjectionMatrix
}
Defines {
TEXTURE : Texture
VERTEX_COLOR : VertexColor
}
} }
} }

@ -1,3 +1,5 @@
#import "Common/ShaderLib/GLSLCompat.glsllib"
uniform mat4 g_WorldViewProjectionMatrix; uniform mat4 g_WorldViewProjectionMatrix;
uniform vec4 m_Color; uniform vec4 m_Color;

@ -37,6 +37,7 @@ varying vec3 SpecularSum;
#endif #endif
#if (defined(PARALLAXMAP) || (defined(NORMALMAP_PARALLAX) && defined(NORMALMAP))) && !defined(VERTEX_LIGHTING) #if (defined(PARALLAXMAP) || (defined(NORMALMAP_PARALLAX) && defined(NORMALMAP))) && !defined(VERTEX_LIGHTING)
uniform float m_ParallaxHeight; uniform float m_ParallaxHeight;
varying vec3 vViewDirPrlx;
#endif #endif
#ifdef LIGHTMAP #ifdef LIGHTMAP
@ -78,18 +79,18 @@ void main(){
#ifdef STEEP_PARALLAX #ifdef STEEP_PARALLAX
#ifdef NORMALMAP_PARALLAX #ifdef NORMALMAP_PARALLAX
//parallax map is stored in the alpha channel of the normal map //parallax map is stored in the alpha channel of the normal map
newTexCoord = steepParallaxOffset(m_NormalMap, vViewDir, texCoord, m_ParallaxHeight); newTexCoord = steepParallaxOffset(m_NormalMap, vViewDirPrlx, texCoord, m_ParallaxHeight);
#else #else
//parallax map is a texture //parallax map is a texture
newTexCoord = steepParallaxOffset(m_ParallaxMap, vViewDir, texCoord, m_ParallaxHeight); newTexCoord = steepParallaxOffset(m_ParallaxMap, vViewDirPrlx, texCoord, m_ParallaxHeight);
#endif #endif
#else #else
#ifdef NORMALMAP_PARALLAX #ifdef NORMALMAP_PARALLAX
//parallax map is stored in the alpha channel of the normal map //parallax map is stored in the alpha channel of the normal map
newTexCoord = classicParallaxOffset(m_NormalMap, vViewDir, texCoord, m_ParallaxHeight); newTexCoord = classicParallaxOffset(m_NormalMap, vViewDirPrlx, texCoord, m_ParallaxHeight);
#else #else
//parallax map is a texture //parallax map is a texture
newTexCoord = classicParallaxOffset(m_ParallaxMap, vViewDir, texCoord, m_ParallaxHeight); newTexCoord = classicParallaxOffset(m_ParallaxMap, vViewDirPrlx, texCoord, m_ParallaxHeight);
#endif #endif
#endif #endif
#else #else
@ -153,8 +154,9 @@ void main(){
#ifdef VERTEX_LIGHTING #ifdef VERTEX_LIGHTING
vec2 light = vertexLightValues.xy; vec2 light = vertexLightValues.xy;
#ifdef COLORRAMP #ifdef COLORRAMP
light.x = texture2D(m_ColorRamp, vec2(light.x, 0.0)).r; diffuseColor.rgb *= texture2D(m_ColorRamp, vec2(light.x, 0.0)).rgb;
light.y = texture2D(m_ColorRamp, vec2(light.y, 0.0)).r; specularColor.rgb *= texture2D(m_ColorRamp, vec2(light.y, 0.0)).rgb;
light.xy = vec2(1.0);
#endif #endif
gl_FragColor.rgb = AmbientSum * diffuseColor.rgb + gl_FragColor.rgb = AmbientSum * diffuseColor.rgb +
@ -184,6 +186,7 @@ void main(){
#ifdef COLORRAMP #ifdef COLORRAMP
diffuseColor.rgb *= texture2D(m_ColorRamp, vec2(light.x, 0.0)).rgb; diffuseColor.rgb *= texture2D(m_ColorRamp, vec2(light.x, 0.0)).rgb;
specularColor.rgb *= texture2D(m_ColorRamp, vec2(light.y, 0.0)).rgb; specularColor.rgb *= texture2D(m_ColorRamp, vec2(light.y, 0.0)).rgb;
light.xy = vec2(1.0);
#endif #endif
// Workaround, since it is not possible to modify varying variables // Workaround, since it is not possible to modify varying variables

@ -48,6 +48,10 @@ varying vec3 lightVec;
uniform vec4 g_LightDirection; uniform vec4 g_LightDirection;
#endif #endif
#if (defined(PARALLAXMAP) || (defined(NORMALMAP_PARALLAX) && defined(NORMALMAP))) && !defined(VERTEX_LIGHTING)
varying vec3 vViewDirPrlx;
#endif
#ifdef USE_REFLECTION #ifdef USE_REFLECTION
uniform vec3 g_CameraPosition; uniform vec3 g_CameraPosition;
@ -107,17 +111,25 @@ void main(){
wvLightPos.w = g_LightPosition.w; wvLightPos.w = g_LightPosition.w;
vec4 lightColor = g_LightColor; vec4 lightColor = g_LightColor;
#if defined(NORMALMAP) && !defined(VERTEX_LIGHTING) #if (defined(NORMALMAP) || defined(PARALLAXMAP)) && !defined(VERTEX_LIGHTING)
vec3 wvTangent = normalize(TransformNormal(modelSpaceTan)); vec3 wvTangent = normalize(TransformNormal(modelSpaceTan));
vec3 wvBinormal = cross(wvNormal, wvTangent); vec3 wvBinormal = cross(wvNormal, wvTangent);
mat3 tbnMat = mat3(wvTangent, wvBinormal * inTangent.w,wvNormal); mat3 tbnMat = mat3(wvTangent, wvBinormal * inTangent.w,wvNormal);
#endif
#if defined(NORMALMAP) && !defined(VERTEX_LIGHTING)
vViewDir = -wvPosition * tbnMat; vViewDir = -wvPosition * tbnMat;
#if (defined(PARALLAXMAP) || (defined(NORMALMAP_PARALLAX) && defined(NORMALMAP)))
vViewDirPrlx = vViewDir;
#endif
lightComputeDir(wvPosition, lightColor.w, wvLightPos, vLightDir, lightVec); lightComputeDir(wvPosition, lightColor.w, wvLightPos, vLightDir, lightVec);
vLightDir.xyz = (vLightDir.xyz * tbnMat).xyz; vLightDir.xyz = (vLightDir.xyz * tbnMat).xyz;
#elif !defined(VERTEX_LIGHTING) #elif !defined(VERTEX_LIGHTING)
vNormal = wvNormal; vNormal = wvNormal;
vViewDir = viewDir; vViewDir = viewDir;
#if defined(PARALLAXMAP)
vViewDirPrlx = -wvPosition * tbnMat;
#endif
lightComputeDir(wvPosition, lightColor.w, wvLightPos, vLightDir, lightVec); lightComputeDir(wvPosition, lightColor.w, wvLightPos, vLightDir, lightVec);
#endif #endif

@ -1,3 +1,4 @@
Exception This material definition is deprecated. Please use Unshaded.j3md instead.
MaterialDef Colored Textured { MaterialDef Colored Textured {
MaterialParameters { MaterialParameters {

@ -1,3 +1,5 @@
#import "Common/ShaderLib/GLSLCompat.glsllib"
#if defined(HAS_GLOWMAP) || defined(HAS_COLORMAP) || (defined(HAS_LIGHTMAP) && !defined(SEPARATE_TEXCOORD)) #if defined(HAS_GLOWMAP) || defined(HAS_COLORMAP) || (defined(HAS_LIGHTMAP) && !defined(SEPARATE_TEXCOORD))
#define NEED_TEXCOORD1 #define NEED_TEXCOORD1
#endif #endif

@ -54,8 +54,8 @@ MaterialDef Unshaded {
} }
Technique { Technique {
VertexShader GLSL100: Common/MatDefs/Misc/Unshaded.vert VertexShader GLSL150: Common/MatDefs/Misc/Unshaded.vert
FragmentShader GLSL100: Common/MatDefs/Misc/Unshaded.frag FragmentShader GLSL150: Common/MatDefs/Misc/Unshaded.frag
WorldParameters { WorldParameters {
WorldViewProjectionMatrix WorldViewProjectionMatrix
@ -76,6 +76,25 @@ MaterialDef Unshaded {
} }
Technique { Technique {
VertexShader GLSL100: Common/MatDefs/Misc/Unshaded.vert
FragmentShader GLSL100: Common/MatDefs/Misc/Unshaded.frag
WorldParameters {
WorldViewProjectionMatrix
ViewProjectionMatrix
ViewMatrix
}
Defines {
INSTANCING : UseInstancing
SEPARATE_TEXCOORD : SeparateTexCoord
HAS_COLORMAP : ColorMap
HAS_LIGHTMAP : LightMap
HAS_VERTEXCOLOR : VertexColor
HAS_COLOR : Color
NUM_BONES : NumberOfBones
DISCARD_ALPHA : AlphaDiscardThreshold
}
} }
Technique PreNormalPass { Technique PreNormalPass {

@ -1,3 +1,4 @@
#import "Common/ShaderLib/GLSLCompat.glsllib"
#import "Common/ShaderLib/Skinning.glsllib" #import "Common/ShaderLib/Skinning.glsllib"
#import "Common/ShaderLib/Instancing.glsllib" #import "Common/ShaderLib/Instancing.glsllib"

@ -1,97 +1,121 @@
MaterialDef UnshadedNodes { MaterialDef UnshadedNodes {
MaterialParameters { MaterialParameters {
Texture2D ColorMap Texture2D ColorMap
Texture2D LightMap Texture2D LightMap
Color Color (Color) Color Color (Color)
Boolean VertexColor (UseVertexColor) Boolean VertexColor (UseVertexColor)
Boolean SeparateTexCoord Boolean SeparateTexCoord
// Alpha threshold for fragment discarding
Float AlphaDiscardThreshold (AlphaTestFallOff) Float AlphaDiscardThreshold (AlphaTestFallOff)
// For hardware skinning
Int NumberOfBones Int NumberOfBones
Matrix4Array BoneMatrices Matrix4Array BoneMatrices
} }
Technique { Technique {
WorldParameters { WorldParameters {
WorldViewProjectionMatrix WorldViewProjectionMatrix
//used for fog
WorldViewMatrix WorldViewMatrix
} }
VertexShaderNodes {
VertexShaderNodes{ ShaderNode GpuSkinning {
ShaderNode GpuSkinning{ Definition : BasicGPUSkinning : Common/MatDefs/ShaderNodes/HardwareSkinning/HardwareSkinning.j3sn
Definition: BasicGPUSkinning : Common/MatDefs/ShaderNodes/HardwareSkinning/HardwareSkinning.j3sn
Condition : NumberOfBones Condition : NumberOfBones
InputMapping{ InputMapping {
modelPosition = Global.position; modelPosition = Global.position
boneMatrices = MatParam.BoneMatrices boneMatrices = MatParam.BoneMatrices
boneWeight = Attr.inHWBoneWeight boneWeight = Attr.inHWBoneWeight
boneIndex = Attr.inHWBoneIndex boneIndex = Attr.inHWBoneIndex
} }
OutputMapping{ OutputMapping {
Global.position = modModelPosition Global.position = modModelPosition
} }
} }
ShaderNode UnshadedVert{ ShaderNode UnshadedVert {
Definition: CommonVert : Common/MatDefs/ShaderNodes/Common/CommonVert.j3sn Definition : CommonVert : Common/MatDefs/ShaderNodes/Common/CommonVert.j3sn
InputMapping{ InputMapping {
worldViewProjectionMatrix = WorldParam.WorldViewProjectionMatrix worldViewProjectionMatrix = WorldParam.WorldViewProjectionMatrix
modelPosition = Global.position.xyz modelPosition = Global.position.xyz
texCoord1 = Attr.inTexCoord: ColorMap || (LightMap && !SeparateTexCoord) texCoord1 = Attr.inTexCoord : ColorMap || (LightMap && !SeparateTexCoord)
texCoord2 = Attr.inTexCoord2: SeparateTexCoord texCoord2 = Attr.inTexCoord2 : SeparateTexCoord
vertColor = Attr.inColor: VertexColor vertColor = Attr.inColor : VertexColor
} }
OutputMapping{ OutputMapping {
Global.position = projPosition Global.position = projPosition
} }
} }
} }
FragmentShaderNodes{ FragmentShaderNodes {
ShaderNode UnshadedFrag{ ShaderNode MatColorMult {
Definition: Unshaded : Common/MatDefs/ShaderNodes/Common/Unshaded.j3sn Definition : ColorMult : Common/MatDefs/ShaderNodes/Basic/ColorMult.j3sn
InputMapping{ InputMappings {
texCoord = UnshadedVert.texCoord1: ColorMap color1 = MatParam.Color
vertColor = UnshadedVert.vertColor: VertexColor color2 = Global.outColor
matColor = MatParam.Color: Color
colorMap = MatParam.ColorMap: ColorMap
color = Global.outColor
} }
OutputMapping{ OutputMappings {
Global.outColor = color Global.outColor = outColor
} }
Condition : Color
} }
ShaderNode VertColorMult {
ShaderNode AlphaDiscardThreshold{ Definition : ColorMult : Common/MatDefs/ShaderNodes/Basic/ColorMult.j3sn
Definition: AlphaDiscard : Common/MatDefs/ShaderNodes/Basic/AlphaDiscard.j3sn InputMappings {
color1 = UnshadedVert.vertColor
color2 = Global.outColor
}
OutputMappings {
Global.outColor = outColor
}
Condition : VertexColor
}
ShaderNode ColorMapTF {
Definition : TextureFetch : Common/MatDefs/ShaderNodes/Basic/TextureFetch.j3sn
InputMappings {
texCoord = UnshadedVert.texCoord1
textureMap = MatParam.ColorMap
}
OutputMappings {
}
Condition : ColorMap
}
ShaderNode ColorMapMult {
Definition : ColorMult : Common/MatDefs/ShaderNodes/Basic/ColorMult.j3sn
InputMappings {
color1 = ColorMapTF.outColor
color2 = Global.outColor
}
OutputMappings {
Global.outColor = outColor
}
Condition : ColorMap
}
ShaderNode AlphaDiscardThreshold {
Definition : AlphaDiscard : Common/MatDefs/ShaderNodes/Basic/AlphaDiscard.j3sn
Condition : AlphaDiscardThreshold Condition : AlphaDiscardThreshold
InputMapping{ InputMapping {
alpha = Global.outColor.a alpha = Global.outColor.a
threshold = MatParam.AlphaDiscardThreshold threshold = MatParam.AlphaDiscardThreshold
} }
} }
ShaderNode LightMap{ ShaderNode LightMapTF {
Definition: LightMapping : Common/MatDefs/ShaderNodes/LightMapping/LightMapping.j3sn Definition : TextureFetch : Common/MatDefs/ShaderNodes/Basic/TextureFetch.j3sn
Condition: LightMap InputMappings {
InputMapping{ textureMap = MatParam.LightMap
texCoord = UnshadedVert.texCoord1: !SeparateTexCoord texCoord = UnshadedVert.texCoord2 : SeparateTexCoord
texCoord = UnshadedVert.texCoord2: SeparateTexCoord texCoord = UnshadedVert.texCoord1 : !SeparateTexCoord
lightMap = MatParam.LightMap }
color = Global.outColor OutputMappings {
}
Condition : LightMap
}
ShaderNode LightMapMult {
Definition : ColorMult : Common/MatDefs/ShaderNodes/Basic/ColorMult.j3sn
OutputMappings {
Global.outColor = outColor
} }
OutputMapping{ InputMappings {
Global.outColor = color color1 = LightMapTF.outColor
color2 = Global.outColor
} }
Condition : LightMap
} }
} }
} }
} }

@ -2,14 +2,15 @@ ShaderNodeDefinitions{
ShaderNodeDefinition TextureFetch { ShaderNodeDefinition TextureFetch {
Type: Fragment Type: Fragment
Shader GLSL100: Common/MatDefs/ShaderNodes/Basic/texture.frag Shader GLSL100: Common/MatDefs/ShaderNodes/Basic/texture.frag
Shader GLSL150: Common/MatDefs/ShaderNodes/Basic/texture15.frag
Documentation{ Documentation{
Fetches a color value in the given texture acording to given texture coordinates Fetches a color value in the given texture acording to given texture coordinates
@input texture the texture to read @input textureMap the texture to read
@input texCoord the texture coordinates @input texCoord the texture coordinates
@output outColor the fetched color @output outColor the fetched color
} }
Input { Input {
sampler2D texture sampler2D textureMap
vec2 texCoord vec2 texCoord
} }
Output { Output {

@ -1,3 +1,3 @@
void main(){ void main(){
outColor = texture2D(texture,texCoord); outColor = texture2D(textureMap,texCoord);
} }

@ -0,0 +1,3 @@
void main(){
outColor = texture(textureMap,texCoord);
}

@ -1,14 +0,0 @@
#if _VERSION_ >= 150
out vec4 outFragColor;
# define texture1D texture
# define texture2D texture
# define texture3D texture
# define texture2DLod texture
# if defined VERTEX_SHADER
# define varying out
# define attribute in
# elif defined FRAGMENT_SHADER
# define varying in
# define gl_FragColor outFragColor
# endif
#endif

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save