Alrik 9 years ago
commit f9f3c0185e
  1. 17
      .gitignore
  2. 20
      .travis.yml
  3. 7
      CONTRIBUTING.md
  4. 58
      build.gradle
  5. 77
      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. 87
      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. 13
      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. 8
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/materials/MaterialContext.java
  17. 10
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/TemporalMesh.java
  18. 48
      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. 4
      jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/CapsuleCollisionShape.java
  25. 52
      jme3-bullet/src/main/java/com/jme3/bullet/objects/PhysicsRigidBody.java
  26. 65
      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. 13
      jme3-core/src/main/java/com/jme3/bounding/BoundingVolume.java
  33. 48
      jme3-core/src/main/java/com/jme3/bounding/Intersection.java
  34. 4
      jme3-core/src/main/java/com/jme3/cinematic/Cinematic.java
  35. 20
      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. 14
      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. 153
      jme3-core/src/main/java/com/jme3/material/Material.java
  48. 135
      jme3-core/src/main/java/com/jme3/material/TechniqueDef.java
  49. 19
      jme3-core/src/main/java/com/jme3/math/FastMath.java
  50. 73
      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. 381
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java
  61. 376
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLTracer.java
  62. 4
      jme3-core/src/main/java/com/jme3/renderer/opengl/TextureUtil.java
  63. 33
      jme3-core/src/main/java/com/jme3/scene/BatchNode.java
  64. 32
      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. 57
      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. 15
      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. 19
      jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.frag
  91. 16
      jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.vert
  92. 3
      jme3-core/src/main/resources/Common/MatDefs/Misc/ColoredTextured.j3md
  93. 2
      jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.frag
  94. 25
      jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.j3md
  95. 1
      jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.vert
  96. 216
      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/
/.nb-gradle/private/
/.nb-gradle/profiles/private/
/.idea/
/dist/
/build/
/netbeans/
/sdk/jdks/local/
/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-desktop/build/
/jme3-android-native/build/
@ -80,6 +82,7 @@
/sdk/jme3-vehicle-creator/build/
/sdk/jme3-welcome-screen/build/
/sdk/jme3-glsl-support/build/
/sdk/jme3-dark-laf/build/
/sdk/nbproject/private/
/sdk/jme3-scenecomposer/nbproject/private/
/sdk/jme3-core/nbproject/private/
@ -125,6 +128,7 @@
*.so
*.jnilib
*.dylib
*.iml
/sdk/dist/
!/jme3-bullet-native/libs/native/windows/x86_64/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/linux/x86/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
- netbeans
branches:
only:
- master
# branches:
# only:
# - master
notifications:
slack:
@ -25,16 +25,26 @@ install:
script:
- ./gradlew check
- ./gradlew createZipDistribution
- "[ $TRAVIS_BRANCH == 'master' ] && [ $TRAVIS_PULL_REQUEST == 'false' ] && ./gradlew uploadArchives || :"
before_deploy:
- export RELEASE_DIST=$(ls build/distributions/*.zip)
deploy:
provider: releases
api_key:
secure: "KbFiMt0a8FxUKvCJUYwikLYaqqGMn1p6k4OsXnGqwptQZEUIayabNLHeaD2kTNT3e6AY1ETwQLff/lB2LttmIo4g5NWW63g1K3A/HwgnhJwETengiProZ/Udl+ugPeDL/+ar43HUhFq4knBnzFKnEcHAThTPVqH/RMDvZf1UUYI="
file: build/distributions/jME3.1.0_snapshot-github_2015-06-20.zip
secure: PuEsJd6juXBH29ByITW3ntSAyrwWs0IeFvXJ5Y2YlhojhSMtTwkoWeB6YmDJWP4fhzbajk4TQ1HlOX2IxJXSW/8ShOEIUlGXz9fHiST0dkSM+iRAUgC5enCLW5ITPTiem7eY9ZhS9miIam7ngce9jHNMh75PTzZrEJtezoALT9w=
file_glob: true
file: "${RELEASE_DIST}"
skip_cleanup: true
on:
repo: jMonkeyEngine/jmonkeyengine
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:
# required libs for android build tools
# 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.
- 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.
#### Core Contributors

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

@ -4,10 +4,9 @@
apply plugin: 'java'
apply plugin: 'maven'
apply plugin: 'maven-publish'
group = 'com.jme3'
version = jmeVersion + '-' + jmeVersionTag
group = 'com.jme3'
version = jmePomVersion
sourceCompatibility = '1.6'
[compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
@ -19,9 +18,23 @@ repositories {
}
}
configurations {
deployerJars
}
dependencies {
// 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 {
@ -56,41 +69,35 @@ artifacts {
}
}
publishing {
publications {
maven(MavenPublication) {
from components.java
artifact sourcesJar
artifact javadocJar
pom.withXml {
asNode().children().last() + {
resolveStrategy = Closure.DELEGATE_FIRST
name POM_NAME
description POM_DESCRIPTION
url POM_URL
scm {
url POM_SCM_URL
connection POM_SCM_CONNECTION
developerConnection POM_SCM_DEVELOPER_CONNECTION
}
licenses {
license {
name POM_LICENSE_NAME
url POM_LICENSE_URL
distribution POM_LICENSE_DISTRIBUTION
}
}
uploadArchives {
repositories.mavenDeployer {
configuration = configurations.deployerJars
// disable this otherwise it will fill up the server with stale jars
uniqueVersion = false
repository(url: "scp://updates.jmonkeyengine.org/var/www/updates/maven") {
authentication(userName: "www-updater", privateKey: "private/www-updater.key")
}
pom.project {
name POM_NAME
description POM_DESCRIPTION
url POM_URL
scm {
url POM_SCM_URL
connection POM_SCM_CONNECTION
developerConnection POM_SCM_DEVELOPER_CONNECTION
}
licenses {
license {
name POM_LICENSE_NAME
url POM_LICENSE_URL
distribution POM_LICENSE_DISTRIBUTION
}
}
}
}
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 {

@ -3,7 +3,9 @@ jmeVersion = 3.1.0
# Version used for application and settings folder, no spaces!
jmeMainVersion = 3.1
# 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
buildJavaDoc = true

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

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

@ -31,8 +31,6 @@
*/
package com.jme3.scene.plugins.blender;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@ -40,25 +38,17 @@ import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme3.animation.Animation;
import com.jme3.asset.AssetNotFoundException;
import com.jme3.asset.BlenderKey;
import com.jme3.export.Savable;
import com.jme3.light.Light;
import com.jme3.math.FastMath;
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.plugins.blender.BlenderContext.LoadedDataType;
import com.jme3.scene.plugins.blender.file.BlenderFileException;
import com.jme3.scene.plugins.blender.file.Pointer;
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.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
@ -157,7 +147,6 @@ public abstract class AbstractBlenderHelper {
* @throws BlenderFileException
* and exception is throw when problems with reading a blend file occur
*/
@SuppressWarnings("unchecked")
protected Object loadLibrary(Structure id) throws BlenderFileException {
Pointer pLib = (Pointer) id.getFieldValue("lib");
if (pLib.isNotNull()) {
@ -167,79 +156,21 @@ public abstract class AbstractBlenderHelper {
String path = library.getFieldValue("filepath").toString();
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;
BlenderKey blenderKey = null;
for (String p : pathsToCheck) {
blenderKey = new BlenderKey(p);
blenderKey.setLoadUnlinkedAssets(true);
try {
loadedAsset = blenderContext.getAssetManager().loadAsset(blenderKey);
break;// break if no exception was thrown
} catch (AssetNotFoundException e) {
LOGGER.log(Level.FINEST, "Cannot locate linked resource at path: {0}.", p);
}
BlenderKey blenderKey = new BlenderKey(path);
blenderKey.setLoadUnlinkedAssets(true);
try {
loadedAsset = blenderContext.getAssetManager().loadAsset(blenderKey);
} catch (AssetNotFoundException e) {
LOGGER.log(Level.FINEST, "Cannot locate linked resource at path: {0}.", path);
}
if (loadedAsset != null) {
Map<String, Map<String, Object>> linkedData = loadedAsset.getUserData("linkedData");
for (Entry<String, Map<String, Object>> entry : linkedData.entrySet()) {
String linkedDataFilePath = "this".equals(entry.getKey()) ? path : entry.getKey();
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);
}
blenderContext.getLinkedFeatures().put(linkedDataFilePath, entry.getValue());
}
} else {
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.asset.AssetManager;
import com.jme3.asset.BlenderKey;
import com.jme3.light.Light;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.post.Filter;
import com.jme3.renderer.Camera;
import com.jme3.scene.Node;
import com.jme3.scene.plugins.blender.animations.BlenderAction;
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.BlockCode;
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
@ -77,7 +82,7 @@ public class BlenderContext {
/** The asset manager. */
private AssetManager assetManager;
/** 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.
*/
@ -233,6 +238,7 @@ public class BlenderContext {
* the block header to store
*/
public void addFileBlockHeader(Long oldMemoryAddress, FileBlockHeader fileBlockHeader) {
blocks.add(fileBlockHeader);
fileBlockHeadersByOma.put(oldMemoryAddress, fileBlockHeader);
List<FileBlockHeader> headers = fileBlockHeadersByCode.get(fileBlockHeader.getCode());
if (headers == null) {
@ -242,6 +248,13 @@ public class BlenderContext {
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
* header is not present then null is returned.
@ -332,22 +345,14 @@ public class BlenderContext {
* The method adds linked content to the blender context.
* @param blenderFilePath
* the path of linked blender file
* @param featureName
* the linked feature name
* @param featureGroup
* the linked feature group (ie. scenes, materials, meshes, etc.)
* @param feature
* the linked feature
*/
public void addLinkedFeature(String blenderFilePath, String featureName, Object feature) {
if (feature != null) {
Map<String, Object> linkedFeatures = this.linkedFeatures.get(blenderFilePath);
if (linkedFeatures == null) {
linkedFeatures = new HashMap<String, Object>();
this.linkedFeatures.put(blenderFilePath, linkedFeatures);
}
if (!linkedFeatures.containsKey(featureName)) {
linkedFeatures.put(featureName, feature);
}
}
@Deprecated
public void addLinkedFeature(String blenderFilePath, String featureGroup, Object feature) {
// the method is deprecated and empty at the moment
}
/**
@ -358,9 +363,106 @@ public class BlenderContext {
* the feature name we want to get
* @return linked feature or null if none was found
*/
@SuppressWarnings("unchecked")
public Object getLinkedFeature(String blenderFilePath, String featureName) {
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 {
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;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
@ -41,9 +44,13 @@ import java.util.logging.Logger;
import com.jme3.animation.Animation;
import com.jme3.asset.AssetInfo;
import com.jme3.asset.AssetKey;
import com.jme3.asset.AssetLoader;
import com.jme3.asset.AssetLocator;
import com.jme3.asset.AssetManager;
import com.jme3.asset.BlenderKey;
import com.jme3.asset.ModelKey;
import com.jme3.asset.StreamAssetInfo;
import com.jme3.light.Light;
import com.jme3.math.ColorRGBA;
import com.jme3.post.Filter;
@ -81,22 +88,17 @@ import com.jme3.texture.Texture;
public class BlenderLoader implements AssetLoader {
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
public Spatial load(AssetInfo assetInfo) throws IOException {
try {
this.setup(assetInfo);
BlenderContext blenderContext = this.setup(assetInfo);
AnimationHelper animationHelper = blenderContext.getHelper(AnimationHelper.class);
animationHelper.loadAnimations();
BlenderKey blenderKey = blenderContext.getBlenderKey();
LoadedFeatures loadedFeatures = new LoadedFeatures();
for (FileBlockHeader block : blocks) {
for (FileBlockHeader block : blenderContext.getBlocks()) {
switch (block.getCode()) {
case BLOCK_OB00:
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.");
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.");
@ -220,7 +222,7 @@ public class BlenderLoader implements AssetLoader {
} catch (Exception e) {
throw new IOException("Unexpected importer exception occured: " + e.getLocalizedMessage(), e);
} 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.
* @param structure
* structure of a scene
* @param blenderContext the blender context
* @return scene's node
* @throws BlenderFileException
* 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);
Node result = new Node(structure.getName());
List<Structure> base = ((Structure) structure.getFieldValue("base")).evaluateListBase();
@ -265,7 +268,7 @@ public class BlenderLoader implements AssetLoader {
* @throws BlenderFileException
* 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
ModelKey modelKey = (ModelKey) assetInfo.getKey();
BlenderKey blenderKey;
@ -273,16 +276,15 @@ public class BlenderLoader implements AssetLoader {
blenderKey = (BlenderKey) modelKey;
} else {
blenderKey = new BlenderKey(modelKey.getName());
blenderKey.setAssetRootPath(modelKey.getFolder());
}
// opening stream
BlenderInputStream inputStream = new BlenderInputStream(assetInfo.openStream());
// reading blocks
blocks = new ArrayList<FileBlockHeader>();
List<FileBlockHeader> blocks = new ArrayList<FileBlockHeader>();
FileBlockHeader fileBlock;
blenderContext = new BlenderContext();
BlenderContext blenderContext = new BlenderContext();
blenderContext.setBlenderVersion(inputStream.getVersionNumber());
blenderContext.setAssetManager(assetInfo.getManager());
blenderContext.setInputStream(inputStream);
@ -317,15 +319,19 @@ public class BlenderLoader implements AssetLoader {
if (sceneFileBlock != null) {
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
* that memory (which can be quite large amount).
*/
protected void clear() {
blenderContext = null;
blocks = null;
protected void clear(AssetInfo assetInfo) {
assetInfo.getManager().unregisterLocator(assetInfo.getKey().getName(), LinkedContentLocator.class);
}
/**
@ -362,4 +368,50 @@ public class BlenderLoader implements AssetLoader {
*/
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];
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];
float[] quaternionRotation = new float[] { localRotation.getX(), localRotation.getY(), localRotation.getZ(), localRotation.getW(), };
float[] eulerRotation = localRotation.toAngles(null);
@ -165,6 +165,8 @@ public class Ipo {
// calculating track data
for (int frame = startFrame; frame <= stopFrame; ++frame) {
boolean translationSet = false;
translation[0] = translation[1] = translation[2] = 0;
int index = frame - startFrame;
times[index] = index * timeBetweenFrames;// start + (frame - 1) * timeBetweenFrames;
for (int j = 0; j < bezierCurves.length; ++j) {
@ -173,15 +175,18 @@ public class Ipo {
// LOCATION
case AC_LOC_X:
translation[0] = (float) value;
translationSet = true;
break;
case AC_LOC_Y:
if (swapAxes && value != 0) {
value = -value;
}
translation[yIndex] = (float) value;
translationSet = true;
break;
case AC_LOC_Z:
translation[zIndex] = (float) value;
translationSet = true;
break;
// EULER ROTATION
@ -235,7 +240,11 @@ public class Ipo {
LOGGER.log(Level.WARNING, "Unknown ipo curve type: {0}.", bezierCurves[j].getType());
}
}
translations[index] = localRotation.multLocal(new Vector3f(translation[0], translation[1], translation[2]));
if(translationSet) {
translations[index] = localRotation.multLocal(new Vector3f(translation[0], translation[1], translation[2]));
} else {
translations[index] = new Vector3f();
}
if(boneContext != null) {
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 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) {
if (bone != null) {
@ -187,12 +187,21 @@ public class ConstraintDefinitionIK extends ConstraintDefinition {
this.add(boneContext);
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);
bonesMatrices.add(new DTransform(transform).toMatrix());
Transform transform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD);
localBonesMatrices.add(new DTransform(transform).toMatrix());
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);
boneMatrix = new Matrix(m);
}
bonesMatrices.set(index, boneMatrix);
localBonesMatrices.set(index, boneMatrix);
}
public Matrix getWorldMatrix(int index) {
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);
result = result.mult(bonesMatrices.get(index));
result = result.mult(localBonesMatrices.get(index));
return new Matrix(result);
}
}

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

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

@ -168,9 +168,11 @@ public final class MaterialContext implements Savable {
this.setTexture(material, combinedTexture.getMappingType(), combinedTexture.getResultTexture());
List<Vector2f> uvs = combinedTexture.getResultUVS();
VertexBuffer uvCoordsBuffer = new VertexBuffer(TextureHelper.TEXCOORD_TYPES[textureIndex++]);
uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, BufferUtils.createFloatBuffer(uvs.toArray(new Vector2f[uvs.size()])));
geometry.getMesh().setBuffer(uvCoordsBuffer);
if(uvs != null && uvs.size() > 0) {
VertexBuffer uvCoordsBuffer = new VertexBuffer(TextureHelper.TEXCOORD_TYPES[textureIndex++]);
uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, BufferUtils.createFloatBuffer(uvs.toArray(new Vector2f[uvs.size()])));
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 {
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,7 +46,9 @@ import com.jme3.scene.plugins.blender.objects.Properties;
*/
public class TemporalMesh extends Geometry {
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. */
protected final BlenderContext blenderContext;
@ -530,7 +532,11 @@ public class TemporalMesh extends Geometry {
for (Entry<String, Integer> entry : boneIndexes.entrySet()) {
if (vertexGroupsForVertex.containsKey(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());
}
}

@ -2,6 +2,7 @@ package com.jme3.scene.plugins.blender.modifiers;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
@ -129,13 +130,18 @@ public class SubdivisionSurfaceModifier extends Modifier {
for (int i = 0; i < temporalMesh.getVertexCount(); ++i) {
// finding adjacent edges that were created by dividing original edges
List<Edge> adjacentOriginalEdges = new ArrayList<Edge>();
for (Edge edge : temporalMesh.getAdjacentEdges(i)) {
if (verticesOnOriginalEdges.contains(edge.getFirstIndex()) || verticesOnOriginalEdges.contains(edge.getSecondIndex())) {
adjacentOriginalEdges.add(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)) {
if (verticesOnOriginalEdges.contains(edge.getFirstIndex()) || verticesOnOriginalEdges.contains(edge.getSecondIndex())) {
adjacentOriginalEdges.add(edge);
}
}
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
}
creasePoints.add(new CreasePoint(i, boundaryVertices.contains(i), adjacentOriginalEdges, temporalMesh));
}
Vector3f[] averageVert = new Vector3f[temporalMesh.getVertexCount()];
@ -174,23 +180,25 @@ public class SubdivisionSurfaceModifier extends Modifier {
}
for (int i = 0; i < averageVert.length; ++i) {
Vector3f v = temporalMesh.getVertices().get(i);
averageVert[i].divideLocal(averageCount[i]);
// computing translation vector
Vector3f t = averageVert[i].subtract(v);
if (!boundaryVertices.contains(i)) {
t.multLocal(4 / (float) averageCount[i]);
}
// moving the vertex
v.addLocal(t);
if(averageVert[i] != null && averageCount[i] > 0) {
Vector3f v = temporalMesh.getVertices().get(i);
averageVert[i].divideLocal(averageCount[i]);
// computing translation vector
Vector3f t = averageVert[i].subtract(v);
if (!boundaryVertices.contains(i)) {
t.multLocal(4 / (float) averageCount[i]);
}
// applying crease weight if neccessary
CreasePoint creasePoint = creasePoints.get(i);
if (creasePoint.getTarget() != null && creasePoint.getWeight() != 0) {
t = creasePoint.getTarget().subtractLocal(v).multLocal(creasePoint.getWeight());
// moving the vertex
v.addLocal(t);
// applying crease weight if neccessary
CreasePoint creasePoint = creasePoints.get(i);
if (creasePoint.getTarget() != null && creasePoint.getWeight() != 0) {
t = creasePoint.getTarget().subtractLocal(v).multLocal(creasePoint.getWeight());
v.addLocal(t);
}
}
}
}

@ -158,6 +158,9 @@ public class CombinedTexture {
} else {
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;
} else {
TemporalMesh temporalMesh = (TemporalMesh) blenderContext.getLoadedFeature(geometriesOMA, LoadedDataType.TEMPORAL_MESH);

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

@ -811,39 +811,72 @@ extern "C" {
/*
* Class: com_jme3_bullet_objects_PhysicsRigidBody
* Method: getAngularFactor
* Signature: (J)F
* Signature: (JLcom/jme3/math/Vector3f;)V
*/
JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getAngularFactor
(JNIEnv *env, jobject object, jlong bodyId) {
JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getAngularFactor
(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 0;
return;
}
return body->getAngularFactor().getX();
jmeBulletUtil::convert(env, &body->getAngularFactor(), factor);
}
/*
* Class: com_jme3_bullet_objects_PhysicsRigidBody
* Method: setAngularFactor
* Signature: (JF)V
* Signature: (JLcom/jme3/math/Vector3f;)V
*/
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);
if (body == NULL) {
jclass newExc = env->FindClass("java/lang/NullPointerException");
env->ThrowNew(newExc, "The native object does not exist.");
return;
}
btVector3 vec1 = btVector3();
vec1.setX(value);
vec1.setY(value);
vec1.setZ(value);
body->setAngularFactor(vec1);
btVector3 vec = btVector3();
jmeBulletUtil::convert(env, factor, &vec);
body->setAngularFactor(vec);
}
/*
* 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
}
#endif

@ -396,18 +396,35 @@ JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getAngula
/*
* Class: com_jme3_bullet_objects_PhysicsRigidBody
* Method: getAngularFactor
* Signature: (J)F
* Signature: (JLcom/jme3/math/Vector3f;)V
*/
JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getAngularFactor
(JNIEnv *, jobject, jlong);
JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsRigidBody_getAngularFactor
(JNIEnv *, jobject, jlong, jobject);
/*
* Class: com_jme3_bullet_objects_PhysicsRigidBody
* Method: setAngularFactor
* Signature: (JF)V
* Signature: (JLcom/jme3/math/Vector3f;)V
*/
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
}

@ -351,7 +351,7 @@ void jmeBulletUtil::addResult(JNIEnv* env, jobject resultlist, btVector3* hitnor
env->SetFloatField(singleresult, jmeClasses::PhysicsRay_hitfraction, m_hitFraction);
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()) {
env->Throw(env->ExceptionOccurred());
return;
@ -371,7 +371,7 @@ void jmeBulletUtil::addSweepResult(JNIEnv* env, jobject resultlist, btVector3* h
env->SetFloatField(singleresult, jmeClasses::PhysicsSweep_hitfraction, m_hitFraction);
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()) {
env->Throw(env->ExceptionOccurred());
return;

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

@ -627,16 +627,44 @@ public class PhysicsRigidBody extends PhysicsCollisionObject {
private native float getAngularSleepingThreshold(long objectId);
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) {
setAngularFactor(objectId, factor);
setAngularFactor(objectId, new Vector3f(factor, factor, factor));
}
public void setAngularFactor(Vector3f factor) {
setAngularFactor(objectId, factor);
}
private native void setAngularFactor(long objectId, Vector3f factor);
public Vector3f getLinearFactor() {
Vector3f vec = new Vector3f();
getLinearFactor(objectId, vec);
return vec;
}
private native void setAngularFactor(long objectId, float factor);
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
@ -673,7 +701,13 @@ public class PhysicsRigidBody extends PhysicsCollisionObject {
capsule.write(getGravity(), "gravity", Vector3f.ZERO);
capsule.write(getFriction(), "friction", 0.5f);
capsule.write(getRestitution(), "restitution", 0);
capsule.write(getAngularFactor(), "angularFactor", 1);
Vector3f angularFactor = getAngularFactor(null);
if (angularFactor.x == angularFactor.y && angularFactor.y == angularFactor.z) {
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(getLinearDamping(), "linearDamping", 0);
@ -703,7 +737,13 @@ public class PhysicsRigidBody extends PhysicsCollisionObject {
setKinematic(capsule.readBoolean("kinematic", false));
setRestitution(capsule.readFloat("restitution", 0));
setAngularFactor(capsule.readFloat("angularFactor", 1));
Vector3f angularFactor = (Vector3f) capsule.readSavable("angularFactor", null);
if(angularFactor == null) {
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));
setSleepingThresholds(capsule.readFloat("linearSleepingThreshold", 0.8f), capsule.readFloat("angularSleepingThreshold", 1.0f));
setCcdMotionThreshold(capsule.readFloat("ccdMotionThreshold", 0));

@ -10,57 +10,30 @@ sourceSets {
srcDir 'src/tools/java'
}
}
}
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.ajoberstar:gradle-git:1.0.0-rc.1'
test {
java {
srcDir 'src/test/java'
}
}
}
import java.text.SimpleDateFormat
import org.ajoberstar.grgit.*
task updateVersion << {
def verfile = file('src/main/java/com/jme3/system/JmeVersion.java')
def jmeGitHash
def jmeShortGitHash
def jmeBuildDate
def jmeBranchName
try {
def grgit = Grgit.open(project.file('.').parent)
jmeGitHash = grgit.head().id
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"
task updateVersionPropertiesFile << {
def verfile = file('src/main/resources/com/jme3/system/version.properties')
verfile.text = "# THIS IS AN AUTO-GENERATED FILE..\n" +
"# DO NOT MODIFY!\n" +
"build.date=${jmeBuildDate}\n" +
"git.revision=${jmeRevision}\n" +
"git.branch=${jmeBranchName}\n" +
"git.hash=${jmeGitHash}\n" +
"git.hash.short=${jmeShortGitHash}\n" +
"git.tag=${jmeGitTag}\n" +
"name.full=jMonkeyEngine ${jmeFullVersion}\n" +
"version.full=${jmeFullVersion}\n" +
"version.number=${jmeVersion}\n" +
"version.tag=${jmeVersionTag}"
}
compileJava.dependsOn(updateVersion)
compileJava.dependsOn(updateVersionPropertiesFile)
dependencies {
}

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

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

@ -31,22 +31,20 @@
*/
package com.jme3.audio.openal;
import com.jme3.audio.AudioSource.Status;
import com.jme3.audio.*;
import com.jme3.audio.AudioSource.Status;
import com.jme3.math.Vector3f;
import com.jme3.util.BufferUtils;
import com.jme3.util.NativeObjectManager;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
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 {
@ -102,16 +100,6 @@ public class ALAudioRenderer implements AudioRenderer, Runnable {
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
ArrayList<Integer> channelList = new ArrayList<Integer>();
for (int i = 0; i < MAX_NUM_CHANNELS; i++) {
@ -131,7 +119,25 @@ public class ALAudioRenderer implements AudioRenderer, Runnable {
ib = BufferUtils.createIntBuffer(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
// 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);
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
ib.position(0).limit(1);

@ -41,6 +41,7 @@ import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import com.jme3.math.*;
import com.jme3.scene.Mesh;
import com.jme3.scene.Spatial;
import com.jme3.util.TempVars;
import java.io.IOException;
import java.nio.FloatBuffer;
@ -313,7 +314,7 @@ public class BoundingBox extends BoundingVolume {
transMatrix.absoluteLocal();
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);
// Assign the biggest rotations after scales.
box.xExtent = FastMath.abs(vars.vect2.getX());
@ -593,18 +594,7 @@ public class BoundingBox extends BoundingVolume {
* @see BoundingVolume#intersectsSphere(com.jme3.bounding.BoundingSphere)
*/
public boolean intersectsSphere(BoundingSphere bs) {
assert Vector3f.isValidVector(center) && Vector3f.isValidVector(bs.center);
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;
return bs.intersectsBoundingBox(this);
}
/**
@ -790,6 +780,7 @@ public class BoundingBox extends BoundingVolume {
}
}
@Override
public int collideWith(Collidable other, CollisionResults results) {
if (other instanceof Ray) {
Ray ray = (Ray) other;
@ -802,6 +793,15 @@ public class BoundingBox extends BoundingVolume {
return 1;
}
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 {
throw new UnsupportedCollisionException("With: " + other.getClass().getSimpleName());
}
@ -818,6 +818,8 @@ public class BoundingBox extends BoundingVolume {
return 1;
}
return 0;
} else if (other instanceof BoundingVolume) {
return intersects((BoundingVolume) other) ? 1 : 0;
} else {
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.JmeImporter;
import com.jme3.math.*;
import com.jme3.scene.Spatial;
import com.jme3.util.BufferUtils;
import com.jme3.util.TempVars;
import java.io.IOException;
@ -670,15 +671,7 @@ public class BoundingSphere extends BoundingVolume {
* @see com.jme.bounding.BoundingVolume#intersectsSphere(com.jme.bounding.BoundingSphere)
*/
public boolean intersectsSphere(BoundingSphere bs) {
assert Vector3f.isValidVector(center) && Vector3f.isValidVector(bs.center);
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;
return Intersection.intersect(bs, center, radius);
}
/*
@ -687,18 +680,7 @@ public class BoundingSphere extends BoundingVolume {
* @see com.jme.bounding.BoundingVolume#intersectsBoundingBox(com.jme.bounding.BoundingBox)
*/
public boolean intersectsBoundingBox(BoundingBox bb) {
assert Vector3f.isValidVector(center) && Vector3f.isValidVector(bb.center);
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;
return Intersection.intersect(bb, center, radius);
}
/*
@ -1013,17 +995,29 @@ public class BoundingSphere extends BoundingVolume {
} else if (other instanceof Triangle){
Triangle t = (Triangle) other;
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 {
throw new UnsupportedCollisionException();
}
}
@Override public int collideWith(Collidable other) {
@Override
public int collideWith(Collidable other) {
if (other instanceof Ray) {
Ray ray = (Ray) other;
return collideWithRay(ray);
} else if (other instanceof Triangle){
return super.collideWith(other);
} else if (other instanceof BoundingVolume) {
return intersects((BoundingVolume)other) ? 1 : 0;
} else {
throw new UnsupportedCollisionException();
}

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

@ -41,10 +41,56 @@ import static java.lang.Math.min;
/**
* This class includes some utility methods for computing intersection
* between bounding volumes and triangles.
*
* @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) {
minMax.set(x0, x0, 0);
if (x1 < minMax.x) {

@ -702,7 +702,9 @@ public class Cinematic extends AbstractCinematicEvent implements AppState {
dispose();
cinematicEvents.clear();
timeLine.clear();
eventsData.clear();
if (eventsData != null) {
eventsData.clear();
}
}
/**

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

@ -48,6 +48,7 @@ import com.jme3.math.Vector3f;
import com.jme3.scene.CollisionData;
import com.jme3.scene.Mesh;
import com.jme3.scene.Mesh.Mode;
import com.jme3.scene.VertexBuffer;
import com.jme3.scene.VertexBuffer.Type;
import com.jme3.scene.mesh.IndexBuffer;
import com.jme3.scene.mesh.VirtualIndexBuffer;
@ -114,8 +115,13 @@ public class BIHTree implements CollisionData {
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();
FloatBuffer vb = (FloatBuffer) vBuffer.getData();
if (ib == null) {
ib = new VirtualIndexBuffer(mesh.getVertexCount(), mesh.getMode());
} else if (mesh.getMode() != Mode.Triangles) {

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

@ -72,8 +72,10 @@ public class DefaultJoystickAxis implements JoystickAxis {
* @param negativeMapping The mapping to receive events when the axis is positive
*/
public void assignAxis(String positiveMapping, String negativeMapping){
inputManager.addMapping(positiveMapping, new JoyAxisTrigger(parent.getJoyId(), axisIndex, false));
inputManager.addMapping(negativeMapping, new JoyAxisTrigger(parent.getJoyId(), axisIndex, true));
if (axisIndex != -1) {
inputManager.addMapping(positiveMapping, new JoyAxisTrigger(parent.getJoyId(), axisIndex, false));
inputManager.addMapping(negativeMapping, new JoyAxisTrigger(parent.getJoyId(), axisIndex, true));
}
}
/**
@ -133,6 +135,14 @@ public class DefaultJoystickAxis implements JoystickAxis {
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
public String toString(){
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
* Dispatcher.
* Unregisters the FlyByCamera from the event Dispatcher.
*/
public void unregisterInput(){

@ -39,6 +39,7 @@ import com.jme3.math.FastMath;
import com.jme3.math.Vector2f;
import com.jme3.util.IntMap;
import com.jme3.util.IntMap.Entry;
import com.jme3.util.SafeArrayList;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.logging.Level;
@ -96,16 +97,15 @@ public class InputManager implements RawInputListener {
private boolean eventsPermitted = false;
private boolean mouseVisible = true;
private boolean safeMode = false;
private float axisDeadZone = 0.05f;
private Vector2f cursorPos = new Vector2f();
private float globalAxisDeadZone = 0.05f;
private final Vector2f cursorPos = new Vector2f();
private Joystick[] joysticks;
private final IntMap<ArrayList<Mapping>> bindings = new IntMap<ArrayList<Mapping>>();
private final HashMap<String, Mapping> mappings = new HashMap<String, Mapping>();
private final IntMap<Long> pressedButtons = new IntMap<Long>();
private final IntMap<Float> axisValues = new IntMap<Float>();
private ArrayList<RawInputListener> rawListeners = new ArrayList<RawInputListener>();
private RawInputListener[] rawListenerArray = null;
private ArrayList<InputEvent> inputQueue = new ArrayList<InputEvent>();
private final SafeArrayList<RawInputListener> rawListeners = new SafeArrayList<RawInputListener>(RawInputListener.class);
private final ArrayList<InputEvent> inputQueue = new ArrayList<InputEvent>();
private static class Mapping {
@ -248,8 +248,8 @@ public class InputManager implements RawInputListener {
}
}
private void invokeAnalogsAndActions(int hash, float value, boolean applyTpf) {
if (value < axisDeadZone) {
private void invokeAnalogsAndActions(int hash, float value, float effectiveDeadZone, boolean applyTpf) {
if (value < effectiveDeadZone) {
invokeAnalogs(hash, value, !applyTpf);
return;
}
@ -287,12 +287,14 @@ public class InputManager implements RawInputListener {
/**
* Callback from RawInputListener. Do not use.
*/
@Override
public void beginInput() {
}
/**
* Callback from RawInputListener. Do not use.
*/
@Override
public void endInput() {
}
@ -304,17 +306,18 @@ public class InputManager implements RawInputListener {
int joyId = evt.getJoyIndex();
int axis = evt.getAxisIndex();
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 hash2 = JoyAxisTrigger.joyAxisHash(joyId, axis, false);
Float val1 = axisValues.get(hash1);
Float val2 = axisValues.get(hash2);
if (val1 != null && val1.floatValue() > axisDeadZone) {
if (val1 != null && val1 > effectiveDeadZone) {
invokeActions(hash1, false);
}
if (val2 != null && val2.floatValue() > axisDeadZone) {
if (val2 != null && val2 > effectiveDeadZone) {
invokeActions(hash2, false);
}
@ -328,11 +331,11 @@ public class InputManager implements RawInputListener {
// Clear the reverse direction's actions in case we
// crossed center too quickly
Float otherVal = axisValues.get(otherHash);
if (otherVal != null && otherVal.floatValue() > axisDeadZone) {
if (otherVal != null && otherVal > effectiveDeadZone) {
invokeActions(otherHash, false);
}
invokeAnalogsAndActions(hash, -value, true);
invokeAnalogsAndActions(hash, -value, effectiveDeadZone, true);
axisValues.put(hash, -value);
axisValues.remove(otherHash);
} else {
@ -342,11 +345,11 @@ public class InputManager implements RawInputListener {
// Clear the reverse direction's actions in case we
// crossed center too quickly
Float otherVal = axisValues.get(otherHash);
if (otherVal != null && otherVal.floatValue() > axisDeadZone) {
if (otherVal != null && otherVal > effectiveDeadZone) {
invokeActions(otherHash, false);
}
invokeAnalogsAndActions(hash, value, true);
invokeAnalogsAndActions(hash, value, effectiveDeadZone, true);
axisValues.put(hash, value);
axisValues.remove(otherHash);
}
@ -355,6 +358,7 @@ public class InputManager implements RawInputListener {
/**
* Callback from RawInputListener. Do not use.
*/
@Override
public void onJoyAxisEvent(JoyAxisEvent evt) {
if (!eventsPermitted) {
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.
*/
@Override
public void onJoyButtonEvent(JoyButtonEvent evt) {
if (!eventsPermitted) {
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) {
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) {
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) {
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.
*/
@Override
public void onMouseMotionEvent(MouseMotionEvent evt) {
if (!eventsPermitted) {
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.
*/
@Override
public void onMouseButtonEvent(MouseButtonEvent evt) {
if (!eventsPermitted) {
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.
*/
@Override
public void onKeyEvent(KeyInputEvent evt) {
if (!eventsPermitted) {
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.
*/
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.
*/
public float getAxisDeadZone() {
return axisDeadZone;
return globalAxisDeadZone;
}
/**
@ -721,7 +729,6 @@ public class InputManager implements RawInputListener {
*/
public void addRawInputListener(RawInputListener listener) {
rawListeners.add(listener);
rawListenerArray = null;
}
/**
@ -734,7 +741,6 @@ public class InputManager implements RawInputListener {
*/
public void removeRawInputListener(RawInputListener listener) {
rawListeners.remove(listener);
rawListenerArray = null;
}
/**
@ -744,13 +750,6 @@ public class InputManager implements RawInputListener {
*/
public void clearRawInputListeners() {
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() {
int queueSize = inputQueue.size();
RawInputListener[] array = getRawListenerArray();
RawInputListener[] array = rawListeners.getArray();
for (RawInputListener listener : array) {
listener.beginInput();

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

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

@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2012 jMonkeyEngine
* Copyright (c) 2009-2012, 2015 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -32,10 +32,12 @@
package com.jme3.light;
import com.jme3.bounding.BoundingBox;
import com.jme3.bounding.BoundingSphere;
import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.scene.Spatial;
@ -53,9 +55,35 @@ public class DirectionalLight extends Light {
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
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.
*/
public void setDirection(Vector3f dir){
public final void setDirection(Vector3f dir){
direction.set(dir);
if (!direction.isUnitVector()) {
direction.normalizeLocal();
@ -89,6 +117,11 @@ public class DirectionalLight extends Light {
return true;
}
@Override
public boolean intersectsSphere(BoundingSphere sphere, TempVars vars) {
return true;
}
@Override
public boolean intersectsFrustum(Camera camera, TempVars vars) {
return true;

@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2012 jMonkeyEngine
* Copyright (c) 2009-2012, 2015 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -32,6 +32,7 @@
package com.jme3.light;
import com.jme3.bounding.BoundingBox;
import com.jme3.bounding.BoundingSphere;
import com.jme3.export.*;
import com.jme3.math.ColorRGBA;
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
@ -115,6 +116,13 @@ public abstract class Light implements Savable, Cloneable {
boolean frustumCheckNeeded = true;
boolean intersectsFrustum = false;
protected Light() {
}
protected Light(ColorRGBA color) {
setColor(color);
}
/**
* 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);
/**
* 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},
* {@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.
*
* 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.BoundingSphere;
import com.jme3.bounding.BoundingVolume;
import com.jme3.bounding.Intersection;
import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import com.jme3.math.FastMath;
import com.jme3.math.Plane;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.scene.Spatial;
@ -62,6 +63,52 @@ public class PointLight extends Light {
protected float radius = 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
public void computeLastDistance(Spatial owner) {
if (owner.getWorldBound() != null) {
@ -88,7 +135,7 @@ public class PointLight extends 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);
}
@ -115,13 +162,13 @@ public class PointLight extends Light {
*
* @throws IllegalArgumentException If radius is negative
*/
public void setRadius(float radius) {
public final void setRadius(float radius) {
if (radius < 0) {
throw new IllegalArgumentException("Light radius cannot be negative");
}
this.radius = radius;
if (radius != 0) {
this.invRadius = 1 / radius;
if (radius != 0f) {
this.invRadius = 1f / radius;
} else {
this.invRadius = 0;
}
@ -146,9 +193,17 @@ public class PointLight extends Light {
return true;
} else {
// Sphere v. box collision
return FastMath.abs(box.getCenter().x - position.x) < radius + box.getXExtent()
&& FastMath.abs(box.getCenter().y - position.y) < radius + box.getYExtent()
&& FastMath.abs(box.getCenter().z - position.z) < radius + box.getZExtent();
return Intersection.intersect(box, position, radius);
}
}
@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.
*
* Redistribution and use in source and binary forms, with or without
@ -32,8 +32,11 @@
package com.jme3.light;
import com.jme3.bounding.BoundingBox;
import com.jme3.bounding.BoundingSphere;
import com.jme3.bounding.BoundingVolume;
import com.jme3.bounding.Intersection;
import com.jme3.export.*;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Plane;
import com.jme3.math.Vector3f;
@ -44,36 +47,121 @@ import java.io.IOException;
/**
* Represents a spot light.
* A spot light emmit a cone of light from a position and in a direction.
* It can be used to fake torch lights or car's lights.
* A spot light emits a cone of light from a position and in a direction.
* It can be used to fake torch lights or cars' lights.
* <p>
* 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
* 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.
* the spot inner angle determin 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 light intensity slowly decrease between the inner cone and the outer cone.
* the spot inner angle determines the cone of light where light has full influence.
* the spot outer angle determines the cone global cone of light of the spot light.
* the light intensity slowly decreases between the inner cone and the outer cone.
* @author Nehon
*/
public class SpotLight extends Light {
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 spotOuterAngle = FastMath.QUARTER_PI / 6;
protected float spotRange = 100;
protected float invSpotRange = 1f / 100;
protected float packedAngleCos=0;
protected float packedAngleCos = 0;
protected float outerAngleCosSqr, outerAngleSinSqr;
protected float outerAngleSinRcp, outerAngleSin, outerAngleCos;
/**
* Creates a SpotLight.
*/
public SpotLight() {
super();
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() {
float innerCos = FastMath.cos(spotInnerAngle);
outerAngleCos = FastMath.cos(spotOuterAngle);
@ -102,9 +190,7 @@ public class SpotLight extends Light {
if (this.spotRange > 0f) {
// Check spot range first.
// Sphere v. box collision
if (FastMath.abs(box.getCenter().x - position.x) >= spotRange + box.getXExtent()
|| FastMath.abs(box.getCenter().y - position.y) >= spotRange + box.getYExtent()
|| FastMath.abs(box.getCenter().z - position.z) >= spotRange + box.getZExtent()) {
if (!Intersection.intersect(box, position, spotRange)) {
return false;
}
}
@ -140,8 +226,48 @@ public class SpotLight extends Light {
}
@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));
for (int i = 5; i >= 0; i--) {
//check origin against the plane
@ -189,7 +315,7 @@ public class SpotLight extends Light {
return direction;
}
public void setDirection(Vector3f direction) {
public final void setDirection(Vector3f direction) {
this.direction.set(direction);
}
@ -197,7 +323,7 @@ public class SpotLight extends Light {
return position;
}
public void setPosition(Vector3f position) {
public final void setPosition(Vector3f position) {
this.position.set(position);
}

@ -69,7 +69,7 @@ import java.util.logging.Logger;
* Setting the parameters can modify the behavior of a
* shader.
* <p/>
*
*
* @author Kirill Vainer
*/
public class Material implements CloneableSmartAsset, Cloneable, Savable {
@ -146,7 +146,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
public String getName() {
return name;
}
/**
* This method sets the name of the material.
* The name is not the same as the asset name.
@ -222,11 +222,11 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
}
/**
* Compares two materials and returns true if they are equal.
* Compares two materials and returns true if they are equal.
* This methods compare definition, parameters, additional render states.
* Since materials are mutable objects, implementing equals() properly is not possible,
* Since materials are mutable objects, implementing equals() properly is not possible,
* hence the name contentEquals().
*
*
* @param otherObj the material to compare to this material
* @return true if the materials are equal.
*/
@ -234,15 +234,15 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
if (!(otherObj instanceof Material)) {
return false;
}
Material other = (Material) otherObj;
// Early exit if the material are the same object
if (this == other) {
return true;
}
// Check material definition
// Check material definition
if (this.getMaterialDef() != other.getMaterialDef()) {
return false;
}
@ -251,12 +251,12 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
if (this.paramValues.size() != other.paramValues.size()) {
return false;
}
// Checking technique
if (this.technique != null || other.technique != null) {
// Techniques are considered equal if their names are the same
// E.g. if user chose custom technique for one material but
// uses default technique for other material, the materials
// E.g. if user chose custom technique for one material but
// uses default technique for other material, the materials
// are not equal.
String thisDefName = this.technique != null ? this.technique.getDef().getName() : "Default";
String otherDefName = other.technique != null ? other.technique.getDef().getName() : "Default";
@ -290,7 +290,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
return false;
}
}
return true;
}
@ -305,7 +305,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
hash = 29 * hash + (this.additionalState != null ? this.additionalState.contentHashCode() : 0);
return hash;
}
/**
* Returns the currently active technique.
* <p>
@ -436,7 +436,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
public Collection<MatParam> getParams() {
return paramValues.values();
}
/**
* Returns the ListMap of all parameters set on this material.
*
@ -473,7 +473,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
*/
public void setParam(String name, VarType type, Object value) {
checkSetParam(type, name);
if (type.isTextureType()) {
setTextureParam(name, type, (Texture)value);
} else {
@ -501,7 +501,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
if (matParam == null) {
return;
}
paramValues.remove(name);
if (matParam instanceof MatParamTexture) {
int texUnit = ((MatParamTexture) matParam).getUnit();
@ -728,7 +728,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
renderer.renderMesh(mesh, lodLevel, 1, null);
}
}
/**
* Uploads the lights in the light list as two uniform arrays.<br/><br/> *
* <p>
@ -747,30 +747,30 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
return 0;
}
Uniform lightData = shader.getUniform("g_LightData");
lightData.setVector4Length(numLights * 3);//8 lights * max 3
Uniform lightData = shader.getUniform("g_LightData");
lightData.setVector4Length(numLights * 3);//8 lights * max 3
Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
if (startIndex != 0) {
if (startIndex != 0) {
// apply additive blending for 2nd and future passes
rm.getRenderer().applyRenderState(additiveLight);
ambientColor.setValue(VarType.Vector4, ColorRGBA.Black);
ambientColor.setValue(VarType.Vector4, ColorRGBA.Black);
}else{
ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList,true));
}
int lightDataIndex = 0;
TempVars vars = TempVars.get();
Vector4f tmpVec = vars.vect4f1;
int curIndex;
int endIndex = numLights + startIndex;
for (curIndex = startIndex; curIndex < endIndex && curIndex < lightList.size(); curIndex++) {
Light l = lightList.get(curIndex);
Light l = lightList.get(curIndex);
if(l.getType() == Light.Type.Ambient){
endIndex++;
endIndex++;
continue;
}
ColorRGBA color = l.getColor();
@ -781,14 +781,14 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
l.getType().getId(),
lightDataIndex);
lightDataIndex++;
switch (l.getType()) {
case Directional:
DirectionalLight dl = (DirectionalLight) l;
Vector3f dir = dl.getDirection();
Vector3f dir = dl.getDirection();
//Data directly sent in view space to avoid a matrix mult for each pixel
tmpVec.set(dir.getX(), dir.getY(), dir.getZ(), 0.0f);
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
// tmpVec.divideLocal(tmpVec.w);
// tmpVec.normalizeLocal();
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), -1, lightDataIndex);
@ -802,7 +802,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
Vector3f pos = pl.getPosition();
float invRadius = pl.getInvRadius();
tmpVec.set(pos.getX(), pos.getY(), pos.getZ(), 1.0f);
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
//tmpVec.divideLocal(tmpVec.w);
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), invRadius, lightDataIndex);
lightDataIndex++;
@ -810,37 +810,37 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
lightData.setVector4InArray(0,0,0,0, lightDataIndex);
lightDataIndex++;
break;
case Spot:
case Spot:
SpotLight sl = (SpotLight) l;
Vector3f pos2 = sl.getPosition();
Vector3f dir2 = sl.getDirection();
float invRange = sl.getInvSpotRange();
float spotAngleCos = sl.getPackedAngleCos();
tmpVec.set(pos2.getX(), pos2.getY(), pos2.getZ(), 1.0f);
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
// tmpVec.divideLocal(tmpVec.w);
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), invRange, lightDataIndex);
lightDataIndex++;
//We transform the spot direction in view space here to save 5 varying later in the lighting shader
//one vec4 less and a vec4 that becomes a vec3
//the downside is that spotAngleCos decoding happens now in the frag shader.
tmpVec.set(dir2.getX(), dir2.getY(), dir2.getZ(), 0.0f);
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
tmpVec.normalizeLocal();
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), spotAngleCos, lightDataIndex);
lightDataIndex++;
break;
break;
default:
throw new UnsupportedOperationException("Unknown type of light: " + l.getType());
}
}
vars.release();
vars.release();
//Padding of unsued buffer space
while(lightDataIndex < numLights * 3) {
lightData.setVector4InArray(0f, 0f, 0f, 0f, lightDataIndex);
lightDataIndex++;
}
lightDataIndex++;
}
return curIndex;
}
@ -887,10 +887,10 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
case Directional:
DirectionalLight dl = (DirectionalLight) l;
Vector3f dir = dl.getDirection();
//FIXME : there is an inconstency here due to backward
//FIXME : there is an inconstency here due to backward
//compatibility of the lighting shader.
//The directional light direction is passed in the
//LightPosition uniform. The lighting shader needs to be
//The directional light direction is passed in the
//LightPosition uniform. The lighting shader needs to be
//reworked though in order to fix this.
tmpLightPosition.set(dir.getX(), dir.getY(), dir.getZ(), -1);
lightPos.setValue(VarType.Vector4, tmpLightPosition);
@ -987,11 +987,11 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
for (TechniqueDef techDef : techDefs) {
if (rendererCaps.containsAll(techDef.getRequiredCaps())) {
// use the first one that supports all the caps
tech = new Technique(this, techDef);
tech = new Technique(this, techDef);
techniques.put(name, tech);
if(tech.getDef().getLightMode() == renderManager.getPreferredLightMode() ||
tech.getDef().getLightMode() == LightMode.Disable){
break;
break;
}
}
lastTech = techDef;
@ -1078,7 +1078,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
Uniform u = uniforms.getValue(i);
if (!u.isSetByCurrentMaterial()) {
if (u.getName().charAt(0) != 'g') {
// Don't reset world globals!
// Don't reset world globals!
// The benefits gained from this are very minimal
// and cause lots of matrix -> FloatBuffer conversions.
u.clearValue();
@ -1093,21 +1093,21 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
* <p>
* The material is rendered as follows:
* <ul>
* <li>Determine which technique to use to render the material -
* either what the user selected via
* {@link #selectTechnique(java.lang.String, com.jme3.renderer.RenderManager)
* Material.selectTechnique()},
* or the first default technique that the renderer supports
* <li>Determine which technique to use to render the material -
* either what the user selected via
* {@link #selectTechnique(java.lang.String, com.jme3.renderer.RenderManager)
* Material.selectTechnique()},
* or the first default technique that the renderer supports
* (based on the technique's {@link TechniqueDef#getRequiredCaps() requested rendering capabilities})<ul>
* <li>If the technique has been changed since the last frame, then it is notified via
* {@link Technique#makeCurrent(com.jme3.asset.AssetManager, boolean, java.util.EnumSet)
* Technique.makeCurrent()}.
* If the technique wants to use a shader to render the model, it should load it at this part -
* the shader should have all the proper defines as declared in the technique definition,
* including those that are bound to material parameters.
* The technique can re-use the shader from the last frame if
* <li>If the technique has been changed since the last frame, then it is notified via
* {@link Technique#makeCurrent(com.jme3.asset.AssetManager, boolean, java.util.EnumSet)
* Technique.makeCurrent()}.
* If the technique wants to use a shader to render the model, it should load it at this part -
* the shader should have all the proper defines as declared in the technique definition,
* including those that are bound to material parameters.
* The technique can re-use the shader from the last frame if
* no changes to the defines occurred.</li></ul>
* <li>Set the {@link RenderState} to use for rendering. The render states are
* <li>Set the {@link RenderState} to use for rendering. The render states are
* applied in this order (later RenderStates override earlier RenderStates):<ol>
* <li>{@link TechniqueDef#getRenderState() Technique Definition's RenderState}
* - i.e. specific renderstate that is required for the shader.</li>
@ -1120,22 +1120,22 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
* <li>Uniforms bound to material parameters are updated based on the current material parameter values.</li>
* <li>Uniforms bound to world parameters are updated from the RenderManager.
* Internally {@link UniformBindingManager} is used for this task.</li>
* <li>Uniforms bound to textures will cause the texture to be uploaded as necessary.
* <li>Uniforms bound to textures will cause the texture to be uploaded as necessary.
* The uniform is set to the texture unit where the texture is bound.</li></ul>
* <li>If the technique uses a shader, the model is then rendered according
* <li>If the technique uses a shader, the model is then rendered according
* to the lighting mode specified on the technique definition.<ul>
* <li>{@link LightMode#SinglePass single pass light mode} fills the shader's light uniform arrays
* <li>{@link LightMode#SinglePass single pass light mode} fills the shader's light uniform arrays
* with the first 4 lights and renders the model once.</li>
* <li>{@link LightMode#MultiPass multi pass light mode} light mode renders the model multiple times,
* for the first light it is rendered opaque, on subsequent lights it is
* <li>{@link LightMode#MultiPass multi pass light mode} light mode renders the model multiple times,
* for the first light it is rendered opaque, on subsequent lights it is
* rendered with {@link BlendMode#AlphaAdditive alpha-additive} blending and depth writing disabled.</li>
* </ul>
* <li>For techniques that do not use shaders,
* <li>For techniques that do not use shaders,
* fixed function OpenGL is used to render the model (see {@link GL1Renderer} interface):<ul>
* <li>OpenGL state ({@link FixedFuncBinding}) that is bound to material parameters is updated. </li>
* <li>The texture set on the material is uploaded and bound.
* <li>The texture set on the material is uploaded and bound.
* Currently only 1 texture is supported for fixed function techniques.</li>
* <li>If the technique uses lighting, then OpenGL lighting state is updated
* <li>If the technique uses lighting, then OpenGL lighting state is updated
* based on the light list on the geometry, otherwise OpenGL lighting is disabled.</li>
* <li>The mesh is uploaded and rendered.</li>
* </ul>
@ -1147,10 +1147,11 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
*/
public void render(Geometry geom, LightList lights, RenderManager 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) {
r.applyRenderState(rm.getForcedRenderState());
@ -1169,7 +1170,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
// reset unchanged uniform flag
clearUniformsSetByCurrent(technique.getShader());
rm.updateUniformBindings(technique.getWorldBindUniforms());
// setup textures and uniforms
for (int i = 0; i < paramValues.size(); i++) {
@ -1212,24 +1213,24 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
// any unset uniforms will be set to 0
resetUniformsNotSetByCurrent(shader);
r.setShader(shader);
renderMeshFromGeometry(r, geom);
}
/**
* Called by {@link RenderManager} to render the geometry by
* using this material.
*
*
* Note that this version of the render method
* does not perform light filtering.
*
*
* @param geom The geometry to render
* @param rm The render manager requesting the rendering
*/
public void render(Geometry geom, RenderManager rm) {
render(geom, geom.getWorldLightList(), rm);
}
public void write(JmeExporter ex) throws IOException {
OutputCapsule oc = ex.getCapsule(this);
oc.write(def.getAssetName(), "material_def", null);
@ -1304,14 +1305,14 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
continue;
}
}
if (im.getFormatVersion() == 0 && param.getName().startsWith("m_")) {
// Ancient version of jME3 ...
param.setName(param.getName().substring(2));
}
if (def.getMaterialParam(param.getName()) == null) {
logger.log(Level.WARNING, "The material parameter is not defined: {0}. Ignoring..",
logger.log(Level.WARNING, "The material parameter is not defined: {0}. Ignoring..",
param.getName());
} else {
checkSetParam(param.getVarType(), param.getName());

@ -40,7 +40,7 @@ import java.util.*;
/**
* Describes a technique definition.
*
*
* @author Kirill Vainer
*/
public class TechniqueDef implements Savable {
@ -49,7 +49,7 @@ public class TechniqueDef implements Savable {
* Version #1: Separate shader language for each shader source.
*/
public static final int SAVABLE_VERSION = 1;
/**
* Describes light rendering mode.
*/
@ -58,15 +58,15 @@ public class TechniqueDef implements Savable {
* Disable light-based rendering
*/
Disable,
/**
* Enable light rendering by using a single pass.
* Enable light rendering by using a single pass.
* <p>
* An array of light positions and light colors is passed to the shader
* containing the world light list for the geometry being rendered.
*/
SinglePass,
/**
* Enable light rendering by using multi-pass rendering.
* <p>
@ -77,7 +77,7 @@ public class TechniqueDef implements Savable {
* passes have it set to black.
*/
MultiPass,
/**
* @deprecated OpenGL1 is not supported anymore
*/
@ -96,15 +96,16 @@ public class TechniqueDef implements Savable {
private EnumMap<Shader.ShaderType,String> shaderLanguages;
private EnumMap<Shader.ShaderType,String> shaderNames;
private DefineList presetDefines;
private boolean usesNodes = false;
private List<ShaderNode> shaderNodes;
private ShaderGenerationInfo shaderGenerationInfo;
private boolean noRender = false;
private RenderState renderState;
private RenderState forcedRenderState;
private LightMode lightMode = LightMode.Disable;
private ShadowMode shadowMode = ShadowMode.Disable;
@ -115,7 +116,7 @@ public class TechniqueDef implements Savable {
* Creates a new technique definition.
* <p>
* Used internally by the J3M/J3MD loader.
*
*
* @param name The name of the technique, should be set to <code>null</code>
* for default techniques.
*/
@ -135,7 +136,7 @@ public class TechniqueDef implements Savable {
/**
* Returns the name of this technique as specified in the J3MD file.
* Default techniques have the name "Default".
*
*
* @return the name of this technique
*/
public String getName(){
@ -153,9 +154,9 @@ public class TechniqueDef implements Savable {
/**
* Set the light mode
*
*
* @param lightMode the light mode
*
*
* @see LightMode
*/
public void setLightMode(LightMode lightMode) {
@ -172,9 +173,9 @@ public class TechniqueDef implements Savable {
/**
* Set the shadow mode.
*
*
* @param shadowMode the shadow mode.
*
*
* @see ShadowMode
*/
public void setShadowMode(ShadowMode shadowMode) {
@ -184,7 +185,7 @@ public class TechniqueDef implements Savable {
/**
* Returns the render state that this technique is using
* @return the render state that this technique is using
* @see #setRenderState(com.jme3.material.RenderState)
* @see #setRenderState(com.jme3.material.RenderState)
*/
public RenderState getRenderState() {
return renderState;
@ -192,15 +193,37 @@ public class TechniqueDef implements Savable {
/**
* Sets the render state that this technique is using.
*
*
* @param renderState the render state that this technique is using.
*
*
* @see RenderState
*/
public void setRenderState(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
*/
@ -208,12 +231,12 @@ public class TechniqueDef implements Savable {
public boolean isUsingShaders(){
return true;
}
/**
* Returns true if this technique uses Shader Nodes, false otherwise.
*
*
* @return true if this technique uses Shader Nodes, false otherwise.
*
*
*/
public boolean isUsingShaderNodes(){
return usesNodes;
@ -222,7 +245,7 @@ public class TechniqueDef implements Savable {
/**
* Gets the {@link Caps renderer capabilities} that are required
* by this technique.
*
*
* @return the required renderer capabilities
*/
public EnumSet<Caps> getRequiredCaps() {
@ -231,7 +254,7 @@ public class TechniqueDef implements Savable {
/**
* Sets the shaders that this technique definition will use.
*
*
* @param vertexShader The name of the vertex shader
* @param fragmentShader The name of the fragment shader
* @param vertLanguage The vertex shader language
@ -242,7 +265,7 @@ public class TechniqueDef implements Savable {
this.shaderNames.put(Shader.ShaderType.Vertex, vertexShader);
this.shaderLanguages.put(Shader.ShaderType.Fragment, fragLanguage);
this.shaderNames.put(Shader.ShaderType.Fragment, fragmentShader);
requiredCaps.clear();
Caps vertCap = Caps.valueOf(vertLanguage);
requiredCaps.add(vertCap);
@ -259,17 +282,17 @@ public class TechniqueDef implements Savable {
*/
public void setShaderFile(EnumMap<Shader.ShaderType, String> shaderNames, EnumMap<Shader.ShaderType, String> shaderLanguages) {
requiredCaps.clear();
for (Shader.ShaderType shaderType : shaderNames.keySet()) {
String language = shaderLanguages.get(shaderType);
String shaderFile = shaderNames.get(shaderType);
this.shaderLanguages.put(shaderType, language);
this.shaderNames.put(shaderType, shaderFile);
Caps vertCap = Caps.valueOf(language);
requiredCaps.add(vertCap);
if (shaderType.equals(Shader.ShaderType.Geometry)) {
requiredCaps.add(Caps.GeometryShader);
} else if (shaderType.equals(Shader.ShaderType.TessellationControl)) {
@ -280,11 +303,11 @@ public class TechniqueDef implements Savable {
/**
* Returns the define name which the given material parameter influences.
*
*
* @param paramName The parameter name to look up
* @return The define name
*
* @see #addShaderParamDefine(java.lang.String, java.lang.String)
*
* @see #addShaderParamDefine(java.lang.String, java.lang.String)
*/
public String getShaderParamDefine(String paramName){
if (defineParams == null) {
@ -297,11 +320,11 @@ public class TechniqueDef implements Savable {
* Adds a define linked to a material parameter.
* <p>
* Any time the material parameter on the parent material is altered,
* the appropriate define on the technique will be modified as well.
* See the method
* the appropriate define on the technique will be modified as well.
* See the method
* {@link DefineList#set(java.lang.String, com.jme3.shader.VarType, java.lang.Object) }
* on the exact details of how the material parameter changes the define.
*
*
* @param paramName The name of the material parameter to link to.
* @param defineName The name of the define parameter, e.g. USE_LIGHTING
*/
@ -314,26 +337,26 @@ public class TechniqueDef implements Savable {
/**
* Returns the {@link DefineList} for the preset defines.
*
*
* @return the {@link DefineList} for the preset defines.
*
* @see #addShaderPresetDefine(java.lang.String, com.jme3.shader.VarType, java.lang.Object)
*
* @see #addShaderPresetDefine(java.lang.String, com.jme3.shader.VarType, java.lang.Object)
*/
public DefineList getShaderPresetDefines() {
return presetDefines;
}
/**
* Adds a preset define.
* Adds a preset define.
* <p>
* Preset defines do not depend upon any parameters to be activated,
* they are always passed to the shader as long as this technique is used.
*
*
* @param defineName The name of the define parameter, e.g. USE_LIGHTING
* @param type The type of the define. See
* @param type The type of the define. See
* {@link DefineList#set(java.lang.String, com.jme3.shader.VarType, java.lang.Object) }
* to see why it matters.
*
*
* @param value The value of the define
*/
public void addShaderPresetDefine(String defineName, VarType type, Object value){
@ -346,18 +369,18 @@ public class TechniqueDef implements Savable {
/**
* Returns the name of the fragment shader used by the technique, or null
* if no fragment shader is specified.
*
*
* @return the name of the fragment shader to be used.
*/
public String getFragmentShaderName() {
return shaderNames.get(Shader.ShaderType.Fragment);
}
/**
* Returns the name of the vertex shader used by the technique, or null
* if no vertex shader is specified.
*
*
* @return the name of the vertex shader to be used.
*/
public String getVertexShaderName() {
@ -370,7 +393,7 @@ public class TechniqueDef implements Savable {
public String getFragmentShaderLanguage() {
return shaderLanguages.get(Shader.ShaderType.Fragment);
}
/**
* Returns the language of the vertex shader used in this technique.
*/
@ -390,10 +413,10 @@ public class TechniqueDef implements Savable {
public String getShaderProgramName(Shader.ShaderType shaderType){
return shaderNames.get(shaderType);
}
/**
* Adds a new world parameter by the given name.
*
*
* @param name The world parameter to add.
* @return True if the world parameter name was found and added
* to the list of world parameters, false otherwise.
@ -402,7 +425,7 @@ public class TechniqueDef implements Savable {
if (worldBinds == null){
worldBinds = new ArrayList<UniformBinding>();
}
try {
worldBinds.add( UniformBinding.valueOf(name) );
return true;
@ -418,11 +441,11 @@ public class TechniqueDef implements Savable {
public void setForcedRenderState(RenderState forcedRenderState) {
this.forcedRenderState = forcedRenderState;
}
/**
* Returns a list of world parameters that are used by this
* technique definition.
*
*
* @return The list of world parameters
*/
public List<UniformBinding> getWorldBindings() {
@ -448,10 +471,11 @@ public class TechniqueDef implements Savable {
oc.write(lightMode, "lightMode", LightMode.Disable);
oc.write(shadowMode, "shadowMode", ShadowMode.Disable);
oc.write(renderState, "renderState", null);
oc.write(noRender, "noRender", false);
oc.write(usesNodes, "usesNodes", false);
oc.writeSavableArrayList((ArrayList)shaderNodes,"shaderNodes", null);
oc.write(shaderGenerationInfo, "shaderGenerationInfo", null);
// TODO: Finish this when Map<String, String> export is available
// oc.write(defineParams, "defineParams", null);
// TODO: Finish this when List<Enum> export is available
@ -470,7 +494,8 @@ public class TechniqueDef implements Savable {
lightMode = ic.readEnum("lightMode", LightMode.class, LightMode.Disable);
shadowMode = ic.readEnum("shadowMode", ShadowMode.class, ShadowMode.Disable);
renderState = (RenderState) ic.readSavable("renderState", null);
noRender = ic.readBoolean("noRender", false);
if (ic.getSavableVersion(TechniqueDef.class) == 0) {
// Old version
shaderLanguages.put(Shader.ShaderType.Vertex,ic.readString("shaderLang", null));
@ -483,7 +508,7 @@ public class TechniqueDef implements Savable {
shaderLanguages.put(Shader.ShaderType.TessellationControl,ic.readString("tsctrlLanguage", null));
shaderLanguages.put(Shader.ShaderType.TessellationEvaluation,ic.readString("tsevalLanguage", null));
}
usesNodes = ic.readBoolean("usesNodes", false);
shaderNodes = ic.readSavableArrayList("shaderNodes", null);
shaderGenerationInfo = (ShaderGenerationInfo) ic.readSavable("shaderGenerationInfo", null);
@ -525,6 +550,6 @@ public class TechniqueDef implements Savable {
//todo: make toString return something usefull
@Override
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;
}
/**
* 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) {
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) {
loadIdentity();
TempVars vars = TempVars.get();
Vector3f f = vars.vect1.set(direction);
Vector3f s = vars.vect2.set(f).crossLocal(up);
Vector3f u = vars.vect3.set(s).crossLocal(f);
// s.normalizeLocal();
// u.normalizeLocal();
m00 = s.x;
m01 = s.y;
m02 = s.z;
m10 = u.x;
m11 = u.y;
m12 = u.z;
m20 = -f.x;
m21 = -f.y;
m22 = -f.z;
// m00 = -left.x;
// m10 = -left.y;
// m20 = -left.z;
//
// m01 = up.x;
// m11 = up.y;
// 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();
// transMatrix.multLocal(this);
// set(transMatrix);
try {
Vector3f fwdVector = vars.vect1.set(direction);
Vector3f leftVector = vars.vect2.set(fwdVector).crossLocal(up);
Vector3f upVector = vars.vect3.set(leftVector).crossLocal(fwdVector);
m00 = leftVector.x;
m01 = leftVector.y;
m02 = leftVector.z;
m03 = -leftVector.dot(location);
m10 = upVector.x;
m11 = upVector.y;
m12 = upVector.z;
m13 = -upVector.dot(location);
m20 = -fwdVector.x;
m21 = -fwdVector.y;
m22 = -fwdVector.z;
m23 = fwdVector.dot(location);
m30 = 0f;
m31 = 0f;
m32 = 0f;
m33 = 1f;
} finally {
vars.release();
}
}
/**

@ -49,7 +49,7 @@ public final class Transform implements Savable, Cloneable, java.io.Serializable
private Quaternion rot = new Quaternion();
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){
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.
*/
public void loadIdentity() {
translation.set(0,0,0);
scale.set(1,1,1);
rot.set(0,0,0,1);
translation.set(0, 0, 0);
scale.set(1, 1, 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
@ -307,22 +330,21 @@ public final class Transform implements Savable, Cloneable, java.io.Serializable
return this;
}
@Override
public void write(JmeExporter e) throws IOException {
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(scale, "scale", Vector3f.UNIT_XYZ);
}
@Override
public void read(JmeImporter e) throws IOException {
InputCapsule capsule = e.getCapsule(this);
rot = (Quaternion)capsule.readSavable("rot", new Quaternion());
translation = (Vector3f)capsule.readSavable("translation", null);
if( translation == null ) {
translation = new Vector3f();
}
scale = (Vector3f)capsule.readSavable("scale", Vector3f.UNIT_XYZ);
rot.set((Quaternion)capsule.readSavable("rot", Quaternion.IDENTITY));
translation.set((Vector3f)capsule.readSavable("translation", Vector3f.ZERO));
scale.set((Vector3f)capsule.readSavable("scale", Vector3f.UNIT_XYZ));
}
@Override

@ -409,6 +409,19 @@ public abstract class Filter implements Savable {
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
* @return

@ -38,6 +38,7 @@ import com.jme3.renderer.*;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.texture.FrameBuffer;
import com.jme3.texture.Image.Format;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture2D;
import com.jme3.ui.Picture;
import com.jme3.util.SafeArrayList;
@ -284,6 +285,12 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
mat.clearParam("NumSamples");
}
}
boolean wantsBilinear = filter.isRequiresBilinear();
if (wantsBilinear) {
tex.setMagFilter(Texture.MagFilter.Bilinear);
tex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps);
}
buff = outputBuffer;
if (i != lastFilterIndex) {
@ -293,6 +300,11 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
}
renderProcessing(r, buff, mat);
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) {
FrameBuffer sceneBuffer = renderFrameBuffer;
if (renderFrameBufferMS != null && !renderer.getCaps().contains(Caps.OpenGL31)) {
if (renderFrameBufferMS != null && !renderer.getCaps().contains(Caps.OpenGL32)) {
renderer.copyFrameBuffer(renderFrameBufferMS, renderFrameBuffer, true);
} else if (renderFrameBufferMS != null) {
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
if (numSamples > 1 && caps.contains(Caps.FrameBufferMultisample)) {
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 msDepth = new Texture2D(width, height, numSamples, Format.Depth);
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.setDepthBuffer(Format.Depth);
filterTexture = new Texture2D(width, height, fbFormat);

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

@ -241,12 +241,12 @@ public class RenderContext {
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;
@ -255,8 +255,6 @@ public class RenderContext {
public ColorRGBA clearColor = new ColorRGBA(0,0,0,0);
public boolean seamlessCubemap = false;
/**
* Reset the RenderContext to default GL state
*/
@ -308,6 +306,5 @@ public class RenderContext {
depthFunc = RenderState.TestFunction.LessOrEqual;
alphaFunc = RenderState.TestFunction.Greater;
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
* @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) {
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) {
this.preferredLightMode = preferredLightMode;
}
/**
* returns the preferred light mode.
* @return the light mode.
*/
public TechniqueDef.LightMode getPreferredLightMode() {
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() {
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) {
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_POSITIVE_Z = 0x8519;
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_MAX_LEVEL = 0x813D;
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_BGR = 0x80E0;
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_COMPONENT32 = 0x81A7;
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_DRAW_BUFFERS_ARB = 0x8824;
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_NUM_PROGRAM_BINARY_FORMATS = 0x87FE;
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.Image;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture.ShadowCompareMode;
import com.jme3.texture.Texture.WrapAxis;
import com.jme3.texture.image.LastTextureState;
import com.jme3.util.BufferUtils;
import com.jme3.util.ListMap;
import com.jme3.util.MipMapGenerator;
@ -68,7 +70,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
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 boolean VALIDATE_SHADER = false;
@ -374,15 +376,18 @@ public class GLRenderer implements Renderer {
if (hasExtension("GL_EXT_texture_filter_anisotropic")) {
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);
limits.put(Limits.RenderBufferSize, getInteger(GLFbo.GL_MAX_RENDERBUFFER_SIZE_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);
}
@ -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));
if (limits.get(Limits.FrameBufferMrtAttachments) > 1) {
caps.add(Caps.FrameBufferMRT);
@ -501,6 +506,11 @@ public class GLRenderer implements Renderer {
// Initialize default state..
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)) {
// Core Profile requires VAO to be bound.
@ -607,17 +617,16 @@ public class GLRenderer implements Renderer {
if (state.isDepthTest() && !context.depthTestEnabled) {
gl.glEnable(GL.GL_DEPTH_TEST);
gl.glDepthFunc(convertTestFunction(context.depthFunc));
context.depthTestEnabled = true;
} else if (!state.isDepthTest() && context.depthTestEnabled) {
gl.glDisable(GL.GL_DEPTH_TEST);
context.depthTestEnabled = false;
}
if (state.getDepthFunc() != context.depthFunc) {
if (state.isDepthTest() && state.getDepthFunc() != context.depthFunc) {
gl.glDepthFunc(convertTestFunction(state.getDepthFunc()));
context.depthFunc = state.getDepthFunc();
}
if (state.isDepthWrite() && !context.depthWriteEnabled) {
gl.glDepthMask(true);
context.depthWriteEnabled = true;
@ -1064,6 +1073,9 @@ public class GLRenderer implements Renderer {
stringBuf.append("\n");
} else {
if (gles2) {
// request GLSL ES (1.00) when compiling under GLES2.
stringBuf.append("#version 100\n");
if (source.getType() == ShaderType.Fragment) {
// GLES2 requires precision qualifier.
stringBuf.append("precision mediump float;\n");
@ -1080,6 +1092,7 @@ public class GLRenderer implements Renderer {
if (linearizeSrgbImages) {
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.getSource());
@ -1422,7 +1435,7 @@ public class GLRenderer implements Renderer {
// NOTE: For depth textures, sets nearest/no-mips mode
// Required to fix "framebuffer unsupported"
// for old NVIDIA drivers!
setupTextureParams(tex);
setupTextureParams(0, tex);
}
glfbo.glFramebufferTexture2DEXT(GLFbo.GL_FRAMEBUFFER_EXT,
@ -1449,25 +1462,44 @@ public class GLRenderer implements Renderer {
rb.getId());
}
}
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) {
if (fb.getNumColorBuffers() == 0 && fb.getDepthBuffer() == null) {
throw new IllegalArgumentException("The framebuffer: " + fb
+ "\nDoesn't have any color/depth buffers");
}
int id = fb.getId();
if (id == -1) {
// create FBO
glfbo.glGenFramebuffersEXT(intBuf1);
id = intBuf1.get(0);
fb.setId(id);
objManager.registerObject(fb);
statistics.onNewFrameBuffer();
}
if (context.boundFBO != id) {
glfbo.glBindFramebufferEXT(GLFbo.GL_FRAMEBUFFER_EXT, id);
// binding an FBO automatically sets draw buf to GL_COLOR_ATTACHMENT0
context.boundDrawBuf = 0;
context.boundFBO = id;
}
bindFrameBuffer(fb);
FrameBuffer.RenderBuffer depthBuf = fb.getDepthBuffer();
if (depthBuf != null) {
@ -1478,7 +1510,8 @@ public class GLRenderer implements Renderer {
FrameBuffer.RenderBuffer colorBuf = fb.getColorBuffer(i);
updateFrameBufferAttachment(fb, colorBuf);
}
setReadDrawBuffers(fb);
checkFrameBufferError();
fb.clearUpdateNeeded();
@ -1506,93 +1539,45 @@ public class GLRenderer implements Renderer {
}
public void setMainFrameBufferOverride(FrameBuffer 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) {
if (fb == null || !fb.isUpdateNeeded()) {
return;
}
}
if (!caps.contains(Caps.FrameBuffer)) {
throw new RendererException("Framebuffer objects are not supported"
+ " by the video hardware");
}
// 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);
}
}
public void setReadDrawBuffers(FrameBuffer fb) {
if (gl2 == null) {
return;
}
final int NONE = -2;
final int INITIAL = -1;
final int MRT_OFF = 100;
if (fb == null) {
// unbind any fbos
if (context.boundFBO != 0) {
glfbo.glBindFramebufferEXT(GLFbo.GL_FRAMEBUFFER_EXT, 0);
statistics.onFrameBufferUse(null, true);
context.boundFBO = 0;
// Set Read/Draw buffers to initial value.
if (context.boundDrawBuf != INITIAL) {
gl2.glDrawBuffer(context.initialDrawBuf);
context.boundDrawBuf = INITIAL;
}
// select back buffer
if (gl2 != null) {
if (context.boundDrawBuf != -1) {
gl2.glDrawBuffer(context.initialDrawBuf);
context.boundDrawBuf = -1;
}
if (context.boundReadBuf != -1) {
gl2.glReadBuffer(context.initialReadBuf);
context.boundReadBuf = -1;
}
if (context.boundReadBuf != INITIAL) {
gl2.glReadBuffer(context.initialReadBuf);
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 {
statistics.onFrameBufferUse(fb, false);
}
if (fb.getNumColorBuffers() == 0) {
// make sure to select NONE as draw buf
// no color buffer attached. select NONE
// no color buffer attached.
if (gl2 != null) {
if (context.boundDrawBuf != -2) {
if (context.boundDrawBuf != 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);
context.boundReadBuf = -2;
context.boundReadBuf = NONE;
}
}
} else {
@ -1612,7 +1597,7 @@ public class GLRenderer implements Renderer {
+ " by the video hardware!");
}
if (context.boundDrawBuf != 100 + fb.getNumColorBuffers()) {
if (context.boundDrawBuf != MRT_OFF + fb.getNumColorBuffers()) {
intBuf16.clear();
for (int i = 0; i < fb.getNumColorBuffers(); i++) {
intBuf16.put(GLFbo.GL_COLOR_ATTACHMENT0_EXT + i);
@ -1620,7 +1605,7 @@ public class GLRenderer implements Renderer {
intBuf16.flip();
glext.glDrawBuffers(intBuf16);
context.boundDrawBuf = 100 + fb.getNumColorBuffers();
context.boundDrawBuf = MRT_OFF + fb.getNumColorBuffers();
}
} else {
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");
}
// 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 fb.getId() > 0;
assert context.boundFBO == fb.getId();
context.boundFB = fb;
@ -1806,87 +1839,80 @@ public class GLRenderer implements Renderer {
}
@SuppressWarnings("fallthrough")
private void setupTextureParams(Texture tex) {
private void setupTextureParams(int unit, Texture tex) {
Image image = tex.getImage();
int target = convertTextureType(tex.getType(), image != null ? image.getMultiSamples() : 1, -1);
boolean haveMips = true;
if (image != null) {
haveMips = image.isGeneratedMipmapsRequired() || image.hasMipmaps();
}
LastTextureState curState = image.getLastTextureState();
// filter things
if (image.getLastTextureState().magFilter != tex.getMagFilter()) {
int magFilter = convertMagFilter(tex.getMagFilter());
gl.glTexParameteri(target, GL.GL_TEXTURE_MAG_FILTER, magFilter);
image.getLastTextureState().magFilter = tex.getMagFilter();
if (curState.magFilter != tex.getMagFilter()) {
bindTextureAndUnit(target, image, unit);
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 (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.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 (caps.contains(Caps.TextureFilterAnisotropic)
&& curState.anisoFilter != tex.getAnisotropicFilter()) {
bindTextureAndUnit(target, image, unit);
gl.glTexParameterf(target,
GLExt.GL_TEXTURE_MAX_ANISOTROPY_EXT,
tex.getAnisotropicFilter());
curState.anisoFilter = tex.getAnisotropicFilter();
}
if (tex.getAnisotropicFilter() > 1) {
if (caps.contains(Caps.TextureFilterAnisotropic)) {
gl.glTexParameterf(target,
GLExt.GL_TEXTURE_MAX_ANISOTROPY_EXT,
tex.getAnisotropicFilter());
}
}
// repeat modes
switch (tex.getType()) {
case ThreeDimensional:
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)));
image.getLastTextureState().rWrap = tex.getWrap(WrapAxis.R);
curState.rWrap = tex.getWrap(WrapAxis.R);
}
//There is no break statement on purpose here
case TwoDimensional:
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)));
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)));
image.getLastTextureState().sWrap = tex.getWrap(WrapAxis.S);
curState.sWrap = tex.getWrap(WrapAxis.S);
}
break;
default:
throw new UnsupportedOperationException("Unknown texture type: " + tex.getType());
}
if(tex.isNeedCompareModeUpdate() && gl2 != null){
// R to Texture compare mode
if (tex.getShadowCompareMode() != Texture.ShadowCompareMode.Off) {
gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_MODE, GL2.GL_COMPARE_R_TO_TEXTURE);
gl2.glTexParameteri(target, GL2.GL_DEPTH_TEXTURE_MODE, GL2.GL_INTENSITY);
if (tex.getShadowCompareMode() == Texture.ShadowCompareMode.GreaterOrEqual) {
ShadowCompareMode texCompareMode = tex.getShadowCompareMode();
if (gl2 != null && curState.shadowCompareMode != texCompareMode) {
bindTextureAndUnit(target, image, unit);
if (texCompareMode != ShadowCompareMode.Off) {
gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_MODE, GL2.GL_COMPARE_REF_TO_TEXTURE);
if (texCompareMode == ShadowCompareMode.GreaterOrEqual) {
gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_FUNC, GL.GL_GEQUAL);
} else {
gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_FUNC, GL.GL_LEQUAL);
}
}else{
//restoring default value
} else {
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.
*
@ -1965,19 +2035,9 @@ public class GLRenderer implements Renderer {
statistics.onNewTexture();
}
// bind texture
// bind texture
int target = convertTextureType(type, img.getMultiSamples(), -1);
if (context.boundTextures[unit] != img) {
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);
}
bindTextureAndUnit(target, img, unit);
if (!img.hasMipmaps() && img.isGeneratedMipmapsRequired()) {
// Image does not have mipmaps, but they are required.
@ -2086,6 +2146,7 @@ public class GLRenderer implements Renderer {
img.clearUpdateNeeded();
}
@Override
public void setTexture(int unit, Texture tex) {
Image image = tex.getImage();
if (image.isUpdateNeeded() || (image.isGeneratedMipmapsRequired() && !image.isMipmapsGenerated())) {
@ -2112,24 +2173,7 @@ public class GLRenderer implements Renderer {
int texId = image.getId();
assert texId != -1;
Image[] textures = context.boundTextures;
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);
setupTextureParams(unit, tex);
}
public void modifyTexture(Texture tex, Image pixels, int x, int y) {
@ -2623,12 +2667,13 @@ public class GLRenderer implements Renderer {
}
}
clearVertexAttribs();
if (indices != null) {
drawTriangleList(indices, mesh, count);
} else {
drawTriangleArray(mesh.getMode(), count, mesh.getVertexCount());
}
clearVertexAttribs();
}
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.Method;
import java.lang.reflect.Proxy;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.LongBuffer;
import java.nio.ShortBuffer;
import java.util.HashMap;
/**
@ -51,6 +57,17 @@ public final class GLTracer implements InvocationHandler {
private final IntMap<String> constMap;
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) {
IntMap<Void> argSlotsMap = new IntMap<Void>();
for (int argSlot : argSlots) {
@ -174,100 +191,305 @@ public final class GLTracer implements InvocationHandler {
new GLTracer(glInterface, constMap));
}
private String translateInteger(String method, int value, int argIndex) {
IntMap<Void> argSlotMap = nonEnumArgMap.get(method);
if (argSlotMap != null && argSlotMap.containsKey(argIndex)) {
return Integer.toString(value);
}
private void printStyle(String style, String string) {
System.out.print(style + string + ANSI_RESET);
}
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);
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 {
return "GL_ENUM_" + Integer.toHexString(value);
//throw new IllegalStateException("Untranslatable enum encountered on " + method +
// " at argument " + argIndex + " with value " + value);
printStyle(ANSI_GREEN, "ENUM_" + Integer.toHexString(value));
}
}
private String translateString(String value) {
return "\"" + value.replaceAll("\0", "\\\\0") + "\"";
private void printIntOrEnum(String method, int value, int argIndex) {
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 {
Object result = method.invoke(obj, args);
String methodName = method.getName();
private void printNewLine() {
System.out.println();
}
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")) {
System.out.print(methodName);
System.out.print("(");
if (args != null) {
Class<?>[] paramTypes = method.getParameterTypes();
for (int i = 0; i < args.length; i++) {
if (paramTypes[i] == int.class) {
int val = (Integer)args[i];
System.out.print(translateInteger(methodName, val, i));
} else if (paramTypes[i] == String.class) {
System.out.print(translateString((String)args[i]));
} else if (paramTypes[i] == String[].class) {
String[] arr = (String[]) args[i];
if (arr.length == 1) {
if (arr[0].length() > 150) {
System.out.print("\"" + arr[0].substring(0, 150) + "...\"");
} else {
System.out.print("\"" + arr[0] + "\"");
}
} else {
System.out.print("String[" + arr.length + "]");
}
} else if (args[i] instanceof IntBuffer) {
IntBuffer buf = (IntBuffer) args[i];
if (buf.capacity() == 16) {
int val = buf.get(0);
System.out.print("out=" + translateInteger(methodName, val, i));
} else if (buf.capacity() == 1) {
System.out.print("out=" + buf.get(0));
} else {
System.out.print(args[i]);
}
} else if (args[i] instanceof ByteBuffer) {
ByteBuffer bb = (ByteBuffer)args[i];
if (bb.capacity() == 250) {
if (bb.get(0) != 0) {
System.out.print("out=GL_TRUE");
} else {
System.out.print("out=GL_FALSE");
}
} else {
System.out.print(args[i]);
}
} else {
System.out.print(args[i]);
}
if (i != args.length - 1) {
System.out.print(", ");
}
// GL calls which actually draw (as opposed to change state)
// will be printed in darker color
methodName = methodName.substring(2);
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("(");
System.out.print(")");
int target = (Integer) args[0];
int param = (Integer) args[1];
int value = (Integer) args[2];
if (method.getReturnType() != void.class) {
if (result instanceof String) {
System.out.println(" = " + translateString((String)result));
} else if (method.getReturnType() == int.class) {
int val = (Integer)result;
System.out.println(" = " + translateInteger(methodName, val, -1));
} else if (method.getReturnType() == boolean.class) {
boolean val = (Boolean)result;
if (val) System.out.println(" = GL_TRUE");
else System.out.println(" = GL_FALSE");
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++) {
if (paramTypes[i] == int.class) {
int val = (Integer)args[i];
printIntOrEnum(methodName, val, i);
} else if (paramTypes[i] == boolean.class) {
printBoolean((Boolean)args[i]);
} else if (paramTypes[i] == String.class) {
printString((String)args[i]);
} else if (paramTypes[i] == String[].class) {
String[] arr = (String[]) args[i];
if (arr.length == 1) {
printString(arr[0]);
} else {
System.out.println(" = ???");
print("string[" + arr.length + "]");
}
} else if (args[i] instanceof IntBuffer) {
IntBuffer buf = (IntBuffer) args[i];
if (buf.capacity() == 16) {
int val = buf.get(0);
printOut();
printIntOrEnum(methodName, val, i);
} else if (buf.capacity() == 1) {
printOut();
print(Integer.toString(buf.get(0)));
} else {
printBuffer(buf);
}
} else if (args[i] instanceof ByteBuffer) {
ByteBuffer bb = (ByteBuffer)args[i];
if (bb.capacity() == 250) {
printOut();
printBoolean(bb.get(0) != 0);
} else {
printBuffer(bb);
}
} else if (args[i] instanceof Buffer) {
printBuffer((Buffer)args[i]);
} else if (args[i] != null) {
print(args[i].toString());
} else {
System.out.println();
printNull();
}
if (i != args.length - 1) {
System.out.print(", ");
}
}
print(")");
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
printMethodName(methodName);
if (methodName.startsWith("gl")) {
try {
// Try to evaluate result first, so we can see output values.
Object result = method.invoke(obj, args);
printArgs(methodName, args, method.getParameterTypes());
printResult(methodName, result, method.getReturnType());
printNewLine();
return result;
} catch (Throwable ex) {
// Execution failed, print args anyway
// but output values will be incorrect.
printArgs(methodName, args, method.getParameterTypes());
printNewLine();
System.out.println("\tException occurred!");
System.out.println(ex.toString());
throw ex;
}
} else {
printNewLine();
return method.invoke(obj, args);
}
return result;
}
}

@ -90,9 +90,11 @@ final class TextureUtil {
}
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);
if (glFmt == null && isSrgb) {
glFmt = getImageFormat(fmt, false);
glFmt = getImageFormat(fmt, false);
logger.log(Level.WARNING, "No sRGB format available for ''{0}''. Failling back to linear.", fmt);
}
if (glFmt == null) {

@ -119,19 +119,6 @@ public class BatchNode extends GeometryGroupNode {
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){
return g.cachedWorldMat;
@ -169,7 +156,7 @@ public class BatchNode extends GeometryGroupNode {
nvb.updateData(normBuf);
batch.needMeshUpdate = true;
batch.geometry.updateModelBound();
}
}
@ -234,7 +221,7 @@ public class BatchNode extends GeometryGroupNode {
batch.geometry.setMesh(m);
batch.geometry.getMesh().updateCounts();
batch.geometry.getMesh().updateBound();
batch.geometry.updateModelBound();
batches.add(batch);
}
if (batches.size() > 0) {
@ -457,6 +444,7 @@ public class BatchNode extends GeometryGroupNode {
int maxWeights = -1;
Mesh.Mode mode = null;
float lineWidth = 1f;
for (Geometry geom : geometries) {
totalVerts += geom.getVertexCount();
totalTris += geom.getTriangleCount();
@ -465,6 +453,7 @@ public class BatchNode extends GeometryGroupNode {
maxVertCount = geom.getVertexCount();
}
Mesh.Mode listMode;
float listLineWidth = 1f;
int components;
switch (geom.getMesh().getMode()) {
case Points:
@ -475,6 +464,7 @@ public class BatchNode extends GeometryGroupNode {
case LineStrip:
case Lines:
listMode = Mesh.Mode.Lines;
listLineWidth = geom.getMesh().getLineWidth();
components = 2;
break;
case TriangleFan:
@ -504,13 +494,21 @@ public class BatchNode extends GeometryGroupNode {
if (mode != null && mode != listMode) {
throw new UnsupportedOperationException("Cannot combine different"
+ " primitive types: " + 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;
}
outMesh.setMaxNumWeights(maxWeights);
outMesh.setMode(mode);
outMesh.setLineWidth(lineWidth);
if (totalVerts >= 65536) {
// make sure we create an UnsignedInt buffer so
// we can fit all of the meshes
@ -747,8 +745,7 @@ public class BatchNode extends GeometryGroupNode {
}
}
}
Geometry geometry;
boolean needMeshUpdate = false;
Geometry geometry;
}
protected void setNeedsFullRebatch(boolean needsFullRebatch) {

@ -195,7 +195,7 @@ public class Node extends Spatial {
void invalidateUpdateList() {
updateListValid = false;
if ( parent != null ) {
parent.invalidateUpdateList();
parent.invalidateUpdateList();
}
}
@ -570,6 +570,35 @@ public class Node extends Spatial {
// 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.
// 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)
{
BoundingVolume bv = this.getWorldBound();
@ -578,6 +607,7 @@ public class Node extends Spatial {
// collideWith without CollisionResults parameter used to avoid allocation when possible
if (bv.collideWith(other) == 0) return 0;
}
*/
for (Spatial child : children.getArray()){
total += child.collideWith(other, results);
}

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

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

@ -198,35 +198,42 @@ public class Curve extends Mesh {
* points
*/
private void createNurbMesh(int nbSubSegments) {
float minKnot = spline.getMinNurbKnot();
float maxKnot = spline.getMaxNurbKnot();
float deltaU = (maxKnot - minKnot) / 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 maxKnot = spline.getMaxNurbKnot();
float deltaU = (maxKnot - minKnot) / nbSubSegments;
float[] array = new float[(nbSubSegments + 1) * 3];
float[] array = new float[(nbSubSegments + 1) * 3];
float u = minKnot;
Vector3f interpolationResult = new Vector3f();
for (int i = 0; i < array.length; i += 3) {
spline.interpolate(u, 0, interpolationResult);
array[i] = interpolationResult.x;
array[i + 1] = interpolationResult.y;
array[i + 2] = interpolationResult.z;
u += deltaU;
}
float u = minKnot;
Vector3f interpolationResult = new Vector3f();
for (int i = 0; i < array.length; i += 3) {
spline.interpolate(u, 0, interpolationResult);
array[i] = interpolationResult.x;
array[i + 1] = interpolationResult.y;
array[i + 2] = interpolationResult.z;
u += deltaU;
}
//calculating indexes
int i = 0;
short[] indices = new short[nbSubSegments << 1];
for (int j = 0; j < nbSubSegments; ++j) {
indices[i++] = (short) j;
indices[i++] = (short) (j + 1);
}
//calculating indexes
int i = 0;
short[] indices = new short[nbSubSegments << 1];
for (int j = 0; j < nbSubSegments; ++j) {
indices[i++] = (short) j;
indices[i++] = (short) (j + 1);
}
this.setMode(Mesh.Mode.Lines);
this.setBuffer(VertexBuffer.Type.Position, 3, array);
this.setBuffer(VertexBuffer.Type.Index, 2, indices);
this.updateBound();
this.updateCounts();
this.setMode(Mesh.Mode.Lines);
this.setBuffer(VertexBuffer.Type.Position, 3, array);
this.setBuffer(VertexBuffer.Type.Index, 2, indices);
this.updateBound();
this.updateCounts();
}
}
private void createLinearMesh() {

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

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

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

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

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

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

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

@ -54,6 +54,8 @@ import java.util.prefs.Preferences;
*/
public final class AppSettings extends HashMap<String, Object> {
private static final long serialVersionUID = 1L;
private static final AppSettings defaults = new AppSettings(false);
/**
@ -108,6 +110,33 @@ public final class AppSettings extends HashMap<String, Object> {
* @see AppSettings#setAudioRenderer(java.lang.String)
*/
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 {
defaults.put("Width", 640);
@ -118,7 +147,7 @@ public final class AppSettings extends HashMap<String, Object> {
defaults.put("StencilBits", 0);
defaults.put("Samples", 0);
defaults.put("Fullscreen", false);
defaults.put("Title", "jMonkey Engine 3.0");
defaults.put("Title", JmeVersion.FULL_NAME);
defaults.put("Renderer", LWJGL_OPENGL2);
defaults.put("AudioRenderer", LWJGL_OPENAL);
defaults.put("DisableJoysticks", true);

@ -153,6 +153,10 @@ public abstract class JmeSystemDelegate {
return false;
} else if (arch.equals("universal")) {
return false;
} else if (arch.equals("aarch32")) {
return false;
} else if (arch.equals("aarch64")) {
return true;
} else if (arch.equals("arm")) {
return false;
} 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 ARM8
*/
Android_ARM8,
/**
* Android x86
*/

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

@ -316,7 +316,6 @@ public abstract class Texture implements CloneableSmartAsset, Savable, Cloneable
private MinFilter minificationFilter = MinFilter.BilinearNoMipMaps;
private MagFilter magnificationFilter = MagFilter.Bilinear;
private ShadowCompareMode shadowCompareMode = ShadowCompareMode.Off;
private boolean needCompareModeUpdate = false;
private int anisotropicFilter;
/**
@ -404,7 +403,6 @@ public abstract class Texture implements CloneableSmartAsset, Savable, Cloneable
"compareMode can not be null.");
}
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
* 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() {
return anisotropicFilter;
@ -636,14 +634,4 @@ public abstract class Texture implements CloneableSmartAsset, Savable, Cloneable
magnificationFilter = capsule.readEnum("magnificationFilter",
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) {
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.MagFilter magFilter;
public Texture.MinFilter minFilter;
public int anisoFilter;
public Texture.ShadowCompareMode shadowCompareMode;
public LastTextureState() {
// All parameters initialized to null (meaning unset).
reset();
}
public void reset() {
@ -56,5 +58,10 @@ public final class LastTextureState {
rWrap = null;
magFilter = 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.Vector3f;
import com.jme3.math.Vector4f;
import java.io.UnsupportedEncodingException;
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
@ -1010,11 +1011,15 @@ public final class BufferUtils {
}
public static ByteBuffer createByteBuffer(String data) {
byte[] bytes = data.getBytes();
ByteBuffer bb = createByteBuffer(bytes.length);
bb.put(bytes);
bb.flip();
return bb;
try {
byte[] bytes = data.getBytes("UTF-8");
ByteBuffer bb = createByteBuffer(bytes.length);
bb.put(bytes);
bb.flip();
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 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 K key;

@ -42,7 +42,6 @@ import java.io.*;
public class LittleEndien extends InputStream implements DataInput {
protected BufferedInputStream in;
protected BufferedReader inRead;
/**
* 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) {
this.in = new BufferedInputStream(in);
inRead = new BufferedReader(new InputStreamReader(in));
}
public int read() throws IOException {
@ -141,7 +139,7 @@ public class LittleEndien extends InputStream implements DataInput {
}
public String readLine() throws IOException {
return inRead.readLine();
throw new IOException("Unsupported operation");
}
public String readUTF() throws IOException {

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

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

@ -7,8 +7,8 @@ MaterialDef Default GUI {
}
Technique {
VertexShader GLSL100: Common/MatDefs/Gui/Gui.vert
FragmentShader GLSL100: Common/MatDefs/Gui/Gui.frag
VertexShader GLSL150: Common/MatDefs/Gui/Gui.vert
FragmentShader GLSL150: Common/MatDefs/Gui/Gui.frag
WorldParameters {
WorldViewProjectionMatrix
@ -21,6 +21,17 @@ MaterialDef Default GUI {
}
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 vec4 m_Color;

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

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

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

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

@ -13,7 +13,7 @@ MaterialDef Unshaded {
Color GlowColor
// For instancing
Boolean UseInstancing
Boolean UseInstancing
// For hardware skinning
Int NumberOfBones
@ -54,8 +54,8 @@ MaterialDef Unshaded {
}
Technique {
VertexShader GLSL100: Common/MatDefs/Misc/Unshaded.vert
FragmentShader GLSL100: Common/MatDefs/Misc/Unshaded.frag
VertexShader GLSL150: Common/MatDefs/Misc/Unshaded.vert
FragmentShader GLSL150: Common/MatDefs/Misc/Unshaded.frag
WorldParameters {
WorldViewProjectionMatrix
@ -76,6 +76,25 @@ MaterialDef Unshaded {
}
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 {

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

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

@ -2,14 +2,15 @@ ShaderNodeDefinitions{
ShaderNodeDefinition TextureFetch {
Type: Fragment
Shader GLSL100: Common/MatDefs/ShaderNodes/Basic/texture.frag
Shader GLSL150: Common/MatDefs/ShaderNodes/Basic/texture15.frag
Documentation{
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
@output outColor the fetched color
}
Input {
sampler2D texture
sampler2D textureMap
vec2 texCoord
}
Output {

@ -1,3 +1,3 @@
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