Merge branch 'master' into expermiental

experimental
Kirill Vainer 9 years ago
commit 15465a020f
  1. 1
      .gitignore
  2. 4
      jme3-android/src/main/java/com/jme3/system/android/JmeAndroidSystem.java
  3. 4
      jme3-blender/src/main/java/com/jme3/asset/BlenderKey.java
  4. 87
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java
  5. 137
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java
  6. 86
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderLoader.java
  7. 1
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/landscape/LandscapeHelper.java
  8. 10
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/TemporalMesh.java
  9. 4
      jme3-bullet/src/main/java/com/jme3/bullet/objects/PhysicsRigidBody.java
  10. 1
      jme3-core/src/main/java/com/jme3/font/BitmapTextPage.java
  11. 32
      jme3-core/src/main/java/com/jme3/scene/Node.java
  12. 13
      jme3-core/src/main/java/com/jme3/system/AppSettings.java
  13. 4
      jme3-core/src/main/java/com/jme3/system/JmeSystemDelegate.java
  14. 5
      jme3-core/src/main/java/com/jme3/system/Platform.java
  15. 2
      jme3-core/src/main/java/com/jme3/texture/Image.java
  16. 2
      jme3-core/src/main/resources/Common/MatDefs/Gui/Gui.frag
  17. 15
      jme3-core/src/main/resources/Common/MatDefs/Gui/Gui.j3md
  18. 2
      jme3-core/src/main/resources/Common/MatDefs/Gui/Gui.vert
  19. 44
      jme3-core/src/main/resources/joystick-mapping.properties
  20. 4
      jme3-core/src/plugins/java/com/jme3/texture/plugins/HDRLoader.java
  21. 2
      jme3-desktop/src/main/java/com/jme3/system/NativeLibraryLoader.java
  22. 80
      jme3-examples/src/main/java/jme3test/network/TestChatClient.java
  23. 74
      jme3-examples/src/main/java/jme3test/network/TestChatClientAndServer.java
  24. 113
      jme3-examples/src/main/java/jme3test/network/TestChatServer.java
  25. 6
      jme3-jogl/build.gradle
  26. 89
      jme3-jogl/src/main/java/com/jme3/input/jogl/NewtMouseInput.java
  27. 9
      jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglGL.java
  28. 11
      jme3-jogl/src/main/java/com/jme3/system/jogl/JoglAbstractDisplay.java
  29. 2
      jme3-jogl/src/main/java/com/jme3/system/jogl/JoglContext.java
  30. 11
      jme3-jogl/src/main/java/com/jme3/system/jogl/JoglNewtAbstractDisplay.java
  31. 6
      jme3-jogl/src/main/java/com/jme3/system/jogl/JoglOffscreenBuffer.java
  32. 49
      jme3-networking/src/main/java/com/jme3/network/base/DefaultClient.java
  33. 71
      jme3-networking/src/main/java/com/jme3/network/base/DefaultServer.java
  34. 2
      jme3-networking/src/main/java/com/jme3/network/base/MessageProtocol.java
  35. 22
      jme3-networking/src/main/java/com/jme3/network/message/SerializerRegistrationsMessage.java
  36. 5
      jme3-networking/src/main/java/com/jme3/network/serializing/Serializer.java
  37. 2
      jme3-networking/src/main/java/com/jme3/network/service/AbstractService.java
  38. 30
      jme3-networking/src/main/java/com/jme3/network/service/ServiceManager.java
  39. 56
      jme3-networking/src/main/java/com/jme3/network/service/rmi/Asynchronous.java
  40. 60
      jme3-networking/src/main/java/com/jme3/network/service/rmi/CallType.java
  41. 112
      jme3-networking/src/main/java/com/jme3/network/service/rmi/ClassInfo.java
  42. 104
      jme3-networking/src/main/java/com/jme3/network/service/rmi/ClassInfoRegistry.java
  43. 137
      jme3-networking/src/main/java/com/jme3/network/service/rmi/MethodInfo.java
  44. 87
      jme3-networking/src/main/java/com/jme3/network/service/rmi/RemoteObjectHandler.java
  45. 195
      jme3-networking/src/main/java/com/jme3/network/service/rmi/RmiClientService.java
  46. 60
      jme3-networking/src/main/java/com/jme3/network/service/rmi/RmiContext.java
  47. 262
      jme3-networking/src/main/java/com/jme3/network/service/rmi/RmiHostedService.java
  48. 387
      jme3-networking/src/main/java/com/jme3/network/service/rmi/RmiRegistry.java
  49. 20
      jme3-networking/src/main/java/com/jme3/network/service/serializer/ClientSerializerRegistrationsService.java
  50. 1
      sdk/jme3-core/src/com/jme3/gde/core/properties/TextureBrowser.form
  51. 43
      sdk/jme3-core/src/com/jme3/gde/core/properties/TextureBrowser.java
  52. 7
      sdk/jme3-core/src/com/jme3/gde/core/properties/TexturePropertyEditor.java
  53. 140
      sdk/jme3-core/src/com/jme3/gde/core/properties/preview/DDSPreview.java
  54. 179
      sdk/jme3-core/src/com/jme3/gde/core/properties/preview/TexturePreview.java
  55. 9
      sdk/jme3-core/src/com/jme3/gde/core/properties/preview/tex3DThumb.frag
  56. 4
      sdk/jme3-core/src/com/jme3/gde/core/properties/preview/tex3DThumb.vert
  57. 25
      sdk/jme3-materialeditor/src/com/jme3/gde/materials/multiview/widgets/TexturePanel.java
  58. 62
      sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/TerrainEditorTopComponent.java
  59. 3
      sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/TerrainToolController.java
  60. 8
      sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/sky/AddSkyboxAction.java
  61. 2
      sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/sky/Bundle.properties
  62. 54
      sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/sky/SkyboxVisualPanel2.form
  63. 193
      sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/sky/SkyboxVisualPanel2.java
  64. 3
      sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/sky/SkyboxWizardPanel2.java

1
.gitignore vendored

@ -146,3 +146,4 @@
/sdk/nbi/stub/ext/components/products/jdk/build/
/sdk/nbi/stub/ext/components/products/jdk/dist/
/sdk/jme3-dark-laf/nbproject/private/
jme3-lwjgl3/build/

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

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

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

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

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

@ -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;

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

@ -737,8 +737,8 @@ public class PhysicsRigidBody extends PhysicsCollisionObject {
setKinematic(capsule.readBoolean("kinematic", false));
setRestitution(capsule.readFloat("restitution", 0));
Vector3f angularFactor = (Vector3f) capsule.readSavable("angularFactor", Vector3f.NAN.clone());
if(angularFactor == Vector3f.NAN) {
Vector3f angularFactor = (Vector3f) capsule.readSavable("angularFactor", null);
if(angularFactor == null) {
setAngularFactor(capsule.readFloat("angularFactor", 1));
} else {
setAngularFactor(angularFactor);

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

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

@ -112,13 +112,22 @@ public final class AppSettings extends HashMap<String, Object> {
public static final String ANDROID_OPENAL_SOFT = "OpenAL_SOFT";
/**
* Use JogAmp's JOGL as the display system
* Use JogAmp's JOGL as the display system, with the OpenGL forward compatible profile
* <p>
* N.B: This backend is EXPERIMENTAL
*
* @see AppSettings#setRenderer(java.lang.String)
*/
public static final String JOGL = "JOGL";
public static final String JOGL_OPENGL_FORWARD_COMPATIBLE = "JOGL_OPENGL_FORWARD_COMPATIBLE";
/**
* Use JogAmp's JOGL as the display system with the backward compatible profile
* <p>
* N.B: This backend is EXPERIMENTAL
*
* @see AppSettings#setRenderer(java.lang.String)
*/
public static final String JOGL_OPENGL_BACKWARD_COMPATIBLE = "JOGL_OPENGL_BACKWARD_COMPATIBLE";
/**
* Use JogAmp's JOAL as the display system

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

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

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

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

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

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

@ -69,3 +69,47 @@ 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
# Logitech F310 gamepad with dip switch DirectInput
Logitech\ Dual\ Action.1=2
Logitech\ Dual\ Action.2=1
Logitech\ Dual\ Action.3=0
Logitech\ Dual\ Action.0=3
# Logitech F310 gamepad with dip switch XInput
Gamepad\ F310\ (Controller).0=2
Gamepad\ F310\ (Controller).1=1
Gamepad\ F310\ (Controller).2=3
Gamepad\ F310\ (Controller).3=0
Gamepad\ F310\ (Controller).6=8
Gamepad\ F310\ (Controller).7=9
Gamepad\ F310\ (Controller).8=10
Gamepad\ F310\ (Controller).9=11
Gamepad\ F310\ (Controller).rx=z
Gamepad\ F310\ (Controller).ry=rz
# requires custom code to support trigger buttons but this
# keeps it from confusing the .rx mapping.
Gamepad\ F310\ (Controller).z=trigger
# Alternate version of the XBOX 360 controller
XBOX\ 360\ For\ Windows\ (Controller).0=2
XBOX\ 360\ For\ Windows\ (Controller).1=1
XBOX\ 360\ For\ Windows\ (Controller).2=3
XBOX\ 360\ For\ Windows\ (Controller).3=0
XBOX\ 360\ For\ Windows\ (Controller).6=8
XBOX\ 360\ For\ Windows\ (Controller).7=9
XBOX\ 360\ For\ Windows\ (Controller).8=10
XBOX\ 360\ For\ Windows\ (Controller).9=11
XBOX\ 360\ For\ Windows\ (Controller).rx=z
XBOX\ 360\ For\ Windows\ (Controller).ry=rz
# requires custom code to support trigger buttons but this
# keeps it from confusing the .rx mapping.
XBOX\ 360\ For\ Windows\ (Controller).z=trigger

@ -308,8 +308,8 @@ public class HDRLoader implements AssetLoader {
}
in.close();
dataStore.rewind();
//TODO, HDR color space? considered linear here
dataStore.rewind();
//HDR files color data is actually stored in linear space.
return new Image(pixelFormat, width, height, dataStore, ColorSpace.Linear);
}

@ -149,7 +149,7 @@ public final class NativeLibraryLoader {
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.Linux64, "native/linux/libglfw.so");
registerNativeLibrary("glfw-lwjgl3", Platform.MacOSX32, "native/macosx/libglfw.dylib");
registerNativeLibrary("glfw-lwjgl3", Platform.MacOSX64, "native/macosx/libglfw.dylib");

@ -32,6 +32,8 @@
package jme3test.network;
import com.jme3.network.Client;
import com.jme3.network.ClientStateListener;
import com.jme3.network.ErrorListener;
import com.jme3.network.Message;
import com.jme3.network.MessageListener;
import com.jme3.network.Network;
@ -51,11 +53,11 @@ import jme3test.network.TestChatServer.ChatMessage;
*/
public class TestChatClient extends JFrame {
private Client client;
private JEditorPane chatLog;
private StringBuilder chatMessages = new StringBuilder();
private JTextField nameField;
private JTextField messageField;
private final Client client;
private final JEditorPane chatLog;
private final StringBuilder chatMessages = new StringBuilder();
private final JTextField nameField;
private final JTextField messageField;
public TestChatClient(String host) throws IOException {
super("jME3 Test Chat Client - to:" + host);
@ -90,7 +92,20 @@ public class TestChatClient extends JFrame {
client = Network.connectToServer(TestChatServer.NAME, TestChatServer.VERSION,
host, TestChatServer.PORT, TestChatServer.UDP_PORT);
client.addMessageListener(new ChatHandler(), ChatMessage.class);
client.addClientStateListener(new ChatClientStateListener());
client.addErrorListener(new ChatErrorListener());
client.start();
System.out.println("Started client:" + client);
}
@Override
public void dispose() {
System.out.println("Chat window closing.");
super.dispose();
if( client.isConnected() ) {
client.close();
}
}
public static String getString(Component owner, String title, String message, String initialValue) {
@ -99,7 +114,12 @@ public class TestChatClient extends JFrame {
}
public static void main(String... args) throws Exception {
TestChatServer.initializeClasses();
// Note: in JME 3.1 this is generally unnecessary as the server will
// send a message with all server-registered classes.
// TestChatServer.initializeClasses();
// Leaving the call commented out to be illustrative regarding the
// common old pattern.
// Grab a host string from the user
String s = getString(null, "Host Info", "Enter chat host:", "localhost");
@ -108,12 +128,23 @@ public class TestChatClient extends JFrame {
return;
}
// Register a shutdown hook to get a message on the console when the
// app actually finishes
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
System.out.println("Chat client is terminating.");
}
});
TestChatClient test = new TestChatClient(s);
test.setVisible(true);
}
private class ChatHandler implements MessageListener<Client> {
@Override
public void messageReceived(Client source, Message m) {
ChatMessage chat = (ChatMessage) m;
@ -134,15 +165,50 @@ public class TestChatClient extends JFrame {
}
}
private class ChatClientStateListener implements ClientStateListener {
@Override
public void clientConnected(Client c) {
System.out.println("clientConnected(" + c + ")");
}
@Override
public void clientDisconnected(Client c, DisconnectInfo info) {
System.out.println("clientDisconnected(" + c + "):" + info);
if( info != null ) {
// The connection was closed by the server
JOptionPane.showMessageDialog(rootPane,
info.reason,
"Connection Closed",
JOptionPane.INFORMATION_MESSAGE);
dispose();
}
}
}
private class ChatErrorListener implements ErrorListener<Client> {
@Override
public void handleError( Client source, Throwable t ) {
System.out.println("handleError(" + source + ", " + t + ")");
JOptionPane.showMessageDialog(rootPane,
String.valueOf(t),
"Connection Error",
JOptionPane.ERROR_MESSAGE);
}
}
private class SendAction extends AbstractAction {
private boolean reliable;
private final boolean reliable;
public SendAction(boolean reliable) {
super(reliable ? "TCP" : "UDP");
this.reliable = reliable;
}
@Override
public void actionPerformed(ActionEvent evt) {
String name = nameField.getText();
String message = messageField.getText();

@ -0,0 +1,74 @@
/*
* Copyright (c) 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 jme3test.network;
/**
* Combines the server instance and a client instance into the
* same JVM to show an example of, and to test, a pattern like
* self-hosted multiplayer games.
*
* @author Paul Speed
*/
public class TestChatClientAndServer {
public static void main( String... args ) throws Exception {
System.out.println("Starting chat server...");
TestChatServer chatServer = new TestChatServer();
chatServer.start();
System.out.println("Waiting for connections on port:" + TestChatServer.PORT);
// Now launch a client
TestChatClient test = new TestChatClient("localhost");
test.setVisible(true);
// Register a shutdown hook to get a message on the console when the
// app actually finishes
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
System.out.println("Client and server test is terminating.");
}
});
// Keep running basically forever or until the server
// shuts down
while( chatServer.isRunning() ) {
synchronized (chatServer) {
chatServer.wait();
}
}
}
}

@ -34,6 +34,7 @@ package jme3test.network;
import com.jme3.network.*;
import com.jme3.network.serializing.Serializable;
import com.jme3.network.serializing.Serializer;
import java.io.IOException;
/**
* A simple test chat server. When SM implements a set
@ -51,51 +52,134 @@ public class TestChatServer {
public static final int PORT = 5110;
public static final int UDP_PORT = 5110;
public static void initializeClasses() {
// Doing it here means that the client code only needs to
// call our initialize.
Serializer.registerClass(ChatMessage.class);
}
public static void main(String... args) throws Exception {
private Server server;
private boolean isRunning;
public TestChatServer() throws IOException {
initializeClasses();
// Use this to test the client/server name version check
Server server = Network.createServer(NAME, VERSION, PORT, UDP_PORT);
server.start();
this.server = Network.createServer(NAME, VERSION, PORT, UDP_PORT);
ChatHandler handler = new ChatHandler();
server.addMessageListener(handler, ChatMessage.class);
server.addConnectionListener(new ChatConnectionListener());
}
public boolean isRunning() {
return isRunning;
}
public synchronized void start() {
if( isRunning ) {
return;
}
server.start();
isRunning = true;
}
public synchronized void close() {
if( !isRunning ) {
return;
}
// Gracefully let any connections know that the server is
// going down. Without this, their connections will simply
// error out.
for( HostedConnection conn : server.getConnections() ) {
conn.close("Server is shutting down.");
}
try {
Thread.sleep(1000); // wait a couple beats to let the messages go out
} catch( InterruptedException e ) {
e.printStackTrace();
}
server.close();
isRunning = false;
notifyAll();
}
protected void runCommand( HostedConnection conn, String user, String command ) {
if( "/shutdown".equals(command) ) {
server.broadcast(new ChatMessage("server", "Server is shutting down."));
close();
} else if( "/help".equals(command) ) {
StringBuilder sb = new StringBuilder();
sb.append("Chat commands:\n");
sb.append("/help - prints this message.\n");
sb.append("/shutdown - shuts down the server.");
server.broadcast(new ChatMessage("server", sb.toString()));
}
}
public static void initializeClasses() {
// Doing it here means that the client code only needs to
// call our initialize.
Serializer.registerClass(ChatMessage.class);
}
public static void main(String... args) throws Exception {
TestChatServer chatServer = new TestChatServer();
chatServer.start();
System.out.println("Waiting for connections on port:" + PORT);
// Keep running basically forever
synchronized (NAME) {
NAME.wait();
while( chatServer.isRunning ) {
synchronized (chatServer) {
chatServer.wait();
}
}
}
private static class ChatHandler implements MessageListener<HostedConnection> {
private class ChatHandler implements MessageListener<HostedConnection> {
public ChatHandler() {
}
@Override
public void messageReceived(HostedConnection source, Message m) {
if (m instanceof ChatMessage) {
// Keep track of the name just in case we
// want to know it for some other reason later and it's
// a good example of session data
source.setAttribute("name", ((ChatMessage) m).getName());
ChatMessage cm = (ChatMessage)m;
source.setAttribute("name", cm.getName());
// Check for a / command
if( cm.message.startsWith("/") ) {
runCommand(source, cm.name, cm.message);
return;
}
System.out.println("Broadcasting:" + m + " reliable:" + m.isReliable());
// Just rebroadcast... the reliable flag will stay the
// same so if it came in on UDP it will go out on that too
source.getServer().broadcast(m);
source.getServer().broadcast(cm);
} else {
System.err.println("Received odd message:" + m);
}
}
}
private class ChatConnectionListener implements ConnectionListener {
@Override
public void connectionAdded( Server server, HostedConnection conn ) {
System.out.println("connectionAdded(" + conn + ")");
}
@Override
public void connectionRemoved(Server server, HostedConnection conn) {
System.out.println("connectionRemoved(" + conn + ")");
}
}
@Serializable
public static class ChatMessage extends AbstractMessage {
@ -126,6 +210,7 @@ public class TestChatServer {
return message;
}
@Override
public String toString() {
return name + ":" + message;
}

@ -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'
}

@ -50,6 +50,8 @@ import com.jogamp.nativewindow.util.DimensionImmutable;
import com.jogamp.nativewindow.util.PixelFormat;
import com.jogamp.nativewindow.util.PixelRectangle;
import com.jogamp.nativewindow.util.Point;
import com.jogamp.newt.event.WindowAdapter;
import com.jogamp.newt.event.WindowEvent;
public class NewtMouseInput implements MouseInput, MouseListener {
@ -106,40 +108,67 @@ public class NewtMouseInput implements MouseInput, MouseListener {
component = comp;
component.addMouseListener(this);
component.addWindowListener(new WindowAdapter(){
@Override
public void windowGainedFocus(WindowEvent e) {
setCursorVisible(visible);
}
@Override
public void windowLostFocus(WindowEvent e) {
//without those lines,
//on Linux (OpenBox) the mouse is not restored if invisible (eg via Alt-Tab)
component.setPointerVisible(true);
component.confinePointer(false);
}
});
}
@Override
public void initialize() {
}
@Override
public void destroy() {
}
@Override
public boolean isInitialized() {
return true;
}
@Override
public void setInputListener(RawInputListener listener) {
this.listener = listener;
}
@Override
public long getInputTimeNanos() {
return System.nanoTime();
}
@Override
public void setCursorVisible(boolean visible) {
if (this.visible != visible) {
lastKnownLocation.setX(0);
lastKnownLocation.setY(0);
this.visible = visible;
component.setPointerVisible(visible);
if (!visible) {
recenterMouse(component);
}
}
lastKnownLocation.setX(0);
lastKnownLocation.setY(0);
this.visible = visible;
component.setPointerVisible(visible);
component.confinePointer(!visible);
hack_confinePointer();
}
private void hack_confinePointer() {
if (component.hasFocus() && component.isPointerConfined() && !component.isPointerVisible()) {
recenterMouse(component);
}
}
@Override
public void update() {
if (!component.hasFocus()) return;
if (cursorMoved) {
int newX = location.getX();
int newY = location.getY();
@ -147,7 +176,7 @@ public class NewtMouseInput implements MouseInput, MouseListener {
// invert DY
int actualX = lastKnownLocation.getX();
int actualY = component.getHeight() - lastKnownLocation.getY();
int actualY = component.getSurfaceHeight() - lastKnownLocation.getY();
MouseMotionEvent evt = new MouseMotionEvent(actualX, actualY,
newX - lastEventX,
lastEventY - newY,
@ -173,43 +202,46 @@ public class NewtMouseInput implements MouseInput, MouseListener {
}
}
@Override
public int getButtonCount() {
return 3;
}
@Override
public void mouseClicked(MouseEvent awtEvt) {
// MouseButtonEvent evt = new MouseButtonEvent(getJMEButtonIndex(arg0), false);
// listener.onMouseButtonEvent(evt);
}
@Override
public void mousePressed(MouseEvent newtEvt) {
MouseButtonEvent evt = new MouseButtonEvent(getJMEButtonIndex(newtEvt), true, newtEvt.getX(), newtEvt.getY());
MouseButtonEvent evt = new MouseButtonEvent(getJMEButtonIndex(newtEvt), true, newtEvt.getX(), component.getSurfaceHeight() - newtEvt.getY());
evt.setTime(newtEvt.getWhen());
synchronized (eventQueue) {
eventQueue.add(evt);
}
}
public void mouseReleased(MouseEvent awtEvt) {
MouseButtonEvent evt = new MouseButtonEvent(getJMEButtonIndex(awtEvt), false, awtEvt.getX(), awtEvt.getY());
@Override
public void mouseReleased(MouseEvent awtEvt) {
MouseButtonEvent evt = new MouseButtonEvent(getJMEButtonIndex(awtEvt), false, awtEvt.getX(), component.getSurfaceHeight() - awtEvt.getY());
evt.setTime(awtEvt.getWhen());
synchronized (eventQueue) {
eventQueue.add(evt);
}
}
@Override
public void mouseEntered(MouseEvent awtEvt) {
if (!visible) {
recenterMouse(component);
}
hack_confinePointer();
}
@Override
public void mouseExited(MouseEvent awtEvt) {
if (!visible) {
recenterMouse(component);
}
hack_confinePointer();
}
@Override
public void mouseWheelMoved(MouseEvent awtEvt) {
//FIXME not sure this is the right way to handle this case
// [0] should be used when the shift key is down
@ -218,10 +250,12 @@ public class NewtMouseInput implements MouseInput, MouseListener {
cursorMoved = true;
}
@Override
public void mouseDragged(MouseEvent awtEvt) {
mouseMoved(awtEvt);
}
@Override
public void mouseMoved(MouseEvent awtEvt) {
if (isRecentering) {
// MHenze (cylab) Fix Issue 35:
@ -239,22 +273,20 @@ public class NewtMouseInput implements MouseInput, MouseListener {
int dy = awtEvt.getY() - lastKnownLocation.getY();
location.setX(location.getX() + dx);
location.setY(location.getY() + dy);
if (!visible) {
recenterMouse(component);
}
hack_confinePointer();
lastKnownLocation.setX(awtEvt.getX());
lastKnownLocation.setY(awtEvt.getY());
cursorMoved = true;
}
}
// MHenze (cylab) Fix Issue 35: A method to generate recenter the mouse to allow the InputSystem to "grab" the mouse
private void recenterMouse(final GLWindow component) {
eventsSinceRecenter = 0;
isRecentering = true;
centerLocation.setX(component.getWidth() / 2);
centerLocation.setY(component.getHeight() / 2);
centerLocation.setX(component.getSurfaceWidth() / 2);
centerLocation.setY(component.getSurfaceHeight() / 2);
centerLocationOnScreen.setX(centerLocation.getX());
centerLocationOnScreen.setY(centerLocation.getY());
@ -287,12 +319,13 @@ public class NewtMouseInput implements MouseInput, MouseListener {
return index;
}
@Override
public void setNativeCursor(JmeCursor cursor) {
final ByteBuffer pixels = Buffers.copyIntBufferAsByteBuffer(cursor.getImagesData());
final DimensionImmutable size = new Dimension(cursor.getWidth(), cursor.getHeight());
final PixelFormat pixFormat = PixelFormat.RGBA8888;
final PixelRectangle.GenericPixelRect rec = new PixelRectangle.GenericPixelRect(pixFormat, size, 0, true, pixels);
final PointerIcon joglCursor = component.getScreen().getDisplay().createPointerIcon(rec, cursor.getXHotSpot(), cursor.getYHotSpot());
final PointerIcon joglCursor = component.getScreen().getDisplay().createPointerIcon(rec, cursor.getXHotSpot(), cursor.getHeight() - cursor.getYHotSpot());
component.setPointerIcon(joglCursor);
}
}
}

@ -532,6 +532,15 @@ public class JoglGL implements GL, GL2, GL3, GL4 {
@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);
}

@ -37,6 +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.system.AppSettings;
import com.jogamp.opengl.util.Animator;
import com.jogamp.opengl.util.AnimatorBase;
import com.jogamp.opengl.util.FPSAnimator;
@ -80,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());

@ -167,7 +167,7 @@ public abstract class JoglContext implements JmeContext {
"required for jMonkeyEngine");
}
if (settings.getRenderer().equals("JOGL")) {
if (settings.getRenderer().startsWith("JOGL")) {
com.jme3.renderer.opengl.GL gl = new JoglGL();
GLExt glext = new JoglGLExt();
GLFbo glfbo = new JoglGLFbo();

@ -37,6 +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.system.AppSettings;
import com.jogamp.newt.opengl.GLWindow;
import com.jogamp.opengl.util.Animator;
import com.jogamp.opengl.util.AnimatorBase;
@ -73,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());

@ -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();

@ -67,18 +67,18 @@ public class DefaultClient implements Client
private static final int CH_UNRELIABLE = 1;
private static final int CH_FIRST = 2;
private ThreadLocal<ByteBuffer> dataBuffer = new ThreadLocal<ByteBuffer>();
private final ThreadLocal<ByteBuffer> dataBuffer = new ThreadLocal<ByteBuffer>();
private int id = -1;
private boolean isRunning = false;
private CountDownLatch connecting = new CountDownLatch(1);
private final CountDownLatch connecting = new CountDownLatch(1);
private String gameName;
private int version;
private MessageListenerRegistry<Client> messageListeners = new MessageListenerRegistry<Client>();
private List<ClientStateListener> stateListeners = new CopyOnWriteArrayList<ClientStateListener>();
private List<ErrorListener<? super Client>> errorListeners = new CopyOnWriteArrayList<ErrorListener<? super Client>>();
private Redispatch dispatcher = new Redispatch();
private List<ConnectorAdapter> channels = new ArrayList<ConnectorAdapter>();
private final MessageListenerRegistry<Client> messageListeners = new MessageListenerRegistry<Client>();
private final List<ClientStateListener> stateListeners = new CopyOnWriteArrayList<ClientStateListener>();
private final List<ErrorListener<? super Client>> errorListeners = new CopyOnWriteArrayList<ErrorListener<? super Client>>();
private final Redispatch dispatcher = new Redispatch();
private final List<ConnectorAdapter> channels = new ArrayList<ConnectorAdapter>();
private ConnectorFactory connectorFactory;
@ -100,6 +100,7 @@ public class DefaultClient implements Client
}
protected void addStandardServices() {
log.fine("Adding standard services...");
services.addService(new ClientSerializerRegistrationsService());
}
@ -128,6 +129,7 @@ public class DefaultClient implements Client
throw new IllegalStateException( "Client is not started." );
}
@Override
public void start()
{
if( isRunning )
@ -179,6 +181,7 @@ public class DefaultClient implements Client
}
}
@Override
public boolean isStarted() {
return isRunning;
}
@ -195,33 +198,42 @@ public class DefaultClient implements Client
}
}
@Override
public boolean isConnected()
{
return id != -1 && isRunning;
}
@Override
public int getId()
{
return id;
}
@Override
public String getGameName()
{
return gameName;
}
@Override
public int getVersion()
{
return version;
}
@Override
public ClientServiceManager getServices()
{
return services;
}
@Override
public void send( Message message )
{
if( log.isLoggable(Level.FINER) ) {
log.log(Level.FINER, "send({0})", message);
}
if( message.isReliable() || channels.get(CH_UNRELIABLE) == null ) {
send(CH_RELIABLE, message, true);
} else {
@ -229,8 +241,12 @@ public class DefaultClient implements Client
}
}
@Override
public void send( int channel, Message message )
{
if( log.isLoggable(Level.FINER) ) {
log.log(Level.FINER, "send({0}, {1})", new Object[]{channel, message});
}
if( channel >= 0 ) {
// Make sure we aren't still connecting. Channels
// won't be valid until we are fully connected since
@ -275,6 +291,7 @@ public class DefaultClient implements Client
channels.get(channel).write(buffer);
}
@Override
public void close()
{
checkRunning();
@ -287,9 +304,11 @@ public class DefaultClient implements Client
if( !isRunning )
return;
// Let the services get a chance to stop before we
// kill the connection.
services.stop();
if( services.isStarted() ) {
// Let the services get a chance to stop before we
// kill the connection.
services.stop();
}
// Send a close message
@ -313,41 +332,49 @@ public class DefaultClient implements Client
services.terminate();
}
@Override
public void addClientStateListener( ClientStateListener listener )
{
stateListeners.add( listener );
}
@Override
public void removeClientStateListener( ClientStateListener listener )
{
stateListeners.remove( listener );
}
@Override
public void addMessageListener( MessageListener<? super Client> listener )
{
messageListeners.addMessageListener( listener );
}
@Override
public void addMessageListener( MessageListener<? super Client> listener, Class... classes )
{
messageListeners.addMessageListener( listener, classes );
}
@Override
public void removeMessageListener( MessageListener<? super Client> listener )
{
messageListeners.removeMessageListener( listener );
}
@Override
public void removeMessageListener( MessageListener<? super Client> listener, Class... classes )
{
messageListeners.removeMessageListener( listener, classes );
}
@Override
public void addErrorListener( ErrorListener<? super Client> listener )
{
errorListeners.add( listener );
}
@Override
public void removeErrorListener( ErrorListener<? super Client> listener )
{
errorListeners.remove( listener );
@ -461,11 +488,13 @@ public class DefaultClient implements Client
protected class Redispatch implements MessageListener<Object>, ErrorListener<Object>
{
@Override
public void messageReceived( Object source, Message m )
{
dispatch( m );
}
@Override
public void handleError( Object source, Throwable t )
{
// Only doing the DefaultClient.this to make the code

@ -66,26 +66,26 @@ public class DefaultServer implements Server
private static final int CH_FIRST = 2;
private boolean isRunning = false;
private AtomicInteger nextId = new AtomicInteger(0);
private final AtomicInteger nextId = new AtomicInteger(0);
private String gameName;
private int version;
private KernelFactory kernelFactory = KernelFactory.DEFAULT;
private final KernelFactory kernelFactory = KernelFactory.DEFAULT;
private KernelAdapter reliableAdapter;
private KernelAdapter fastAdapter;
private List<KernelAdapter> channels = new ArrayList<KernelAdapter>();
private List<Integer> alternatePorts = new ArrayList<Integer>();
private Redispatch dispatcher = new Redispatch();
private Map<Integer,HostedConnection> connections = new ConcurrentHashMap<Integer,HostedConnection>();
private Map<Endpoint,HostedConnection> endpointConnections
private final List<KernelAdapter> channels = new ArrayList<KernelAdapter>();
private final List<Integer> alternatePorts = new ArrayList<Integer>();
private final Redispatch dispatcher = new Redispatch();
private final Map<Integer,HostedConnection> connections = new ConcurrentHashMap<Integer,HostedConnection>();
private final Map<Endpoint,HostedConnection> endpointConnections
= new ConcurrentHashMap<Endpoint,HostedConnection>();
// Keeps track of clients for whom we've only received the UDP
// registration message
private Map<Long,Connection> connecting = new ConcurrentHashMap<Long,Connection>();
private final Map<Long,Connection> connecting = new ConcurrentHashMap<Long,Connection>();
private MessageListenerRegistry<HostedConnection> messageListeners
private final MessageListenerRegistry<HostedConnection> messageListeners
= new MessageListenerRegistry<HostedConnection>();
private List<ConnectionListener> connectionListeners = new CopyOnWriteArrayList<ConnectionListener>();
private final List<ConnectionListener> connectionListeners = new CopyOnWriteArrayList<ConnectionListener>();
private HostedServiceManager services;
@ -108,24 +108,29 @@ public class DefaultServer implements Server
}
protected void addStandardServices() {
log.fine("Adding standard services...");
services.addService(new ServerSerializerRegistrationsService());
}
@Override
public String getGameName()
{
return gameName;
}
@Override
public int getVersion()
{
return version;
}
@Override
public HostedServiceManager getServices()
{
return services;
}
@Override
public int addChannel( int port )
{
if( isRunning )
@ -164,6 +169,7 @@ public class DefaultServer implements Server
}
}
@Override
public void start()
{
if( isRunning )
@ -185,11 +191,13 @@ public class DefaultServer implements Server
services.start();
}
@Override
public boolean isRunning()
{
return isRunning;
}
@Override
public void close()
{
if( !isRunning )
@ -214,13 +222,19 @@ public class DefaultServer implements Server
}
}
@Override
public void broadcast( Message message )
{
broadcast( null, message );
}
@Override
public void broadcast( Filter<? super HostedConnection> filter, Message message )
{
if( log.isLoggable(Level.FINER) ) {
log.log(Level.FINER, "broadcast({0}, {1})", new Object[]{filter, message});
}
if( connections.isEmpty() )
return;
@ -237,8 +251,13 @@ public class DefaultServer implements Server
}
}
@Override
public void broadcast( int channel, Filter<? super HostedConnection> filter, Message message )
{
if( log.isLoggable(Level.FINER) ) {
log.log(Level.FINER, "broadcast({0}, {1}. {2})", new Object[]{channel, filter, message});
}
if( connections.isEmpty() )
return;
@ -251,46 +270,55 @@ public class DefaultServer implements Server
channels.get(channel+CH_FIRST).broadcast( adapter, buffer, true, false );
}
@Override
public HostedConnection getConnection( int id )
{
return connections.get(id);
}
@Override
public boolean hasConnections()
{
return !connections.isEmpty();
}
@Override
public Collection<HostedConnection> getConnections()
{
return Collections.unmodifiableCollection((Collection<HostedConnection>)connections.values());
}
@Override
public void addConnectionListener( ConnectionListener listener )
{
connectionListeners.add(listener);
}
@Override
public void removeConnectionListener( ConnectionListener listener )
{
connectionListeners.remove(listener);
}
@Override
public void addMessageListener( MessageListener<? super HostedConnection> listener )
{
messageListeners.addMessageListener( listener );
}
@Override
public void addMessageListener( MessageListener<? super HostedConnection> listener, Class... classes )
{
messageListeners.addMessageListener( listener, classes );
}
@Override
public void removeMessageListener( MessageListener<? super HostedConnection> listener )
{
messageListeners.removeMessageListener( listener );
}
@Override
public void removeMessageListener( MessageListener<? super HostedConnection> listener, Class... classes )
{
messageListeners.removeMessageListener( listener, classes );
@ -484,12 +512,12 @@ public class DefaultServer implements Server
protected class Connection implements HostedConnection
{
private int id;
private final int id;
private boolean closed;
private Endpoint[] channels;
private int setChannelCount = 0;
private Map<String,Object> sessionData = new ConcurrentHashMap<String,Object>();
private final Map<String,Object> sessionData = new ConcurrentHashMap<String,Object>();
public Connection( int channelCount )
{
@ -523,23 +551,30 @@ public class DefaultServer implements Server
return setChannelCount == channels.length;
}
@Override
public Server getServer()
{
return DefaultServer.this;
}
@Override
public int getId()
{
return id;
}
@Override
public String getAddress()
{
return channels[CH_RELIABLE] == null ? null : channels[CH_RELIABLE].getAddress();
}
@Override
public void send( Message message )
{
if( log.isLoggable(Level.FINER) ) {
log.log(Level.FINER, "send({0})", message);
}
ByteBuffer buffer = MessageProtocol.messageToBuffer(message, null);
if( message.isReliable() || channels[CH_UNRELIABLE] == null ) {
channels[CH_RELIABLE].send( buffer );
@ -548,8 +583,12 @@ public class DefaultServer implements Server
}
}
@Override
public void send( int channel, Message message )
{
if( log.isLoggable(Level.FINER) ) {
log.log(Level.FINER, "send({0}, {1})", new Object[]{channel, message});
}
checkChannel(channel);
ByteBuffer buffer = MessageProtocol.messageToBuffer(message, null);
channels[channel+CH_FIRST].send(buffer);
@ -573,6 +612,7 @@ public class DefaultServer implements Server
fireConnectionRemoved( this );
}
@Override
public void close( String reason )
{
// Send a reason
@ -593,6 +633,7 @@ public class DefaultServer implements Server
}
}
@Override
public Object setAttribute( String name, Object value )
{
if( value == null )
@ -601,11 +642,13 @@ public class DefaultServer implements Server
}
@SuppressWarnings("unchecked")
@Override
public <T> T getAttribute( String name )
{
return (T)sessionData.get(name);
}
@Override
public Set<String> attributeNames()
{
return Collections.unmodifiableSet(sessionData.keySet());
@ -621,6 +664,7 @@ public class DefaultServer implements Server
protected class Redispatch implements MessageListener<HostedConnection>
{
@Override
public void messageReceived( HostedConnection source, Message m )
{
dispatch( source, m );
@ -629,13 +673,14 @@ public class DefaultServer implements Server
protected class FilterAdapter implements Filter<Endpoint>
{
private Filter<? super HostedConnection> delegate;
private final Filter<? super HostedConnection> delegate;
public FilterAdapter( Filter<? super HostedConnection> delegate )
{
this.delegate = delegate;
}
@Override
public boolean apply( Endpoint input )
{
HostedConnection conn = getConnection( input );

@ -53,7 +53,7 @@ import java.util.LinkedList;
*/
public class MessageProtocol
{
private LinkedList<Message> messages = new LinkedList<Message>();
private final LinkedList<Message> messages = new LinkedList<Message>();
private ByteBuffer current;
private int size;
private Byte carry;

@ -94,7 +94,7 @@ public class SerializerRegistrationsMessage extends AbstractMessage {
public static Registration[] compiled;
private static final Serializer fieldSerializer = new FieldSerializer();
private Registration[] registrations;
private Registration[] registrations;
public SerializerRegistrationsMessage() {
setReliable(true);
@ -132,7 +132,25 @@ public class SerializerRegistrationsMessage extends AbstractMessage {
Serializer.setReadOnly(true);
}
public void registerAll() {
public void registerAll() {
// See if we will have problems because our registry is locked
if( Serializer.isReadOnly() ) {
// Check to see if maybe we are executing this from the
// same JVM that sent the message, ie: client and server are running on
// the same JVM
// There could be more advanced checks than this but for now we'll
// assume that if the registry was compiled here then it means
// we are also the server process. Note that this wouldn't hold true
// under complicated examples where there are clients of one server
// that also run their own servers but realistically they would have
// to disable the ServerSerializerRegistrationsServer anyway.
if( compiled != null ) {
log.log( Level.INFO, "Skipping registration as registry is locked, presumably by a local server process.");
return;
}
}
for( Registration reg : registrations ) {
log.log( Level.INFO, "Registering:{0}", reg);
reg.register();

@ -45,6 +45,7 @@ import java.nio.ByteBuffer;
import java.util.*;
import java.util.jar.Attributes;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;
/**
@ -403,6 +404,10 @@ public abstract class Serializer {
if (reg == null) {
throw new SerializerException( "Class not registered:" + type );
}
if( log.isLoggable(Level.FINER) ) {
log.log(Level.FINER, "writing class:{0} with ID:{1}", new Object[]{type, reg.getId()});
}
buffer.putShort(reg.getId());
return reg;
}

@ -106,6 +106,6 @@ public abstract class AbstractService<S extends ServiceManager> implements Servi
@Override
public String toString() {
return getClass().getName() + "[serviceManager.class=" + serviceManager.getClass() + "]";
return getClass().getName() + "[serviceManager.class=" + (serviceManager != null ? serviceManager.getClass() : "") + "]";
}
}

@ -34,6 +34,8 @@ package com.jme3.network.service;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* The base service manager class from which the HostedServiceManager
@ -44,7 +46,9 @@ import java.util.concurrent.CopyOnWriteArrayList;
*/
public abstract class ServiceManager<T> {
private List<Service<T>> services = new CopyOnWriteArrayList<Service<T>>();
static final Logger log = Logger.getLogger(ServiceManager.class.getName());
private final List<Service<T>> services = new CopyOnWriteArrayList<Service<T>>();
private volatile boolean started = false;
protected ServiceManager() {
@ -76,6 +80,9 @@ public abstract class ServiceManager<T> {
return;
}
for( Service<T> s : services ) {
if( log.isLoggable(Level.FINE) ) {
log.log(Level.FINE, "Starting service:{0}", s);
}
s.start();
}
started = true;
@ -96,6 +103,9 @@ public abstract class ServiceManager<T> {
throw new IllegalStateException(getClass().getSimpleName() + " not started.");
}
for( Service<T> s : services ) {
if( log.isLoggable(Level.FINE) ) {
log.log(Level.FINE, "Stopping service:{0}", s);
}
s.stop();
}
started = false;
@ -106,9 +116,18 @@ public abstract class ServiceManager<T> {
* has already been started then the service will also be started.
*/
public <S extends Service<T>> void addService( S s ) {
if( log.isLoggable(Level.FINE) ) {
log.log(Level.FINE, "addService({0})", s);
}
services.add(s);
if( log.isLoggable(Level.FINE) ) {
log.log(Level.FINE, "Initializing service:{0}", s);
}
s.initialize(getParent());
if( started ) {
if( log.isLoggable(Level.FINE) ) {
log.log(Level.FINE, "Starting service:{0}", s);
}
s.start();
}
}
@ -120,10 +139,19 @@ public abstract class ServiceManager<T> {
* the service will be terminated.
*/
public <S extends Service<T>> void removeService( S s ) {
if( log.isLoggable(Level.FINE) ) {
log.log(Level.FINE, "removeService({0})", s);
}
if( started ) {
if( log.isLoggable(Level.FINE) ) {
log.log(Level.FINE, "Stopping service:{0}", s);
}
s.stop();
}
services.remove(s);
if( log.isLoggable(Level.FINE) ) {
log.log(Level.FINE, "Terminating service:{0}", s);
}
s.terminate(getParent());
}

@ -0,0 +1,56 @@
/*
* Copyright (c) 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.network.service.rmi;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
/**
* Indicates that a given method should be executed asynchronously
* through the RMI service. This must annotate the method on the
* shared interface for it to have an effect. If reliable=false
* is specified then remote method invocation is done over UDP
* instead of TCP, ie: unreliably... but faster.
*
* @author Paul Speed
*/
@Retention(value=RUNTIME)
@Target(value=METHOD)
public @interface Asynchronous {
boolean reliable() default true;
}

@ -0,0 +1,60 @@
/*
* Copyright (c) 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.network.service.rmi;
/**
* Internal type denoting the type of call to make when remotely
* invoking methods.
*
* @author Paul Speed
*/
public enum CallType {
/**
* Caller will block until a response is received and returned.
*/
Synchronous,
/**
* Caller does not block or wait for a response. The other end
* of the connection will also not send one.
*/
Asynchronous,
/**
* Similar to asynchronous in that no response is expected or sent
* but differs in that the call will be sent over UDP and so may
* not make it to the other end.
*/
Unreliable
}

@ -0,0 +1,112 @@
/*
* Copyright (c) 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.network.service.rmi;
import com.jme3.network.serializing.Serializable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/**
* Internal information about a shared class. This is the information
* that is sent over the wire for shared types.
*
* @author Paul Speed
*/
@Serializable
public final class ClassInfo {
private String name;
private short typeId;
private MethodInfo[] methods;
/**
* For serialization only.
*/
public ClassInfo() {
}
public ClassInfo( short typeId, Class type ) {
this.typeId = typeId;
this.name = type.getName();
this.methods = toMethodInfo(type, type.getMethods());
}
public String getName() {
return name;
}
public Class getType() {
try {
return Class.forName(name);
} catch( ClassNotFoundException e ) {
throw new RuntimeException("Error finding class for:" + this, e);
}
}
public short getId() {
return typeId;
}
public MethodInfo getMethod( short id ) {
return methods[id];
}
public MethodInfo getMethod( Method m ) {
for( MethodInfo mi : methods ) {
if( mi.matches(m) ) {
return mi;
}
}
return null;
}
private MethodInfo[] toMethodInfo( Class type, Method[] methods ) {
List<MethodInfo> result = new ArrayList<MethodInfo>();
short methodId = 0;
for( Method m : methods ) {
// Simple... add all methods exposed through the interface
result.add(new MethodInfo(methodId++, m));
}
return result.toArray(new MethodInfo[result.size()]);
}
public MethodInfo[] getMethods() {
return methods;
}
@Override
public String toString() {
return "ClassInfo[" + name + "]";
}
}

@ -0,0 +1,104 @@
/*
* Copyright (c) 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.network.service.rmi;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* Internal registry of shared types and their ClassInfo and MethodInfo
* objects.
*
* @author Paul Speed
*/
public class ClassInfoRegistry {
//private final LoadingCache<Class, ClassInfo> cache; // Guava version
private final Map<Class, ClassInfo> cache = new HashMap<Class, ClassInfo>();
private final AtomicInteger nextClassId = new AtomicInteger();
private final ReadWriteLock lock = new ReentrantReadWriteLock();
public ClassInfoRegistry() {
//this.cache = CacheBuilder.newBuilder().build(new ClassInfoLoader()); // Guava version
}
public ClassInfo getClassInfo( Class type ) {
//return cache.getUnchecked(type); // Guava version
// More complicated without guava
lock.readLock().lock();
try {
ClassInfo result = cache.get(type);
if( result != null ) {
return result;
}
// Else we need to create it and store it... so grab the write
// lock
lock.readLock().unlock();
lock.writeLock().lock();
try {
// Note: it's technically possible that a race with another thread
// asking for the same class already created one between our read unlock
// and our write lock. No matter as it's cheap to create one and does
// no harm. Code is simpler without the double-check.
result = new ClassInfo((short)nextClassId.getAndIncrement(), type);
cache.put(type, result);
// Regrab the read lock before leaving... kind of unnecessary but
// it makes the method cleaner and widens the gap of lock races.
// Downgrading a write lock to read is ok.
lock.readLock().lock();
return result;
} finally {
// Unlock the write lock while still holding onto read
lock.writeLock().unlock();
}
} finally {
lock.readLock().unlock();
}
}
/*
would be more straight-forward with guava Guava version
private class ClassInfoLoader extends CacheLoader<Class, ClassInfo> {
@Override
public ClassInfo load( Class type ) {
return new ClassInfo((short)nextClassId.getAndIncrement(), type);
}
}*/
}

@ -0,0 +1,137 @@
/*
* Copyright (c) 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.network.service.rmi;
import com.jme3.network.serializing.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import javax.jws.Oneway;
/**
* Internal information about shared methods. This is part of the data that
* is passed over the wire when an object is shared.
*
* @author Paul Speed
*/
@Serializable
public final class MethodInfo {
public static final MethodInfo NULL_INFO = new MethodInfo();
private String representation;
private short id;
private CallType callType;
private transient Method method;
/**
* For serialization only.
*/
public MethodInfo() {
}
public MethodInfo( short id, Method m ) {
this.id = id;
this.method = m;
this.representation = methodToString(m);
this.callType = getCallType(m);
}
public Object invoke( Object target, Object... parms ) {
try {
return method.invoke(target, parms);
} catch (IllegalAccessException e) {
throw new RuntimeException("Error invoking:" + method + " on:" + target, e);
} catch (IllegalArgumentException e) {
throw new RuntimeException("Error invoking:" + method + " on:" + target, e);
} catch (InvocationTargetException e) {
throw new RuntimeException("Error invoking:" + method + " on:" + target, e);
}
}
public short getId() {
return id;
}
public CallType getCallType() {
return callType;
}
public boolean matches( Method m ) {
return representation.equals(methodToString(m));
}
public static String methodToString( Method m ) {
StringBuilder sb = new StringBuilder();
for( Class t : m.getParameterTypes() ) {
if( sb.length() > 0 )
sb.append(", ");
sb.append(t.getName());
}
return m.getReturnType().getName() + " " + m.getName() + "(" + sb + ")";
}
public static CallType getCallType( Method m ) {
if( m.getReturnType() != Void.TYPE )
return CallType.Synchronous;
if( m.getAnnotation(Oneway.class) != null )
return CallType.Asynchronous;
if( m.getAnnotation(Asynchronous.class) == null )
return CallType.Synchronous;
Asynchronous async = m.getAnnotation(Asynchronous.class);
return async.reliable() ? CallType.Asynchronous : CallType.Unreliable;
}
@Override
public int hashCode() {
return representation.hashCode();
}
@Override
public boolean equals( Object o ) {
if( o == this ) {
return true;
}
if( o == null || o.getClass() != getClass() ) {
return false;
}
MethodInfo other = (MethodInfo)o;
return representation.equals(other.representation);
}
@Override
public String toString() {
return "MethodInfo[#" + getId() + ", callType=" + callType + ", " + representation + "]";
}
}

@ -0,0 +1,87 @@
/*
* Copyright (c) 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.network.service.rmi;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Used internally to remotely invoke methods on RMI shared objects.
*
* @author Paul Speed
*/
public class RemoteObjectHandler implements InvocationHandler {
private final RmiRegistry rmi;
private final byte channel;
private final short objectId;
private final ClassInfo typeInfo;
private final Map<Method, MethodInfo> methodIndex = new ConcurrentHashMap<Method, MethodInfo>();
public RemoteObjectHandler( RmiRegistry rmi, byte channel, short objectId, ClassInfo typeInfo ) {
this.rmi = rmi;
this.channel = channel;
this.objectId = objectId;
this.typeInfo = typeInfo;
}
protected MethodInfo getMethodInfo( Method method ) {
MethodInfo mi = methodIndex.get(method);
if( mi == null ) {
mi = typeInfo.getMethod(method);
if( mi == null ) {
mi = MethodInfo.NULL_INFO;
}
methodIndex.put(method, mi);
}
return mi == MethodInfo.NULL_INFO ? null : mi;
}
@Override
public Object invoke(Object o, Method method, Object[] os) throws Throwable {
MethodInfo mi = getMethodInfo(method);
if( mi == null ) {
// Try to invoke locally
return method.invoke(this, os);
}
return rmi.invokeRemote(channel, objectId, mi.getId(), mi.getCallType(), os);
}
@Override
public String toString() {
return "RemoteObject[#" + objectId + ", " + typeInfo.getName() + "]";
}
}

@ -0,0 +1,195 @@
/*
* Copyright (c) 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.network.service.rmi;
import com.jme3.network.MessageConnection;
import com.jme3.network.service.AbstractClientService;
import com.jme3.network.service.ClientServiceManager;
import com.jme3.network.service.rpc.RpcClientService;
import java.util.ArrayList;
import java.util.List;
/**
* A service that can be added to the client to support a simple
* shared objects protocol.
*
* <p>Objects are shared by adding them to the RmiRegistry with one of the
* share() methods. Shared objects must have a separate interface and implementation.
* The interface is what the other end of the connection will use to interact
* with the object and that interface class must be available on both ends of
* the connection. The implementing class need only be on the sharing end.</p>
*
* <p>Shared objects can be accessed on the other end of the connection by
* using one of the RmiRegistry's getRemoteObject() methods. These can be
* used to lookup an object by class if it is a shared singleton or by name
* if it was registered with a name.</p>
*
* <p>Note: This RMI implementation is not as advanced as Java's regular
* RMI as it won't marshall shared references, ie: you can't pass
* a shared objects as an argument to another shared object's method.</p>
*
* @author Paul Speed
*/
public class RmiClientService extends AbstractClientService {
private RpcClientService rpc;
private byte defaultChannel;
private short rmiObjectId;
private RmiRegistry rmi;
private volatile boolean isStarted = false;
private final List<ObjectInfo> pending = new ArrayList<ObjectInfo>();
public RmiClientService() {
this((short)-1, (byte)MessageConnection.CHANNEL_DEFAULT_RELIABLE);
}
public RmiClientService( short rmiObjectId, byte defaultChannel ) {
this.defaultChannel = defaultChannel;
this.rmiObjectId = rmiObjectId;
}
/**
* Shares the specified object with the server and associates it with the
* specified type. Objects shared in this way are available in the connection-specific
* RMI registry on the server and are not available to other connections.
*/
public <T> void share( T object, Class<? super T> type ) {
share(defaultChannel, object, type);
}
/**
* Shares the specified object with the server and associates it with the
* specified type. Objects shared in this way are available in the connection-specific
* RMI registry on the server and are not available to other connections.
* All object related communication will be done over the specified connection
* channel.
*/
public <T> void share( byte channel, T object, Class<? super T> type ) {
share(channel, type.getName(), object, type);
}
/**
* Shares the specified object with the server and associates it with the
* specified name. Objects shared in this way are available in the connection-specific
* RMI registry on the server and are not available to other connections.
*/
public <T> void share( String name, T object, Class<? super T> type ) {
share(defaultChannel, name, object, type);
}
/**
* Shares the specified object with the server and associates it with the
* specified name. Objects shared in this way are available in the connection-specific
* RMI registry on the server and are not available to other connections.
* All object related communication will be done over the specified connection
* channel.
*/
public <T> void share( byte channel, String name, T object, Class<? super T> type ) {
if( !isStarted ) {
synchronized(pending) {
if( !isStarted ) {
pending.add(new ObjectInfo(channel, name, object, type));
return;
}
}
}
// Else we can add it directly.
rmi.share(channel, name, object, type);
}
/**
* Looks up a remote object on the server by type and returns a local proxy to the
* remote object that was shared on the other end of the network connection.
*/
public <T> T getRemoteObject( Class<T> type ) {
return rmi.getRemoteObject(type);
}
/**
* Looks up a remote object on the server by name and returns a local proxy to the
* remote object that was shared on the other end of the network connection.
*/
public <T> T getRemoteObject( String name, Class<T> type ) {
return rmi.getRemoteObject(name, type);
}
@Override
protected void onInitialize( ClientServiceManager s ) {
rpc = getService(RpcClientService.class);
if( rpc == null ) {
throw new RuntimeException("RmiClientService requires RpcClientService");
}
// Register it now so that it is available when the
// server starts to send us stuff. Waiting until start()
// is too late in this case.
rmi = new RmiRegistry(rpc.getRpcConnection(), rmiObjectId, defaultChannel);
}
@Override
public void start() {
super.start();
// Register all of the classes that have been waiting.
synchronized(pending) {
for( ObjectInfo info : pending ) {
rmi.share(info.channel, info.name, info.object, info.type);
}
pending.clear();
isStarted = true;
}
}
private class ObjectInfo {
byte channel;
String name;
Object object;
Class type;
public ObjectInfo( byte channel, String name, Object object, Class type ) {
this.channel = channel;
this.name = name;
this.object = object;
this.type = type;
}
@Override
public String toString() {
return "ObjectInfo[" + channel + ", " + name + ", " + object + ", " + type + "]";
}
}
}

@ -0,0 +1,60 @@
/*
* Copyright (c) 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.network.service.rmi;
import com.jme3.network.HostedConnection;
/**
* Keeps track of the current connection performing a particular
* RMI call. RMI-based services can use this to find out which
* connection is calling a particular method without having to
* pass additional problematic data on the method calls.
*
* @author Paul Speed
*/
public class RmiContext {
private static final ThreadLocal<HostedConnection> connection = new ThreadLocal<HostedConnection>();
/**
* Returns the HostedConnection that is responsible for any
* RMI-related calls on this thread.
*/
public static HostedConnection getRmiConnection() {
return connection.get();
}
static void setRmiConnection( HostedConnection conn ) {
connection.set(conn);
}
}

@ -0,0 +1,262 @@
/*
* Copyright (c) 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.network.service.rmi;
import com.jme3.network.HostedConnection;
import com.jme3.network.MessageConnection;
import com.jme3.network.Server;
import com.jme3.network.serializing.Serializer;
import com.jme3.network.service.AbstractHostedService;
import com.jme3.network.service.HostedServiceManager;
import com.jme3.network.service.rpc.RpcHostedService;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A service that can be added to the host to support a simple
* shared objects protocol.
*
* <p>Objects are shared by adding them to the RmiRegistry with one of the
* share() methods. Shared objects must have a separate interface and implementation.
* The interface is what the other end of the connection will use to interact
* with the object and that interface class must be available on both ends of
* the connection. The implementing class need only be on the sharing end.</p>
*
* <p>Shared objects can be accessed on the other end of the connection by
* using one of the RmiRegistry's getRemoteObject() methods. These can be
* used to lookup an object by class if it is a shared singleton or by name
* if it was registered with a name.</p>
*
* <p>On the hosting side, a special shardGlobal() method is provided that
* will register shared objects that will automatically be provided to every
* new joining client and they will all be calling the same server-side instance.
* Normally, shared objects themselves are connection specific and handled
* at the connection layer. The shareGlobal() space is a way to have global
* resources passed directly though the need is relatively rare.</p>
*
* <p>Note: This RMI implementation is not as advanced as Java's regular
* RMI as it won't marshall shared references, ie: you can't pass
* a shared objects as an argument to another shared object's method.</p>
*
* @author Paul Speed
*/
public class RmiHostedService extends AbstractHostedService {
static final Logger log = Logger.getLogger(RpcHostedService.class.getName());
public static final String ATTRIBUTE_NAME = "rmi";
private RpcHostedService rpcService;
private short rmiId;
private byte defaultChannel;
private boolean autoHost;
private final Map<String, GlobalShare> globalShares = new ConcurrentHashMap<String, GlobalShare>();
public RmiHostedService() {
this((short)-1, (byte)MessageConnection.CHANNEL_DEFAULT_RELIABLE, true);
}
public RmiHostedService( short rmiId, byte defaultChannel, boolean autoHost ) {
this.rmiId = rmiId;
this.defaultChannel = defaultChannel;
this.autoHost = autoHost;
Serializer.registerClasses(ClassInfo.class, MethodInfo.class);
}
/**
* Shares a server-wide object associated with the specified type. All connections
* with RMI hosting started will have access to this shared object as soon as they
* connect and they will all share the same instance. It is up to the shared object
* to handle any multithreading that might be required.
*/
public <T> void shareGlobal( T object, Class<? super T> type ) {
shareGlobal(defaultChannel, type.getName(), object, type);
}
/**
* Shares a server-wide object associated with the specified name. All connections
* with RMI hosting started will have access to this shared object as soon as they
* connect and they will all share the same instance. It is up to the shared object
* to handle any multithreading that might be required.
*/
public <T> void shareGlobal( String name, T object, Class<? super T> type ) {
shareGlobal(defaultChannel, name, object, type);
}
/**
* Shares a server-wide object associated with the specified name over the specified
* channel. All connections with RMI hosting started will have access to this shared
* object as soon as they connect and they will all share the same instance. It is up
* to the shared object to handle any multithreading that might be required.
* All network communcation associated with the shared object will be done over
* the specified channel.
*/
public <T> void shareGlobal( byte channel, String name, T object, Class<? super T> type ) {
GlobalShare share = new GlobalShare(channel, object, type);
GlobalShare existing = globalShares.put(name, share);
if( existing != null ) {
// Shouldn't need to do anything actually.
}
// Go through all of the children
for( HostedConnection conn : getServer().getConnections() ) {
RmiRegistry child = getRmiRegistry(conn);
if( child == null ) {
continue;
}
child.share(channel, name, object, type);
}
}
/**
* Set to true if all new connections should automatically have RMI hosting started.
* Set to false if the game-specific connection setup will call startHostingOnConnection()
* after some connection setup is done (for example, logging in). Note: generally
* is is safe to autohost RMI as long as callers are careful about what they've added
* using shareGlobal(). One reasonable use-case is to shareGlobal() some kind of login
* service and nothing else. All other shared objects would then be added as connection
* specific objects during successful login processing.
*/
public void setAutoHost( boolean b ) {
this.autoHost = b;
}
/**
* Returns true if RMI hosting is automatically started for all new connections.
*/
public boolean getAutoHost() {
return autoHost;
}
/**
* Returns the RMI registry for the specific HostedConection. Each connection
* has its own registry with its own connection-specific shared objects.
*/
public RmiRegistry getRmiRegistry( HostedConnection hc ) {
return hc.getAttribute(ATTRIBUTE_NAME);
}
/**
* Sets up RMI hosting services for the hosted connection allowing
* getRmiRegistry() to return a valid RmiRegistry object.
* This method is called automatically for all new connections if
* autohost is set to true.
*/
public void startHostingOnConnection( HostedConnection hc ) {
if( log.isLoggable(Level.FINEST) ) {
log.log(Level.FINEST, "startHostingOnConnection:{0}", hc);
}
RmiRegistry rmi = new RmiRegistry(hc, rpcService.getRpcConnection(hc),
rmiId, defaultChannel);
hc.setAttribute(ATTRIBUTE_NAME, rmi);
// Register any global shares
for( Map.Entry<String, GlobalShare> e : globalShares.entrySet() ) {
GlobalShare share = e.getValue();
rmi.share(share.channel, e.getKey(), share.object, share.type);
}
}
/**
* Removes any RMI hosting services associated with the specified
* connection. Calls to getRmiRegistry() will return null for
* this connection.
* This method is called automatically for all leaving connections if
* autohost is set to true.
*/
public void stopHostingOnConnection( HostedConnection hc ) {
RmiRegistry rmi = hc.getAttribute(ATTRIBUTE_NAME);
if( rmi == null ) {
return;
}
if( log.isLoggable(Level.FINEST) ) {
log.log(Level.FINEST, "stopHostingOnConnection:{0}", hc);
}
hc.setAttribute(ATTRIBUTE_NAME, null);
//rpc.close();
}
@Override
protected void onInitialize( HostedServiceManager s ) {
this.rpcService = getService(RpcHostedService.class);
if( rpcService == null ) {
throw new RuntimeException("RmiHostedService requires RpcHostedService");
}
}
/**
* Called internally when a new connection is detected for
* the server. If the current autoHost property is true then
* startHostingOnConnection(hc) is called.
*/
@Override
public void connectionAdded(Server server, HostedConnection hc) {
if( log.isLoggable(Level.FINEST) ) {
log.log(Level.FINEST, "connectionAdded({0}, {1})", new Object[]{server, hc});
}
if( autoHost ) {
startHostingOnConnection(hc);
}
}
/**
* Called internally when an existing connection is leaving
* the server. If the current autoHost property is true then
* stopHostingOnConnection(hc) is called.
*/
@Override
public void connectionRemoved(Server server, HostedConnection hc) {
if( log.isLoggable(Level.FINEST) ) {
log.log(Level.FINEST, "connectionRemoved({0}, {1})", new Object[]{server, hc});
}
if( autoHost ) {
stopHostingOnConnection(hc);
}
}
private class GlobalShare {
byte channel;
Object object;
Class type;
public GlobalShare( byte channel, Object object, Class type ) {
this.channel = channel;
this.object = object;
this.type = type;
}
}
}

@ -0,0 +1,387 @@
/*
* Copyright (c) 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.network.service.rmi;
import com.jme3.network.HostedConnection;
import com.jme3.network.MessageConnection;
import com.jme3.network.service.rpc.RpcConnection;
import com.jme3.network.service.rpc.RpcHandler;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
*
* @author Paul Speed
*/
public class RmiRegistry {
static final Logger log = Logger.getLogger(RmiRegistry.class.getName());
// RPC IDs for calling our remote endpoint
private static final short NEW_CLASS = 0;
private static final short ADD_OBJECT = 1;
private static final short REMOVE_OBJECT = 2;
private RpcConnection rpc;
private short rmiId;
private byte defaultChannel;
private final RmiHandler rmiHandler = new RmiHandler();
private final ClassInfoRegistry classCache = new ClassInfoRegistry();
private final AtomicInteger nextObjectId = new AtomicInteger();
private final ObjectIndex<SharedObject> local = new ObjectIndex<SharedObject>();
private final ObjectIndex<Object> remote = new ObjectIndex<Object>();
// Only used on the server to provide thread-local context for
// local RMI calls.
private HostedConnection context;
public RmiRegistry( RpcConnection rpc, short rmiId, byte defaultChannel ) {
this(null, rpc, rmiId, defaultChannel);
}
public RmiRegistry( HostedConnection context, RpcConnection rpc, short rmiId, byte defaultChannel ) {
this.context = context;
this.rpc = rpc;
this.rmiId = rmiId;
this.defaultChannel = defaultChannel;
rpc.registerHandler(rmiId, rmiHandler);
}
/**
* Exposes the specified object to the other end of the connection as
* the specified interface type. The object can be looked up by type
* on the other end.
*/
public <T> void share( T object, Class<? super T> type ) {
share(defaultChannel, object, type);
}
/**
* Exposes, through a specific connection channel, the specified object
* to the other end of the connection as the specified interface type.
* The object can be looked up by type on the other end.
* The specified channel will be used for all network communication
* specific to this object.
*/
public <T> void share( byte channel, T object, Class<? super T> type ) {
share(channel, type.getName(), object, type);
}
/**
* Exposes the specified object to the other end of the connection as
* the specified interface type and associates it with the specified name.
* The object can be looked up by the associated name on the other end of
* the connection.
*/
public <T> void share( String name, T object, Class<? super T> type ) {
share(defaultChannel, name, object, type);
}
/**
* Exposes, through a specific connection channel, the specified object to
* the other end of the connection as the specified interface type and associates
* it with the specified name.
* The object can be looked up by the associated name on the other end of
* the connection.
* The specified channel will be used for all network communication
* specific to this object.
*/
public <T> void share( byte channel, String name, T object, Class<? super T> type ) {
ClassInfo typeInfo = classCache.getClassInfo(type);
local.lock.writeLock().lock();
try {
// First see if we've told the remote end about this class
// before
if( local.classes.put(typeInfo.getId(), typeInfo) == null ) {
// It's new
rpc.callAsync(defaultChannel, rmiId, NEW_CLASS, typeInfo);
// Because type info IDs are global to the class cache,
// we could in theory keep a global index that we broadcast
// on first connection setup... we need only prepopulate
// the index in that case.
}
// See if we already shared an object under that name
SharedObject existing = local.byName.remove(name);
if( existing != null ) {
local.byId.remove(existing.objectId);
rpc.removeHandler(existing.objectId, rmiHandler);
// Need to delete the old one from the remote end
rpc.callAsync(defaultChannel, rmiId, REMOVE_OBJECT, existing.objectId);
// We don't reuse the ID because it's kind of dangerous.
// Churning through a new ID is our safety net for accidents.
}
SharedObject newShare = new SharedObject(name, object, type, typeInfo);
local.byName.put(name, newShare);
local.byId.put(newShare.objectId, newShare);
// Make sure we are setup to receive the remote method calls through
// the RPC service
rpc.registerHandler(newShare.objectId, rmiHandler);
// Let the other end know
rpc.callAsync(defaultChannel, rmiId, ADD_OBJECT, channel, newShare.objectId, name, typeInfo.getId());
// We send the ADD_OBJECT to the other end before releasing the
// lock to avoid a potential inconsistency if two threads try to
// jam the same name at the same time. Otherwise, if the timing were
// right, the remove for one object could get there before its add.
} finally {
local.lock.writeLock().unlock();
}
}
/**
* Returns a local object that was previously registered with share() using
* just type registration.
*/
public <T> T getLocalObject( Class<T> type ) {
return getLocalObject(type.getName(), type);
}
/**
* Returns a local object that was previously registered with share() using
* name registration.
*/
public <T> T getLocalObject( String name, Class<T> type ) {
local.lock.readLock().lock();
try {
return type.cast(local.byName.get(name));
} finally {
local.lock.readLock().unlock();
}
}
/**
* Looks up a remote object by type and returns a local proxy to the remote object
* that was shared on the other end of the network connection. If this is called
* from a client then it is accessing a shared object registered on the server.
* If this is called from the server then it is accessing a shared object registered
* on the client.
*/
public <T> T getRemoteObject( Class<T> type ) {
return getRemoteObject(type.getName(), type);
}
/**
* Looks up a remote object by name and returns a local proxy to the remote object
* that was shared on the other end of the network connection. If this is called
* from a client then it is accessing a shared object registered on the server.
* If this is called from the server then it is accessing a shared object registered
* on the client.
*/
public <T> T getRemoteObject( String name, Class<T> type ) {
remote.lock.readLock().lock();
try {
return type.cast(remote.byName.get(name));
} finally {
remote.lock.readLock().unlock();
}
}
protected void addRemoteClass( ClassInfo info ) {
if( remote.classes.put(info.getId(), info) != null ) {
throw new RuntimeException("Error class already exists for ID:" + info.getId());
}
}
protected void removeRemoteObject( short objectId ) {
if( log.isLoggable(Level.FINEST) ) {
log.log(Level.FINEST, "removeRemoteObject({0})", objectId);
}
throw new UnsupportedOperationException("Removal not yet implemented.");
}
protected void addRemoteObject( byte channel, short objectId, String name, ClassInfo typeInfo ) {
if( log.isLoggable(Level.FINEST) ) {
log.finest("addRemoveObject(" + objectId + ", " + name + ", " + typeInfo + ")");
}
remote.lock.writeLock().lock();
try {
Object existing = remote.byName.get(name);
if( existing != null ) {
throw new RuntimeException("Object already registered for:" + name);
}
RemoteObjectHandler remoteHandler = new RemoteObjectHandler(this, channel, objectId, typeInfo);
Object remoteObject = Proxy.newProxyInstance(getClass().getClassLoader(),
new Class[] {typeInfo.getType()},
remoteHandler);
remote.byName.put(name, remoteObject);
remote.byId.put(objectId, remoteObject);
} finally {
remote.lock.writeLock().unlock();
}
}
protected Object invokeRemote( byte channel, short objectId, short procId, CallType callType, Object[] args ) {
if( log.isLoggable(Level.FINEST) ) {
log.finest("invokeRemote(" + channel + ", " + objectId + ", " + procId + ", "
+ callType + ", " + (args == null ? "null" : Arrays.asList(args)) + ")");
}
switch( callType ) {
case Asynchronous:
log.finest("Sending reliable asynchronous.");
rpc.callAsync(channel, objectId, procId, args);
return null;
case Unreliable:
log.finest("Sending unreliable asynchronous.");
rpc.callAsync((byte)MessageConnection.CHANNEL_DEFAULT_UNRELIABLE, objectId, procId, args);
return null;
default:
case Synchronous:
log.finest("Sending synchronous.");
Object result = rpc.callAndWait(channel, objectId, procId, args);
if( log.isLoggable(Level.FINEST) ) {
log.finest("->got:" + result);
}
return result;
}
}
/**
* Handle remote object registry updates from the other end.
*/
protected void rmiUpdate( short procId, Object[] args ) {
if( log.isLoggable(Level.FINEST) ) {
log.finest("rmiUpdate(" + procId + ", " + Arrays.asList(args) + ")");
}
switch( procId ) {
case NEW_CLASS:
addRemoteClass((ClassInfo)args[0]);
break;
case REMOVE_OBJECT:
removeRemoteObject((Short)args[0]);
break;
case ADD_OBJECT:
ClassInfo info = remote.classes.get((Short)args[3]);
addRemoteObject((Byte)args[0], (Short)args[1], (String)args[2], info);
break;
}
}
/**
* Handle the actual remote object method calls.
*/
protected Object invokeLocal( short objectId, short procId, Object[] args ) {
// Actually could use a regular concurrent map for this
// Only lock the local registry during lookup and
// not invocation. It prevents a deadlock if the invoked method
// tries to share an object. It should be safe.
SharedObject share = local.byId.get(objectId);
local.lock.readLock().lock();
try {
share = local.byId.get(objectId);
} finally {
local.lock.readLock().unlock();
}
try {
RmiContext.setRmiConnection(context);
return share.invoke(procId, args);
} finally {
RmiContext.setRmiConnection(null);
}
}
private class SharedObject {
private final short objectId;
private final String name;
private final Object object;
private final Class type;
private final ClassInfo classInfo;
public SharedObject( String name, Object object, Class type, ClassInfo classInfo ) {
this.objectId = (short)nextObjectId.incrementAndGet();
this.name = name;
this.object = object;
this.type = type;
this.classInfo = classInfo;
}
public Object invoke( short procId, Object[] args ) {
return classInfo.getMethod(procId).invoke(object, args);
}
}
private class RmiHandler implements RpcHandler {
@Override
public Object call( RpcConnection conn, short objectId, short procId, Object... args ) {
if( objectId == rmiId ) {
rmiUpdate(procId, args);
return null;
} else {
return invokeLocal(objectId, procId, args);
}
}
}
/**
* Keeps a coincident index between short ID, name, and related class info.
* There will be one of these to track our local objects and one to track
* the remote objects and a lock that can guard them.
*/
private class ObjectIndex<T> {
final Map<String, T> byName = new HashMap<String, T>();
final Map<Short, T> byId = new HashMap<Short, T>();
final Map<Short, ClassInfo> classes = new HashMap<Short, ClassInfo>();
final ReadWriteLock lock = new ReentrantReadWriteLock();
public ObjectIndex() {
}
}
}

@ -39,6 +39,8 @@ import com.jme3.network.message.SerializerRegistrationsMessage;
import com.jme3.network.serializing.Serializer;
import com.jme3.network.service.AbstractClientService;
import com.jme3.network.service.ClientServiceManager;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
@ -48,19 +50,27 @@ import com.jme3.network.service.ClientServiceManager;
*/
public class ClientSerializerRegistrationsService extends AbstractClientService
implements MessageListener<Client> {
static final Logger log = Logger.getLogger(SerializerRegistrationsMessage.class.getName());
@Override
protected void onInitialize( ClientServiceManager serviceManager ) {
// Make sure our message type is registered
// This is the minimum we'd need just to be able to register
// the rest... otherwise we can't even receive this message.
Serializer.registerClass(SerializerRegistrationsMessage.class);
Serializer.registerClass(SerializerRegistrationsMessage.Registration.class);
// Make sure our message type is registered if it isn't already
if( Serializer.getExactSerializerRegistration(SerializerRegistrationsMessage.class) == null ) {
// This is the minimum we'd need just to be able to register
// the rest... otherwise we can't even receive this message.
Serializer.registerClass(SerializerRegistrationsMessage.class);
Serializer.registerClass(SerializerRegistrationsMessage.Registration.class);
} else {
log.log(Level.INFO, "Skipping registration of SerializerRegistrationsMessage.");
}
// Add our listener for that message type
serviceManager.getClient().addMessageListener(this, SerializerRegistrationsMessage.class);
}
@Override
public void messageReceived( Client source, Message m ) {
// We only wait for one kind of message...
SerializerRegistrationsMessage msg = (SerializerRegistrationsMessage)m;

@ -9,6 +9,7 @@
</Properties>
<SyntheticProperties>
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
<SyntheticProperty name="generateCenter" type="boolean" value="false"/>
</SyntheticProperties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>

@ -31,9 +31,8 @@
*/
package com.jme3.gde.core.properties;
import com.jme3.asset.TextureKey;
import com.jme3.gde.core.assets.ProjectAssetManager;
import com.jme3.gde.core.properties.preview.DDSPreview;
import com.jme3.gde.core.properties.preview.TexturePreview;
import com.jme3.gde.core.util.TreeUtil;
import com.jme3.texture.Texture;
import java.awt.event.MouseEvent;
@ -42,6 +41,7 @@ import java.awt.event.WindowListener;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.prefs.Preferences;
import javax.swing.DefaultListSelectionModel;
@ -52,8 +52,6 @@ import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import jme3tools.converters.ImageToAwt;
import org.openide.util.ImageUtilities;
/**
* Displays all textures in the ProjectAssetManager,
@ -68,7 +66,7 @@ public class TextureBrowser extends javax.swing.JDialog implements TreeSelection
private ProjectAssetManager assetManager;
private TexturePropertyEditor editor;
private DDSPreview ddsPreview;
private TexturePreview texPreview;
private Preferences prefs;
private static final String PREF_LAST_SELECTED = "lastSelectedTexture";
@ -234,8 +232,8 @@ private void noTexturebuttonActionPerformed(java.awt.event.ActionEvent evt) {//G
if (node != null && node.isLeaf()) {
String selected = TreeUtil.getPath(node.getUserObjectPath());
selected = selected.substring(0, selected.lastIndexOf("/"));
Texture tex = assetManager.loadTexture(selected);
editor.setValue(tex);
// Texture tex = assetManager.loadTexture(selected);
// editor.setValue(tex);
editor.setAsText(selected);
return true;
}
@ -270,7 +268,7 @@ private void noTexturebuttonActionPerformed(java.awt.event.ActionEvent evt) {//G
private void setSelectedTexture(Texture texture) {
if (texture != null) {
Logger.getLogger(TextureBrowser.class.getName()).finer("Looking for Texture: " + texture.getName());
Logger.getLogger(TextureBrowser.class.getName()).log(Level.FINER, "Looking for Texture: {0}", texture.getName());
String[] path = ("/" + texture.getName()).split("/");
TreePath parent = new TreePath((TreeNode) jTree1.getModel().getRoot());
TreePath selectedTreePath = TreeUtil.buildTreePath(jTree1, parent, path, 0, true);
@ -287,6 +285,7 @@ private void noTexturebuttonActionPerformed(java.awt.event.ActionEvent evt) {//G
}
}
@Override
public void valueChanged(TreeSelectionEvent e) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) jTree1.getLastSelectedPathComponent();
@ -295,25 +294,14 @@ private void noTexturebuttonActionPerformed(java.awt.event.ActionEvent evt) {//G
return;
}
//Object nodeInfo = node.getUserObject();
if (node.isLeaf()) {
String selected = TreeUtil.getPath(node.getUserObjectPath());
selected = selected.substring(0, selected.lastIndexOf("/"));
Icon newicon = null;
if (selected.toLowerCase().endsWith(".dds")) {
if (ddsPreview == null) {
ddsPreview = new DDSPreview(assetManager);
}
ddsPreview.requestPreview(selected, (String) node.getUserObject(), 450, 450, imagePreviewLabel, infoLabel);
} else {
Texture tex = assetManager.loadTexture(selected);
newicon = ImageUtilities.image2Icon(ImageToAwt.convert(tex.getImage(), false, true, 0));
assetManager.deleteFromCache(new TextureKey(selected));
imagePreviewLabel.setIcon(newicon);
infoLabel.setText(" " + node.getUserObject() + " w : " + newicon.getIconWidth() + " h : " + newicon.getIconHeight());
if (texPreview == null) {
texPreview = new TexturePreview(assetManager);
}
texPreview.requestPreview(selected, (String) node.getUserObject(), 450, 450, imagePreviewLabel, infoLabel);
prefs.put(PREF_LAST_SELECTED, selected);
} else {
imagePreviewLabel.setIcon(null);
@ -323,27 +311,34 @@ private void noTexturebuttonActionPerformed(java.awt.event.ActionEvent evt) {//G
}
@Override
public void windowOpened(WindowEvent e) {
}
@Override
public void windowClosing(WindowEvent e) {
if (ddsPreview != null) {
ddsPreview.cleanUp();
if (texPreview != null) {
texPreview.cleanUp();
}
}
@Override
public void windowClosed(WindowEvent e) {
}
@Override
public void windowIconified(WindowEvent e) {
}
@Override
public void windowDeiconified(WindowEvent e) {
}
@Override
public void windowActivated(WindowEvent e) {
}
@Override
public void windowDeactivated(WindowEvent e) {
}

@ -80,7 +80,14 @@ public class TexturePropertyEditor implements PropertyEditor {
}
}
@Override
public Object getValue() {
if(texture == null && assetKey != null){
if (manager == null){
manager = SceneApplication.getApplication().getAssetManager();
}
texture = manager.loadTexture(assetKey);
}
return texture;
}

@ -1,140 +0,0 @@
/*
* Copyright (c) 2009-2010 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.gde.core.properties.preview;
import com.jme3.asset.TextureKey;
import com.jme3.gde.core.assets.ProjectAssetManager;
import com.jme3.gde.core.scene.PreviewRequest;
import com.jme3.gde.core.scene.SceneApplication;
import com.jme3.gde.core.scene.SceneListener;
import com.jme3.gde.core.scene.SceneRequest;
import com.jme3.material.Material;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Quad;
import com.jme3.texture.Texture;
import com.jme3.util.SkyFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
/**
*
* @author Nehon
*/
public class DDSPreview implements SceneListener {
private final ProjectAssetManager assetManager;
private JComponent picPreview;
private final Geometry quad;
private final Geometry quad3D;
private final Material material;
private final Material material3D;
public DDSPreview(ProjectAssetManager assetManager) {
this.assetManager = assetManager;
Quad quadMesh = new Quad(4.5f, 4.5f);
Quad quadMesh3D = new Quad(4.5f, 4.5f);
quadMesh3D.scaleTextureCoordinates(new Vector2f(4, 4));
quad = new Geometry("previewQuad", quadMesh);
quad.setLocalTranslation(new Vector3f(-2.25f, -2.25f, 0));
quad3D = new Geometry("previewQuad", quadMesh3D);
quad3D.setLocalTranslation(new Vector3f(-2.25f, -2.25f, 0));
material3D = new Material(assetManager, "com/jme3/gde/core/properties/preview/tex3DThumb.j3md");
material3D.setFloat("InvDepth", 1f / 16f);
material3D.setInt("Rows", 4);
material = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
SceneApplication.getApplication().addSceneListener(this);
}
public void requestPreview(String textureName, String displayName, int width, int height, JComponent picLabel, JLabel infoLabel) {
TextureKey key = new TextureKey(textureName);
picPreview = picLabel;
assetManager.deleteFromCache(key);
Texture t = assetManager.loadTexture(key);
Spatial geom = quad;
if (key.getTextureTypeHint() == Texture.Type.TwoDimensional) {
material.setTexture("ColorMap", t);
geom.setMaterial(material);
if (infoLabel != null) {
infoLabel.setText(" " + displayName + " w : " + t.getImage().getWidth() + " h : " + t.getImage().getHeight());
}
} else if (key.getTextureTypeHint() == Texture.Type.ThreeDimensional) {
geom = quad3D;
assetManager.deleteFromCache(key);
key.setTextureTypeHint(Texture.Type.ThreeDimensional);
t = assetManager.loadTexture(key);
material3D.setTexture("Texture", t);
geom.setMaterial(material3D);
if (infoLabel != null) {
infoLabel.setText(" " + displayName + " (Texture3D) w : " + t.getImage().getWidth() + " h : " + t.getImage().getHeight() + " d : " + t.getImage().getDepth());
}
} else if (key.getTextureTypeHint() == Texture.Type.CubeMap) {
assetManager.deleteFromCache(key);
geom = SkyFactory.createSky(assetManager, textureName, SkyFactory.EnvMapType.CubeMap);
if (infoLabel != null) {
infoLabel.setText(" " + displayName + " (CubeMap) w : " + t.getImage().getWidth() + " h : " + t.getImage().getHeight());
}
}
PreviewRequest request = new PreviewRequest(this, geom, width, height);
request.getCameraRequest().setLocation(new Vector3f(0, 0, 5.3f));
request.getCameraRequest().setLookAt(new Vector3f(0, 0, 0), Vector3f.UNIT_Y.mult(-1));
SceneApplication.getApplication().createPreview(request);
}
public void cleanUp() {
SceneApplication.getApplication().removeSceneListener(this);
}
public void sceneOpened(SceneRequest request) {
}
public void sceneClosed(SceneRequest request) {
}
public void previewCreated(PreviewRequest request) {
if (request.getRequester() == this) {
final ImageIcon icon = new ImageIcon(request.getImage());
if (picPreview instanceof JLabel) {
((JLabel) picPreview).setIcon(icon);
}
if (picPreview instanceof JButton) {
((JButton) picPreview).setIcon(icon);
}
}
}
}

@ -0,0 +1,179 @@
/*
* Copyright (c) 2009-2010 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.gde.core.properties.preview;
import com.jme3.asset.TextureKey;
import com.jme3.gde.core.assets.ProjectAssetManager;
import com.jme3.gde.core.scene.PreviewRequest;
import com.jme3.gde.core.scene.SceneApplication;
import com.jme3.gde.core.scene.SceneListener;
import com.jme3.gde.core.scene.SceneRequest;
import com.jme3.material.Material;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Quad;
import com.jme3.texture.Texture;
import com.jme3.util.SkyFactory;
import java.util.concurrent.Callable;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
/**
*
* @author Nehon
*/
public class TexturePreview implements SceneListener {
private final ProjectAssetManager assetManager;
private JComponent picPreview;
private final Geometry quad;
private final Geometry quad3D;
private final Material material;
private final Material material3D;
public TexturePreview(ProjectAssetManager assetManager) {
this.assetManager = assetManager;
Quad quadMesh = new Quad(4.5f, 4.5f);
Quad quadMesh3D = new Quad(4.5f, 4.5f);
quadMesh3D.scaleTextureCoordinates(new Vector2f(4, 4));
quad = new Geometry("previewQuad", quadMesh);
quad.setLocalTranslation(new Vector3f(-2.25f, -2.25f, 0));
quad3D = new Geometry("previewQuad", quadMesh3D);
quad3D.setLocalTranslation(new Vector3f(-2.25f, -2.25f, 0));
material3D = new Material(assetManager, "com/jme3/gde/core/properties/preview/tex3DThumb.j3md");
material3D.setFloat("InvDepth", 1f / 16f);
material3D.setInt("Rows", 4);
material = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
SceneApplication.getApplication().addSceneListener(this);
}
public void requestPreview(final String textureName, final String displayName, final int width, final int height, final JComponent picLabel, final JLabel infoLabel) {
picPreview = picLabel;
clearPreview();
if (infoLabel != null) {
infoLabel.setText(" Creating preview...");
}
SceneApplication.getApplication().enqueue(new Callable<Void>() {
@Override
public Void call() throws Exception {
TextureKey key = new TextureKey(textureName);
Texture t = assetManager.loadTexture(key);
Spatial geom = quad;
if (key.getTextureTypeHint() == Texture.Type.TwoDimensional) {
material.setTexture("ColorMap", t);
geom.setMaterial(material);
setLabel(infoLabel, displayName, t.getImage().getWidth(), t.getImage().getHeight(), -1);
} else if (key.getTextureTypeHint() == Texture.Type.ThreeDimensional) {
geom = quad3D;
material3D.setTexture("Texture", t);
geom.setMaterial(material3D);
setLabel(infoLabel, displayName + " (Texture3D)", t.getImage().getWidth(), t.getImage().getHeight(), t.getImage().getDepth());
} else if (key.getTextureTypeHint() == Texture.Type.CubeMap) {
geom = SkyFactory.createSky(assetManager, textureName, SkyFactory.EnvMapType.CubeMap);
setLabel(infoLabel, displayName + " (CubeMap)", t.getImage().getWidth(), t.getImage().getHeight(), -1);
}
PreviewRequest request = new PreviewRequest(TexturePreview.this, geom, width, height);
request.getCameraRequest().setLocation(new Vector3f(0, 0, 5.3f));
request.getCameraRequest().setLookAt(new Vector3f(0, 0, 0), Vector3f.UNIT_Y.mult(-1));
SceneApplication.getApplication().createPreview(request);
return null;
}
});
}
public void cleanUp() {
SceneApplication.getApplication().removeSceneListener(this);
}
@Override
public void sceneOpened(SceneRequest request) {
}
@Override
public void sceneClosed(SceneRequest request) {
}
private void setLabel(final JLabel label, final String text, final int width, final int height, final int depth) {
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
if (label != null) {
String labText = " " + text + " w : " + width + " h : " + height;
if (depth > 0) {
labText += " d : " + depth;
}
label.setText(labText);
}
}
});
}
private void clearPreview() {
if (picPreview instanceof JLabel) {
((JLabel) picPreview).setIcon(null);
}
if (picPreview instanceof JButton) {
((JButton) picPreview).setIcon(null);
}
}
@Override
public void previewCreated(final PreviewRequest request) {
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
if (request.getRequester() == TexturePreview.this) {
final ImageIcon icon = new ImageIcon(request.getImage());
if (picPreview instanceof JLabel) {
((JLabel) picPreview).setIcon(icon);
}
if (picPreview instanceof JButton) {
((JButton) picPreview).setIcon(icon);
}
}
}
});
}
}

@ -5,10 +5,9 @@ uniform float m_InvDepth;
varying vec2 texCoord;
void main(){
float depthx=floor(texCoord.x);
float depthy=(m_Rows-1.0) - floor(texCoord.y);
//vec3 texC=vec3(texCoord.x,texCoord.y ,0.7);//
float depthx = floor(texCoord.x);
float depthy = (float(m_Rows) - 1.0) - floor(texCoord.y);
vec3 texC=vec3(fract(texCoord.x),fract(texCoord.y),(depthy*m_Rows+depthx)*m_InvDepth);//
gl_FragColor= texture3D(m_Texture,texC);
vec3 texC = vec3(fract(texCoord.x), fract(texCoord.y), (depthy * float(m_Rows) + depthx) * m_InvDepth);//
gl_FragColor = texture3D(m_Texture, texC);
}

@ -6,6 +6,6 @@ attribute vec3 inPosition;
varying vec2 texCoord;
void main(){
gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition,1.0);
texCoord=inTexCoord;
gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);
texCoord = inTexCoord;
}

@ -13,7 +13,7 @@ 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.core.properties.preview.TexturePreview;
import com.jme3.gde.materials.MaterialProperty;
import com.jme3.gde.materials.multiview.MaterialEditorTopComponent;
import com.jme3.texture.Texture;
@ -39,7 +39,7 @@ public class TexturePanel extends MaterialPropertyWidget {
private boolean flip = false;
private boolean repeat = false;
private String textureName = null;
private DDSPreview ddsPreview;
private TexturePreview texPreview;
private final ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1);
/** Creates new form SelectionPanel */
@ -53,22 +53,13 @@ public class TexturePanel extends MaterialPropertyWidget {
if (!"".equals(textureName)) {
exec.execute(new Runnable() {
@Override
public void run() {
try{
Texture tex = manager.loadTexture(textureName);
if (textureName.toLowerCase().endsWith(".dds")) {
if (ddsPreview == null) {
ddsPreview = new DDSPreview(manager);
if (texPreview == null) {
texPreview = new TexturePreview(manager);
}
ddsPreview.requestPreview(textureName, "", 80, 80, texturePreview, null);
} else {
final Icon newicon = ImageUtilities.image2Icon(resizeImage(ImageToAwt.convert(tex.getImage(), false, true, 0)));
SwingUtilities.invokeLater(new Runnable() {
public void run() {
texturePreview.setIcon(newicon);
}
});
}
texPreview.requestPreview(textureName, "", 80, 25, texturePreview, null);
} catch (AssetNotFoundException a) {
Logger.getLogger(MaterialEditorTopComponent.class.getName()).log(Level.WARNING, "Could not load texture {0}", textureName);
}
@ -318,8 +309,8 @@ public class TexturePanel extends MaterialPropertyWidget {
@Override
public void cleanUp() {
if (ddsPreview != null) {
ddsPreview.cleanUp();
if (texPreview != null) {
texPreview.cleanUp();
}
exec.shutdownNow();
}

@ -34,7 +34,7 @@ package com.jme3.gde.terraineditor;
import com.jme3.gde.core.assets.AssetDataObject;
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.core.properties.preview.TexturePreview;
import com.jme3.gde.core.scene.PreviewRequest;
import com.jme3.gde.core.scene.SceneApplication;
import com.jme3.gde.core.scene.SceneListener;
@ -122,7 +122,7 @@ public final class TerrainEditorTopComponent extends TopComponent implements Sce
//private TerrainNodeListener terrainDeletedNodeListener;
private boolean availableNormalTextures;
private HelpCtx ctx = new HelpCtx("sdk.terrain_editor");
private DDSPreview ddsPreview;
private TexturePreview texPreview;
private Map<String, JButton> buttons = new HashMap<String, JButton>();
private JPanel insideToolSettings;
@ -153,35 +153,29 @@ public final class TerrainEditorTopComponent extends TopComponent implements Sce
private float max = 0;
private final Object lock = new Object();
@Override
public void incrementProgress(float f) {
progress += f;
progressHandle.progress((int) progress);
}
@Override
public void setMonitorMax(float f) {
max = f;
// java.awt.EventQueue.invokeLater(new Runnable() {
// public void run() {
// synchronized(lock){
if (progressHandle == null) {
progressHandle = ProgressHandleFactory.createHandle("Calculating terrain entropies...");
progressHandle.start((int) max);
}
// }
// }
// });
}
@Override
public float getMonitorMax() {
return max;
}
@Override
public void progressComplete() {
// SwingUtilities.invokeLater(new Runnable() {
// public void run() {
progressHandle.finish();
// }
// });
}
}
@ -1631,6 +1625,13 @@ public final class TerrainEditorTopComponent extends TopComponent implements Sce
protected abstract Texture getTextureFromModel(int index);
protected abstract boolean supportsNullTexture();
private TexturePreview getTexturePreview(){
if (texPreview == null) {
texPreview = new TexturePreview((ProjectAssetManager) SceneApplication.getApplication().getAssetManager());
}
return texPreview;
}
private JButton getButton(Object value, final int row, final int column) {
@ -1656,21 +1657,9 @@ public final class TerrainEditorTopComponent extends TopComponent implements Sce
}
Texture tex = getTextureFromModel(index); // delegate to sub-class
//Texture tex = SceneApplication.getApplication().getAssetManager().loadTexture((String)value);
if (tex != null) {
String selected = tex.getKey().getName();
if (selected.toLowerCase().endsWith(".dds")) {
if (ddsPreview == null) {
ddsPreview = new DDSPreview((ProjectAssetManager) SceneApplication.getApplication().getAssetManager());
}
ddsPreview.requestPreview(selected, "", 80, 80, lbl, null);
} else {
Icon icon = ImageUtilities.image2Icon(ImageToAwt.convert(tex.getImage(), false, true, 0));
lbl.setIcon(icon);
}
getTexturePreview().requestPreview(selected, "", 80, 80, lbl, null);
}
}
@ -1694,24 +1683,17 @@ public final class TerrainEditorTopComponent extends TopComponent implements Sce
TexturePropertyEditor editor = new TexturePropertyEditor(selectedTex);
Component view = editor.getCustomEditor();
view.setVisible(true);
Texture tex = (Texture) editor.getValue();
if (editor.getValue() != null) {
String selected = tex.getKey().getName();
if (selected.toLowerCase().endsWith(".dds")) {
if (ddsPreview == null) {
ddsPreview = new DDSPreview((ProjectAssetManager) SceneApplication.getApplication().getAssetManager());
}
ddsPreview.requestPreview(selected, "", 80, 80, lbl, null);
} else {
Icon newicon = ImageUtilities.image2Icon(ImageToAwt.convert(tex.getImage(), false, true, 0));
lbl.setIcon(newicon);
}
if (editor.getAsText() != null) {
String selected = editor.getAsText();
getTexturePreview().requestPreview(selected, "", 80, 80, lbl, null);
Texture tex = SceneApplication.getApplication().getAssetManager().loadTexture(selected);
setTextureInModel(row, tex);
} else if (supportsNullTexture()) {
lbl.setIcon(null);
}
setTextureInModel(row, tex);
} finally {
alreadyChoosing = false;
}

@ -260,8 +260,7 @@ public class TerrainToolController extends SceneToolController {
* The action on the tool has ended (mouse button up), record the Undo (for painting only now)
*/
void doTerrainEditToolActionEnded() {
if (terrainTool != null) {
System.out.println("undo tagged");
if (terrainTool != null) {
terrainTool.actionEnded(jmeRootNode, editorController.getCurrentDataObject());
}
}

@ -69,14 +69,16 @@ public class AddSkyboxAction extends AbstractNewSpatialWizardAction {
} else {
Texture textureSingle = (Texture) wiz.getProperty("textureSingle");
Vector3f normalScale = (Vector3f) wiz.getProperty("normalScale");
boolean useSpheremap = (Boolean) wiz.getProperty("useSpheremap");
SkyFactory.EnvMapType type = (SkyFactory.EnvMapType) wiz.getProperty("envMapType");
boolean flipY = (Boolean) wiz.getProperty("flipY");
// reload the texture so we can use flipY
TextureKey key = (TextureKey) textureSingle.getKey();
TextureKey newKey = new TextureKey(key.getName(), flipY);
newKey.setGenerateMips(true);
newKey.setAsCube(!useSpheremap);
return SkyFactory.createSky(pm, pm.loadTexture(newKey), normalScale, useSpheremap);
if(type == SkyFactory.EnvMapType.CubeMap){
newKey.setTextureTypeHint(Texture.Type.CubeMap);
}
return SkyFactory.createSky(pm, pm.loadTexture(newKey), normalScale, type);
}
}

@ -17,7 +17,6 @@ SkyboxVisualPanel2.multipleTexWestLoadButton.text=Load
SkyboxVisualPanel2.multipleTexTopLoadButton.text=Load
SkyboxVisualPanel2.multipleTexBottomLoadButton.text=Load
SkyboxVisualPanel2.jLabel9.text=Normal Scale (x,y,z):
SkyboxVisualPanel2.spheremapCheckBox.text=Sphere map
SkyboxVisualPanel2.normal1X.text=1
SkyboxVisualPanel2.normal1Y.text=1
SkyboxVisualPanel2.normal1Z.text=1
@ -32,3 +31,4 @@ SkyboxVisualPanel2.topPic.text=
SkyboxVisualPanel2.bottomPic.text=
SkyboxVisualPanel2.singlePic.text=
SkyboxVisualPanel2.flipYcheckBox.text=Flip Y
SkyboxVisualPanel2.jLabel10.text=Map type

@ -1,4 +1,4 @@
<?xml version="1.1" encoding="UTF-8" ?>
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
@ -127,7 +127,7 @@
</Group>
</Group>
</Group>
<EmptySpace pref="27" max="32767" attributes="0"/>
<EmptySpace pref="29" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@ -172,7 +172,7 @@
<Component id="jLabel5" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="multipleTexTopLoadButton" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="westPic" min="-2" pref="20" max="-2" attributes="0"/>
@ -376,11 +376,17 @@
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="flipYcheckBox" alignment="0" min="-2" pref="100" max="-2" attributes="1"/>
<Group type="102" alignment="0" attributes="0">
<Component id="jLabel8" min="-2" max="-2" attributes="0"/>
<Component id="jLabel10" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="mapTypeCombo" min="-2" pref="166" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="jLabel8" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="21" max="-2" attributes="0"/>
<Component id="singleTexLoadButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<EmptySpace min="-2" pref="2" max="-2" attributes="0"/>
<Component id="singlePic" min="-2" pref="20" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="39" max="-2" attributes="0"/>
<Component id="jLabel9" min="-2" max="-2" attributes="0"/>
@ -391,12 +397,8 @@
<EmptySpace min="6" pref="6" max="-2" attributes="0"/>
<Component id="normal2Z" min="-2" pref="21" max="-2" attributes="1"/>
</Group>
<Group type="103" alignment="0" groupAlignment="1" max="-2" attributes="0">
<Component id="flipYcheckBox" alignment="1" max="32767" attributes="1"/>
<Component id="spheremapCheckBox" alignment="1" max="32767" attributes="1"/>
</Group>
</Group>
<EmptySpace pref="31" max="32767" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@ -420,7 +422,10 @@
</Group>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="spheremapCheckBox" min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="mapTypeCombo" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="jLabel10" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="flipYcheckBox" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="75" max="32767" attributes="0"/>
@ -474,24 +479,39 @@
</Property>
</Properties>
</Component>
<Component class="javax.swing.JCheckBox" name="spheremapCheckBox">
<Component class="javax.swing.JLabel" name="singlePic">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="com/jme3/gde/terraineditor/sky/Bundle.properties" key="SkyboxVisualPanel2.spheremapCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="com/jme3/gde/terraineditor/sky/Bundle.properties" key="SkyboxVisualPanel2.singlePic.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="singlePic">
<Component class="javax.swing.JCheckBox" name="flipYcheckBox">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="com/jme3/gde/terraineditor/sky/Bundle.properties" key="SkyboxVisualPanel2.singlePic.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="com/jme3/gde/terraineditor/sky/Bundle.properties" key="SkyboxVisualPanel2.flipYcheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JCheckBox" name="flipYcheckBox">
<Component class="javax.swing.JComboBox" name="mapTypeCombo">
<Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
<StringArray count="4">
<StringItem index="0" value="Item 1"/>
<StringItem index="1" value="Item 2"/>
<StringItem index="2" value="Item 3"/>
<StringItem index="3" value="Item 4"/>
</StringArray>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="mapTypeComboActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="jLabel10">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="com/jme3/gde/terraineditor/sky/Bundle.properties" key="SkyboxVisualPanel2.flipYcheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="com/jme3/gde/terraineditor/sky/Bundle.properties" key="SkyboxVisualPanel2.jLabel10.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>

@ -33,10 +33,12 @@ package com.jme3.gde.terraineditor.sky;
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.core.properties.preview.TexturePreview;
import com.jme3.gde.core.scene.SceneApplication;
import com.jme3.texture.Texture;
import com.jme3.util.SkyFactory;
import java.awt.Component;
import javax.swing.DefaultComboBoxModel;
import javax.swing.Icon;
import javax.swing.JCheckBox;
import javax.swing.JPanel;
@ -46,11 +48,17 @@ import org.openide.util.ImageUtilities;
public final class SkyboxVisualPanel2 extends JPanel {
private DDSPreview ddsPreview;
private TexturePreview texPreview;
/** Creates new form SkyboxVisualPanel2 */
public SkyboxVisualPanel2() {
initComponents();
DefaultComboBoxModel<SkyFactory.EnvMapType> model = new DefaultComboBoxModel<SkyFactory.EnvMapType>();
for (SkyFactory.EnvMapType value : SkyFactory.EnvMapType.values()) {
model.addElement(value);
}
mapTypeCombo.setModel(model);
}
@Override
@ -101,6 +109,14 @@ public final class SkyboxVisualPanel2 extends JPanel {
return editorWest;
}
private TexturePreview getTexturePreview(){
if (texPreview == null) {
texPreview = new TexturePreview((ProjectAssetManager) SceneApplication.getApplication().getAssetManager());
}
return texPreview;
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
@ -140,11 +156,12 @@ public final class SkyboxVisualPanel2 extends JPanel {
normal2X = new javax.swing.JTextField();
normal2Y = new javax.swing.JTextField();
normal2Z = new javax.swing.JTextField();
spheremapCheckBox = new javax.swing.JCheckBox();
singlePic = new javax.swing.JLabel();
flipYcheckBox = new javax.swing.JCheckBox();
mapTypeCombo = new javax.swing.JComboBox();
jLabel10 = new javax.swing.JLabel();
titleLabel.setFont(new java.awt.Font("Tahoma", 1, 14));
titleLabel.setFont(new java.awt.Font("Tahoma", 1, 14)); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(titleLabel, org.openide.util.NbBundle.getMessage(SkyboxVisualPanel2.class, "SkyboxVisualPanel2.titleLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(SkyboxVisualPanel2.class, "SkyboxVisualPanel2.jLabel1.text")); // NOI18N
@ -279,7 +296,7 @@ public final class SkyboxVisualPanel2 extends JPanel {
.addComponent(multipleTexTopLoadButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(topPic, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)))))
.addContainerGap(27, Short.MAX_VALUE))
.addContainerGap(29, Short.MAX_VALUE))
);
multipleTexturePanelLayout.setVerticalGroup(
multipleTexturePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
@ -315,7 +332,7 @@ public final class SkyboxVisualPanel2 extends JPanel {
.addGroup(multipleTexturePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel5)
.addComponent(multipleTexTopLoadButton))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addGroup(multipleTexturePanelLayout.createSequentialGroup()
.addComponent(westPic, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
@ -346,12 +363,19 @@ public final class SkyboxVisualPanel2 extends JPanel {
normal2Z.setText(org.openide.util.NbBundle.getMessage(SkyboxVisualPanel2.class, "SkyboxVisualPanel2.normal2Z.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(spheremapCheckBox, org.openide.util.NbBundle.getMessage(SkyboxVisualPanel2.class, "SkyboxVisualPanel2.spheremapCheckBox.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(singlePic, org.openide.util.NbBundle.getMessage(SkyboxVisualPanel2.class, "SkyboxVisualPanel2.singlePic.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(flipYcheckBox, org.openide.util.NbBundle.getMessage(SkyboxVisualPanel2.class, "SkyboxVisualPanel2.flipYcheckBox.text")); // NOI18N
mapTypeCombo.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
mapTypeCombo.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
mapTypeComboActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(jLabel10, org.openide.util.NbBundle.getMessage(SkyboxVisualPanel2.class, "SkyboxVisualPanel2.jLabel10.text")); // NOI18N
javax.swing.GroupLayout singleTexturePanelLayout = new javax.swing.GroupLayout(singleTexturePanel);
singleTexturePanel.setLayout(singleTexturePanelLayout);
singleTexturePanelLayout.setHorizontalGroup(
@ -359,11 +383,16 @@ public final class SkyboxVisualPanel2 extends JPanel {
.addGroup(singleTexturePanelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(singleTexturePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(flipYcheckBox, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGroup(singleTexturePanelLayout.createSequentialGroup()
.addComponent(jLabel8)
.addComponent(jLabel10)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(mapTypeCombo, javax.swing.GroupLayout.PREFERRED_SIZE, 166, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(singleTexturePanelLayout.createSequentialGroup()
.addComponent(jLabel8)
.addGap(21, 21, 21)
.addComponent(singleTexLoadButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGap(2, 2, 2)
.addComponent(singlePic, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(39, 39, 39)
.addComponent(jLabel9)
@ -372,11 +401,8 @@ public final class SkyboxVisualPanel2 extends JPanel {
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(normal2Y, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(6, 6, 6)
.addComponent(normal2Z, javax.swing.GroupLayout.PREFERRED_SIZE, 21, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(singleTexturePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
.addComponent(flipYcheckBox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(spheremapCheckBox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
.addContainerGap(31, Short.MAX_VALUE))
.addComponent(normal2Z, javax.swing.GroupLayout.PREFERRED_SIZE, 21, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
singleTexturePanelLayout.setVerticalGroup(
singleTexturePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
@ -394,7 +420,9 @@ public final class SkyboxVisualPanel2 extends JPanel {
.addComponent(normal2Z, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(jLabel9))))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(spheremapCheckBox)
.addGroup(singleTexturePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(mapTypeCombo, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(jLabel10))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(flipYcheckBox)
.addContainerGap(75, Short.MAX_VALUE))
@ -428,148 +456,76 @@ public final class SkyboxVisualPanel2 extends JPanel {
private void multipleTexSouthLoadButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_multipleTexSouthLoadButtonActionPerformed
Component view = editorSouth.getCustomEditor();
view.setVisible(true);
if (editorSouth.getValue() != null) {
Texture tex = (Texture) editorSouth.getValue();
String selected = tex.getKey().getName();
if (selected.toLowerCase().endsWith(".dds")) {
if (ddsPreview == null) {
ddsPreview = new DDSPreview((ProjectAssetManager) SceneApplication.getApplication().getAssetManager());
}
ddsPreview.requestPreview(selected, "", 80, 80, southPic, null);
} else {
Icon newicon = ImageUtilities.image2Icon(ImageToAwt.convert(tex.getImage(), false, true, 0));
southPic.setIcon(newicon);
}
if (editorSouth.getAsText()!= null) {
String selected = editorSouth.getAsText();
getTexturePreview().requestPreview(selected, "", 80, 80, southPic, null);
}
}//GEN-LAST:event_multipleTexSouthLoadButtonActionPerformed
private void multipleTexNorthLoadButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_multipleTexNorthLoadButtonActionPerformed
Component view = editorNorth.getCustomEditor();
view.setVisible(true);
if (editorNorth.getValue() != null) {
Texture tex = (Texture) editorNorth.getValue();
String selected = tex.getKey().getName();
if (selected.toLowerCase().endsWith(".dds")) {
if (ddsPreview == null) {
ddsPreview = new DDSPreview((ProjectAssetManager) SceneApplication.getApplication().getAssetManager());
}
ddsPreview.requestPreview(selected, "", 80, 80, northPic, null);
} else {
Icon newicon = ImageUtilities.image2Icon(ImageToAwt.convert(tex.getImage(), false, true, 0));
northPic.setIcon(newicon);
}
if (editorNorth.getAsText() != null) {
String selected = editorNorth.getAsText();
getTexturePreview().requestPreview(selected, "", 80, 80, northPic, null);
}
}//GEN-LAST:event_multipleTexNorthLoadButtonActionPerformed
private void multipleTexEastLoadButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_multipleTexEastLoadButtonActionPerformed
Component view = editorEast.getCustomEditor();
view.setVisible(true);
if (editorEast.getValue() != null) {
Texture tex = (Texture) editorEast.getValue();
String selected = tex.getKey().getName();
if (selected.toLowerCase().endsWith(".dds")) {
if (ddsPreview == null) {
ddsPreview = new DDSPreview((ProjectAssetManager) SceneApplication.getApplication().getAssetManager());
}
ddsPreview.requestPreview(selected, "", 80, 80, eastPic, null);
} else {
Icon newicon = ImageUtilities.image2Icon(ImageToAwt.convert(tex.getImage(), false, true, 0));
eastPic.setIcon(newicon);
}
if (editorEast.getAsText() != null) {
String selected = editorEast.getAsText();
getTexturePreview().requestPreview(selected, "", 80, 80, eastPic, null);
}
}//GEN-LAST:event_multipleTexEastLoadButtonActionPerformed
private void multipleTexWestLoadButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_multipleTexWestLoadButtonActionPerformed
Component view = editorWest.getCustomEditor();
view.setVisible(true);
if (editorWest.getValue() != null) {
Texture tex = (Texture) editorWest.getValue();
String selected = tex.getKey().getName();
if (selected.toLowerCase().endsWith(".dds")) {
if (ddsPreview == null) {
ddsPreview = new DDSPreview((ProjectAssetManager) SceneApplication.getApplication().getAssetManager());
}
ddsPreview.requestPreview(selected, "", 80, 80, westPic, null);
} else {
Icon newicon = ImageUtilities.image2Icon(ImageToAwt.convert(tex.getImage(), false, true, 0));
westPic.setIcon(newicon);
}
if (editorWest.getAsText() != null) {
String selected = editorWest.getAsText();
getTexturePreview().requestPreview(selected, "", 80, 80, westPic, null);
}
}//GEN-LAST:event_multipleTexWestLoadButtonActionPerformed
private void multipleTexTopLoadButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_multipleTexTopLoadButtonActionPerformed
Component view = editorTop.getCustomEditor();
view.setVisible(true);
if (editorTop.getValue() != null) {
Texture tex = (Texture) editorTop.getValue();
String selected = tex.getKey().getName();
if (selected.toLowerCase().endsWith(".dds")) {
if (ddsPreview == null) {
ddsPreview = new DDSPreview((ProjectAssetManager) SceneApplication.getApplication().getAssetManager());
}
ddsPreview.requestPreview(selected, "", 80, 80, topPic, null);
} else {
Icon newicon = ImageUtilities.image2Icon(ImageToAwt.convert(tex.getImage(), false, true, 0));
topPic.setIcon(newicon);
}
if (editorTop.getAsText() != null) {
String selected = editorTop.getAsText();
getTexturePreview().requestPreview(selected, "", 80, 80, topPic, null);
}
}//GEN-LAST:event_multipleTexTopLoadButtonActionPerformed
private void multipleTexBottomLoadButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_multipleTexBottomLoadButtonActionPerformed
Component view = editorBottom.getCustomEditor();
view.setVisible(true);
if (editorBottom.getValue() != null) {
Texture tex = (Texture) editorBottom.getValue();
String selected = tex.getKey().getName();
if (selected.toLowerCase().endsWith(".dds")) {
if (ddsPreview == null) {
ddsPreview = new DDSPreview((ProjectAssetManager) SceneApplication.getApplication().getAssetManager());
}
ddsPreview.requestPreview(selected, "", 80, 80, bottomPic, null);
} else {
Icon newicon = ImageUtilities.image2Icon(ImageToAwt.convert(tex.getImage(), false, true, 0));
bottomPic.setIcon(newicon);
}
if (editorBottom.getAsText() != null) {
String selected = editorBottom.getAsText();
getTexturePreview().requestPreview(selected, "", 80, 80, bottomPic, null);
}
}//GEN-LAST:event_multipleTexBottomLoadButtonActionPerformed
private void singleTexLoadButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_singleTexLoadButtonActionPerformed
Component view = editorSingle.getCustomEditor();
view.setVisible(true);
if (editorSingle.getValue() != null) {
Texture tex = (Texture) editorSingle.getValue();
String selected = tex.getKey().getName();
if (selected.toLowerCase().endsWith(".dds")) {
if (ddsPreview == null) {
ddsPreview = new DDSPreview((ProjectAssetManager) SceneApplication.getApplication().getAssetManager());
}
ddsPreview.requestPreview(selected, "", 80, 80, singlePic, null);
} else {
Icon newicon = ImageUtilities.image2Icon(ImageToAwt.convert(tex.getImage(), false, true, 0));
singlePic.setIcon(newicon);
}
if (editorSingle.getAsText()!= null) {
String selected = editorSingle.getAsText();
getTexturePreview().requestPreview(selected, "", 80, 80, singlePic, null);
}
}//GEN-LAST:event_singleTexLoadButtonActionPerformed
private void mapTypeComboActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_mapTypeComboActionPerformed
// TODO add your handling code here:
}//GEN-LAST:event_mapTypeComboActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel bottomPic;
private javax.swing.JLabel eastPic;
private javax.swing.JCheckBox flipYcheckBox;
private javax.swing.JLabel jLabel1;
private javax.swing.JLabel jLabel10;
private javax.swing.JLabel jLabel2;
private javax.swing.JLabel jLabel3;
private javax.swing.JLabel jLabel4;
@ -578,6 +534,7 @@ public final class SkyboxVisualPanel2 extends JPanel {
private javax.swing.JLabel jLabel7;
private javax.swing.JLabel jLabel8;
private javax.swing.JLabel jLabel9;
private javax.swing.JComboBox mapTypeCombo;
private javax.swing.JButton multipleTexBottomLoadButton;
private javax.swing.JButton multipleTexEastLoadButton;
private javax.swing.JButton multipleTexNorthLoadButton;
@ -596,7 +553,6 @@ public final class SkyboxVisualPanel2 extends JPanel {
private javax.swing.JButton singleTexLoadButton;
private javax.swing.JPanel singleTexturePanel;
private javax.swing.JLabel southPic;
private javax.swing.JCheckBox spheremapCheckBox;
private javax.swing.JLabel titleLabel;
private javax.swing.JLabel topPic;
private javax.swing.JLabel westPic;
@ -626,10 +582,11 @@ public final class SkyboxVisualPanel2 extends JPanel {
return normal2Z;
}
public JCheckBox getSpheremapCheckBox() {
return spheremapCheckBox;
public SkyFactory.EnvMapType getEnvMapType(){
return (SkyFactory.EnvMapType)mapTypeCombo.getSelectedItem();
}
public JCheckBox getFlipYCheckBox() {
return flipYcheckBox;
}

@ -123,6 +123,7 @@ public class SkyboxWizardPanel2 implements WizardDescriptor.Panel {
}
}
@Override
public void storeSettings(Object settings) {
WizardDescriptor wiz = (WizardDescriptor) settings;
SkyboxVisualPanel2 comp = (SkyboxVisualPanel2) getComponent();
@ -143,7 +144,7 @@ public class SkyboxWizardPanel2 implements WizardDescriptor.Panel {
float y = new Float(comp.getNormal2Y().getText());
float z = new Float(comp.getNormal2Z().getText());
wiz.putProperty("normalScale", new Vector3f(x,y,z) );
wiz.putProperty("useSpheremap", comp.getSpheremapCheckBox().isSelected());
wiz.putProperty("envMapType", comp.getEnvMapType());
wiz.putProperty("flipY", comp.getFlipYCheckBox().isSelected());
}
}

Loading…
Cancel
Save