diff --git a/.gitignore b/.gitignore index 606f718e9..5e7951c48 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ /.gradle/ /.nb-gradle/private/ /.nb-gradle/profiles/private/ +/.idea/ /dist/ /build/ /netbeans/ @@ -127,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 diff --git a/.travis.yml b/.travis.yml index bdc1336f7..48e75d0b4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,7 @@ install: script: - ./gradlew check - ./gradlew createZipDistribution - - "[ $TRAVIS_BRANCH == 'master' ] && [ $TRAVIS_PULL_REQUEST == 'false' ] && ./gradlew uploadArchives;" + - "[ $TRAVIS_BRANCH == 'master' ] && [ $TRAVIS_PULL_REQUEST == 'false' ] && ./gradlew uploadArchives || :" before_deploy: - export RELEASE_DIST=$(ls build/distributions/*.zip) @@ -43,8 +43,7 @@ deploy: before_install: - git fetch --unshallow - - openssl aes-256-cbc -K $encrypted_a1949b55824a_key -iv $encrypted_a1949b55824a_iv - -in private/www-updater.key.enc -out private/www-updater.key -d + - "[ $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 diff --git a/common.gradle b/common.gradle index 69b695ecd..237ee5e7f 100644 --- a/common.gradle +++ b/common.gradle @@ -24,7 +24,9 @@ configurations { 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" } diff --git a/jme3-blender/src/main/java/com/jme3/asset/BlenderKey.java b/jme3-blender/src/main/java/com/jme3/asset/BlenderKey.java index c412c07b3..3b72088ab 100644 --- a/jme3-blender/src/main/java/com/jme3/asset/BlenderKey.java +++ b/jme3-blender/src/main/java/com/jme3/asset/BlenderKey.java @@ -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; } diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java index e11101c14..142f757e2 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java @@ -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 pathsToCheck = new ArrayList(); - 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> linkedData = loadedAsset.getUserData("linkedData"); + for (Entry> entry : linkedData.entrySet()) { String linkedDataFilePath = "this".equals(entry.getKey()) ? path : entry.getKey(); - - List scenes = (List) entry.getValue().get("scenes"); - for (Node scene : scenes) { - blenderContext.addLinkedFeature(linkedDataFilePath, "SC" + scene.getName(), scene); - } - List objects = (List) entry.getValue().get("objects"); - for (Node object : objects) { - blenderContext.addLinkedFeature(linkedDataFilePath, "OB" + object.getName(), object); - } - List meshes = (List) entry.getValue().get("meshes"); - for (TemporalMesh mesh : meshes) { - blenderContext.addLinkedFeature(linkedDataFilePath, "ME" + mesh.getName(), mesh); - } - List materials = (List) entry.getValue().get("materials"); - for (MaterialContext materialContext : materials) { - blenderContext.addLinkedFeature(linkedDataFilePath, "MA" + materialContext.getName(), materialContext); - } - List textures = (List) entry.getValue().get("textures"); - for (Texture texture : textures) { - blenderContext.addLinkedFeature(linkedDataFilePath, "TE" + texture.getName(), texture); - } - List images = (List) entry.getValue().get("images"); - for (Texture image : images) { - blenderContext.addLinkedFeature(linkedDataFilePath, "IM" + image.getName(), image); - } - List animations = (List) entry.getValue().get("animations"); - for (Animation animation : animations) { - blenderContext.addLinkedFeature(linkedDataFilePath, "AC" + animation.getName(), animation); - } - List cameras = (List) entry.getValue().get("cameras"); - for (Camera camera : cameras) { - blenderContext.addLinkedFeature(linkedDataFilePath, "CA" + camera.getName(), camera); - } - List lights = (List) 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 filters = (List) 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); diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java index 6e8042b09..58114cf28 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java @@ -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 blocks; + protected List blocks = new ArrayList(); /** * 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 headers = fileBlockHeadersByCode.get(fileBlockHeader.getCode()); if (headers == null) { @@ -242,6 +248,13 @@ public class BlenderContext { headers.add(fileBlockHeader); } + /** + * @return the block headers + */ + public List 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 linkedFeatures = this.linkedFeatures.get(blenderFilePath); - if (linkedFeatures == null) { - linkedFeatures = new HashMap(); - 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 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 scenes = (List) linkedFeatures.get("scenes"); + if(scenes != null) { + for(Node scene : scenes) { + if(featureName.equals(scene.getName())) { + return scene; + } + } + } + } else if("OB".equals(namePrefix)) { + List features = (List) linkedFeatures.get("objects"); + if(features != null) { + for(Node feature : features) { + if(featureName.equals(feature.getName())) { + return feature; + } + } + } + } else if("ME".equals(namePrefix)) { + List features = (List) linkedFeatures.get("meshes"); + if(features != null) { + for(Node feature : features) { + if(featureName.equals(feature.getName())) { + return feature; + } + } + } + } else if("MA".equals(namePrefix)) { + List features = (List) linkedFeatures.get("materials"); + if(features != null) { + for(MaterialContext feature : features) { + if(featureName.equals(feature.getName())) { + return feature; + } + } + } + } else if("TX".equals(namePrefix)) { + List features = (List) linkedFeatures.get("textures"); + if(features != null) { + for(Texture feature : features) { + if(featureName.equals(feature.getName())) { + return feature; + } + } + } + } else if("IM".equals(namePrefix)) { + List features = (List) linkedFeatures.get("images"); + if(features != null) { + for(Texture feature : features) { + if(featureName.equals(feature.getName())) { + return feature; + } + } + } + } else if("AC".equals(namePrefix)) { + List features = (List) linkedFeatures.get("animations"); + if(features != null) { + for(Animation feature : features) { + if(featureName.equals(feature.getName())) { + return feature; + } + } + } + } else if("CA".equals(namePrefix)) { + List features = (List) linkedFeatures.get("cameras"); + if(features != null) { + for(Camera feature : features) { + if(featureName.equals(feature.getName())) { + return feature; + } + } + } + } else if("LA".equals(namePrefix)) { + List features = (List) linkedFeatures.get("lights"); + if(features != null) { + for(Light feature : features) { + if(featureName.equals(feature.getName())) { + return feature; + } + } + } + } else if("FI".equals(featureName)) { + List features = (List) 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() + " ]"; + } } diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderLoader.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderLoader.java index dd0160063..f519cb9b5 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderLoader.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderLoader.java @@ -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 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 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(); + List blocks = new ArrayList(); 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; + } + } } diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/landscape/LandscapeHelper.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/landscape/LandscapeHelper.java index 8466d5cce..32160c13a 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/landscape/LandscapeHelper.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/landscape/LandscapeHelper.java @@ -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; diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/materials/MaterialContext.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/materials/MaterialContext.java index 51c7072b1..3b5a8eefb 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/materials/MaterialContext.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/materials/MaterialContext.java @@ -168,9 +168,11 @@ public final class MaterialContext implements Savable { this.setTexture(material, combinedTexture.getMappingType(), combinedTexture.getResultTexture()); List 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); } diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/SubdivisionSurfaceModifier.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/SubdivisionSurfaceModifier.java index 2f3d12d60..2945b3413 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/SubdivisionSurfaceModifier.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/SubdivisionSurfaceModifier.java @@ -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 adjacentOriginalEdges = new ArrayList(); - for (Edge edge : temporalMesh.getAdjacentEdges(i)) { - if (verticesOnOriginalEdges.contains(edge.getFirstIndex()) || verticesOnOriginalEdges.contains(edge.getSecondIndex())) { - adjacentOriginalEdges.add(edge); + Collection 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); + } } } } diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/CombinedTexture.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/CombinedTexture.java index b7d6958ca..e406447eb 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/CombinedTexture.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/CombinedTexture.java @@ -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); diff --git a/jme3-bullet-native/src/native/cpp/com_jme3_bullet_collision_shapes_GImpactCollisionShape.cpp b/jme3-bullet-native/src/native/cpp/com_jme3_bullet_collision_shapes_GImpactCollisionShape.cpp index d3e0ba719..f416e46b4 100644 --- a/jme3-bullet-native/src/native/cpp/com_jme3_bullet_collision_shapes_GImpactCollisionShape.cpp +++ b/jme3-bullet-native/src/native/cpp/com_jme3_bullet_collision_shapes_GImpactCollisionShape.cpp @@ -51,6 +51,7 @@ extern "C" { jmeClasses::initJavaClasses(env); btTriangleIndexVertexArray* array = reinterpret_cast(meshId); btGImpactMeshShape* shape = new btGImpactMeshShape(array); + shape->updateBound(); return reinterpret_cast(shape); } diff --git a/jme3-bullet-native/src/native/cpp/jmeBulletUtil.cpp b/jme3-bullet-native/src/native/cpp/jmeBulletUtil.cpp index 04b56a545..a51bcf833 100644 --- a/jme3-bullet-native/src/native/cpp/jmeBulletUtil.cpp +++ b/jme3-bullet-native/src/native/cpp/jmeBulletUtil.cpp @@ -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; diff --git a/jme3-core/build.gradle b/jme3-core/build.gradle index 2a45d440b..8d55c3e76 100644 --- a/jme3-core/build.gradle +++ b/jme3-core/build.gradle @@ -10,6 +10,11 @@ sourceSets { srcDir 'src/tools/java' } } + test { + java { + srcDir 'src/test/java' + } + } } task updateVersionPropertiesFile << { diff --git a/jme3-core/src/main/java/com/jme3/asset/ImplHandler.java b/jme3-core/src/main/java/com/jme3/asset/ImplHandler.java index 73e6d8df3..3bbd19c14 100644 --- a/jme3-core/src/main/java/com/jme3/asset/ImplHandler.java +++ b/jme3-core/src/main/java/com/jme3/asset/ImplHandler.java @@ -75,7 +75,7 @@ final class ImplHandler { this.assetManager = assetManager; } - protected static class ImplThreadLocal extends ThreadLocal { + protected static class ImplThreadLocal extends ThreadLocal { private final Class 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 local : locatorsList){ + AssetInfo info = local.get().locate(assetManager, key); + if (info != null) { return info; + } } return null; diff --git a/jme3-core/src/main/java/com/jme3/audio/openal/ALAudioRenderer.java b/jme3-core/src/main/java/com/jme3/audio/openal/ALAudioRenderer.java index 62f04018a..0878e23fa 100644 --- a/jme3-core/src/main/java/com/jme3/audio/openal/ALAudioRenderer.java +++ b/jme3-core/src/main/java/com/jme3/audio/openal/ALAudioRenderer.java @@ -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 channelList = new ArrayList(); 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); diff --git a/jme3-core/src/main/java/com/jme3/bounding/BoundingBox.java b/jme3-core/src/main/java/com/jme3/bounding/BoundingBox.java index bdba19fe3..c087950a3 100644 --- a/jme3-core/src/main/java/com/jme3/bounding/BoundingBox.java +++ b/jme3-core/src/main/java/com/jme3/bounding/BoundingBox.java @@ -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()); } diff --git a/jme3-core/src/main/java/com/jme3/bounding/BoundingSphere.java b/jme3-core/src/main/java/com/jme3/bounding/BoundingSphere.java index d5dd56203..3137f2cbf 100644 --- a/jme3-core/src/main/java/com/jme3/bounding/BoundingSphere.java +++ b/jme3-core/src/main/java/com/jme3/bounding/BoundingSphere.java @@ -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(); } diff --git a/jme3-core/src/main/java/com/jme3/bounding/BoundingVolume.java b/jme3-core/src/main/java/com/jme3/bounding/BoundingVolume.java index f9be2e573..4491fcedf 100644 --- a/jme3-core/src/main/java/com/jme3/bounding/BoundingVolume.java +++ b/jme3-core/src/main/java/com/jme3/bounding/BoundingVolume.java @@ -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(); + } } - } diff --git a/jme3-core/src/main/java/com/jme3/bounding/Intersection.java b/jme3-core/src/main/java/com/jme3/bounding/Intersection.java index 9deb62abc..c627e23e5 100644 --- a/jme3-core/src/main/java/com/jme3/bounding/Intersection.java +++ b/jme3-core/src/main/java/com/jme3/bounding/Intersection.java @@ -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) { diff --git a/jme3-core/src/main/java/com/jme3/input/FlyByCamera.java b/jme3-core/src/main/java/com/jme3/input/FlyByCamera.java index d1dbe0aef..dd9f00dc1 100644 --- a/jme3-core/src/main/java/com/jme3/input/FlyByCamera.java +++ b/jme3-core/src/main/java/com/jme3/input/FlyByCamera.java @@ -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(){ diff --git a/jme3-core/src/main/java/com/jme3/light/AmbientLight.java b/jme3-core/src/main/java/com/jme3/light/AmbientLight.java index 8dd5f9266..a2dc1cd38 100644 --- a/jme3-core/src/main/java/com/jme3/light/AmbientLight.java +++ b/jme3-core/src/main/java/com/jme3/light/AmbientLight.java @@ -32,6 +32,7 @@ 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; @@ -62,6 +63,11 @@ public class AmbientLight extends Light { return true; } + @Override + public boolean intersectsSphere(BoundingSphere sphere, TempVars vars) { + return true; + } + @Override public boolean intersectsFrustum(Camera camera, TempVars vars) { return true; @@ -69,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 diff --git a/jme3-core/src/main/java/com/jme3/light/DefaultLightFilter.java b/jme3-core/src/main/java/com/jme3/light/DefaultLightFilter.java index b38577062..de4e088da 100644 --- a/jme3-core/src/main/java/com/jme3/light/DefaultLightFilter.java +++ b/jme3-core/src/main/java/com/jme3/light/DefaultLightFilter.java @@ -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 processedLights = new HashSet(); + @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 { @@ -77,8 +79,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; + } } } diff --git a/jme3-core/src/main/java/com/jme3/light/DirectionalLight.java b/jme3-core/src/main/java/com/jme3/light/DirectionalLight.java index 87fbf695a..b1abb65cf 100644 --- a/jme3-core/src/main/java/com/jme3/light/DirectionalLight.java +++ b/jme3-core/src/main/java/com/jme3/light/DirectionalLight.java @@ -32,6 +32,7 @@ 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; @@ -80,7 +81,9 @@ public class DirectionalLight extends Light { @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; } /** @@ -114,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; diff --git a/jme3-core/src/main/java/com/jme3/light/Light.java b/jme3-core/src/main/java/com/jme3/light/Light.java index c0fc039a3..24d8ccee3 100644 --- a/jme3-core/src/main/java/com/jme3/light/Light.java +++ b/jme3-core/src/main/java/com/jme3/light/Light.java @@ -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; @@ -204,7 +205,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. + *

+ * 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} diff --git a/jme3-core/src/main/java/com/jme3/light/PointLight.java b/jme3-core/src/main/java/com/jme3/light/PointLight.java index ff3b3295f..4b5224c30 100644 --- a/jme3-core/src/main/java/com/jme3/light/PointLight.java +++ b/jme3-core/src/main/java/com/jme3/light/PointLight.java @@ -32,13 +32,15 @@ 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.ColorRGBA; import com.jme3.math.FastMath; +import com.jme3.math.ColorRGBA; import com.jme3.math.Vector3f; import com.jme3.renderer.Camera; import com.jme3.scene.Spatial; @@ -191,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); } } diff --git a/jme3-core/src/main/java/com/jme3/light/SpotLight.java b/jme3-core/src/main/java/com/jme3/light/SpotLight.java index 9b551d3d8..bc1335b5b 100644 --- a/jme3-core/src/main/java/com/jme3/light/SpotLight.java +++ b/jme3-core/src/main/java/com/jme3/light/SpotLight.java @@ -32,7 +32,9 @@ 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; @@ -188,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; } } @@ -226,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 diff --git a/jme3-core/src/main/java/com/jme3/math/FastMath.java b/jme3-core/src/main/java/com/jme3/math/FastMath.java index d9d944057..4f1a7feed 100644 --- a/jme3-core/src/main/java/com/jme3/math/FastMath.java +++ b/jme3-core/src/main/java/com/jme3/math/FastMath.java @@ -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; } /** diff --git a/jme3-core/src/main/java/com/jme3/post/Filter.java b/jme3-core/src/main/java/com/jme3/post/Filter.java index a6e2e3c01..feaacbe5d 100644 --- a/jme3-core/src/main/java/com/jme3/post/Filter.java +++ b/jme3-core/src/main/java/com/jme3/post/Filter.java @@ -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 in between 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 diff --git a/jme3-core/src/main/java/com/jme3/post/FilterPostProcessor.java b/jme3-core/src/main/java/com/jme3/post/FilterPostProcessor.java index b28aecece..2f52eb425 100644 --- a/jme3-core/src/main/java/com/jme3/post/FilterPostProcessor.java +++ b/jme3-core/src/main/java/com/jme3/post/FilterPostProcessor.java @@ -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); + } } } } diff --git a/jme3-core/src/main/java/com/jme3/renderer/Limits.java b/jme3-core/src/main/java/com/jme3/renderer/Limits.java index 86cddb178..81db88f5e 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/Limits.java +++ b/jme3-core/src/main/java/com/jme3/renderer/Limits.java @@ -77,4 +77,6 @@ public enum Limits { DepthTextureSamples, VertexUniformVectors, + + TextureAnisotropy, } diff --git a/jme3-core/src/main/java/com/jme3/renderer/RenderContext.java b/jme3-core/src/main/java/com/jme3/renderer/RenderContext.java index 16c07dc71..8287a270e 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/RenderContext.java +++ b/jme3-core/src/main/java/com/jme3/renderer/RenderContext.java @@ -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; } } diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GL.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GL.java index 4a9380cf4..1b9c3f94d 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GL.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GL.java @@ -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; diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GL2.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GL2.java index db09fdd02..abf6a77d7 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GL2.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GL2.java @@ -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; diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLExt.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLExt.java index 27f0eb8bd..c77ff6449 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLExt.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLExt.java @@ -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; diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java index dd30da089..50e603d2f 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java @@ -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,17 +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 + || 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); } @@ -403,7 +406,7 @@ public class GLRenderer implements Renderer { } } - if (hasExtension("GL_ARB_draw_buffers") || gl3 != null) { + 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); @@ -503,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. @@ -609,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; @@ -1428,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, @@ -1455,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) { @@ -1484,7 +1510,8 @@ public class GLRenderer implements Renderer { FrameBuffer.RenderBuffer colorBuf = fb.getColorBuffer(i); updateFrameBufferAttachment(fb, colorBuf); } - + + setReadDrawBuffers(fb); checkFrameBufferError(); fb.clearUpdateNeeded(); @@ -1512,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 { @@ -1618,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); @@ -1626,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()); @@ -1639,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; @@ -1812,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); } /** @@ -1950,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. * @@ -1971,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. @@ -2092,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())) { @@ -2118,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) { @@ -2629,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) { diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLTracer.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLTracer.java index 1b0d70749..f252d22dc 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLTracer.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLTracer.java @@ -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 constMap; private static final HashMap> nonEnumArgMap = new HashMap>(); + 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 argSlotsMap = new IntMap(); 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 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 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; } } diff --git a/jme3-core/src/main/java/com/jme3/scene/debug/Grid.java b/jme3-core/src/main/java/com/jme3/scene/debug/Grid.java index 5b6d0661e..563981804 100644 --- a/jme3-core/src/main/java/com/jme3/scene/debug/Grid.java +++ b/jme3-core/src/main/java/com/jme3/scene/debug/Grid.java @@ -99,6 +99,7 @@ public class Grid extends Mesh { updateBound(); updateCounts(); + setStatic(); } } diff --git a/jme3-core/src/main/java/com/jme3/scene/debug/WireFrustum.java b/jme3-core/src/main/java/com/jme3/scene/debug/WireFrustum.java index c050d1edb..4f5364753 100644 --- a/jme3-core/src/main/java/com/jme3/scene/debug/WireFrustum.java +++ b/jme3-core/src/main/java/com/jme3/scene/debug/WireFrustum.java @@ -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); } diff --git a/jme3-core/src/main/java/com/jme3/scene/shape/Cylinder.java b/jme3-core/src/main/java/com/jme3/scene/shape/Cylinder.java index f0966328f..1d215eb6b 100644 --- a/jme3-core/src/main/java/com/jme3/scene/shape/Cylinder.java +++ b/jme3-core/src/main/java/com/jme3/scene/shape/Cylinder.java @@ -389,6 +389,7 @@ public class Cylinder extends Mesh { } updateBound(); + setStatic(); } @Override diff --git a/jme3-core/src/main/java/com/jme3/scene/shape/Quad.java b/jme3-core/src/main/java/com/jme3/scene/shape/Quad.java index 194535851..3e0c6ce7e 100644 --- a/jme3-core/src/main/java/com/jme3/scene/shape/Quad.java +++ b/jme3-core/src/main/java/com/jme3/scene/shape/Quad.java @@ -124,6 +124,7 @@ public class Quad extends Mesh { } updateBound(); + setStatic(); } diff --git a/jme3-core/src/main/java/com/jme3/scene/shape/Sphere.java b/jme3-core/src/main/java/com/jme3/scene/shape/Sphere.java index 89db073a7..44aa41723 100644 --- a/jme3-core/src/main/java/com/jme3/scene/shape/Sphere.java +++ b/jme3-core/src/main/java/com/jme3/scene/shape/Sphere.java @@ -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 { diff --git a/jme3-core/src/main/java/com/jme3/system/AppSettings.java b/jme3-core/src/main/java/com/jme3/system/AppSettings.java index 484458929..2d7071c9c 100644 --- a/jme3-core/src/main/java/com/jme3/system/AppSettings.java +++ b/jme3-core/src/main/java/com/jme3/system/AppSettings.java @@ -110,6 +110,33 @@ public final class AppSettings extends HashMap { * @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 + *

+ * 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 + *

+ * 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 + *

+ * N.B: This backend is EXPERIMENTAL + * + * @see AppSettings#setRenderer(java.lang.String) + */ + public static final String JOAL = "JOAL"; static { defaults.put("Width", 640); @@ -120,7 +147,7 @@ public final class AppSettings extends HashMap { 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); diff --git a/jme3-core/src/main/java/com/jme3/texture/Texture.java b/jme3-core/src/main/java/com/jme3/texture/Texture.java index 582d06565..6be00700b 100644 --- a/jme3-core/src/main/java/com/jme3/texture/Texture.java +++ b/jme3-core/src/main/java/com/jme3/texture/Texture.java @@ -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; - } - - } diff --git a/jme3-core/src/main/java/com/jme3/texture/image/LastTextureState.java b/jme3-core/src/main/java/com/jme3/texture/image/LastTextureState.java index e7f2a2a14..32c1b718b 100644 --- a/jme3-core/src/main/java/com/jme3/texture/image/LastTextureState.java +++ b/jme3-core/src/main/java/com/jme3/texture/image/LastTextureState.java @@ -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; } } diff --git a/jme3-core/src/main/java/com/jme3/util/ListMap.java b/jme3-core/src/main/java/com/jme3/util/ListMap.java index 0de4d3f75..379fff741 100644 --- a/jme3-core/src/main/java/com/jme3/util/ListMap.java +++ b/jme3-core/src/main/java/com/jme3/util/ListMap.java @@ -43,21 +43,6 @@ import java.util.Map.Entry; */ public final class ListMap extends AbstractMap implements Cloneable, Serializable { - public static void main(String[] args){ - Map map = new ListMap(); - 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 implements Map.Entry, Cloneable { private final K key; diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.frag b/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.frag index d2ade5199..0aa4d3f36 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.frag +++ b/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.frag @@ -154,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 + @@ -183,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 diff --git a/jme3-core/src/main/resources/Common/MatDefs/Misc/ColoredTextured.j3md b/jme3-core/src/main/resources/Common/MatDefs/Misc/ColoredTextured.j3md index 6b74c3074..497b7d0a7 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Misc/ColoredTextured.j3md +++ b/jme3-core/src/main/resources/Common/MatDefs/Misc/ColoredTextured.j3md @@ -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 { } -} \ No newline at end of file +} diff --git a/jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.frag b/jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.frag index 73df8d740..e615d8f1e 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.frag +++ b/jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.frag @@ -1,4 +1,4 @@ -#import "Common/ShaderLib/GLSL150Compat.glsllib" +#import "Common/ShaderLib/GLSLCompat.glsllib" #if defined(HAS_GLOWMAP) || defined(HAS_COLORMAP) || (defined(HAS_LIGHTMAP) && !defined(SEPARATE_TEXCOORD)) #define NEED_TEXCOORD1 diff --git a/jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.vert b/jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.vert index 454708d92..ba7899ca5 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.vert +++ b/jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.vert @@ -1,4 +1,4 @@ -#import "Common/ShaderLib/GLSL150Compat.glsllib" +#import "Common/ShaderLib/GLSLCompat.glsllib" #import "Common/ShaderLib/Skinning.glsllib" #import "Common/ShaderLib/Instancing.glsllib" diff --git a/jme3-core/src/main/resources/Common/ShaderLib/GLSL150Compat.glsllib b/jme3-core/src/main/resources/Common/ShaderLib/GLSL150Compat.glsllib deleted file mode 100644 index 87f07a3f5..000000000 --- a/jme3-core/src/main/resources/Common/ShaderLib/GLSL150Compat.glsllib +++ /dev/null @@ -1,14 +0,0 @@ -#if __VERSION__ >= 130 -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 \ No newline at end of file diff --git a/jme3-core/src/main/resources/Common/ShaderLib/GLSLCompat.glsllib b/jme3-core/src/main/resources/Common/ShaderLib/GLSLCompat.glsllib new file mode 100644 index 000000000..3d23b7915 --- /dev/null +++ b/jme3-core/src/main/resources/Common/ShaderLib/GLSLCompat.glsllib @@ -0,0 +1,34 @@ +#if defined GL_ES +# define hfloat highp float +# define hvec2 highp vec2 +# define hvec3 highp vec3 +# define hvec4 highp vec4 +# define lfloat lowp float +# define lvec2 lowp vec2 +# define lvec3 lowp vec3 +# define lvec4 lowp vec4 +#else +# define hfloat float +# define hvec2 vec2 +# define hvec3 vec3 +# define hvec4 vec4 +# define lfloat float +# define lvec2 vec2 +# define lvec3 vec3 +# define lvec4 vec4 +#endif + +#if __VERSION__ >= 130 +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 \ No newline at end of file diff --git a/jme3-core/src/main/resources/joystick-mapping.properties b/jme3-core/src/main/resources/joystick-mapping.properties index d7ce2f50a..966832036 100644 --- a/jme3-core/src/main/resources/joystick-mapping.properties +++ b/jme3-core/src/main/resources/joystick-mapping.properties @@ -63,3 +63,9 @@ Xbox\ 360\ Wireless\ Receiver.AXIS_RX=z Xbox\ 360\ Wireless\ Receiver.AXIS_RY=rz Xbox\ 360\ Wireless\ Receiver.z=AXIS_RX Xbox\ 360\ Wireless\ Receiver.rz=AXIS_RY + +# Microsoft PC-joystick driver +Microsoft\ PC-joystick\ driver.12=POV +Y +Microsoft\ PC-joystick\ driver.13=POV +X +Microsoft\ PC-joystick\ driver.14=POV -Y +Microsoft\ PC-joystick\ driver.15=POV -X diff --git a/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java b/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java index 3c6af9043..9762635a1 100644 --- a/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java +++ b/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java @@ -43,7 +43,6 @@ import com.jme3.math.Vector3f; import com.jme3.shader.Shader; import com.jme3.shader.VarType; import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; import com.jme3.texture.Texture2D; import com.jme3.texture.image.ColorSpace; import com.jme3.util.PlaceholderAssets; @@ -52,10 +51,13 @@ import com.jme3.util.blockparser.Statement; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; import java.util.EnumMap; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class J3MLoader implements AssetLoader { @@ -137,59 +139,146 @@ public class J3MLoader implements AssetLoader { technique.setShadowMode(sm); } - private Object readValue(VarType type, String value) throws IOException{ - if (type.isTextureType()){ -// String texturePath = readString("[\n;(//)(\\})]"); - String texturePath = value.trim(); - boolean flipY = false; - boolean repeat = false; - if (texturePath.startsWith("Flip Repeat ")){ - texturePath = texturePath.substring(12).trim(); - flipY = true; - repeat = true; - }else if (texturePath.startsWith("Flip ")){ - texturePath = texturePath.substring(5).trim(); - flipY = true; - }else if (texturePath.startsWith("Repeat ")){ - texturePath = texturePath.substring(7).trim(); - repeat = true; + private List tokenizeTextureValue(final String value) { + final List matchList = new ArrayList(); + final Pattern regex = Pattern.compile("[^\\s\"']+|\"([^\"]*)\"|'([^']*)'"); + final Matcher regexMatcher = regex.matcher(value.trim()); + + while (regexMatcher.find()) { + if (regexMatcher.group(1) != null) { + matchList.add(regexMatcher.group(1)); + } else if (regexMatcher.group(2) != null) { + matchList.add(regexMatcher.group(2)); + } else { + matchList.add(regexMatcher.group()); } + } - TextureKey texKey = new TextureKey(texturePath, flipY); - switch (type) { - case Texture3D: - texKey.setTextureTypeHint(Texture.Type.ThreeDimensional); - break; - case TextureArray: - texKey.setTextureTypeHint(Texture.Type.TwoDimensionalArray); - break; - case TextureCubeMap: - texKey.setTextureTypeHint(Texture.Type.CubeMap); - break; - } - texKey.setGenerateMips(true); - - Texture tex; - try { - tex = assetManager.loadTexture(texKey); - } catch (AssetNotFoundException ex){ - logger.log(Level.WARNING, "Cannot locate {0} for material {1}", new Object[]{texKey, key}); - tex = null; + return matchList; + } + + private List parseTextureOptions(final List values) { + final List matchList = new ArrayList(); + + if (values.isEmpty() || values.size() == 1) { + return matchList; + } + + // Loop through all but the last value, the last one is going to be the path. + for (int i = 0; i < values.size() - 1; i++) { + final String value = values.get(i); + final TextureOption textureOption = TextureOption.getTextureOption(value); + + if (textureOption == null && !value.contains("\\") && !value.contains("/") && !values.get(0).equals("Flip") && !values.get(0).equals("Repeat")) { + logger.log(Level.WARNING, "Unknown texture option \"{0}\" encountered for \"{1}\" in material \"{2}\"", new Object[]{value, key, material.getKey().getName()}); + } else if (textureOption != null){ + final String option = textureOption.getOptionValue(value); + matchList.add(new TextureOptionValue(textureOption, option)); } - if (tex != null){ - if (repeat){ - tex.setWrap(WrapMode.Repeat); + } + + return matchList; + } + + private boolean isTexturePathDeclaredTheTraditionalWay(final int numberOfValues, final int numberOfTextureOptions, final String texturePath) { + return (numberOfValues > 1 && (texturePath.startsWith("Flip Repeat ") || texturePath.startsWith("Flip ") || + texturePath.startsWith("Repeat ") || texturePath.startsWith("Repeat Flip "))) || numberOfTextureOptions == 0; + } + + private Texture parseTextureType(final VarType type, final String value) { + final List textureValues = tokenizeTextureValue(value); + final List textureOptionValues = parseTextureOptions(textureValues); + + TextureKey textureKey = null; + + // If there is only one token on the value, it must be the path to the texture. + if (textureValues.size() == 1) { + textureKey = new TextureKey(textureValues.get(0), false); + } else { + String texturePath = value.trim(); + + // If there are no valid "new" texture options specified but the path is split into several parts, lets parse the old way. + if (isTexturePathDeclaredTheTraditionalWay(textureValues.size(), textureOptionValues.size(), texturePath)) { + boolean flipY = false; + + if (texturePath.startsWith("Flip Repeat ") || texturePath.startsWith("Repeat Flip ")) { + texturePath = texturePath.substring(12).trim(); + flipY = true; + } else if (texturePath.startsWith("Flip ")) { + texturePath = texturePath.substring(5).trim(); + flipY = true; + } else if (texturePath.startsWith("Repeat ")) { + texturePath = texturePath.substring(7).trim(); } - }else{ - tex = new Texture2D(PlaceholderAssets.getPlaceholderImage(assetManager)); - if (repeat){ - tex.setWrap(WrapMode.Repeat); + + // Support path starting with quotes (double and single) + if (texturePath.startsWith("\"") || texturePath.startsWith("'")) { + texturePath = texturePath.substring(1); + } + + // Support path ending with quotes (double and single) + if (texturePath.endsWith("\"") || texturePath.endsWith("'")) { + texturePath = texturePath.substring(0, texturePath.length() - 1); } - tex.setKey(texKey); - tex.setName(texKey.getName()); + + textureKey = new TextureKey(texturePath, flipY); } - return tex; - }else{ + + if (textureKey == null) { + textureKey = new TextureKey(textureValues.get(textureValues.size() - 1), false); + } + + // Apply texture options to the texture key + if (!textureOptionValues.isEmpty()) { + for (final TextureOptionValue textureOptionValue : textureOptionValues) { + textureOptionValue.applyToTextureKey(textureKey); + } + } + } + + switch (type) { + case Texture3D: + textureKey.setTextureTypeHint(Texture.Type.ThreeDimensional); + break; + case TextureArray: + textureKey.setTextureTypeHint(Texture.Type.TwoDimensionalArray); + break; + case TextureCubeMap: + textureKey.setTextureTypeHint(Texture.Type.CubeMap); + break; + } + + textureKey.setGenerateMips(true); + + Texture texture; + + try { + texture = assetManager.loadTexture(textureKey); + } catch (AssetNotFoundException ex){ + logger.log(Level.WARNING, "Cannot locate {0} for material {1}", new Object[]{textureKey, key}); + texture = null; + } + + if (texture == null){ + texture = new Texture2D(PlaceholderAssets.getPlaceholderImage(assetManager)); + texture.setKey(textureKey); + texture.setName(textureKey.getName()); + } + + // Apply texture options to the texture + if (!textureOptionValues.isEmpty()) { + for (final TextureOptionValue textureOptionValue : textureOptionValues) { + textureOptionValue.applyToTexture(texture); + } + } + + return texture; + } + + private Object readValue(final VarType type, final String value) throws IOException{ + if (type.isTextureType()) { + return parseTextureType(type, value); + } else { String[] split = value.trim().split(whitespacePattern); switch (type){ case Float: @@ -632,4 +721,125 @@ public class J3MLoader implements AssetLoader { } } + /** + * Texture options allow you to specify how a texture should be initialized by including an option before + * the path to the texture in the .j3m file. + *

+ * Example: + *

+     *     DiffuseMap: MinTrilinear MagBilinear WrapRepeat_S "some/path/to a/texture.png"
+     *     
+ * This would apply a minification filter of "Trilinear", a magnification filter of "Bilinear" and set the wrap mode to "Repeat". + *

+ *

+ * Note: If several filters of the same type are added, eg. MinTrilinear MinNearestLinearMipMap, the last one will win. + *

+ */ + private enum TextureOption { + + /** + * Applies a {@link com.jme3.texture.Texture.MinFilter} to the texture. + */ + Min { + @Override + public void applyToTexture(final String option, final Texture texture) { + texture.setMinFilter(Texture.MinFilter.valueOf(option)); + } + }, + + /** + * Applies a {@link com.jme3.texture.Texture.MagFilter} to the texture. + */ + Mag { + @Override + public void applyToTexture(final String option, final Texture texture) { + texture.setMagFilter(Texture.MagFilter.valueOf(option)); + } + }, + + /** + * Applies a {@link com.jme3.texture.Texture.WrapMode} to the texture. This also supports {@link com.jme3.texture.Texture.WrapAxis} + * by adding "_AXIS" to the texture option. For instance if you wanted to repeat on the S (horizontal) axis, you + * would use
WrapRepeat_S
as a texture option. + */ + Wrap { + @Override + public void applyToTexture(final String option, final Texture texture) { + final int separatorPosition = option.indexOf("_"); + + if (separatorPosition >= option.length() - 2) { + final String axis = option.substring(separatorPosition + 1); + final String mode = option.substring(0, separatorPosition); + final Texture.WrapAxis wrapAxis = Texture.WrapAxis.valueOf(axis); + texture.setWrap(wrapAxis, Texture.WrapMode.valueOf(mode)); + } else { + texture.setWrap(Texture.WrapMode.valueOf(option)); + } + } + }, + + /** + * Applies a {@link com.jme3.texture.Texture.WrapMode#Repeat} to the texture. This is simply an alias for + * WrapRepeat, please use WrapRepeat instead if possible as this may become deprecated later on. + */ + Repeat { + @Override + public void applyToTexture(final String option, final Texture texture) { + Wrap.applyToTexture("Repeat", texture); + } + }, + + /** + * Applies flipping on the Y axis to the {@link TextureKey#setFlipY(boolean)}. + */ + Flip { + @Override + public void applyToTextureKey(final String option, final TextureKey textureKey) { + textureKey.setFlipY(true); + } + }; + + public String getOptionValue(final String option) { + return option.substring(name().length()); + } + + public void applyToTexture(final String option, final Texture texture) { + } + + public void applyToTextureKey(final String option, final TextureKey textureKey) { + } + + public static TextureOption getTextureOption(final String option) { + for(final TextureOption textureOption : TextureOption.values()) { + if (option.startsWith(textureOption.name())) { + return textureOption; + } + } + + return null; + } + } + + /** + * Internal object used for holding a {@link com.jme3.material.plugins.J3MLoader.TextureOption} and it's value. Also + * contains a couple of convenience methods for applying the TextureOption to either a TextureKey or a Texture. + */ + private static class TextureOptionValue { + + private final TextureOption textureOption; + private final String value; + + public TextureOptionValue(TextureOption textureOption, String value) { + this.textureOption = textureOption; + this.value = value; + } + + public void applyToTextureKey(final TextureKey textureKey) { + textureOption.applyToTextureKey(value, textureKey); + } + + public void applyToTexture(final Texture texture) { + textureOption.applyToTexture(value, texture); + } + } } diff --git a/jme3-core/src/test/java/com/jme3/collision/BoundingCollisionTest.java b/jme3-core/src/test/java/com/jme3/collision/BoundingCollisionTest.java new file mode 100644 index 000000000..506a1732d --- /dev/null +++ b/jme3-core/src/test/java/com/jme3/collision/BoundingCollisionTest.java @@ -0,0 +1,203 @@ +/* + * 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.collision; + +import static com.jme3.collision.CollisionUtil.*; + +import com.jme3.bounding.BoundingBox; +import com.jme3.bounding.BoundingSphere; +import com.jme3.math.FastMath; +import com.jme3.math.Ray; +import com.jme3.math.Vector3f; +import com.jme3.scene.Geometry; +import com.jme3.scene.shape.Quad; +import org.junit.Test; + +/** + * Tests collision detection between bounding volumes. + * + * @author Kirill Vainer + */ +public class BoundingCollisionTest { + + @Test + public void testBoxBoxCollision() { + BoundingBox box1 = new BoundingBox(Vector3f.ZERO, 1, 1, 1); + BoundingBox box2 = new BoundingBox(Vector3f.ZERO, 1, 1, 1); + checkCollision(box1, box2, 1); + + // Put it at the very edge - should still intersect. + box2.setCenter(new Vector3f(2f, 0f, 0f)); + checkCollision(box1, box2, 1); + + // Put it a wee bit farther - no intersection expected + box2.setCenter(new Vector3f(2f + FastMath.ZERO_TOLERANCE, 0, 0)); + checkCollision(box1, box2, 0); + + // Check the corners. + box2.setCenter(new Vector3f(2f, 2f, 2f)); + checkCollision(box1, box2, 1); + + box2.setCenter(new Vector3f(2f, 2f, 2f + FastMath.ZERO_TOLERANCE)); + checkCollision(box1, box2, 0); + } + + @Test + public void testSphereSphereCollision() { + BoundingSphere sphere1 = new BoundingSphere(1, Vector3f.ZERO); + BoundingSphere sphere2 = new BoundingSphere(1, Vector3f.ZERO); + checkCollision(sphere1, sphere2, 1); + + // Put it at the very edge - should still intersect. + sphere2.setCenter(new Vector3f(2f, 0f, 0f)); + checkCollision(sphere1, sphere2, 1); + + // Put it a wee bit farther - no intersection expected + sphere2.setCenter(new Vector3f(2f + FastMath.ZERO_TOLERANCE, 0, 0)); + checkCollision(sphere1, sphere2, 0); + } + + @Test + public void testBoxSphereCollision() { + BoundingBox box1 = new BoundingBox(Vector3f.ZERO, 1, 1, 1); + BoundingSphere sphere2 = new BoundingSphere(1, Vector3f.ZERO); + checkCollision(box1, sphere2, 1); + + // Put it at the very edge - for sphere vs. box, it will not intersect + sphere2.setCenter(new Vector3f(2f, 0f, 0f)); + checkCollision(box1, sphere2, 0); + + // Put it a wee bit closer - should intersect. + sphere2.setCenter(new Vector3f(2f - FastMath.ZERO_TOLERANCE, 0, 0)); + checkCollision(box1, sphere2, 1); + + // Test if the algorithm converts the sphere + // to a box before testing the collision (incorrect) + float sqrt3 = FastMath.sqrt(3); + + sphere2.setCenter(Vector3f.UNIT_XYZ.mult(2)); + sphere2.setRadius(sqrt3); + checkCollision(box1, sphere2, 0); + + // Make it a wee bit larger. + sphere2.setRadius(sqrt3 + FastMath.ZERO_TOLERANCE); + checkCollision(box1, sphere2, 1); + } + + @Test + public void testBoxRayCollision() { + BoundingBox box = new BoundingBox(Vector3f.ZERO, 1, 1, 1); + Ray ray = new Ray(Vector3f.ZERO, Vector3f.UNIT_Z); + + // XXX: seems incorrect, ray inside box should only generate + // one result... + checkCollision(box, ray, 2); + + ray.setOrigin(new Vector3f(0, 0, -5)); + checkCollision(box, ray, 2); + + // XXX: is this right? the ray origin is on the box's side.. + ray.setOrigin(new Vector3f(0, 0, 2f)); + checkCollision(box, ray, 0); + + ray.setOrigin(new Vector3f(0, 0, -2f)); + checkCollision(box, ray, 2); + + // parallel to the edge, touching the side + ray.setOrigin(new Vector3f(0, 1f, -2f)); + checkCollision(box, ray, 2); + + // still parallel, but not touching the side + ray.setOrigin(new Vector3f(0, 1f + FastMath.ZERO_TOLERANCE, -2f)); + checkCollision(box, ray, 0); + } + + @Test + public void testBoxTriangleCollision() { + BoundingBox box = new BoundingBox(Vector3f.ZERO, 1, 1, 1); + Geometry geom = new Geometry("geom", new Quad(1, 1)); + checkCollision(box, geom, 2); // Both triangles intersect + + // The box touches the edges of the triangles. + box.setCenter(new Vector3f(-1f, 0, 0)); + checkCollision(box, geom, 2); + + // Move it slightly farther.. + box.setCenter(new Vector3f(-1f - FastMath.ZERO_TOLERANCE, 0, 0)); + checkCollision(box, geom, 0); + + // Parallel triangle / box side, touching + box.setCenter(new Vector3f(0, 0, -1f)); + checkCollision(box, geom, 2); + + // Not touching + box.setCenter(new Vector3f(0, 0, -1f - FastMath.ZERO_TOLERANCE)); + checkCollision(box, geom, 0); + + // Test collisions only against one of the triangles + box.setCenter(new Vector3f(-1f, 1.5f, 0f)); + checkCollision(box, geom, 1); + + box.setCenter(new Vector3f(1.5f, -1f, 0f)); + checkCollision(box, geom, 1); + } + + @Test + public void testSphereTriangleCollision() { + BoundingSphere sphere = new BoundingSphere(1, Vector3f.ZERO); + Geometry geom = new Geometry("geom", new Quad(1, 1)); + checkCollision(sphere, geom, 2); + + // The box touches the edges of the triangles. + sphere.setCenter(new Vector3f(-1f + FastMath.ZERO_TOLERANCE, 0, 0)); + checkCollision(sphere, geom, 2); + + // Move it slightly farther.. + sphere.setCenter(new Vector3f(-1f - FastMath.ZERO_TOLERANCE, 0, 0)); + checkCollision(sphere, geom, 0); + + // Parallel triangle / box side, touching + sphere.setCenter(new Vector3f(0, 0, -1f)); + checkCollision(sphere, geom, 2); + + // Not touching + sphere.setCenter(new Vector3f(0, 0, -1f - FastMath.ZERO_TOLERANCE)); + checkCollision(sphere, geom, 0); + + // Test collisions only against one of the triangles + sphere.setCenter(new Vector3f(-0.9f, 1.2f, 0f)); + checkCollision(sphere, geom, 1); + + sphere.setCenter(new Vector3f(1.2f, -0.9f, 0f)); + checkCollision(sphere, geom, 1); + } +} diff --git a/jme3-core/src/test/java/com/jme3/collision/CollisionUtil.java b/jme3-core/src/test/java/com/jme3/collision/CollisionUtil.java new file mode 100644 index 000000000..43d7b20f0 --- /dev/null +++ b/jme3-core/src/test/java/com/jme3/collision/CollisionUtil.java @@ -0,0 +1,80 @@ +/* + * 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.collision; + +import com.jme3.bounding.BoundingVolume; + +/** + * Utilities for testing collision. + * + * @author Kirill Vainer + */ +final class CollisionUtil { + + private static void checkCollisionBase(Collidable a, Collidable b, int expected) { + // Test bounding volume methods + if (a instanceof BoundingVolume && b instanceof BoundingVolume) { + BoundingVolume bv1 = (BoundingVolume) a; + BoundingVolume bv2 = (BoundingVolume) b; + assert bv1.intersects(bv2) == (expected != 0); + } + + // Test standard collideWith method + CollisionResults results = new CollisionResults(); + int numCollisions = a.collideWith(b, results); + assert results.size() == numCollisions; + assert numCollisions == expected; + + // force the results to be sorted here.. + results.getClosestCollision(); + + if (results.size() > 0) { + assert results.getCollision(0) == results.getClosestCollision(); + } + if (results.size() == 1) { + assert results.getClosestCollision() == results.getFarthestCollision(); + } + } + + /** + * Tests various collisions between the two collidables and + * the transitive property. + * + * @param a First collidable + * @param b Second collidable + * @param expect Number of expected results + */ + public static void checkCollision(Collidable a, Collidable b, int expected) { + checkCollisionBase(a, b, expected); + checkCollisionBase(b, a, expected); + } +} diff --git a/jme3-core/src/test/java/com/jme3/light/LightFilterTest.java b/jme3-core/src/test/java/com/jme3/light/LightFilterTest.java new file mode 100644 index 000000000..447682964 --- /dev/null +++ b/jme3-core/src/test/java/com/jme3/light/LightFilterTest.java @@ -0,0 +1,271 @@ +/* + * 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.light; + +import com.jme3.bounding.BoundingSphere; +import com.jme3.math.FastMath; +import com.jme3.math.Vector3f; +import com.jme3.renderer.Camera; +import com.jme3.scene.Geometry; +import com.jme3.scene.shape.Box; +import com.jme3.util.TempVars; +import org.junit.Before; +import org.junit.Test; + +/** + * Test light filtering for various light types. + * + * @author Kirill Vainer + */ +public class LightFilterTest { + + private DefaultLightFilter filter; + private Camera cam; + private Geometry geom; + private LightList list; + + private void checkFilteredLights(int expected) { + geom.updateGeometricState(); + filter.setCamera(cam); // setCamera resets the intersection cache + list.clear(); + filter.filterLights(geom, list); + assert list.size() == expected; + } + + @Before + public void setUp() { + filter = new DefaultLightFilter(); + + cam = new Camera(512, 512); + cam.setFrustumPerspective(45, 1, 1, 1000); + cam.setLocation(Vector3f.ZERO); + cam.lookAtDirection(Vector3f.UNIT_Z, Vector3f.UNIT_Y); + filter.setCamera(cam); + + Box box = new Box(1, 1, 1); + geom = new Geometry("geom", box); + geom.setLocalTranslation(0, 0, 10); + geom.updateGeometricState(); + list = new LightList(geom); + } + + @Test + public void testAmbientFiltering() { + geom.addLight(new AmbientLight()); + checkFilteredLights(1); // Ambient lights must never be filtered + + // Test for bounding Sphere + geom.setModelBound(new BoundingSphere(0.5f, Vector3f.ZERO)); + checkFilteredLights(1); // Ambient lights must never be filtered + } + + @Test + public void testDirectionalFiltering() { + geom.addLight(new DirectionalLight(Vector3f.UNIT_Y)); + checkFilteredLights(1); // Directional lights must never be filtered + + // Test for bounding Sphere + geom.setModelBound(new BoundingSphere(0.5f, Vector3f.ZERO)); + checkFilteredLights(1); // Directional lights must never be filtered + } + + @Test + public void testPointFiltering() { + PointLight pl = new PointLight(Vector3f.ZERO); + geom.addLight(pl); + checkFilteredLights(1); // Infinite point lights must never be filtered + + // Light at origin does not intersect geom which is at Z=10 + pl.setRadius(1); + checkFilteredLights(0); + + // Put it closer to geom, the very edge of the sphere touches the box. + // Still not considered an intersection though. + pl.setPosition(new Vector3f(0, 0, 8f)); + checkFilteredLights(0); + + // And more close - now its an intersection. + pl.setPosition(new Vector3f(0, 0, 8f + FastMath.ZERO_TOLERANCE)); + checkFilteredLights(1); + + // Move the geometry away + geom.move(0, 0, FastMath.ZERO_TOLERANCE); + checkFilteredLights(0); + + // Test if the algorithm converts the sphere + // to a box before testing the collision (incorrect) + float sqrt3 = FastMath.sqrt(3); + + pl.setPosition(new Vector3f(2, 2, 8)); + pl.setRadius(sqrt3); + checkFilteredLights(0); + + // Make it a wee bit larger. + pl.setRadius(sqrt3 + FastMath.ZERO_TOLERANCE); + checkFilteredLights(1); + + // Rotate the camera so it is up, light is outside frustum. + cam.lookAtDirection(Vector3f.UNIT_Y, Vector3f.UNIT_Y); + checkFilteredLights(0); + + // ================================== + // Tests for bounding Sphere + geom.setModelBound(new BoundingSphere(1f, Vector3f.ZERO)); + geom.setLocalTranslation(0, 0, 2); + pl.setPosition(new Vector3f(0, 0, 2f)); + + // Infinite point lights must never be filtered + pl.setRadius(0); + checkFilteredLights(1); + + pl.setRadius(1f); + // Put the light at the very close to the geom, + // the very edge of the sphere touches the other bounding sphere + // Still not considered an intersection though. + pl.setPosition(new Vector3f(0, 0, 0)); + checkFilteredLights(0); + + // And more close - now its an intersection. + pl.setPosition(new Vector3f(0, 0, 0f + FastMath.ZERO_TOLERANCE)); + checkFilteredLights(1); + + geom.setLocalTranslation(0, 0, 0); + // In this case its an intersection for pointLight v. box + // But not for pointLight v. sphere + // Vector3f(0, 0.5f, 0.5f).normalize().mult(2) ~ >= (0.0, 1.4142135, 1.4142135) + //pl.setPosition(new Vector3f(0, 0.5f, 0.5f).normalizeLocal().multLocal(2 + FastMath.ZERO_TOLERANCE)); + pl.setPosition(new Vector3f(0f, 1.4142135f, 1.4142135f).multLocal(1+FastMath.ZERO_TOLERANCE)); + checkFilteredLights(0); + + // Make the distance a wee bit closer, now its an intersection + //pl.setPosition(new Vector3f(0, 0.5f, 0.5f).normalizeLocal().multLocal(2 - FastMath.ZERO_TOLERANCE)); + pl.setPosition(new Vector3f(0f, 1.4142135f, 1.4142135f).multLocal(1-FastMath.ZERO_TOLERANCE)); + checkFilteredLights(1); + + // it's a point light, also test for the other corner + pl.setPosition(new Vector3f(0f, -1.4142135f, -1.4142135f).multLocal(1-FastMath.ZERO_TOLERANCE)); + checkFilteredLights(0); + + } + + @Test + public void testSpotFiltering() { + SpotLight sl = new SpotLight(Vector3f.ZERO, Vector3f.UNIT_Z); + sl.setSpotRange(0); + geom.addLight(sl); + checkFilteredLights(1); // Infinite spot lights are only filtered + // if the geometry is outside the infinite cone. + + TempVars vars = TempVars.get(); + try { + // The spot is not touching the near plane of the camera yet, + // should still be culled. + sl.setSpotRange(1f - FastMath.ZERO_TOLERANCE); + assert !sl.intersectsFrustum(cam, vars); + // should be culled from the geometry's PoV + checkFilteredLights(0); + + // Now it touches the near plane. + sl.setSpotRange(1f); + // still culled from the geometry's PoV + checkFilteredLights(0); + assert sl.intersectsFrustum(cam, vars); + } finally { + vars.release(); + } + + // make it barely reach the geometry + sl.setSpotRange(9f); + checkFilteredLights(0); + + // make it reach the geometry (touching its bound) + sl.setSpotRange(9f + FastMath.ZERO_TOLERANCE); + checkFilteredLights(1); + + // rotate the cone a bit so it no longer faces the geom + sl.setDirection(new Vector3f(0.316f, 0, 0.948f).normalizeLocal()); + checkFilteredLights(0); + + // extent the range much farther + sl.setSpotRange(20); + checkFilteredLights(0); + + // Create box of size X=10 (double the extent) + // now, the spot will touch the box. + geom.setMesh(new Box(5, 1, 1)); + checkFilteredLights(1); + + // ================================== + // Tests for bounding sphere, with a radius of 1f (in the box geom) + sl.setPosition(Vector3f.ZERO); + sl.setDirection(Vector3f.UNIT_Z); + geom.setLocalTranslation(Vector3f.ZERO); + geom.setModelBound(new BoundingSphere(1f, Vector3f.ZERO)); + + // Infinit spot lights are only filtered + // if the geometry is outside the infinite cone. + sl.setSpotRange(0); + checkFilteredLights(1); + + //the geommetry is outside the infinit cone (cone direction going away from the geom) + sl.setPosition(Vector3f.UNIT_Z.mult(1+FastMath.ZERO_TOLERANCE)); + checkFilteredLights(0); + + //place the spote ligth in the corner of the box geom, (in order to test bounding sphere) + sl.setDirection(new Vector3f(1, 1, 0).normalizeLocal()); + geom.setLocalTranslation(0, 0, 10); + sl.setPosition(sl.getDirection().mult(-2f).add(geom.getLocalTranslation())); + + // make it barely reach the sphere, incorect with a box + sl.setSpotRange(1f - FastMath.ZERO_TOLERANCE); + checkFilteredLights(0); + + // make it reach the sphere + sl.setSpotRange(1f + FastMath.ZERO_TOLERANCE); + checkFilteredLights(1); + + // extent the range + sl.setPosition(Vector3f.ZERO); + sl.setDirection(Vector3f.UNIT_Z); + sl.setSpotRange(20); + checkFilteredLights(1); + + // rotate the cone a bit so it no longer faces the geom + sl.setDirection(new Vector3f(0, 0.3f, 0.7f).normalizeLocal()); + checkFilteredLights(0); + + // Create sphere of size X=10 (double the radius) + // now, the spot will touch the sphere. + geom.setModelBound(new BoundingSphere(5f, Vector3f.ZERO)); + checkFilteredLights(1); + } +} diff --git a/jme3-core/src/test/java/com/jme3/light/LightSortTest.java b/jme3-core/src/test/java/com/jme3/light/LightSortTest.java new file mode 100644 index 000000000..593cc9d3e --- /dev/null +++ b/jme3-core/src/test/java/com/jme3/light/LightSortTest.java @@ -0,0 +1,115 @@ +/* + * 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.light; + +import com.jme3.math.Vector3f; +import com.jme3.scene.Geometry; +import com.jme3.scene.Mesh; +import com.jme3.scene.Node; +import org.junit.Test; + +/** + * Test light sorting (in the scene graph) for various light types. + * + * @author Kirill Vainer + */ +public class LightSortTest { + + @Test + public void testSimpleSort() { + Geometry g = new Geometry("test", new Mesh()); + LightList list = new LightList(g); + + list.add(new SpotLight(Vector3f.ZERO, Vector3f.UNIT_X)); + list.add(new PointLight(Vector3f.UNIT_X)); + list.add(new DirectionalLight(Vector3f.UNIT_X)); + list.add(new AmbientLight()); + + list.sort(true); + + assert list.get(0) instanceof AmbientLight; // Ambients always first + assert list.get(1) instanceof DirectionalLight; // .. then directionals + assert list.get(2) instanceof SpotLight; // Spot is 0 units away from geom + assert list.get(3) instanceof PointLight; // .. and point is 1 unit away. + } + + @Test + public void testSceneGraphSort() { + Node n = new Node("node"); + Geometry g = new Geometry("geom", new Mesh()); + SpotLight spot = new SpotLight(Vector3f.ZERO, Vector3f.UNIT_X); + PointLight point = new PointLight(Vector3f.UNIT_X); + DirectionalLight directional = new DirectionalLight(Vector3f.UNIT_X); + AmbientLight ambient = new AmbientLight(); + + // Some lights are on the node + n.addLight(spot); + n.addLight(point); + + // .. and some on the geometry. + g.addLight(directional); + g.addLight(ambient); + + n.attachChild(g); + n.updateGeometricState(); + + LightList list = g.getWorldLightList(); + + // check the sorting (when geom is at 0,0,0) + assert list.get(0) instanceof AmbientLight; + assert list.get(1) instanceof DirectionalLight; + assert list.get(2) instanceof SpotLight; + assert list.get(3) instanceof PointLight; + + // move the geometry closer to the point light + g.setLocalTranslation(Vector3f.UNIT_X); + n.updateGeometricState(); + + assert list.get(0) instanceof AmbientLight; + assert list.get(1) instanceof DirectionalLight; + assert list.get(2) instanceof PointLight; + assert list.get(3) instanceof SpotLight; + + // now move the point light away from the geometry + // and the spot light closer + + // XXX: doesn't work! jME can't detect that the light moved! +// point.setPosition(Vector3f.ZERO); +// spot.setPosition(Vector3f.UNIT_X); +// n.updateGeometricState(); +// +// assert list.get(0) instanceof AmbientLight; +// assert list.get(1) instanceof DirectionalLight; +// assert list.get(2) instanceof SpotLight; +// assert list.get(3) instanceof PointLight; + } +} diff --git a/jme3-core/src/test/java/com/jme3/material/plugins/J3MLoaderTest.java b/jme3-core/src/test/java/com/jme3/material/plugins/J3MLoaderTest.java new file mode 100644 index 000000000..6438baaac --- /dev/null +++ b/jme3-core/src/test/java/com/jme3/material/plugins/J3MLoaderTest.java @@ -0,0 +1,117 @@ +package com.jme3.material.plugins; + +import com.jme3.asset.AssetInfo; +import com.jme3.asset.AssetKey; +import com.jme3.asset.AssetManager; +import com.jme3.asset.TextureKey; +import com.jme3.material.MatParamTexture; +import com.jme3.material.Material; +import com.jme3.material.MaterialDef; +import com.jme3.shader.VarType; +import com.jme3.texture.Texture; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.runners.MockitoJUnitRunner; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * @author Daniel Johansson + * @since 2015-07-20 + */ +@RunWith(MockitoJUnitRunner.class) +public class J3MLoaderTest { + + private J3MLoader j3MLoader; + + @Mock + private AssetInfo assetInfo; + + @Mock + private AssetManager assetManager; + + @Mock + private AssetKey assetKey; + + @Mock + private MaterialDef materialDef; + + @Before + public void setUp() throws Exception { + when(assetKey.getExtension()).thenReturn(".j3m"); + when(assetInfo.getManager()).thenReturn(assetManager); + when(assetInfo.getKey()).thenReturn(assetKey); + when(assetManager.loadAsset(any(AssetKey.class))).thenReturn(materialDef); + + j3MLoader = new J3MLoader(); + } + + @Test + public void oldStyleTextureParameters_shouldBeSupported() throws Exception { + when(assetInfo.openStream()).thenReturn(J3MLoader.class.getResourceAsStream("/texture-parameters-oldstyle.j3m")); + + final Texture textureOldStyle = Mockito.mock(Texture.class); + final Texture textureOldStyleUsingQuotes = Mockito.mock(Texture.class); + + final TextureKey textureKeyUsingQuotes = setupMockForTexture("OldStyleUsingQuotes", "old style using quotes/texture.png", true, textureOldStyleUsingQuotes); + final TextureKey textureKeyOldStyle = setupMockForTexture("OldStyle", "old style/texture.png", true, textureOldStyle); + + j3MLoader.load(assetInfo); + + verify(assetManager).loadTexture(textureKeyUsingQuotes); + verify(assetManager).loadTexture(textureKeyOldStyle); + verify(textureOldStyle).setWrap(Texture.WrapMode.Repeat); + verify(textureOldStyleUsingQuotes).setWrap(Texture.WrapMode.Repeat); + } + + @Test + public void newStyleTextureParameters_shouldBeSupported() throws Exception { + when(assetInfo.openStream()).thenReturn(J3MLoader.class.getResourceAsStream("/texture-parameters-newstyle.j3m")); + + final Texture textureNoParameters = Mockito.mock(Texture.class); + final Texture textureFlip = Mockito.mock(Texture.class); + final Texture textureRepeat = Mockito.mock(Texture.class); + final Texture textureRepeatAxis = Mockito.mock(Texture.class); + final Texture textureMin = Mockito.mock(Texture.class); + final Texture textureMag = Mockito.mock(Texture.class); + final Texture textureCombined = Mockito.mock(Texture.class); + + final TextureKey textureKeyNoParameters = setupMockForTexture("Empty", "empty.png", false, textureNoParameters); + final TextureKey textureKeyFlip = setupMockForTexture("Flip", "flip.png", true, textureFlip); + setupMockForTexture("Repeat", "repeat.png", false, textureRepeat); + setupMockForTexture("RepeatAxis", "repeat-axis.png", false, textureRepeatAxis); + setupMockForTexture("Min", "min.png", false, textureMin); + setupMockForTexture("Mag", "mag.png", false, textureMag); + setupMockForTexture("Combined", "combined.png", true, textureCombined); + + j3MLoader.load(assetInfo); + + verify(assetManager).loadTexture(textureKeyNoParameters); + verify(assetManager).loadTexture(textureKeyFlip); + + verify(textureRepeat).setWrap(Texture.WrapMode.Repeat); + verify(textureRepeatAxis).setWrap(Texture.WrapAxis.T, Texture.WrapMode.Repeat); + verify(textureMin).setMinFilter(Texture.MinFilter.Trilinear); + verify(textureMag).setMagFilter(Texture.MagFilter.Bilinear); + + verify(textureCombined).setMagFilter(Texture.MagFilter.Nearest); + verify(textureCombined).setMinFilter(Texture.MinFilter.BilinearNoMipMaps); + verify(textureCombined).setWrap(Texture.WrapMode.Repeat); + } + + private TextureKey setupMockForTexture(final String paramName, final String path, final boolean flipY, final Texture texture) { + when(materialDef.getMaterialParam(paramName)).thenReturn(new MatParamTexture(VarType.Texture2D, paramName, texture, 0, null)); + + final TextureKey textureKey = new TextureKey(path, flipY); + textureKey.setGenerateMips(true); + + when(assetManager.loadTexture(textureKey)).thenReturn(texture); + + return textureKey; + } +} diff --git a/jme3-core/src/test/java/com/jme3/math/FastMathTest.java b/jme3-core/src/test/java/com/jme3/math/FastMathTest.java new file mode 100644 index 000000000..a74390d42 --- /dev/null +++ b/jme3-core/src/test/java/com/jme3/math/FastMathTest.java @@ -0,0 +1,59 @@ +/* + * 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.math; + +import org.junit.Test; + +/** + * Verifies that algorithms in {@link FastMath} are working correctly. + * + * @author Kirill Vainer + */ +public class FastMathTest { + + private int nearestPowerOfTwoSlow(int number) { + return (int) Math.pow(2, Math.ceil(Math.log(number) / Math.log(2))); + } + + @Test + public void testNearestPowerOfTwo() { + for (int i = -100; i < 1; i++) { + assert FastMath.nearestPowerOfTwo(i) == 1; + } + for (int i = 1; i < 10000; i++) { + int nextPowerOf2 = FastMath.nearestPowerOfTwo(i); + assert i <= nextPowerOf2; + assert FastMath.isPowerOfTwo(nextPowerOf2); + assert nextPowerOf2 == nearestPowerOfTwoSlow(i); + } + } +} diff --git a/jme3-core/src/test/java/com/jme3/util/ListMapTest.java b/jme3-core/src/test/java/com/jme3/util/ListMapTest.java new file mode 100644 index 000000000..5f4cf3eee --- /dev/null +++ b/jme3-core/src/test/java/com/jme3/util/ListMapTest.java @@ -0,0 +1,73 @@ +/* + * 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.util; + +import java.util.Map.Entry; +import org.junit.Test; + +/** + * Check if the {@link ListMap} class is working correctly. + * + * @author Kirill Vainer + */ +public class ListMapTest { + + @Test + public void testListMap() { + ListMap listMap = new ListMap(); + listMap.put("bob", "hello"); + assert "hello".equals(listMap.get("bob")); + assert "hello".equals(listMap.remove("bob")); + assert listMap.size() == 0; + assert listMap.isEmpty(); + + listMap.put("abc", "1"); + listMap.put("def", "2"); + listMap.put("ghi", "3"); + listMap.put("jkl", "4"); + listMap.put("mno", "5"); + assert "3".equals(listMap.get("ghi")); + assert listMap.size() == 5; + assert !listMap.isEmpty(); + + // check iteration order, should be consistent + for (int i = 0; i < listMap.size(); i++) { + String expectedValue = Integer.toString(i + 1); + String key = listMap.getKey(i); + String value = listMap.getValue(i); + Entry entry = listMap.getEntry(i); + assert key.equals(entry.getKey()); + assert value.equals(entry.getValue()); + assert expectedValue.equals(value); + } + } +} diff --git a/jme3-core/src/test/resources/texture-parameters-newstyle.j3m b/jme3-core/src/test/resources/texture-parameters-newstyle.j3m new file mode 100644 index 000000000..a7619d948 --- /dev/null +++ b/jme3-core/src/test/resources/texture-parameters-newstyle.j3m @@ -0,0 +1,11 @@ +Material Test : matdef.j3md { + MaterialParameters { + Empty: "empty.png" + Flip: Flip "flip.png" + Repeat: WrapRepeat "repeat.png" + Min: MinTrilinear "min.png" + Mag: MagBilinear "mag.png" + RepeatAxis: WrapRepeat_T "repeat-axis.png" + Combined: MagNearest MinBilinearNoMipMaps Flip WrapRepeat "combined.png" + } +} \ No newline at end of file diff --git a/jme3-core/src/test/resources/texture-parameters-oldstyle.j3m b/jme3-core/src/test/resources/texture-parameters-oldstyle.j3m new file mode 100644 index 000000000..7f34af464 --- /dev/null +++ b/jme3-core/src/test/resources/texture-parameters-oldstyle.j3m @@ -0,0 +1,6 @@ +Material Test : matdef.j3md { + MaterialParameters { + OldStyle: Flip Repeat old style/texture.png + OldStyleUsingQuotes: Repeat Flip "old style using quotes/texture.png" + } +} \ No newline at end of file diff --git a/jme3-desktop/src/main/java/com/jme3/app/state/MjpegFileWriter.java b/jme3-desktop/src/main/java/com/jme3/app/state/MjpegFileWriter.java index 4c4cd6562..b3d751465 100644 --- a/jme3-desktop/src/main/java/com/jme3/app/state/MjpegFileWriter.java +++ b/jme3-desktop/src/main/java/com/jme3/app/state/MjpegFileWriter.java @@ -205,7 +205,6 @@ public class MjpegFileWriter { baos.write(fcc3); baos.write(intBytes(swapInt(listSize))); baos.write(fcc4); - baos.close(); return baos.toByteArray(); } @@ -275,7 +274,6 @@ public class MjpegFileWriter { baos.write(intBytes(swapInt(dwReserved[1]))); baos.write(intBytes(swapInt(dwReserved[2]))); baos.write(intBytes(swapInt(dwReserved[3]))); - baos.close(); return baos.toByteArray(); } @@ -295,7 +293,6 @@ public class MjpegFileWriter { baos.write(fcc); baos.write(intBytes(swapInt(size))); baos.write(fcc2); - baos.close(); return baos.toByteArray(); } @@ -365,7 +362,6 @@ public class MjpegFileWriter { baos.write(intBytes(swapInt(top))); baos.write(intBytes(swapInt(right))); baos.write(intBytes(swapInt(bottom))); - baos.close(); return baos.toByteArray(); } @@ -420,7 +416,6 @@ public class MjpegFileWriter { baos.write(intBytes(swapInt(biYPelsPerMeter))); baos.write(intBytes(swapInt(biClrUsed))); baos.write(intBytes(swapInt(biClrImportant))); - baos.close(); return baos.toByteArray(); } @@ -445,7 +440,6 @@ public class MjpegFileWriter { baos.write(fcc); baos.write(intBytes(swapInt(listSize))); baos.write(fcc2); - baos.close(); return baos.toByteArray(); } @@ -480,8 +474,6 @@ public class MjpegFileWriter { baos.write(in.toBytes()); } - baos.close(); - return baos.toByteArray(); } } @@ -504,7 +496,6 @@ public class MjpegFileWriter { baos.write(intBytes(swapInt(dwFlags))); baos.write(intBytes(swapInt(dwOffset))); baos.write(intBytes(swapInt(dwSize))); - baos.close(); return baos.toByteArray(); } @@ -525,7 +516,6 @@ public class MjpegFileWriter { baos.write(fcc); baos.write(intBytes(swapInt(size))); baos.write(data); - baos.close(); return baos.toByteArray(); } @@ -552,7 +542,6 @@ public class MjpegFileWriter { imgWrtr.write(null, new IIOImage(bi, null, null), jpgWrtPrm); imgOutStrm.close(); - baos.close(); return baos.toByteArray(); } } diff --git a/jme3-desktop/src/main/java/com/jme3/system/NativeLibraryLoader.java b/jme3-desktop/src/main/java/com/jme3/system/NativeLibraryLoader.java index 2917350f6..32bd681c5 100644 --- a/jme3-desktop/src/main/java/com/jme3/system/NativeLibraryLoader.java +++ b/jme3-desktop/src/main/java/com/jme3/system/NativeLibraryLoader.java @@ -31,11 +31,7 @@ */ package com.jme3.system; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import java.io.*; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; @@ -48,9 +44,8 @@ import java.util.logging.Logger; /** * Utility class to register, extract, and load native libraries. *
- * Register your own libraries via the - * {@link #registerNativeLibrary(java.lang.String, com.jme3.system.Platform, java.lang.String, boolean) } - * method, for each platform. + * Register your own libraries via the {@link #registerNativeLibrary(String, Platform, String, String)} method, for + * each platform. * You can then extract this library (depending on platform), by * using {@link #loadNativeLibrary(java.lang.String, boolean) }. *
@@ -132,7 +127,7 @@ public final class NativeLibraryLoader { registerNativeLibrary("lwjgl", Platform.Linux64, "native/linux/liblwjgl64.so"); registerNativeLibrary("lwjgl", Platform.MacOSX32, "native/macosx/liblwjgl.dylib"); registerNativeLibrary("lwjgl", Platform.MacOSX64, "native/macosx/liblwjgl.dylib"); - + // OpenAL // For OSX: Need to add lib prefix when extracting registerNativeLibrary("openal", Platform.Windows32, "native/windows/OpenAL32.dll"); @@ -141,7 +136,40 @@ public final class NativeLibraryLoader { registerNativeLibrary("openal", Platform.Linux64, "native/linux/libopenal64.so"); registerNativeLibrary("openal", Platform.MacOSX32, "native/macosx/openal.dylib", "libopenal.dylib"); registerNativeLibrary("openal", Platform.MacOSX64, "native/macosx/openal.dylib", "libopenal.dylib"); - + + // LWJGL 3.x + registerNativeLibrary("lwjgl3", Platform.Windows32, "native/windows/lwjgl32.dll"); + registerNativeLibrary("lwjgl3", Platform.Windows64, "native/windows/lwjgl.dll"); + registerNativeLibrary("lwjgl3", Platform.Linux32, "native/linux/liblwjgl32.so"); + registerNativeLibrary("lwjgl3", Platform.Linux64, "native/linux/liblwjgl.so"); + registerNativeLibrary("lwjgl3", Platform.MacOSX32, "native/macosx/liblwjgl.dylib"); + registerNativeLibrary("lwjgl3", Platform.MacOSX64, "native/macosx/liblwjgl.dylib"); + + // GLFW for LWJGL 3.x + registerNativeLibrary("glfw-lwjgl3", Platform.Windows32, "native/windows/glfw32.dll"); + registerNativeLibrary("glfw-lwjgl3", Platform.Windows64, "native/windows/glfw.dll"); + registerNativeLibrary("glfw-lwjgl3", Platform.Linux32, "native/linux/libglfw32.so"); + registerNativeLibrary("glfw-lwjgl3", Platform.Linux64, "native/linux/libglfw.dll"); + registerNativeLibrary("glfw-lwjgl3", Platform.MacOSX32, "native/macosx/libglfw.dylib"); + registerNativeLibrary("glfw-lwjgl3", Platform.MacOSX64, "native/macosx/libglfw.dylib"); + + // jemalloc for LWJGL 3.x + registerNativeLibrary("jemalloc-lwjgl3", Platform.Windows32, "native/windows/jemalloc32.dll"); + registerNativeLibrary("jemalloc-lwjgl3", Platform.Windows64, "native/windows/jemalloc.dll"); + registerNativeLibrary("jemalloc-lwjgl3", Platform.Linux32, "native/linux/libjemalloc32.so"); + registerNativeLibrary("jemalloc-lwjgl3", Platform.Linux64, "native/linux/libjemalloc.so"); + registerNativeLibrary("jemalloc-lwjgl3", Platform.MacOSX32, "native/macosx/libjemalloc.dylib"); + registerNativeLibrary("jemalloc-lwjgl3", Platform.MacOSX64, "native/macosx/libjemalloc.dylib"); + + // OpenAL for LWJGL 3.x + // For OSX: Need to add lib prefix when extracting + registerNativeLibrary("openal-lwjgl3", Platform.Windows32, "native/windows/OpenAL32.dll"); + registerNativeLibrary("openal-lwjgl3", Platform.Windows64, "native/windows/OpenAL.dll"); + registerNativeLibrary("openal-lwjgl3", Platform.Linux32, "native/linux/libopenal32.so"); + registerNativeLibrary("openal-lwjgl3", Platform.Linux64, "native/linux/libopenal.so"); + registerNativeLibrary("openal-lwjgl3", Platform.MacOSX32, "native/macosx/openal.dylib", "libopenal.dylib"); + registerNativeLibrary("openal-lwjgl3", Platform.MacOSX64, "native/macosx/openal.dylib", "libopenal.dylib"); + // BulletJme registerNativeLibrary("bulletjme", Platform.Windows32, "native/windows/x86/bulletjme.dll"); registerNativeLibrary("bulletjme", Platform.Windows64, "native/windows/x86_64/bulletjme.dll"); @@ -461,7 +489,7 @@ public final class NativeLibraryLoader { if (url == null) { return; } - + String loadedAsFileName; if (library.getExtractedAsName() != null) { loadedAsFileName = library.getExtractedAsName(); @@ -508,7 +536,7 @@ public final class NativeLibraryLoader { throw new UnsupportedOperationException("JVM is running under " + "reduced permissions. Cannot load native libraries."); } - + Platform platform = JmeSystem.getPlatform(); NativeLibrary library = nativeLibraryMap.get(new NativeLibrary.Key(name, platform)); @@ -526,27 +554,28 @@ public final class NativeLibraryLoader { } } - String pathInJar = library.getPathInNativesJar(); - + final String pathInJar = library.getPathInNativesJar(); + if (pathInJar == null) { // This platform does not require the native library to be loaded. return; } - String fileNameInJar; + final String fileNameInJar; + if (pathInJar.contains("/")) { fileNameInJar = pathInJar.substring(pathInJar.lastIndexOf("/") + 1); } else { fileNameInJar = pathInJar; } - + URL url = Thread.currentThread().getContextClassLoader().getResource(pathInJar); if (url == null) { // Try the root of the classpath as well. url = Thread.currentThread().getContextClassLoader().getResource(fileNameInJar); } - + if (url == null) { // Attempt to load it as a system library. String unmappedName = unmapLibraryName(fileNameInJar); @@ -642,7 +671,7 @@ public final class NativeLibraryLoader { } finally { // XXX: HACK. Vary loading method based on library name.. // lwjgl and jinput handle loading by themselves. - if (name.equals("lwjgl")) { + if (name.equals("lwjgl") || name.equals("lwjgl3")) { System.setProperty("org.lwjgl.librarypath", extactionDirectory.getAbsolutePath()); } else if (name.equals("jinput")) { diff --git a/jme3-effects/src/main/java/com/jme3/post/filters/FXAAFilter.java b/jme3-effects/src/main/java/com/jme3/post/filters/FXAAFilter.java index 7b1dfb37a..37fec3bcd 100644 --- a/jme3-effects/src/main/java/com/jme3/post/filters/FXAAFilter.java +++ b/jme3-effects/src/main/java/com/jme3/post/filters/FXAAFilter.java @@ -69,6 +69,12 @@ public class FXAAFilter extends Filter { protected Material getMaterial() { return material; } + + @Override + protected boolean isRequiresBilinear() { + // FXAA wants the input texture to be filtered. + return true; + } public void setSpanMax(float spanMax) { this.spanMax = spanMax; diff --git a/jme3-effects/src/main/resources/Common/MatDefs/Post/CrossHatch15.frag b/jme3-effects/src/main/resources/Common/MatDefs/Post/CrossHatch15.frag index 8daa4f7fd..842484604 100644 --- a/jme3-effects/src/main/resources/Common/MatDefs/Post/CrossHatch15.frag +++ b/jme3-effects/src/main/resources/Common/MatDefs/Post/CrossHatch15.frag @@ -17,7 +17,9 @@ uniform float m_Luminance5; uniform float m_LineDistance; uniform float m_LineThickness; - + +out vec4 fragColor; + void main() { vec4 texVal = getColor(m_Texture, texCoord); float linePixel = 0; @@ -49,5 +51,5 @@ void main() { // Mix paper color with existing color information vec4 paperColor = mix(m_PaperColor, texVal, m_ColorInfluencePaper); - gl_FragColor = mix(paperColor, lineColor, linePixel); + fragColor = mix(paperColor, lineColor, linePixel); } \ No newline at end of file diff --git a/jme3-effects/src/main/resources/Common/MatDefs/Post/DepthOfField15.frag b/jme3-effects/src/main/resources/Common/MatDefs/Post/DepthOfField15.frag index 80cf3c530..de3c586ae 100644 --- a/jme3-effects/src/main/resources/Common/MatDefs/Post/DepthOfField15.frag +++ b/jme3-effects/src/main/resources/Common/MatDefs/Post/DepthOfField15.frag @@ -11,6 +11,8 @@ uniform float m_YScale; vec2 m_NearFar = vec2( 0.1, 1000.0 ); +out vec4 fragColor; + void main() { vec4 texVal = getColor( m_Texture, texCoord ); @@ -44,7 +46,7 @@ void main() { if( unfocus < 0.2 ) { // If we are mostly in focus then don't bother with the // convolution filter - gl_FragColor = texVal; + fragColor = texVal; } else { // Perform a wide convolution filter and we scatter it // a bit to avoid some texture look-ups. Instead of @@ -83,7 +85,7 @@ void main() { sum = sum / 12.0; - gl_FragColor = mix( texVal, sum, unfocus ); + fragColor = mix( texVal, sum, unfocus ); // I used this for debugging the range // gl_FragColor.r = unfocus; diff --git a/jme3-effects/src/main/resources/Common/MatDefs/Post/Fade15.frag b/jme3-effects/src/main/resources/Common/MatDefs/Post/Fade15.frag index c99de34ad..7bd2d4882 100644 --- a/jme3-effects/src/main/resources/Common/MatDefs/Post/Fade15.frag +++ b/jme3-effects/src/main/resources/Common/MatDefs/Post/Fade15.frag @@ -4,8 +4,9 @@ uniform COLORTEXTURE m_Texture; uniform float m_Value; in vec2 texCoord; +out vec4 fragColor; void main() { vec4 texVal = getColor(m_Texture, texCoord); - gl_FragColor = texVal * m_Value; + fragColor = texVal * m_Value; } \ No newline at end of file diff --git a/jme3-effects/src/main/resources/Common/MatDefs/Post/Fog15.frag b/jme3-effects/src/main/resources/Common/MatDefs/Post/Fog15.frag index 65a340723..7a1f756be 100644 --- a/jme3-effects/src/main/resources/Common/MatDefs/Post/Fog15.frag +++ b/jme3-effects/src/main/resources/Common/MatDefs/Post/Fog15.frag @@ -8,6 +8,7 @@ uniform float m_FogDensity; uniform float m_FogDistance; in vec2 texCoord; +out vec4 fragColor; vec2 m_FrustumNearFar=vec2(1.0,m_FogDistance); const float LOG2 = 1.442695; @@ -19,6 +20,6 @@ void main() { float fogFactor = exp2( -m_FogDensity * m_FogDensity * depth * depth * LOG2 ); fogFactor = clamp(fogFactor, 0.0, 1.0); - gl_FragColor =mix(m_FogColor,texVal,fogFactor); + fragColor =mix(m_FogColor,texVal,fogFactor); } \ No newline at end of file diff --git a/jme3-effects/src/main/resources/Common/MatDefs/Post/Overlay15.frag b/jme3-effects/src/main/resources/Common/MatDefs/Post/Overlay15.frag index 7dc4d1e4b..d6609f38f 100644 --- a/jme3-effects/src/main/resources/Common/MatDefs/Post/Overlay15.frag +++ b/jme3-effects/src/main/resources/Common/MatDefs/Post/Overlay15.frag @@ -3,9 +3,10 @@ uniform COLORTEXTURE m_Texture; uniform vec4 m_Color; in vec2 texCoord; +out vec4 fragColor; void main() { vec4 texVal = getColor(m_Texture, texCoord); - gl_FragColor = texVal * m_Color; + fragColor = texVal * m_Color; } diff --git a/jme3-effects/src/main/resources/Common/MatDefs/Post/Posterization15.frag b/jme3-effects/src/main/resources/Common/MatDefs/Post/Posterization15.frag index f55ac5bf2..5d8d7072b 100644 --- a/jme3-effects/src/main/resources/Common/MatDefs/Post/Posterization15.frag +++ b/jme3-effects/src/main/resources/Common/MatDefs/Post/Posterization15.frag @@ -2,7 +2,8 @@ uniform COLORTEXTURE m_Texture; in vec2 texCoord; - +out vec4 fragColor; + uniform int m_NumColors; uniform float m_Gamma; uniform float m_Strength; @@ -16,5 +17,5 @@ void main() { texVal = texVal / m_NumColors; texVal = pow(texVal, vec4(1.0/m_Gamma)); - gl_FragColor = mix(getColor(m_Texture, texCoord), texVal, m_Strength); + fragColor = mix(getColor(m_Texture, texCoord), texVal, m_Strength); } \ No newline at end of file diff --git a/jme3-effects/src/main/resources/Common/MatDefs/Post/bloomFinal15.frag b/jme3-effects/src/main/resources/Common/MatDefs/Post/bloomFinal15.frag index 34268c565..b8f322d11 100644 --- a/jme3-effects/src/main/resources/Common/MatDefs/Post/bloomFinal15.frag +++ b/jme3-effects/src/main/resources/Common/MatDefs/Post/bloomFinal15.frag @@ -6,10 +6,11 @@ uniform sampler2D m_BloomTex; uniform float m_BloomIntensity; in vec2 texCoord; +out vec4 fragColor; void main(){ vec4 colorRes = getColor(m_Texture,texCoord); vec4 bloom = texture2D(m_BloomTex, texCoord); - gl_FragColor = bloom * m_BloomIntensity + colorRes; + fragColor = bloom * m_BloomIntensity + colorRes; } diff --git a/jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssaoBlur15.frag b/jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssaoBlur15.frag index c1112b23b..be2e6bcde 100644 --- a/jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssaoBlur15.frag +++ b/jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssaoBlur15.frag @@ -10,6 +10,7 @@ uniform float m_YScale; uniform vec2 m_FrustumNearFar; in vec2 texCoord; +out vec4 fragColor; vec4 getResult(vec4 color){ @@ -125,7 +126,7 @@ float readDepth(in vec2 uv){ void main(){ // float depth =texture2D(m_DepthTexture,uv).r; - gl_FragColor=getResult(convolutionFilter()); + fragColor=getResult(convolutionFilter()); // gl_FragColor=getResult(bilateralFilter()); // gl_FragColor=getColor(m_SSAOMap,texCoord); diff --git a/jme3-examples/build.gradle b/jme3-examples/build.gradle index 9ddb9cf78..09e71997e 100644 --- a/jme3-examples/build.gradle +++ b/jme3-examples/build.gradle @@ -21,6 +21,7 @@ dependencies { // compile project(':jme3-bullet-native') compile project(':jme3-jbullet') compile project(':jme3-jogg') + compile project(':jme3-jogl') compile project(':jme3-lwjgl') compile project(':jme3-networking') compile project(':jme3-niftygui') diff --git a/jme3-examples/src/main/java/jme3test/asset/TestOnlineJar.java b/jme3-examples/src/main/java/jme3test/asset/TestOnlineJar.java index e12769cd8..036d9318c 100644 --- a/jme3-examples/src/main/java/jme3test/asset/TestOnlineJar.java +++ b/jme3-examples/src/main/java/jme3test/asset/TestOnlineJar.java @@ -36,10 +36,10 @@ import com.jme3.app.SimpleApplication; import com.jme3.asset.TextureKey; import com.jme3.asset.plugins.HttpZipLocator; import com.jme3.material.Material; -import com.jme3.math.Vector3f; import com.jme3.scene.Geometry; import com.jme3.scene.shape.Quad; import com.jme3.texture.Texture; +import com.jme3.ui.Picture; /** * This tests loading a file from a jar stored online. @@ -59,22 +59,27 @@ public class TestOnlineJar extends SimpleApplication { quadMesh.updateGeometry(1, 1, true); Geometry quad = new Geometry("Textured Quad", quadMesh); - assetManager.registerLocator("http://jmonkeyengine.googlecode.com/files/town.zip", - HttpZipLocator.class); + + assetManager.registerLocator("http://jmonkeyengine.googlecode.com/files/town.zip", + HttpZipLocator.class); + assetManager.registerLocator("http://jmonkeyengine.googlecode.com/files/wildhouse.zip", + HttpZipLocator.class); - TextureKey key = new TextureKey("grass.jpg", false); - key.setGenerateMips(true); - Texture tex = assetManager.loadTexture(key); - - Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); - mat.setTexture("ColorMap", tex); - quad.setMaterial(mat); - - float aspect = tex.getImage().getWidth() / (float) tex.getImage().getHeight(); - quad.setLocalScale(new Vector3f(aspect * 1.5f, 1.5f, 1)); - quad.center(); - - rootNode.attachChild(quad); + Picture pic1 = new Picture("Picture1"); + pic1.move(0, 0, -1); + pic1.setPosition(0, 0); + pic1.setWidth(128); + pic1.setHeight(128); + pic1.setImage(assetManager, "grass.jpg", false); + guiNode.attachChild(pic1); + + Picture pic2 = new Picture("Picture1"); + pic2.move(0, 0, -1); + pic2.setPosition(128, 0); + pic2.setWidth(128); + pic2.setHeight(128); + pic2.setImage(assetManager, "glasstile2.png", false); + guiNode.attachChild(pic2); } } diff --git a/jme3-examples/src/main/java/jme3test/model/anim/TestCustomAnim.java b/jme3-examples/src/main/java/jme3test/model/anim/TestCustomAnim.java index e220eeb37..b5eeb5365 100644 --- a/jme3-examples/src/main/java/jme3test/model/anim/TestCustomAnim.java +++ b/jme3-examples/src/main/java/jme3test/model/anim/TestCustomAnim.java @@ -73,6 +73,13 @@ public class TestCustomAnim extends SimpleApplication { Box box = new Box(1, 1, 1); + VertexBuffer weightsHW = new VertexBuffer(Type.HWBoneWeight); + VertexBuffer indicesHW = new VertexBuffer(Type.HWBoneIndex); + indicesHW.setUsage(Usage.CpuOnly); + weightsHW.setUsage(Usage.CpuOnly); + box.setBuffer(weightsHW); + box.setBuffer(indicesHW); + // Setup bone weight buffer FloatBuffer weights = FloatBuffer.allocate( box.getVertexCount() * 4 ); VertexBuffer weightsBuf = new VertexBuffer(Type.BoneWeight); diff --git a/jme3-examples/src/main/java/jme3test/renderer/TestInconsistentCompareDetection.java b/jme3-examples/src/main/java/jme3test/renderer/TestInconsistentCompareDetection.java index a93a75c16..c1f99b35d 100644 --- a/jme3-examples/src/main/java/jme3test/renderer/TestInconsistentCompareDetection.java +++ b/jme3-examples/src/main/java/jme3test/renderer/TestInconsistentCompareDetection.java @@ -63,7 +63,7 @@ public class TestInconsistentCompareDetection extends SimpleApplication { cam.setLocation(new Vector3f(-11.674385f, 7.892636f, 33.133106f)); cam.setRotation(new Quaternion(0.06426433f, 0.90940624f, -0.15329266f, 0.38125014f)); - Material m = new Material(assetManager, "Common/MatDefs/Misc/ColoredTextured.j3md"); + Material m = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); m.setColor("Color", ColorRGBA.White); t1 = assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall.jpg"); diff --git a/jme3-jbullet/build.gradle b/jme3-jbullet/build.gradle index bcf1660bc..0e3967ab2 100644 --- a/jme3-jbullet/build.gradle +++ b/jme3-jbullet/build.gradle @@ -16,4 +16,5 @@ dependencies { compile files('../lib/jbullet.jar', '../lib/stack-alloc.jar') compile project(':jme3-core') compile project(':jme3-terrain') +// compile project(':jme3-bullet') } diff --git a/jme3-jogl/build.gradle b/jme3-jogl/build.gradle index e82139729..df24c15ec 100644 --- a/jme3-jogl/build.gradle +++ b/jme3-jogl/build.gradle @@ -5,7 +5,7 @@ if (!hasProperty('mainClass')) { dependencies { compile project(':jme3-core') compile project(':jme3-desktop') - compile 'org.jogamp.gluegen:gluegen-rt-main:2.3.1' - compile 'org.jogamp.jogl:jogl-all-main:2.3.1' - compile 'org.jogamp.joal:joal-main:2.3.1' + compile 'org.jogamp.gluegen:gluegen-rt-main:2.3.2' + compile 'org.jogamp.jogl:jogl-all-main:2.3.2' + compile 'org.jogamp.joal:joal-main:2.3.2' } diff --git a/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglGL.java b/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglGL.java new file mode 100644 index 000000000..3da2ed49e --- /dev/null +++ b/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglGL.java @@ -0,0 +1,603 @@ +package com.jme3.renderer.jogl; + +import com.jme3.renderer.RendererException; +import com.jme3.renderer.opengl.GL; +import com.jme3.renderer.opengl.GL2; +import com.jme3.renderer.opengl.GL3; + +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; + +import com.jme3.renderer.opengl.GL4; +import com.jogamp.opengl.GLContext; + +public class JoglGL implements GL, GL2, GL3, GL4 { + + private static int getLimitBytes(ByteBuffer buffer) { + checkLimit(buffer); + return buffer.limit(); + } + + private static int getLimitBytes(ShortBuffer buffer) { + checkLimit(buffer); + return buffer.limit() * 2; + } + + private static int getLimitBytes(FloatBuffer buffer) { + checkLimit(buffer); + return buffer.limit() * 4; + } + + private static int getLimitCount(Buffer buffer, int elementSize) { + checkLimit(buffer); + return buffer.limit() / elementSize; + } + + private static void checkLimit(Buffer buffer) { + if (buffer == null) { + return; + } + if (buffer.limit() == 0) { + throw new RendererException("Attempting to upload empty buffer (limit = 0), that's an error"); + } + if (buffer.remaining() == 0) { + throw new RendererException("Attempting to upload empty buffer (remaining = 0), that's an error"); + } + } + + @Override + public void resetStats() { + } + + @Override + public void glActiveTexture(int param1) { + GLContext.getCurrentGL().glActiveTexture(param1); + } + + @Override + public void glAlphaFunc(int param1, float param2) { + GLContext.getCurrentGL().getGL2ES1().glAlphaFunc(param1, param2); + } + + @Override + public void glAttachShader(int param1, int param2) { + GLContext.getCurrentGL().getGL2ES2().glAttachShader(param1, param2); + } + + @Override + public void glBindBuffer(int param1, int param2) { + GLContext.getCurrentGL().glBindBuffer(param1, param2); + } + + @Override + public void glBindTexture(int param1, int param2) { + GLContext.getCurrentGL().glBindTexture(param1, param2); + } + + @Override + public void glBlendFunc(int param1, int param2) { + GLContext.getCurrentGL().glBlendFunc(param1, param2); + } + + @Override + public void glBufferData(int param1, long param2, int param3) { + GLContext.getCurrentGL().glBufferData(param1, param2, null, param3); + } + + @Override + public void glBufferData(int param1, FloatBuffer param2, int param3) { + checkLimit(param2); + GLContext.getCurrentGL().glBufferData(param1, getLimitBytes(param2), param2, param3); + } + + @Override + public void glBufferData(int param1, ShortBuffer param2, int param3) { + checkLimit(param2); + GLContext.getCurrentGL().glBufferData(param1, getLimitBytes(param2), param2, param3); + } + + @Override + public void glBufferData(int param1, ByteBuffer param2, int param3) { + checkLimit(param2); + GLContext.getCurrentGL().glBufferData(param1, getLimitBytes(param2), param2, param3); + } + + @Override + public void glBufferSubData(int param1, long param2, FloatBuffer param3) { + checkLimit(param3); + GLContext.getCurrentGL().glBufferSubData(param1, param2, getLimitBytes(param3), param3); + } + + @Override + public void glBufferSubData(int param1, long param2, ShortBuffer param3) { + checkLimit(param3); + GLContext.getCurrentGL().glBufferSubData(param1, param2, getLimitBytes(param3), param3); + } + + @Override + public void glBufferSubData(int param1, long param2, ByteBuffer param3) { + checkLimit(param3); + GLContext.getCurrentGL().glBufferSubData(param1, param2, getLimitBytes(param3), param3); + } + + @Override + public void glClear(int param1) { + GLContext.getCurrentGL().glClear(param1); + } + + @Override + public void glClearColor(float param1, float param2, float param3, float param4) { + GLContext.getCurrentGL().glClearColor(param1, param2, param3, param4); + } + + @Override + public void glColorMask(boolean param1, boolean param2, boolean param3, boolean param4) { + GLContext.getCurrentGL().glColorMask(param1, param2, param3, param4); + } + + @Override + public void glCompileShader(int param1) { + GLContext.getCurrentGL().getGL2ES2().glCompileShader(param1); + } + + @Override + public void glCompressedTexImage2D(int param1, int param2, int param3, int param4, int param5, int param6, ByteBuffer param7) { + checkLimit(param7); + GLContext.getCurrentGL().glCompressedTexImage2D(param1, param2, param3, param4, param5, param6, getLimitBytes(param7), param7); + } + + @Override + public void glCompressedTexImage3D(int param1, int param2, int param3, int param4, int param5, int param6, int param7, ByteBuffer param8) { + checkLimit(param8); + GLContext.getCurrentGL().getGL2ES2().glCompressedTexImage3D(param1, param2, param3, param4, param5, param6, param7, getLimitBytes(param8), param8); + } + + @Override + public void glCompressedTexSubImage2D(int param1, int param2, int param3, int param4, int param5, int param6, int param7, ByteBuffer param8) { + checkLimit(param8); + GLContext.getCurrentGL().glCompressedTexSubImage2D(param1, param2, param3, param4, param5, param6, param7, getLimitBytes(param8), param8); + } + + @Override + public void glCompressedTexSubImage3D(int param1, int param2, int param3, int param4, int param5, int param6, int param7, int param8, int param9, ByteBuffer param10) { + checkLimit(param10); + GLContext.getCurrentGL().getGL2ES2().glCompressedTexSubImage3D(param1, param2, param3, param4, param5, param6, param7, param8, param9, getLimitBytes(param10), param10); + } + + @Override + public int glCreateProgram() { + return GLContext.getCurrentGL().getGL2ES2().glCreateProgram(); + } + + @Override + public int glCreateShader(int param1) { + return GLContext.getCurrentGL().getGL2ES2().glCreateShader(param1); + } + + @Override + public void glCullFace(int param1) { + GLContext.getCurrentGL().glCullFace(param1); + } + + @Override + public void glDeleteBuffers(IntBuffer param1) { + checkLimit(param1); + GLContext.getCurrentGL().glDeleteBuffers(param1.limit(), param1); + } + + @Override + public void glDeleteProgram(int param1) { + GLContext.getCurrentGL().getGL2ES2().glDeleteProgram(param1); + } + + @Override + public void glDeleteShader(int param1) { + GLContext.getCurrentGL().getGL2ES2().glDeleteShader(param1); + } + + @Override + public void glDeleteTextures(IntBuffer param1) { + checkLimit(param1); + GLContext.getCurrentGL().glDeleteTextures(param1.limit() ,param1); + } + + @Override + public void glDepthFunc(int param1) { + GLContext.getCurrentGL().glDepthFunc(param1); + } + + @Override + public void glDepthMask(boolean param1) { + GLContext.getCurrentGL().glDepthMask(param1); + } + + @Override + public void glDepthRange(double param1, double param2) { + GLContext.getCurrentGL().glDepthRange(param1, param2); + } + + @Override + public void glDetachShader(int param1, int param2) { + GLContext.getCurrentGL().getGL2ES2().glDetachShader(param1, param2); + } + + @Override + public void glDisable(int param1) { + GLContext.getCurrentGL().glDisable(param1); + } + + @Override + public void glDisableVertexAttribArray(int param1) { + GLContext.getCurrentGL().getGL2ES2().glDisableVertexAttribArray(param1); + } + + @Override + public void glDrawArrays(int param1, int param2, int param3) { + GLContext.getCurrentGL().glDrawArrays(param1, param2, param3); + } + + @Override + public void glDrawBuffer(int param1) { + GLContext.getCurrentGL().getGL2GL3().glDrawBuffer(param1); + } + + @Override + public void glDrawRangeElements(int param1, int param2, int param3, int param4, int param5, long param6) { + GLContext.getCurrentGL().getGL2ES3().glDrawRangeElements(param1, param2, param3, param4, param5, param6); + } + + @Override + public void glEnable(int param1) { + GLContext.getCurrentGL().glEnable(param1); + } + + @Override + public void glEnableVertexAttribArray(int param1) { + GLContext.getCurrentGL().getGL2ES2().glEnableVertexAttribArray(param1); + } + + @Override + public void glGenBuffers(IntBuffer param1) { + checkLimit(param1); + GLContext.getCurrentGL().glGenBuffers(param1.limit(), param1); + } + + @Override + public void glGenTextures(IntBuffer param1) { + checkLimit(param1); + GLContext.getCurrentGL().glGenTextures(param1.limit(), param1); + } + + @Override + public void glGetBoolean(int param1, ByteBuffer param2) { + checkLimit(param2); + GLContext.getCurrentGL().glGetBooleanv(param1, param2); + } + + @Override + public void glGetBufferSubData(int target, long offset, ByteBuffer data) { + checkLimit(data); + GLContext.getCurrentGL().getGL2GL3().glGetBufferSubData(target, offset, getLimitBytes(data), data); + } + + @Override + public int glGetError() { + return GLContext.getCurrentGL().glGetError(); + } + + @Override + public void glGetInteger(int param1, IntBuffer param2) { + checkLimit(param2); + GLContext.getCurrentGL().glGetIntegerv(param1, param2); + } + + @Override + public void glGetProgram(int param1, int param2, IntBuffer param3) { + checkLimit(param3); + GLContext.getCurrentGL().getGL2ES2().glGetProgramiv(param1, param2, param3); + } + + @Override + public void glGetShader(int param1, int param2, IntBuffer param3) { + checkLimit(param3); + GLContext.getCurrentGL().getGL2ES2().glGetShaderiv(param1, param2, param3); + } + + @Override + public String glGetString(int param1) { + return GLContext.getCurrentGL().glGetString(param1); + } + + @Override + public String glGetString(int param1, int param2) { + return GLContext.getCurrentGL().getGL2ES3().glGetStringi(param1, param2); + } + + @Override + public boolean glIsEnabled(int param1) { + return GLContext.getCurrentGL().glIsEnabled(param1); + } + + @Override + public void glLineWidth(float param1) { + GLContext.getCurrentGL().glLineWidth(param1); + } + + @Override + public void glLinkProgram(int param1) { + GLContext.getCurrentGL().getGL2ES2().glLinkProgram(param1); + } + + @Override + public void glPixelStorei(int param1, int param2) { + GLContext.getCurrentGL().glPixelStorei(param1, param2); + } + + @Override + public void glPointSize(float param1) { + GLContext.getCurrentGL().getGL2ES1().glPointSize(param1); + } + + @Override + public void glPolygonMode(int param1, int param2) { + GLContext.getCurrentGL().getGL2().glPolygonMode(param1, param2); + } + + @Override + public void glPolygonOffset(float param1, float param2) { + GLContext.getCurrentGL().glPolygonOffset(param1, param2); + } + + @Override + public void glReadBuffer(int param1) { + GLContext.getCurrentGL().getGL2ES3().glReadBuffer(param1); + } + + @Override + public void glReadPixels(int param1, int param2, int param3, int param4, int param5, int param6, ByteBuffer param7) { + checkLimit(param7); + GLContext.getCurrentGL().glReadPixels(param1, param2, param3, param4, param5, param6, param7); + } + + @Override + public void glReadPixels(int param1, int param2, int param3, int param4, int param5, int param6, long param7) { + GLContext.getCurrentGL().glReadPixels(param1, param2, param3, param4, param5, param6, param7); + } + + @Override + public void glScissor(int param1, int param2, int param3, int param4) { + GLContext.getCurrentGL().glScissor(param1, param2, param3, param4); + } + + @Override + public void glStencilFuncSeparate(int param1, int param2, int param3, int param4) { + GLContext.getCurrentGL().getGL2ES2().glStencilFuncSeparate(param1, param2, param3, param4); + } + + @Override + public void glStencilOpSeparate(int param1, int param2, int param3, int param4) { + GLContext.getCurrentGL().getGL2ES2().glStencilOpSeparate(param1, param2, param3, param4); + } + + @Override + public void glTexImage2D(int param1, int param2, int param3, int param4, int param5, int param6, int param7, int param8, ByteBuffer param9) { + checkLimit(param9); + GLContext.getCurrentGL().glTexImage2D(param1, param2, param3, param4, param5, param6, param7, param8, param9); + } + + @Override + public void glTexImage3D(int param1, int param2, int param3, int param4, int param5, int param6, int param7, int param8, int param9, ByteBuffer param10) { + checkLimit(param10); + GLContext.getCurrentGL().getGL2ES2().glTexImage3D(param1, param2, param3, param4, param5, param6, param7, param8, param9, param10); + } + + @Override + public void glTexParameterf(int param1, int param2, float param3) { + GLContext.getCurrentGL().glTexParameterf(param1, param2, param3); + } + + @Override + public void glTexParameteri(int param1, int param2, int param3) { + GLContext.getCurrentGL().glTexParameteri(param1, param2, param3); + } + + @Override + public void glTexSubImage2D(int param1, int param2, int param3, int param4, int param5, int param6, int param7, int param8, ByteBuffer param9) { + checkLimit(param9); + GLContext.getCurrentGL().glTexSubImage2D(param1, param2, param3, param4, param5, param6, param7, param8, param9); + } + + @Override + public void glTexSubImage3D(int param1, int param2, int param3, int param4, int param5, int param6, int param7, int param8, int param9, int param10, ByteBuffer param11) { + checkLimit(param11); + GLContext.getCurrentGL().getGL2ES2().glTexSubImage3D(param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11); + } + + @Override + public void glUniform1(int param1, FloatBuffer param2) { + checkLimit(param2); + GLContext.getCurrentGL().getGL2ES2().glUniform1fv(param1, getLimitCount(param2, 1), param2); + } + + @Override + public void glUniform1(int param1, IntBuffer param2) { + checkLimit(param2); + GLContext.getCurrentGL().getGL2ES2().glUniform1iv(param1, getLimitCount(param2, 1), param2); + } + + @Override + public void glUniform1f(int param1, float param2) { + GLContext.getCurrentGL().getGL2ES2().glUniform1f(param1, param2); + } + + @Override + public void glUniform1i(int param1, int param2) { + GLContext.getCurrentGL().getGL2ES2().glUniform1i(param1, param2); + } + + @Override + public void glUniform2(int param1, IntBuffer param2) { + checkLimit(param2); + GLContext.getCurrentGL().getGL2ES2().glUniform2iv(param1, getLimitCount(param2, 2), param2); + } + + @Override + public void glUniform2(int param1, FloatBuffer param2) { + checkLimit(param2); + GLContext.getCurrentGL().getGL2ES2().glUniform2fv(param1, getLimitCount(param2, 2), param2); + } + + @Override + public void glUniform2f(int param1, float param2, float param3) { + GLContext.getCurrentGL().getGL2ES2().glUniform2f(param1, param2, param3); + } + + @Override + public void glUniform3(int param1, IntBuffer param2) { + checkLimit(param2); + GLContext.getCurrentGL().getGL2ES2().glUniform3iv(param1, getLimitCount(param2, 3), param2); + } + + @Override + public void glUniform3(int param1, FloatBuffer param2) { + checkLimit(param2); + GLContext.getCurrentGL().getGL2ES2().glUniform3fv(param1, getLimitCount(param2, 3), param2); + } + + @Override + public void glUniform3f(int param1, float param2, float param3, float param4) { + GLContext.getCurrentGL().getGL2ES2().glUniform3f(param1, param2, param3, param4); + } + + @Override + public void glUniform4(int param1, FloatBuffer param2) { + checkLimit(param2); + GLContext.getCurrentGL().getGL2ES2().glUniform4fv(param1, getLimitCount(param2, 4), param2); + } + + @Override + public void glUniform4(int param1, IntBuffer param2) { + checkLimit(param2); + GLContext.getCurrentGL().getGL2ES2().glUniform4iv(param1, getLimitCount(param2, 4), param2); + } + + @Override + public void glUniform4f(int param1, float param2, float param3, float param4, float param5) { + GLContext.getCurrentGL().getGL2ES2().glUniform4f(param1, param2, param3, param4, param5); + } + + @Override + public void glUniformMatrix3(int param1, boolean param2, FloatBuffer param3) { + checkLimit(param3); + GLContext.getCurrentGL().getGL2ES2().glUniformMatrix3fv(param1, getLimitCount(param3, 3 * 3), param2, param3); + } + + @Override + public void glUniformMatrix4(int param1, boolean param2, FloatBuffer param3) { + checkLimit(param3); + GLContext.getCurrentGL().getGL2ES2().glUniformMatrix4fv(param1, getLimitCount(param3, 4 * 4), param2, param3); + } + + @Override + public void glUseProgram(int param1) { + GLContext.getCurrentGL().getGL2ES2().glUseProgram(param1); + } + + @Override + public void glVertexAttribPointer(int param1, int param2, int param3, boolean param4, int param5, long param6) { + GLContext.getCurrentGL().getGL2ES2().glVertexAttribPointer(param1, param2, param3, param4, param5, param6); + } + + @Override + public void glViewport(int param1, int param2, int param3, int param4) { + GLContext.getCurrentGL().glViewport(param1, param2, param3, param4); + } + + @Override + public int glGetAttribLocation(int param1, String param2) { + // JOGL 2.0 doesn't need a null-terminated string + return GLContext.getCurrentGL().getGL2ES2().glGetAttribLocation(param1, param2); + } + + @Override + public int glGetUniformLocation(int param1, String param2) { + // JOGL 2.0 doesn't need a null-terminated string + return GLContext.getCurrentGL().getGL2ES2().glGetUniformLocation(param1, param2); + } + + @Override + public void glShaderSource(int param1, String[] param2, IntBuffer param3) { + checkLimit(param3); + + int param3pos = param3.position(); + try { + for (final String param2string : param2) { + param3.put(Math.max(param2string.length(), param2string.getBytes().length)); + } + } finally { + param3.position(param3pos); + } + GLContext.getCurrentGL().getGL2ES2().glShaderSource(param1, param2.length, param2, param3); + } + + @Override + public String glGetProgramInfoLog(int program, int maxSize) { + ByteBuffer buffer = ByteBuffer.allocateDirect(maxSize); + buffer.order(ByteOrder.nativeOrder()); + ByteBuffer tmp = ByteBuffer.allocateDirect(4); + tmp.order(ByteOrder.nativeOrder()); + IntBuffer intBuffer = tmp.asIntBuffer(); + + GLContext.getCurrentGL().getGL2ES2().glGetProgramInfoLog(program, maxSize, intBuffer, buffer); + int numBytes = intBuffer.get(0); + byte[] bytes = new byte[numBytes]; + buffer.get(bytes); + return new String(bytes); + } + + @Override + public String glGetShaderInfoLog(int shader, int maxSize) { + ByteBuffer buffer = ByteBuffer.allocateDirect(maxSize); + buffer.order(ByteOrder.nativeOrder()); + ByteBuffer tmp = ByteBuffer.allocateDirect(4); + tmp.order(ByteOrder.nativeOrder()); + IntBuffer intBuffer = tmp.asIntBuffer(); + + GLContext.getCurrentGL().getGL2ES2().glGetShaderInfoLog(shader, maxSize, intBuffer, buffer); + int numBytes = intBuffer.get(0); + byte[] bytes = new byte[numBytes]; + buffer.get(bytes); + return new String(bytes); + } + + @Override + public void glBindFragDataLocation(int param1, int param2, String param3) { + GLContext.getCurrentGL().getGL2GL3().glBindFragDataLocation(param1, param2, param3); + } + + @Override + public void glBindVertexArray(int param1) { + GLContext.getCurrentGL().getGL2ES3().glBindVertexArray(param1); + } + + @Override + public void glGenVertexArrays(IntBuffer param1) { + checkLimit(param1); + GLContext.getCurrentGL().getGL2ES3().glGenVertexArrays(param1.limit(), param1); + } + + @Override + public void glPatchParameter(int count) { + GLContext.getCurrentGL().getGL3().glPatchParameteri(com.jogamp.opengl.GL3.GL_PATCH_VERTICES, count); + } + + @Override + public void glDeleteVertexArrays(IntBuffer arrays) { + checkLimit(arrays); + GLContext.getCurrentGL().getGL2ES3().glDeleteVertexArrays(arrays.limit(), arrays); + } +} diff --git a/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglGLExt.java b/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglGLExt.java new file mode 100644 index 000000000..a87e8a035 --- /dev/null +++ b/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglGLExt.java @@ -0,0 +1,88 @@ +package com.jme3.renderer.jogl; + +import com.jme3.renderer.RendererException; +import com.jme3.renderer.opengl.GLExt; +import com.jogamp.opengl.GLContext; + +import java.nio.Buffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; + +public class JoglGLExt implements GLExt { + + private static int getLimitBytes(IntBuffer buffer) { + checkLimit(buffer); + return buffer.limit() * 4; + } + + private static void checkLimit(Buffer buffer) { + if (buffer == null) { + return; + } + if (buffer.limit() == 0) { + throw new RendererException("Attempting to upload empty buffer (limit = 0), that's an error"); + } + if (buffer.remaining() == 0) { + throw new RendererException("Attempting to upload empty buffer (remaining = 0), that's an error"); + } + } + + @Override + public void glBufferData(int target, IntBuffer data, int usage) { + checkLimit(data); + GLContext.getCurrentGL().glBufferData(target, getLimitBytes(data), data, usage); + } + + @Override + public void glBufferSubData(int target, long offset, IntBuffer data) { + checkLimit(data); + GLContext.getCurrentGL().glBufferSubData(target, getLimitBytes(data), offset, data); + } + + @Override + public void glDrawArraysInstancedARB(int mode, int first, int count, int primcount) { + GLContext.getCurrentGL().getGL2ES3().glDrawArraysInstanced(mode, first, count, primcount); + } + + @Override + public void glDrawBuffers(IntBuffer bufs) { + checkLimit(bufs); + GLContext.getCurrentGL().getGL2ES2().glDrawBuffers(bufs.limit(), bufs); + } + + @Override + public void glDrawElementsInstancedARB(int mode, int indices_count, int type, long indices_buffer_offset, int primcount) { + GLContext.getCurrentGL().getGL2ES3().glDrawElementsInstanced(mode, indices_count, type, indices_buffer_offset, primcount); + } + + @Override + public void glGetMultisample(int pname, int index, FloatBuffer val) { + checkLimit(val); + GLContext.getCurrentGL().getGL2ES2().glGetMultisamplefv(pname, index, val); + } + + @Override + public void glTexImage2DMultisample(int target, int samples, int internalformat, int width, int height, boolean fixedsamplelocations) { + GLContext.getCurrentGL().getGL2ES2().glTexImage2DMultisample(target, samples, internalformat, width, height, fixedsamplelocations); + } + + @Override + public void glVertexAttribDivisorARB(int index, int divisor) { + GLContext.getCurrentGL().getGL2ES3().glVertexAttribDivisor(index, divisor); + } + + @Override + public Object glFenceSync(int condition, int flags) { + return GLContext.getCurrentGL().getGL3ES3().glFenceSync(condition, flags); + } + + @Override + public int glClientWaitSync(Object sync, int flags, long timeout) { + return GLContext.getCurrentGL().getGL3ES3().glClientWaitSync(((Long) sync).longValue(), flags, timeout); + } + + @Override + public void glDeleteSync(Object sync) { + GLContext.getCurrentGL().getGL3ES3().glDeleteSync(((Long) sync).longValue()); + } +} diff --git a/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglGLFbo.java b/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglGLFbo.java new file mode 100644 index 000000000..2691d2e24 --- /dev/null +++ b/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglGLFbo.java @@ -0,0 +1,97 @@ +package com.jme3.renderer.jogl; + +import com.jme3.renderer.RendererException; +import com.jme3.renderer.opengl.GLFbo; +import com.jogamp.opengl.GLContext; + +import java.nio.Buffer; +import java.nio.IntBuffer; + +/** + * Implements GLFbo + * + * @author Kirill Vainer + */ +public class JoglGLFbo implements GLFbo { + + private static void checkLimit(Buffer buffer) { + if (buffer == null) { + return; + } + if (buffer.limit() == 0) { + throw new RendererException("Attempting to upload empty buffer (limit = 0), that's an error"); + } + if (buffer.remaining() == 0) { + throw new RendererException("Attempting to upload empty buffer (remaining = 0), that's an error"); + } + } + + @Override + public void glBlitFramebufferEXT(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) { + GLContext.getCurrentGL().getGL2ES3().glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); + } + + @Override + public void glRenderbufferStorageMultisampleEXT(int target, int samples, int internalformat, int width, int height) { + GLContext.getCurrentGL().glRenderbufferStorageMultisample(target, samples, internalformat, width, height); + } + + @Override + public void glBindFramebufferEXT(int param1, int param2) { + GLContext.getCurrentGL().glBindFramebuffer(param1, param2); + } + + @Override + public void glBindRenderbufferEXT(int param1, int param2) { + GLContext.getCurrentGL().glBindRenderbuffer(param1, param2); + } + + @Override + public int glCheckFramebufferStatusEXT(int param1) { + return GLContext.getCurrentGL().glCheckFramebufferStatus(param1); + } + + @Override + public void glDeleteFramebuffersEXT(IntBuffer param1) { + checkLimit(param1); + GLContext.getCurrentGL().glDeleteFramebuffers(param1.limit(), param1); + } + + @Override + public void glDeleteRenderbuffersEXT(IntBuffer param1) { + checkLimit(param1); + GLContext.getCurrentGL().glDeleteRenderbuffers(param1.limit(), param1); + } + + @Override + public void glFramebufferRenderbufferEXT(int param1, int param2, int param3, int param4) { + GLContext.getCurrentGL().glFramebufferRenderbuffer(param1, param2, param3, param4); + } + + @Override + public void glFramebufferTexture2DEXT(int param1, int param2, int param3, int param4, int param5) { + GLContext.getCurrentGL().glFramebufferTexture2D(param1, param2, param3, param4, param5); + } + + @Override + public void glGenFramebuffersEXT(IntBuffer param1) { + checkLimit(param1); + GLContext.getCurrentGL().glGenFramebuffers(param1.limit(), param1); + } + + @Override + public void glGenRenderbuffersEXT(IntBuffer param1) { + checkLimit(param1); + GLContext.getCurrentGL().glGenRenderbuffers(param1.limit(), param1); + } + + @Override + public void glGenerateMipmapEXT(int param1) { + GLContext.getCurrentGL().glGenerateMipmap(param1); + } + + @Override + public void glRenderbufferStorageEXT(int param1, int param2, int param3, int param4) { + GLContext.getCurrentGL().glRenderbufferStorage(param1, param2, param3, param4); + } +} diff --git a/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglRenderer.java b/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglRenderer.java deleted file mode 100644 index 111719265..000000000 --- a/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglRenderer.java +++ /dev/null @@ -1,2699 +0,0 @@ -/* - * Copyright (c) 2009-2012 jMonkeyEngine - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of 'jMonkeyEngine' nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.jme3.renderer.jogl; - -import com.jme3.light.LightList; -import com.jme3.material.RenderState; -import com.jme3.math.ColorRGBA; -import com.jme3.math.Matrix4f; -import com.jme3.math.Quaternion; -import com.jme3.math.Vector2f; -import com.jme3.math.Vector3f; -import com.jme3.math.Vector4f; -import com.jme3.renderer.Caps; -import com.jme3.renderer.IDList; -import com.jme3.renderer.RenderContext; -import com.jme3.renderer.Renderer; -import com.jme3.renderer.RendererException; -import com.jme3.renderer.Statistics; -import com.jme3.scene.Mesh; -import com.jme3.scene.Mesh.Mode; -import com.jme3.scene.VertexBuffer; -import com.jme3.scene.VertexBuffer.Type; -import com.jme3.scene.VertexBuffer.Usage; -import com.jme3.shader.Attribute; -import com.jme3.shader.Shader; -import com.jme3.shader.Shader.ShaderSource; -import com.jme3.shader.Uniform; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.FrameBuffer.RenderBuffer; -import com.jme3.texture.Image; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapAxis; -import com.jme3.util.BufferUtils; -import com.jme3.util.ListMap; -import com.jme3.util.NativeObjectManager; -import java.nio.Buffer; -import java.nio.ByteBuffer; -import java.nio.FloatBuffer; -import java.nio.IntBuffer; -import java.util.EnumSet; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; -import com.jogamp.nativewindow.NativeWindowFactory; -import com.jogamp.opengl.GL; -import com.jogamp.opengl.GL2; -import com.jogamp.opengl.GL2ES1; -import com.jogamp.opengl.GL2ES2; -import com.jogamp.opengl.GL2ES3; -import com.jogamp.opengl.GL2GL3; -import com.jogamp.opengl.GL3; -import com.jogamp.opengl.GLContext; -import jme3tools.converters.MipMapGenerator; -import jme3tools.shader.ShaderDebug; - -public class JoglRenderer implements Renderer { - - private static final Logger logger = Logger.getLogger(JoglRenderer.class.getName()); - private static final boolean VALIDATE_SHADER = false; - private final ByteBuffer nameBuf = BufferUtils.createByteBuffer(250); - private final StringBuilder stringBuf = new StringBuilder(250); - private final IntBuffer intBuf1 = BufferUtils.createIntBuffer(1); - private final IntBuffer intBuf16 = BufferUtils.createIntBuffer(16); - protected FloatBuffer fb16 = BufferUtils.createFloatBuffer(16); - private RenderContext context = new RenderContext(); - private NativeObjectManager objManager = new NativeObjectManager(); - private EnumSet caps = EnumSet.noneOf(Caps.class); - //current state - private Shader boundShader; - private int initialDrawBuf, initialReadBuf; - private int glslVer; - private int vertexTextureUnits; - private int fragTextureUnits; - private int vertexUniforms; - private int fragUniforms; - private int vertexAttribs; - private int maxFBOSamples; - private int maxFBOAttachs; - private int maxMRTFBOAttachs; - private int maxRBSize; - private int maxTexSize; - private int maxCubeTexSize; - private int maxVertCount; - private int maxTriCount; - private int maxColorTexSamples; - private int maxDepthTexSamples; - private FrameBuffer lastFb = null; - private FrameBuffer mainFbOverride = null; - private final Statistics statistics = new Statistics(); - private int vpX, vpY, vpW, vpH; - private int clipX, clipY, clipW, clipH; - private boolean linearizeSrgbImages; - - public JoglRenderer() { - } - - protected void updateNameBuffer() { - int len = stringBuf.length(); - - nameBuf.position(0); - nameBuf.limit(len); - for (int i = 0; i < len; i++) { - nameBuf.put((byte) stringBuf.charAt(i)); - } - - nameBuf.rewind(); - } - - @Override - public Statistics getStatistics() { - return statistics; - } - - @Override - public EnumSet getCaps() { - return caps; - } - - public void initialize() { - GL gl = GLContext.getCurrentGL(); - //logger.log(Level.FINE, "Vendor: {0}", gl.glGetString(GL.GL_VENDOR)); - //logger.log(Level.FINE, "Renderer: {0}", gl.glGetString(GL.GL_RENDERER)); - //logger.log(Level.FINE, "Version: {0}", gl.glGetString(GL.GL_VERSION)); - if (gl.isExtensionAvailable("GL_VERSION_2_0")) { - caps.add(Caps.OpenGL20); - if (gl.isExtensionAvailable("GL_VERSION_2_1")) { - caps.add(Caps.OpenGL21); - if (gl.isExtensionAvailable("GL_VERSION_3_0")) { - caps.add(Caps.OpenGL30); - if (gl.isExtensionAvailable("GL_VERSION_3_1")) { - caps.add(Caps.OpenGL31); - if (gl.isExtensionAvailable("GL_VERSION_3_2")) { - caps.add(Caps.OpenGL32); - } - } - } - } - } - - //workaround, always assume we support GLSL100 - //some cards just don't report this correctly - caps.add(Caps.GLSL100); - - String versionStr = null; - if (caps.contains(Caps.OpenGL20) || gl.isGL2ES2()) { - versionStr = gl.glGetString(GL2ES2.GL_SHADING_LANGUAGE_VERSION); - } - if (versionStr == null || versionStr.equals("")) { - glslVer = -1; - throw new UnsupportedOperationException("GLSL and OpenGL2 is " + - "required for the JOGL " + - "renderer!"); - } - - // Fix issue in TestRenderToMemory when GL_FRONT is the main - // buffer being used. - gl.glGetIntegerv(GL2GL3.GL_DRAW_BUFFER, intBuf1); - initialDrawBuf = intBuf1.get(0); - gl.glGetIntegerv(GL2GL3.GL_READ_BUFFER, intBuf1); - initialReadBuf = intBuf1.get(0); - - // XXX: This has to be GL_BACK for canvas on Mac - // Since initialDrawBuf is GL_FRONT for pbuffer, gotta - // change this value later on ... -// initialDrawBuf = GL_BACK; -// initialReadBuf = GL_BACK; - - int spaceIdx = versionStr.indexOf(" "); - if (spaceIdx >= 1) { - versionStr = versionStr.substring(0, spaceIdx); - } - - try { - float version = Float.parseFloat(versionStr); - glslVer = (int) (version * 100); - } catch (NumberFormatException e) { - // the parsing fails on Raspberry Pi - if (NativeWindowFactory.getNativeWindowType(false).equals(NativeWindowFactory.TYPE_BCM_VC_IV)) { - logger.warning("Failed parsing GLSL version assuming it's v1.00"); - glslVer = 100; - } - } - - switch (glslVer) { - default: - if (glslVer < 400) { - break; - } - - // so that future OpenGL revisions wont break jme3 - - // fall through intentional - case 400: - case 330: - case 150: - caps.add(Caps.GLSL150); - case 140: - caps.add(Caps.GLSL140); - case 130: - caps.add(Caps.GLSL130); - case 120: - caps.add(Caps.GLSL120); - case 110: - caps.add(Caps.GLSL110); - case 100: - caps.add(Caps.GLSL100); - break; - } - - if (!caps.contains(Caps.GLSL100)) { - logger.log(Level.WARNING, "Force-adding GLSL100 support, since OpenGL2 is supported."); - caps.add(Caps.GLSL100); - } - - gl.glGetIntegerv(GL2ES2.GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, intBuf16); - vertexTextureUnits = intBuf16.get(0); - logger.log(Level.FINER, "VTF Units: {0}", vertexTextureUnits); - if (vertexTextureUnits > 0) { - caps.add(Caps.VertexTextureFetch); - } - - gl.glGetIntegerv(GL2ES2.GL_MAX_TEXTURE_IMAGE_UNITS, intBuf16); - fragTextureUnits = intBuf16.get(0); - logger.log(Level.FINER, "Texture Units: {0}", fragTextureUnits); - - gl.glGetIntegerv(GL2GL3.GL_MAX_VERTEX_UNIFORM_COMPONENTS, intBuf16); - vertexUniforms = intBuf16.get(0); - logger.log(Level.FINER, "Vertex Uniforms: {0}", vertexUniforms); - - gl.glGetIntegerv(GL2GL3.GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, intBuf16); - fragUniforms = intBuf16.get(0); - logger.log(Level.FINER, "Fragment Uniforms: {0}", fragUniforms); - - gl.glGetIntegerv(GL2ES2.GL_MAX_VERTEX_ATTRIBS, intBuf16); - vertexAttribs = intBuf16.get(0); - logger.log(Level.FINER, "Vertex Attributes: {0}", vertexAttribs); - - gl.glGetIntegerv(GL2GL3.GL_MAX_VARYING_FLOATS, intBuf16); - int varyingFloats = intBuf16.get(0); - logger.log(Level.FINER, "Varying Floats: {0}", varyingFloats); - - gl.glGetIntegerv(GL.GL_SUBPIXEL_BITS, intBuf16); - int subpixelBits = intBuf16.get(0); - logger.log(Level.FINER, "Subpixel Bits: {0}", subpixelBits); - - gl.glGetIntegerv(GL2GL3.GL_MAX_ELEMENTS_VERTICES, intBuf16); - maxVertCount = intBuf16.get(0); - logger.log(Level.FINER, "Preferred Batch Vertex Count: {0}", maxVertCount); - - gl.glGetIntegerv(GL2GL3.GL_MAX_ELEMENTS_INDICES, intBuf16); - maxTriCount = intBuf16.get(0); - logger.log(Level.FINER, "Preferred Batch Index Count: {0}", maxTriCount); - - gl.glGetIntegerv(GL.GL_MAX_TEXTURE_SIZE, intBuf16); - maxTexSize = intBuf16.get(0); - logger.log(Level.FINER, "Maximum Texture Resolution: {0}", maxTexSize); - - gl.glGetIntegerv(GL.GL_MAX_CUBE_MAP_TEXTURE_SIZE, intBuf16); - maxCubeTexSize = intBuf16.get(0); - logger.log(Level.FINER, "Maximum CubeMap Resolution: {0}", maxCubeTexSize); - - if (gl.isExtensionAvailable("GL_ARB_color_buffer_float")) { - // XXX: Require both 16 and 32 bit float support for FloatColorBuffer. - if (gl.isExtensionAvailable("GL_ARB_half_float_pixel")) { - caps.add(Caps.FloatColorBuffer); - } - } - - if (gl.isExtensionAvailable("GL_ARB_depth_buffer_float")) { - caps.add(Caps.FloatDepthBuffer); - } - - if (caps.contains(Caps.OpenGL30)) { - caps.add(Caps.PackedDepthStencilBuffer); - } - - if (gl.isExtensionAvailable("GL_ARB_draw_instanced") || gl.isExtensionAvailable("GL_ARB_instanced_arrays")) { - caps.add(Caps.MeshInstancing); - } - - if (gl.isExtensionAvailable("GL_ARB_texture_buffer_object")) { - caps.add(Caps.TextureBuffer); - } - - if (gl.isExtensionAvailable("GL_ARB_texture_float")) { - if (gl.isExtensionAvailable("GL_ARB_half_float_pixel")) { - caps.add(Caps.FloatTexture); - } - } - - if (gl.isExtensionAvailable("GL_ARB_vertex_array_object")) { - caps.add(Caps.VertexBufferArray); - } - - if (gl.isExtensionAvailable("GL_ARB_texture_non_power_of_two")) { - caps.add(Caps.NonPowerOfTwoTextures); - } - else { - logger.log(Level.WARNING, "Your graphics card does not " - + "support non-power-of-2 textures. " - + "Some features might not work."); - } - - if (gl.isExtensionAvailable("GL_EXT_packed_float") || gl.isExtensionAvailable("GL_VERSION_3_0")) { - // This format is part of the OGL3 specification - caps.add(Caps.PackedFloatColorBuffer); - if (gl.isExtensionAvailable("GL_ARB_half_float_pixel")) { - // because textures are usually uploaded as RGB16F - // need half-float pixel - caps.add(Caps.PackedFloatTexture); - } - } - - if (gl.isExtensionAvailable("GL_EXT_texture_array") || gl.isExtensionAvailable("GL_VERSION_3_0")) { - caps.add(Caps.TextureArray); - } - - if (gl.isExtensionAvailable("GL_EXT_texture_shared_exponent") || gl.isExtensionAvailable("GL_VERSION_3_0")) { - caps.add(Caps.SharedExponentTexture); - } - - if (gl.isExtensionAvailable("GL_EXT_framebuffer_object")) { - caps.add(Caps.FrameBuffer); - - gl.glGetIntegerv(GL.GL_MAX_RENDERBUFFER_SIZE, intBuf16); - maxRBSize = intBuf16.get(0); - logger.log(Level.FINER, "FBO RB Max Size: {0}", maxRBSize); - - gl.glGetIntegerv(GL2GL3.GL_MAX_COLOR_ATTACHMENTS, intBuf16); - maxFBOAttachs = intBuf16.get(0); - logger.log(Level.FINER, "FBO Max renderbuffers: {0}", maxFBOAttachs); - - if (gl.isExtensionAvailable("GL_EXT_framebuffer_multisample")) { - caps.add(Caps.FrameBufferMultisample); - - gl.glGetIntegerv(GL2GL3.GL_MAX_SAMPLES, intBuf16); - maxFBOSamples = intBuf16.get(0); - logger.log(Level.FINER, "FBO Max Samples: {0}", maxFBOSamples); - } - - if (gl.isExtensionAvailable("GL_ARB_texture_multisample")) { - caps.add(Caps.TextureMultisample); - - gl.glGetIntegerv(GL3.GL_MAX_COLOR_TEXTURE_SAMPLES, intBuf16); - maxColorTexSamples = intBuf16.get(0); - logger.log(Level.FINER, "Texture Multisample Color Samples: {0}", maxColorTexSamples); - - gl.glGetIntegerv(GL3.GL_MAX_DEPTH_TEXTURE_SAMPLES, intBuf16); - maxDepthTexSamples = intBuf16.get(0); - logger.log(Level.FINER, "Texture Multisample Depth Samples: {0}", maxDepthTexSamples); - } - - gl.glGetIntegerv(GL2ES2.GL_MAX_DRAW_BUFFERS, intBuf16); - maxMRTFBOAttachs = intBuf16.get(0); - if (maxMRTFBOAttachs > 1) { - caps.add(Caps.FrameBufferMRT); - logger.log(Level.FINER, "FBO Max MRT renderbuffers: {0}", maxMRTFBOAttachs); - } - - //if (gl.isExtensionAvailable("GL_ARB_draw_buffers")) { - // caps.add(Caps.FrameBufferMRT); - // gl.glGetIntegerv(GL2GL3.GL_MAX_DRAW_BUFFERS, intBuf16); - // maxMRTFBOAttachs = intBuf16.get(0); - // logger.log(Level.FINER, "FBO Max MRT renderbuffers: {0}", maxMRTFBOAttachs); - //} - } - - if (gl.isExtensionAvailable("GL_ARB_multisample")) { - gl.glGetIntegerv(GL.GL_SAMPLE_BUFFERS, intBuf16); - boolean available = intBuf16.get(0) != 0; - gl.glGetIntegerv(GL.GL_SAMPLES, intBuf16); - int samples = intBuf16.get(0); - logger.log(Level.FINER, "Samples: {0}", samples); - boolean enabled = gl.glIsEnabled(GL.GL_MULTISAMPLE); - if (samples > 0 && available && !enabled) { - gl.glEnable(GL.GL_MULTISAMPLE); - } - caps.add(Caps.Multisample); - } - - //supports sRGB pipeline - if ((gl.isExtensionAvailable("GL_ARB_framebuffer_sRGB") && gl.isExtensionAvailable("GL_EXT_texture_sRGB")) || gl.isExtensionAvailable("GL_VERSION_3_0")){ - caps.add(Caps.Srgb); - } - - logger.log(Level.FINE, "Caps: {0}", caps); - } - - @Override - public void invalidateState() { - context.reset(); - boundShader = null; - lastFb = null; - - GL gl = GLContext.getCurrentGL(); - gl.glGetIntegerv(GL2GL3.GL_DRAW_BUFFER, intBuf1); - initialDrawBuf = intBuf1.get(0); - gl.glGetIntegerv(GL2GL3.GL_READ_BUFFER, intBuf1); - initialReadBuf = intBuf1.get(0); - } - - @Override - public void resetGLObjects() { - logger.log(Level.FINE, "Reseting objects and invalidating state"); - objManager.resetObjects(); - statistics.clearMemory(); - invalidateState(); - } - - @Override - public void cleanup() { - logger.log(Level.FINE, "Deleting objects and invalidating state"); - objManager.deleteAllObjects(this); - statistics.clearMemory(); - invalidateState(); - } - -// private void checkCap(Caps cap) { -// if (!caps.contains(cap)) { -// throw new UnsupportedOperationException("Required capability missing: " + cap.name()); -// } -// } - - /*********************************************************************\ - |* Render State *| - \*********************************************************************/ - @Override - public void setDepthRange(float start, float end) { - GL gl = GLContext.getCurrentGL(); - gl.glDepthRange(start, end); - } - - @Override - public void clearBuffers(boolean color, boolean depth, boolean stencil) { - GL gl = GLContext.getCurrentGL(); - int bits = 0; - if (color) { - //See explanations of the depth below, we must enable color write to be able to clear the color buffer - if (context.colorWriteEnabled == false) { - gl.glColorMask(true, true, true, true); - context.colorWriteEnabled = true; - } - bits = GL.GL_COLOR_BUFFER_BIT; - } - if (depth) { - - //glClear(GL_DEPTH_BUFFER_BIT) seems to not work when glDepthMask is false - //here s some link on openl board - //http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=257223 - //if depth clear is requested, we enable the depthMask - if (context.depthWriteEnabled == false) { - gl.glDepthMask(true); - context.depthWriteEnabled = true; - } - bits |= GL.GL_DEPTH_BUFFER_BIT; - } - if (stencil) { - bits |= GL.GL_STENCIL_BUFFER_BIT; - } - if (bits != 0) { - gl.glClear(bits); - } - } - - @Override - public void setBackgroundColor(ColorRGBA color) { - GL gl = GLContext.getCurrentGL(); - gl.glClearColor(color.r, color.g, color.b, color.a); - } - - @Override - public void setAlphaToCoverage(boolean value) { - if (caps.contains(Caps.Multisample)) { - GL gl = GLContext.getCurrentGL(); - if (value) { - gl.glEnable(GL.GL_SAMPLE_ALPHA_TO_COVERAGE); - } else { - gl.glDisable(GL.GL_SAMPLE_ALPHA_TO_COVERAGE); - } - } - } - - @Override - public void applyRenderState(RenderState state) { - GL gl = GLContext.getCurrentGL(); - if (state.isWireframe() && !context.wireframe) { - if (gl.isGL2GL3()) { - gl.getGL2GL3().glPolygonMode(GL.GL_FRONT_AND_BACK, GL2GL3.GL_LINE); - } - context.wireframe = true; - } else if (!state.isWireframe() && context.wireframe) { - if (gl.isGL2GL3()) { - gl.getGL2GL3().glPolygonMode(GL.GL_FRONT_AND_BACK, GL2GL3.GL_FILL); - } - context.wireframe = false; - } - - 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) { - gl.glDepthFunc(convertTestFunction(state.getDepthFunc())); - context.depthFunc = state.getDepthFunc(); - } - if (gl.isGL2ES1()) { - if (state.isAlphaTest() && !context.alphaTestEnabled) { - gl.glEnable(GL2ES1.GL_ALPHA_TEST); - gl.getGL2ES1().glAlphaFunc(convertTestFunction(context.alphaFunc), context.alphaTestFallOff); - context.alphaTestEnabled = true; - } else if (!state.isAlphaTest() && context.alphaTestEnabled) { - gl.glDisable(GL2ES1.GL_ALPHA_TEST); - context.alphaTestEnabled = false; - } - if (state.getAlphaFallOff() != context.alphaTestFallOff) { - gl.getGL2ES1().glAlphaFunc(convertTestFunction(context.alphaFunc), context.alphaTestFallOff); - context.alphaTestFallOff = state.getAlphaFallOff(); - } - if (state.getAlphaFunc() != context.alphaFunc) { - gl.getGL2ES1().glAlphaFunc(convertTestFunction(state.getAlphaFunc()), context.alphaTestFallOff); - context.alphaFunc = state.getAlphaFunc(); - } - } - - if (state.isDepthWrite() && !context.depthWriteEnabled) { - gl.glDepthMask(true); - context.depthWriteEnabled = true; - } else if (!state.isDepthWrite() && context.depthWriteEnabled) { - gl.glDepthMask(false); - context.depthWriteEnabled = false; - } - - if (state.isColorWrite() && !context.colorWriteEnabled) { - gl.glColorMask(true, true, true, true); - context.colorWriteEnabled = true; - } else if (!state.isColorWrite() && context.colorWriteEnabled) { - gl.glColorMask(false, false, false, false); - context.colorWriteEnabled = false; - } - - if (state.isPointSprite() && !context.pointSprite) { - // Only enable/disable sprite - if (context.boundTextures[0] != null) { - if (context.boundTextureUnit != 0) { - gl.glActiveTexture(GL.GL_TEXTURE0); - context.boundTextureUnit = 0; - } - if (gl.isGL2ES1()) { - gl.glEnable(GL2ES1.GL_POINT_SPRITE); - } - if (gl.isGL2GL3()) { - gl.glEnable(GL2GL3.GL_VERTEX_PROGRAM_POINT_SIZE); - } - } - context.pointSprite = true; - } else if (!state.isPointSprite() && context.pointSprite) { - if (context.boundTextures[0] != null) { - if (context.boundTextureUnit != 0) { - gl.glActiveTexture(GL.GL_TEXTURE0); - context.boundTextureUnit = 0; - } - if (gl.isGL2ES1()) { - gl.glDisable(GL2ES1.GL_POINT_SPRITE); - } - if (gl.isGL2GL3()) { - gl.glDisable(GL2GL3.GL_VERTEX_PROGRAM_POINT_SIZE); - } - context.pointSprite = false; - } - } - - if (state.isPolyOffset()) { - if (!context.polyOffsetEnabled) { - gl.glEnable(GL.GL_POLYGON_OFFSET_FILL); - gl.glPolygonOffset(state.getPolyOffsetFactor(), - state.getPolyOffsetUnits()); - context.polyOffsetEnabled = true; - context.polyOffsetFactor = state.getPolyOffsetFactor(); - context.polyOffsetUnits = state.getPolyOffsetUnits(); - } else { - if (state.getPolyOffsetFactor() != context.polyOffsetFactor - || state.getPolyOffsetUnits() != context.polyOffsetUnits) { - gl.glPolygonOffset(state.getPolyOffsetFactor(), - state.getPolyOffsetUnits()); - context.polyOffsetFactor = state.getPolyOffsetFactor(); - context.polyOffsetUnits = state.getPolyOffsetUnits(); - } - } - } else { - if (context.polyOffsetEnabled) { - gl.glDisable(GL.GL_POLYGON_OFFSET_FILL); - context.polyOffsetEnabled = false; - context.polyOffsetFactor = 0; - context.polyOffsetUnits = 0; - } - } - - if (state.getFaceCullMode() != context.cullMode) { - if (state.getFaceCullMode() == RenderState.FaceCullMode.Off) { - gl.glDisable(GL.GL_CULL_FACE); - } else { - gl.glEnable(GL.GL_CULL_FACE); - } - - switch (state.getFaceCullMode()) { - case Off: - break; - case Back: - gl.glCullFace(GL.GL_BACK); - break; - case Front: - gl.glCullFace(GL.GL_FRONT); - break; - case FrontAndBack: - gl.glCullFace(GL.GL_FRONT_AND_BACK); - break; - default: - throw new UnsupportedOperationException("Unrecognized face cull mode: " - + state.getFaceCullMode()); - } - - context.cullMode = state.getFaceCullMode(); - } - - if (state.getBlendMode() != context.blendMode) { - if (state.getBlendMode() == RenderState.BlendMode.Off) { - gl.glDisable(GL.GL_BLEND); - } else { - gl.glEnable(GL.GL_BLEND); - switch (state.getBlendMode()) { - case Off: - break; - case Additive: - gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE); - break; - case AlphaAdditive: - gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE); - break; - case Color: - gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_COLOR); - break; - case Alpha: - gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA); - break; - case PremultAlpha: - gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_ALPHA); - break; - case Modulate: - gl.glBlendFunc(GL.GL_DST_COLOR, GL.GL_ZERO); - break; - case ModulateX2: - gl.glBlendFunc(GL.GL_DST_COLOR, GL.GL_SRC_COLOR); - break; - case Screen: - gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_COLOR); - break; - case Exclusion: - gl.glBlendFunc(GL.GL_ONE_MINUS_DST_COLOR, GL.GL_ONE_MINUS_SRC_COLOR); - break; - default: - throw new UnsupportedOperationException("Unrecognized blend mode: " - + state.getBlendMode()); - } - } - - context.blendMode = state.getBlendMode(); - } - - if (context.stencilTest != state.isStencilTest() - || context.frontStencilStencilFailOperation != state.getFrontStencilStencilFailOperation() - || context.frontStencilDepthFailOperation != state.getFrontStencilDepthFailOperation() - || context.frontStencilDepthPassOperation != state.getFrontStencilDepthPassOperation() - || context.backStencilStencilFailOperation != state.getBackStencilStencilFailOperation() - || context.backStencilDepthFailOperation != state.getBackStencilDepthFailOperation() - || context.backStencilDepthPassOperation != state.getBackStencilDepthPassOperation() - || context.frontStencilFunction != state.getFrontStencilFunction() - || context.backStencilFunction != state.getBackStencilFunction()) { - - context.frontStencilStencilFailOperation = state.getFrontStencilStencilFailOperation(); //terrible looking, I know - context.frontStencilDepthFailOperation = state.getFrontStencilDepthFailOperation(); - context.frontStencilDepthPassOperation = state.getFrontStencilDepthPassOperation(); - context.backStencilStencilFailOperation = state.getBackStencilStencilFailOperation(); - context.backStencilDepthFailOperation = state.getBackStencilDepthFailOperation(); - context.backStencilDepthPassOperation = state.getBackStencilDepthPassOperation(); - context.frontStencilFunction = state.getFrontStencilFunction(); - context.backStencilFunction = state.getBackStencilFunction(); - - if (state.isStencilTest()) { - gl.glEnable(GL.GL_STENCIL_TEST); - gl.getGL2ES2().glStencilOpSeparate(GL.GL_FRONT, - convertStencilOperation(state.getFrontStencilStencilFailOperation()), - convertStencilOperation(state.getFrontStencilDepthFailOperation()), - convertStencilOperation(state.getFrontStencilDepthPassOperation())); - gl.getGL2ES2().glStencilOpSeparate(GL.GL_BACK, - convertStencilOperation(state.getBackStencilStencilFailOperation()), - convertStencilOperation(state.getBackStencilDepthFailOperation()), - convertStencilOperation(state.getBackStencilDepthPassOperation())); - gl.getGL2ES2().glStencilFuncSeparate(GL.GL_FRONT, - convertTestFunction(state.getFrontStencilFunction()), - 0, Integer.MAX_VALUE); - gl.getGL2ES2().glStencilFuncSeparate(GL.GL_BACK, - convertTestFunction(state.getBackStencilFunction()), - 0, Integer.MAX_VALUE); - } else { - gl.glDisable(GL.GL_STENCIL_TEST); - } - } - } - - private int convertStencilOperation(RenderState.StencilOperation stencilOp) { - switch (stencilOp) { - case Keep: - return GL.GL_KEEP; - case Zero: - return GL.GL_ZERO; - case Replace: - return GL.GL_REPLACE; - case Increment: - return GL.GL_INCR; - case IncrementWrap: - return GL.GL_INCR_WRAP; - case Decrement: - return GL.GL_DECR; - case DecrementWrap: - return GL.GL_DECR_WRAP; - case Invert: - return GL.GL_INVERT; - default: - throw new UnsupportedOperationException("Unrecognized stencil operation: " + stencilOp); - } - } - - private int convertTestFunction(RenderState.TestFunction testFunc) { - switch (testFunc) { - case Never: - return GL.GL_NEVER; - case Less: - return GL.GL_LESS; - case LessOrEqual: - return GL.GL_LEQUAL; - case Greater: - return GL.GL_GREATER; - case GreaterOrEqual: - return GL.GL_GEQUAL; - case Equal: - return GL.GL_EQUAL; - case NotEqual: - return GL.GL_NOTEQUAL; - case Always: - return GL.GL_ALWAYS; - default: - throw new UnsupportedOperationException("Unrecognized test function: " + testFunc); - } - } - - /*********************************************************************\ - |* Camera and World transforms *| - \*********************************************************************/ - @Override - public void setViewPort(int x, int y, int w, int h) { - if (x != vpX || vpY != y || vpW != w || vpH != h) { - GL gl = GLContext.getCurrentGL(); - gl.glViewport(x, y, w, h); - vpX = x; - vpY = y; - vpW = w; - vpH = h; - } - } - - @Override - public void setClipRect(int x, int y, int width, int height) { - GL gl = GLContext.getCurrentGL(); - if (!context.clipRectEnabled) { - gl.glEnable(GL.GL_SCISSOR_TEST); - context.clipRectEnabled = true; - } - if (clipX != x || clipY != y || clipW != width || clipH != height) { - gl.glScissor(x, y, width, height); - clipX = x; - clipY = y; - clipW = width; - clipH = height; - } - } - - @Override - public void clearClipRect() { - if (context.clipRectEnabled) { - GL gl = GLContext.getCurrentGL(); - gl.glDisable(GL.GL_SCISSOR_TEST); - context.clipRectEnabled = false; - - clipX = 0; - clipY = 0; - clipW = 0; - clipH = 0; - } - } - - @Override - public void postFrame() { - objManager.deleteUnused(this); - } - - /*********************************************************************\ - |* Shaders *| - \*********************************************************************/ - protected void updateUniformLocation(Shader shader, Uniform uniform) { - GL gl = GLContext.getCurrentGL(); - // passing a null terminated string is not necessary with JOGL 2.0 - int loc = gl.getGL2ES2().glGetUniformLocation(shader.getId(), uniform.getName()); - if (loc < 0) { - uniform.setLocation(-1); - // uniform is not declared in shader - logger.log(Level.FINE, "Uniform {0} is not declared in shader {1}.", new Object[]{uniform.getName(), shader.getSources()}); - - } else { - uniform.setLocation(loc); - } - } - - protected void bindProgram(Shader shader) { - int shaderId = shader.getId(); - if (context.boundShaderProgram != shaderId) { - GL gl = GLContext.getCurrentGL(); - gl.getGL2ES2().glUseProgram(shaderId); - statistics.onShaderUse(shader, true); - boundShader = shader; - context.boundShaderProgram = shaderId; - } else { - statistics.onShaderUse(shader, false); - } - } - - protected void updateUniform(Shader shader, Uniform uniform) { - int shaderId = shader.getId(); - - assert uniform.getName() != null; - assert shaderId > 0; - - bindProgram(shader); - - int loc = uniform.getLocation(); - if (loc == -1) { - return; - } - - if (loc == -2) { - // get uniform location - updateUniformLocation(shader, uniform); - if (uniform.getLocation() == -1) { - // not declared, ignore - uniform.clearUpdateNeeded(); - return; - } - loc = uniform.getLocation(); - } - - if (uniform.getVarType() == null) { - return; // value not set yet.. - } - statistics.onUniformSet(); - - uniform.clearUpdateNeeded(); - FloatBuffer fb; - IntBuffer ib; - GL gl = GLContext.getCurrentGL(); - switch (uniform.getVarType()) { - case Float: - Float f = (Float) uniform.getValue(); - gl.getGL2ES2().glUniform1f(loc, f.floatValue()); - break; - case Vector2: - Vector2f v2 = (Vector2f) uniform.getValue(); - gl.getGL2ES2().glUniform2f(loc, v2.getX(), v2.getY()); - break; - case Vector3: - Vector3f v3 = (Vector3f) uniform.getValue(); - gl.getGL2ES2().glUniform3f(loc, v3.getX(), v3.getY(), v3.getZ()); - break; - case Vector4: - Object val = uniform.getValue(); - if (val instanceof ColorRGBA) { - ColorRGBA c = (ColorRGBA) val; - gl.getGL2ES2().glUniform4f(loc, c.r, c.g, c.b, c.a); - } else if (val instanceof Vector4f) { - Vector4f c = (Vector4f) val; - gl.getGL2ES2().glUniform4f(loc, c.x, c.y, c.z, c.w); - } else { - Quaternion c = (Quaternion) uniform.getValue(); - gl.getGL2ES2().glUniform4f(loc, c.getX(), c.getY(), c.getZ(), c.getW()); - } - break; - case Boolean: - Boolean b = (Boolean) uniform.getValue(); - gl.getGL2ES2().glUniform1i(loc, b.booleanValue() ? GL.GL_TRUE : GL.GL_FALSE); - break; - case Matrix3: - fb = (FloatBuffer) uniform.getValue(); - assert fb.remaining() == 9; - gl.getGL2ES2().glUniformMatrix3fv(loc, 1, false, fb); - break; - case Matrix4: - fb = (FloatBuffer) uniform.getValue(); - assert fb.remaining() == 16; - gl.getGL2ES2().glUniformMatrix4fv(loc, 1, false, fb); - break; - case IntArray: - ib = (IntBuffer) uniform.getValue(); - gl.getGL2ES2().glUniform1iv(loc, ib.remaining(), ib); - break; - case FloatArray: - fb = (FloatBuffer) uniform.getValue(); - gl.getGL2ES2().glUniform1fv(loc, fb.remaining(), fb); - break; - case Vector2Array: - fb = (FloatBuffer) uniform.getValue(); - gl.getGL2ES2().glUniform2fv(loc, fb.remaining(), fb); - break; - case Vector3Array: - fb = (FloatBuffer) uniform.getValue(); - gl.getGL2ES2().glUniform3fv(loc, fb.remaining(), fb); - break; - case Vector4Array: - fb = (FloatBuffer) uniform.getValue(); - gl.getGL2ES2().glUniform4fv(loc, fb.remaining(), fb); - break; - case Matrix4Array: - fb = (FloatBuffer) uniform.getValue(); - gl.getGL2ES2().glUniformMatrix4fv(loc, 1, false, fb); - break; - case Int: - Integer i = (Integer) uniform.getValue(); - gl.getGL2ES2().glUniform1i(loc, i.intValue()); - break; - default: - throw new UnsupportedOperationException("Unsupported uniform type: " + uniform.getVarType()); - } - } - - protected void updateShaderUniforms(Shader shader) { - ListMap uniforms = shader.getUniformMap(); - for (int i = 0; i < uniforms.size(); i++) { - Uniform uniform = uniforms.getValue(i); - if (uniform.isUpdateNeeded()) { - updateUniform(shader, uniform); - } - } - } - - protected void resetUniformLocations(Shader shader) { - ListMap uniforms = shader.getUniformMap(); - for (int i = 0; i < uniforms.size(); i++) { - Uniform uniform = uniforms.getValue(i); - uniform.reset(); // e.g check location again - } - } - - public int convertShaderType(Shader.ShaderType type) { - switch (type) { - case Fragment: - return GL2ES2.GL_FRAGMENT_SHADER; - case Vertex: - return GL2ES2.GL_VERTEX_SHADER; -// case Geometry: -// return GL3.GL_GEOMETRY_SHADER_ARB; - default: - throw new UnsupportedOperationException("Unrecognized shader type."); - } - } - - public void updateShaderSourceData(ShaderSource source) { - int id = source.getId(); - GL gl = GLContext.getCurrentGL(); - if (id == -1) { - // Create id -// if (gl.isGL2ES2()) { - id = gl.getGL2ES2().glCreateShader(convertShaderType(source.getType())); -// } -// else { -// if (gl.isGL2()) { -// id = gl.getGL2().glCreateShaderObjectARB(convertShaderType(source.getType())); -// } -// } - if (id <= 0) { - throw new RendererException("Invalid ID received when trying to create shader."); - } - - source.setId(id); - } else { - throw new RendererException("Cannot recompile shader source"); - } - - // Upload shader source. - // Merge the defines and source code. - String language = source.getLanguage(); - stringBuf.setLength(0); - if (language.startsWith("GLSL")) { - int version = Integer.parseInt(language.substring(4)); - if (version > 100) { - stringBuf.append("#version "); - stringBuf.append(language.substring(4)); - if (version >= 150) { - stringBuf.append(" core"); - } - stringBuf.append("\n"); - } - } - updateNameBuffer(); - - byte[] definesCodeData = source.getDefines().getBytes(); - byte[] sourceCodeData = source.getSource().getBytes(); - ByteBuffer codeBuf = BufferUtils.createByteBuffer(nameBuf.limit() - + definesCodeData.length - + sourceCodeData.length); - codeBuf.put(nameBuf); - codeBuf.put(definesCodeData); - codeBuf.put(sourceCodeData); - codeBuf.flip(); - - byte[] array = new byte[codeBuf.limit()]; - codeBuf.rewind(); - codeBuf.get(array); - codeBuf.rewind(); - - gl.getGL2ES2().glShaderSource(id, 1, new String[]{new String(array)}, new int[]{array.length}, 0); - gl.getGL2ES2().glCompileShader(id); - - gl.getGL2ES2().glGetShaderiv(id, GL2ES2.GL_COMPILE_STATUS, intBuf1); - - boolean compiledOK = intBuf1.get(0) == GL.GL_TRUE; - String infoLog = null; - - if (VALIDATE_SHADER || !compiledOK) { - // even if compile succeeded, check - // log for warnings - gl.getGL2ES2().glGetShaderiv(id, GL2ES2.GL_INFO_LOG_LENGTH, intBuf1); - int length = intBuf1.get(0); - if (length > 3) { - // get infos - ByteBuffer logBuf = BufferUtils.createByteBuffer(length); - gl.getGL2ES2().glGetShaderInfoLog(id, length, null, logBuf); - byte[] logBytes = new byte[length]; - logBuf.get(logBytes, 0, length); - // convert to string, etc - infoLog = new String(logBytes); - } - } - - if (compiledOK) { - if (infoLog != null) { - logger.log(Level.FINE, "{0} compile success\n{1}", - new Object[]{source.getName(), infoLog}); - } else { - logger.log(Level.FINE, "{0} compile success", source.getName()); - } - source.clearUpdateNeeded(); - } else { - logger.log(Level.WARNING, "Bad compile of:\n{0}", - new Object[]{ShaderDebug.formatShaderSource(stringBuf.toString() + source.getDefines() + source.getSource())}); - if (infoLog != null) { - throw new RendererException("compile error in: " + source + "\n" + infoLog); - } else { - throw new RendererException("compile error in: " + source + "\nerror: "); - } - } - } - - public void updateShaderData(Shader shader) { - GL gl = GLContext.getCurrentGL(); - int id = shader.getId(); - boolean needRegister = false; - if (id == -1) { - // create program - id = gl.getGL2ES2().glCreateProgram(); - if (id == 0) { - throw new RendererException("Invalid ID (" + id + ") received when trying to create shader program."); - } - - shader.setId(id); - needRegister = true; - } - - for (ShaderSource source : shader.getSources()) { - if (source.isUpdateNeeded()) { - updateShaderSourceData(source); - } - gl.getGL2ES2().glAttachShader(id, source.getId()); - } - - if (gl.isGL2GL3() && gl.isExtensionAvailable("GL_EXT_gpu_shader4")) { - // Check if GLSL version is 1.5 for shader - gl.getGL2GL3().glBindFragDataLocation(id, 0, "outFragColor"); - // For MRT - for (int i = 0; i < maxMRTFBOAttachs; i++) { - gl.getGL2GL3().glBindFragDataLocation(id, i, "outFragData[" + i + "]"); - } - } - - // Link shaders to program - gl.getGL2ES2().glLinkProgram(id); - - // Check link status - gl.getGL2ES2().glGetProgramiv(id, GL2ES2.GL_LINK_STATUS, intBuf1); - boolean linkOK = intBuf1.get(0) == GL.GL_TRUE; - String infoLog = null; - - if (VALIDATE_SHADER || !linkOK) { - gl.getGL2ES2().glGetProgramiv(id, GL2ES2.GL_INFO_LOG_LENGTH, intBuf1); - int length = intBuf1.get(0); - if (length > 3) { - // get infos - ByteBuffer logBuf = BufferUtils.createByteBuffer(length); - gl.getGL2ES2().glGetProgramInfoLog(id, length, null, logBuf); - - // convert to string, etc - byte[] logBytes = new byte[length]; - logBuf.get(logBytes, 0, length); - infoLog = new String(logBytes); - } - } - - if (linkOK) { - if (infoLog != null) { - logger.log(Level.FINE, "shader link success. \n{0}", infoLog); - } else { - logger.fine("shader link success"); - } - shader.clearUpdateNeeded(); - if (needRegister) { - // Register shader for clean up if it was created in this method. - objManager.registerObject(shader); - statistics.onNewShader(); - } else { - // OpenGL spec: uniform locations may change after re-link - resetUniformLocations(shader); - } - } else { - if (infoLog != null) { - throw new RendererException("Shader failed to link, shader:" + shader + "\n" + infoLog); - } else { - throw new RendererException("Shader failed to link, shader:" + shader + "\ninfo: "); - } - } - } - - @Override - public void setShader(Shader shader) { - if (shader == null) { - throw new IllegalArgumentException("Shader cannot be null"); - } else { - if (shader.isUpdateNeeded()) { - updateShaderData(shader); - } - - // NOTE: might want to check if any of the - // sources need an update? - - assert shader.getId() > 0; - - updateShaderUniforms(shader); - bindProgram(shader); - } - } - - @Override - public void deleteShaderSource(ShaderSource source) { - if (source.getId() < 0) { - logger.warning("Shader source is not uploaded to GPU, cannot delete."); - return; - } - source.clearUpdateNeeded(); - GL gl = GLContext.getCurrentGL(); - gl.getGL2ES2().glDeleteShader(source.getId()); - source.resetObject(); - } - - @Override - public void deleteShader(Shader shader) { - if (shader.getId() == -1) { - logger.warning("Shader is not uploaded to GPU, cannot delete."); - return; - } - - GL gl = GLContext.getCurrentGL(); - for (ShaderSource source : shader.getSources()) { - if (source.getId() != -1) { - gl.getGL2ES2().glDetachShader(shader.getId(), source.getId()); - deleteShaderSource(source); - } - } - - gl.getGL2ES2().glDeleteProgram(shader.getId()); - statistics.onDeleteShader(); - shader.resetObject(); - } - - /*********************************************************************\ - |* Framebuffers *| - \*********************************************************************/ - @Override - public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst, boolean copyDepth) { - GL gl = GLContext.getCurrentGL(); - if (gl.isExtensionAvailable("GL_EXT_framebuffer_blit") && gl.isGL2GL3()) { - int srcX0 = 0; - int srcY0 = 0; - int srcX1; - int srcY1; - - int dstX0 = 0; - int dstY0 = 0; - int dstX1; - int dstY1; - - int prevFBO = context.boundFBO; - - if (mainFbOverride != null) { - if (src == null) { - src = mainFbOverride; - } - if (dst == null) { - dst = mainFbOverride; - } - } - - if (src != null && src.isUpdateNeeded()) { - updateFrameBuffer(src); - } - - if (dst != null && dst.isUpdateNeeded()) { - updateFrameBuffer(dst); - } - - if (src == null) { - gl.glBindFramebuffer(GL2GL3.GL_READ_FRAMEBUFFER, 0); - srcX0 = vpX; - srcY0 = vpY; - srcX1 = vpX + vpW; - srcY1 = vpY + vpH; - } else { - gl.glBindFramebuffer(GL2GL3.GL_READ_FRAMEBUFFER, src.getId()); - srcX1 = src.getWidth(); - srcY1 = src.getHeight(); - } - if (dst == null) { - gl.glBindFramebuffer(GL2GL3.GL_DRAW_FRAMEBUFFER, 0); - dstX0 = vpX; - dstY0 = vpY; - dstX1 = vpX + vpW; - dstY1 = vpY + vpH; - } else { - gl.glBindFramebuffer(GL2GL3.GL_DRAW_FRAMEBUFFER, dst.getId()); - dstX1 = dst.getWidth(); - dstY1 = dst.getHeight(); - } - int mask = GL.GL_COLOR_BUFFER_BIT; - if (copyDepth) { - mask |= GL.GL_DEPTH_BUFFER_BIT; - } - gl.getGL2GL3().glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, - dstX0, dstY0, dstX1, dstY1, mask, - GL.GL_NEAREST); - gl.glBindFramebuffer(GL2GL3.GL_FRAMEBUFFER, prevFBO); - - - try { - checkFrameBufferError(); - } catch (IllegalStateException ex) { - logger.log(Level.SEVERE, "Source FBO:\n{0}", src); - logger.log(Level.SEVERE, "Dest FBO:\n{0}", dst); - throw ex; - } - } else { - throw new RendererException("EXT_framebuffer_blit required."); - // TODO: support non-blit copies? - } - } - - private String getTargetBufferName(int buffer) { - switch (buffer) { - case GL.GL_NONE: - return "NONE"; - case GL.GL_FRONT: - return "GL_FRONT"; - case GL.GL_BACK: - return "GL_BACK"; - default: - if (buffer >= GL.GL_COLOR_ATTACHMENT0 - && buffer <= GL2ES2.GL_COLOR_ATTACHMENT15) { - return "GL_COLOR_ATTACHMENT" - + (buffer - GL.GL_COLOR_ATTACHMENT0); - } else { - return "UNKNOWN? " + buffer; - } - } - } - - private void printRealRenderBufferInfo(FrameBuffer fb, RenderBuffer rb, String name) { - GL gl = GLContext.getCurrentGL(); - System.out.println("== Renderbuffer " + name + " =="); - System.out.println("RB ID: " + rb.getId()); - System.out.println("Is proper? " + gl.glIsRenderbuffer(rb.getId())); - - int attachment = convertAttachmentSlot(rb.getSlot()); - - gl.glGetFramebufferAttachmentParameteriv(GL2GL3.GL_DRAW_FRAMEBUFFER, - attachment, - GL.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, intBuf16); - int type = intBuf16.get(0); - gl.glGetFramebufferAttachmentParameteriv(GL2GL3.GL_DRAW_FRAMEBUFFER, - attachment, - GL.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, intBuf16); - int rbName = intBuf16.get(0); - - switch (type) { - case GL.GL_NONE: - System.out.println("Type: None"); - break; - case GL.GL_TEXTURE: - System.out.println("Type: Texture"); - break; - case GL.GL_RENDERBUFFER: - System.out.println("Type: Buffer"); - System.out.println("RB ID: " + rbName); - break; - } - - - - } - - private void printRealFrameBufferInfo(FrameBuffer fb) { - GL gl = GLContext.getCurrentGL(); - final byte[] param = new byte[1]; - gl.glGetBooleanv(GL2GL3.GL_DOUBLEBUFFER, param, 0); - boolean doubleBuffer = param[0] != (byte) 0x00; - gl.glGetIntegerv(GL2GL3.GL_DRAW_BUFFER, intBuf16); - String drawBuf = getTargetBufferName(intBuf16.get(0)); - gl.glGetIntegerv(GL2GL3.GL_READ_BUFFER, intBuf16); - String readBuf = getTargetBufferName(intBuf16.get(0)); - - int fbId = fb.getId(); - gl.glGetIntegerv(GL2GL3.GL_DRAW_FRAMEBUFFER_BINDING, intBuf16); - int curDrawBinding = intBuf16.get(0); - gl.glGetIntegerv(GL2GL3.GL_READ_FRAMEBUFFER_BINDING, intBuf16); - int curReadBinding = intBuf16.get(0); - - System.out.println("=== OpenGL FBO State ==="); - System.out.println("Context doublebuffered? " + doubleBuffer); - System.out.println("FBO ID: " + fbId); - System.out.println("Is proper? " + gl.glIsFramebuffer(fbId)); - System.out.println("Is bound to draw? " + (fbId == curDrawBinding)); - System.out.println("Is bound to read? " + (fbId == curReadBinding)); - System.out.println("Draw buffer: " + drawBuf); - System.out.println("Read buffer: " + readBuf); - - if (context.boundFBO != fbId) { - gl.glBindFramebuffer(GL2GL3.GL_DRAW_FRAMEBUFFER, fbId); - context.boundFBO = fbId; - } - - if (fb.getDepthBuffer() != null) { - printRealRenderBufferInfo(fb, fb.getDepthBuffer(), "Depth"); - } - for (int i = 0; i < fb.getNumColorBuffers(); i++) { - printRealRenderBufferInfo(fb, fb.getColorBuffer(i), "Color" + i); - } - } - - private void checkFrameBufferError() { - GL gl = GLContext.getCurrentGL(); - int status = gl.glCheckFramebufferStatus(GL.GL_FRAMEBUFFER); - switch (status) { - case GL.GL_FRAMEBUFFER_COMPLETE: - break; - case GL.GL_FRAMEBUFFER_UNSUPPORTED: - // Choose different formats - throw new IllegalStateException("Framebuffer object format is " - + "unsupported by the video hardware."); - case GL.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: - throw new IllegalStateException("Framebuffer has erronous attachment."); - case GL.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: - throw new IllegalStateException("Framebuffer is missing required attachment."); - case GL.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: - throw new IllegalStateException( - "Framebuffer attachments must have same dimensions."); - case GL.GL_FRAMEBUFFER_INCOMPLETE_FORMATS: - throw new IllegalStateException("Framebuffer attachments must have same formats."); - case GL2GL3.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: - throw new IllegalStateException("Incomplete draw buffer."); - case GL2GL3.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: - throw new IllegalStateException("Incomplete read buffer."); - case GL2GL3.GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: - throw new IllegalStateException("Incomplete multisample buffer."); - default: - // Programming error; will fail on all hardware - throw new IllegalStateException("Some video driver error " - + "or programming error occured. " - + "Framebuffer object status is invalid. "); - } - } - - private void updateRenderBuffer(FrameBuffer fb, RenderBuffer rb) { - GL gl = GLContext.getCurrentGL(); - int id = rb.getId(); - if (id == -1) { - gl.glGenRenderbuffers(1, intBuf1); - id = intBuf1.get(0); - rb.setId(id); - } - - if (context.boundRB != id) { - gl.glBindRenderbuffer(GL.GL_RENDERBUFFER, id); - context.boundRB = id; - } - - if (fb.getWidth() > maxRBSize || fb.getHeight() > maxRBSize) { - throw new RendererException("Resolution " + fb.getWidth() - + ":" + fb.getHeight() + " is not supported."); - } - - TextureUtil.GLImageFormat glFmt = TextureUtil.getImageFormatWithError(rb.getFormat(), fb.isSrgb()); - - if (fb.getSamples() > 1 && gl.isExtensionAvailable("GL_EXT_framebuffer_multisample") - && gl.isGL2GL3()/*&& gl.isFunctionAvailable("glRenderbufferStorageMultisample")*/) { - int samples = fb.getSamples(); - if (maxFBOSamples < samples) { - samples = maxFBOSamples; - } - gl.getGL2GL3() - .glRenderbufferStorageMultisample(GL.GL_RENDERBUFFER, samples, - glFmt.internalFormat, fb.getWidth(), - fb.getHeight()); - } else { - gl.glRenderbufferStorage(GL.GL_RENDERBUFFER, - glFmt.internalFormat, fb.getWidth(), fb.getHeight()); - } - } - - private int convertAttachmentSlot(int attachmentSlot) { - // can also add support for stencil here - if (attachmentSlot == FrameBuffer.SLOT_DEPTH) { - return GL.GL_DEPTH_ATTACHMENT; - } else if (attachmentSlot == FrameBuffer.SLOT_DEPTH_STENCIL) { - return GL2ES3.GL_DEPTH_STENCIL_ATTACHMENT; - } else if (attachmentSlot < 0 || attachmentSlot >= 16) { - throw new UnsupportedOperationException("Invalid FBO attachment slot: " + attachmentSlot); - } - - return GL.GL_COLOR_ATTACHMENT0 + attachmentSlot; - } - - public void updateRenderTexture(FrameBuffer fb, RenderBuffer rb) { - GL gl = GLContext.getCurrentGL(); - Texture tex = rb.getTexture(); - Image image = tex.getImage(); - if (image.isUpdateNeeded()) { - updateTexImageData(image, tex.getType(), 0); - - // NOTE: For depth textures, sets nearest/no-mips mode - // Required to fix "framebuffer unsupported" - // for old NVIDIA drivers! - setupTextureParams(tex); - } - - gl.glFramebufferTexture2D(GL.GL_FRAMEBUFFER, - convertAttachmentSlot(rb.getSlot()), - convertTextureType(tex.getType(), image.getMultiSamples(), rb.getFace()), - image.getId(), - 0); - } - - public void updateFrameBufferAttachment(FrameBuffer fb, RenderBuffer rb) { - boolean needAttach; - if (rb.getTexture() == null) { - // if it hasn't been created yet, then attach is required. - needAttach = rb.getId() == -1; - updateRenderBuffer(fb, rb); - } else { - needAttach = false; - updateRenderTexture(fb, rb); - } - if (needAttach) { - GL gl = GLContext.getCurrentGL(); - gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, convertAttachmentSlot(rb.getSlot()), - GL.GL_RENDERBUFFER, rb.getId()); - } - } - - public void updateFrameBuffer(FrameBuffer fb) { - GL gl = GLContext.getCurrentGL(); - int id = fb.getId(); - if (id == -1) { - // create FBO - gl.glGenFramebuffers(1, intBuf1); - id = intBuf1.get(0); - fb.setId(id); - objManager.registerObject(fb); - - statistics.onNewFrameBuffer(); - } - - if (context.boundFBO != id) { - gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, id); - // binding an FBO automatically sets draw buf to GL_COLOR_ATTACHMENT0 - context.boundDrawBuf = 0; - context.boundFBO = id; - } - - FrameBuffer.RenderBuffer depthBuf = fb.getDepthBuffer(); - if (depthBuf != null) { - updateFrameBufferAttachment(fb, depthBuf); - } - - for (int i = 0; i < fb.getNumColorBuffers(); i++) { - FrameBuffer.RenderBuffer colorBuf = fb.getColorBuffer(i); - updateFrameBufferAttachment(fb, colorBuf); - } - - fb.clearUpdateNeeded(); - } - - public Vector2f[] getFrameBufferSamplePositions(FrameBuffer fb) { - if (fb.getSamples() <= 1) { - throw new IllegalArgumentException("Framebuffer must be multisampled"); - } - - setFrameBuffer(fb); - - Vector2f[] samplePositions = new Vector2f[fb.getSamples()]; - FloatBuffer samplePos = BufferUtils.createFloatBuffer(2); - GL gl = GLContext.getCurrentGL(); - if (gl.isGL2GL3()) { - for (int i = 0; i < samplePositions.length; i++) { - gl.getGL3().glGetMultisamplefv(GL3.GL_SAMPLE_POSITION, i, samplePos); - samplePos.clear(); - samplePositions[i] = new Vector2f(samplePos.get(0) - 0.5f, - samplePos.get(1) - 0.5f); - } - } - return samplePositions; - } - - @Override - public void setMainFrameBufferOverride(FrameBuffer fb) { - mainFbOverride = fb; - } - - @Override - public void setFrameBuffer(FrameBuffer fb) { - GL gl = GLContext.getCurrentGL(); - if (!gl.isExtensionAvailable("GL_EXT_framebuffer_object")) { - throw new RendererException("Framebuffer objects are not supported" + - " by the video hardware"); - } - - if (fb == null && mainFbOverride != null) { - fb = mainFbOverride; - } - - if (lastFb == fb) { - if (fb == null || !fb.isUpdateNeeded()) { - return; - } - } - - // generate mipmaps for last FB if needed - if (lastFb != null) { - for (int i = 0; i < lastFb.getNumColorBuffers(); i++) { - RenderBuffer rb = lastFb.getColorBuffer(i); - Texture tex = rb.getTexture(); - if (tex != null - && tex.getMinFilter().usesMipMapLevels()) { - setTexture(0, rb.getTexture()); - - int textureType = convertTextureType(tex.getType(), tex.getImage().getMultiSamples(), rb.getFace()); - gl.glEnable(textureType); - gl.glGenerateMipmap(textureType); - gl.glDisable(textureType); - } - } - } - - if (fb == null) { - // unbind any fbos - if (context.boundFBO != 0) { - gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, 0); - statistics.onFrameBufferUse(null, true); - - context.boundFBO = 0; - } - // select back buffer - if (context.boundDrawBuf != -1) { - if (gl.isGL2GL3()) { - gl.getGL2GL3().glDrawBuffer(initialDrawBuf); - } - context.boundDrawBuf = -1; - } - if (context.boundReadBuf != -1) { - if (gl.isGL2GL3()) { - gl.getGL2GL3().glReadBuffer(initialReadBuf); - } - context.boundReadBuf = -1; - } - - lastFb = 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); - } - - if (context.boundFBO != fb.getId()) { - gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, fb.getId()); - statistics.onFrameBufferUse(fb, true); - - // update viewport to reflect framebuffer's resolution - setViewPort(0, 0, fb.getWidth(), fb.getHeight()); - - context.boundFBO = fb.getId(); - } else { - statistics.onFrameBufferUse(fb, false); - } - if (fb.getNumColorBuffers() == 0) { - // make sure to select NONE as draw buf - // no color buffer attached. select NONE - if (context.boundDrawBuf != -2) { - if (gl.isGL2GL3()) { - gl.getGL2GL3().glDrawBuffer(GL.GL_NONE); - } - context.boundDrawBuf = -2; - } - if (context.boundReadBuf != -2) { - if (gl.isGL2GL3()) { - gl.getGL2GL3().glReadBuffer(GL.GL_NONE); - } - context.boundReadBuf = -2; - } - } else { - if (fb.getNumColorBuffers() > maxFBOAttachs) { - throw new RendererException("Framebuffer has more color " - + "attachments than are supported" - + " by the video hardware!"); - } - if (fb.isMultiTarget()) { - if (fb.getNumColorBuffers() > maxMRTFBOAttachs) { - throw new RendererException("Framebuffer has more" - + " multi targets than are supported" - + " by the video hardware!"); - } - - if (context.boundDrawBuf != 100 + fb.getNumColorBuffers()) { - intBuf16.clear(); - for (int i = 0; i < fb.getNumColorBuffers(); i++) { - intBuf16.put(GL.GL_COLOR_ATTACHMENT0 + i); - } - - intBuf16.flip(); - if (gl.isGL2GL3()) { - gl.getGL2GL3().glDrawBuffers(intBuf16.limit(), intBuf16); - } - context.boundDrawBuf = 100 + fb.getNumColorBuffers(); - } - } else { - RenderBuffer rb = fb.getColorBuffer(fb.getTargetIndex()); - // select this draw buffer - if (context.boundDrawBuf != rb.getSlot()) { - if (gl.isGL2GL3()) { - gl.getGL2GL3().glDrawBuffer(GL.GL_COLOR_ATTACHMENT0 + rb.getSlot()); - } - context.boundDrawBuf = rb.getSlot(); - } - } - } - - assert fb.getId() >= 0; - assert context.boundFBO == fb.getId(); - lastFb = fb; - - try { - checkFrameBufferError(); - } catch (IllegalStateException ex) { - logger.log(Level.SEVERE, "=== jMonkeyEngine FBO State ===\n{0}", fb); - printRealFrameBufferInfo(fb); - throw ex; - } - } - } - - @Override - public void readFrameBuffer(FrameBuffer fb, ByteBuffer byteBuf) { - GL gl = GLContext.getCurrentGL(); - if (fb != null) { - RenderBuffer rb = fb.getColorBuffer(); - if (rb == null) { - throw new IllegalArgumentException("Specified framebuffer" - + " does not have a colorbuffer"); - } - - setFrameBuffer(fb); - if (context.boundReadBuf != rb.getSlot()) { - if (gl.isGL2GL3()) { - gl.getGL2GL3().glReadBuffer(GL.GL_COLOR_ATTACHMENT0 + rb.getSlot()); - } - context.boundReadBuf = rb.getSlot(); - } - } else { - setFrameBuffer(null); - } - - gl.glReadPixels(vpX, vpY, vpW, vpH, /*GL.GL_RGBA*/ GL.GL_BGRA, GL.GL_UNSIGNED_BYTE, byteBuf); - } - - private void deleteRenderBuffer(FrameBuffer fb, RenderBuffer rb) { - intBuf1.put(0, rb.getId()); - GL gl = GLContext.getCurrentGL(); - gl.glDeleteRenderbuffers(1, intBuf1); - } - - @Override - public void deleteFrameBuffer(FrameBuffer fb) { - if (fb.getId() != -1) { - GL gl = GLContext.getCurrentGL(); - if (context.boundFBO == fb.getId()) { - gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, 0); - context.boundFBO = 0; - } - - if (fb.getDepthBuffer() != null) { - deleteRenderBuffer(fb, fb.getDepthBuffer()); - } - if (fb.getColorBuffer() != null) { - deleteRenderBuffer(fb, fb.getColorBuffer()); - } - - intBuf1.put(0, fb.getId()); - gl.glDeleteFramebuffers(1, intBuf1); - fb.resetObject(); - - statistics.onDeleteFrameBuffer(); - } - } - - /*********************************************************************\ - |* Textures *| - \*********************************************************************/ - private int convertTextureType(Texture.Type type, int samples, int face) { - GL gl = GLContext.getCurrentGL(); - if (samples > 1 && !gl.isExtensionAvailable("GL_ARB_texture_multisample")) { - throw new RendererException("Multisample textures are not supported" + - " by the video hardware."); - } - - switch (type) { - case TwoDimensional: - if (samples > 1) { - return GL3.GL_TEXTURE_2D_MULTISAMPLE; - } else { - return GL.GL_TEXTURE_2D; - } - case TwoDimensionalArray: - if (samples > 1) { - return GL3.GL_TEXTURE_2D_MULTISAMPLE_ARRAY; - } else { - return GL2ES3.GL_TEXTURE_2D_ARRAY; - } - case ThreeDimensional: - return GL2ES2.GL_TEXTURE_3D; - case CubeMap: - if (face < 0) { - return GL.GL_TEXTURE_CUBE_MAP; - } else if (face < 6) { - return GL.GL_TEXTURE_CUBE_MAP_POSITIVE_X + face; - } else { - throw new UnsupportedOperationException("Invalid cube map face index: " + face); - } - default: - throw new UnsupportedOperationException("Unknown texture type: " + type); - } - } - - private int convertMagFilter(Texture.MagFilter filter) { - switch (filter) { - case Bilinear: - return GL.GL_LINEAR; - case Nearest: - return GL.GL_NEAREST; - default: - throw new UnsupportedOperationException("Unknown mag filter: " + filter); - } - } - - private int convertMinFilter(Texture.MinFilter filter) { - switch (filter) { - case Trilinear: - return GL.GL_LINEAR_MIPMAP_LINEAR; - case BilinearNearestMipMap: - return GL.GL_LINEAR_MIPMAP_NEAREST; - case NearestLinearMipMap: - return GL.GL_NEAREST_MIPMAP_LINEAR; - case NearestNearestMipMap: - return GL.GL_NEAREST_MIPMAP_NEAREST; - case BilinearNoMipMaps: - return GL.GL_LINEAR; - case NearestNoMipMaps: - return GL.GL_NEAREST; - default: - throw new UnsupportedOperationException("Unknown min filter: " + filter); - } - } - - private int convertWrapMode(Texture.WrapMode mode) { - switch (mode) { - case BorderClamp: - return GL2GL3.GL_CLAMP_TO_BORDER; - case Clamp: - // Falldown intentional. - case EdgeClamp: - return GL.GL_CLAMP_TO_EDGE; - case Repeat: - return GL.GL_REPEAT; - case MirroredRepeat: - return GL.GL_MIRRORED_REPEAT; - default: - throw new UnsupportedOperationException("Unknown wrap mode: " + mode); - } - } - - @SuppressWarnings("fallthrough") - private void setupTextureParams(Texture tex) { - GL gl = GLContext.getCurrentGL(); - Image image = tex.getImage(); - int target = convertTextureType(tex.getType(), image != null ? image.getMultiSamples() : 1, -1); - - // filter things - int minFilter = convertMinFilter(tex.getMinFilter()); - int magFilter = convertMagFilter(tex.getMagFilter()); - gl.glTexParameteri(target, GL.GL_TEXTURE_MIN_FILTER, minFilter); - gl.glTexParameteri(target, GL.GL_TEXTURE_MAG_FILTER, magFilter); - - if (tex.getAnisotropicFilter() > 1) { - if (gl.isExtensionAvailable("GL_EXT_texture_filter_anisotropic")) { - gl.glTexParameterf(target, - GL.GL_TEXTURE_MAX_ANISOTROPY_EXT, - tex.getAnisotropicFilter()); - } - } - - if (context.pointSprite) { - return; // Attempt to fix glTexParameter crash for some ATI GPUs - } - // repeat modes - switch (tex.getType()) { - case ThreeDimensional: - case CubeMap: // cubemaps use 3D coords - gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_WRAP_R, convertWrapMode(tex.getWrap(WrapAxis.R))); - case TwoDimensional: - case TwoDimensionalArray: - gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_WRAP_T, convertWrapMode(tex.getWrap(WrapAxis.T))); - // fall down here is intentional.. -// case OneDimensional: - gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_WRAP_S, convertWrapMode(tex.getWrap(WrapAxis.S))); - break; - default: - throw new UnsupportedOperationException("Unknown texture type: " + tex.getType()); - } - - if (tex.isNeedCompareModeUpdate()) { - // R to Texture compare mode - if (tex.getShadowCompareMode() != Texture.ShadowCompareMode.Off) { - gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_COMPARE_MODE, GL2.GL_COMPARE_R_TO_TEXTURE); - gl.glTexParameteri(target, GL2.GL_DEPTH_TEXTURE_MODE, GL2.GL_INTENSITY); - if (tex.getShadowCompareMode() == Texture.ShadowCompareMode.GreaterOrEqual) { - gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_COMPARE_FUNC, GL.GL_GEQUAL); - } else { - gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_COMPARE_FUNC, GL.GL_LEQUAL); - } - } else { - //restoring default value - gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_COMPARE_MODE, GL.GL_NONE); - } - tex.compareModeUpdated(); - } - } - - /** - * Uploads the given image to the GL driver. - * - * @param img The image to upload - * @param type How the data in the image argument should be interpreted. - * @param unit The texture slot to be used to upload the image, not important - */ - public void updateTexImageData(Image img, Texture.Type type, int unit) { - int texId = img.getId(); - GL gl = GLContext.getCurrentGL(); - if (texId == -1) { - // create texture - gl.glGenTextures(1, intBuf1); - texId = intBuf1.get(0); - img.setId(texId); - objManager.registerObject(img); - - statistics.onNewTexture(); - } - - // bind texture - int target = convertTextureType(type, img.getMultiSamples(), -1); - if (context.boundTextureUnit != unit) { - gl.glActiveTexture(GL.GL_TEXTURE0 + unit); - context.boundTextureUnit = unit; - } - if (context.boundTextures[unit] != img) { - gl.glBindTexture(target, texId); - context.boundTextures[unit] = img; - - statistics.onTextureUse(img, true); - } - - if (!img.hasMipmaps() && img.isGeneratedMipmapsRequired()) { - // Image does not have mipmaps, but they are required. - // Generate from base level. - - if (!gl.isExtensionAvailable("GL_VERSION_3_0")) { - if (gl.isGL2ES1()) { - gl.glTexParameteri(target, GL2ES1.GL_GENERATE_MIPMAP, GL.GL_TRUE); - img.setMipmapsGenerated(true); - } - } else { - // For OpenGL3 and up. - // We'll generate mipmaps via glGenerateMipmapEXT (see below) - } - } else if (img.hasMipmaps()) { - // Image already has mipmaps, set the max level based on the - // number of mipmaps we have. - if (gl.isGL2GL3()) { - gl.glTexParameteri(target, GL2ES3.GL_TEXTURE_MAX_LEVEL, img.getMipMapSizes().length - 1); - } - } else { - // Image does not have mipmaps and they are not required. - // Specify that that the texture has no mipmaps. - gl.glTexParameteri(target, GL2ES3.GL_TEXTURE_MAX_LEVEL, 0); - } - - int imageSamples = img.getMultiSamples(); - if (imageSamples > 1) { - if (img.getFormat().isDepthFormat()) { - img.setMultiSamples(Math.min(maxDepthTexSamples, imageSamples)); - } else { - img.setMultiSamples(Math.min(maxColorTexSamples, imageSamples)); - } - } - - // Yes, some OpenGL2 cards (GeForce 5) still dont support NPOT. - if (!gl.isExtensionAvailable("GL_ARB_texture_non_power_of_two") && img.isNPOT()) { - if (img.getData(0) == null) { - throw new RendererException("non-power-of-2 framebuffer textures are not supported by the video hardware"); - } else { - MipMapGenerator.resizeToPowerOf2(img); - } - } - - // Check if graphics card doesn't support multisample textures - if (!gl.isExtensionAvailable("GL_ARB_texture_multisample")) { - if (img.getMultiSamples() > 1) { - throw new RendererException("Multisample textures not supported by graphics hardware"); - } - } - - if (target == GL.GL_TEXTURE_CUBE_MAP) { - List data = img.getData(); - if (data.size() != 6) { - logger.log(Level.WARNING, "Invalid texture: {0}\n" - + "Cubemap textures must contain 6 data units.", img); - return; - } - for (int i = 0; i < 6; i++) { - TextureUtil.uploadTexture(img, GL.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, i, 0, linearizeSrgbImages); - } - } else if (target == GL2ES3.GL_TEXTURE_2D_ARRAY) { - if (!caps.contains(Caps.TextureArray)) { - throw new RendererException("Texture arrays not supported by graphics hardware"); - } - - List data = img.getData(); - - // -1 index specifies prepare data for 2D Array - TextureUtil.uploadTexture(img, target, -1, 0, linearizeSrgbImages); - - for (int i = 0; i < data.size(); i++) { - // upload each slice of 2D array in turn - // this time with the appropriate index - TextureUtil.uploadTexture(img, target, i, 0, linearizeSrgbImages); - } - } else { - TextureUtil.uploadTexture(img, target, 0, 0, linearizeSrgbImages); - } - - if (img.getMultiSamples() != imageSamples) { - img.setMultiSamples(imageSamples); - } - - if (gl.isExtensionAvailable("GL_VERSION_3_0")) { - if (!img.hasMipmaps() && img.isGeneratedMipmapsRequired() && img.getData() != null) { - // XXX: Required for ATI - gl.glEnable(target); - gl.glGenerateMipmap(target); - gl.glDisable(target); - img.setMipmapsGenerated(true); - } - } - - img.clearUpdateNeeded(); - } - - @Override - public void setTexture(int unit, Texture tex) { - GL gl = GLContext.getCurrentGL(); - Image image = tex.getImage(); - if (image.isUpdateNeeded() || (image.isGeneratedMipmapsRequired() && !image.isMipmapsGenerated())) { - updateTexImageData(image, tex.getType(), unit); - } - - int texId = image.getId(); - assert texId != -1; - - Image[] textures = context.boundTextures; - - int type = convertTextureType(tex.getType(), image.getMultiSamples(), -1); -// if (!context.textureIndexList.moveToNew(unit)) { -// if (context.boundTextureUnit != unit){ -// gl.glActiveTexture(GL.GL_TEXTURE0 + unit); -// context.boundTextureUnit = unit; -// } -// gl.glEnable(type); -// } - - if (context.boundTextureUnit != unit) { - gl.glActiveTexture(GL.GL_TEXTURE0 + unit); - context.boundTextureUnit = unit; - } - if (textures[unit] != image) { - gl.glBindTexture(type, texId); - textures[unit] = image; - - statistics.onTextureUse(image, true); - } else { - statistics.onTextureUse(image, false); - } - - setupTextureParams(tex); - } - - @Override - public void modifyTexture(Texture tex, Image pixels, int x, int y) { - setTexture(0, tex); - TextureUtil.uploadSubTexture(pixels, convertTextureType(tex.getType(), tex.getImage().getMultiSamples(), -1), 0, x, y, linearizeSrgbImages); - } - - public void clearTextureUnits() { - /*GL gl = GLContext.getCurrentGL(); - IDList textureList = context.textureIndexList; - Texture[] textures = context.boundTextures; - for (int i = 0; i < textureList.oldLen; i++) { - int idx = textureList.oldList[i]; - - if (context.boundTextureUnit != idx) { - gl.glActiveTexture(GL.GL_TEXTURE0 + idx); - context.boundTextureUnit = idx; - } - gl.glDisable(convertTextureType(textures[idx].getType())); - textures[idx] = null; - } - context.textureIndexList.copyNewToOld();*/ - } - - @Override - public void deleteImage(Image image) { - int texId = image.getId(); - if (texId != -1) { - intBuf1.put(0, texId); - intBuf1.position(0).limit(1); - GL gl = GLContext.getCurrentGL(); - gl.glDeleteTextures(1, intBuf1); - image.resetObject(); - - statistics.onDeleteTexture(); - } - } - - /*********************************************************************\ - |* Vertex Buffers and Attributes *| - \*********************************************************************/ - private int convertUsage(Usage usage) { - switch (usage) { - case Static: - return GL.GL_STATIC_DRAW; - case Dynamic: - return GL.GL_DYNAMIC_DRAW; - case Stream: - return GL2ES2.GL_STREAM_DRAW; - default: - throw new RuntimeException("Unknown usage type: " + usage); - } - } - - private int convertFormat(VertexBuffer.Format format) { - switch (format) { - case Byte: - return GL.GL_BYTE; - case UnsignedByte: - return GL.GL_UNSIGNED_BYTE; - case Short: - return GL.GL_SHORT; - case UnsignedShort: - return GL.GL_UNSIGNED_SHORT; - case Int: - return GL2ES2.GL_INT; - case UnsignedInt: - return GL.GL_UNSIGNED_INT; - case Float: - return GL.GL_FLOAT; - case Double: - return GL2GL3.GL_DOUBLE; - default: - throw new UnsupportedOperationException("Unknown buffer format."); - - } - } - - @Override - public void updateBufferData(VertexBuffer vb) { - GL gl = GLContext.getCurrentGL(); - int bufId = vb.getId(); - boolean created = false; - if (bufId == -1) { - // create buffer - gl.glGenBuffers(1, intBuf1); - bufId = intBuf1.get(0); - vb.setId(bufId); - objManager.registerObject(vb); - - //statistics.onNewVertexBuffer(); - - created = true; - } - - // bind buffer - int target; - if (vb.getBufferType() == VertexBuffer.Type.Index) { - target = GL.GL_ELEMENT_ARRAY_BUFFER; - if (context.boundElementArrayVBO != bufId) { - gl.glBindBuffer(target, bufId); - context.boundElementArrayVBO = bufId; - //statistics.onVertexBufferUse(vb, true); - } else { - //statistics.onVertexBufferUse(vb, false); - } - } else { - target = GL.GL_ARRAY_BUFFER; - if (context.boundArrayVBO != bufId) { - gl.glBindBuffer(target, bufId); - context.boundArrayVBO = bufId; - //statistics.onVertexBufferUse(vb, true); - } else { - //statistics.onVertexBufferUse(vb, false); - } - } - - int usage = convertUsage(vb.getUsage()); - Buffer data = vb.getData(); - data.rewind(); - - if (created || vb.hasDataSizeChanged()) { - // upload data based on format - gl.glBufferData(target, data.capacity() * vb.getFormat().getComponentSize(), data, usage); - } else { - gl.glBufferSubData(target, 0, data.capacity() * vb.getFormat().getComponentSize(), data); - } - - vb.clearUpdateNeeded(); - } - - @Override - public void deleteBuffer(VertexBuffer vb) { - GL gl = GLContext.getCurrentGL(); - int bufId = vb.getId(); - if (bufId != -1) { - // delete buffer - intBuf1.put(0, bufId); - intBuf1.position(0).limit(1); - gl.glDeleteBuffers(1, intBuf1); - vb.resetObject(); - - //statistics.onDeleteVertexBuffer(); - } - } - - public void clearVertexAttribs() { - GL gl = GLContext.getCurrentGL(); - IDList attribList = context.attribIndexList; - for (int i = 0; i < attribList.oldLen; i++) { - int idx = attribList.oldList[i]; - gl.getGL2ES2().glDisableVertexAttribArray(idx); - if (context.boundAttribs[idx].isInstanced() && gl.isGL3ES3()) { - gl.getGL3ES3().glVertexAttribDivisor(idx, 0); - } - context.boundAttribs[idx] = null; - } - context.attribIndexList.copyNewToOld(); - } - - public void setVertexAttrib(VertexBuffer vb, VertexBuffer idb) { - if (vb.getBufferType() == VertexBuffer.Type.Index) { - throw new IllegalArgumentException("Index buffers not allowed to be set to vertex attrib"); - } - - int programId = context.boundShaderProgram; - if (programId > 0) { - GL gl = GLContext.getCurrentGL(); - Attribute attrib = boundShader.getAttribute(vb.getBufferType()); - int loc = attrib.getLocation(); - if (loc == -1) { - return; // not defined - } - if (loc == -2) { - stringBuf.setLength(0); - // JOGL 2.0 doesn't need a null terminated string - stringBuf.append("in").append(vb.getBufferType().name()); - loc = gl.getGL2ES2().glGetAttribLocation(programId, stringBuf.toString()); - - // not really the name of it in the shader (inPosition\0) but - // the internal name of the enum (Position). - if (loc < 0) { - attrib.setLocation(-1); - return; // not available in shader. - } else { - attrib.setLocation(loc); - } - } - - if (vb.isInstanced()) { - if (!gl.isExtensionAvailable("GL_ARB_instanced_arrays") - || !gl.isExtensionAvailable("GL_ARB_draw_instanced")) { - throw new RendererException("Instancing is required, " - + "but not supported by the " - + "graphics hardware"); - } - } - int slotsRequired = 1; - if (vb.getNumComponents() > 4) { - if (vb.getNumComponents() % 4 != 0) { - throw new RendererException("Number of components in multi-slot " - + "buffers must be divisible by 4"); - } - slotsRequired = vb.getNumComponents() / 4; - } - - if (vb.isUpdateNeeded() && idb == null) { - updateBufferData(vb); - } - - VertexBuffer[] attribs = context.boundAttribs; - for (int i = 0; i < slotsRequired; i++) { - if (!context.attribIndexList.moveToNew(loc + i)) { - gl.getGL2ES2().glEnableVertexAttribArray(loc + i); - //System.out.println("Enabled ATTRIB IDX: "+loc + i); - } - } - if (attribs[loc] != vb) { - // NOTE: Use id from interleaved buffer if specified - int bufId = idb != null ? idb.getId() : vb.getId(); - assert bufId != -1; - if (context.boundArrayVBO != bufId) { - gl.glBindBuffer(GL.GL_ARRAY_BUFFER, bufId); - context.boundArrayVBO = bufId; - //statistics.onVertexBufferUse(vb, true); - } else { - //statistics.onVertexBufferUse(vb, false); - } - - if (slotsRequired == 1) { - gl.getGL2ES2().glVertexAttribPointer(loc, - vb.getNumComponents(), - convertFormat(vb.getFormat()), - vb.isNormalized(), - vb.getStride(), - vb.getOffset()); - } else { - for (int i = 0; i < slotsRequired; i++) { - // The pointer maps the next 4 floats in the slot. - // E.g. - // P1: XXXX____________XXXX____________ - // P2: ____XXXX____________XXXX________ - // P3: ________XXXX____________XXXX____ - // P4: ____________XXXX____________XXXX - // stride = 4 bytes in float * 4 floats in slot * num slots - // offset = 4 bytes in float * 4 floats in slot * slot index - gl.getGL2ES2().glVertexAttribPointer(loc + i, - 4, - convertFormat(vb.getFormat()), - vb.isNormalized(), - 4 * 4 * slotsRequired, - 4 * 4 * i); - } - } - - for (int i = 0; i < slotsRequired; i++) { - int slot = loc + i; - if (vb.isInstanced() && (attribs[slot] == null || !attribs[slot].isInstanced())) { - // non-instanced -> instanced - gl.getGL3ES3().glVertexAttribDivisor(slot, vb.getInstanceSpan()); - } else if (!vb.isInstanced() && attribs[slot] != null && attribs[slot].isInstanced()) { - // instanced -> non-instanced - gl.getGL3ES3().glVertexAttribDivisor(slot, 0); - } - attribs[slot] = vb; - } - } - } else { - throw new IllegalStateException("Cannot render mesh without shader bound"); - } - } - - public void setVertexAttrib(VertexBuffer vb) { - setVertexAttrib(vb, null); - } - - public void drawTriangleArray(Mesh.Mode mode, int count, int vertCount) { - boolean useInstancing = count > 1 && caps.contains(Caps.MeshInstancing); - GL gl = GLContext.getCurrentGL(); - if (useInstancing) { - if (gl.isGL2GL3()) { - gl.getGL2GL3().glDrawArraysInstanced(convertElementMode(mode), 0, - vertCount, count); - } - } else { - gl.glDrawArrays(convertElementMode(mode), 0, vertCount); - } - } - - public void drawTriangleList(VertexBuffer indexBuf, Mesh mesh, int count) { - if (indexBuf.getBufferType() != VertexBuffer.Type.Index) { - throw new IllegalArgumentException("Only index buffers are allowed as triangle lists."); - } - - if (indexBuf.isUpdateNeeded()) { - updateBufferData(indexBuf); - } - - int bufId = indexBuf.getId(); - assert bufId != -1; - - GL gl = GLContext.getCurrentGL(); - - if (context.boundElementArrayVBO != bufId) { - gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, bufId); - context.boundElementArrayVBO = bufId; - //statistics.onVertexBufferUse(indexBuf, true); - } else { - //statistics.onVertexBufferUse(indexBuf, true); - } - - int vertCount = mesh.getVertexCount(); - boolean useInstancing = count > 1 && caps.contains(Caps.MeshInstancing); - - if (mesh.getMode() == Mode.Hybrid) { - int[] modeStart = mesh.getModeStart(); - int[] elementLengths = mesh.getElementLengths(); - - int elMode = convertElementMode(Mode.Triangles); - int fmt = convertFormat(indexBuf.getFormat()); - int elSize = indexBuf.getFormat().getComponentSize(); - int listStart = modeStart[0]; - int stripStart = modeStart[1]; - int fanStart = modeStart[2]; - int curOffset = 0; - for (int i = 0; i < elementLengths.length; i++) { - if (i == stripStart) { - elMode = convertElementMode(Mode.TriangleStrip); - } else if (i == fanStart) { - elMode = convertElementMode(Mode.TriangleStrip); - } - int elementLength = elementLengths[i]; - - if (useInstancing) { - if (gl.isGL2()) { - indexBuf.getData().position(curOffset); - indexBuf.getData().limit(curOffset + elementLength); - - gl.getGL2().glDrawElementsInstanced(elMode, - elementLength, - fmt, - indexBuf.getData(), - count); - } else { - throw new IllegalArgumentException( - "instancing is not supported."); - } - } else { - if (gl.isGL2GL3()) { - gl.getGL2GL3().glDrawRangeElements(elMode, - 0, - vertCount, - elementLength, - fmt, - curOffset); - } else { - indexBuf.getData().position(curOffset); - gl.getGL2().glDrawElements(elMode, elementLength, fmt, - indexBuf.getData()); - } - } - - curOffset += elementLength * elSize; - } - } else { - if (useInstancing) { - if (gl.isGL2()) { - gl.getGL2().glDrawElementsInstanced(convertElementMode(mesh.getMode()), - indexBuf.getData().limit(), - convertFormat(indexBuf.getFormat()), - indexBuf.getData(), - count); - } else { - throw new IllegalArgumentException( - "instancing is not supported."); - } - } else { - if (gl.isGL2GL3()) { - gl.getGL2GL3().glDrawRangeElements(convertElementMode(mesh.getMode()), - 0, - vertCount, - indexBuf.getData().limit(), - convertFormat(indexBuf.getFormat()), - 0); - } else { - indexBuf.getData().rewind(); - gl.glDrawElements(convertElementMode(mesh.getMode()), - indexBuf.getData().limit(), - convertFormat(indexBuf.getFormat()), 0); - } - } - } - } - - /** - * *******************************************************************\ |* - * Render Calls *| - \******************************************************************** - */ - private int convertElementMode(Mesh.Mode mode) { - switch (mode) { - case Points: - return GL.GL_POINTS; - case Lines: - return GL.GL_LINES; - case LineLoop: - return GL.GL_LINE_LOOP; - case LineStrip: - return GL.GL_LINE_STRIP; - case Triangles: - return GL.GL_TRIANGLES; - case TriangleFan: - return GL.GL_TRIANGLE_FAN; - case TriangleStrip: - return GL.GL_TRIANGLE_STRIP; - default: - throw new UnsupportedOperationException("Unrecognized mesh mode: " + mode); - } - } - - public void updateVertexArray(Mesh mesh, VertexBuffer instanceData) { - int id = mesh.getId(); - GL gl = GLContext.getCurrentGL(); - - if (id == -1) { - IntBuffer temp = intBuf1; - if (gl.isGL2GL3()) { - gl.getGL2GL3().glGenVertexArrays(1, temp); - } - id = temp.get(0); - mesh.setId(id); - } - - if (context.boundVertexArray != id) { - if (gl.isGL2GL3()) { - gl.getGL2GL3().glBindVertexArray(id); - } - context.boundVertexArray = id; - } - - VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData); - if (interleavedData != null && interleavedData.isUpdateNeeded()) { - updateBufferData(interleavedData); - } - - if (instanceData != null) { - setVertexAttrib(instanceData, null); - } - - for (VertexBuffer vb : mesh.getBufferList().getArray()) { - if (vb.getBufferType() == Type.InterleavedData - || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers - || vb.getBufferType() == Type.Index) { - continue; - } - - if (vb.getStride() == 0) { - // not interleaved - setVertexAttrib(vb); - } else { - // interleaved - setVertexAttrib(vb, interleavedData); - } - } - } - - private void renderMeshVertexArray(Mesh mesh, int lod, int count, VertexBuffer instanceData) { - if (mesh.getId() == -1) { - updateVertexArray(mesh, instanceData); - } else { - // TODO: Check if it was updated - } - - if (context.boundVertexArray != mesh.getId()) { - GL gl = GLContext.getCurrentGL(); - if (gl.isGL2GL3()) { - gl.getGL2GL3().glBindVertexArray(mesh.getId()); - } - context.boundVertexArray = mesh.getId(); - } - -// IntMap buffers = mesh.getBuffers(); - VertexBuffer indices; - if (mesh.getNumLodLevels() > 0) { - indices = mesh.getLodLevel(lod); - } else { - indices = mesh.getBuffer(Type.Index); - } - if (indices != null) { - drawTriangleList(indices, mesh, count); - } else { - drawTriangleArray(mesh.getMode(), count, mesh.getVertexCount()); - } - clearVertexAttribs(); - clearTextureUnits(); - } - - private void renderMeshDefault(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) { - - // Here while count is still passed in. Can be removed when/if - // the method is collapsed again. -pspeed - count = Math.max(mesh.getInstanceCount(), count); - - VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData); - if (interleavedData != null && interleavedData.isUpdateNeeded()) { - updateBufferData(interleavedData); - } - - VertexBuffer indices; - if (mesh.getNumLodLevels() > 0) { - indices = mesh.getLodLevel(lod); - } else { - indices = mesh.getBuffer(Type.Index); - } - - if (instanceData != null) { - for (VertexBuffer vb : instanceData) { - setVertexAttrib(vb, null); - } - } - - for (VertexBuffer vb : mesh.getBufferList().getArray()) { - if (vb.getBufferType() == Type.InterleavedData - || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers - || vb.getBufferType() == Type.Index) { - continue; - } - - if (vb.getStride() == 0) { - // not interleaved - setVertexAttrib(vb); - } else { - // interleaved - setVertexAttrib(vb, interleavedData); - } - } - - if (indices != null) { - drawTriangleList(indices, mesh, count); - } else { - drawTriangleArray(mesh.getMode(), count, mesh.getVertexCount()); - } - clearVertexAttribs(); - clearTextureUnits(); - } - - @Override - public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) { - if (mesh.getVertexCount() == 0) { - return; - } - - GL gl = GLContext.getCurrentGL(); - if (context.pointSprite && mesh.getMode() != Mode.Points) { - // XXX: Hack, disable point sprite mode if mesh not in point mode - if (context.boundTextures[0] != null) { - if (context.boundTextureUnit != 0) { - gl.glActiveTexture(GL.GL_TEXTURE0); - context.boundTextureUnit = 0; - } - if (gl.isGL2ES1()) { - gl.glDisable(GL2ES1.GL_POINT_SPRITE); - } - if (gl.isGL2GL3()) { - gl.glDisable(GL2GL3.GL_VERTEX_PROGRAM_POINT_SIZE); - } - context.pointSprite = false; - } - } - - if (context.pointSize != mesh.getPointSize()) { - if (gl.isGL2GL3()) { - gl.getGL2GL3().glPointSize(mesh.getPointSize()); - } - context.pointSize = mesh.getPointSize(); - } - if (context.lineWidth != mesh.getLineWidth()) { - gl.glLineWidth(mesh.getLineWidth()); - context.lineWidth = mesh.getLineWidth(); - } - - statistics.onMeshDrawn(mesh, lod); -// if (gl.isExtensionAvailable("GL_ARB_vertex_array_object")){ -// renderMeshVertexArray(mesh, lod, count); -// }else{ - renderMeshDefault(mesh, lod, count, instanceData); -// } - } - - @Override - public void setMainFrameBufferSrgb(boolean srgb) { - //Gamma correction - if(srgb && caps.contains(Caps.Srgb)){ - GLContext.getCurrentGL().glEnable(GL3.GL_FRAMEBUFFER_SRGB); - logger.log(Level.FINER, "SRGB FrameBuffer enabled (Gamma Correction)"); - }else{ - GLContext.getCurrentGL().glDisable(GL3.GL_FRAMEBUFFER_SRGB); - } - - } - - @Override - public void setLinearizeSrgbImages(boolean linearize) { - linearizeSrgbImages = linearize; - } - - public void readFrameBufferWithFormat(FrameBuffer fb, ByteBuffer byteBuf, Image.Format format) { - throw new UnsupportedOperationException("Not supported yet. URA will make that work seamlessly"); - } -} diff --git a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglAbstractDisplay.java b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglAbstractDisplay.java index 2248e4b22..3b468f1aa 100644 --- a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglAbstractDisplay.java +++ b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglAbstractDisplay.java @@ -37,7 +37,7 @@ import com.jme3.input.MouseInput; import com.jme3.input.TouchInput; import com.jme3.input.awt.AwtKeyInput; import com.jme3.input.awt.AwtMouseInput; -import com.jme3.renderer.jogl.JoglRenderer; +import com.jme3.system.AppSettings; import com.jogamp.opengl.util.Animator; import com.jogamp.opengl.util.AnimatorBase; import com.jogamp.opengl.util.FPSAnimator; @@ -47,15 +47,6 @@ import java.awt.GraphicsEnvironment; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; -import com.jogamp.opengl.DebugGL2; -import com.jogamp.opengl.DebugGL3; -import com.jogamp.opengl.DebugGL3bc; -import com.jogamp.opengl.DebugGL4; -import com.jogamp.opengl.DebugGL4bc; -import com.jogamp.opengl.DebugGLES1; -import com.jogamp.opengl.DebugGLES2; -import com.jogamp.opengl.DebugGLES3; -import com.jogamp.opengl.GL; import com.jogamp.opengl.GLAutoDrawable; import com.jogamp.opengl.GLCapabilities; import com.jogamp.opengl.GLEventListener; @@ -90,9 +81,13 @@ public abstract class JoglAbstractDisplay extends JoglContext implements GLEvent device = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(); - //FIXME use the settings to know whether to use the max programmable profile - //then call GLProfile.getMaxProgrammable(true); - GLCapabilities caps = new GLCapabilities(GLProfile.getMaxFixedFunc(true)); + GLCapabilities caps; + if (settings.getRenderer().equals(AppSettings.JOGL_OPENGL_FORWARD_COMPATIBLE)) { + caps = new GLCapabilities(GLProfile.getMaxProgrammable(true)); + } else { + caps = new GLCapabilities(GLProfile.getMaxFixedFunc(true)); + } + caps.setHardwareAccelerated(true); caps.setDoubleBuffered(true); caps.setStencilBits(settings.getStencilBits()); @@ -127,57 +122,9 @@ public abstract class JoglAbstractDisplay extends JoglContext implements GLEvent canvas.setSize(settings.getWidth(), settings.getHeight()); canvas.setIgnoreRepaint(true); canvas.addGLEventListener(this); - - if (settings.getBoolean("GraphicsDebug")) { - canvas.invoke(false, new GLRunnable() { - public boolean run(GLAutoDrawable glad) { - GL gl = glad.getGL(); - if (gl.isGLES()) { - if (gl.isGLES1()) { - glad.setGL(new DebugGLES1(gl.getGLES1())); - } else { - if (gl.isGLES2()) { - glad.setGL(new DebugGLES2(gl.getGLES2())); - } else { - if (gl.isGLES3()) { - glad.setGL(new DebugGLES3(gl.getGLES3())); - } - } - } - } else { - if (gl.isGL4bc()) { - glad.setGL(new DebugGL4bc(gl.getGL4bc())); - } else { - if (gl.isGL4()) { - glad.setGL(new DebugGL4(gl.getGL4())); - } else { - if (gl.isGL3bc()) { - glad.setGL(new DebugGL3bc(gl.getGL3bc())); - } else { - if (gl.isGL3()) { - glad.setGL(new DebugGL3(gl.getGL3())); - } else { - if (gl.isGL2()) { - glad.setGL(new DebugGL2(gl.getGL2())); - } - } - } - } - } - } - return true; - } - }); - } - renderer = new JoglRenderer(); - - canvas.invoke(false, new GLRunnable() { - public boolean run(GLAutoDrawable glad) { - renderer.setMainFrameBufferSrgb(settings.getGammaCorrection()); - return true; - } - }); + //FIXME not sure it is the best place to do that + renderable.set(true); } protected void startGLCanvas() { @@ -192,9 +139,6 @@ public abstract class JoglAbstractDisplay extends JoglContext implements GLEvent animator.start(); wasAnimating = true; - - //FIXME not sure it is the best place to do that - renderable.set(true); } protected void onCanvasAdded() { diff --git a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglCanvas.java b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglCanvas.java index 9a409b85e..b56eb43c1 100644 --- a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglCanvas.java +++ b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglCanvas.java @@ -41,28 +41,34 @@ public class JoglCanvas extends JoglAbstractDisplay implements JmeCanvasContext private static final Logger logger = Logger.getLogger(JoglCanvas.class.getName()); private int width, height; + private boolean runningFirstTime = true; public JoglCanvas(){ super(); initGLCanvas(); } - public Type getType() { + @Override + public Type getType() { return Type.Canvas; } - public void setTitle(String title) { + @Override + public void setTitle(String title) { } - public void restart() { + @Override + public void restart() { } - public void create(boolean waitFor){ + @Override + public void create(boolean waitFor){ if (waitFor) waitFor(true); } - public void destroy(boolean waitFor){ + @Override + public void destroy(boolean waitFor){ if (waitFor) waitFor(false); if (animator.isAnimating()) @@ -81,13 +87,20 @@ public class JoglCanvas extends JoglAbstractDisplay implements JmeCanvasContext startGLCanvas(); } - public void init(GLAutoDrawable drawable) { + @Override + public void init(GLAutoDrawable drawable) { canvas.requestFocus(); super.internalCreate(); logger.fine("Display created."); - renderer.initialize(); + // At this point, the OpenGL context is active. + if (runningFirstTime){ + // THIS is the part that creates the renderer. + // It must always be called, now that we have the pbuffer workaround. + initContextFirstTime(); + runningFirstTime = false; + } listener.initialize(); } @@ -97,7 +110,8 @@ public class JoglCanvas extends JoglAbstractDisplay implements JmeCanvasContext super.startGLCanvas(); } - public void display(GLAutoDrawable glad) { + @Override + public void display(GLAutoDrawable glad) { if (!created.get() && renderer != null){ listener.destroy(); logger.fine("Canvas destroyed."); @@ -129,7 +143,8 @@ public class JoglCanvas extends JoglAbstractDisplay implements JmeCanvasContext } - public Canvas getCanvas() { + @Override + public Canvas getCanvas() { return canvas; } diff --git a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglContext.java b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglContext.java index 5d687af8d..497fa8edb 100644 --- a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglContext.java +++ b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglContext.java @@ -36,17 +36,32 @@ import com.jme3.input.JoyInput; import com.jme3.input.KeyInput; import com.jme3.input.MouseInput; import com.jme3.renderer.Renderer; -import com.jme3.renderer.jogl.JoglRenderer; +import com.jme3.renderer.RendererException; +import com.jme3.renderer.jogl.JoglGL; +import com.jme3.renderer.jogl.JoglGLExt; +import com.jme3.renderer.jogl.JoglGLFbo; +import com.jme3.renderer.opengl.GL2; +import com.jme3.renderer.opengl.GL3; +import com.jme3.renderer.opengl.GL4; +import com.jme3.renderer.opengl.GLDebugDesktop; +import com.jme3.renderer.opengl.GLExt; +import com.jme3.renderer.opengl.GLFbo; +import com.jme3.renderer.opengl.GLRenderer; +import com.jme3.renderer.opengl.GLTiming; +import com.jme3.renderer.opengl.GLTimingState; +import com.jme3.renderer.opengl.GLTracer; import com.jme3.system.AppSettings; import com.jme3.system.JmeContext; import com.jme3.system.NanoTimer; import com.jme3.system.NativeLibraryLoader; import com.jme3.system.SystemListener; import com.jme3.system.Timer; + import java.nio.IntBuffer; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; + import com.jogamp.opengl.GL; import com.jogamp.opengl.GL2GL3; import com.jogamp.opengl.GLContext; @@ -62,7 +77,7 @@ public abstract class JoglContext implements JmeContext { protected final Object createdLock = new Object(); protected AppSettings settings = new AppSettings(true); - protected JoglRenderer renderer; + protected Renderer renderer; protected Timer timer; protected SystemListener listener; @@ -77,43 +92,53 @@ public abstract class JoglContext implements JmeContext { } } - public void setSystemListener(SystemListener listener){ + @Override + public void setSystemListener(SystemListener listener){ this.listener = listener; } - public void setSettings(AppSettings settings) { + @Override + public void setSettings(AppSettings settings) { this.settings.copyFrom(settings); } - public boolean isRenderable(){ + @Override + public boolean isRenderable(){ return renderable.get(); } - public AppSettings getSettings() { + @Override + public AppSettings getSettings() { return settings; } - public Renderer getRenderer() { + @Override + public Renderer getRenderer() { return renderer; } - public MouseInput getMouseInput() { + @Override + public MouseInput getMouseInput() { return mouseInput; } - public KeyInput getKeyInput() { + @Override + public KeyInput getKeyInput() { return keyInput; } - public JoyInput getJoyInput() { + @Override + public JoyInput getJoyInput() { return joyInput; } - public Timer getTimer() { + @Override + public Timer getTimer() { return timer; } - public boolean isCreated() { + @Override + public boolean isCreated() { return created.get(); } @@ -135,13 +160,76 @@ public abstract class JoglContext implements JmeContext { } } } + + protected void initContextFirstTime(){ + if (GLContext.getCurrent().getGLVersionNumber().getMajor() < 2) { + throw new RendererException("OpenGL 2.0 or higher is " + + "required for jMonkeyEngine"); + } + + if (settings.getRenderer().startsWith("JOGL")) { + com.jme3.renderer.opengl.GL gl = new JoglGL(); + GLExt glext = new JoglGLExt(); + GLFbo glfbo = new JoglGLFbo(); + + if (settings.getBoolean("GraphicsDebug")) { + gl = new GLDebugDesktop(gl, glext, glfbo); + glext = (GLExt) gl; + glfbo = (GLFbo) gl; + } + + if (settings.getBoolean("GraphicsTiming")) { + GLTimingState timingState = new GLTimingState(); + gl = (com.jme3.renderer.opengl.GL) GLTiming.createGLTiming(gl, timingState, GL.class, GL2.class, GL3.class, GL4.class); + glext = (GLExt) GLTiming.createGLTiming(glext, timingState, GLExt.class); + glfbo = (GLFbo) GLTiming.createGLTiming(glfbo, timingState, GLFbo.class); + } + + if (settings.getBoolean("GraphicsTrace")) { + gl = (com.jme3.renderer.opengl.GL) GLTracer.createDesktopGlTracer(gl, GL.class, GL2.class, GL3.class, GL4.class); + glext = (GLExt) GLTracer.createDesktopGlTracer(glext, GLExt.class); + glfbo = (GLFbo) GLTracer.createDesktopGlTracer(glfbo, GLFbo.class); + } + + renderer = new GLRenderer(gl, glext, glfbo); + renderer.initialize(); + } else { + throw new UnsupportedOperationException("Unsupported renderer: " + settings.getRenderer()); + } + + if (GLContext.getCurrentGL().isExtensionAvailable("GL_ARB_debug_output") && settings.getBoolean("GraphicsDebug")) { + GLContext.getCurrent().enableGLDebugMessage(true); + GLContext.getCurrent().addGLDebugListener(new JoglGLDebugOutputHandler()); + } + + renderer.setMainFrameBufferSrgb(settings.getGammaCorrection()); + renderer.setLinearizeSrgbImages(settings.getGammaCorrection()); - public void internalCreate() { + // Init input + if (keyInput != null) { + keyInput.initialize(); + } + + if (mouseInput != null) { + mouseInput.initialize(); + } + + if (joyInput != null) { + joyInput.initialize(); + } + } + + public void internalCreate() { timer = new NanoTimer(); synchronized (createdLock){ created.set(true); createdLock.notifyAll(); } + if (renderable.get()){ + initContextFirstTime(); + } else { + assert getType() == Type.Canvas; + } } protected void internalDestroy() { diff --git a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglGLDebugOutputHandler.java b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglGLDebugOutputHandler.java new file mode 100644 index 000000000..5a8b58715 --- /dev/null +++ b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglGLDebugOutputHandler.java @@ -0,0 +1,80 @@ +/* + * 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.jogl; + +import java.util.HashMap; + +import com.jogamp.opengl.GL2ES2; +import com.jogamp.opengl.GLDebugListener; +import com.jogamp.opengl.GLDebugMessage; + +class JoglGLDebugOutputHandler implements GLDebugListener { + + private static final HashMap constMap = new HashMap(); + private static final String MESSAGE_FORMAT = + "[JME3] OpenGL debug message\r\n" + + " ID: %d\r\n" + + " Source: %s\r\n" + + " Type: %s\r\n" + + " Severity: %s\r\n" + + " Message: %s"; + + static { + constMap.put(GL2ES2.GL_DEBUG_SOURCE_API, "API"); + constMap.put(GL2ES2.GL_DEBUG_SOURCE_APPLICATION, "APPLICATION"); + constMap.put(GL2ES2.GL_DEBUG_SOURCE_OTHER, "OTHER"); + constMap.put(GL2ES2.GL_DEBUG_SOURCE_SHADER_COMPILER, "SHADER_COMPILER"); + constMap.put(GL2ES2.GL_DEBUG_SOURCE_THIRD_PARTY, "THIRD_PARTY"); + constMap.put(GL2ES2.GL_DEBUG_SOURCE_WINDOW_SYSTEM, "WINDOW_SYSTEM"); + + constMap.put(GL2ES2.GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR, "DEPRECATED_BEHAVIOR"); + constMap.put(GL2ES2.GL_DEBUG_TYPE_ERROR, "ERROR"); + constMap.put(GL2ES2.GL_DEBUG_TYPE_OTHER, "OTHER"); + constMap.put(GL2ES2.GL_DEBUG_TYPE_PERFORMANCE, "PERFORMANCE"); + constMap.put(GL2ES2.GL_DEBUG_TYPE_PORTABILITY, "PORTABILITY"); + constMap.put(GL2ES2.GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR, "UNDEFINED_BEHAVIOR"); + + constMap.put(GL2ES2.GL_DEBUG_SEVERITY_HIGH, "HIGH"); + constMap.put(GL2ES2.GL_DEBUG_SEVERITY_MEDIUM, "MEDIUM"); + constMap.put(GL2ES2.GL_DEBUG_SEVERITY_LOW, "LOW"); + } + + @Override + public void messageSent(GLDebugMessage event) { + String sourceStr = constMap.get(event.getDbgSource()); + String typeStr = constMap.get(event.getDbgType()); + String severityStr = constMap.get(event.getDbgSeverity()); + + System.err.println(String.format(MESSAGE_FORMAT, event.getDbgId(), sourceStr, typeStr, severityStr, event.getDbgMsg())); + } + +} diff --git a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglNewtAbstractDisplay.java b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglNewtAbstractDisplay.java index f05c92b95..9c7a49d17 100644 --- a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglNewtAbstractDisplay.java +++ b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglNewtAbstractDisplay.java @@ -37,7 +37,7 @@ import com.jme3.input.MouseInput; import com.jme3.input.TouchInput; import com.jme3.input.jogl.NewtKeyInput; import com.jme3.input.jogl.NewtMouseInput; -import com.jme3.renderer.jogl.JoglRenderer; +import com.jme3.system.AppSettings; import com.jogamp.newt.opengl.GLWindow; import com.jogamp.opengl.util.Animator; import com.jogamp.opengl.util.AnimatorBase; @@ -46,15 +46,6 @@ import com.jogamp.opengl.util.FPSAnimator; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; -import com.jogamp.opengl.DebugGL2; -import com.jogamp.opengl.DebugGL3; -import com.jogamp.opengl.DebugGL3bc; -import com.jogamp.opengl.DebugGL4; -import com.jogamp.opengl.DebugGL4bc; -import com.jogamp.opengl.DebugGLES1; -import com.jogamp.opengl.DebugGLES2; -import com.jogamp.opengl.DebugGLES3; -import com.jogamp.opengl.GL; import com.jogamp.opengl.GLAutoDrawable; import com.jogamp.opengl.GLCapabilities; import com.jogamp.opengl.GLEventListener; @@ -83,10 +74,12 @@ public abstract class JoglNewtAbstractDisplay extends JoglContext implements GLE protected void initGLCanvas() { loadNatives(); - //FIXME use the settings to know whether to use the max programmable profile - //then call GLProfile.getMaxProgrammable(true); - //FIXME use the default profile only on embedded devices - GLCapabilities caps = new GLCapabilities(GLProfile.getDefault()); + GLCapabilities caps; + if (settings.getRenderer().equals(AppSettings.JOGL_OPENGL_FORWARD_COMPATIBLE)) { + caps = new GLCapabilities(GLProfile.getMaxProgrammable(true)); + } else { + caps = new GLCapabilities(GLProfile.getMaxFixedFunc(true)); + } caps.setHardwareAccelerated(true); caps.setDoubleBuffered(true); caps.setStencilBits(settings.getStencilBits()); @@ -106,57 +99,9 @@ public abstract class JoglNewtAbstractDisplay extends JoglContext implements GLE canvas.requestFocus(); canvas.setSize(settings.getWidth(), settings.getHeight()); canvas.addGLEventListener(this); - - if (settings.getBoolean("GraphicsDebug")) { - canvas.invoke(false, new GLRunnable() { - public boolean run(GLAutoDrawable glad) { - GL gl = glad.getGL(); - if (gl.isGLES()) { - if (gl.isGLES1()) { - glad.setGL(new DebugGLES1(gl.getGLES1())); - } else { - if (gl.isGLES2()) { - glad.setGL(new DebugGLES2(gl.getGLES2())); - } else { - if (gl.isGLES3()) { - glad.setGL(new DebugGLES3(gl.getGLES3())); - } - } - } - } else { - if (gl.isGL4bc()) { - glad.setGL(new DebugGL4bc(gl.getGL4bc())); - } else { - if (gl.isGL4()) { - glad.setGL(new DebugGL4(gl.getGL4())); - } else { - if (gl.isGL3bc()) { - glad.setGL(new DebugGL3bc(gl.getGL3bc())); - } else { - if (gl.isGL3()) { - glad.setGL(new DebugGL3(gl.getGL3())); - } else { - if (gl.isGL2()) { - glad.setGL(new DebugGL2(gl.getGL2())); - } - } - } - } - } - } - return true; - } - }); - } - - renderer = new JoglRenderer(); - canvas.invoke(false, new GLRunnable() { - public boolean run(GLAutoDrawable glad) { - renderer.setMainFrameBufferSrgb(settings.getGammaCorrection()); - return true; - } - }); + //FIXME not sure it is the best place to do that + renderable.set(true); } protected void startGLCanvas() { @@ -171,9 +116,6 @@ public abstract class JoglNewtAbstractDisplay extends JoglContext implements GLE animator.start(); wasAnimating = true; - - //FIXME not sure it is the best place to do that - renderable.set(true); } protected void onCanvasAdded() { diff --git a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglNewtCanvas.java b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglNewtCanvas.java index 3ed501580..e4e81a5df 100644 --- a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglNewtCanvas.java +++ b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglNewtCanvas.java @@ -41,6 +41,7 @@ public class JoglNewtCanvas extends JoglNewtAbstractDisplay implements JmeCanvas private static final Logger logger = Logger.getLogger(JoglNewtCanvas.class.getName()); private int width, height; + private boolean runningFirstTime = true; private NewtCanvasAWT newtAwtCanvas; @@ -53,7 +54,9 @@ public class JoglNewtCanvas extends JoglNewtAbstractDisplay implements JmeCanvas protected final void initGLCanvas() { super.initGLCanvas(); newtAwtCanvas = new NewtCanvasAWT(canvas) { - @Override + private static final long serialVersionUID = 1L; + + @Override public void addNotify() { super.addNotify(); onCanvasAdded(); @@ -67,22 +70,27 @@ public class JoglNewtCanvas extends JoglNewtAbstractDisplay implements JmeCanvas }; } - public Type getType() { + @Override + public Type getType() { return Type.Canvas; } - public void setTitle(String title) { + @Override + public void setTitle(String title) { } - public void restart() { + @Override + public void restart() { } - public void create(boolean waitFor){ + @Override + public void create(boolean waitFor){ if (waitFor) waitFor(true); } - public void destroy(boolean waitFor){ + @Override + public void destroy(boolean waitFor){ if (waitFor) waitFor(false); if (animator.isAnimating()) @@ -101,13 +109,20 @@ public class JoglNewtCanvas extends JoglNewtAbstractDisplay implements JmeCanvas startGLCanvas(); } - public void init(GLAutoDrawable drawable) { + @Override + public void init(GLAutoDrawable drawable) { canvas.requestFocus(); super.internalCreate(); logger.fine("Display created."); - renderer.initialize(); + // At this point, the OpenGL context is active. + if (runningFirstTime){ + // THIS is the part that creates the renderer. + // It must always be called, now that we have the pbuffer workaround. + initContextFirstTime(); + runningFirstTime = false; + } listener.initialize(); } @@ -117,7 +132,8 @@ public class JoglNewtCanvas extends JoglNewtAbstractDisplay implements JmeCanvas super.startGLCanvas(); } - public void display(GLAutoDrawable glad) { + @Override + public void display(GLAutoDrawable glad) { if (!created.get() && renderer != null){ listener.destroy(); logger.fine("Canvas destroyed."); diff --git a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglNewtDisplay.java b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglNewtDisplay.java index 84a99e8d2..56eab406a 100644 --- a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglNewtDisplay.java +++ b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglNewtDisplay.java @@ -173,6 +173,8 @@ public class JoglNewtDisplay extends JoglNewtAbstractDisplay { if (waitFor){ waitFor(false); } + if (animator.isAnimating()) + animator.stop(); } public void restart() { diff --git a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglOffscreenBuffer.java b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglOffscreenBuffer.java index cd2b84f73..56aa2d53f 100644 --- a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglOffscreenBuffer.java +++ b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglOffscreenBuffer.java @@ -37,16 +37,18 @@ import com.jme3.input.MouseInput; import com.jme3.input.TouchInput; import com.jme3.input.dummy.DummyKeyInput; import com.jme3.input.dummy.DummyMouseInput; -import com.jogamp.newt.NewtVersion; + import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; + import com.jogamp.opengl.GL; import com.jogamp.opengl.GLCapabilities; import com.jogamp.opengl.GLContext; import com.jogamp.opengl.GLDrawableFactory; import com.jogamp.opengl.GLOffscreenAutoDrawable; import com.jogamp.opengl.GLProfile; +import com.jogamp.opengl.JoglVersion; public class JoglOffscreenBuffer extends JoglContext implements Runnable { @@ -123,7 +125,7 @@ public class JoglOffscreenBuffer extends JoglContext implements Runnable { @Override public void run(){ loadNatives(); - logger.log(Level.FINE, "Using JOGL {0}", NewtVersion.getInstance().getImplementationVersion()); + logger.log(Level.FINE, "Using JOGL {0}", JoglVersion.getInstance().getImplementationVersion()); initInThread(); while (!needClose.get()){ runLoop(); diff --git a/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java b/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java index bf99c84eb..2b7df131a 100644 --- a/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java +++ b/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java @@ -13,7 +13,7 @@ import java.nio.ShortBuffer; import com.jme3.renderer.opengl.GL4; import org.lwjgl.opengl.*; -public class LwjglGL implements GL, GL2, GL3, GL4 { +public final class LwjglGL implements GL, GL2, GL3, GL4 { private static void checkLimit(Buffer buffer) { if (buffer == null) { diff --git a/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGLExt.java b/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGLExt.java index 2c6a63fd7..e3b9e6c77 100644 --- a/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGLExt.java +++ b/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGLExt.java @@ -13,7 +13,7 @@ import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL20; import org.lwjgl.opengl.GLSync; -public class LwjglGLExt implements GLExt { +public final class LwjglGLExt implements GLExt { private static void checkLimit(Buffer buffer) { if (buffer == null) { diff --git a/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGLFboEXT.java b/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGLFboEXT.java index 159000a6c..40571f5ed 100644 --- a/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGLFboEXT.java +++ b/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGLFboEXT.java @@ -13,7 +13,7 @@ import org.lwjgl.opengl.EXTFramebufferObject; * * @author Kirill Vainer */ -public class LwjglGLFboEXT implements GLFbo { +public final class LwjglGLFboEXT implements GLFbo { private static void checkLimit(Buffer buffer) { if (buffer == null) { diff --git a/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGLFboGL3.java b/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGLFboGL3.java index acc540273..14e0cc9e6 100644 --- a/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGLFboGL3.java +++ b/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGLFboGL3.java @@ -11,7 +11,7 @@ import org.lwjgl.opengl.GL30; * * @author Kirill Vainer */ -public class LwjglGLFboGL3 implements GLFbo { +public final class LwjglGLFboGL3 implements GLFbo { private static void checkLimit(Buffer buffer) { if (buffer == null) { diff --git a/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglAbstractDisplay.java b/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglAbstractDisplay.java index 7cdca0a57..802b12a0f 100644 --- a/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglAbstractDisplay.java +++ b/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglAbstractDisplay.java @@ -40,7 +40,6 @@ import com.jme3.input.lwjgl.JInputJoyInput; import com.jme3.input.lwjgl.LwjglKeyInput; import com.jme3.input.lwjgl.LwjglMouseInput; import com.jme3.system.AppSettings; -import com.jme3.system.JmeContext.Type; import com.jme3.system.JmeSystem; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; diff --git a/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java b/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java index c88f7b734..ee12d1fea 100644 --- a/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java +++ b/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java @@ -52,12 +52,8 @@ import com.jme3.renderer.opengl.GLRenderer; import com.jme3.renderer.opengl.GLTiming; import com.jme3.renderer.opengl.GLTimingState; import com.jme3.renderer.opengl.GLTracer; -import com.jme3.system.AppSettings; -import com.jme3.system.JmeContext; -import com.jme3.system.JmeSystem; -import com.jme3.system.NativeLibraryLoader; -import com.jme3.system.SystemListener; -import com.jme3.system.Timer; +import com.jme3.system.*; + import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; @@ -166,8 +162,7 @@ public abstract class LwjglContext implements JmeContext { } } } - - protected void loadNatives() { + protected void loadNatives() { if (JmeSystem.isLowPermissions()) { return; } diff --git a/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglGLDebugOutputHandler.java b/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglGLDebugOutputHandler.java index c8f329f13..d943fe6ee 100644 --- a/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglGLDebugOutputHandler.java +++ b/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglGLDebugOutputHandler.java @@ -73,6 +73,7 @@ class LwjglGLDebugOutputHandler implements ARBDebugOutputCallback.Handler { String severityStr = constMap.get(severity); System.err.println(String.format(MESSAGE_FORMAT, id, sourceStr, typeStr, severityStr, message)); + Thread.dumpStack(); } } diff --git a/jme3-lwjgl3/build.gradle b/jme3-lwjgl3/build.gradle new file mode 100644 index 000000000..3ee3b0f65 --- /dev/null +++ b/jme3-lwjgl3/build.gradle @@ -0,0 +1,15 @@ +if (!hasProperty('mainClass')) { + ext.mainClass = '' +} + +repositories { + maven { + url "https://oss.sonatype.org/content/repositories/snapshots" + } +} + +dependencies { + compile project(':jme3-core') + compile project(':jme3-desktop') + compile files('lib/lwjgl-3.0.0b-35.jar', 'lib/lwjgl-3.0.0b-35-natives.jar') +} diff --git a/jme3-lwjgl3/lib/lwjgl-3.0.0b-35-natives.jar b/jme3-lwjgl3/lib/lwjgl-3.0.0b-35-natives.jar new file mode 100644 index 000000000..79b12bc5d Binary files /dev/null and b/jme3-lwjgl3/lib/lwjgl-3.0.0b-35-natives.jar differ diff --git a/jme3-lwjgl3/lib/lwjgl-3.0.0b-35.jar b/jme3-lwjgl3/lib/lwjgl-3.0.0b-35.jar new file mode 100644 index 000000000..d1466e8aa Binary files /dev/null and b/jme3-lwjgl3/lib/lwjgl-3.0.0b-35.jar differ diff --git a/jme3-lwjgl3/src/main/java/com/jme3/audio/lwjgl/LwjglAL.java b/jme3-lwjgl3/src/main/java/com/jme3/audio/lwjgl/LwjglAL.java new file mode 100644 index 000000000..313bdd5ee --- /dev/null +++ b/jme3-lwjgl3/src/main/java/com/jme3/audio/lwjgl/LwjglAL.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2009-2012 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */package com.jme3.audio.lwjgl; + +import com.jme3.audio.openal.AL; +import com.jme3.system.NativeLibraryLoader; +import com.jme3.system.Platform; +import org.lwjgl.openal.AL10; +import org.lwjgl.openal.AL11; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; + +public final class LwjglAL implements AL { + + public LwjglAL() { + } + + public String alGetString(int parameter) { + return AL10.alGetString(parameter); + } + + public int alGenSources() { + return AL10.alGenSources(); + } + + public int alGetError() { + return AL10.alGetError(); + } + + public void alDeleteSources(int numSources, IntBuffer sources) { + if (sources.position() != 0) throw new AssertionError(); + if (sources.limit() != numSources) throw new AssertionError(); + AL10.alDeleteSources(sources); + } + + public void alGenBuffers(int numBuffers, IntBuffer buffers) { + if (buffers.position() != 0) throw new AssertionError(); + if (buffers.limit() != numBuffers) throw new AssertionError(); + AL10.alGenBuffers(buffers); + } + + public void alDeleteBuffers(int numBuffers, IntBuffer buffers) { + if (buffers.position() != 0) throw new AssertionError(); + if (buffers.limit() != numBuffers) throw new AssertionError(); + AL10.alDeleteBuffers(buffers); + } + + public void alSourceStop(int source) { + AL10.alSourceStop(source); + } + + public void alSourcei(int source, int param, int value) { + AL10.alSourcei(source, param, value); + } + + public void alBufferData(int buffer, int format, ByteBuffer data, int size, int frequency) { + if (data.position() != 0) throw new AssertionError(); + if (data.limit() != size) throw new AssertionError(); + AL10.alBufferData(buffer, format, data, frequency); + } + + public void alSourcePlay(int source) { + AL10.alSourcePlay(source); + } + + public void alSourcePause(int source) { + AL10.alSourcePause(source); + } + + public void alSourcef(int source, int param, float value) { + AL10.alSourcef(source, param, value); + } + + public void alSource3f(int source, int param, float value1, float value2, float value3) { + AL10.alSource3f(source, param, value1, value2, value3); + } + + public int alGetSourcei(int source, int param) { + return AL10.alGetSourcei(source, param); + } + + public void alSourceUnqueueBuffers(int source, int numBuffers, IntBuffer buffers) { + if (buffers.position() != 0) throw new AssertionError(); + if (buffers.limit() != numBuffers) throw new AssertionError(); + AL10.alSourceUnqueueBuffers(source, buffers); + } + + public void alSourceQueueBuffers(int source, int numBuffers, IntBuffer buffers) { + if (buffers.position() != 0) throw new AssertionError(); + if (buffers.limit() != numBuffers) throw new AssertionError(); + AL10.alSourceQueueBuffers(source, buffers); + } + + public void alListener(int param, FloatBuffer data) { + AL10.alListenerfv(param, data); + } + + public void alListenerf(int param, float value) { + AL10.alListenerf(param, value); + } + + public void alListener3f(int param, float value1, float value2, float value3) { + AL10.alListener3f(param, value1, value2, value3); + } + + public void alSource3i(int source, int param, int value1, int value2, int value3) { + AL11.alSource3i(source, param, value1, value2, value3); + } + +} diff --git a/jme3-lwjgl3/src/main/java/com/jme3/audio/lwjgl/LwjglALC.java b/jme3-lwjgl3/src/main/java/com/jme3/audio/lwjgl/LwjglALC.java new file mode 100644 index 000000000..b99a939b8 --- /dev/null +++ b/jme3-lwjgl3/src/main/java/com/jme3/audio/lwjgl/LwjglALC.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2009-2012 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.audio.lwjgl; + +import com.jme3.audio.openal.ALC; +import org.lwjgl.openal.ALC10; +import org.lwjgl.openal.ALContext; +import org.lwjgl.openal.ALDevice; + +import java.nio.IntBuffer; + +import static org.lwjgl.openal.ALC10.alcGetContextsDevice; +import static org.lwjgl.openal.ALC10.alcGetCurrentContext; + +public class LwjglALC implements ALC { + + private ALDevice device; + private ALContext context; + + public void createALC() { + device = ALDevice.create(); + context = ALContext.create(device); + } + + public void destroyALC() { + if (context != null) { + context.destroy(); + } + + if (device != null) { + device.destroy(); + } + } + + public boolean isCreated() { + return context != null; + } + + public String alcGetString(final int parameter) { + final long context = alcGetCurrentContext(); + final long device = alcGetContextsDevice(context); + return ALC10.alcGetString(device, parameter); + } + + public boolean alcIsExtensionPresent(final String extension) { + final long context = alcGetCurrentContext(); + final long device = alcGetContextsDevice(context); + return ALC10.alcIsExtensionPresent(device, extension); + } + + public void alcGetInteger(final int param, final IntBuffer buffer, final int size) { + if (buffer.position() != 0) throw new AssertionError(); + if (buffer.limit() != size) throw new AssertionError(); + + final long context = alcGetCurrentContext(); + final long device = alcGetContextsDevice(context); + final int value = ALC10.alcGetInteger(device, param); + //buffer.put(value); + } + + public void alcDevicePauseSOFT() { + } + + public void alcDeviceResumeSOFT() { + } + +} diff --git a/jme3-lwjgl3/src/main/java/com/jme3/audio/lwjgl/LwjglEFX.java b/jme3-lwjgl3/src/main/java/com/jme3/audio/lwjgl/LwjglEFX.java new file mode 100644 index 000000000..ecd67211f --- /dev/null +++ b/jme3-lwjgl3/src/main/java/com/jme3/audio/lwjgl/LwjglEFX.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2009-2012 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.audio.lwjgl; + +import com.jme3.audio.openal.EFX; +import org.lwjgl.openal.EXTEfx; + +import java.nio.IntBuffer; + +public class LwjglEFX implements EFX { + + public void alGenAuxiliaryEffectSlots(int numSlots, IntBuffer buffers) { + if (buffers.position() != 0) throw new AssertionError(); + if (buffers.limit() != numSlots) throw new AssertionError(); + EXTEfx.alGenAuxiliaryEffectSlots(buffers); + } + + public void alGenEffects(int numEffects, IntBuffer buffers) { + if (buffers.position() != 0) throw new AssertionError(); + if (buffers.limit() != numEffects) throw new AssertionError(); + EXTEfx.alGenEffects(buffers); + } + + public void alEffecti(int effect, int param, int value) { + EXTEfx.alEffecti(effect, param, value); + } + + public void alAuxiliaryEffectSloti(int effectSlot, int param, int value) { + EXTEfx.alAuxiliaryEffectSloti(effectSlot, param, value); + } + + public void alDeleteEffects(int numEffects, IntBuffer buffers) { + if (buffers.position() != 0) throw new AssertionError(); + if (buffers.limit() != numEffects) throw new AssertionError(); + EXTEfx.alDeleteEffects(buffers); + } + + public void alDeleteAuxiliaryEffectSlots(int numEffectSlots, IntBuffer buffers) { + if (buffers.position() != 0) throw new AssertionError(); + if (buffers.limit() != numEffectSlots) throw new AssertionError(); + EXTEfx.alDeleteAuxiliaryEffectSlots(buffers); + } + + public void alGenFilters(int numFilters, IntBuffer buffers) { + if (buffers.position() != 0) throw new AssertionError(); + if (buffers.limit() != numFilters) throw new AssertionError(); + EXTEfx.alGenFilters(buffers); + } + + public void alFilteri(int filter, int param, int value) { + EXTEfx.alFilteri(filter, param, value); + } + + public void alFilterf(int filter, int param, float value) { + EXTEfx.alFilterf(filter, param, value); + } + + public void alDeleteFilters(int numFilters, IntBuffer buffers) { + if (buffers.position() != 0) throw new AssertionError(); + if (buffers.limit() != numFilters) throw new AssertionError(); + EXTEfx.alDeleteFilters(buffers); + } + + public void alEffectf(int effect, int param, float value) { + EXTEfx.alEffectf(effect, param, value); + } + +} diff --git a/jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwJoystickInput.java b/jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwJoystickInput.java new file mode 100644 index 000000000..b1063d6b9 --- /dev/null +++ b/jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwJoystickInput.java @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2009-2012 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.input.lwjgl; + +import com.jme3.input.*; +import com.jme3.input.event.JoyAxisEvent; +import com.jme3.input.event.JoyButtonEvent; +import org.lwjgl.opengl.GL11; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Logger; + +import static org.lwjgl.glfw.GLFW.*; + +/** + * @author Daniel Johansson (dannyjo) + * @since 3.1 + */ +public class GlfwJoystickInput implements JoyInput { + + private static final Logger LOGGER = Logger.getLogger(InputManager.class.getName()); + + private boolean initialized = false; + private RawInputListener listener; + private Map joysticks = new HashMap(); + + public void setJoyRumble(int joyId, float amount) { + if (joyId >= joysticks.size()) { + throw new IllegalArgumentException(); + } + } + + @Override + public Joystick[] loadJoysticks(final InputManager inputManager) { + for (int i = 0; i < GLFW_JOYSTICK_LAST; i++) { + if (glfwJoystickPresent(i) == GL11.GL_TRUE) { + final String name = glfwGetJoystickName(i); + final GlfwJoystick joystick = new GlfwJoystick(inputManager, this, i, name); + joysticks.put(i, joystick); + + final FloatBuffer floatBuffer = glfwGetJoystickAxes(i); + + int axisIndex = 0; + while (floatBuffer.hasRemaining()) { + floatBuffer.get(); + + final String logicalId = JoystickCompatibilityMappings.remapComponent(joystick.getName(), convertAxisIndex(axisIndex)); + final JoystickAxis joystickAxis = new DefaultJoystickAxis(inputManager, joystick, axisIndex, convertAxisIndex(axisIndex), logicalId, true, false, 0.0f); + joystick.addAxis(axisIndex, joystickAxis); + axisIndex++; + } + + final ByteBuffer byteBuffer = glfwGetJoystickButtons(i); + + int buttonIndex = 0; + while (byteBuffer.hasRemaining()) { + byteBuffer.get(); + final String logicalId = JoystickCompatibilityMappings.remapComponent(joystick.getName(), String.valueOf(buttonIndex)); + joystick.addButton(new DefaultJoystickButton(inputManager, joystick, buttonIndex, String.valueOf(buttonIndex), logicalId)); + buttonIndex++; + } + } + } + + return joysticks.values().toArray(new GlfwJoystick[joysticks.size()]); + } + + private String convertAxisIndex(final int index) { + if (index == 0) { + return "pov_x"; + } else if (index == 1) { + return "pov_y"; + } else if (index == 2) { + return "z"; + } else if (index == 3) { + return "rz"; + } + + return String.valueOf(index); + } + + public void initialize() { + initialized = true; + } + + public void update() { + for (final Map.Entry entry : joysticks.entrySet()) { + // Axes + final FloatBuffer axisValues = glfwGetJoystickAxes(entry.getKey()); + + for (final JoystickAxis axis : entry.getValue().getAxes()) { + final float value = axisValues.get(axis.getAxisId()); + listener.onJoyAxisEvent(new JoyAxisEvent(axis, value)); + } + + // Buttons + final ByteBuffer byteBuffer = glfwGetJoystickButtons(entry.getKey()); + + for (final JoystickButton button : entry.getValue().getButtons()) { + final boolean pressed = byteBuffer.get(button.getButtonId()) == GLFW_PRESS; + listener.onJoyButtonEvent(new JoyButtonEvent(button, pressed)); + } + } + } + + public void destroy() { + initialized = false; + } + + public boolean isInitialized() { + return initialized; + } + + public void setInputListener(RawInputListener listener) { + this.listener = listener; + } + + public long getInputTimeNanos() { + return 0; + } + + protected class GlfwJoystick extends AbstractJoystick { + + private JoystickAxis povAxisX; + private JoystickAxis povAxisY; + + public GlfwJoystick(InputManager inputManager, JoyInput joyInput, int joyId, String name) { + super(inputManager, joyInput, joyId, name); + } + + public void addAxis(final int index, final JoystickAxis axis) { + super.addAxis(axis); + + if (index == 0) { + povAxisX = axis; + } else if (index == 1) { + povAxisY = axis; + } + } + + @Override + protected void addButton(JoystickButton button) { + super.addButton(button); + } + + @Override + public JoystickAxis getXAxis() { + return povAxisX; + } + + @Override + public JoystickAxis getYAxis() { + return povAxisY; + } + + @Override + public JoystickAxis getPovXAxis() { + return povAxisX; + } + + @Override + public JoystickAxis getPovYAxis() { + return povAxisY; + } + + @Override + public int getXAxisIndex() { + return povAxisX.getAxisId(); + } + + @Override + public int getYAxisIndex() { + return povAxisY.getAxisId(); + } + } +} + + + diff --git a/jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwKeyInput.java b/jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwKeyInput.java new file mode 100644 index 000000000..20cd07672 --- /dev/null +++ b/jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwKeyInput.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2009-2012 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.input.lwjgl; + +import com.jme3.input.KeyInput; +import com.jme3.input.RawInputListener; +import com.jme3.input.event.KeyInputEvent; +import com.jme3.system.lwjgl.LwjglWindow; +import org.lwjgl.glfw.GLFWKeyCallback; + +import java.util.LinkedList; +import java.util.Queue; +import java.util.logging.Logger; + +import static org.lwjgl.glfw.GLFW.*; + +public class GlfwKeyInput implements KeyInput { + + private static final Logger logger = Logger.getLogger(GlfwKeyInput.class.getName()); + + private LwjglWindow context; + private RawInputListener listener; + private boolean initialized; + private GLFWKeyCallback keyCallback; + private Queue keyInputEvents = new LinkedList(); + + public GlfwKeyInput(LwjglWindow context) { + this.context = context; + } + + public void initialize() { + if (!context.isRenderable()) { + return; + } + + glfwSetKeyCallback(context.getWindowHandle(), keyCallback = new GLFWKeyCallback() { + @Override + public void invoke(long window, int key, int scancode, int action, int mods) { + final KeyInputEvent evt = new KeyInputEvent(scancode, (char) key, GLFW_PRESS == action, GLFW_REPEAT == action); + evt.setTime(getInputTimeNanos()); + keyInputEvents.add(evt); + } + }); + + glfwSetInputMode(context.getWindowHandle(), GLFW_STICKY_KEYS, 1); + + initialized = true; + logger.fine("Keyboard created."); + } + + public int getKeyCount() { + // This might not be correct + return GLFW_KEY_LAST - GLFW_KEY_SPACE; + } + + public void update() { + if (!context.isRenderable()) { + return; + } + + while (!keyInputEvents.isEmpty()) { + listener.onKeyEvent(keyInputEvents.poll()); + } + } + + public void destroy() { + if (!context.isRenderable()) { + return; + } + + keyCallback.release(); + logger.fine("Keyboard destroyed."); + } + + public boolean isInitialized() { + return initialized; + } + + public void setInputListener(RawInputListener listener) { + this.listener = listener; + } + + public long getInputTimeNanos() { + return (long) (glfwGetTime() * 1000000000); + } +} diff --git a/jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwMouseInput.java b/jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwMouseInput.java new file mode 100644 index 000000000..05a2df5db --- /dev/null +++ b/jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwMouseInput.java @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2009-2012 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.input.lwjgl; + +import com.jme3.cursors.plugins.JmeCursor; +import com.jme3.input.MouseInput; +import com.jme3.input.RawInputListener; +import com.jme3.input.event.MouseButtonEvent; +import com.jme3.input.event.MouseMotionEvent; +import com.jme3.system.lwjgl.LwjglWindow; +import org.lwjgl.glfw.GLFWCursorPosCallback; +import org.lwjgl.glfw.GLFWMouseButtonCallback; +import org.lwjgl.glfw.GLFWScrollCallback; + +import java.nio.ByteBuffer; +import java.util.LinkedList; +import java.util.Queue; +import java.util.logging.Logger; + +import static org.lwjgl.glfw.GLFW.*; + +/** + * Captures mouse input using GLFW callbacks. It then temporarily stores these in event queues which are processed in the + * {@link #update()} method. Due to some of the GLFW button id's there is a conversion method in this class which will + * convert the GLFW left, middle and right mouse button to JME3 left, middle and right button codes. + * + * @author Daniel Johansson (dannyjo) + * @since 3.1 + */ +public class GlfwMouseInput implements MouseInput { + + private static final Logger logger = Logger.getLogger(GlfwMouseInput.class.getName()); + + private LwjglWindow context; + private RawInputListener listener; + private boolean cursorVisible = true; + private int mouseX; + private int mouseY; + private int mouseWheel; + private boolean initialized; + private GLFWCursorPosCallback cursorPosCallback; + private GLFWScrollCallback scrollCallback; + private GLFWMouseButtonCallback mouseButtonCallback; + private Queue mouseMotionEvents = new LinkedList(); + private Queue mouseButtonEvents = new LinkedList(); + + public GlfwMouseInput(final LwjglWindow context) { + this.context = context; + } + + public void initialize() { + glfwSetCursorPosCallback(context.getWindowHandle(), cursorPosCallback = new GLFWCursorPosCallback() { + @Override + public void invoke(long window, double xpos, double ypos) { + int xDelta; + int yDelta; + int x = (int) Math.round(xpos); + int y = context.getSettings().getHeight() - (int) Math.round(ypos); + + if (mouseX == 0) { + mouseX = x; + } + + if (mouseY == 0) { + mouseY = y; + } + + xDelta = x - mouseX; + yDelta = y - mouseY; + mouseX = x; + mouseY = y; + + if (xDelta != 0 || yDelta != 0) { + final MouseMotionEvent mouseMotionEvent = new MouseMotionEvent(x, y, xDelta, yDelta, mouseWheel, 0); + mouseMotionEvent.setTime(getInputTimeNanos()); + mouseMotionEvents.add(mouseMotionEvent); + } + } + }); + + glfwSetScrollCallback(context.getWindowHandle(), scrollCallback = new GLFWScrollCallback() { + @Override + public void invoke(final long window, final double xOffset, final double yOffset) { + mouseWheel += yOffset; + + final MouseMotionEvent mouseMotionEvent = new MouseMotionEvent(mouseX, mouseY, 0, 0, mouseWheel, (int) Math.round(yOffset)); + mouseMotionEvent.setTime(getInputTimeNanos()); + mouseMotionEvents.add(mouseMotionEvent); + } + }); + + glfwSetMouseButtonCallback(context.getWindowHandle(), mouseButtonCallback = new GLFWMouseButtonCallback() { + @Override + public void invoke(final long window, final int button, final int action, final int mods) { + final MouseButtonEvent mouseButtonEvent = new MouseButtonEvent(convertButton(button), action == GLFW_PRESS, mouseX, mouseY); + mouseButtonEvent.setTime(getInputTimeNanos()); + mouseButtonEvents.add(mouseButtonEvent); + } + }); + + setCursorVisible(cursorVisible); + logger.fine("Mouse created."); + initialized = true; + } + + public boolean isInitialized() { + return initialized; + } + + public int getButtonCount() { + return GLFW_MOUSE_BUTTON_LAST + 1; + } + + public void update() { + while (!mouseMotionEvents.isEmpty()) { + listener.onMouseMotionEvent(mouseMotionEvents.poll()); + } + + while (!mouseButtonEvents.isEmpty()) { + listener.onMouseButtonEvent(mouseButtonEvents.poll()); + } + } + + public void destroy() { + if (!context.isRenderable()) { + return; + } + + cursorPosCallback.release(); + scrollCallback.release(); + mouseButtonCallback.release(); + + logger.fine("Mouse destroyed."); + } + + public void setCursorVisible(boolean visible) { + cursorVisible = visible; + + if (!context.isRenderable()) { + return; + } + + if (cursorVisible) { + glfwSetInputMode(context.getWindowHandle(), GLFW_CURSOR, GLFW_CURSOR_NORMAL); + } else { + glfwSetInputMode(context.getWindowHandle(), GLFW_CURSOR, GLFW_CURSOR_DISABLED); + } + } + + public void setInputListener(RawInputListener listener) { + this.listener = listener; + } + + public long getInputTimeNanos() { + return (long) (glfwGetTime() * 1000000000); + } + + public void setNativeCursor(final JmeCursor jmeCursor) { + if (jmeCursor != null) { + final ByteBuffer byteBuffer = org.lwjgl.BufferUtils.createByteBuffer(jmeCursor.getImagesData().capacity()); + byteBuffer.asIntBuffer().put(jmeCursor.getImagesData().array()); + final long cursor = glfwCreateCursor(byteBuffer, jmeCursor.getXHotSpot(), jmeCursor.getYHotSpot()); + glfwSetCursor(context.getWindowHandle(), cursor); + } + } + + /** + * Simply converts the GLFW button code to a JME button code. If there is no match it just returns the GLFW button + * code. Bare in mind GLFW supports 8 different mouse buttons. + * + * @param glfwButton the raw GLFW button index. + * @return the mapped {@link MouseInput} button id. + */ + private int convertButton(final int glfwButton) { + if (glfwButton == GLFW_MOUSE_BUTTON_LEFT) { + return MouseInput.BUTTON_LEFT; + } else if(glfwButton == GLFW_MOUSE_BUTTON_MIDDLE) { + return MouseInput.BUTTON_MIDDLE; + } else if(glfwButton == GLFW_MOUSE_BUTTON_RIGHT) { + return MouseInput.BUTTON_RIGHT; + } + + return glfwButton; + } +} diff --git a/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java b/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java new file mode 100644 index 000000000..187b232f2 --- /dev/null +++ b/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java @@ -0,0 +1,489 @@ +/* + * Copyright (c) 2009-2012 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.renderer.lwjgl; + +import com.jme3.renderer.RendererException; +import com.jme3.renderer.opengl.GL; +import com.jme3.renderer.opengl.GL2; +import com.jme3.renderer.opengl.GL3; +import com.jme3.renderer.opengl.GL4; +import com.jme3.system.NativeLibraryLoader; +import com.jme3.system.Platform; +import org.lwjgl.opengl.*; + +import java.nio.*; + +public class LwjglGL implements GL, GL2, GL3, GL4 { + + private static void checkLimit(Buffer buffer) { + if (buffer == null) { + return; + } + if (buffer.limit() == 0) { + throw new RendererException("Attempting to upload empty buffer (limit = 0), that's an error"); + } + if (buffer.remaining() == 0) { + throw new RendererException("Attempting to upload empty buffer (remaining = 0), that's an error"); + } + } + + public void resetStats() { + } + + public void glActiveTexture(int param1) { + GL13.glActiveTexture(param1); + } + + public void glAlphaFunc(int param1, float param2) { + GL11.glAlphaFunc(param1, param2); + } + + public void glAttachShader(int param1, int param2) { + GL20.glAttachShader(param1, param2); + } + + public void glBindBuffer(int param1, int param2) { + GL15.glBindBuffer(param1, param2); + } + + public void glBindTexture(int param1, int param2) { + GL11.glBindTexture(param1, param2); + } + + public void glBlendFunc(int param1, int param2) { + GL11.glBlendFunc(param1, param2); + } + + public void glBufferData(int param1, long param2, int param3) { + GL15.glBufferData(param1, param2, param3); + } + + public void glBufferData(int param1, FloatBuffer param2, int param3) { + checkLimit(param2); + GL15.glBufferData(param1, param2, param3); + } + + public void glBufferData(int param1, ShortBuffer param2, int param3) { + checkLimit(param2); + GL15.glBufferData(param1, param2, param3); + } + + public void glBufferData(int param1, ByteBuffer param2, int param3) { + checkLimit(param2); + GL15.glBufferData(param1, param2, param3); + } + + public void glBufferSubData(int param1, long param2, FloatBuffer param3) { + checkLimit(param3); + GL15.glBufferSubData(param1, param2, param3); + } + + public void glBufferSubData(int param1, long param2, ShortBuffer param3) { + checkLimit(param3); + GL15.glBufferSubData(param1, param2, param3); + } + + public void glBufferSubData(int param1, long param2, ByteBuffer param3) { + checkLimit(param3); + GL15.glBufferSubData(param1, param2, param3); + } + + public void glClear(int param1) { + GL11.glClear(param1); + } + + public void glClearColor(float param1, float param2, float param3, float param4) { + GL11.glClearColor(param1, param2, param3, param4); + } + + public void glColorMask(boolean param1, boolean param2, boolean param3, boolean param4) { + GL11.glColorMask(param1, param2, param3, param4); + } + + public void glCompileShader(int param1) { + GL20.glCompileShader(param1); + } + + public void glCompressedTexImage2D(int param1, int param2, int param3, int param4, int param5, int param6, ByteBuffer param7) { + checkLimit(param7); + GL13.glCompressedTexImage2D(param1, param2, param3, param4, param5, param6, param7); + } + + public void glCompressedTexImage3D(int param1, int param2, int param3, int param4, int param5, int param6, int param7, ByteBuffer param8) { + checkLimit(param8); + GL13.glCompressedTexImage3D(param1, param2, param3, param4, param5, param6, param7, param8); + } + + public void glCompressedTexSubImage2D(int param1, int param2, int param3, int param4, int param5, int param6, int param7, ByteBuffer param8) { + checkLimit(param8); + GL13.glCompressedTexSubImage2D(param1, param2, param3, param4, param5, param6, param7, param8); + } + + public void glCompressedTexSubImage3D(int param1, int param2, int param3, int param4, int param5, int param6, int param7, int param8, int param9, ByteBuffer param10) { + checkLimit(param10); + GL13.glCompressedTexSubImage3D(param1, param2, param3, param4, param5, param6, param7, param8, param9, param10); + } + + public int glCreateProgram() { + return GL20.glCreateProgram(); + } + + public int glCreateShader(int param1) { + return GL20.glCreateShader(param1); + } + + public void glCullFace(int param1) { + GL11.glCullFace(param1); + } + + public void glDeleteBuffers(IntBuffer param1) { + checkLimit(param1); + GL15.glDeleteBuffers(param1); + } + + public void glDeleteProgram(int param1) { + GL20.glDeleteProgram(param1); + } + + public void glDeleteShader(int param1) { + GL20.glDeleteShader(param1); + } + + public void glDeleteTextures(IntBuffer param1) { + checkLimit(param1); + GL11.glDeleteTextures(param1); + } + + public void glDepthFunc(int param1) { + GL11.glDepthFunc(param1); + } + + public void glDepthMask(boolean param1) { + GL11.glDepthMask(param1); + } + + public void glDepthRange(double param1, double param2) { + GL11.glDepthRange(param1, param2); + } + + public void glDetachShader(int param1, int param2) { + GL20.glDetachShader(param1, param2); + } + + public void glDisable(int param1) { + GL11.glDisable(param1); + } + + public void glDisableVertexAttribArray(int param1) { + GL20.glDisableVertexAttribArray(param1); + } + + public void glDrawArrays(int param1, int param2, int param3) { + GL11.glDrawArrays(param1, param2, param3); + } + + public void glDrawBuffer(int param1) { + GL11.glDrawBuffer(param1); + } + + public void glDrawRangeElements(int param1, int param2, int param3, int param4, int param5, long param6) { + GL12.glDrawRangeElements(param1, param2, param3, param4, param5, param6); + } + + public void glEnable(int param1) { + GL11.glEnable(param1); + } + + public void glEnableVertexAttribArray(int param1) { + GL20.glEnableVertexAttribArray(param1); + } + + public void glGenBuffers(IntBuffer param1) { + checkLimit(param1); + GL15.glGenBuffers(param1); + } + + public void glGenTextures(IntBuffer param1) { + checkLimit(param1); + GL11.glGenTextures(param1); + } + + public void glGetBoolean(int param1, ByteBuffer param2) { + checkLimit(param2); + GL11.glGetBooleanv(param1, param2); + } + + public void glGetBufferSubData(int target, long offset, ByteBuffer data) { + checkLimit(data); + GL15.glGetBufferSubData(target, offset, data); + } + + public int glGetError() { + return GL11.glGetError(); + } + + public void glGetInteger(int param1, IntBuffer param2) { + checkLimit(param2); + GL11.glGetIntegerv(param1, param2); + } + + public void glGetProgram(int param1, int param2, IntBuffer param3) { + checkLimit(param3); + GL20.glGetProgramiv(param1, param2, param3); + } + + public void glGetShader(int param1, int param2, IntBuffer param3) { + checkLimit(param3); + GL20.glGetShaderiv(param1, param2, param3); + } + + public String glGetString(int param1) { + return GL11.glGetString(param1); + } + + public String glGetString(int param1, int param2) { + return GL30.glGetStringi(param1, param2); + } + + public boolean glIsEnabled(int param1) { + return GL11.glIsEnabled(param1); + } + + public void glLineWidth(float param1) { + GL11.glLineWidth(param1); + } + + public void glLinkProgram(int param1) { + GL20.glLinkProgram(param1); + } + + public void glPixelStorei(int param1, int param2) { + GL11.glPixelStorei(param1, param2); + } + + public void glPointSize(float param1) { + GL11.glPointSize(param1); + } + + public void glPolygonMode(int param1, int param2) { + GL11.glPolygonMode(param1, param2); + } + + public void glPolygonOffset(float param1, float param2) { + GL11.glPolygonOffset(param1, param2); + } + + public void glReadBuffer(int param1) { + GL11.glReadBuffer(param1); + } + + public void glReadPixels(int param1, int param2, int param3, int param4, int param5, int param6, ByteBuffer param7) { + checkLimit(param7); + GL11.glReadPixels(param1, param2, param3, param4, param5, param6, param7); + } + + public void glReadPixels(int param1, int param2, int param3, int param4, int param5, int param6, long param7) { + GL11.glReadPixels(param1, param2, param3, param4, param5, param6, param7); + } + + public void glScissor(int param1, int param2, int param3, int param4) { + GL11.glScissor(param1, param2, param3, param4); + } + + public void glStencilFuncSeparate(int param1, int param2, int param3, int param4) { + GL20.glStencilFuncSeparate(param1, param2, param3, param4); + } + + public void glStencilOpSeparate(int param1, int param2, int param3, int param4) { + GL20.glStencilOpSeparate(param1, param2, param3, param4); + } + + public void glTexImage2D(int param1, int param2, int param3, int param4, int param5, int param6, int param7, int param8, ByteBuffer param9) { + checkLimit(param9); + GL11.glTexImage2D(param1, param2, param3, param4, param5, param6, param7, param8, param9); + } + + public void glTexImage3D(int param1, int param2, int param3, int param4, int param5, int param6, int param7, int param8, int param9, ByteBuffer param10) { + checkLimit(param10); + GL12.glTexImage3D(param1, param2, param3, param4, param5, param6, param7, param8, param9, param10); + } + + public void glTexParameterf(int param1, int param2, float param3) { + GL11.glTexParameterf(param1, param2, param3); + } + + public void glTexParameteri(int param1, int param2, int param3) { + GL11.glTexParameteri(param1, param2, param3); + } + + public void glTexSubImage2D(int param1, int param2, int param3, int param4, int param5, int param6, int param7, int param8, ByteBuffer param9) { + checkLimit(param9); + GL11.glTexSubImage2D(param1, param2, param3, param4, param5, param6, param7, param8, param9); + } + + public void glTexSubImage3D(int param1, int param2, int param3, int param4, int param5, int param6, int param7, int param8, int param9, int param10, ByteBuffer param11) { + checkLimit(param11); + GL12.glTexSubImage3D(param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11); + } + + public void glUniform1(int param1, FloatBuffer param2) { + checkLimit(param2); + GL20.glUniform1fv(param1, param2); + } + + public void glUniform1(int param1, IntBuffer param2) { + checkLimit(param2); + GL20.glUniform1iv(param1, param2); + } + + public void glUniform1f(int param1, float param2) { + GL20.glUniform1f(param1, param2); + } + + public void glUniform1i(int param1, int param2) { + GL20.glUniform1i(param1, param2); + } + + public void glUniform2(int param1, IntBuffer param2) { + checkLimit(param2); + GL20.glUniform2iv(param1, param2); + } + + public void glUniform2(int param1, FloatBuffer param2) { + checkLimit(param2); + GL20.glUniform2fv(param1, param2); + } + + public void glUniform2f(int param1, float param2, float param3) { + GL20.glUniform2f(param1, param2, param3); + } + + public void glUniform3(int param1, IntBuffer param2) { + checkLimit(param2); + GL20.glUniform3iv(param1, param2); + } + + public void glUniform3(int param1, FloatBuffer param2) { + checkLimit(param2); + GL20.glUniform3fv(param1, param2); + } + + public void glUniform3f(int param1, float param2, float param3, float param4) { + GL20.glUniform3f(param1, param2, param3, param4); + } + + public void glUniform4(int param1, FloatBuffer param2) { + checkLimit(param2); + GL20.glUniform4fv(param1, param2); + } + + public void glUniform4(int param1, IntBuffer param2) { + checkLimit(param2); + GL20.glUniform4iv(param1, param2); + } + + public void glUniform4f(int param1, float param2, float param3, float param4, float param5) { + GL20.glUniform4f(param1, param2, param3, param4, param5); + } + + public void glUniformMatrix3(int param1, boolean param2, FloatBuffer param3) { + checkLimit(param3); + GL20.glUniformMatrix3fv(param1, param2, param3); + } + + public void glUniformMatrix4(int param1, boolean param2, FloatBuffer param3) { + checkLimit(param3); + GL20.glUniformMatrix4fv(param1, param2, param3); + } + + public void glUseProgram(int param1) { + GL20.glUseProgram(param1); + } + + public void glVertexAttribPointer(int param1, int param2, int param3, boolean param4, int param5, long param6) { + GL20.glVertexAttribPointer(param1, param2, param3, param4, param5, param6); + } + + public void glViewport(int param1, int param2, int param3, int param4) { + GL11.glViewport(param1, param2, param3, param4); + } + + public int glGetAttribLocation(int param1, String param2) { + // NOTE: LWJGL requires null-terminated strings + return GL20.glGetAttribLocation(param1, param2 + "\0"); + } + + public int glGetUniformLocation(int param1, String param2) { + // NOTE: LWJGL requires null-terminated strings + return GL20.glGetUniformLocation(param1, param2 + "\0"); + } + + public void glShaderSource(int param1, String[] param2, IntBuffer param3) { + checkLimit(param3); + GL20.glShaderSource(param1, param2); + } + + public String glGetProgramInfoLog(int program, int maxSize) { + return GL20.glGetProgramInfoLog(program, maxSize); + } + + public String glGetShaderInfoLog(int shader, int maxSize) { + return GL20.glGetShaderInfoLog(shader, maxSize); + } + + @Override + public void glBindFragDataLocation(int param1, int param2, String param3) { + GL30.glBindFragDataLocation(param1, param2, param3); + } + + @Override + public void glBindVertexArray(int param1) { + GL30.glBindVertexArray(param1); + } + + @Override + public void glGenVertexArrays(IntBuffer param1) { + checkLimit(param1); + GL30.glGenVertexArrays(param1); + } + + @Override + public void glPatchParameter(int count) { + GL40.glPatchParameteri(GL40.GL_PATCH_VERTICES,count); + } + + @Override + public void glDeleteVertexArrays(IntBuffer arrays) { + checkLimit(arrays); + ARBVertexArrayObject.glDeleteVertexArrays(arrays); + } +} diff --git a/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGLExt.java b/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGLExt.java new file mode 100644 index 000000000..2ed2ec215 --- /dev/null +++ b/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGLExt.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2009-2012 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.renderer.lwjgl; + +import com.jme3.renderer.RendererException; +import com.jme3.renderer.opengl.GLExt; +import org.lwjgl.opengl.*; + +import java.nio.Buffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; + +public class LwjglGLExt implements GLExt { + + private static void checkLimit(Buffer buffer) { + if (buffer == null) { + return; + } + if (buffer.limit() == 0) { + throw new RendererException("Attempting to upload empty buffer (limit = 0), that's an error"); + } + if (buffer.remaining() == 0) { + throw new RendererException("Attempting to upload empty buffer (remaining = 0), that's an error"); + } + } + + @Override + public void glBufferData(int target, IntBuffer data, int usage) { + checkLimit(data); + GL15.glBufferData(target, data, usage); + } + + @Override + public void glBufferSubData(int target, long offset, IntBuffer data) { + checkLimit(data); + GL15.glBufferSubData(target, offset, data); + } + + @Override + public void glDrawArraysInstancedARB(int mode, int first, int count, int primcount) { + ARBDrawInstanced.glDrawArraysInstancedARB(mode, first, count, primcount); + } + + @Override + public void glDrawBuffers(IntBuffer bufs) { + checkLimit(bufs); + GL20.glDrawBuffers(bufs); + } + + @Override + public void glDrawElementsInstancedARB(int mode, int indices_count, int type, long indices_buffer_offset, int primcount) { + ARBDrawInstanced.glDrawElementsInstancedARB(mode, indices_count, type, indices_buffer_offset, primcount); + } + + @Override + public void glGetMultisample(int pname, int index, FloatBuffer val) { + checkLimit(val); + ARBTextureMultisample.glGetMultisamplefv(pname, index, val); + } + + @Override + public void glTexImage2DMultisample(int target, int samples, int internalformat, int width, int height, boolean fixedsamplelocations) { + ARBTextureMultisample.glTexImage2DMultisample(target, samples, internalformat, width, height, fixedsamplelocations); + } + + @Override + public void glVertexAttribDivisorARB(int index, int divisor) { + ARBInstancedArrays.glVertexAttribDivisorARB(index, divisor); + } + + @Override + public Object glFenceSync(int condition, int flags) { + return ARBSync.glFenceSync(condition, flags); + } + + @Override + public int glClientWaitSync(final Object sync, final int flags, final long timeout) { + return ARBSync.glClientWaitSync((Long) sync, flags, timeout); + } + + @Override + public void glDeleteSync(final Object sync) { + ARBSync.glDeleteSync((Long) sync); + } +} diff --git a/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGLFboEXT.java b/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGLFboEXT.java new file mode 100644 index 000000000..5d9fe8dc1 --- /dev/null +++ b/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGLFboEXT.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2009-2012 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.renderer.lwjgl; + +import com.jme3.renderer.RendererException; +import com.jme3.renderer.opengl.GLFbo; +import org.lwjgl.opengl.EXTFramebufferBlit; +import org.lwjgl.opengl.EXTFramebufferMultisample; +import org.lwjgl.opengl.EXTFramebufferObject; + +import java.nio.Buffer; +import java.nio.IntBuffer; + +/** + * Implements GLFbo via GL_EXT_framebuffer_object. + * + * @author Kirill Vainer + */ +public class LwjglGLFboEXT implements GLFbo { + + private static void checkLimit(Buffer buffer) { + if (buffer == null) { + return; + } + if (buffer.limit() == 0) { + throw new RendererException("Attempting to upload empty buffer (limit = 0), that's an error"); + } + if (buffer.remaining() == 0) { + throw new RendererException("Attempting to upload empty buffer (remaining = 0), that's an error"); + } + } + + @Override + public void glBlitFramebufferEXT(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) { + EXTFramebufferBlit.glBlitFramebufferEXT(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); + } + + @Override + public void glRenderbufferStorageMultisampleEXT(int target, int samples, int internalformat, int width, int height) { + EXTFramebufferMultisample.glRenderbufferStorageMultisampleEXT(target, samples, internalformat, width, height); + } + + @Override + public void glBindFramebufferEXT(int param1, int param2) { + EXTFramebufferObject.glBindFramebufferEXT(param1, param2); + } + + @Override + public void glBindRenderbufferEXT(int param1, int param2) { + EXTFramebufferObject.glBindRenderbufferEXT(param1, param2); + } + + @Override + public int glCheckFramebufferStatusEXT(int param1) { + return EXTFramebufferObject.glCheckFramebufferStatusEXT(param1); + } + + @Override + public void glDeleteFramebuffersEXT(IntBuffer param1) { + checkLimit(param1); + EXTFramebufferObject.glDeleteFramebuffersEXT(param1); + } + + @Override + public void glDeleteRenderbuffersEXT(IntBuffer param1) { + checkLimit(param1); + EXTFramebufferObject.glDeleteRenderbuffersEXT(param1); + } + + @Override + public void glFramebufferRenderbufferEXT(int param1, int param2, int param3, int param4) { + EXTFramebufferObject.glFramebufferRenderbufferEXT(param1, param2, param3, param4); + } + + @Override + public void glFramebufferTexture2DEXT(int param1, int param2, int param3, int param4, int param5) { + EXTFramebufferObject.glFramebufferTexture2DEXT(param1, param2, param3, param4, param5); + } + + @Override + public void glGenFramebuffersEXT(IntBuffer param1) { + checkLimit(param1); + EXTFramebufferObject.glGenFramebuffersEXT(param1); + } + + @Override + public void glGenRenderbuffersEXT(IntBuffer param1) { + checkLimit(param1); + EXTFramebufferObject.glGenRenderbuffersEXT(param1); + } + + @Override + public void glGenerateMipmapEXT(int param1) { + EXTFramebufferObject.glGenerateMipmapEXT(param1); + } + + @Override + public void glRenderbufferStorageEXT(int param1, int param2, int param3, int param4) { + EXTFramebufferObject.glRenderbufferStorageEXT(param1, param2, param3, param4); + } +} diff --git a/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGLFboGL3.java b/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGLFboGL3.java new file mode 100644 index 000000000..5a7a9825e --- /dev/null +++ b/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGLFboGL3.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2009-2012 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.renderer.lwjgl; + +import com.jme3.renderer.RendererException; +import com.jme3.renderer.opengl.GLFbo; +import org.lwjgl.opengl.GL30; + +import java.nio.Buffer; +import java.nio.IntBuffer; + +/** + * Implements GLFbo via OpenGL3+. + * + * @author Kirill Vainer + */ +public class LwjglGLFboGL3 implements GLFbo { + + private static void checkLimit(Buffer buffer) { + if (buffer == null) { + return; + } + if (buffer.limit() == 0) { + throw new RendererException("Attempting to upload empty buffer (limit = 0), that's an error"); + } + if (buffer.remaining() == 0) { + throw new RendererException("Attempting to upload empty buffer (remaining = 0), that's an error"); + } + } + + @Override + public void glBlitFramebufferEXT(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) { + GL30.glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); + } + + @Override + public void glRenderbufferStorageMultisampleEXT(int target, int samples, int internalformat, int width, int height) { + GL30.glRenderbufferStorageMultisample(target, samples, internalformat, width, height); + } + + @Override + public void glBindFramebufferEXT(int param1, int param2) { + GL30.glBindFramebuffer(param1, param2); + } + + @Override + public void glBindRenderbufferEXT(int param1, int param2) { + GL30.glBindRenderbuffer(param1, param2); + } + + @Override + public int glCheckFramebufferStatusEXT(int param1) { + return GL30.glCheckFramebufferStatus(param1); + } + + @Override + public void glDeleteFramebuffersEXT(IntBuffer param1) { + checkLimit(param1); + GL30.glDeleteFramebuffers(param1); + } + + @Override + public void glDeleteRenderbuffersEXT(IntBuffer param1) { + checkLimit(param1); + GL30.glDeleteRenderbuffers(param1); + } + + @Override + public void glFramebufferRenderbufferEXT(int param1, int param2, int param3, int param4) { + GL30.glFramebufferRenderbuffer(param1, param2, param3, param4); + } + + @Override + public void glFramebufferTexture2DEXT(int param1, int param2, int param3, int param4, int param5) { + GL30.glFramebufferTexture2D(param1, param2, param3, param4, param5); + } + + @Override + public void glGenFramebuffersEXT(IntBuffer param1) { + checkLimit(param1); + GL30.glGenFramebuffers(param1); + } + + @Override + public void glGenRenderbuffersEXT(IntBuffer param1) { + checkLimit(param1); + GL30.glGenRenderbuffers(param1); + } + + @Override + public void glGenerateMipmapEXT(int param1) { + GL30.glGenerateMipmap(param1); + } + + @Override + public void glRenderbufferStorageEXT(int param1, int param2, int param3, int param4) { + GL30.glRenderbufferStorage(param1, param2, param3, param4); + } +} diff --git a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglContext.java b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglContext.java new file mode 100644 index 000000000..61ea20b71 --- /dev/null +++ b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglContext.java @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2009-2012 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.lwjgl; + +import com.jme3.input.lwjgl.GlfwJoystickInput; +import com.jme3.input.lwjgl.GlfwKeyInput; +import com.jme3.input.lwjgl.GlfwMouseInput; +import com.jme3.renderer.Renderer; +import com.jme3.renderer.RendererException; +import com.jme3.renderer.lwjgl.LwjglGL; +import com.jme3.renderer.lwjgl.LwjglGLExt; +import com.jme3.renderer.lwjgl.LwjglGLFboEXT; +import com.jme3.renderer.lwjgl.LwjglGLFboGL3; +import com.jme3.renderer.opengl.*; +import com.jme3.system.*; +import org.lwjgl.Sys; +import org.lwjgl.glfw.GLFW; +import org.lwjgl.opengl.ARBDebugOutput; +import org.lwjgl.opengl.ARBFramebufferObject; +import org.lwjgl.opengl.EXTFramebufferMultisample; +import org.lwjgl.opengl.GLCapabilities; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static org.lwjgl.opengl.GL.createCapabilities; +import static org.lwjgl.opengl.GL11.GL_TRUE; +import static org.lwjgl.opengl.GL11.glGetInteger; + +/** + * A LWJGL implementation of a graphics context. + */ +public abstract class LwjglContext implements JmeContext { + + private static final Logger logger = Logger.getLogger(LwjglContext.class.getName()); + + protected static final String THREAD_NAME = "jME3 Main"; + + protected AtomicBoolean created = new AtomicBoolean(false); + protected AtomicBoolean renderable = new AtomicBoolean(false); + protected final Object createdLock = new Object(); + + protected AppSettings settings = new AppSettings(true); + protected Renderer renderer; + protected GlfwKeyInput keyInput; + protected GlfwMouseInput mouseInput; + protected GlfwJoystickInput joyInput; + protected Timer timer; + protected SystemListener listener; + + public void setSystemListener(SystemListener listener) { + this.listener = listener; + } + + protected void printContextInitInfo() { + logger.log(Level.INFO, "LWJGL {0} context running on thread {1}\n" + + " * Graphics Adapter: GLFW {2}", + new Object[]{Sys.getVersion(), Thread.currentThread().getName(), GLFW.glfwGetVersionString()}); + } + + protected int determineMaxSamples() { + // If we already have a valid context, determine samples using current context. + if (GLFW.glfwExtensionSupported("GL_ARB_framebuffer_object") == GL_TRUE) { + return glGetInteger(ARBFramebufferObject.GL_MAX_SAMPLES); + } else if (GLFW.glfwExtensionSupported("GL_EXT_framebuffer_multisample") == GL_TRUE) { + return glGetInteger(EXTFramebufferMultisample.GL_MAX_SAMPLES_EXT); + } + + return Integer.MAX_VALUE; + } + + protected void loadNatives() { + if (JmeSystem.isLowPermissions()) { + return; + } + + if ("LWJGL".equals(settings.getAudioRenderer())) { + NativeLibraryLoader.loadNativeLibrary("openal-lwjgl3", true); + } + + if (NativeLibraryLoader.isUsingNativeBullet()) { + NativeLibraryLoader.loadNativeLibrary("bulletjme", true); + } + + NativeLibraryLoader.loadNativeLibrary("glfw-lwjgl3", true); + NativeLibraryLoader.loadNativeLibrary("jemalloc-lwjgl3", true); + NativeLibraryLoader.loadNativeLibrary("lwjgl3", true); + } + + protected int getNumSamplesToUse() { + int samples = 0; + if (settings.getSamples() > 1) { + samples = settings.getSamples(); + final int supportedSamples = determineMaxSamples(); + if (supportedSamples < samples) { + logger.log(Level.WARNING, + "Couldn't satisfy antialiasing samples requirement: x{0}. " + + "Video hardware only supports: x{1}", + new Object[]{samples, supportedSamples}); + + samples = supportedSamples; + } + } + return samples; + } + + protected void initContextFirstTime() { + final GLCapabilities capabilities = createCapabilities(settings.getRenderer().equals(AppSettings.LWJGL_OPENGL3)); + + if (!capabilities.OpenGL20) { + throw new RendererException("OpenGL 2.0 or higher is required for jMonkeyEngine"); + } + + if (settings.getRenderer().equals(AppSettings.LWJGL_OPENGL2) + || settings.getRenderer().equals(AppSettings.LWJGL_OPENGL3)) { + GL gl = new LwjglGL(); + GLExt glext = new LwjglGLExt(); + GLFbo glfbo; + + if (capabilities.OpenGL30) { + glfbo = new LwjglGLFboGL3(); + } else { + glfbo = new LwjglGLFboEXT(); + } + + if (settings.getBoolean("GraphicsDebug")) { + gl = new GLDebugDesktop(gl, glext, glfbo); + glext = (GLExt) gl; + glfbo = (GLFbo) gl; + } + + if (settings.getBoolean("GraphicsTiming")) { + GLTimingState timingState = new GLTimingState(); + gl = (GL) GLTiming.createGLTiming(gl, timingState, GL.class, GL2.class, GL3.class, GL4.class); + glext = (GLExt) GLTiming.createGLTiming(glext, timingState, GLExt.class); + glfbo = (GLFbo) GLTiming.createGLTiming(glfbo, timingState, GLFbo.class); + } + + if (settings.getBoolean("GraphicsTrace")) { + gl = (GL) GLTracer.createDesktopGlTracer(gl, GL.class, GL2.class, GL3.class, GL4.class); + glext = (GLExt) GLTracer.createDesktopGlTracer(glext, GLExt.class); + glfbo = (GLFbo) GLTracer.createDesktopGlTracer(glfbo, GLFbo.class); + } + + renderer = new GLRenderer(gl, glext, glfbo); + renderer.initialize(); + } else { + throw new UnsupportedOperationException("Unsupported renderer: " + settings.getRenderer()); + } + + if (capabilities.GL_ARB_debug_output && settings.getBoolean("GraphicsDebug")) { + ARBDebugOutput.glDebugMessageCallbackARB(new LwjglGLDebugOutputHandler(), 0); // User param is zero. Not sure what we could use that for. + } + + renderer.setMainFrameBufferSrgb(settings.getGammaCorrection()); + renderer.setLinearizeSrgbImages(settings.getGammaCorrection()); + + // Init input + if (keyInput != null) { + keyInput.initialize(); + } + + if (mouseInput != null) { + mouseInput.initialize(); + } + + if (joyInput != null) { + joyInput.initialize(); + } + + renderable.set(true); + } + + public void internalDestroy() { + renderer = null; + timer = null; + renderable.set(false); + synchronized (createdLock) { + created.set(false); + createdLock.notifyAll(); + } + } + + public void internalCreate() { + synchronized (createdLock) { + created.set(true); + createdLock.notifyAll(); + } + + initContextFirstTime(); + } + + public void create() { + create(false); + } + + public void destroy() { + destroy(false); + } + + protected void waitFor(boolean createdVal) { + synchronized (createdLock) { + while (created.get() != createdVal) { + try { + createdLock.wait(); + } catch (InterruptedException ignored) { + } + } + } + } + + public boolean isCreated() { + return created.get(); + } + + public boolean isRenderable() { + return renderable.get(); + } + + public void setSettings(AppSettings settings) { + this.settings.copyFrom(settings); + } + + public AppSettings getSettings() { + return settings; + } + + public Renderer getRenderer() { + return renderer; + } + + public Timer getTimer() { + return timer; + } + +} diff --git a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglDisplay.java b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglDisplay.java new file mode 100644 index 000000000..3eb749adc --- /dev/null +++ b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglDisplay.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2009-2012 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.lwjgl; + +/** + * @author Daniel Johansson + */ +public class LwjglDisplay extends LwjglWindow { + + public LwjglDisplay() { + super(Type.Display); + } +} diff --git a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglGLDebugOutputHandler.java b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglGLDebugOutputHandler.java new file mode 100644 index 000000000..aa9c46511 --- /dev/null +++ b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglGLDebugOutputHandler.java @@ -0,0 +1,78 @@ +/* + * 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.lwjgl; + +import org.lwjgl.opengl.ARBDebugOutput; +import org.lwjgl.opengl.GLDebugMessageARBCallback; + +import java.util.HashMap; + +class LwjglGLDebugOutputHandler extends GLDebugMessageARBCallback { + + private static final HashMap constMap = new HashMap(); + private static final String MESSAGE_FORMAT = + "[JME3] OpenGL debug message\r\n" + + " ID: %d\r\n" + + " Source: %s\r\n" + + " Type: %s\r\n" + + " Severity: %s\r\n" + + " Message: %s"; + + static { + constMap.put(ARBDebugOutput.GL_DEBUG_SOURCE_API_ARB, "API"); + constMap.put(ARBDebugOutput.GL_DEBUG_SOURCE_APPLICATION_ARB, "APPLICATION"); + constMap.put(ARBDebugOutput.GL_DEBUG_SOURCE_OTHER_ARB, "OTHER"); + constMap.put(ARBDebugOutput.GL_DEBUG_SOURCE_SHADER_COMPILER_ARB, "SHADER_COMPILER"); + constMap.put(ARBDebugOutput.GL_DEBUG_SOURCE_THIRD_PARTY_ARB, "THIRD_PARTY"); + constMap.put(ARBDebugOutput.GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB, "WINDOW_SYSTEM"); + + constMap.put(ARBDebugOutput.GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB, "DEPRECATED_BEHAVIOR"); + constMap.put(ARBDebugOutput.GL_DEBUG_TYPE_ERROR_ARB, "ERROR"); + constMap.put(ARBDebugOutput.GL_DEBUG_TYPE_OTHER_ARB, "OTHER"); + constMap.put(ARBDebugOutput.GL_DEBUG_TYPE_PERFORMANCE_ARB, "PERFORMANCE"); + constMap.put(ARBDebugOutput.GL_DEBUG_TYPE_PORTABILITY_ARB, "PORTABILITY"); + constMap.put(ARBDebugOutput.GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB, "UNDEFINED_BEHAVIOR"); + + constMap.put(ARBDebugOutput.GL_DEBUG_SEVERITY_HIGH_ARB, "HIGH"); + constMap.put(ARBDebugOutput.GL_DEBUG_SEVERITY_MEDIUM_ARB, "MEDIUM"); + constMap.put(ARBDebugOutput.GL_DEBUG_SEVERITY_LOW_ARB, "LOW"); + } + + @Override + public void invoke(int source, int type, int id, int severity, int length, long message, long userParam) { + String sourceStr = constMap.get(source); + String typeStr = constMap.get(type); + String severityStr = constMap.get(severity); + + System.err.println(String.format(MESSAGE_FORMAT, id, sourceStr, typeStr, severityStr, message)); + } +} diff --git a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglOffscreenBuffer.java b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglOffscreenBuffer.java new file mode 100644 index 000000000..8fdde51af --- /dev/null +++ b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglOffscreenBuffer.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2009-2012 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.lwjgl; + +import com.jme3.system.JmeContext; + +/** + * @author Daniel Johansson + */ +public class LwjglOffscreenBuffer extends LwjglWindow { + + public LwjglOffscreenBuffer() { + super(JmeContext.Type.OffscreenSurface); + } +} diff --git a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java new file mode 100644 index 000000000..e1f4dcf73 --- /dev/null +++ b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java @@ -0,0 +1,509 @@ +/* + * Copyright (c) 2009-2012 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.lwjgl; + +import com.jme3.input.JoyInput; +import com.jme3.input.KeyInput; +import com.jme3.input.MouseInput; +import com.jme3.input.TouchInput; +import com.jme3.input.lwjgl.GlfwJoystickInput; +import com.jme3.input.lwjgl.GlfwKeyInput; +import com.jme3.input.lwjgl.GlfwMouseInput; +import com.jme3.system.AppSettings; +import com.jme3.system.JmeContext; +import com.jme3.system.JmeSystem; +import com.jme3.system.NanoTimer; +import org.lwjgl.Sys; +import org.lwjgl.glfw.*; + +import java.awt.*; +import java.awt.image.BufferedImage; +import java.nio.ByteBuffer; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static org.lwjgl.glfw.GLFW.*; +import static org.lwjgl.opengl.GL11.GL_FALSE; +import static org.lwjgl.opengl.GL11.GL_TRUE; +import static org.lwjgl.system.MemoryUtil.NULL; + +/** + * A wrapper class over the GLFW framework in LWJGL 3. + * + * @author Daniel Johansson + */ +public abstract class LwjglWindow extends LwjglContext implements Runnable { + + private static final Logger LOGGER = Logger.getLogger(LwjglWindow.class.getName()); + + protected AtomicBoolean needClose = new AtomicBoolean(false); + protected final AtomicBoolean needRestart = new AtomicBoolean(false); + protected boolean wasActive = false; + protected boolean autoFlush = true; + protected boolean allowSwapBuffers = false; + private long window = -1; + private final JmeContext.Type type; + private int frameRateLimit = -1; + private double frameSleepTime; + + private GLFWErrorCallback errorCallback; + private GLFWWindowSizeCallback windowSizeCallback; + private GLFWWindowFocusCallback windowFocusCallback; + + public LwjglWindow(final JmeContext.Type type) { + if (!JmeContext.Type.Display.equals(type) && !JmeContext.Type.OffscreenSurface.equals(type) && !JmeContext.Type.Canvas.equals(type)) { + throw new IllegalArgumentException("Unsupported type '" + type.name() + "' provided"); + } + + this.type = type; + } + + /** + * @return Type.Display or Type.Canvas + */ + public JmeContext.Type getType() { + return type; + } + + /** + * Set the title if its a windowed display + * + * @param title the title to set + */ + public void setTitle(final String title) { + if (created.get() && window != -1) { + glfwSetWindowTitle(window, title); + } + } + + /** + * Restart if its a windowed or full-screen display. + */ + public void restart() { + if (created.get()) { + needRestart.set(true); + } else { + LOGGER.warning("Display is not created, cannot restart window."); + } + } + + /** + * Apply the settings, changing resolution, etc. + * + * @param settings the settings to apply when creating the context. + */ + protected void createContext(final AppSettings settings) { + glfwSetErrorCallback(errorCallback = new GLFWErrorCallback() { + @Override + public void invoke(int error, long description) { + final String message = Callbacks.errorCallbackDescriptionString(description); + listener.handleError(message, new Exception(message)); + } + }); + + if (glfwInit() != GL_TRUE) { + throw new IllegalStateException("Unable to initialize GLFW"); + } + + glfwDefaultWindowHints(); + glfwWindowHint(GLFW_VISIBLE, GL_FALSE); + + // TODO: Add support for monitor selection + long monitor = NULL; + + if (settings.isFullscreen()) { + monitor = glfwGetPrimaryMonitor(); + } + + final ByteBuffer videoMode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + + if (settings.getWidth() <= 0 || settings.getHeight() <= 0) { + settings.setResolution(GLFWvidmode.width(videoMode), GLFWvidmode.height(videoMode)); + } + + window = glfwCreateWindow(settings.getWidth(), settings.getHeight(), settings.getTitle(), monitor, NULL); + + if (window == NULL) { + throw new RuntimeException("Failed to create the GLFW window"); + } + + glfwWindowHint(GLFW_RESIZABLE, settings.isResizable() ? GL_TRUE : GL_FALSE); + glfwWindowHint(GLFW_DEPTH_BITS, settings.getDepthBits()); + glfwWindowHint(GLFW_STENCIL_BITS, settings.getStencilBits()); + glfwWindowHint(GLFW_SAMPLES, settings.getSamples()); + glfwWindowHint(GLFW_STEREO, settings.useStereo3D() ? GL_TRUE : GL_FALSE); + glfwWindowHint(GLFW_REFRESH_RATE, settings.getFrequency()); + + // Not sure how else to support bits per pixel + if (settings.getBitsPerPixel() == 24) { + glfwWindowHint(GLFW_RED_BITS, 8); + glfwWindowHint(GLFW_GREEN_BITS, 8); + glfwWindowHint(GLFW_BLUE_BITS, 8); + } else if (settings.getBitsPerPixel() == 16) { + glfwWindowHint(GLFW_RED_BITS, 5); + glfwWindowHint(GLFW_GREEN_BITS, 6); + glfwWindowHint(GLFW_BLUE_BITS, 5); + } + + glfwWindowHint(GLFW_ALPHA_BITS, settings.getAlphaBits()); + + glfwSetWindowFocusCallback(window, windowFocusCallback = new GLFWWindowFocusCallback() { + @Override + public void invoke(final long window, final int focused) { + final boolean focus = (focused == GL_TRUE); + + if (wasActive != focus) { + if (!wasActive) { + listener.gainFocus(); + timer.reset(); + } else { + listener.loseFocus(); + } + + wasActive = !wasActive; + } + } + }); + + // Center the window + if (!settings.isFullscreen() && Type.Display.equals(type)) { + glfwSetWindowPos(window, (GLFWvidmode.width(videoMode) - settings.getWidth()) / 2, (GLFWvidmode.height(videoMode) - settings.getHeight()) / 2); + } + + // Make the OpenGL context current + glfwMakeContextCurrent(window); + + // Enable vsync + if (settings.isVSync()) { + glfwSwapInterval(1); + } else { + glfwSwapInterval(0); + } + + // Make the window visible + if (Type.Display.equals(type)) { + glfwShowWindow(window); + } + + // Add a resize callback which delegates to the listener + glfwSetWindowSizeCallback(window, windowSizeCallback = new GLFWWindowSizeCallback() { + @Override + public void invoke(final long window, final int width, final int height) { + settings.setResolution(width, height); + listener.reshape(width, height); + } + }); + + allowSwapBuffers = settings.isSwapBuffers(); + + // TODO: When GLFW 3.2 is released and included in LWJGL 3.x then we should hopefully be able to set the window icon. + } + + /** + * Destroy the context. + */ + protected void destroyContext() { + try { + if (renderer != null) { + renderer.cleanup(); + } + + errorCallback.release(); + windowSizeCallback.release(); + windowFocusCallback.release(); + + if (window != 0) { + glfwDestroyWindow(window); + } + } catch (Exception ex) { + listener.handleError("Failed to destroy context", ex); + } + } + + public void create(boolean waitFor) { + if (created.get()) { + LOGGER.warning("create() called when display is already created!"); + return; + } + + new Thread(this, THREAD_NAME).start(); + + if (waitFor) { + waitFor(true); + } + } + + /** + * Does LWJGL display initialization in the OpenGL thread + */ + protected boolean initInThread() { + try { + if (!JmeSystem.isLowPermissions()) { + // Enable uncaught exception handler only for current thread + Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { + public void uncaughtException(Thread thread, Throwable thrown) { + listener.handleError("Uncaught exception thrown in " + thread.toString(), thrown); + if (needClose.get()) { + // listener.handleError() has requested the + // context to close. Satisfy request. + deinitInThread(); + } + } + }); + } + + timer = new NanoTimer(); + + // For canvas, this will create a pbuffer, + // allowing us to query information. + // When the canvas context becomes available, it will + // be replaced seamlessly. + createContext(settings); + printContextInitInfo(); + + created.set(true); + super.internalCreate(); + } catch (Exception ex) { + try { + if (window != -1) { + glfwDestroyWindow(window); + } + } catch (Exception ex2) { + LOGGER.log(Level.WARNING, null, ex2); + } + + listener.handleError("Failed to create display", ex); + return false; // if we failed to create display, do not continue + } + + listener.initialize(); + return true; + } + + /** + * execute one iteration of the render loop in the OpenGL thread + */ + protected void runLoop() { + // If a restart is required, lets recreate the context. + if (needRestart.getAndSet(false)) { + try { + createContext(settings); + } catch (Exception ex) { + LOGGER.log(Level.SEVERE, "Failed to set display settings!", ex); + } + + LOGGER.fine("Display restarted."); + } + + if (!created.get()) { + throw new IllegalStateException(); + } + + listener.update(); + + // All this does is call swap buffers + // If the canvas is not active, there's no need to waste time + // doing that .. + if (renderable.get()) { + // calls swap buffers, etc. + try { + if (allowSwapBuffers && autoFlush) { + glfwSwapBuffers(window); + } + } catch (Throwable ex) { + listener.handleError("Error while swapping buffers", ex); + } + } + + glfwPollEvents(); + + // Subclasses just call GLObjectManager clean up objects here + // it is safe .. for now. + if (renderer != null) { + renderer.postFrame(); + } + + if (autoFlush) { + if (frameRateLimit != getSettings().getFrameRate()) { + setFrameRateLimit(getSettings().getFrameRate()); + } + } else if (frameRateLimit != 20) { + setFrameRateLimit(20); + } + + // If software frame rate limiting has been asked for, lets calculate sleep time based on a base value calculated + // from 1000 / frameRateLimit in milliseconds subtracting the time it has taken to render last frame. + // This gives an approximate limit within 3 fps of the given frame rate limit. + if (frameRateLimit > 0) { + final double sleep = frameSleepTime - (timer.getTimePerFrame() / 1000.0); + final long sleepMillis = (long) sleep; + final int additionalNanos = (int) ((sleep - sleepMillis) * 1000000.0); + + if (sleepMillis >= 0 && additionalNanos >= 0) { + try { + Thread.sleep(sleepMillis, additionalNanos); + } catch (InterruptedException ignored) { + } + } + } + } + + private void setFrameRateLimit(int frameRateLimit) { + this.frameRateLimit = frameRateLimit; + frameSleepTime = 1000.0 / this.frameRateLimit; + } + + /** + * De-initialize in the OpenGL thread. + */ + + protected void deinitInThread() { + destroyContext(); + + listener.destroy(); + LOGGER.fine("Display destroyed."); + super.internalDestroy(); + } + + public void run() { + if (listener == null) { + throw new IllegalStateException("SystemListener is not set on context!" + + "Must set with JmeContext.setSystemListener()."); + } + + loadNatives(); + LOGGER.log(Level.FINE, "Using LWJGL {0}", Sys.getVersion()); + + if (!initInThread()) { + LOGGER.log(Level.SEVERE, "Display initialization failed. Cannot continue."); + return; + } + + while (true) { + if (glfwWindowShouldClose(window) == GL_TRUE) { + listener.requestClose(false); + } + + runLoop(); + + if (needClose.get()) { + break; + } + } + + deinitInThread(); + } + + public JoyInput getJoyInput() { + if (joyInput == null) { + joyInput = new GlfwJoystickInput(); + } + return joyInput; + } + + public MouseInput getMouseInput() { + if (mouseInput == null) { + mouseInput = new GlfwMouseInput(this); + } + return mouseInput; + } + + public KeyInput getKeyInput() { + if (keyInput == null) { + keyInput = new GlfwKeyInput(this); + } + + return keyInput; + } + + public TouchInput getTouchInput() { + return null; + } + + public void setAutoFlushFrames(boolean enabled) { + this.autoFlush = enabled; + } + + public void destroy(boolean waitFor) { + needClose.set(true); + + if (waitFor) { + waitFor(false); + } + } + + public long getWindowHandle() { + return window; + } + + + // TODO: Implement support for window icon when GLFW supports it. + + private ByteBuffer[] imagesToByteBuffers(Object[] images) { + ByteBuffer[] out = new ByteBuffer[images.length]; + for (int i = 0; i < images.length; i++) { + BufferedImage image = (BufferedImage) images[i]; + out[i] = imageToByteBuffer(image); + } + return out; + } + + private ByteBuffer imageToByteBuffer(BufferedImage image) { + if (image.getType() != BufferedImage.TYPE_INT_ARGB_PRE) { + BufferedImage convertedImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB_PRE); + Graphics2D g = convertedImage.createGraphics(); + double width = image.getWidth() * (double) 1; + double height = image.getHeight() * (double) 1; + g.drawImage(image, (int) ((convertedImage.getWidth() - width) / 2), + (int) ((convertedImage.getHeight() - height) / 2), + (int) (width), (int) (height), null); + g.dispose(); + image = convertedImage; + } + + byte[] imageBuffer = new byte[image.getWidth() * image.getHeight() * 4]; + int counter = 0; + for (int i = 0; i < image.getHeight(); i++) { + for (int j = 0; j < image.getWidth(); j++) { + int colorSpace = image.getRGB(j, i); + imageBuffer[counter + 0] = (byte) ((colorSpace << 8) >> 24); + imageBuffer[counter + 1] = (byte) ((colorSpace << 16) >> 24); + imageBuffer[counter + 2] = (byte) ((colorSpace << 24) >> 24); + imageBuffer[counter + 3] = (byte) (colorSpace >> 24); + counter += 4; + } + } + return ByteBuffer.wrap(imageBuffer); + } +} diff --git a/jme3-niftygui/build.gradle b/jme3-niftygui/build.gradle index abe25f6e4..fe4f46462 100644 --- a/jme3-niftygui/build.gradle +++ b/jme3-niftygui/build.gradle @@ -14,3 +14,16 @@ dependencies { compile 'lessvoid:nifty-default-controls:1.4.1' compile 'lessvoid:nifty-style-black:1.4.1' } + +uploadArchives { + repositories.mavenDeployer { + pom.project { + repositories { + repository { + id "nifty-maven-repo.sourceforge.net" + url "http://nifty-gui.sourceforge.net/nifty-maven-repo" + } + } + } + } +} \ No newline at end of file diff --git a/sdk/build.gradle b/sdk/build.gradle index d1d71b8fc..99c850987 100644 --- a/sdk/build.gradle +++ b/sdk/build.gradle @@ -418,7 +418,7 @@ task cleanSdk() <<{ file("JME3TestsTemplateAndroid/src/jmetest/").deleteDir() } -jar.dependsOn(buildSdk) -clean.dependsOn(cleanSdk); - +tasks.remove(uploadArchives) +jar.dependsOn(buildSdk) +clean.dependsOn(cleanSdk) diff --git a/sdk/jme3-materialeditor/src/com/jme3/gde/materials/multiview/widgets/TexturePanel.java b/sdk/jme3-materialeditor/src/com/jme3/gde/materials/multiview/widgets/TexturePanel.java index b414a1126..0129535e4 100644 --- a/sdk/jme3-materialeditor/src/com/jme3/gde/materials/multiview/widgets/TexturePanel.java +++ b/sdk/jme3-materialeditor/src/com/jme3/gde/materials/multiview/widgets/TexturePanel.java @@ -10,15 +10,19 @@ */ package com.jme3.gde.materials.multiview.widgets; +import com.jme3.asset.AssetNotFoundException; import com.jme3.gde.core.assets.ProjectAssetManager; import com.jme3.gde.core.properties.TexturePropertyEditor; import com.jme3.gde.core.properties.preview.DDSPreview; import com.jme3.gde.materials.MaterialProperty; +import com.jme3.gde.materials.multiview.MaterialEditorTopComponent; import com.jme3.texture.Texture; import java.awt.Component; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.swing.Icon; import javax.swing.SwingUtilities; import jme3tools.converters.ImageToAwt; @@ -50,7 +54,7 @@ public class TexturePanel extends MaterialPropertyWidget { exec.execute(new Runnable() { public void run() { - + try{ Texture tex = manager.loadTexture(textureName); if (textureName.toLowerCase().endsWith(".dds")) { if (ddsPreview == null) { @@ -65,6 +69,9 @@ public class TexturePanel extends MaterialPropertyWidget { } }); } + } catch (AssetNotFoundException a) { + Logger.getLogger(MaterialEditorTopComponent.class.getName()).log(Level.WARNING, "Could not load texture {0}", textureName); + } } }); } diff --git a/sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/SceneEditTool.java b/sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/SceneEditTool.java index 372984f8e..c2a4753c4 100644 --- a/sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/SceneEditTool.java +++ b/sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/SceneEditTool.java @@ -453,6 +453,9 @@ public abstract class SceneEditTool { Geometry arrowX = new Geometry("arrowX", new Arrow(new Vector3f(arrowSize, 0, 0))); Geometry arrowY = new Geometry("arrowY", new Arrow(new Vector3f(0, arrowSize, 0))); Geometry arrowZ = new Geometry("arrowZ", new Arrow(new Vector3f(0, 0, arrowSize))); + arrowX.getMesh().setLineWidth(2f); + arrowY.getMesh().setLineWidth(2f); + arrowZ.getMesh().setLineWidth(2f); axis.attachChild(arrowX); axis.attachChild(arrowY); axis.attachChild(arrowZ); diff --git a/sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/SelectTool.java b/sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/SelectTool.java index 87f3c283f..013cfb492 100644 --- a/sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/SelectTool.java +++ b/sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/SelectTool.java @@ -10,6 +10,7 @@ import com.jme3.gde.core.sceneexplorer.nodes.JmeSpatial; import com.jme3.gde.core.sceneviewer.SceneViewerTopComponent; import com.jme3.gde.scenecomposer.SceneEditTool; import com.jme3.math.Vector2f; +import com.jme3.math.Vector3f; import com.jme3.scene.Node; import com.jme3.scene.Spatial; import com.jme3.terrain.Terrain; @@ -33,7 +34,7 @@ import org.openide.loaders.DataObject; */ public class SelectTool extends SceneEditTool { - private boolean wasDraggingR = false; + private boolean wasDraggingR, wasDraggingL = false; private boolean wasDownR = false; /** @@ -52,13 +53,24 @@ public class SelectTool extends SceneEditTool { */ @Override public void actionPrimary(Vector2f screenCoord, boolean pressed, final JmeNode rootNode, DataObject dataObject) { - + if (!pressed){ + if (!wasDraggingL) { + Vector3f result = pickWorldLocation(getCamera(), screenCoord, rootNode); + if (result != null) { + if (toolController.isSnapToGrid()) { + result.set(Math.round(result.x), result.y, Math.round(result.z)); + } + toolController.doSetCursorLocation(result); + } + } + wasDraggingL = false; + } } @Override public void actionSecondary(final Vector2f screenCoord, boolean pressed, final JmeNode rootNode, DataObject dataObject) { if (pressed) { - Spatial selected = toolController.getSelectedSpatial(); + Spatial selected;// = toolController.getSelectedSpatial(); // mouse down if (!wasDraggingR && !wasDownR) { // wasn't dragging and was not down already @@ -137,6 +149,7 @@ public class SelectTool extends SceneEditTool { @Override public void draggedPrimary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject currentDataObject) { + wasDraggingL = pressed; } @Override diff --git a/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/Bundle.properties b/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/Bundle.properties index 1f7cfa66f..69d691908 100644 --- a/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/Bundle.properties +++ b/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/Bundle.properties @@ -124,7 +124,7 @@ TerrainEditorTopComponent.AbsoluteCheckbox.tooltip=Define the height to adjust t TerrainEditorTopComponent.slopeLockCheckbox.tooltip=Contains the slope between the two slope nodes TerrainEditorTopComponent.borderDistanceLabel.tooltip=Distance means how far from the terrain's edge the border will be raised (thickness of the border). TerrainEditorTopComponent.borderHeightLAbel.tooltip=Height means how high the border will go (also accept negative values). -TerrainEditorTopComponent.paintButton.toolTipText=Erase a texture from the terrain +TerrainEditorTopComponent.paintButton.toolTipText=Paint a texture on the terrain. RMB to Erase. TerrainEditorTopComponent.paintButton.text= RenameTerrainVisualPanel1.ranemeLabel.text=Rename Terrain Alphamaps RenameTerrainVisualPanel1.renameField.text= diff --git a/sdk/nbproject/platform.properties b/sdk/nbproject/platform.properties index 73f8590d7..65b38a709 100644 --- a/sdk/nbproject/platform.properties +++ b/sdk/nbproject/platform.properties @@ -1,4 +1,6 @@ branding.token=jmonkeyplatform +keystore=../nbproject/private/keystore.jks +nbm_alias=jmeupdates cluster.path=\ ${nbplatform.active.dir}/extide:\ ${nbplatform.active.dir}/harness:\ diff --git a/settings.gradle b/settings.gradle index 89b8fc075..106a609bf 100644 --- a/settings.gradle +++ b/settings.gradle @@ -15,6 +15,7 @@ include 'jme3-desktop' include 'jme3-blender' include 'jme3-jogl' include 'jme3-lwjgl' +include 'jme3-lwjgl3' // Other external dependencies include 'jme3-jbullet' @@ -36,7 +37,7 @@ include 'jme3-testdata' include 'jme3-examples' if(buildAndroidExamples == "true"){ - include 'jme3-android-examples' + include 'jme3-android-examples' } if(buildSdkProject == "true"){